
MoViNets 实战指南基于 TensorFlow models 仓库实现移动端高效视频动作识别【免费下载链接】modelsModels and examples built with TensorFlow项目地址: https://gitcode.com/GitHub_Trending/mode/models本文以 TensorFlow models 仓库中的 MoViNet 官方实现official/projects/movinet/为主体完整讲解 MoViNets 这一可在移动端运行的视频分类模型族的设计动机、Base 与 Streaming 两类模型的区别、Kinetics 400/600 预训练权重与 TF Lite 延迟数据、从 Python 推理到 TF Lite 部署再到训练评测的完整流程。读完本文你可以独立构建 MoViNet 骨干网络、以逐帧流式方式对视频进行动作分类、将模型导出为 TF Lite 并在 CPU 上推理也能复现其 Kinetics 600 训练配置。MoViNetsMobile Video Networks是论文MoViNets: Mobile Video Networks for Efficient Video RecognitionarXiv:2103.11511的官方 TensorFlow 实现定位为既准确又能在移动设备上跑的视频分类模型族覆盖从最小的 MoViNet-T0/A0 到最大的 MoViNet-A5/A6。仓库中该模块的入口文档为 official/projects/movinet/README.md核心实现在 official/projects/movinet/modeling/movinet.py 与 official/projects/movinet/modeling/movinet_model.py。设计动机弥合 2D 高效 CNN 与 3D 高精度 CNN 之间的鸿沟视频动作识别领域长期存在一个矛盾2D MobileNet 类 CNN 速度快、能实时处理流式视频但对时序建模弱预测噪声大3D CNN 准确度高但显存与算力开销大无法用于流式视频。MoViNets 用三种手段弥合这一差距README Description 一节全模型族的效率-精度权衡MoViNet-A0 到 A6 共 7 个规格从 2.7 GFLOPs 到 280 GFLOPs 覆盖不同部署预算流式Streaming模型采用因果causal卷积加流缓冲区stream buffer显著降低内存占用可逐帧处理无限长视频时序集成Temporal ensembles例如 MoViNet-A6 可由 MoViNet-A4 与 A5 组合而成进一步提升精度。README 给出的代表性数据在 Kinetics 600 上MoViNet-A6 达到 84.8% top-1 精度优于 ViViT83.0%与 VATT83.6%等视觉 Transformer 方案且 FLOPs 少约 10 倍流式 MoViNet-A0 达到 72% 精度比 MobileNetV3-large68%FLOPs 少约 3 倍。此外MoViNets 逐帧输出高质量预测避免了传统多片段multi-clip评测方式带来的重复计算与时序范围受限问题。模型家族、预训练权重与 TF Lite 延迟Kinetics 600 基础模型BaseBase 模型实现标准 3D 卷积、不含流缓冲区。由于 CPU/移动端对tf.nn.conv3d支持有限README 明确提示 Base 模型不推荐用于 CPU 或移动端快速推理移动端请使用下面的流式模型。模型Top-1Top-5输入形状帧 x 高 x 宽GFLOPsMoViNet-A0-Base72.2890.9250 x 172 x 1722.7MoViNet-A1-Base76.6993.4050 x 172 x 1726.0MoViNet-A2-Base78.6294.1750 x 224 x 22410MoViNet-A3-Base81.7995.67120 x 256 x 25657MoViNet-A4-Base83.4896.1680 x 290 x 290110MoViNet-A5-Base84.2796.39120 x 320 x 320280表中 GFLOPs 为 Kinetics 600 上单视频的算力开销所有模型均以与训练相同分辨率的单片段评测。MoViNet-A6 可构造为 A4 与 A5 的集成。Kinetics 600 流式模型Streaming流式模型使用因果 (21)D 卷积加流缓冲区以 (21)D 卷积替代 3D 卷积从而调用经过优化的tf.nn.conv2d算子在 CPU 上获得快速推理。流式模型既可以逐帧运行也可以像 Base 模型一样整段视频一次运行。模型Top-1Top-5输入形状*GFLOPsMoViNet-A0-Stream72.0590.6350 x 172 x 1722.7MoViNet-A1-Stream76.4593.2550 x 172 x 1726.0MoViNet-A2-Stream78.4094.0550 x 224 x 22410MoViNet-A3-Stream80.0994.84120 x 256 x 25657MoViNet-A4-Stream81.4995.6680 x 290 x 290110MoViNet-A5-Stream82.3795.79120 x 320 x 320280*流式模式下帧数对应 10 秒片段累积的总时长。注意两点实现细节README 明确指出位置编码Positional Encoding只用于 A3/A4/A5这三个大模型的 squeeze-excitation 块带有位置编码去掉后精度明显下降A0/A1/A2 则不带精度不受影响。这与源码一致——在 official/projects/movinet/modeling/movinet.py 的_build_network中use_positional_encoding只有在self._causal为真时才实际生效第 502-503 行。当前流式 checkpoint 已更新为略有差异的新架构需要旧版 checkpoint 时在 URL 中.tar.gz前插入_legacy如movinet_a0_stream_legacy.tar.gz。TF Lite 流式模型与单帧延迟仓库提供转换好的 TF Lite 模型float32 单帧延迟单 CPU 核心实测于 Pixel 4/Android 11 与 x86 Intel Xeon W-2135模型输入形状Pixel 4 延迟x86 延迟文件大小MoViNet-A0-Stream1 x 1 x 172 x 17222 ms16 ms13 MBMoViNet-A1-Stream1 x 1 x 172 x 17242 ms33 ms45 MBMoViNet-A2-Stream1 x 1 x 224 x 224200 ms66 ms53 MBMoViNet-A3-Stream1 x 1 x 256 x 256-120 ms73 MBMoViNet-A4-Stream1 x 1 x 290 x 290-300 ms101 MBMoViNet-A5-Stream1 x 1 x 320 x 320-450 ms153 MB作为参照MoViNet-A0-Stream 的延迟与 MobileNetV3-Large224 输入相当但 Kinetics 600 上 top-1 精度高约 5 个百分点。此外还有量化版 TF Lite 模型可经 TF Hub 获取2022-03-14 更新。Kinetics 400 权重仓库同时提供 Kinetics 400 的 Base 模型 checkpointA0-Base 69.40%、A1-Base 74.57%、A2-Base 75.91%、A3-Base 79.34%、A4-Base 80.64%、A5-Base 81.39%Top-1输入形状与 GFLOPs 与 Kinetics 600 对应模型一致。加载时只需把分类头设置为num_classes400。架构细节从 BLOCK_SPECS 到流状态骨干结构的声明式定义official/projects/movinet/modeling/movinet.py 用三个 dataclass 声明每个模型的结构StemSpec输入块filters、kernel_size、strides、MovinetBlockSpec每个 block 内的若干层base_filters、expand_filters、kernel_sizes、strides与HeadSpecproject_filters、head_filters。文件顶部定义了两个命名常量便于书写规格K13/K15/K33/K53表示 (1,3,3)、(1,5,5)、(3,3,3)、(5,3,3) 的 3D 卷积核S11/S12/S22/S21表示对应的步幅。以 MoViNet-A0 为例第 81-109 行a0: ( StemSpec(filters8, kernel_sizeK13, stridesS12), MovinetBlockSpec(base_filters8, expand_filters(24,), kernel_sizes(K15,), strides(S12,)), MovinetBlockSpec(base_filters32, expand_filters(80, 80, 80), kernel_sizes(K33, K33, K33), strides(S12, S11, S11)), # ... 共 5 个 MovinetBlockSpec ... HeadSpec(project_filters480, head_filters2048), )BLOCK_SPECS字典中定义了a0~a5以及更小的t0比 A0 更小更快的规格。从源码结构看模型规模随model_id增大而主要体现在空间卷积核更宽K53 出现更多、块数更多、expand_filters 更大所有模型共享head_filters2048t0 为 1024的分类头宽度。卷积类型 conv_type 的三种取值Movinet.__init__对conv_type的校验与注释movinet.py 第 335-344 行给出了三种取值的精确定义3d默认 3D 卷积2plus1d(21)D 卷积底层用Conv2D 2D 重塑实现——例如一个 5x3x3 卷积核拆为先 3x3、再 5x1 的卷积这正是流式模型在 CPU 上快的重要原因3d_2plus1d(21)D 卷积但底层用 Conv3D如 5x3x3 拆为 1x3x3 加 5x1x1训练吞吐更好但不适合直接导出给 TF Lite需先用工具转换见下文 TF Lite 一节。se_typesqueeze-excitation 的全局池化方式取值3d/2d/2plus3d/none其中2plus3d是 2D 与 3D 全局平均池化结果的拼接——流式 checkpoint 正是用se_type2plus3d。流状态states是流式推理的核心流式模型把记忆显式化为一个状态字典。从 movinet.py 的_get_initial_state_shapes第 539-634 行可以看到状态名的构造规则每个含时间卷积核的层拥有state_block{b}_layer{l}_stream_buffer形状中时间维为kernel_size[0] - 1即缓存卷积核宽度减一的过去帧特征每个使用 3D squeeze-excitation 的层拥有state_block{b}_layer{l}_pool_buffer形状 [B,1,1,1,C]累积求和缓冲与state_block{b}_layer{l}_pool_frame_countint32 帧计数用于累积式全局平均池化开启位置编码时额外有*_pos_enc_frame_countHead 处有state_head_pool_buffer与state_head_pool_frame_count。对应地init_states(input_shape)会返回所有状态置零的字典frame_count类状态用 int32其余用模型 dtypeinitial_state_specs则返回同名InputSpec字典供 Keras 建图时作为额外输入。分类模型 MovinetClassifier 进一步封装了这套机制当骨干use_external_statesTrue时__init__会为每个状态创建tf_keras.Input使模型输入变成{**states, image}的字典输出为(logits, states)二元组_build_backbone还显式校验了输入/输出状态集合与形状的一致性第 119-144 行防止状态名不匹配导致的静默错误。层次实现位于 official/projects/movinet/modeling/movinet_layers.py其中StreamBuffer、StreamConvBlock、StreamSqueezeExcitation、MobileBottleneck、SkipBlock、MovinetBlock、Stem、Head、ClassifierHead等类分别对应上述结构组件。推理示例Base 与 Streaming 两种用法以下两段代码完整继承自 README Prediction Examples可直接复制运行需先pip install -rofficial/projects/movinet/requirements.txt。Base 模型一次性推理import tensorflow as tf from official.projects.movinet.modeling import movinet from official.projects.movinet.modeling import movinet_model # Create backbone and model. backbone movinet.Movinet( model_ida0, causalFalse, use_external_statesFalse, ) model movinet_model.MovinetClassifier( backbone, num_classes600, output_statesFalse) # Create your example input here. # Refer to the paper for recommended input shapes. inputs tf.ones([1, 8, 172, 172, 3]) # [Optional] Build the model and load a pretrained checkpoint model.build(inputs.shape) checkpoint_dir /path/to/checkpoint checkpoint_path tf.train.latest_checkpoint(checkpoint_dir) checkpoint tf.train.Checkpoint(modelmodel) status checkpoint.restore(checkpoint_path) status.assert_existing_objects_matched() # Run the model prediction. output model(inputs) prediction tf.argmax(output, -1)要点Base 模型causalFalse、use_external_statesFalse调用方式就是普通的 Keras 前向传播输入为[batch, num_frames, H, W, 3]的整段视频。Streaming 模型逐帧推理import tensorflow as tf from official.projects.movinet.modeling import movinet from official.projects.movinet.modeling import movinet_model model_id a0 use_positional_encoding model_id in {a3, a4, a5} # Create backbone and model. backbone movinet.Movinet( model_idmodel_id, causalTrue, conv_type2plus1d, se_type2plus3d, activationhard_swish, gating_activationhard_sigmoid, use_positional_encodinguse_positional_encoding, use_external_statesTrue, ) model movinet_model.MovinetClassifier( backbone, num_classes600, output_statesTrue) # Create your example input here. inputs tf.ones([1, 8, 172, 172, 3]) # [Optional] Build the model and load a pretrained checkpoint. model.build(inputs.shape) checkpoint_dir /path/to/checkpoint checkpoint_path tf.train.latest_checkpoint(checkpoint_dir) checkpoint tf.train.Checkpoint(modelmodel) status checkpoint.restore(checkpoint_path) status.assert_existing_objects_matched() # Split the video into individual frames. # Note: we can also split into larger clips as well (e.g., 8-frame clips). # Running on larger clips will slightly reduce latency overhead, but # will consume more memory. frames tf.split(inputs, inputs.shape[1], axis1) # Initialize the dict of states. All state tensors are initially zeros. init_states model.init_states(tf.shape(inputs)) # Run the model prediction by looping over each frame. states init_states predictions [] for frame in frames: output, states model({**states, image: frame}) predictions.append(output) # The video classification will simply be the last output of the model. final_prediction tf.argmax(predictions[-1], -1) # Alternatively, we can run the network on the entire input video. # The output should be effectively the same # (but it may differ a small amount due to floating point errors). non_streaming_output, _ model({**init_states, image: inputs}) non_streaming_prediction tf.argmax(non_streaming_output, -1)这里有几个值得对照源码理解的细节流式模型必须同时满足causalTrue与use_external_statesTrue。movinet.py 第 398-399 行显式抛出异常非 causal 模式下使用外部状态会被拒绝init_states(tf.shape(inputs))返回的状态全部为 0 张量与_get_initial_state_shapes推导的形状一一对应逐帧循环时model({**states, image: frame})的返回值是(logits, states)——下一轮的states直接覆盖上一轮输出这正是状态字典设计的意义缓冲区、累积池化和帧计数都随帧推进自动更新帧可以换成更大的片段如 8 帧能略微降低调用开销但会增加内存整段视频一次跑通与逐帧循环结果在浮点误差范围内应一致README 也把这一点作为可验证的预期行为视频级标签取最后一帧的输出predictions[-1]因为因果模型逐帧输出的是截至当前的预测。仓库还附带两个可直接运行的示例动作识别演示 GIFofficial/projects/movinet/files/jumpingjack.gif以及 Kinetics 600 类别表 official/projects/movinet/files/kinetics_600_labels.txt官方 Colab 教程 movinet_tutorial.ipynb 与流式训练/推理教程 movinet_streaming_model_training_and_inference.ipynb 覆盖了完整操作路径tools/plot_movinet_video_stream_predictions.ipynb 则用于生成视频预测曲线图。部署到移动端TF Lite 导出全流程本节完整继承 README TF Lite Example 的流程共三步可选3D 权重转换、导出 SavedModel、转换并运行 TF Lite。第一步可选3d_2plus1d 权重转 2plus1d 图流式模型训练时通常用conv_type3d_2plus1d以获得更高训练吞吐。要在 CPU 上获得更好的推理性能需把权重转换到2plus1d图结构仓库提供现成工具 tools/convert_3d_2plus1d.py配套测试见 tools/convert_3d_2plus1d_test.py。若你的 checkpoint 本身就是2plus1d训练的可跳过此步。第二步导出 TF SavedModel以 MoViNet-A0-Stream 为例运行 tools/export_saved_model.pypython3 export_saved_model.py \ --model_ida0 \ --causalTrue \ --conv_type2plus1d \ --se_type2plus3d \ --activationhard_swish \ --gating_activationhard_sigmoid \ --use_positional_encodingFalse \ --num_classes600 \ --batch_size1 \ --num_frames1 \ --image_size172 \ --bundle_input_init_states_fnFalse \ --checkpoint_path/path/to/checkpoint \ --export_path/tmp/movinet_a0_stream参数说明对照 configs/movinet.py 中Movinet配置类的默认值--model_ida0~a5对应BLOCK_SPECS中的架构--causalTrue、--conv_type2plus1d、--se_type2plus3d流式模型三要素与上文架构细节一致--activationhard_swish与--gating_activationhard_sigmoid流式 checkpoint 使用的硬件友好激活配置类默认值为swish/sigmoidBase 模型训练即使用默认值--use_positional_encoding仅 a3/a4/a5 需要置 True--batch_size1 --num_frames1 --image_size172移动端单帧推理规格--bundle_input_init_states_fn控制是否在 SavedModel 中附带生成初始状态的函数--num_classes600 对应 Kinetics 600400 对应 Kinetics 400。第三步转换为 TF Lite 并用 Interpreter 逐帧运行saved_model_dir /tmp/movinet_a0_stream converter tf.lite.TFLiteConverter.from_saved_model(saved_model_dir) tflite_model converter.convert() with open(/tmp/movinet_a0_stream.tflite, wb) as f: f.write(tflite_model)推理时SavedModel 的每个状态输入都有一个serving_default_...签名键需要还原为状态名并初始化为 0 张量然后逐帧调用 signature runner# Create the interpreter and signature runner interpreter tf.lite.Interpreter(/tmp/movinet_a0_stream.tflite) runner interpreter.get_signature_runner() # Extract state names and create the initial (zero) states def state_name(name: str) - str: return name[len(serving_default_):-len(:0)] init_states { state_name(x[name]): tf.zeros(x[shape], dtypex[dtype]) for x in interpreter.get_input_details() } del init_states[image] # Insert your video clip here video tf.ones([1, 8, 172, 172, 3]) clips tf.split(video, video.shape[1], axis1) # To run on a video, pass in one frame at a time states init_states for clip in clips: # Input shape: [1, 1, 172, 172, 3] outputs runner(**states, imageclip) logits outputs.pop(logits) states outputs注意init_states的构造方式image是普通输入而非状态需要删除其余输入键都是状态流缓冲区、累积池化缓冲、帧计数初始值全部为 0这与 Python 端model.init_states()的语义完全对应。仓库另外提供量化脚本 tools/quantize_movinet.pyREADME 提到的量化 TF Lite 二进制即由此类流程产出。部署到真实移动设备请遵循 TensorFlow Lite 官方指南。训练与评测配置驱动的官方流程训练与评测命令入口脚本 official/projects/movinet/train.py 基于官方train_lib.run_experiment框架先解析 YAML 配置与 Gin 绑定设置混合精度策略bfloat16用于 TPU、mixed_float16用于 GPU再按--mode分派训练/评测。完整命令继承自 READMEMODEtrain_and_eval # Can also be train if using a separate evaluator job CONFIG_FILEofficial/projects/movinet/configs/yaml/movinet_a0_k600_8x8.yaml python3 official/projects/movinet/train.py \ --experimentmovinet_kinetics600 \ --mode${MODE} \ --model_dir/tmp/movinet_a0_base/ \ --config_file${CONFIG_FILE}纯评测使用MODEeval训练期间持续评测则用eval_continuous命令结构相同。--experimentmovinet_kinetics600对应 configs/movinet.py 中经exp_factory.register_config_factory(movinet_kinetics600)注册的经验工厂函数它复用video_classification_kinetics600()并强制训练/验证数据 dtype 为bfloat16。关键训练配置逐项解读以官方 A0/Kinetics-600 TPU 配置 configs/yaml/movinet_a0_k600_8x8.yaml 为例文件头注明该配置达到 72.28% Top-1与 README 表格一致runtimedistribution_strategy: tpu、mixed_precision_dtype: bfloat16——8x8 TPU pod 的混合精度训练同目录还有movinet_a0_gpu.yamlGPU 版与movinet_a0_k600_cpu_local.yamlCPU 本地调试版数据feature_shape: [50, 172, 172, 3]、temporal_stride: 5、random_stride_range: 1——50 帧、时间步长 5即约 10 秒片段global_batch_size: 1024增强为aug_type: autoaug并限定裁剪面积比 0.08~1.0、宽高比 0.5~2.0、min_image_size: 192模型model_id: a0、stochastic_depth_drop_rate: 0.2随机深度按层线性递增见 movinet.py 第 486-488 行drop_rate * idx / num_layers、dropout_rate: 0.2、use_sync_bn: true、激活swish损失l2_weight_decay: 0.00003、label_smoothing: 0.1优化器RMSProprho: 0.9, momentum: 0.9, epsilon: 1.0, clipnorm: 1.0学习率为余弦衰减初值 1.8decay_steps: 85785加线性 warmup2145 步总步数train_steps: 85785验证num_test_clips: 1、num_test_crops: 1与 README单片段、训练分辨率的评测口径一致。configs/yaml 目录下为 A0~A5 各提供 Base*_k600_8x8.yaml与 Stream*_stream_k600_8x8.yaml两套 TPU 配置另有 T0 的两套配置修改模型规模时只需替换--config_file即可。测试侧可用 configs/movinet_test.py、train_test.py 以及 modeling/movinet_test.py、modeling/movinet_model_test.py 验证配置解析与建图逻辑。版本演进、许可与引用README 的 History 记录了模块演进2021-05-11初始提交2021-05-30增加流式 MoViNet checkpoint 与示例2021-07-12增加 TF Lite 支持并把 3D 流式模型替换为对移动端更友好的 (21)D 流式模型这解释了为何流式模型采用2plus1d而非3d2022-03-14支持量化 TF Lite 模型并更新 Colab notebook。该模块要求 TensorFlow 2.4 及以上、Python 3.6 及以上环境依赖清单见 official/projects/movinet/requirements.txt。整个 MoViNet 模块遵循Apache License 2.0许可。若在论文中引用本实现请使用article{kondratyuk2021movinets, title{MoViNets: Mobile Video Networks for Efficient Video Recognition}, author{Dan Kondratyuk, Liangzhe Yuan, Yandong Li, Li Zhang, Matthew Brown, and Boqing Gong}, journal{arXiv preprint arXiv:2103.11511}, year{2021} }小结MoViNets 的价值在于用一套统一的状态化因果架构同时覆盖了离线批量评测与端上流式实时识别两种场景Base 模型用标准 3D 卷积换取简单性Streaming 模型用 (21)D 因果卷积加显式状态字典流缓冲区、累积池化缓冲、帧计数把任意长视频变成逐帧可增量计算的过程。结合仓库中的预训练权重、TF Lite 导出工具链convert_3d_2plus1d.py→export_saved_model.py→TFLiteConverter以及配置驱动的训练入口 train.py开发者可以在 2.7 GFLOPs 的 A0 到 280 GFLOPs 的 A5 之间按部署预算灵活选型并把整条训练—评测—导出—端侧推理链路全部跑通。【免费下载链接】modelsModels and examples built with TensorFlow项目地址: https://gitcode.com/GitHub_Trending/mode/models创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考