视频描述生成(整段视频)
更新时间:2026-09-14
简介
视频描述生成算子(整段视频路线):抽帧后把整段帧序列(tchw 转成 bcthw)一次性喂给 Video-BLIP,生成 caption_num 条候选,再按 keep_candidate_mode 归约。生成参数默认为 num_beams=4、temperature=0.7、top_p=0.9、repetition_penalty=1.5、do_sample=True。
功能描述
- 模型侧在
video/_video_blip.py里定义两个子类:VideoBlipVisionModel把(b, c, t, h, w)摊成(b*t, c, h, w)过视觉塔再还原时间维;VideoBlipForConditionalGeneration跳过Blip2ForConditionalGeneration.__init__、直接调祖父类,才能把vision_model换成支持时间维的版本 - 加载后固定设置
processor.num_query_tokens=32与model.config.image_token_index=50265;早期 checkpoint 的 processor 配置里没有num_query_tokens,而transformers>=4.47的Blip2Processor.__call__会拿它去减max_length,为 None 时直接抛TypeError,算子初始化时会用model.config.num_query_tokens补齐 - 抽帧语义:
all_keyframes只解 I 帧;uniform在frame_num=1时取中间帧、=2时取首尾帧、>2时含首尾均匀取;抽帧只用 PyAV,不走依赖 torchcodec 的解码路径 max_frames上限(默认 8):Video-BLIP 一次吃整段帧序列,显存随帧数线性上涨,必须兜底;超限时按 uniform 语义抽稀caption_num条候选是重复调generate得到的(do_sample=True固定开启),每条候选把batch_decode出的文本用". "拼接keep_candidate_mode三种取值:random_any随机留 1 条、similar_one_simhash留与参考文本 simhash 汉明距离最近的 1 条、all全部保留;参考文本作为可选第二列reference_text传入,缺失时退化为random_any- simhash 用自带的 64 位实现(window_size=2 的 shingle + 汉明距离),不引入
simhash-pybind;候选之间差异明显时选中结果稳定,极端相近的候选汉明距离容易并列,选中哪一条不保证唯一 - 行数恒定:不做样本增删与占位符回填
- 输入为 None、抽不到帧、或该行生成异常时返回空列表,异常只记日志不打断整列
- 权重只从
model_path/model_name本地目录加载,初始化时校验目录存在,不联网下载、不做运行时 pip 安装
算子参数
输入
| 输入 | 含义 |
|---|---|
| video | 视频列,内容形式由 video_src_type 决定(本地/BOS/HTTP 路径、Base64 字符串、二进制) |
| reference_text | 可选,参考文本列,仅 keep_candidate_mode="similar_one_simhash" 时使用 |
输出
| 输出 | 含义 |
|---|---|
| captions | list<large_string>,归约后的视频级 caption;random_any / similar_one_simhash 返回 1 条,all 返回 caption_num 条,失败或抽不到帧返回空列表 |
参数
| 参数名称 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| video_src_type | str | "video_url" | 视频输入类型:video_url(本地/BOS/HTTP 路径)、video_base64、video_binary |
| model_path | str | "/opt/aihc/model" | 权重根目录 |
| model_name | str | "kpyu/video-blip-opt-2.7b-ego4d" | 相对 model_path 的 Video-BLIP 权重子目录 |
| dtype | str | "float16" | 权重精度:float16 / float32 / bfloat16 |
| frame_sampling_method | str | "all_keyframes" | 抽帧方式:all_keyframes(只解 I 帧)或 uniform(均匀抽 frame_num 帧) |
| frame_num | int | 3 | uniform 模式抽帧数 |
| max_frames | int | 8 | 单视频帧上限,超出按 uniform 语义抽稀;<=0 表示不限 |
| caption_num | int | 1 | 每个视频生成的候选 caption 条数 |
| keep_candidate_mode | str | "random_any" | 候选归约方式:random_any / similar_one_simhash / all |
| prompt | str 或 None | None | 全局提示词,非空时作为生成的文本前缀 |
| max_new_tokens | int | 128 | 生成长度上限 |
| num_beams | int | 4 | beam 数 |
| temperature | float | 0.7 | 采样温度 |
| top_p | float | 0.9 | nucleus 采样阈值 |
| repetition_penalty | float | 1.5 | 重复惩罚 |
| horizontal_flip | bool | False | 是否水平翻转每帧 |
| vertical_flip | bool | False | 是否垂直翻转每帧 |
| rank | int | 0 | 多卡场景 worker 序号,设备取 cuda:(rank % 可见卡数) |
注意事项
do_sample在本算子里固定为 True,没有开关;因此同一视频多次运行的 caption 不完全可复现。- 帧数直接决定显存:整段帧序列一次进视觉塔,
max_frames调大前先确认显存。 - 默认权重名里的
ego4d指其在 Ego4D 第一人称视频上微调,素材域与之差距大时描述质量会下降,换权重需自行评估。 - 与「视频描述生成(抽帧)」的差别:本算子把帧序列作为一个整体输入,能利用帧间信息;后者逐帧生成再用
". "拼接。
调用示例
Python
1from __future__ import annotations
2
3import os
4
5import daft
6from daft import col
7
8from daft.aihc.common.udf import aihc_udf
9from daft.aihc.functions.video.video_captioning_from_video import VideoCaptioningFromVideo
10
11os.environ.setdefault("BOS_ENDPOINT", "http://bj.bcebos.com")
12os.environ.setdefault("BOS_REGION", "bj")
13
14if __name__ == "__main__":
15 if os.getenv("DAFT_RUNNER", "native") == "ray":
16 import ray
17 ray.init(dashboard_host="0.0.0.0", ignore_reinit_error=True)
18 daft.set_runner_ray()
19 daft.set_execution_config(actor_udf_ready_timeout=6000, min_cpu_per_task=0)
20
21ds = daft.from_pydict({"video": ["bos://your-bucket/sample.mp4"]})
22 ds = ds.with_column(
23 "captions",
24 aihc_udf(
25 VideoCaptioningFromVideo,
26 construct_args={
27 "video_src_type": "video_url",
28 "model_path": "/path/to/models",
29 "model_name": "kpyu/video-blip-opt-2.7b-ego4d",
30 "dtype": "float16",
31 "frame_sampling_method": "uniform",
32 "frame_num": 4,
33 "caption_num": 1,
34 "max_new_tokens": 32,
35 },
36 num_cpus=1,
37 num_gpus=1,
38 concurrency=1,
39 batch_size=1,
40 )(col("video")),
41 )
42 ds.show()
评价此篇文章
