目录
[单模态 vs 多模态](#单模态 vs 多模态)
[① 图像预处理(统一输入格式)](#① 图像预处理(统一输入格式))
[② 加载预训练视觉编码器](#② 加载预训练视觉编码器)
[③ 前向传播,抽取特征向量](#③ 前向传播,抽取特征向量)
[④ 向量归一化(L2 归一化,必做)](#④ 向量归一化(L2 归一化,必做))
[⑤ 持久化存储](#⑤ 持久化存储)
[① 向量维度怎么选?](#① 向量维度怎么选?)
[② 相似度计算方式](#② 相似度计算方式)
[① 采样率(Sampling Rate):定义时间精度](#① 采样率(Sampling Rate):定义时间精度)
[② 分帧(Framing):建立分析单元](#② 分帧(Framing):建立分析单元)
[4. 25MS与1/44100之间的关系?我人听见的是一个声音嘛?](#4. 25MS与1/44100之间的关系?我人听见的是一个声音嘛?)
[方案 1:HuBERT 人声音频向量化(语音场景首选)](#方案 1:HuBERT 人声音频向量化(语音场景首选))
[方案 2:CLAP 音频 - 文本跨模态向量化(语音 RAG 最强)](#方案 2:CLAP 音频 - 文本跨模态向量化(语音 RAG 最强))
[① 池化方式选择](#① 池化方式选择)
[② 相似度计算](#② 相似度计算)
[方案 1:CLIP 帧抽取 + CLAP 音频融合(最简通用,跨模态对齐)](#方案 1:CLIP 帧抽取 + CLAP 音频融合(最简通用,跨模态对齐))
[方案2:X-CLIP(Video Transformer 编码器抽取视频向量)](#方案2:X-CLIP(Video Transformer 编码器抽取视频向量))
[完整多模态视频编码器方案(视觉 Transformer + 音频编码器融合)](#完整多模态视频编码器方案(视觉 Transformer + 音频编码器融合))
一、什么是多模态
模态 就是信息的载体形式:文字、语音、图片、视频、表格、3D 模型、传感器信号等,每一类都叫一种模态。 多模态,就是模型 / 系统能同时接收、理解、生成、融合两种及以上不同类型的信息,不再只单一处理文字。
二、常见模态分类
- 文本模态:聊天文字、文档、代码、指令
- 音频模态:人声语音、背景音乐、环境音效
- 图像模态:照片、插画、截图、图纸
- 视频模态:连续画面 + 同步音频(图像 + 音频组合)
- 其他模态:点云、表格、PDF、体感动作、传感器数据
三、多模态原理
单模态 vs 多模态
- 单模态 LLM:只能看文字,你发图片它看不懂,只能文字问答;
- 多模态大模型:既能读文字、听语音、看图片、解析视频,还能跨模态互相转换。
多模态模型会用编码器(Encoder)把图片、音频、文字等不同格式信息,统一转换成同一维度的数值向量(嵌入表征),映射到同一个特征空间,让机器能统一对比、关联、推理不同模态信息,实现跨模态理解。
底层数据处理:
- 文本 → Text Encoder → Text Embedding
- 图片 → Image Encoder → Image Embedding
- 音频 → Audio Encoder → Audio Embedding
- 视频 → Video Encoder → Video Embedding
这些 Embedding 最终进入同一个向量空间
文本 / 图片 / 音频 / 视频
↓
不同编码器
↓
统一向量空间
↓
向量数据库
↓
多模态检索
↓
大模型生成回答
四、文本
Markdown / PDS / 文档 -> 文本切片 -> Embedding -> 向量数据库
五、图片
图片 -> 图像编码模型 - > 图像向量 -> 向量数据库
核心原理:
①原始图片是三维张量:【H高度,W宽度,3通道RGB】,RGB像素值 0 - 255.
② 用预训练 CNN / 视觉大模型(ResNet、CLIP、SigLIP 等)做特征提取;
③ 去掉最后分类全连接层,取出模型中间输出的特征张量,做池化压缩成一维定长向量;
④ 可选归一化,存入向量数据库(Milvus、FAISS、Chroma、Redis)做检索。
流程:
① 图像预处理(统一输入格式)
模型对输入图片有强制规范,必须标准化:
<1> 缩放裁剪:统一尺寸(如 224×224、384×384);
<2> 像素归一化:像素值从0~255映射到0~1,再用数据集均值、方差标准化;
<3> 通道调整:PIL 图片默认 RGB,排除 Alpha 透明通道;
<4> 转为模型要求张量格式:[batch, 3, H, W](PyTorch NCHW 格式)。
② 加载预训练视觉编码器
两大技术路线:
<1> 传统 CNN(ResNet/EfficientNet):只提取视觉特征,无法和文本向量对齐;
<2> CLIP/SigLIP(跨模态对齐模型):图片向量、文字向量处在同一个特征空间,图文互搜首选,工业主流。
③ 前向传播,抽取特征向量
<1> CNN:取全局平均池化 GAP 后的输出,直接得到一维向量;
<2> CLIP:调用image_encoder,返回图像 Embedding。
④ 向量归一化(L2 归一化,必做)
向量每个元素除以自身 L2 范数,向量模长 = 1。 归一化后用余弦相似度计算图片相似度 等价于内积,向量检索速度大幅提升。
⑤ 持久化存储
定长浮点数组(如 768 维、512 维)存入向量库,后续可:
<1> 以图搜图
<2> 输入文字,转文本向量,检索相似图片。
方案代码:
方案1:CLIP 图片向量化(推荐,图文跨模态对齐)
python
from PIL import Image
import torch
import numpy as np
from transformers import CLIPProcessor, CLIPModel
# 1. 加载模型与预处理工具
model_name = "openai/clip-vit-base-patch32"
model = CLIPModel.from_pretrained(model_name)
processor = CLIPProcessor.from_pretrained(model_name)
model.eval()
# 2. 加载图片
img = Image.open("test.jpg").convert("RGB")
# 3. 预处理
inputs = processor(images=img, return_tensors="pt")
# 4. 推理提取图像向量(禁用梯度,提速省显存)
with torch.no_grad():
img_emb = model.get_image_features(**inputs)
# 5. L2归一化
img_emb = img_emb / img_emb.norm(p=2, dim=-1, keepdim=True)
# 6. 转为numpy一维向量(可直接入库)
vec = img_emb.squeeze(0).numpy()
print("向量维度:", vec.shape) # clip-vit-base-patch32 输出512维
print("前10个向量值:", np.round(vec[:10], 4))
方案2:ResNet 传统 CNN 图片向量化(纯视觉检索)
只提取图像特征,不能和文本匹配:
python
from PIL import Image
import torch
import torchvision.models as models
import torchvision.transforms as T
import numpy as np
# 1. 加载预训练ResNet,去掉最后分类层
resnet = models.resnet50(pretrained=True)
resnet.eval()
# 截取到平均池化层,输出特征
feature_extractor = torch.nn.Sequential(*list(resnet.children())[:-1])
# 2. 标准化预处理
transform = T.Compose([
T.Resize((224, 224)),
T.ToTensor(),
T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
# 3. 图片处理+向量化
img = Image.open("test.jpg").convert("RGB")
img_tensor = transform(img).unsqueeze(0)
with torch.no_grad():
feat = feature_extractor(img_tensor)
# 展平+归一化
vec = feat.squeeze().numpy()
vec = vec / np.linalg.norm(vec)
print("ResNet向量维度:", vec.shape) # 2048维
关键细节
① 向量维度怎么选?
CLIP ViT-B/32:512 维(通用、速度快)
CLIP ViT-L/14:768 维(精度更高,占用存储翻倍)
ResNet50:2048 维,向量体积大,检索慢
② 相似度计算方式
归一化后两个图片向量vec1、vec2: 余弦相似度 = np.dot(vec1, vec2) 值越接近 1,图片内容越相似。
容易出错点
① 直接用像素数组当向量:像素受亮度、缩放、平移干扰,完全无法语义匹配;
② 不归一化就入库:余弦相似度计算失效,检索结果错乱;
③ 训练模式提取特征:没有torch.no_grad(),显存暴涨、向量波动不稳定。
其他轻量化替代方案
① OpenCLIP:优化版 CLIP,支持更多中文场景;
② Sentence-Transformers 封装的 CLIP:一行代码完成图片向量化;
③ 阿里云 / 腾讯云图片向量 API:无需本地部署模型,在线调用直接返回向量。
六、音频
音频向量化:把时域波形 / 频谱音频文件,编码成固定长度一维浮点嵌入向量(Embedding)。语义、音色、内容相近的音频,向量在特征空间距离更近,用于音频检索、声纹识别、语音 RAG、语音相似度比对、语音分类。
音频文件 → 重采样 + 单声道预处理 → CLAP/HuBERT 编码音频向量 → 归一化 → Milvus 向量库存储
① 上传新音频:编码向量,相似度检索召回相似音频片段;
② 输入文字描述:文本编码成同空间向量,直接检索对应音频。
核心原理:
音频数据化表示的整个过程,就像是把一个连续的、抽象的物理现象(声音) ,翻译成一个离散的、结构化的数字表格。
1.原始音频数据
音频原始采样是一维时间序列:[采样点数,],数值是声波振幅,没法直接做相似度计算。
常见参数:采样率 16kHz、单声道、16bit 量化。(原始波形图)
2.从时域到频域的转换(频谱图)
核心工具: 傅里叶变换(FFT)
转换目的: FFT 将一段很短的声音(例如 25 毫秒)分解为构成它的所有基本频率 及其强
-
数据形态: 这产生了 频谱图(Spectrogram)
-
X 轴(时间): 保持不变,代表声音的持续时间。
-
Y 轴(频率): 替换了振幅,代表声音的音高(低频是低音,高频是高音)。
-
颜色/亮度: 代表该频率的能量或强度(响度)。
-
-
你可以这样理解: 这就像是把一首歌(波形图)分解成了所有单独的乐器及其各自的音量(频谱图)。
-
频谱图是一种二维的、像图像一样的数字表格。
转换提取特征
波形信号转梅尔频谱 MFCC、梅尔语谱图 Mel-Spectrogram(CNN/Transformer 音频模型标准输入)
预训练音频编码器(Audio Encoder)映射为 Embedding 向量 音频特征送入预训练模型,去掉分类头,抽取全局特征,压缩成定长一维向量,可选 L2 归一化,存入向量库。
( 编码器对频谱图进行分帧,然后使用自监督学习将每一小块(一小段时间和频率范围)的特征压缩成一个高维的向量 )

最终进入 LLM 的就是这个序列,它与文本的 Tokens 序列是同一种结构。

3.声音数据怎么和时间关联?
在 LLM 处理的音频数据中,连续的数字序列(声学特征向量序列)**是通过采样(Sampling)和分帧(Framing)**这两个步骤,被硬性地和时间关联起来的。
LLM 最终接收的音频数据形态是 \\text{向量}_{1}, \\text{向量}_{2}, \\text{向量}_{3}, \\ldots ,这个序列之所以能代表时间,是因为在预处理阶段,我们建立了固定的时间参考系。
① 采样率(Sampling Rate):定义时间精度
原始的连续声波必须转化为离散的数字信号。采样率定义了时间上的最小分辨率。
<1> 机制: 计算机在每秒钟对声音的振幅(响度)进行固定次数的测量。
例如,CD 音质的采样率是 44,100 Hz,意味着每秒测量 44,100 次。
<2> 关联 : 这意味着序列中的每一个原始数字点都代表了 1/44100 秒的时间。这建立了数据点和绝对时间之间的基础关联。
② 分帧(Framing):建立分析单元
在生成频谱图和声学特征向量之前,我们不会单独分析每一个采样点,而是将一小段连续的采样点打包成一个"帧"。
<1> 机制 :将音频流切割成许多小的、有重叠的短帧(例如 25 毫秒)
<2> 关联 : 每一个最终的特征向量 都是从一个 25 毫秒的短帧中提取出来的
<3> 序列与时间: 由于这些帧是沿着原始时间轴顺序排列的
-
V1代表 0 毫秒到 25 毫秒的声音特征。
-
V2代表 10 毫秒到 35 毫秒的声音特征(通常帧之间有重叠)。
-
这个向量序列的长度 直接反映了音频的持续时间。

总结: 连续的数字序列之所以能跟时间关联,是因为我们通过固定的采样率和固定长度的分帧,将连续的时间流转化成了离散的、等间隔的数字单位。
4. 25MS与1/44100之间的关系?我人听见的是一个声音嘛?
您的耳朵听到的声音在 25 毫秒内是连续的,但这并不妨碍计算机为了分析和识别,而将这 25 毫秒划定为一个固定的处理单元。




应用:
- 语音专属向量(人声、对话、ASR 配套 RAG):HuBERT、Wav2Vec2、Whisper(两种用法)
音频 -> ASR 语音识别 -> 文本 -> 文本向量化
2.跨模态对齐(音频↔文字) :CLAP(音频版 CLIP),音频向量和文本向量同特征空间,语音搜文字、文字搜音频
音频不只有文字,还有声音特征。
比如:音色、语速、情绪、背景声音、音乐节奏、环境噪音
音频波形 -> 音频编码模型 -> 音频向量
| 模型 | 输入 | 向量用途 | 向量维度 | 特点 |
|---|---|---|---|---|
| VGGish | 梅尔频谱 | 音乐、环境音效检索 | 128 维 | 轻量老牌,仅音频内比对 |
| Wav2Vec2 / HuBERT | 原始波形 | 人声、语音内容表征 | 768 维 | Facebook 语音专用,ASR 配套首选 |
| Whisper | 梅尔频谱 | 语音识别 + 同时提取音频特征 | 1024 维 | 可同时转文字 + 出向量 |
| CLAP | 原始波形 | 音频 - 文本跨模态检索 | 512/768 维 | 音频、文本向量同空间,语音 RAG 必备 |
5.方案代码:
环境:
bash
pip install torch transformers librosa soundfile numpy
方案 1:HuBERT 人声音频向量化(语音场景首选)
只做人声内容向量化,适合语音知识库、语音片段检索
python
import librosa
import torch
import numpy as np
from transformers import HubertModel, Wav2Vec2Processor
# 1. 加载模型&处理器
model_name = "facebook/hubert-base-ls960"
processor = Wav2Vec2Processor.from_pretrained(model_name)
model = HubertModel.from_pretrained(model_name)
model.eval()
# 2. 加载音频,强制重采样到16kHz(模型固定输入)
audio_path = "voice.wav"
waveform, sr = librosa.load(audio_path, sr=16000)
# 3. 预处理
inputs = processor(
waveform,
sampling_rate=16000,
return_tensors="pt"
)
# 4. 推理提取特征,取全局均值池化得到单条向量
with torch.no_grad():
outputs = model(**inputs)
# last_hidden_state: [batch, time_step, hidden_dim]
hidden_states = outputs.last_hidden_state
# 时间维度均值池化,压缩为一维向量
audio_emb = torch.mean(hidden_states, dim=1)
# 5. L2归一化(向量检索必做)
audio_emb = audio_emb / torch.norm(audio_emb, p=2, dim=-1, keepdim=True)
vec = audio_emb.squeeze(0).numpy()
print(f"向量维度:{vec.shape}")
方案 2:CLAP 音频 - 文本跨模态向量化(语音 RAG 最强)
音频向量和文本向量处在同一特征空间,支持:文字描述搜音频、音频搜匹配文本
python
import librosa
import torch
import numpy as np
from transformers import ClapModel, ClapProcessor
model_name = "laion/clap-htsat-unfused"
processor = ClapProcessor.from_pretrained(model_name)
model = ClapModel.from_pretrained(model_name)
model.eval()
# 加载音频
waveform, sr = librosa.load("audio.wav", sr=48000)
# 音频编码
audio_inputs = processor(audios=waveform, sampling_rate=48000, return_tensors="pt")
with torch.no_grad():
audio_emb = model.get_audio_features(**audio_inputs)
# 归一化
audio_emb = audio_emb / audio_emb.norm(p=2, dim=-1, keepdim=True)
audio_vec = audio_emb.squeeze(0).numpy()
# 同步提取文本向量,可直接余弦匹配
text_inputs = processor(text=["一段人声说话"], return_tensors="pt")
with torch.no_grad():
text_emb = model.get_text_features(**text_inputs)
text_emb = text_emb / text_emb.norm(p=2, dim=-1, keepdim=True)
text_vec = text_emb.squeeze(0).numpy()
# 计算音频-文本相似度
sim = np.dot(audio_vec, text_vec)
print("音频文本余弦相似度:", sim)
关键细节
① 池化方式选择
模型输出是时序特征 [时间步, 隐藏维度],必须压缩成单个定长向量:
- 均值池化(推荐):整段音频全局表征,抗时长变化
- 首尾 token:仅取第一个时间步,短音频尚可,长音频效果差
② 相似度计算
归一化后,余弦相似度 = 向量内积 np.dot(vec1, vec2),值域 -1,1,越接近 1 音频内容越相似。
容易出错点
- 采样率不匹配:Wav2Vec/HuBERT 强制 16kHz,音频不重采样向量完全失效
- 不关闭梯度计算:
torch.no_grad()遗漏,显存占用极高、推理缓慢 - 多声道音频:必须转单声道,模型仅支持单通道波形输入
- 超长音频不截断:10 分钟以上长音频时序过长,内存溢出,建议分段编码、分段入库
其他轻量化替代方案
① 无需本地 GPU:调用阿里云、百度智能云音频向量 API,直接 HTTP 接口返回向量;
② 批量处理:循环遍历音频文件夹,批量生成向量批量导入 FAISS/Milvus。
七、视频
核心原理:
视频的本质 = 1s 60张图 + 声音
LLM理解视频就是理解 图片+声音
怎么理解时间线?
transformer天生自左向右????
给LLM学习向量(图片1、音频1、图片2、音频2、图片3、音频3)
LLM 学习并识别视频是一个复杂的多模态任务,因为它必须同时处理空间信息(图像内容)、时间信息(动作变化)以及音频信息(声音内容) 。它通过将视频的不同维度分解并转化为统一的向量序列来实现学习。
LLM 处理视频的关键在于时空分解和跨模态对齐。
A. 空间编码 (Spatial Encoding - 内容)
处理: 视频的每一帧都被视为一张独立的图像。它通过 Vision Transformer (ViT) 类似的架构,被切分成 图像块(Patches),并转化为视觉特征向量序列。
目的: 识别视频中每一瞬间的内容,例如画面中的人、物体、背景等。
B. 时间编码 (Temporal Encoding - 动作)
处理: 专门的时间编码器(或基于 Transformer 的时序层)会分析连续多帧的特征向量序列。
目的: 学习帧与帧之间的变化关系和动作流。这使得模型能够识别出事件和动作(如"跑动"、"说话"、"物体移动"),而不是孤立的静态图像。
音频编码与多模态同步
视频通常包含音频流,这需要单独处理并与视觉信息对齐:
音频编码: 视频中的声音被提取,经过采样、分帧 等处理,转化为声学特征向量序列(类似于 LLM 学习音频的过程)。
同步对齐: LLM 通过训练学习将视觉动作 (如嘴唇的开合序列)与音频序列(如说话的声音)进行同步关联。这使得模型能进行语音识别、唇语解读以及理解背景音。
统一的时空序列处理
经过编码,LLM 的核心 Transformer 接收到的是一个巨大的、复杂的时空序列:
\\text{LLM 输入} = \[\\text{文本指令}\] + \[\\text{帧 1 视觉/时间特征}\] + \[\\text{帧 2 视觉/时间特征}\] + \\ldots + \[\\text{音频特征}\]
-
自注意力机制: 在这个统一的序列中,模型的自注意力机制 必须同时在 图像块之间、时间帧之间、以及视觉和文本之间****计算关联性。
-
推理生成: 模型在理解了所有输入模态后,能够生成复杂的推理答案,例如:"根据视频中人物的动作和声音,他们接下来可能会去厨房。"
总结来说,LLM 学习视频是基于解构-编码-对齐 的原则,将连续的视频流转化为一个它能处理的统一数字语言序列
核销实现思路:
① 拆分成「帧图像 + 音频」分别编码,再融合(工业最常用)
视频拆解:
- 均匀抽取关键帧(多张图片)→ 图像编码器(CLIP/SigLIP)得到帧向量
- 分离视频音轨 → 音频编码器(CLAP/HuBERT)得到音频向量
- 时序聚合 + 特征融合,拼接 / 加权融合成单个全局视频向量
② 端到端视频专用 Transformer 模型(直接输入视频片段)
VideoCLIP、X-CLIP、InternVideo、TimeSformer 等视频原生模型,一次性输入连续帧序列,自带时序建模,直接输出全局视频向量,画面动态变化、镜头运动、时序逻辑都能捕捉。
标准流程:
① 视频解析:ffmpeg 提取视频流、音频流;按固定间隔抽关键帧(每秒 1 帧 / 每 3 秒 1 帧,避免冗余)
② 模态预处理
<1> 帧:统一分辨率、归一化,适配图像编码器
<2> 音频:重采样到 16kHz/48kHz、转单声道
③ 分模态编码得到子向量
<1> 多帧图像:每帧单独编码向量,做时序均值 / 最大池化,得到画面全局向量
<2> 音轨:编码得到音频全局向量
④ 特征融合
方式 1:直接拼接(画面向量 + 音频向量,维度叠加)
方式 2:加权求和(设置画面权重 α、音频权重 1-α)
方式 3:全连接层映射到统一固定维度
⑤ L2 归一化,最终得到单个定长视频向量
⑥ 存入 Milvus/FAISS 做向量检索
代码方案:
方案 1:CLIP 帧抽取 + CLAP 音频融合(最简通用,跨模态对齐)
优势:不用训练视频模型,复用成熟图文、音文对齐模型,视频向量还能和文本向量做跨模态检索(文字搜视频)。
bash
pip install torch transformers opencv-python librosa soundfile ffmpeg-python numpy
python
import cv2
import librosa
import torch
import numpy as np
from transformers import CLIPModel, CLIPProcessor, ClapModel, ClapProcessor
# 设备
device = "cuda" if torch.cuda.is_available() else "cpu"
# 1. 加载图像、音频编码模型
clip_name = "openai/clip-vit-base-patch32"
clip_model = CLIPModel.from_pretrained(clip_name).eval().to(device)
clip_processor = CLIPProcessor.from_pretrained(clip_name)
clap_name = "laion/clap-htsat-unfused"
clap_model = ClapModel.from_pretrained(clap_name).eval().to(device)
clap_processor = ClapProcessor.from_pretrained(clap_name)
def extract_video_frames(vid_path, sample_rate=1):
"""抽帧:每秒取1帧"""
cap = cv2.VideoCapture(vid_path)
fps = cap.get(cv2.CAP_PROP_FPS)
frame_interval = int(fps / sample_rate)
frames = []
idx = 0
while True:
ret, frame = cap.read()
if not ret:
break
if idx % frame_interval == 0:
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frames.append(rgb)
idx += 1
cap.release()
return frames
# ---------------------- 1. 画面向量化 ----------------------
video_path = "test.mp4"
frames = extract_video_frames(video_path, sample_rate=1)
# 逐帧编码
frame_embs = []
with torch.no_grad():
for img in frames:
inputs = clip_processor(images=img, return_tensors="pt").to(device)
emb = clip_model.get_image_features(**inputs)
emb = emb / emb.norm(p=2, dim=-1, keepdim=True)
frame_embs.append(emb)
# 时序均值池化,得到全局画面向量
frame_stack = torch.cat(frame_embs, dim=0)
vis_emb = frame_stack.mean(dim=0, keepdim=True)
# ---------------------- 2. 音频向量化 ----------------------
audio_wave, sr = librosa.load(video_path, sr=48000)
audio_in = clap_processor(audios=audio_wave, sampling_rate=48000, return_tensors="pt").to(device)
with torch.no_grad():
aud_emb = clap_model.get_audio_features(**audio_in)
aud_emb = aud_emb / aud_emb.norm(p=2, dim=-1, keepdim=True)
# ---------------------- 3. 融合得到最终视频向量 ----------------------
# 方式1:拼接融合
video_emb_cat = torch.cat([vis_emb, aud_emb], dim=-1)
# L2归一化
final_emb = video_emb_cat / video_emb_cat.norm(p=2, dim=-1, keepdim=True)
video_vec = final_emb.squeeze(0).cpu().numpy()
print(f"最终视频向量维度:{video_vec.shape}")
方案2:X-CLIP(Video Transformer 编码器抽取视频向量)
bash
pip install torch transformers opencv-python pillow numpy
python
import cv2
import torch
import numpy as np
from transformers import XCLIPProcessor, XCLIPModel
device = "cuda" if torch.cuda.is_available() else "cpu"
# 加载X-CLIP:视频专用Transformer编码器
model_name = "microsoft/xclip-base-patch32-frames16"
processor = XCLIPProcessor.from_pretrained(model_name)
model = XCLIPModel.from_pretrained(model_name).eval().to(device)
def sample_video_frames(vid_path, num_frames=16):
"""均匀采样固定帧数,适配X-CLIP输入要求"""
cap = cv2.VideoCapture(vid_path)
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
frame_indices = np.linspace(0, total_frames-1, num_frames, dtype=int)
frames = []
for idx in frame_indices:
cap.set(cv2.CAP_PROP_POS_FRAMES, idx)
ret, frame = cap.read()
if ret:
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frames.append(rgb)
cap.release()
return frames
# 1. 读取视频+采样帧
frames = sample_video_frames("test.mp4", num_frames=16)
# 2. Transformer编码器预处理
inputs = processor(
videos=frames,
return_tensors="pt"
).to(device)
# 3. Video Transformer前向推理,提取视频全局向量
with torch.no_grad():
out = model(**inputs)
video_emb = out.video_embeds
# L2归一化
video_emb = video_emb / video_emb.norm(p=2, dim=-1, keepdim=True)
video_vec = video_emb.squeeze(0).cpu().numpy()
print(f"Video Transformer输出向量维度:{video_vec.shape}")
关键细节
① 相比「CLIP 抽帧 + 手动融合」的优缺点
优点:
<1> Transformer 原生建模时序运动,能区分「人抬手→人放下手」这类动态差异;逐帧 CLIP 只能看单张画面,分不清动作顺序
<2> 端到端一键输出视频向量,不用分别处理画面、音频、手动拼接加权
<3> X-CLIP 等双塔结构天然和文本向量对齐,直接支持文搜视频
缺点:
<1> 输入必须固定帧数,长视频需要切片
<2> 参数量更大,推理速度比单帧 CLIP 慢
<3> 不自带音频建模,纯视觉 Video Transformer 听不到视频声音
完整多模态视频编码器方案(视觉 Transformer + 音频编码器融合)
真实业务落地标准链路: 视频 → 视觉流送入X-CLIP(Video Transformer) 得到视觉向量 视频 → 分离音轨送入 CLAP 得到音频向量 向量加权 / 拼接融合 → 归一化 → 最终完整多模态视频向量入库
落地选型:
-
仅视觉检索、动作识别:TimeSformer
-
视频检索、文搜视频、视频 RAG:X-CLIP(首选)
-
长视频精读、视频问答:InternVideo2
-
轻量化场景:依旧可以用「CLIP 抽帧均值池化」,牺牲时序精度换速度