☰
MMAction2 模型微调实战指南:从 Kinetics-400 预训练模型迁移到自定义数据集
2026/10/13 6:02:52 网站建设 项目流程
  • 人工智能
  • 计算机视觉
  • 深度学习

【免费下载链接】mmaction2

OpenMMLab's Next Generation Video Understanding Toolbox and Benchmark

项目地址:https://gitcode.com/gh_mirrors/mm/mmaction2
点击查看免费下载

本篇指南围绕 MMAction2 的模型微调(Finetuning)主题展开,完整讲解如何复用仓库中基于大型数据集(如 Kinetics-400)训练好的视频理解模型,通过修改配置将其迁移到 UCF101 等新数据集上,以获得更好的下游任务性能。读完本文,你将掌握模板配置的选择与复制、分类头类别数修改、数据集路径与注释文件配置、训练计划(学习率与周期)调整、预训练权重加载以及单卡/多卡训练命令的完整实战方案。

微调的整体流程

在 MMAction2 中,将一个模型微调到新数据集上通常分为两步:

  1. 为新数据集添加支持:包括准备数据文件和注释文件,参见 准备数据集 与 自定义数据集。
  2. 修改配置文件:即本指南的核心内容。在配置系统中,针对"不存在的键"需要新增,针对"已存在的键"需要修改,二者缺一不可。

以官方提供的 TSN 模板配置tsn_imagenet-pretrained-r50_8xb32-1x1x3-100e_kinetics400-rgb.py(位于 configs/recognition/tsn/)为例,微调需要重点关注四个部分:分类头(Head)、数据集(Dataset)、训练计划(Training Schedule)和预训练模型加载(load_from)。

选择模板配置

首先把模板配置复制到相同目录下并重命名。本例将其改名为tsn_ucf101.py,作为 UCF101 微调的基础:

cp configs/recognition/tsn/tsn_imagenet-pretrained-r50_8xb32-1x1x3-100e_kinetics400-rgb.py \ configs/recognition/tsn/tsn_ucf101.py

选择模板时,可以从配置文件命名读懂其全部信息。按照 配置系统教程 的命名约定{algorithm info}_{module info}_{training info}_{data info}.py:

  • tsn:算法为 TSN(Temporal Segment Networks);
  • imagenet-pretrained:主干网络使用 ImageNet 预训练权重;
  • r50:主干为 ResNet-50;
  • 8xb32:8 卡 × 每卡 32 个样本;
  • 1x1x3:采样参数clip_len x frame_interval x num_clips,即每段取 1 帧、间隔 1 帧、共 3 个片段;
  • 100e:训练 100 个 epoch;
  • kinetics400-rgb:训练数据为 Kinetics-400 的 RGB 模态。

_base_继承机制决定了模板配置由三类基础配置组合而成,详见 configs/base/models/tsn_r50.py、configs/base/schedules/sgd_100e.py 与 configs/base/default_runtime.py。

修改分类头(Modify Head)

新数据集的类别数通常与预训练数据集不同,因此必须修改cls_head中的num_classes。预训练模型的权重除最后一层预测层(fc_cls)外都会被复用,所以修改类别数是安全且必要的。

本例中 UCF101 共有 101 个类别,需要把 Kinetics-400 的 400 改为 101:

# model settings model = dict( cls_head=dict( type='TSNHead', num_classes=101 # change from 400 to 101 ))

从源码角度看,TSNHead 在__init__中通过nn.Linear(self.in_channels, self.num_classes)构建fc_cls全连接层,输出维度由num_classes决定;其init_weights仅对fc_cls做标准差为init_std(默认 0.01)的正态初始化,其余层复用预训练权重。因此改动num_classes只会重建输出层,不会破坏主干特征提取能力。

修改类别数时需同步确认几个关联配置:

  • model.cls_head.in_channels:主干输出的通道数(ResNet-50 为 2048),一般无需改动;
  • model.cls_head.average_clips:测试时多个 clip 预测结果的聚合方式(默认'prob',按概率平均);
  • model.cls_head.consensus:TSN 的片段级共识模块(默认dict(type='AvgConsensus', dim=1))。

修改数据集(Modify Dataset)

MMAction2 原生支持 UCF101、Kinetics-400、Moments in Time、Multi-Moments in Time、THUMOS14、Something-Something V1&V2、ActivityNet 等数据集。对于仓库未内置的新数据集,需要参考 准备数据集 和 自定义数据集 将其整理为已有数据集类型支持的格式。

UCF101 已被多种数据集类型支持,这里直接使用VideoDataset。需要修改以下四个关键键:

# dataset settings dataset_type = 'VideoDataset' data_root = 'data/ucf101/videos_train/' data_root_val = 'data/ucf101/videos_val/' ann_file_train = 'data/ucf101/ucf101_train_list.txt' ann_file_val = 'data/ucf101/ucf101_val_list.txt'

从 VideoDataset 实现 可以确认注释文件的格式:一个纯文本文件,每行表示一个样本视频,由相对路径和类别标签以空格分隔:

some/path/000.mp4 1 some/path/001.mp4 1 some/path/002.mp4 2

load_data_list会按data_prefix(即data_root)拼接相对路径,因此注释文件里应写相对路径而非绝对路径;同时支持多标签(multi_class=True时每行可含多个标签)和推理用无标签行(标签自动置为 -1)。数据集准备细节(含tools/data/下各数据集的 shell 脚本)请参考 准备数据集。

数据加载流程由train_pipeline/val_pipeline/test_pipeline定义。模板中训练阶段使用DecordInit+SampleFrames(clip_len=1, frame_interval=1, num_clips=3)+MultiScaleCrop(输入 224,尺度(1, 0.875, 0.75, 0.66))+Flip(概率 0.5)做数据增强;验证阶段用CenterCrop;测试阶段用num_clips=25+TenCrop做多视角评估。若新数据集数据量较小,可适当降低增强强度以避免过拟合。

修改训练计划(Modify Training Schedule)

微调通常需要更小的学习率和更少的训练周期,以防破坏预训练特征。模板的 100 epoch 计划位于 configs/base/schedules/sgd_100e.py,微调时修改如下:

train_cfg = dict( type='EpochBasedTrainLoop', max_epochs=50, # change from 100 to 50 val_begin=1, val_interval=1) val_cfg = dict(type='ValLoop') test_cfg = dict(type='TestLoop') # learning policy param_scheduler = [ dict( type='MultiStepLR', begin=0, end=50, # change from 100 to 50 by_epoch=True, milestones=[20, 40], # change milestones gamma=0.1) ] # optimizer optim_wrapper = dict( optimizer=dict( type='SGD', lr=0.005, # change from 0.01 to 0.005 momentum=0.9, weight_decay=0.0001), clip_grad=dict(max_norm=40, norm_type=2))

各关键参数的调整要点:

  • max_epochs:从 100 缩减到 50(可按新数据集规模进一步调整);
  • milestones:学习率衰减节点,需与max_epochs匹配,本例从[40, 80]改为[20, 40],即学习率在 20/40 epoch 时各乘以gamma=0.1;
  • lr:初始学习率从 0.01 降为 0.005。若新数据集较小,可进一步降到 0.001 量级;
  • clip_grad:梯度裁剪(max_norm=40, norm_type=2)建议保留,可防止微调初期梯度爆炸。

另外配置末尾的auto_scale_lr(enable=False, base_batch_size=256)表示默认不自动缩放学习率;base_batch_size = 8 GPUs × 32 samples per GPU。若实际使用的 GPU 数或 batch size 不同,可通过--auto-scale-lr参数开启自动按批量大小缩放学习率。

使用预训练模型(Use Pre-Trained Model)

要让预训练权重加载到整个网络,在配置中设置load_from。基础配置 configs/base/default_runtime.py 中默认load_from = None,得益于配置系统的继承设计,子配置只需直接覆盖该键即可:

# use the pre-trained model for the whole TSN network load_from = 'https://download.openmmlab.com/mmaction/v1.0/recognition/tsn/tsn_imagenet-pretrained-r50_8xb32-1x1x3-100e_kinetics400-rgb/tsn_imagenet-pretrained-r50_8xb32-1x1x3-100e_kinetics400-rgb_20220906-cd10898e.pth' # model path can be found in model zoo

预训练权重下载地址可以从对应模型的 metafile.yml 的Weights字段获取,也可以从 Model Zoo 页面查找。加载时的注意事项:

  • load_from只负责加载权重,不负责恢复训练状态;若希望断点续训,应使用resume或tools/train.py的--resume参数;
  • 预训练模型默认加载整个网络的权重;若只希望冻结主干或加载部分层,可通过tools/train.py的--cfg-options或自定义init_cfg实现;
  • 类别数不一致时,fc_cls层的权重因输出维度不同会自然跳过加载,这正是先修改num_classes再加载预训练权重的顺序要求。

完整的微调配置文件

将上述修改汇总,即可得到一份完整的微调配置。下面给出完整展开版(便于理解全部细节):

_base_ = [ '../../_base_/models/tsn_r50.py', '../../_base_/schedules/sgd_100e.py', '../../_base_/default_runtime.py' ] # model settings model = dict( cls_head=dict( type='TSNHead', num_classes=101 # change from 400 to 101 )) # dataset settings dataset_type = 'VideoDataset' data_root = 'data/ucf101/videos_train/' data_root_val = 'data/ucf101/videos_val/' ann_file_train = 'data/ucf101/ucf101_train_list.txt' ann_file_val = 'data/ucf101/ucf101_val_list.txt' file_client_args = dict(io_backend='disk') train_pipeline = [ dict(type='DecordInit', **file_client_args), dict(type='SampleFrames', clip_len=1, frame_interval=1, num_clips=3), dict(type='DecordDecode'), dict(type='Resize', scale=(-1, 256)), dict( type='MultiScaleCrop', input_size=224, scales=(1, 0.875, 0.75, 0.66), random_crop=False, max_wh_scale_gap=1), dict(type='Resize', scale=(224, 224), keep_ratio=False), dict(type='Flip', flip_ratio=0.5), dict(type='FormatShape', input_format='NCHW'), dict(type='PackActionInputs') ] val_pipeline = [ dict(type='DecordInit', **file_client_args), dict( type='SampleFrames', clip_len=1, frame_interval=1, num_clips=3, test_mode=True), dict(type='DecordDecode'), dict(type='Resize', scale=(-1, 256)), dict(type='CenterCrop', crop_size=224), dict(type='FormatShape', input_format='NCHW'), dict(type='PackActionInputs') ] test_pipeline = [ dict(type='DecordInit', **file_client_args), dict( type='SampleFrames', clip_len=1, frame_interval=1, num_clips=25, test_mode=True), dict(type='DecordDecode'), dict(type='Resize', scale=(-1, 256)), dict(type='TenCrop', crop_size=224), dict(type='FormatShape', input_format='NCHW'), dict(type='PackActionInputs') ] train_dataloader = dict( batch_size=32, num_workers=8, persistent_workers=True, sampler=dict(type='DefaultSampler', shuffle=True), dataset=dict( type=dataset_type, ann_file=ann_file_train, data_prefix=dict(video=data_root), pipeline=train_pipeline)) val_dataloader = dict( batch_size=32, num_workers=8, persistent_workers=True, sampler=dict(type='DefaultSampler', shuffle=False), dataset=dict( type=dataset_type, ann_file=ann_file_val, data_prefix=dict(video=data_root_val), pipeline=val_pipeline, test_mode=True)) test_dataloader = dict( batch_size=1, num_workers=8, persistent_workers=True, sampler=dict(type='DefaultSampler', shuffle=False), dataset=dict( type=dataset_type, ann_file=ann_file_val, data_prefix=dict(video=data_root_val), pipeline=test_pipeline, test_mode=True)) train_cfg = dict( type='EpochBasedTrainLoop', max_epochs=50, # change from 100 to 50 val_begin=1, val_interval=1) val_cfg = dict(type='ValLoop') test_cfg = dict(type='TestLoop') # learning policy param_scheduler = [ dict( type='MultiStepLR', begin=0, end=50, # change from 100 to 50 by_epoch=True, milestones=[20, 40], # change milestones gamma=0.1) ] # optimizer optim_wrapper = dict( optimizer=dict( type='SGD', lr=0.005, # change from 0.01 to 0.005 momentum=0.9, weight_decay=0.0001), clip_grad=dict(max_norm=40, norm_type=2)) val_evaluator = dict(type='AccMetric') test_evaluator = val_evaluator default_hooks = dict(checkpoint=dict(interval=3, max_keep_ckpts=3)) # Default setting for scaling LR automatically # - `enable` means enable scaling LR automatically # or not by default. # - `base_batch_size` = (8 GPUs) x (32 samples per GPU). auto_scale_lr = dict(enable=False, base_batch_size=256) # use the pre-trained model for the whole TSN network load_from = 'https://download.openmmlab.com/mmaction/v1.0/recognition/tsn/tsn_imagenet-pretrained-r50_8xb32-1x1x3-100e_kinetics400-rgb/tsn_imagenet-pretrained-r50_8xb32-1x1x3-100e_kinetics400-rgb_20220906-cd10898e.pth'

更简洁的继承写法

更推荐的做法是直接继承模板配置,只覆盖需要修改的键。注意自定义配置必须与configs/recognition/tsn/tsn_imagenet-pretrained-r50_8xb32-1x1x3-100e_kinetics400-rgb.py放在同一目录下,_base_中直接写模板文件名即可:

_base_ = [ 'tsn_imagenet-pretrained-r50_8xb32-1x1x3-100e_kinetics400-rgb.py' # inherit template config ] # model settings model = dict( cls_head=dict( type='TSNHead', num_classes=101)) # change from 400 to 101 # dataset settings dataset_type = 'VideoDataset' data_root = 'data/ucf101/videos_train/' data_root_val = 'data/ucf101/videos_val/' ann_file_train = 'data/ucf101/ucf101_train_list.txt' ann_file_val = 'data/ucf101/ucf101_val_list.txt' train_dataloader = dict( dataset=dict( ann_file=ann_file_train, data_prefix=dict(video=data_root))) val_dataloader = dict( dataset=dict( ann_file=ann_file_val, data_prefix=dict(video=data_root_val))) test_dataloader = dict( dataset=dict( ann_file=ann_file_val, data_prefix=dict(video=data_root_val))) train_cfg = dict( type='EpochBasedTrainLoop', max_epochs=50, # change from 100 to 50 val_begin=1, val_interval=1) val_cfg = dict(type='ValLoop') test_cfg = dict(type='TestLoop') param_scheduler = [ dict( type='MultiStepLR', begin=0, end=50, # change from 100 to 50 by_epoch=True, milestones=[20, 40], # change milestones gamma=0.1) ] optim_wrapper = dict( optimizer=dict( type='SGD', lr=0.005, # change from 0.01 to 0.005 momentum=0.9, weight_decay=0.0001), clip_grad=dict(max_norm=40, norm_type=2)) # use the pre-trained model for the whole TSN network load_from = 'https://download.openmmlab.com/mmaction/v1.0/recognition/tsn/tsn_imagenet-pretrained-r50_8xb32-1x1x3-100e_kinetics400-rgb/tsn_imagenet-pretrained-r50_8xb32-1x1x3-100e_kinetics400-rgb_20220906-cd10898e.pth'

这种继承写法大幅减少重复代码,同时天然继承了模板中经过验证的 pipeline、dataloader、evaluator 与 hooks 配置,是官方推荐实践。关于配置继承层级、--cfg-options运行时改参等更多细节,可阅读 配置系统教程。

启动训练

使用 tools/train.py 即可开始微调:

python tools/train.py ${CONFIG_FILE} [optional arguments]

确定性训练示例(固定随机种子并开启 CUDNN 确定性):

python tools/train.py configs/recognition/tsn/tsn_ucf101.py \ --seed=0 --deterministic

tools/train.py支持的常用参数(详见 tools/train.py 的parse_args):

  • --work-dir:指定日志和模型保存目录(默认为./work_dirs/{配置文件文件名});
  • --resume:断点续训。指定 checkpoint 路径则从该权重恢复;传auto则自动从 work_dir 中最新的 checkpoint 恢复;
  • --amp:开启自动混合精度(AMP)训练;
  • --no-validate:训练期间不执行验证;
  • --auto-scale-lr:根据实际 batch size 自动缩放学习率;
  • --seed:设置随机种子;--deterministic:设置 CUDNN 确定性选项;
  • --cfg-options:在命令行就地覆盖配置键,例如--cfg-options model.backbone.norm_eval=False,列表类型值需带引号(如model.data_preprocessor.mean="[128,128,128]");
  • --launcher:分布式启动方式(none/pytorch/slurm/mpi)。

多卡训练使用 tools/dist_train.sh:

bash tools/dist_train.sh ${CONFIG_FILE} ${GPUS} [optional arguments]

例如 8 卡训练:

bash tools/dist_train.sh configs/recognition/tsn/tsn_ucf101.py 8 \ --seed=0 --deterministic

测试与评估

训练完成后,使用 tools/test.py 评估微调模型的精度:

python tools/test.py ${CONFIG_FILE} ${CHECKPOINT} [optional arguments]

例如:

python tools/test.py configs/recognition/tsn/tsn_ucf101.py \ ./work_dirs/tsn_ucf101/best_acc1_epoch_50.pth

常用参数包括--dump(将预测结果保存为 pkl 供离线评估)、--show/--show-dir(可视化预测结果)、--cfg-options与--launcher。评估指标由配置中的val_evaluator = dict(type='AccMetric')决定,输出 Top-1 / Top-5 准确率。训练与测试的完整说明见 训练与测试教程。

微调实践要点小结

  • 先改头,再加载权重:num_classes修改会在构建模型时重建fc_cls输出层,后续加载预训练权重时该层因维度不匹配被自动跳过,无需手动处理。
  • 学习率不宜过大:微调一般将学习率降为原计划的 1/2 到 1/10(如 0.01 → 0.005 或更低),并配合梯度裁剪防止震荡。
  • epoch 数与数据量匹配:新数据集越小,训练周期越短;同时适当调整MultiStepLR的milestones,保证学习率衰减仍发生在训练后半程。
  • 优先使用继承式配置:将自定义配置与模板放在同一目录并只覆盖差异键,可以最大化复用已验证的管线,降低出错概率。
  • 数据格式先对齐:确保注释文件中的路径为相对路径、与data_root拼接后真实存在,标签从 0 开始连续编号;VideoDataset每行为视频相对路径 标签,RawFrameDataset每行为帧目录 总帧数 标签。
  • 人工智能
  • 计算机视觉
  • 深度学习

【免费下载链接】mmaction2

OpenMMLab's Next Generation Video Understanding Toolbox and Benchmark

项目地址:https://gitcode.com/gh_mirrors/mm/mmaction2
点击查看免费下载

相关推荐

上一篇:解决OBS Studio浏览器源远程调试难题:从卡顿到丝滑的实战指南
下一篇:gh_mirrors/images5/images多语言支持详解:Java、Go、Rust等开发环境配置指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询