恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
视频动作识别实战:SlowFast双路径时序建模与PyTorch实现
首页
资讯中心
/
视频动作识别实战:SlowFast双路径时序建模与PyTorch实现
视频动作识别实战:SlowFast双路径时序建模与PyTorch实现
发布时间:2026/9/9 20:34:33
视频动作识别一直是计算机视觉里“看着简单做起来很棘手”的方向。单张图像的任务分类、检测、分割已经被卷积网络吃得很透可一旦把时间维度加进来问题就变了模型既要看懂“画面里有什么”又要捕捉“物体怎么动”还要在两者之间做平衡。之前做相关项目时我试过直接堆 3D CNN也试过双流网络要么计算量爆炸要么识别精度上不去。后面系统读了 SlowFast 这篇论文并复现工程后才算把“慢看语义、快看运动”这套思路真正捋顺。本文会用一整套闭环的方式把视频时序建模里的核心概念、SlowFast 的网络设计原理、PyTorch 手写实现、训练实验要点和工业落地环节完整拆开来讲。无论你是想入门视频理解的新手还是已经有项目经验、准备把模型部署到线上的工程师这篇文章都可以当作一份系统化的参考笔记。1. 视频时序建模与 SlowFast 的前世今生1.1 视频动作识别到底难在哪里视频理解任务和图像理解有一个本质区别图像只有空间信息而视频在空间之外还有一个时间轴。动作识别Action Recognition就是给定一段视频让模型输出这段视频属于哪个动作类别比如“跑步”“踢足球”“倒水”“弹吉他”。很多人第一反应是不就把每帧图像送给 CNN 吗帧间再做个平均不就行了但实际跑一遍会发现两个问题空间语义和运动信息不在同一个时间尺度上。“跑步”这个动作人形外观变化很慢但四肢摆动频率很高。如果只看低频采样模型很容易把“慢跑”和“走路”搞混如果只看高频运动特征又容易忽略“画面里是一个人在跑步”这个关键上下文。3D 卷积网络的参数和计算量随帧数线性增长。简单地把所有帧堆到一起送进 3D CNNGPU 显存承受不住而且冗余帧会让模型把大量计算浪费在“没有变化的背景”上。所以视频时序建模的核心问题变成如何用可控的计算成本同时建模“慢变的空间语义”和“快变的运动信息”。1.2 早期方案的局限在 SlowFast 之前业界比较典型的方案有三类。第一类是双流网络Two-Stream Network一条支路输入 RGB 帧另一条支路输入光流图。光流能很好地描述运动但光流需要离线计算存储和预处理成本都很高而且光流本身又是一套独立的模型端到端训练比较麻烦。第二类是时域分段采样比如 TSNTemporal Segment Networks。思路是把视频均匀分段每段取一帧或几帧送入 2D CNN最后融合。这种方式计算量小但本质上没有显式建模帧间运动关系对短促动作和细微动作不敏感。第三类是基于 3D 卷积的 I3D它把 2D 卷积核扩展为 3D能同时捕获空间和时间特征效果比前两者好但参数和计算开销同样感人。SlowFast 的出发点就是针对上述痛点做取舍与其用一个大网络同时处理所有信息不如拆成两个支路各司其职再通过侧向连接融合。1.3 大脑视觉通路带来的灵感SlowFast 的命名和设计灵感来自灵长类视觉系统中的两条通路。视网膜神经节细胞中有两类与运动感知密切相关的细胞一类主要感知物体形状和静止语义放电频率较低但空间分辨率高另一类对运动敏感时间分辨率高但空间信息弱。人类视觉实际上就是靠这种“分工合作”来快速理解动态场景的。SlowFast 把这个生物机制转译为网络结构一个慢路径Slow Pathway以较低帧率提取空间语义一个快路径Fast Pathway以较高帧率捕捉运动变化。慢路径不追求时间分辨率所以可以用更深的网络快路径为了省计算把通道数设得很小。两者通过侧向连接交互最终融合出完整表征。2. 环境准备与项目结构2.1 运行环境说明本文的实验和示例代码都基于 PyTorch如果你要完整跑通训练流程推荐使用下面的环境组合操作系统Ubuntu 20.04 或更高版本Windows 10/11 也可以运行示例代码Python 版本3.8 以上推荐 3.10PyTorch2.0 或更新版本需要根据你的 CUDA 版本选择安装方式torchvision与 PyTorch 版本配套OpenCV-Python用于视频读取与帧采样FFmpeg用于视频解码OpenCV 的视频读取底层依赖它显卡训练建议单卡 24GB 显存以上如 RTX 3090/4090、A10/A100推理部署可以用小显存卡或仅 CPU 测试。如果本地没有 GPU也可以先用小模型和少量帧做纯 CPU 的代码调试验证逻辑无误后再放到 GPU 服务器上训练。版本必须根据实际项目调整本文示例以常见环境为主重点是展示完整的实现思路。2.2 安装依赖创建一个独立虚拟环境然后安装基础包python -m venv slowfast_env source slowfast_env/bin/activate pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install opencv-python pip install onnx onnxruntime pip install tqdm tensorboard其中onnx和onnxruntime用于后面的工业部署导出。如果只在研究阶段做小实验可以先不装这两项。2.3 示例项目结构为了保持代码清晰我推荐采用下面这种目录组织方式slowfast-tutorial/ ├── config.py # 超参数配置 ├── dataset.py # 视频读取与帧采样 ├── model.py # SlowFast 网络定义 ├── train.py # 训练脚本 ├── evaluate.py # 评估脚本 ├── export_onnx.py # 导出 ONNX 模型 ├── infer.py # 单视频推理 ├── checkpoints/ # 模型权重保存目录 └── data/ # 视频数据存放目录这样的结构适合中小型项目和教学代码。真正做大型项目时通常会用 PySlowFast 这类官方开源框架它是基于 Detectron2 搭建的目录复杂很多但核心设计仍然是这套慢快双路径架构。3. SlowFast 核心设计原理拆解3.1 慢路径与快路径的分工慢路径Slow Pathway以较大的时间步长tau采样视频帧。所谓时间步长可以理解为“每隔多少帧取一帧”。慢路径通常采样 8 帧左右且输入帧率低因此它拿到的是一组稀疏但空间分辨率稳定的帧序列网络可以放心做空间语义建模。快路径Fast Pathway以较小的时间步长采样。论文中的经典配置是慢路径每 16 帧取一帧共 8 帧快路径每 4 帧取一帧共 32 帧。两者在时间轴上覆盖相同的视频时长但快路径的帧数多出 4 倍于是能捕捉到手部快速晃动、物体突然偏移这类时序细节。为什么快路径的通道数要设得小作者的出发点是快速变化的运动信号在通道维上可能存在冗余不需要像慢路径那样用宽的通道去表达。经典配置中快路径的通道数是慢路径的 1/8这一设计大幅压缩了计算量让整体耗时增加只控制在 20% 左右。3.2 侧向连接的作用慢路径和快路径不是各自独立训练再简单拼接的它们之间必须交换信息。慢路径需要知道“目标正在以什么方式运动”快路径也需要知道“这个运动中的对象是什么”。侧向连接Lateral Connection就是完成这个融合任务的模块。从快路径向慢路径做融合时由于快路径的时间维长度是慢路径的alpha倍需要先做一次时间维下采样。论文中采用的方式是使用一个卷积核大小为(5, 1, 1)、步长为(alpha, 1, 1)的 3D 卷积把快路径特征的帧数压到和慢路径一致再通过残差连接加到慢路径特征上。这样一来慢路径在每一层都能感知到高频运动信息快路径反过来也能通过侧向连接接收慢路径传递的语义两者的信息在训练过程中互相矫正。3.3 为什么两路要覆盖同一时长这里有一个关键设计细节慢路径和快路径虽然帧率不同但覆盖的视频时长必须一致。如果慢路径覆盖 3 秒快路径覆盖 0.5 秒那么快路径只能感知到局部运动慢路径负责的语义窗口和快路径负责的运动窗口无法对齐侧向连接也无从融合。在代码实现上这意味着要对同一段视频的帧序列做两次采样一次按大步长取出稀疏帧一次按小步长取出密集帧然后分别喂给两个路径。3.4 与其他视频识别方案的对比用一个表格来对比常见方案会更直观方案输入形式运动建模方式主要问题2D CNN 平均池化单帧 / 多帧无显式建模丢失时序信息精度低双流网络RGB 光流光流表达运动光流计算慢存储开销大TSN分段采样帧最后融合缺乏帧间关系建模I3D连续视频帧3D 卷积核直接卷积计算量爆炸冗余计算多SlowFast慢路径稀疏帧 快路径密集帧双路径 侧向连接实现复杂度略高SlowFast 的优势在于用较低的计算增量换来了明显的精度提升并且运动信息的获取不需要额外计算光流整个网络可以端到端训练。4. PyTorch 手写一个简化版 SlowFast为了让原理落到代码上这一节我们从零搭建一个可运行的简化版 SlowFast。注意这里是教学实现去掉了原始论文中复杂的残差块变体和预训练权重细节但保留了最核心的“双路径 侧向连接”机制。4.1 视频帧采样器首先实现视频读取和时间维度双采样。这个模块相当于数据管道的第一环也是慢路径和快路径产生时间差的关键。# 文件路径dataset.py import cv2 import numpy as np def read_video_frames(video_path, height256, width256): cap cv2.VideoCapture(video_path) frames [] while True: ok, frame cap.read() if not ok: break frame cv2.resize(frame, (width, height)) frames.append(frame) cap.release() if len(frames) 0: raise ValueError(f无法读取视频文件: {video_path}) return np.stack(frames) def sample_slow_fast_frames(video_path, T_slow8, tau_slow16, T_fast32, tau_fast4, height256, width256): 从同一段视频中分别按不同时间步长采样慢路径和快路径输入。 frames read_video_frames(video_path, height, width) total len(frames) # 慢路径均匀采样 T_slow 帧 slow_idx np.linspace(0, total - 1, T_slow).astype(int) # 快路径均匀采样 T_fast 帧 fast_idx np.linspace(0, total - 1, T_fast).astype(int) slow_frames frames[slow_idx] # 形状: (T_slow, H, W, 3) fast_frames frames[fast_idx] # 形状: (T_fast, H, W, 3) # 转换为 PyTorch 习惯的 (C, T, H, W) 格式 slow_frames np.transpose(slow_frames, (3, 0, 1, 2)) fast_frames np.transpose(fast_frames, (3, 0, 1, 2)) return slow_frames.astype(np.float32), fast_frames.astype(np.float32)这里采用np.linspace做均匀采样优点是逻辑简单适合推理阶段。训练阶段一般会加上随机偏移避免模型过拟合到固定帧位置。4.2 网络主体结构接下来定义两个路径的基础模块。为了降低理解成本我们用简单的 3D 卷积块替代论文中的 Bottleneck 结构。# 文件路径model.py import torch import torch.nn as nn class Conv3dBlock(nn.Module): 一个简单的 3D 卷积 BN ReLU 模块。 def __init__(self, in_channels, out_channels, kernel_size(3, 3, 3), stride(1, 1, 1), padding(1, 1, 1)): super().__init__() self.conv nn.Conv3d(in_channels, out_channels, kernel_sizekernel_size, stridestride, paddingpadding) self.bn nn.BatchNorm3d(out_channels) self.relu nn.ReLU(inplaceTrue) def forward(self, x): return self.relu(self.bn(self.conv(x)))下面实现慢路径。它输入的是稀疏帧通道数较大网络深度也可以适当增加。class SlowPath(nn.Module): def __init__(self, in_channels3, base_channels64, num_classes400): super().__init__() self.stem Conv3dBlock(in_channels, base_channels, kernel_size(1, 7, 7), stride(1, 2, 2), padding(0, 3, 3)) self.block1 Conv3dBlock(base_channels, base_channels * 2, kernel_size(3, 3, 3), stride(1, 2, 2)) self.block2 Conv3dBlock(base_channels * 2, base_channels * 4, kernel_size(3, 3, 3), stride(1, 2, 2)) self.global_pool nn.AdaptiveAvgPool3d(1) self.fc nn.Linear(base_channels * 4, num_classes) def forward(self, x): # x: (B, 3, T_slow, H, W) x self.stem(x) x self.block1(x) x self.block2(x) x self.global_pool(x) x x.view(x.size(0), -1) return self.fc(x)快路径在结构上与慢路径类似但通道数更小同时时间维长度更大。为了让代码容易理解我们直接把两者定义在一个模型中。class FastPath(nn.Module): def __init__(self, in_channels3, base_channels8, num_classes400): super().__init__() # 通道数按 beta1/8 压缩例如慢路径 64 对应快路径 8 self.stem Conv3dBlock(in_channels, base_channels, kernel_size(5, 7, 7), stride(1, 2, 2), padding(2, 3, 3)) self.block1 Conv3dBlock(base_channels, base_channels * 2, kernel_size(3, 3, 3), stride(1, 2, 2)) self.block2 Conv3dBlock(base_channels * 2, base_channels * 4, kernel_size(3, 3, 3), stride(1, 2, 2)) self.global_pool nn.AdaptiveAvgPool3d(1) self.fc nn.Linear(base_channels * 4, num_classes) def forward(self, x): # x: (B, 3, T_fast, H, W) x self.stem(x) x self.block1(x) x self.block2(x) x self.global_pool(x) x x.view(x.size(0), -1) return self.fc(x)4.3 侧向连接与完整模型侧向连接最关键的是时间维对齐。由于快路径帧数是慢路径的alpha倍我们需要用步长为alpha的 3D 卷积把快路径特征的时间维压缩到与慢路径一致同时把通道数变换到慢路径的通道数。class LateralConnection(nn.Module): def __init__(self, fast_channels, slow_channels, alpha4): super().__init__() self.conv nn.Conv3d(fast_channels, slow_channels, kernel_size(5, 1, 1), stride(alpha, 1, 1), padding(2, 0, 0)) def forward(self, fast_feat, slow_feat): fused self.conv(fast_feat) if fused.shape ! slow_feat.shape: # 空间维可能不一致时做最简化的插值对齐 fused torch.nn.functional.interpolate( fused, sizeslow_feat.shape[2:], modetrilinear, align_cornersFalse) return slow_feat fused class SimpleSlowFast(nn.Module): def __init__(self, num_classes400, alpha4, beta8): super().__init__() # 假设慢路径基础通道为 64快路径是它的 1/8 slow_base 64 fast_base slow_base // beta self.slow_path SlowPath(in_channels3, base_channelsslow_base, num_classesnum_classes) self.fast_path FastPath(in_channels3, base_channelsfast_base, num_classesnum_classes) self.lateral1 LateralConnection(fast_channelsfast_base * 2, slow_channelsslow_base * 2, alphaalpha) self.lateral2 LateralConnection(fast_channelsfast_base * 4, slow_channelsslow_base * 4, alphaalpha) # 直接用简单加法完成最终分类融合 self.fc nn.Linear(num_classes * 2, num_classes) def forward(self, slow_x, fast_x): # 前向传播中为了演示我们分别从两个路径抽取部分中间特征来完成侧向连接。 slow_feat self.slow_path.stem(slow_x) slow_feat self.slow_path.block1(slow_feat) slow_feat self.slow_path.block2(slow_feat) fast_feat self.fast_path.stem(fast_x) fast_feat self.fast_path.block1(fast_feat) fast_feat self.lateral1(fast_feat, slow_feat) fast_feat self.fast_path.block2(fast_feat) slow_feat self.lateral2(fast_feat, slow_feat) slow_pool self.slow_path.global_pool(slow_feat).flatten(1) fast_pool self.fast_path.global_pool(fast_feat).flatten(1) logits torch.cat([slow_pool, fast_pool], dim1) return self.fc(logits)这段代码为了可读性做了简化真正的 PySlowFast 会在多个层级重复侧向连接并用残差结构逐步加深。本文示例已经保持了“慢路径 快路径 侧向连接”三个核心组件读者完全可以在此基础上添加更深的网络结构。4.4 推理示例完成定义后我们可以写一个最简单的推理脚本验证前向传播是否正常。# 文件路径infer_demo.py import torch from model import SimpleSlowFast model SimpleSlowFast(num_classes400) model.eval() # 模拟一个 batch 的输入 slow_input torch.randn(2, 3, 8, 256, 256) fast_input torch.randn(2, 3, 32, 256, 256) with torch.no_grad(): output model(slow_input, fast_input) print(输出形状:, output.shape) preds output.argmax(dim1) print(预测类别索引:, preds)运行后能看到输出形状为(2, 400)说明模型结构没有问题。实际训练时还需要把输入像素归一化到[0, 1]或按 ImageNet 统计值做标准化。5. 训练与实验要点5.1 数据集选择视频识别最常用的公开数据集是 Kinetics-400、Kinetics-600、Something-Something V2 和 AVA。Kinetics 系列包含日常生活动作类别多、场景杂Something-Something 更强调动作本身和物体交互关系对时间建模能力要求更高AVA 则面向时空动作检测需要输出动作发生的空间位置。如果只是学习训练流程可以先准备一批小的自制视频集分成几类动作比如“挥手”“跳跃”“坐下”每类收集 50 到 100 个短视频验证整套流程能否跑通再迁移到大数据集。5.2 数据增强与采样策略训练阶段和推理阶段的采样策略可以不一样。训练时先从视频中随机裁剪一段时长在这段时长内分别按慢、快时间步长采样同时使用多尺度随机裁剪、水平翻转、颜色抖动等增强手段提升模型的泛化能力。推理时通常采用中心裁剪和均匀采样也可以使用 Ten-Crop 或多段视频投票来提升精度。下面是一个训练阶段随机采样逻辑的参考片段# 训练采样示意先随机取起始点再均匀取帧 def sample_frames_train(frames, T_slow, T_fast, temporal_jitter0.5): total len(frames) seg_len total // T_fast start int(np.random.uniform(0, seg_len * (1 - temporal_jitter))) idx_fast np.linspace(start, total - 1, T_fast).astype(int) idx_slow np.linspace(start, total - 1, T_slow).astype(int) return frames[idx_slow], frames[idx_fast]5.3 损失函数与优化器分类任务直接使用交叉熵损失。优化器推荐带动量的 SGD实验中发现它比 Adam 更稳定。训练初期使用 warmup 策略让学习率从小慢慢变大避免模型在初始阶段震荡。学习率后面用余弦退火或分段下降具体数值要根据 batch size 和数据集规模调整。5.4 实验指标与期望结果以 Kinetics-400 为例SlowFast 论文中不同配置的 Top-1 准确率通常在 73% 到 79% 之间ResNet50 骨架在 8×16 采样配置下大约在 73% 到 75% 区间增大输入帧数、加深网络以及使用更强的测试增强后可以进一步提升。这里不写死某个具体数字因为不同的训练时长、图像分辨率和数据增强策略都会带来差异。自己训练时先关注模型是否收敛再关注准确率。如果训练几十个 epoch 后验证集 Top-1 仍在 50% 以下通常不是模型结构问题而是数据量、学习率或采样逻辑出了问题。6. 工业落地与工程优化6.1 导出 ONNX 模型研究阶段的 PyTorch 模型无法直接高效部署到生产环境通常需要先导出为 ONNX 中间格式再转成 TensorRT、OpenVINO 等推理引擎的模型格式。导出示例# 文件路径export_onnx.py import torch from model import SimpleSlowFast model SimpleSlowFast(num_classes400) checkpoint torch.load(checkpoints/best_model.pth, map_locationcpu) model.load_state_dict(checkpoint[model_state_dict]) model.eval() slow_input torch.randn(1, 3, 8, 256, 256) fast_input torch.randn(1, 3, 32, 256, 256) torch.onnx.export( model, (slow_input, fast_input), slowfast.onnx, input_names[slow_input, fast_input], output_names[logits], dynamic_axes{slow_input: {0: batch}, fast_input: {0: batch}}, opset_version13, ) print(ONNX 模型导出成功)这里强调一点真正使用 ONNX 导出生产模型时最好把 BN 层折叠、输入尺寸固定能显著提升推理速度。6.2 基于 ONNX Runtime 的服务化推理导出后可以用 ONNX Runtime 在 CPU 或 GPU 上推理这样服务端不需要再安装完整 PyTorch 环境。下面是一个简单的推理函数# 文件路径infer.py import cv2 import numpy as np import onnxruntime as ort def load_onnx_model(onnx_path): providers [CUDAExecutionProvider, CPUExecutionProvider] return ort.InferenceSession(onnx_path, providersproviders) def preprocess(video_path): cap cv2.VideoCapture(video_path) frames [] while len(frames) 128: ok, frame cap.read() if not ok: break frame cv2.resize(frame, (256, 256)) frames.append(frame) cap.release() # 按步长抽帧 slow_idx np.linspace(0, len(frames) - 1, 8).astype(int) fast_idx np.linspace(0, len(frames) - 1, 32).astype(int) slow np.transpose(frames[slow_idx], (3, 0, 1, 2))[None] fast np.transpose(frames[fast_idx], (3, 0, 1, 2))[None] return slow.astype(np.float32), fast.astype(np.float32) def infer_video(onnx_path, video_path): session load_onnx_model(onnx_path) slow, fast preprocess(video_path) outputs session.run(None, {slow_input: slow, fast_input: fast}) logits outputs[0][0] return int(np.argmax(logits)), float(np.max(logits))这种部署方式对视频时长做了简单截断只取前 128 帧真实项目中需要根据业务场景设计更合理的滑动窗口策略。6.3 工程化落地注意事项工业部署不能只关注模型精度还要考虑帧缓存、消息队列、GPU 调度和容灾。典型的视频识别服务流程是视频上传 → 抽帧 → 预处理 → 模型推理 → 结果后处理 → 入库。其中抽帧和预处理通常是 CPU 任务模型推理是 GPU 任务两者需要通过线程池或消息队列解耦避免互相阻塞。如果业务视频很长建议先做镜头切分或关键帧提取再对每个片段独立识别。这样既能降低计算量也能更细粒度地定位动作发生的时间点。对于误检问题后处理阶段通常还要加置信度阈值、时序平滑和类别去噪逻辑。7. 常见问题与排查思路7.1 显存不足OOM训练时如果 GPU 显存不够优先降低 batch size然后考虑缩小输入分辨率最后再考虑减少快路径帧数。还可以开启混合精度训练用 AMP 把单精度 float32 换成半精度显存占用能减少一半左右。PyTorch 2.x 中可以直接用torch.autocast和GradScaler。7.2 损失不下降模型训练初期损失不下降常见原因有三个学习率设置过大或过小、数据加载顺序有问题、标签与输入数据不对齐。建议先用很小的数据集比如 100 个视频做一次 overfit 实验确保模型有能力把训练集学到高分再回到完整数据集上调整超参数。7.3 精度上不去精度不理想时优先排查采样逻辑。慢路径和快路径是否覆盖了相同的视频时长快路径的帧数是否足够表达运动其次是数据增强如果训练集和测试集分布差异大模型泛化能力自然弱。最后才是网络结构研究场景中还可以尝试加入 Non-Local 模块或换成更深的骨干网络。7.4 推理速度慢推理阶段速度瓶颈通常在 3D 卷积。可以先固定输入尺寸导出 ONNX再转 TensorRT 做 FP16/INT8 量化。如果对精度要求不高也可以减少快路径帧数或者只在靠近分类头的几层使用高帧率输入牺牲少量精度换速度。下面用表格做一个快速排查清单问题现象常见原因解决思路训练时 OOMbatch size 过大或分辨率过高降低 batch size、开启混合精度、减小输入尺寸损失函数持续不降学习率不合理或数据标签错位先做小样本 overfit再调学习率精度低于预期采样时间窗口不一致检查慢快路径采样是否覆盖相同时长推理延迟高未做格式转换和算子融合导出 ONNX转 TensorRT开启 FP16部署后结果抖动缺少时序平滑后处理增加置信度阈值和滑动窗口投票8. 工程经验与下一步学习建议从论文复现到工业落地我对 SlowFast 最大的感受是它的核心思想非常简单但工程细节非常多。初学者最容易踩的坑就是只盯着网络结构忽略了数据采样、时间对齐和部署优化这三个环节。做视频识别项目时建议把至少一半时间花在数据管道和后处理上模型结构反而是相对成熟的部分。如果你准备深入学习 SlowFast我建议按以下路线推进先去 PySlowFast 官方仓库把环境跑通使用预训练权重在公开数据集上做推理建立“模型输出长什么样”的直觉再动手实现一个自己的采样器把不同时间步长对识别结果的影响做成对照实验然后阅读论文里的消融实验表重点关注 Fast 路径通道数beta、时间步长比alpha、侧向连接层数这三个关键变量最后再尝试把模型接入自己的业务数据做类别定制和数据增强优化。在工程实践中我特别推荐记录每一次实验的输入配置、训练日志和模型评估结果视频识别实验的可复现性比视觉分类更依赖细节。这些配置项帧数、时间步长、采样方式、增强策略任何一个发生变化最终准确率和部署速度都会明显不同。视频时序建模方向还有很多进阶话题值得探索比如结合光流或音频的多模态识别、面向时空动作检测的 SlowFast-AVA、以及结合 Transformer 的 VideoSwin 和 MViT。理解了 SlowFast 的慢快路径设计之后再去看这些模型会轻松很多因为它们的出发点是一样的用更合理的结构去匹配视频中不同维度的信息变化速度。