理解batch
- batch size: yolo 每次会从整个数据集中随机选出一个batch的数据进行前向传播训练.
- 计算此btch上所有样本的损失函数值.
- 通过损失对各层参数计梯度求导, 计算出权重参数新值
- 将计算出的更新值应用到模型上, 完成参数的批量更新
- 重复采样下一个batch, 反复完成前向和后向传播, 直至完成一个全量的epoch
- 即yolov8采用的训练策略是batch 更新方式.
理解epoch
- epoch 是训练一个完整数据集的过程, 训练一个epoch后, 会自动完成一次 val, 我们可以观察收敛情况.
训练过程中如何进行val和如何结束训练
- YoloV8 在每个epoch完成后, 会使用val数据集进行验证, 来计算loss和mAP等指标
- 如果当前epoch的loss比之前最佳的loss还大, 就会增加一个early_stops计数, 如果比最佳loss小, 则early_stops清零. 如果early_stops计数大于阈值, 则进行早停(early_stopping)操作, 即整个训练不需要一定要完整预设的epoch次数.
- 早停机制是防止过拟合的重要手段.
- 我们可以通过 yolov8 的 patience 设置early_stops阈值
YoloV8的Fine-tuning和预训练和from scratch训练
yolo命令行model的参数的说明既可以选择 yolov8n.pt, 也可以选择 yolov8n.yaml, 区别是:
- model=yolov8n.pt, 即为Fine-tuning训练, yolov8n.pt 模型文件已经包含了 yolo v8网络结构、超参数、训练参数、 权重参数信息, 它是官方的pre-trained 模型文件, 官方基于大规模数据集(coco 数据集)的80个类别训练而成.
- model=yolov8n.yaml pretrained=yolov8n.pt, 这是一个全新模型配置文件 + 预训练模型文件的组合, 训练过程将使用yaml模型配置文件, 包含模型的网络结构, 超参数和训练参数等信息, 但初始权重使用预训练的yolov8n.pt, 这样收敛速度较快, 训练过程比完整的from scratch要会短一些.
- model=yolov8n.yaml pretrained=False ,这是from scratch 训练, yolov8n.yaml文件包含模型的网络结构, 超参数和训练参数等信息, 我们可以基于这样的模型定义+自定义数据集训练出自己的模型权重文件, 用于后续的预测. 训练耗时最长.
yolov8预训练模型是基于coco和imagenet大型数据集做的训练, 所以这样的预训练权重已经非常具备通用性了, 对于99%的情况都适用, 如果完成从0开始做预训练,初始权重太过随机,很难收敛,最终网路训练结果也不会太好.
如果我们修改了网络, 预训练权重基本上不能使用了, 需要从头开始训练网络, 从头训练网络需要有好的算力和大的数据集, 否则做不成.
深度学习通过冻结部分参数提升效率效率
- 深度学习模型通常包括通用特征提取(卷积+池化)和高层分类(卷积+池化)和结果输出(全连接层)三个阶段.
- 特征提取是通用特征, 这部分参数通常无需调整
- 所以, 可通过冻结特征提取参数来减少计算量, 提升fine-tuning的效率, 步骤一般为:
- (1)先冻结前面几层卷积和池化层, 仅仅更新后面负责高级特征处理的卷积和池化层(统称为later classifier)
- (2) 最后解冻全连接层, 完成分类任务的微调
cd myEnv\Scripts
# 基于预训练 yolov8n.pt 进行自有数据的training, 换句话说, 是针对自有数据进行模型的 fine-tuning, 训练耗时较短.
.\yolo task=detect mode=train data=coco8.yaml model=yolov8n.pt
# 使用 yolov8n.yaml 模型定义文件重新训练, 并使用预训练的 yolov8n.pt 作为初始权重值, 训练耗时较长.
# pretrained参数可以设置成bool值, 也可以设置为一个pt文件
.\yolo task=detect mode=train data=coco8.yaml model=yolov8n.yaml pretrained=yolov8n.pt
# 使用 yolov8n.yaml 模型定义文件进行完全重新训练, 训练耗时最长.
.\yolo task=detect mode=train data=coco8.yaml model=yolov8n.yaml pretrained=False
顶层逻辑伪代码结构
下面是伪代码, 仅用于理解yolo 训练过程的顶层逻辑
import torch
from torch.utils.tensorboard import SummaryWriter
from yolov8 import Darknet
# 初始化模型、优化器等
model = Darknet()
optimizer = SGD(model.params)
writer = SummaryWriter('logs/yolov8')
for epoch in range(epochs):
## 每个epoch 的 train 过程
model.train() #模型切换到训练状态
for batch_idx, (images, targets) in enumerate(train_loader):
# 前向后向传播
pred = model(images)
loss = calc_loss(pred, targets)
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 记录指标
writer.add_scalar('Loss/train', loss, global_step)
global_step += 1
## 每个epoch 的 validate
model.eval() #模型切换到val状态
val_loss = 0
for images, targets in val_loader:
pred = model(images)
loss = calc_loss(pred, targets)
val_loss += loss
val_loss /= len(val_loader)
writer.add_scalar('Loss/val', val_loss, global_step)
## 保存最优模型
if val_loss < best_loss:
torch.save(model)
writer.close()