ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

5 分钟跑通 V-JEPA:自监督视频表征学习新手实战指南

5 分钟跑通 V-JEPA:自监督视频表征学习新手实战指南

5 分钟跑通 V-JEPA:自监督视频表征学习新手实战指南

【免费下载链接】jepaPyTorch code and models for V-JEPA self-supervised learning from video.项目地址: https://gitcode.com/GitHub_Trending/je/jepa

V-JEPA(Video Joint Embedding Predictive Architecture)是 Meta 开源的视频自监督表征学习框架:不标注、不重建像素,只靠"看"视频帧就能学出可用的视觉特征。这篇指南带你完成环境安装、本地启动预训练、跑通 frozen 探针评测三步,并把真正影响行为的配置参数逐行讲清楚。

三步跑通 V-JEPA:克隆、安装、单卡起训

环境准备与安装

项目要求 Python ≥ 3.9,依赖在 requirements.txt 里,核心是 PyTorch 2.x、decord(视频解码)、submitit(分布式提交):

git clone https://gitcode.com/GitHub_Trending/je/jepa cd jepa conda create -n jepa python=3.9 pip conda activate jepa python setup.py install # 会按 requirements.txt 安装全部依赖

仓库结构很克制,四个顶层目录各司其职(README 中的 Code Structure 一节):

app/ 唯一允许写训练循环的地方,入口在 app/main.py evals/ 冻结骨干的探针评测,入口在 evals/main.py src/ 数据集、模型、掩码、工具包 configs/ 唯一允许放配置文件的地方,pretrain/ 与 evals/ 分开放

所有实验参数都通过 YAML 配置文件传入,没有命令行超参——这一点决定了你调参的方式是"改 YAML"而不是"加 --xxx"。

准备视频数据 CSV

V-JEPA 预训练是无监督的,数据侧只需要一个 CSV 索引文件,格式为"绝对路径 + 任意整数标签"(预训练阶段标签列会被直接忽略,填 0 即可):

/path/to/video1.mp4 0 /path/to/video2.mp4 0 /path/to/video3.mp4 0

注意两点:路径必须是绝对路径;如果要跑有监督的视频分类评测(evals/video_classification_frozen/),则必须把这一列换成真实类别标签。

改配置并启动本地预训练

以最小的 configs/pretrain/vith16.yaml 为例,启动前只需改三处路径:

data: datasets: - /your_path/to/video_index.csv # 你上面准备的 CSV 索引 logging: folder: /your/absolute/path/logs # 日志与 checkpoint 目录,必须是存在的绝对路径

然后在仓库根目录执行(官方示例,app/main.py 会为每张卡 spawn 一个进程):

python -m app.main \ --fname configs/pretrain/vith16.yaml \ --devices cuda:0 cuda:1 cuda:2

跑起来后你会在logging.folder下看到params-pretrain.yaml(实际生效配置的回写)和逐步保存的 checkpoint。

一个诚实的提醒:官方在 README 中明确说明,本地单机的作用是调试代码与数据管线;复现论文结果需要 16 节点 × 8 GPU 的分布式训练(nodes: 16, tasks_per_node: 8),那部分见文末"进阶"一节。

原理速览:为什么 V-JEPA 不用像素重建

用三句话说清核心机制:

  1. 从视频里随机挖掉若干时空块,剩下的区域叫 context,被挖掉的叫 target;
  2. 编码器只把 context 区域编码成特征,一个更小的 predictor 网络在潜在空间里预测被挖区域的特征;
  3. 损失算在特征空间(预测特征 vs. 目标编码器给出的真值特征),全程不接触像素重建、负样本或文本标注。

这就是 JEPA(Joint-Embedding Predictive Architecture)的思想落到视频上的版本:模型学到的是"视频接下来会发生什么"的表征级理解,而不是逐像素拟合。掩码的生成逻辑在 src/masks/,模型定义在 src/models/。

预训练配置逐行拆解:data、mask、model、optimization

下面按 configs/pretrain/vith16.yaml 拆四组真正影响行为的参数。

data 段:决定模型"看到什么"

data: num_frames: 16 # 每个片段取 16 帧 tubelet_size: 2 # 时序管片大小:每 2 帧合并成一个 patch sampling_rate: 4 # 帧采样间隔 crop_size: 224 # 空间裁剪分辨率 patch_size: 16 # 空间 patch 大小 batch_size: 24 # 每卡 batch size,显存不够时优先调它 filter_short_videos: false # 是否过滤过短视频

16 帧 ÷ tubelet 2 = 8 个时序位置,空间 224 ÷ 16 = 14×14,即每个视频约 1568 个 token。

mask 段:决定"挖掉多少"

默认是两级掩码(对应data.mask_type: multiblock3d,实现在 src/masks/multiblock3d.py):

mask: - num_blocks: 8 # 挖 8 个小块 spatial_scale: [0.15, 0.15] # 每块空间占比约 15%(细粒度) temporal_scale: [1.0, 1.0] # 覆盖全部时间(整段都要预测) - num_blocks: 2 # 再挖 2 个大块 spatial_scale: [0.7, 0.7] # 每块空间占比 70%(粗粒度)

细块 + 大块的双尺度设计,让模型同时学局部纹理和整段运动。想换另一种策略(随机时间管状掩码),把data.mask_type改成random_tube即可,对应实现是 src/masks/random_tube.py。

model 段:决定"多大多小"

model: model_name: vit_huge # 骨干规格,可选 vit_tiny~vit_gigantic pred_depth: 12 # predictor 层数 pred_embed_dim: 384 # predictor 隐层宽度(比骨干窄,省算力) use_mask_tokens: true # 用 2 个可学习 mask token 作为预测输入

骨干选项在 src/models/vision_transformer.py 中定义(vit_tiny到vit_gigantic),predictor 在 src/models/predictor.py。改model_name就能换骨干规模,是显存紧张时最直接的开关。

optimization 与 meta 段:调度与精度

optimization: epochs: 300 # 训练轮数 warmup: 40 # 学习率线性预热轮数 lr: 0.000625 # 峰值学习率 clip_grad: 10.0 # 梯度裁剪阈值 ema: [0.998, 1.0] # 双 EMA 的衰减率区间 meta: use_sdpa: true # 使用 PyTorch 缩放点积注意力,省显存 dtype: bfloat16 # 混合精度;A100 及以上推荐,老卡改 float32

实战场景:冻结骨干 + 轻量探针评测

V-JEPA 落地时的标准姿势是:骨干完全冻结,只训一个 attentive probe。这比"端到端微调"便宜一个数量级,且能直接复用官方 checkpoint。

场景一:Kinetics-400 视频分类

  1. 下载官方 pretrained checkpoint(vith16.pth.tar,README 的 MODEL ZOO 一节有链接)放到本地目录;
  2. 准备 K400 训练/验证的 CSV 索引(这次标签列必须写真实类别);
  3. 修改 configs/evals/vith16_k400_16x8x3.yaml 中的data.dataset_train、data.dataset_val和pretrain.folder;
  4. 本地起评测:
python -m evals.main \ --fname configs/evals/vith16_k400_16x8x3.yaml \ --devices cuda:0 cuda:1

配置里eval_name: video_classification_frozen指定了用 evals/video_classification_frozen/eval.py;frames_per_clip: 16, num_segments: 8, num_views_per_segment: 3即 16 帧 × 8 段 × 3 视图的评测协议,这也是文件名后缀16x8x3的来历。

场景二:换骨干 / 换分辨率,收益有多大

下面这张表直接来自 README 的 MODEL ZOO(frozen 骨干 + attentive probe 的 top-1 accuracy,单位 %),可以当作"改造前 vs 改造后"的选型参考:

数据集ViT-L 224ViT-H 224ViT-H 384
K400(16x8x3)80.882.081.9
SSv2(16x2x3)69.571.472.2
ImageNet-1K74.875.977.4
Places20560.361.762.8
iNat2167.867.972.6

结论很清晰:分类密集、语义细的任务(SSv2、iNat21)对分辨率更敏感,224→384 能拿到 0.8~4.7 个点;动作分类(K400)上换 ViT-H 反而略低于 384 版,选型要看任务。对应的五个数据集评测配置都在 configs/evals/ 下,各有一个vith16_*.yaml,改pretrain.checkpoint路径即可复用。

常见问题速查

现象原因解决方式
启动即报params-pretrain.yaml写失败logging.folder不存在或不是绝对路径(app/main.py 启动时就写这个文件)先手动创建目录,改为绝对路径;README 明确要求启动前更新配置内所有路径
视频加载崩溃 / 解出空帧decord 不支持该编码,或视频被截断损坏换 mp4 等通用容器格式;把data.filter_short_videos设为true过滤异常样本
训练 OOM默认batch_size: 24× 16 帧 × 224 分辨率占用较高先降data.batch_size,再考虑降model_name或crop_size
bfloat16相关报错老 GPU(Ampere 之前)对 bf16 支持不全把meta.dtype改为float32(app/vjepa/train.py 中会自动关闭混合精度)
跑有监督评测时标签全是 0CSV 标签列沿用预训练的占位 0按 README 要求,把标签列替换为真实类别标签
本地跑和论文数字对不上官方复现依赖 16 节点 × 8 GPU 分布式,本地版仅用于调试接受本地只验证管线;复现数字走 SLURM 分布式(见进阶)

进阶:分布式提交与掩码策略扩展

  • SLURM 三步起分布式训练:入口是 app/main_distributed.py(基于 submitit),--folder指定 stdout/stderr 落盘目录,--partition指定队列;评测同理用 evals/main_distributed.py。
  • 加掩码变体:mask段是列表,追加一条num_blocks+spatial_scale组合即可增加一个尺度;每个字段语义见 src/masks/multiblock3d.py 中_MaskGenerator的参数(如max_temporal_keep控制 context 可保留的最大时间比例)。
  • 换骨干规模:model.model_name支持vit_tiny / vit_small / vit_base / vit_large / vit_huge / vit_giant / vit_gigantic,配套配置参照 configs/pretrain/ 下三份 YAML 的写法。

官方三个 pretrained checkpoint(ViT-L 224、ViT-H 224、ViT-H 384,均训于 90K iterations、VideoMix2M)及全部探针 checkpoint 的下载地址都列在 README 的 MODEL ZOO 一节,配合上面的评测配置可以直接复现表中数字。

【免费下载链接】jepaPyTorch code and models for V-JEPA self-supervised learning from video.项目地址: https://gitcode.com/GitHub_Trending/je/jepa

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进