4. 视频特征提取(SlowFast)
任务说明
本案例使用PyTorch官方TorchHub提供的预训练SlowFast模型(slowfast_r50)提取视频片段的特征表示,并与I3D模型(i3d_r50)在同一组视频输入上提取的特征进行对比。通过对比两类模型输出的特征向量维度、数值分布及语义区分度,理解双路径帧率架构与三维卷积架构在视频表征上的差异。
环境依赖
torch>=1.9
torchvision
pytorchvideo
av # 视频解码后端
核心实现
import torch
import torch.nn.functional as F
from torchvision.transforms import Compose, Resize, CenterCrop
from pytorchvideo.data.encoded_video import EncodedVideo
from pytorchvideo.transforms import ApplyTransformToKey, ShortSideScale, UniformTemporalSubsample
from torchvision.transforms._transforms_video import NormalizeVideo
# ---------- 配置 ----------
SLOWFAST_ALPHA = 4 # SlowFast的alpha参数(slow路径帧率 = fast路径帧率 / alpha)
NUM_CLASSES = 400 # 预训练类别数(Kinetics-400)
CHECKPOINT_PATH = "slowfast_r50.pyth" # 若本地无缓存会自动下载
# ---------- 视频预处理 ----------
def build_transform(crop_size, num_frames, mode="slowfast"):
"""构建SlowFast或I3D所需的视频预处理流水线"""
if mode == "slowfast":
# SlowFast需要两个不同帧率的输入流
transform = Compose([
ApplyTransformToKey(
key="video",
transform=Compose([
UniformTemporalSubsample(num_frames * SLOWFAST_ALPHA), # 先采样fast路径帧数
ShortSideScale(size=256),
]),
),
])
else: # I3D
transform = Compose([
ApplyTransformToKey(
key="video",
transform=Compose([
UniformTemporalSubsample(num_frames),
ShortSideScale(size=256),
]),
),
])
return transform
def load_video(video_path, num_frames, mode):
"""加载视频并返回预处理后的张量"""
video = EncodedVideo.from_path(video_path, decoder="pyav")
transform = build_transform(crop_size=256, num_frames=num_frames, mode=mode)
clip = video.get_clip(start_sec=0, end_sec=min(8, video.duration))
processed = transform(clip)
return processed["video"] # shape: [C, T, H, W]
# ---------- 模型加载 ----------
def load_slowfast():
"""加载预训练SlowFast R50"""
model = torch.hub.load("facebookresearch/pytorchvideo", "slowfast_r50", pretrained=True)
model.eval()
return model
def load_i3d():
"""加载预训练I3D R50"""
model = torch.hub.load("facebookresearch/pytorchvideo", "i3d_r50", pretrained=True)
model.eval()
return model
# ---------- 特征提取 ----------
def extract_slowfast_features(model, video_tensor):
"""输入video_tensor: [C, T*alpha, H, W],输出SlowFast特征"""
# SlowFast需要将输入拆分为slow/fast两个路径
fast_pathway = video_tensor
# slow路径按alpha间隔采样帧
slow_pathway = video_tensor[:, ::SLOWFAST_ALPHA, :, :]
# 标准化(ImageNet统计)
mean = torch.tensor([0.45, 0.45, 0.45]).view(3, 1, 1)
std = torch.tensor([0.225, 0.225, 0.225]).view(3, 1, 1)
slow_pathway = (slow_pathway - mean) / std
fast_pathway = (fast_pathway - mean) / std
# 输入模型
with torch.no_grad():
features = model([slow_pathway, fast_pathway]) # 返回预分类特征
return features
def extract_i3d_features(model, video_tensor):
"""输入video_tensor: [C, T, H, W],输出I3D特征"""
mean = torch.tensor([0.45, 0.45, 0.45]).view(3, 1, 1)
std = torch.tensor([0.225, 0.225, 0.225]).view(3, 1, 1)
video_tensor = (video_tensor - mean) / std
with torch.no_grad():
features = model(video_tensor)
return features
# ---------- 主流程 ----------
def compare_features(video_path):
"""提取并对比SlowFast与I3D特征"""
# 1. 加载模型
sf_model = load_slowfast()
i3d_model = load_i3d()
# 2. 加载视频(SlowFast需要更多帧)
sf_frames = 32 # fast路径帧数
i3d_frames = 32
sf_video = load_video(video_path, sf_frames, mode="slowfast")
i3d_video = load_video(video_path, i3d_frames, mode="i3d")
# 3. 提取特征
sf_feat = extract_slowfast_features(sf_model, sf_video) # [1, 2304]
i3d_feat = extract_i3d_features(i3d_model, i3d_video) # [1, 2048]
# 4. 对比分析
print(f"SlowFast特征维度: {sf_feat.shape[1]}")
print(f"I3D特征维度: {i3d_feat.shape[1]}")
print(f"SlowFast特征均值: {sf_feat.mean().item():.4f}, 标准差: {sf_feat.std().item():.4f}")
print(f"I3D特征均值: {i3d_feat.mean().item():.4f}, 标准差: {i3d_feat.std().item():.4f}")
# 5. 余弦相似度(验证语义一致性)
cos_sim = F.cosine_similarity(sf_feat, i3d_feat, dim=1)
print(f"SlowFast与I3D特征余弦相似度: {cos_sim.item():.4f}")
return sf_feat, i3d_feat
if __name__ == "__main__":
# 使用示例视频(可替换为本地视频路径)
video_path = "sample_video.mp4"
sf_feat, i3d_feat = compare_features(video_path)
运行与验证
- 准备视频:将任意短视频(建议5~10秒,MP4格式)命名为
sample_video.mp4放在脚本同目录。 - 执行脚本:
python compare_slowfast_i3d.py(首次运行会自动下载预训练权重,约200MB)。 - 通过条件:
- 程序无报错完成特征提取;
- SlowFast输出维度为
2304(含slow+fast路径拼接),I3D输出维度为2048; - 两类特征均值在0附近(因已做归一化),标准差在0.5~1.5之间;
- 余弦相似度通常为0.3~0.7(不同视频有差异),表明两者捕捉到部分共享语义但表征方式不同。
关键说明
- SlowFast的
alpha=4意味着fast路径以全帧率采样,slow路径每4帧取1帧,模型内部通过侧向连接融合两条路径信息,最终输出特征维度为2304(slow路径2048维 + fast路径256维拼接)。 - I3D使用统一的3D卷积核处理全部帧,输出单一2048维特征。
- 本实现使用
pytorchvideo的预训练权重,输入需为[C, T, H, W]格式且像素值在[0,1]范围,内部会做归一化处理。 - 若需提取中间层特征(而非最终分类前特征),可修改模型返回点,但本案例聚焦于标准特征对比。