Transformers 中的 PeVideo:面向零样本视频分类与视频-文本检索的对比学习视频编码器实战指南

发布时间:2026/9/8 20:25:19
Transformers 中的 PeVideo:面向零样本视频分类与视频-文本检索的对比学习视频编码器实战指南 Transformers 中的 PeVideo面向零样本视频分类与视频-文本检索的对比学习视频编码器实战指南【免费下载链接】transformers Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformersPE VideoPerception Encoder Video感知编码器的视频分支是 Meta Perception Encoder 家族中负责处理视频模态的模型它将视频片段与文本在同一个共享嵌入空间中对齐从而仅用一个预训练主干即可完成零样本视频分类和视频-文本检索。本文以 pe_video.md 为骨架结合 modeling_pe_video.py 等源码从模型设计、配置体系、预处理细节到推理实践完整还原该模型在 Transformers 中的接入方式与底层原理。读完本文你将能独立加载facebook/pe-av-large权重对任意视频做带文本标签的零样本打分并理解其帧采样、padding mask、RoPE 时间轴等设计取舍。模型背景Meta Perception Encoder 家族的视频分支根据仓库文档 pe_video.md 的记录PE Video 模型于 2025-04-17 发布并在 2025-12-16 被合入 Hugging Face Transformers。它隶属于 Meta 的 Perception EncoderPE模型家族论文编号 arXiv 2504.13181是该家族的视频video分支——同族还包括音频audio等分支并共享同一套对比学习范式。PE Video 的核心思想非常简洁用对比学习把视频片段与文本描述对齐到同一个共享嵌入空间。训练完成后这一个预训练主干就能直接做两件事零样本视频分类给定若干文本候选类别计算视频与每个类别的相似度并打分视频-文本检索把视频嵌入与文本嵌入在共享空间内做相似度排序即可互相检索。模型仓库中与本文强相关的入口还包括快速上手用的facebook/pe-av-large检查点它同时涵盖感知编码器的音视频能力这也是为什么仓库文档将官方检查点集合统称为 perception-encoder-audio-visual。需要注意facebook/pe-av-large是共享的音频-视觉检查点PE Video 模块加载它后会使用其中的视频塔。Quickstart一行行跑通零样本视频分类文档 pe_video.md 给出了一个可直接运行的完整示例。我们把它拆开并结合源码细节逐段讲解。import torch from transformers import AutoProcessor, PeVideoModel from transformers.video_utils import load_video processor AutoProcessor.from_pretrained(facebook/pe-av-large) model PeVideoModel.from_pretrained( facebook/pe-av-large, device_mapauto, ) video, _ load_video(https://huggingface.co/datasets/hf-internal-testing/fixtures_videos/resolve/main/tennis.mp4) labels [a person playing tennis, a person cooking, a cat sleeping] video_inputs processor.video_processor(video, num_frames16, return_tensorspt).to(model.device) text_inputs processor.tokenizer(labels, paddingTrue, return_tensorspt).to(model.device) inputs {**video_inputs, **text_inputs} with torch.no_grad(): outputs model(**inputs) probs outputs.logits_video_text.sigmoid() print({label: p.item() for label, p in zip(labels, probs[0])})代码里每一行都值得展开说明预处理分两步PeVideoProcessor见 processing_pe_video.py是一个轻量ProcessorMixin同时暴露video_processor视频塔与tokenizer文本塔两个属性二者分别处理视频帧与文本标签最后用解包语法{**video_inputs, **text_inputs}拼成模型输入。video_processor负责视频塔输入视频需要先抽帧、再归一化缩放产出键为pixel_values_videos的张量。传给num_frames16表示均匀抽取 16 帧采样细节见下文时间轴处理小节return_tensorspt决定是否做变长 padding 并返回padding_mask_videos。tokenizer负责文本塔输入把候选标签文本批量 tokenize得到input_ids与attention_mask。logits_video_text是行语义的关键如 modeling_pe_video.py 所示logits_video_text video_embeds text_video_embeds.T即视频嵌入与各条文本嵌入的内积形状为(视频条数, 文本条数)。随后逐元素做sigmoid即可把 logits 转成 0~1 的多标签式概率因此示例里对每个标签输出一个独立概率。若只放一条视频、若干候选文本probs[0]恰好就是这条视频在每个候选类别上的置信度——这就是零样本分类的核心。若想控制 loss 或拿原始嵌入PeVideoOutputmodeling_pe_video.py会返回logits_video_text、text_video_embeds、video_embeds、两塔各自的outputs以及可选的loss字段。三条关键使用要点Usage tips的源码级解读文档在 Usage tips and notes 里强调了三个最容易踩坑的点我们逐条追到源码。1. 变长视频请用padding_mask_videos而不是attention_mask视频塔内部的 self-attention 需要哪些帧是真实帧的指示这个角色由padding_mask_videos承担文本塔的attention_mask不参与视频帧的遮蔽。关键行为藏在 video_processing_pe_video.py 的PeVideoVideoProcessor._preprocess中def _preprocess(self, videos, **kwargs): # Always set return_tensors to None since it wont pad variable length videos # Well handle this after we call the parents method return_tensors kwargs.pop(return_tensors, None) result super()._preprocess(videos, **kwargs) pixels result.pixel_values_videos data {pixel_values_videos: pixels} if return_tensors: lengths torch.tensor([video.size(0) for video in pixels]) pixels torch.nn.utils.rnn.pad_sequence(pixels, batch_firstTrue, padding_value0.0) data[pixel_values_videos] pixels if lengths.unique().size(0) 1: mask torch.arange(lengths.max())[None] lengths[:, None] data[padding_mask_videos] mask return BatchFeature(datadata, tensor_typereturn_tensors)处理变长视频时父类逻辑不负责 padding因此子类先强制把return_tensors弹出、以列表形式拿到各条 clip 的帧张量仅当调用方显式传入return_tensors如pt时才用pad_sequence把变长帧序列按最长帧数对齐为(batch_size, num_frames, C, H, W)填充值 0.0padding_mask_videos只会在批内长度不一致时返回实现用torch.arange(lengths.max())[None] lengths[:, None]生成布尔 mask1表示真实帧、0表示 padding 帧PeVideoEncoder.forward 的 docstring 同样明确了这一语义反之若不传return_tensors你得到的是一组每条视频一个张量的列表且没有mask。换句话说处理变长视频时必须同时满足传return_tensors 拿padding_mask_videos两个条件否则要么无法 padding、要么丢了 mask。测试 test_modeling_pe_video.py 中也刻意构造了valid_lengths在[1, num_frames]区间内的随机合法长度来覆盖这一路径。2.num_frames决定均匀采样缺省则退回基于 fps 的采样视频塔把时间轴当做一个真实存在的维度因此到底取哪些帧非常重要。video_processing_pe_video.py 中的PeVideoVideoProcessor.sample_frames实现为def sample_frames(self, metadata, num_framesNone, fpsNone, **kwargs): if num_frames: total_frames metadata.total_num_frames num_frames num_frames if num_frames is not None else self.num_frames frame_idxs [int(i * (total_frames - 1) / (num_frames - 1)) for i in range(num_frames)] return torch.tensor(frame_idxs) else: return super().sample_frames(metadata, num_frames, fps, **kwargs)传入num_frames时采用固定长度均匀采样在闭区间[0, total_frames - 1]上等间距取帧frame_idxs首尾一定覆盖视频的第一帧和最后一帧不传num_frames时if num_frames:为假直接回退到基类BaseVideoProcessor.sample_frames的基于 fps 的采样逻辑。文档特别提醒检查点在训练时通常针对特定的帧数quickstart 用的num_frames16就是常见取值因此推理时应尽量匹配检查点训练时使用的帧数不要随意切换采样策略否则分布偏移可能显著影响打分质量。底层视频解码则统一由 video_utils.py 的load_video完成——支持本地路径与 URL默认后端为pyav也可显式选择decord / opencv / torchvision / torchcodec并规定num_frames、fps、sample_indices_fn三者互斥。3.main_input_name的差异会影响通用工具路由这一点关系到 Transformers 生态里的通用工具代码。两个类的main_input_name并不相同视频编码器PeVideoEncoder的main_input_name pixel_values_videosmodeling_pe_video.py 的PeVideoPreTrainedModel与 PeVideoEncoder 均如此而完整模型PeVideoModel的main_input_name input_idsmodeling_pe_video.py。原因很直观PeVideoModel是文本塔 视频塔的双塔结构通用 API例如某些 pipeline、序列化或参数路由代码会通过main_input_name判断主输入是文本还是视觉特征而对双塔模型而言文本侧的input_ids才是惯例上的主入口。如果你写代码时依赖model.main_input_name来决定把张量放到哪个字段请务必分清对象是PeVideoEncoder还是PeVideoModel。架构原理双塔 共享嵌入空间从 PeVideoConfig 与 PeVideoModel 可以看出PE Video 是典型的双塔对比结构┌──────────── 文本塔 ────────────┐ input_ids ───►│ ModernBERT (AutoModel) │──► text_video_head ──► text_video_embeds └────────────────────────────────┘ ▼ logits_video_text video_embeds text_video_embeds.T ▲ ┌──────────── 视频塔 ────────────┐ pixel_values ──►│ Timm ViT → patch embedder │ videos │ → 6× Transformer → pooler │──► video_head ──► video_embeds └────────────────────────────────┘4.1 视频塔PeVideoEncoderPeVideoEncodermodeling_pe_video.py的 forward 依次经过四个阶段帧嵌入PeVideoEncoderEmbedder把(batch, num_frames, C, H, W)展平成(batch*num_frames, C, H, W)喂给AutoModelForImageClassification.from_config(config.vision_config)所构造的视觉主干——默认是 timm wrapper 下的vit_pe_core_large_patch14_336ViT-Large、patch 14、输入 336×336见 configuration_pe_video.py。每帧被压成一个 logits 向量随后F.normalize、两层线性映射projdata_proj得到逐帧 token 序列即patch embedder 的输入 embedsPatch EmbedderPeVideoEncoderPatchEmbedder在序列最前面拼接一个可学习的class token然后送入 1D ResNet 块含 PeVideoMaskedGroupNorm SiLU kernel3 的 Conv1d。GroupNorm 被改造成只对真实帧做统计padding_mask参与 mean/var 计算并对输出做* padding_mask从而保证 padding 帧不会污染归一化统计量RoPE 6 层 Transformerclass token 与帧序列一起进入PeVideoEncoderLayerRMSNorm、GQA 风格多头注意力 q/k 上的 RMSNorm、SwiGLU MLP。attention 是双向的self.is_causal False并通过create_bidirectional_mask把帧 mask 转换成注意力 mask输出投影最后经 RMSNorm 与无 bias 线性层后PeVideoEncoder.forward返回BaseModelOutputWithPooling——last_hidden_state hidden_states[:, 1:]去掉 class token 的逐帧输出pooler_output hidden_states[:, 0]class token 即视频级表征。值得注意的是RoPE 位置编码PeVideoEncoderRotaryEmbedding和 patch embedder 都把时间轴当作第一等公民维度来编码位置 id 沿帧序列生成rope_theta20000按head_dim计算逆频率。正因为时间信息被 RoPE 烘焙进序列内部模型才能直接编码变长片段配合 mask而不必逐帧独立 tile后手工融合。4.2 文本塔与对比头文本塔通过AutoModel.from_config(config.text_config)构造默认是ModernBERT主干hidden_size1024, intermediate_size2624, num_hidden_layers22, num_attention_heads16见 configuration_pe_video.pyPeVideoModel.forwardmodeling_pe_video.py里文本侧取text_outputs.hidden_states[-1][:, 0]CLS 位置作为文本序列表征视频侧直接取video_outputs.pooler_output两侧分别过PeVideoContrastiveHeadLayerNorm 无 bias 线性投影modeling_pe_video.py投影到同一hidden_size空间最终 logits 还要经过可学习的text_video_logit_scale与text_video_logit_bias缩放平移。4.3 对比损失当return_lossTrue时modeling_pe_video.py代码用单位矩阵torch.eye(batch_size)作为标签对labels * logits_video_text计算-logsigmoid(...).sum() / batch_size——这是一个在批内自建正负样本对对角线为正对的对比式 sigmoid loss即视频塔与文本塔的对齐信号来源。推理时你无需 loss只需对 logits 做sigmoid()得到各标签的概率。配置参数全景PE Video 提供两级配置完整模型PeVideoConfig与其子配置PeVideoEncoderConfig。二者都标记了base_config_key audio_video_config见 configuration_pe_video.py说明在感知编码器家族里音视频配置结构是同构的、可互换的这也呼应了音视频共享检查点的生态设计。PeVideoConfig完整双塔模型model_type pe_video字段默认值说明text_configNone回退到默认 ModernBERT 参数文本塔配置可传 dict 或PreTrainedConfigdict 会与默认 ModernBERT 参数合并video_configNone视频塔配置可传 dict 或PreTrainedConfig最终实例化为PeVideoEncoderConfigmodel_typepe_video注册的模型类型标识PeVideoEncoderConfig视频编码器model_type pe_video_encoder字段默认值说明vision_configtimm_wrappervit_pe_core_large_patch14_336do_poolingTruenum_classes1024global_poolmap逐帧视觉主干TimmWrapper负责把每帧压成向量hidden_size1792Transformer 隐藏维度intermediate_size4800MLP 中间维度num_hidden_layers6Transformer 层数num_attention_heads14注意力头数num_key_value_headsNone自动等于num_attention_headsGQA 的 KV 头数为None时退化为 MHAhead_dim128每个注意力头的维度RoPE 按它计算逆频率hidden_actsiluMLP 激活函数配合 SwiGLU 门控max_position_embeddings10000RoPE 缓存的最大序列长度initializer_range0.02初始化标准差rms_norm_eps1e-5RMSNorm 的 epsrope_parameters{rope_theta: 20000}RoPE 超参theta可切换高级 rope_typeattention_biasFalse注意力投影是否带 biasattention_dropout0.0注意力 dropout训练时生效配置类本身是带strict的 dataclass来自 huggingface_hub 的严格校验并在__post_init__中完成默认值推导与嵌套配置实例化——即把num_key_value_headsNone填成注意力头数、把rope_parameters填成{rope_theta: 20000}、把vision_configdict 交给CONFIG_MAPPING[timm_wrapper]渲染。推理输入/输出约定与测试佐证输入约定PeVideoModel.forwardmodeling_pe_video.pyinput_ids文本塔的 token id形状(batch_size_text, seq_len)attention_mask文本塔注意力 maskpixel_values_videos视频塔输入(batch_size, num_frames, C, H, W)padding_mask_videos(batch_size, num_frames)1表示真实帧、0表示 padding 帧return_loss置True时计算对比 loss。输出约定PeVideoOutput中含logits_video_text相似度 logits、text_video_embeds、video_embeds、两塔各自的隐藏输出text_outputs/video_outputs以及可选的loss。单元测试 test_modeling_pe_video.py 印证了以上约定测试构造pixel_values_videos与随机合法长度的padding_mask_videos直接调用PeVideoEncoder(pixel_values_videos, padding_mask_videos...)与PeVideoModel(input_ids, pixel_values_videos, attention_mask, padding_mask_videos)并把pixel_values_videos、padding_mask_videos列入模型的额外输入键集合。常见陷阱与使用建议变长视频必须成对使用processor.video_processor(..., return_tensors...)与padding_mask_videos要么同时生效、要么同时缺席。只传张量而忽略 mask会在批量长度不一时让 padding 帧参与注意力GroupNorm 虽然屏蔽了 padding但 Transformer 层需要 mask 配合create_bidirectional_mask才能真正屏蔽注意力。帧数对齐检查点固定长度均匀采样是 PE Video 的默认路径facebook/pe-av-large一类的检查点通常在固定帧数下训练推理时最好使用与训练一致的num_frames。想要 fps 采样需显式不传num_frames此时会退回基类实现。区分两个模型的main_input_name视频塔是pixel_values_videos、完整模型是input_ids。任何读取main_input_name来猜测输入类型的通用代码都必须先弄清拿到的是哪一级对象。文本候选用批量 tokenizequickstart 里把多个标签文本一次 tokenizepaddingTruelogits_video_text的第 0 维对应视频条数、第 1 维对应标签文本数用sigmoid而非softmax标签间不是互斥关系可做多标签式打分。解码后端可选load_video默认pyav加载长视频可考虑显式传backendtorchcodec提升解码效率需额外安装对应依赖见 video_utils.py 的可用后端枚举。PE Video 的接入完整遵循 Transformers 的标准组件划分——PreTrainedConfig配置、PreTrainedModel建模、ProcessorMixin预处理三者解耦同时通过帧级视觉主干 时间序列 Transformer RoPE的组合把时间轴变成了真正参与注意力计算的维度。理解这一结构后无论做零样本分类、视频-文本检索还是后续微调你都能准确地组织输入并预判模型行为。【免费下载链接】transformers Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考