AI视频理解:从标签生成到内容摘要的算法选型与后端服务设计

一、背景与问题定义

用户上传完视频后,平台需要知道这个视频"讲了什么"——这是搜索召回、推荐分发、内容审核和标签体系的基础。传统做法依赖创作者手动填写标题、标签和描述,但三个问题始终存在:填写率低(长尾创作者只有 40% 会认真填写)、质量参差不齐("好视频"这种无效标签占比超过 15%)、以及维度单一(手动标签很难覆盖视频的全部内容要素)。

AI 视频理解的目标是:在无人干预的情况下,自动产出结构化的视频理解结果,包括多层级标签、关键场景的时间戳、时序动作识别,以及一段 100~200 字的内容摘要。

本文从模型选型、特征提取管线、批量异步架构三个维度展开。

二、模型选型与整体架构

2.1 算法选型

视频理解涉及三个层次,分别对应不同模型:

层次任务推荐模型理由
视觉特征提取理解画面内容CLIP-ViT-L/14图文对齐能力强,标签语义匹配好
时序动作识别理解"发生了什么"VideoMAE预训练效果好,小样本微调即可适配
文本摘要生成综合多模态信息产出文字Qwen2-VL-7B多模态大模型,端到端理解+生成

2.2 整体架构

三、特征提取管线的详细实现

3.1 关键帧抽取

不是均匀抽帧,而是基于场景切换检测的智能抽帧。使用 FFmpeg 的 scenechange 过滤器检测画面突变:

ffmpeg -i input.mp4 \
  -vf "select='gt(scene,0.3)',scale=384:384" \
  -vsync vfr keyframes_%04d.jpg

场景切换阈值设为 0.3,能捕获约 90% 的转场。一个 10 分钟的视频通常产出 40~80 张关键帧。

3.2 CLIP 特征提取服务

import torch
from PIL import Image
from transformers import CLIPProcessor, CLIPModel
import asyncio
from concurrent.futures import ThreadPoolExecutor

class CLIPFeatureExtractor:
    def __init__(self, model_path: str, device: str = "cuda"):
        self.model = CLIPModel.from_pretrained(model_path).to(device).eval()
        self.processor = CLIPProcessor.from_pretrained(model_path)
        self.device = device
        self.executor = ThreadPoolExecutor(max_workers=4)
    
    async def extract_batch(self, image_paths: list[str]) -> list[torch.Tensor]:
        """批量提取图像特征,返回归一化后的 Embedding"""
        loop = asyncio.get_event_loop()
        
        def _infer():
            images = [Image.open(p).convert("RGB") for p in image_paths]
            inputs = self.processor(images=images, return_tensors="pt").to(self.device)
            with torch.no_grad():
                image_features = self.model.get_image_features(**inputs)
            return torch.nn.functional.normalize(image_features, p=2, dim=1)
        
        features = await loop.run_in_executor(self.executor, _infer)
        return [features[i] for i in range(len(image_paths))]

3.3 标签生成

标签生成分为两个步骤:标签召回和标签排序。

标签召回:将 CLIP 提取的视频 Embedding 与标签体系中所有候选标签的文本 Embedding 做余弦相似度计算,召回 Top 50 候选标签。

def recall_tags(video_embedding: torch.Tensor, 
                tag_embeddings: dict[str, torch.Tensor], 
                top_k: int = 50) -> list[tuple[str, float]]:
    """基于余弦相似度的标签召回"""
    scores = {}
    for tag, tag_emb in tag_embeddings.items():
        similarity = torch.cosine_similarity(video_embedding, tag_emb, dim=0).item()
        scores[tag] = similarity
    
    return sorted(scores.items(), key=lambda x: x[1], reverse=True)[:top_k]

标签排序:召回结果送入一个轻量排序模型(XGBoost + 多模态特征),综合考虑视觉相似度、文本描述匹配度和标签共现频率,产出最终的 5~8 个标签。

3.4 视频摘要生成

摘要生成使用 Qwen2-VL-7B 多模态大模型。输入包括:5~8 张关键帧、ASR 转文本结果、以及标签信息。Prompt 设计:

你是一个视频内容分析专家。以下是视频的关键信息:
- 标签:{tags}
- 语音文本:{asr_text}
- 关键帧:[5~8张图片]

请基于以上信息生成一段 100-200 字的中文视频内容摘要,
要求:客观描述视频内容,不添加主观评价,
包含:视频主题、主要内容环节、关键人物或场景。

关键工程优化:摘要生成不要求强实时,对所有用户返回 200ms 以内的响应,摘要结果异步写入并在生成完成后通过推送通知前端更新。

四、批量异步架构与回调机制

4.1 异步任务编排

视频理解的整体延迟在 30~90 秒(取决于视频时长和模型排队),不能用同步 HTTP 等待。架构设计为全异步:

@Service
public class VideoUnderstandingService {
    
    @Autowired
    private FeatureExtractionOrchestrator featureOrchestrator;
    @Autowired
    private TagGenerationService tagService;
    @Autowired
    private SummaryGenerationService summaryService;
    @Autowired
    private VideoUnderstandingRepository repository;
    
    @KafkaListener(topics = "video.upload.completed")
    public void onVideoUploaded(VideoUploadedEvent event) {
        // 1. 特征提取(关键帧 + ASR)
        FeatureResult features = featureOrchestrator.extractAll(event.getVideoId());
        
        // 2. 标签生成(同步,依赖特征)
        List<Tag> tags = tagService.generate(features);
        
        // 3. 摘要生成(异步,提交到任务队列)
        CompletableFuture<String> summaryFuture = 
            summaryService.generateAsync(features, tags);
        
        // 4. 先保存标签结果,立即可用
        repository.saveUnderstanding(event.getVideoId(), 
            VideoUnderstanding.builder()
                .tags(tags)
                .keyframeTimestamps(features.getKeyframeTimestamps())
                .actionSegments(features.getActionSegments())
                .summary("生成中...")  // placeholder
                .build());
        
        // 5. 摘要完成后回调更新
        summaryFuture.thenAccept(summary -> {
            repository.updateSummary(event.getVideoId(), summary);
            notifyService.pushUpdate(event.getUserId(), event.getVideoId());
        }).exceptionally(ex -> {
            log.error("Summary generation failed for {}", event.getVideoId(), ex);
            repository.updateSummary(event.getVideoId(), "摘要生成失败");
            return null;
        });
    }
}

4.2 批量处理优化

对于新上传的视频,不是每个都单独启动推理管线,而是做批次聚合。聚合策略:每 30 秒或累积 20 条任务时,将同类型的推理任务(如 CLIP 特征提取)打包为一个批次,送入 GPU 做批量推理。

批量推理的吞吐是单条推理的 8~12 倍——因为 GPU 的矩阵乘法在 batch 维度上高度并行,batch_size=32 时 GPU 利用率从 35% 提升到 85%。

五、总结

AI 视频理解的工程挑战不在模型本身,而在"如何在成本和延迟约束下稳定地服务于百万级视频"。关键设计选择:关键帧智能抽取将处理帧数控制在合理范围;CLIP+VideoMAE 的组合覆盖了空间和时间两个维度;批量聚合推理充分利用 GPU 算力;全异步架构避免了长延迟对用户体验的影响。

后续方向:引入视频大模型(Video-LLaMA、Gemini)做更细粒度的理解(如"视频中第 3 分钟出现了一辆红色轿车");利用强化学习优化摘要质量(以用户点击率作为 reward);构建跨视频的语义链接("这个视频是那三个视频的后续内容"),为推荐和搜索提供更丰富的结构化信息。

Logo

火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。

更多推荐