LLM —— 多模态(文本、图片、音频、视频)

目录

一、什么是多模态

二、常见模态分类

三、多模态原理

[单模态 vs 多模态](#单模态 vs 多模态)

四、文本

五、图片

核心原理:

流程:

[① 图像预处理(统一输入格式)](#① 图像预处理(统一输入格式))

[② 加载预训练视觉编码器](#② 加载预训练视觉编码器)

[③ 前向传播,抽取特征向量](#③ 前向传播,抽取特征向量)

[④ 向量归一化(L2 归一化,必做)](#④ 向量归一化(L2 归一化,必做))

[⑤ 持久化存储](#⑤ 持久化存储)

方案代码:

关键细节

[① 向量维度怎么选?](#① 向量维度怎么选?)

[② 相似度计算方式](#② 相似度计算方式)

容易出错点

其他轻量化替代方案

六、音频

核心原理:

1.原始音频数据

2.从时域到频域的转换(频谱图)

3.声音数据怎么和时间关联?

[① 采样率(Sampling Rate):定义时间精度](#① 采样率(Sampling Rate):定义时间精度)

[② 分帧(Framing):建立分析单元](#② 分帧(Framing):建立分析单元)

[4. 25MS与1/44100之间的关系?我人听见的是一个声音嘛?](#4. 25MS与1/44100之间的关系?我人听见的是一个声音嘛?)

5.方案代码:

[方案 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 模型、传感器信号等,每一类都叫一种模态。 多模态,就是模型 / 系统能同时接收、理解、生成、融合两种及以上不同类型的信息,不再只单一处理文字。

二、常见模态分类

  1. 文本模态:聊天文字、文档、代码、指令
  2. 音频模态:人声语音、背景音乐、环境音效
  3. 图像模态:照片、插画、截图、图纸
  4. 视频模态:连续画面 + 同步音频(图像 + 音频组合)
  5. 其他模态:点云、表格、PDF、体感动作、传感器数据

三、多模态原理

单模态 vs 多模态

  • 单模态 LLM:只能看文字,你发图片它看不懂,只能文字问答;
  • 多模态大模型:既能读文字、听语音、看图片、解析视频,还能跨模态互相转换。

多模态模型会用编码器(Encoder)把图片、音频、文字等不同格式信息,统一转换成同一维度的数值向量(嵌入表征),映射到同一个特征空间,让机器能统一对比、关联、推理不同模态信息,实现跨模态理解。

底层数据处理:

  1. 文本 → Text Encoder → Text Embedding
  2. 图片 → Image Encoder → Image Embedding
  3. 音频 → Audio Encoder → Audio Embedding
  4. 视频 → 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 毫秒划定为一个固定的处理单元。

应用:

  1. 语音专属向量(人声、对话、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 学习视频是基于解构-编码-对齐 的原则,将连续的视频流转化为一个它能处理的统一数字语言序列

核销实现思路:

① 拆分成「帧图像 + 音频」分别编码,再融合(工业最常用)

视频拆解:

  1. 均匀抽取关键帧(多张图片)→ 图像编码器(CLIP/SigLIP)得到帧向量
  2. 分离视频音轨 → 音频编码器(CLAP/HuBERT)得到音频向量
  3. 时序聚合 + 特征融合,拼接 / 加权融合成单个全局视频向量

② 端到端视频专用 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 抽帧均值池化」,牺牲时序精度换速度

相关推荐
CCC:CarCrazeCurator1 小时前
线性 RNN 并行计算原理详解
人工智能·深度学习
逸模1 小时前
逸模 VS CAD+SU系列(三)工程量---逸模模型级智能算量,数据同源闭环 助力公装项目精准控本高效拓店
人工智能·笔记·算量·公装·构件库
basketball6161 小时前
AI Infra 硬件体系与编程模型:15. CUDA编程基础:混合精度计算
人工智能·nvidia·cuda
roman_日积跬步-终至千里1 小时前
【AI Engineering】Loop Engineering初探:在不确定性中构造确定性的工程方法
大数据·人工智能
大山佬1 小时前
Linux 内核驱动开发与 BSP 移植:从设备树到内核模块的系统构建
人工智能
思陌Ai算法定制1 小时前
【心血管影像AI预测预后】心肌梗死后心脏MRI如何更早识别高风险患者?
人工智能·影像组学·心血管影像·心脏mri·心肌梗死·stemi·微循环阻塞
云烟成雨TD1 小时前
Agent Scope Java 2.x 系列【6】消息层
java·人工智能·agent
云烟成雨TD1 小时前
Spring AI Alibaba 1.x 系列【74】Agentic RAG 与混合 RAG
java·人工智能·spring
Tiansan66661 小时前
郑州AI问答推广:2家优质服务商剖析
人工智能·郑州ai问答推广2家