TensorFlow Models CVRL 实战指南:时空视频对比学习(video_ssl)的训练与线性评估
【免费下载链接】modelsModels and examples built with TensorFlow项目地址: https://gitcode.com/GitHub_Trending/mode/models
本篇基于 TensorFlow 官方模型仓库中official/projects/video_ssl项目的 README 及其配套源码展开,完整讲解 CVRL(Contrastive Video Representation Learning,对比视频表示学习)方法在 TF2 中的落地实现:从时空对比损失、双视角视频增强数据管线,到 TPU 上的预训练配置与冻结骨干的线性评估流程。读完本文,你可以理解 CVRL 的自监督训练机制如何在未标注视频上学习时空表征,并能基于仓库中的实验配置复现 Kinetics-400/600 上的预训练与 top-1 线性分类评估。
一、方法概述:什么是 CVRL
CVRL 是一种自监督时空视频表示学习方法,其核心思想是:从同一段短视频中抽取两个经过不同数据增强的片段(view),用对比损失将它们在嵌入空间中拉近,同时把来自不同视频的片段推远。这样无需任何人工标注,模型仅凭"两个片段是否来自同一段视频"这一弱监督信号,即可学到可迁移的时空视觉表征。据项目 README 描述,该方法显著缩小了无监督与有监督视频表示学习之间的差距,并大幅收窄了与当时有监督 SOTA 的精度差。
该目录是 CVPR 2021 论文Spatiotemporal Contrastive Video Representation Learning(arXiv:2008.03800)的官方 TF2 实现,仓库同时提供论文中的实验结果图与预训练模型。项目的两个核心工作流分别是:
- 预训练(pretrain):在未标注 Kinetics 视频上用对比损失训练 R3D-50 骨干 + 投影头;
- 线性评估(linear eval):冻结预训练好的骨干,只训练一个线性分类头,用于度量所学表征的质量。
目录结构
| 文件 / 目录 | 作用 |
|---|---|
| train.py | 训练驱动入口,解析 exp_config 与 yaml 实验配置 |
| configs/video_ssl.py | 模型、损失、数据的配置定义与 4 个实验配置工厂 |
| configs/experiments/cvrl_pretrain_k600_200ep.yaml | Kinetics-600 对比预训练完整配置 |
| configs/experiments/cvrl_linear_eval_k600.yaml | Kinetics-600 线性评估完整配置 |
| modeling/video_ssl_model.py | VideoSSLModel:骨干 + MLP 投影头 |
| losses/losses.py | InfoNCE 对比损失与 TPU 跨副本拼接 |
| dataloaders/video_ssl_input.py | 双视角视频采样、裁剪与 SSL 增强 |
| ops/video_ssl_preprocess_ops.py | 时序一致的 3D 颜色抖动、高斯模糊、Solarization |
| tasks/pretrain.py | 预训练任务:前向、对比损失、训练步 |
| tasks/linear_eval.py | 线性评估任务:加载骨干 checkpoint 并冻结 |
| video_ssl.ipynb | 使用预训练模型提取特征的示例 Notebook |
二、模型架构:骨干网络之上的对比投影头
预训练模型由 configs/video_ssl.py 中的VideoSSLModel配置类驱动,关键字段如下:
| 配置项 | 默认值 | 含义 |
|---|---|---|
normalize_feature | False | 是否对骨干池化后的特征做 L2 归一化(线性评估时置True) |
hidden_dim | 2048 | MLP 隐层宽度 |
hidden_layer_num | 3 | MLP 隐层数量(预训练为 3 层,线性评估置0,即退化为单层投影) |
projection_dim | 128 | 投影头输出维度(对比嵌入维度) |
hidden_norm_activation | NormActivation(use_sync_bn=False, norm_momentum=0.997, norm_epsilon=1e-5) | MLP 中的归一化 + 激活配置,TPU 上通常开启 SyncBN |
模型构建实现见 modeling/video_ssl_model.py,VideoSSLModel的前向结构是:
- 3D 骨干(R3D-50):通过
backbones.factory.build_backbone依据backbone配置构建(对应 yaml 中的resnet_3d块,1x 变体即各阶段时序卷积核全为 1,见 configs/experiments/cvrl_pretrain_k600_200ep.yaml 中block_specs); - GlobalAveragePooling3D得到视频级特征;
- 可选的
tf.nn.l2_normalize(normalize_feature=True时启用,见 video_ssl_model.py); - MLP 隐层:
Dense(hidden_dim) → BatchNormalization/SyncBatchNormalization → 激活共hidden_layer_num组(TPU 多副本场景使用SyncBatchNormalization保证归一化统计跨副本一致); - 投影头:
Dense(projection_dim)输出最终用于对比学习的 128 维嵌入。
一个值得注意的实现细节:构建函数build_video_ssl_pretrain_model中有一条断言(video_ssl_model.py),要求 MLP 中归一化层的use_sync_bn与骨干保持一致,避免骨干与投影头的归一化行为在分布式训练下分叉。
模型通过工厂注册为video_ssl_model(video_ssl_model.py),因此在 yaml 中只需写model_type: 'video_ssl_model'即可由框架实例化。
三、对比损失:温度化 InfoNCE 与 TPU 跨副本负样本
损失定义在 losses/losses.py 的contrastive_loss中,是标准的对称 InfoNCE 实现,核心逻辑:
- 正负样本划分:输入
hidden在 batch 维上按 2 分割为hidden1/hidden2(两个视角)。若normalize_hidden=True,先做 L2 归一化(配置默认开启,见 configs/video_ssl.py 的SSLLosses)。 - 温度缩放:所有相似度矩阵除以温度
temperature(配置默认0.1)。 - 对称损失:
loss_a = HCE(labels, [logits_ab, logits_aa]),loss_b = HCE(labels, [logits_ba, logits_bb]),总损失为两者之和。对角线(自身)位置用-1e9掩码排除。 - TPU 跨副本扩展负样本池:多副本时通过
tpu_cross_replica_concat(losses.py)用tf.scatter_nd + all_reduce(SUM)技巧把各 TPU core 的嵌入拼成一个全局大 batch,使每个样本拥有2 × global_batch_size个候选对比对象。这也是为什么配置中全局 batch size 直接决定负样本规模。 - L2 权重衰减:对所有名字含
kernel的可训练变量加weight_decay * Σ l2_loss,默认1e-6。
损失函数还顺带返回监控指标contrast_acc(对比分类准确率)与contrast_entropy,预训练任务中注册为流式指标(tasks/pretrain.py),训练时可在日志中直接观察对比学习收敛情况。
损失由VideoSSLPretrainTask.build_losses接入(tasks/pretrain.py),train_step中先取tf.distribute.get_strategy().num_replicas_in_sync作为副本数传入,混合精度下还会把模型输出统一 cast 到 float32 再算损失(tasks/pretrain.py),保证 InfoNCE 数值稳定。
四、数据管线:双视角采样与时空一致增强
自监督预训练的数据增强是方法的关键,实现集中在 dataloaders/video_ssl_input.py 的_process_image中,训练路径(is_training=True, is_ssl=True)依次为:
- 双片段时序采样:调用
video_ssl_preprocess_ops.sample_ssl_sequence,从"线性递减分布"中采出同一段视频中的两个子片段(而非完全随机),保证两个 view 保留一定时序重叠,这是"时空"对比学习的核心设计之一; - 空间增强:两个 view 各自独立做随机尺度裁剪缩放(aspect ratio 均匀采自
[0.5, 2.0],面积采自[0.3, 1.0],random_crop_resize)与随机左右翻转; - 时序一致的颜色抖动:
random_color_jitter_3d对整个片段共享同一组亮度/对比度/饱和度/色相参数,确保增强在时间维度上保持一致(video_ssl_preprocess_ops.py); - 时序一致的高斯模糊 + Solarization:两个 view 分别以不同概率参数做
random_blur,仅第二个 view 额外做random_solarization; - 归一化:像素值统一映射回
[0, 1](zero_centering_image=True时映射到[-1, 1])。
后处理_postprocess_image(video_ssl_input.py)会把两个 view 从时间维拆到 batch 维(batch 变为 2×),这与contrastive_loss中tf.split(hidden, 2, axis=0)的约定严格对应。
评估路径则走经典的 10 clip × 3 crop 增强:num_test_clips=10(线性间隔采样 10 个片段)、num_test_crops=3(三分裁剪),同样在 batch 维展开。
五、配置与预训练流程
实验注册
configs/video_ssl.py 通过exp_factory.register_config_factory注册了 4 个实验:
| 实验名 | 数据集 | 用途 |
|---|---|---|
video_ssl_pretrain_kinetics400 | Kinetics-400 | 对比预训练(16 帧 × 224²,temporal_stride=2) |
video_ssl_pretrain_kinetics600 | Kinetics-600 | 对比预训练 |
video_ssl_linear_eval_kinetics400 | Kinetics-400 | 线性评估(训练 32 帧 × 224²,验证 32 帧 × 256²,normalize_feature=True、hidden_layer_num=0、projection_dim=600) |
video_ssl_linear_eval_kinetics600 | Kinetics-600 | 线性评估 |
其中线性评估配置还显式设置了num_test_clips=10、num_test_crops=3(configs/video_ssl.py),与论文中的测试增强协议一致。
预训练配置详解
cvrl_pretrain_k600_200ep.yaml 给出了一份面向 TPU 的完整可运行配置,关键参数:
runtime: distribution_strategy: 'tpu' mixed_precision_dtype: 'bfloat16' task: model: dropout_rate: 1.0 # 配合 L2 衰减使用 norm_activation: use_sync_bn: true # TPU 下骨干启用同步 BN hidden_norm_activation: use_sync_bn: true backbone: resnet_3d: model_id: 50 stem_conv_temporal_kernel_size: 5 stem_conv_temporal_stride: 2 stem_pool_temporal_stride: 1 # 1x 变体:所有残差块 temporal_kernel_sizes 全为 1 train_data: name: kinetics600 feature_shape: (16, 224, 224, 3) # 每 clip 16 帧(双 view 共 32 帧) temporal_stride: 2 global_batch_size: 1024 # 负样本池 = 2048 个嵌入 dtype: 'bfloat16' losses: l2_weight_decay: 0.000001 trainer: optimizer_config: learning_rate: cosine: initial_learning_rate: 0.32 decay_steps: 71488 optimizer: sgd: nesterov: false warmup: linear: warmup_steps: 1787 train_steps: 71488 steps_per_loop: 100要点解读:
feature_shape的时间维为 16,是因为数据管线会把每视频采出的两个 16 帧 clip 沿时间维拼成 32 帧输入,再在后处理中拆到 batch 维;global_batch_size=1024意味着对比损失中每个样本面对约 2048 个候选(2 × global_batch_size),是 CVRL 大负样本策略的直接体现;- 学习率 0.32 + cosine 衰减 + 线性 warmup 1787 步,对应 200 epoch 的 K600 训练量(
train_steps=71488),整体遵循"大 batch + 大学习率"的视觉对比学习惯例。
启动命令
训练入口 train.py 基于官方official.core.train_lib框架:main先parse_configuration(FLAGS)解析出实验参数,task_factory.get_task按task配置类取出对应任务,最后run_experiment驱动训练。按仓库中其他项目的通用调用方式,预训练命令形如:
python official/projects/video_ssl/train.py \ --exp_config=video_ssl_pretrain_kinetics600 \ --config_file=official/projects/video_ssl/configs/experiments/cvrl_pretrain_k600_200ep.yaml \ --model_dir=/tmp/cvrl_pretrain_k600 \ --mode=train其中--exp_config指定上面注册的实验名,--config_file在其基础上覆盖超参数(如学习率、步数、batch size)。train_and_eval模式下入口会断言训练/验证的feature_shape一致(train.py),因此对比预训练通常使用纯train模式。
六、线性评估:只训练线性头的表征测试
线性评估是检验自监督表征质量的标准协议:冻结骨干、仅训练其上的线性分类层。仓库实现见 tasks/linear_eval.py:
- 加载骨干权重:
initialize从init_checkpoint恢复,且仅当init_checkpoint_modules == 'backbone'时用tf.train.Checkpoint(backbone=model.backbone)恢复骨干参数(linear_eval.py)。因此 yaml 中需配置:
task: init_checkpoint: 'r3d_1x_k600_800ep_backbone-1' init_checkpoint_modules: 'backbone' model: dropout_rate: 1.0 backbone: resnet_3d: # 与预训练完全相同的 R3D-50 1x 结构 train_data: feature_shape: (32, 224, 224, 3) # 评估阶段 32 帧 losses: l2_weight_decay: 0.0 # 线性头不使用权重衰减- 冻结骨干:
train_step每次迭代开头执行model.backbone.trainable = False,梯度只流向线性头(linear_eval.py); - 测试增强:验证集使用
num_test_clips=10、num_test_crops=3,即 10 个片段 × 3 个裁剪的 test-time 增强,验证 batch 64、关闭drop_remainder。
线性评估实验配置 cvrl_linear_eval_k600.yaml 中,线性头学习率高达32.0(cosine 衰减 35744 步),这是线性探针的典型做法——大学习率、短训练;validation_interval: 100用于周期性记录 top-1 精度。运行方式与预训练相同,将--exp_config换为video_ssl_linear_eval_kinetics600即可。
七、官方预训练检查点与结果
README 提供了在 Kinetics-400/600 无标签视频上从零(随机初始化)训练的 R3D-50 (1x) 检查点,以及论文使用的 ImageNet 2D ResNet-50 膨胀基线("ImageNet inflated",架构同为 R3D-50,权重由 ImageNet 预训练的 2D ResNet-50 膨胀而来)。各检查点对应的 K400/K600 top-1 线性评估精度如下(参数 31.7M):
| 模型 | 数据集 | Epochs | K400 Linear Eval. | K600 Linear Eval. |
|---|---|---|---|---|
| R3D-50 (1x) | ImageNet(膨胀基线) | - | 53.5% | 54.7% |
| R3D-50 (1x) | Kinetics-400 | 200 | 63.8% | - |
| R3D-50 (1x) | Kinetics-400 | 800 | 66.1% | - |
| R3D-50 (1x) | Kinetics-600 | 800 | 68.5% | 70.4% |
检查点为约 127 MB 的 tar.gz 文件(如r3d_1x_k600_800ep.tar.gz),下载链接列于 README 的 "Pre-trained Model Checkpoints" 表格中。解压后在评估 yaml 的init_checkpoint中填入对应路径即可复用。Kinetics-600 top-1 线性精度的对比图(CVRL 与有监督 SOTA 的差距收敛情况)同样见 README 中 "Experimental Results" 一节。
此外,video_ssl.ipynb 展示了如何用加载好的预训练模型对视频提取特征,适合快速上手特征复用场景。
八、小结:复现要点清单
- 双视角来自同一视频:两个 clip 由
sample_ssl_sequence从线性递减分布采样,空间增强独立、时间增强(颜色抖动/模糊)片段级一致; - 对比损失参数:温度 0.1、嵌入 L2 归一化、对称 InfoNCE、TPU 跨副本拼接负样本池,
global_batch_size=1024决定负样本规模; - 预训练配置:R3D-50 1x + 3 层 2048 隐层 MLP + 128 维投影头,SGD 0.32 大学习率 + cosine + warmup,bfloat16 混合精度;
- 线性评估协议:仅恢复并冻结骨干(
init_checkpoint_modules: 'backbone'),线性头大学习率短训练,验证用 10 clip × 3 crop; - 所有路径均可在本仓库内对照:配置在 official/projects/video_ssl/configs,实现在 modeling / losses / dataloaders / tasks。
论文引用信息(来自 README):
@inproceedings{qian2021spatiotemporal, title={Spatiotemporal contrastive video representation learning}, author={Qian, Rui and Meng, Tianjian and Gong, Boqing and Yang, Ming-Hsuan and Wang, Huisheng and Belongie, Serge and Cui, Yin}, booktitle={CVPR}, year={2021} }【免费下载链接】modelsModels and examples built with TensorFlow项目地址: https://gitcode.com/GitHub_Trending/mode/models
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考