文章目录
深度学习项目流程全解析1. 数据获取方法1.1 开源数据集1.2 外包平台1.3 自己采集和标注1.4 通过网络爬取
2. 数据本地化2.1. 图片本地化2.2 加载图片数据集2.3 本地图片序列化
3. 过拟合处理3.1 数据增强3.1.1 数据增强的方法
3.2 标准化3.3 DROP-OUT3.4 欠拟合注意事项
4. 训练过程可视化4.1 该可视化什么(核心清单):4.2 Tensor Board4.3 准备工作4.4 保存训练过程曲线4.5 曲线查看4.6 保存网络结构4.7 模型参数可视化4.8 记录训练数据4.9 避坑指南
5. 验证结果数据化5.1 数据结果Excel5.2 模型指标矩阵化5.2.1 混淆矩阵5.2.2 常见指标5.2.3 理解对角线5.2.4 模型指标计算及可视化2.4.1 分类报告2.4.2 准确度(Accuracy)2.4.3 精确度(Precision)2.4.4 召回率(Recall)2.4.5 F1分数(F1-Score)2.4.6 混淆矩阵及可视化
6. 网络性能提升6.1 使用更复杂的模型6.1.1 导入模型6.1.2 使用模型
6.2 继续训练6.3 预训练和迁移学习6.3.1 导入6.3.2 初始化6.3.3 保存初始权重文件6.3.4 修改网络结构6.3.5 调整权重参数6.3.6 新参数+新模型
6.4 调整优化器和学习率6.4.1 冻结层6.4.2 学习率的调整
7. 总结
深度学习项目流程全解析
1. 数据获取方法
1.1 开源数据集
公开数据集:MNIST、CIFAR-10、ImageNet 等网络爬取:爬虫、API 接口获取自建数据集:人工采集、标注工具(LabelImg、CVAT)第三方平台:Kaggle、UCI、Google Dataset Search
1.2 外包平台
效果好,成本高
外包平台(Amazon Mechanical Turk,阿里众包,百度数据众包,京东微工等) 还有咸鱼
1.3 自己采集和标注
质量高,效率低,时间成本高
pip install labelimg
1.4 通过网络爬取
import requests
from bs4 import BeautifulSoup
import pandas as pd
def crawl_news():
# 目标网页(这里以人民网国际频道为例)
url = "http://world.people.com.cn/"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36"
}
# 发送请求
response = requests.get(url, headers=headers)
response.encoding = response.apparent_encoding # 自动识别网页编码
if response.status_code != 200:
print("请求失败:", response.status_code)
return
# 解析 HTML
soup = BeautifulSoup(response.text, "html.parser")
# 找到新闻标题(人民网首页新闻一般放在 <a> 标签中)
news_list = []
for a in soup.select("a"): # 提取所有链接
title = a.get_text(strip=True)
link = a.get("href")
if title and link and len(title) > 5: # 过滤掉空的/短的标题
news_list.append({"title": title, "link": link})
# 保存到 CSV
df = pd.DataFrame(news_list)
df.to_csv("news.csv", index=False, encoding="utf-8-sig")
print(f"成功抓取 {len(df)} 条新闻,已保存到 news.csv")
if __name__ == "__main__":
crawl_news()
2. 数据本地化
使用公开数据集时,会自动保存到本地。如果已下载,就不会重复下载。如果需要以图片的形式保存到本地以方便观察和重新处理,可以按照如下方式处理。
2.1. 图片本地化
使用一下代码保存图片到本地
dir = os.path.dirname(__file__)
def save2local():
trainimgsdir = os.path.join(dir, "MNIST/trainimgs")
testimgsdir = os.path.join(dir, "MNIST/testimgs")
if not os.path.exists(trainimgsdir):
os.makedirs(trainimgsdir)
if not os.path.exists(testimgsdir):
os.makedirs(testimgsdir)
trainset = torchvision.datasets.MNIST(
root=datapath,
train=True,
download=True,
transform=transforms.Compose([transforms.ToTensor()]),
)
for idx, (img, label) in enumerate(trainset):
labdir = os.path.join(trainimgsdir, str(label))
os.makedirs(labdir, exist_ok=True)
pilimg = transforms.ToPILImage()(img)
# 保存成单通道的灰度图
pilimg = pilimg.convert("L")
pilimg.save(os.path.join(labdir, f"{idx}.png"))
# 加载测试集
testset = torchvision.datasets.MNIST(
root=datapath,
train=False,
download=True,
transform=transforms.Compose([transforms.ToTensor()]),
)
for idx, (img, label) in enumerate(testset):
labdir = os.path.join(testimgsdir, str(label))
os.makedirs(labdir, exist_ok=True)
pilimg = transforms.ToPILImage()(img)
# 保存成单通道的灰度图
pilimg = pilimg.convert("L")
pilimg.save(os.path.join(labdir, f"{idx}.png"))
print("所有图片保存成功~~")
2.2 加载图片数据集
直接下载的图片文件目录也可以直接使用
trainpath = os.path.join(dir, "MNIST/trainimgs")
trainset = torchvision.datasets.ImageFolder(root=trainpath, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=32, shuffle=True)
2.3 本地图片序列化
把本地图片存储为 pickle序列化格式,然后通过 tar格式的形式分发。
import os
import pickle
import torchvision
import torchvision.transforms as transforms
dir = os.path.dirname(__file__)
datapath = os.path.join(dir, "MNIST")
def save_pickle():
trainset = torchvision.datasets.MNIST(
root=datapath,
train=True,
download=True,
transform=transforms.ToTensor()
)
testset = torchvision.datasets.MNIST(
root=datapath,
train=False,
download=True,
transform=transforms.ToTensor()
)
# 存储路径
outdir = os.path.join(dir, "MNIST_pickle")
os.makedirs(outdir, exist_ok=True)
# 将整个训练集序列化成一个文件
train_path = os.path.join(outdir, "train.pkl")
with open(train_path, "wb") as f:
pickle.dump([(img.numpy(), label) for img, label in trainset], f)
# 将整个测试集序列化成一个文件
test_path = os.path.join(outdir, "test.pkl")
with open(test_path, "wb") as f:
pickle.dump([(img.numpy(), label) for img, label in testset], f)
print("训练集和测试集已保存为 pickle 格式")
3. 过拟合处理
3.1 数据增强
可以使用transform完成对图像的数据增强,防止过拟合发生
3.1.1 数据增强的方法
随机旋转 镜像 缩放 图像模糊 裁剪 翻转 饱和度 亮度 灰度 色相 噪声 锐化 颜色反转 多样本增强。
其中多样本增强:
SamplePairing操作:随机选择两张图片分别经过基础数据增强操作处理后,叠加合成一个新的样本,标签为原样本标签中的一种。
①、多样本线性插值:Mixup 标签更平滑
②、直接复制:CutMix, Cutout,直接复制粘贴样本
③、Mosic:四张图片合并到一起进行训练
数据增强的好处:查出更多训练数据,大幅度降低数据采集和标注成本
提升泛化能力,模型过拟合风险降低,提高模型泛化能力
3.2 标准化
那么这里需要解释的是:标准化本身不是直接的正则化方法。首先过拟合的主要原因是模型在训练时学习了不必要的特征或者被某些噪声点影响。标准化主要作用是缓解过拟合。
标准化后梯度下降效率提升,优化更快地收敛避免某些特征影响模型,也就是增加了模型平衡减少模型的复杂度,模型不会过度依赖某些数值较大的特征减少权重参数被迫变大,从而降低模型队训练数据噪声的拟合配合正则化效果会更好
3.3 DROP-OUT
处理过拟合问题
这个东西呢通过以下这些机制来解决过拟合问题:
首先他可以打破某些神经元之间的共适应,因为某些神经元可能会捆绑起来,在固定场合下工作每次丢弃神经元都会产生一个子网络,就相当于学习一个子网路集合,就是模型学习这些子网络平均化后的效果有点类似引入噪声,增加正则化效果。
3.4 欠拟合注意事项
欠拟合: 如果模型在训练集和验证集上表现都不够好,考虑增加模型的层级或训练更多的周期
4. 训练过程可视化
训练可视化不仅能让你直观地看到模型是否收敛、有没有机会过拟学习率、梯度是否正常、模型学到什么特征。还能帮助调参和复现实验
4.1 该可视化什么(核心清单):
训练/验证 Loss与Accuracy曲线(必做)
学习率曲线(尤其使用lr scheduler时)
权重与梯度分布/梯度范数(检测梯度消失,爆炸)
卷积核(filter)与中间特征图(feature maps)可视化
混淆矩阵/分类报告/top-k错误样本(结果数据化)
可视化示例预测(show inputs + ground-truth + preds + probs)
ROC / PR 曲线(二分类或多类 one-vs-rest)
Embedding 投影(t-SNE/UMAP)与标签上色(用于特征可视化)
Grad-CAM / 可解释性热力图(分析网络关注区域)
多次实验/超参对比(对比多条曲线或使用 TensorBoard run 比较)
TensorBoard(PyTorch 的
) — 基本且功能齐全(标量、图像、直方图、embedding)。
torch.utils.tensorboard.SummaryWriter
Matplotlib / Seaborn — 做静态图(用于保存到文章、报告)。
tqdm — 显示训练进度。
sklearn.metrics — 混淆矩阵、ROC、PR 等指标。
可选(线上/团队协作):Weights & Biases、Neptune、Comet(这些可做实验跟踪与对比)。
4.2 Tensor Board
官方文档: https://pytorch.org/docs/stable/tensorboard.html
4.3 准备工作
导入tensorboard操作模块
from torch.utils.tensorboard import SummaryWriter
指定tensorboard日志保存路径:可以指定多个实例对象
dir = os.path.dirname(__file__)
tbpath = os.path.join(dir, "tensorboard")
# 指定tensorboard日志保存路径
writer = SummaryWriter(log_dir=tbpath)
4.4 保存训练过程曲线
记录训练数据
# 记录训练数据到可视化面板
writer.add_scalar("Loss/train", loss, epoch)
writer.add_scalar("Accuracy/train", acc, epoch)
训练完后记得关闭
writer.close()
4.5 曲线查看
安装:安装的是执行指令,是一个本地化的服务器
pip install tensorboard
在训练完成后,查看训练结果,在当前目录下,打开控制台窗口:
tensorboard --logdir=runs
控制台会提示一个访问地址,用浏览器直接访问即可。
4.6 保存网络结构
保存网络结构到tensorboard
# 保存模型结构到tensorboard
writer.add_graph(net, input_to_model=torch.randn(1, 1, 28, 28))
writer.close()
启动tensorboard,在graphs菜单即可看到模型结构
4.7 模型参数可视化
# 获取模型参数并循环记录
params = net.named_parameters()
for name, param in params:
writer.add_histogram(f"{name}_{i}", param.clone().cpu().data.numpy(), epoch)
4.8 记录训练数据
中的
tensorboard
函数用于将图像数据记录到TensorBoard,以便可视化和分析。这对于查看训练过程中生成的图像、调试和理解模型的行为非常有用,如帮助检查预处理是否生效。
add_image
#查看预处理的旋转是否生效
for i, data in enumerate(trainloader, 0):
inputs, labels = data
if i % 100 == 0:
img_grid = torchvision.utils.make_grid(inputs)
writer.add_image(f"r_m_{epoch}_{i * 100}", img_grid, epoch * len(trainloader) + i)
4.9 避坑指南
尽可能使用新版本的pyTorch
pip install tensorboard
在创建实例对象时使用默认值
# 导入训练过程可视化工具tensorboard
from torch.utils.tensorboard import SummaryWriter
# Writer will output to ./runs/ directory by default
writer = SummaryWriter()
启动tensorboard:
tensorboard --logdir=runs
不要在IDE(如vsCode)里面安装tensorboard插件
5. 验证结果数据化
我们可以把预测结果全部记录下来,以观察其效果,用于分析和评估我们的模型情况。
5.1 数据结果Excel
导入数据分析模块pandas
import pandas as pd
把推理结果softmax化,并放到全局的列表里面
#定义全局的 pd_data :10分类,加了分类id 和 分类名称, 12个
pd_data = np.empty((0, 12))
#在训练的过程中把训练结果整理进来
out_softmax = torch.softmax(out, dim=1).cpu().detach().numpy()
target_y = y.cpu().detach().numpy()
#根据目标值找到分类名
target_name = np.array([vaild_dataset.classes[i] for i in target_y])
# 把真实所属分类追加到数据中
out_softmax = np.concatenate(
(out_softmax, target_y.reshape(-1, 1), target_name.reshape(-1, 1)), axis=1
)
#数据追加
pd_data = np.concatenate((pd_data, out_softmax), axis=0)
保存数据到CSV
# 数据有了,找到列名:和前面追加的目标值及类名要呼应上
columnsn = np.concatenate(
(vaild_dataset.classes, np.array(["target", "真实值"])), axis=0
)
pd_data_df = pd.DataFrame(pd_data, columns=columnsn)
# 把数据保存到Excel:CSV
csvpath = os.path.join(dir, "vaild")
if not os.path.exists(csvpath):
os.makedirs(csvpath)
pd_data_df.to_csv(os.path.join(csvpath, "vaild.csv"), encoding="GBK")
5.2 模型指标矩阵化
5.2.1 混淆矩阵
混淆矩阵是一种特定的表格布局,用于可视化监督学习算法的性能,特别是分类算法。在这个矩阵中,每一行代表实际类别,每一列代表预测类别。矩阵的每个单元格则包含了在该实际类别和预测类别下的样本数量。通过混淆矩阵,我们不仅可以计算出诸如准确度、精确度和召回率等评估指标,还可以更全面地了解模型在不同类别上的性能。
混淆矩阵的四个基本组成部分是:
True Positives(TP):当模型预测为正类,并且该预测是正确的,我们称之为真正(True Positive);True Negatives(TN):当模型预测为负类,并且该预测是正确的,我们称之为真负(True Negative);False Positives(FP):当模型预测为正类,但该预测是错误的,我们称之为假正(False Positive);False Negatives(FN):当模型预测为负类,但该预测是错误的,我们称之为假负(False Negative)
5.2.2 常见指标
参考下图
5.2.3 理解对角线
对角线上的元素越大越好
5.2.4 模型指标计算及可视化
2.4.1 分类报告
csvpath = os.path.join(dir, "vaild")
# 读取CSV数据
csvdata = pd.read_csv(
os.path.join(csvpath, "vaild.csv"), encoding="GBK", index_col=0
)
# 拿到真实标签
true_label = csvdata["target"].values
print(true_label, type(true_label), len(true_label))
# 获取预测标签
predict_label = csvdata.iloc[:, :-2].values
print(predict_label, predict_label.shape)
# 预测分类及分数的提取
predict_label_ind = np.argmax(predict_label, axis=1)
predict_label_score = np.max(predict_label, axis=1)
print(predict_label_ind, predict_label_score)
# 根据预测值和真实值生成分类报告
report = classification_report(y_true=true_label, y_pred=predict_label_ind)
print(report)
效果:
2.4.2 准确度(Accuracy)
# 获取准确度
acc = accuracy_score(y_true=true_label, y_pred=predict_label_ind)
print("准确度:", acc)
2.4.3 精确度(Precision)
# 获取精确度Precision
precision = precision_score(y_true=true_label, y_pred=predict_label_ind, average="macro")
print("精确度:", precision)
2.4.4 召回率(Recall)
#召回率(Recall)
recall = recall_score(y_true=true_label, y_pred=predict_label_ind, average="macro")
print("召回率:", recall)
2.4.5 F1分数(F1-Score)
#### F1分数(F1-Score)
f1 = f1_score(y_true=true_label, y_pred=predict_label_ind, average="macro")
print("F1分数:", f1)
2.4.6 混淆矩阵及可视化
import os
from sklearn.metrics import *
import pandas as pd
from matplotlib import pyplot as plt
# plt中文乱码问题
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
# 路径兼容处理
current_path = os.path.dirname(__file__)
excel_path = os.path.relpath(
os.path.join(current_path, r"./metrics", "validation_metrics.xlsx")
)
def report():
# 读取Excel数据
excel_data = pd.read_excel(excel_path)
label = excel_data["label"].values
predict = excel_data["predict"].values
# 整体报表
labels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
matrix = confusion_matrix(label, predict)
print(matrix)
# 下面的代码就是简单的plt绘制过程
plt.matshow(matrix, cmap=plt.cm.Greens)
# 显示颜色条
plt.colorbar()
# 显示具体的数字的过程
for i in range(len(matrix)):
for j in range(len(matrix)):
plt.annotate(
matrix[i, j],
xy=(j, i),
horizontalalignment="center",
verticalalignment="center",
)
# 美化的东西
plt.xlabel("Pred labels")
plt.ylabel("True labels")
plt.xticks(range(len(labels)), labels, rotation=45)
plt.yticks(range(len(labels)), labels)
plt.title("训练结果混淆矩阵视图")
plt.show()
if __name__ == "__main__":
report()
效果:
6. 网络性能提升
6.1 使用更复杂的模型
官方提供了强大的模型可供我们使用:
https://pytorch.org/vision/0.17/models.html#classification
6.1.1 导入模型
# 导入模型:我这里依然使用了以前的模型名称,只是为了不改代码
from torchvision.models import resnet18 as ImageClassifier
6.1.2 使用模型
#num_classes参数很重要,是你要的分类数量,默认是1000
model = ImageClassifier(num_classes=10)
就这么就完事了~~~
6.2 继续训练
50轮次训练完成之后,在tensorboard里面观察到,整体的准确率稳定上升,但是效果还不是很好,此时应该在原训练好的权重参数基础之上继续训练。
不能每次都从0开始训练。
6.3 预训练和迁移学习
在原始的已经学习了基本特征的权重参数基础之上,继续进行训练,而不是每次都从0开始。
原始权重参数:
官方经典网络模型的预训练参数:别人已经训练好了;也可以是自己训练好的权重文件;
迁移学习步骤:
6.3.1 导入
from torchvision.models import resnet18, ResNet18_Weights
6.3.2 初始化
weight = ResNet18_Weights.DEFAULT
model = resnet18(weights=weight)
model.to(device)
6.3.3 保存初始权重文件
# 保存模型权重文件到本地
if not os.path.exists(os.path.join(mdelpath, f"model_res18.pth")):
torch.save(model.state_dict(), os.path.join(mdelpath, f"model_res18.pth"))
6.3.4 修改网络结构
重新加载resnet18模型并修改网络结构。
ResNet18默认有1000个类别,和我们的需求不匹配需要修改网络结构
# 重新加载网络模型:需要根据分类任务进行模型结构调整
pretrained_model = resnet18(weights=None)
# print(pretrained_model)
in_features_num = pretrained_model.fc.in_features
pretrained_model.fc = nn.Linear(in_features_num, 10)
6.3.5 调整权重参数
以满足调整网络结构后的新模型,主要在全连接层
# 加载刚才下载的权重参数
weight18 = torch.load(os.path.join(mdelpath, f"model_res18.pth"))
print(weight18.keys())
# 全连接层被我们修改了,需要删除历史的全连接层参数
weight18.pop("fc.weight")
weight18.pop("fc.bias")
# 获取自己的模型的参数信息
my_resnet18_dict = pretrained_model.state_dict()
# 去除不必要的权重参数
weight18 = {k:v for k, v in weight18.items() if k in my_resnet18_dict}
#更新
my_resnet18_dict.update(weight18)
6.3.6 新参数+新模型
# 处理完后把最新的参数更新到模型中
pretrained_model.load_state_dict(my_resnet18_dict)
model = pretrained_model.to(device)
6.4 调整优化器和学习率
6.4.1 冻结层
对于不在需要训练的网络层,可以把梯度更新关闭:根据具体需求来,会影响训练效果。
# 冻结层:自己打印和调试,是完全可以的
for name, value in pretrained_model.named_parameters():
if name != "fc.weight" and name != "fc.bias":
value.requires_grad = False
# 开始筛选需要进行梯度更新的参数,而不是全部
params_grade_true = filter(lambda x: x.requires_grad, pretrained_model.parameters() )
# 创建优化器
# optimizer = optim.Adam(model.parameters(), lr=learning_rate)
optimizer = optim.Adam(params_grade_true, lr=learning_rate)
6.4.2 学习率的调整
是 PyTorch 中的一种学习率调度器,用于以固定步长周期性地降低学习率。
StepLR
#step_size:每经过多少个 epoch,学习率减少一次。
#gamma:学习率每次减少时的倍率因子。 0.01 ---> 30个ecposh之后,变成0.001---> 30个ecposh之后,变成0.0001
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
# 一轮训练完更新学习率
scheduler.step()
7. 总结
数据处理 → 模型训练 → 可视化 → 验证评估 → 优化迭代不同任务可灵活组合,最终目标是 提升泛化性能 和 应用效果