FineVideo:幕后故事

开放的视频数据集稀缺,这减缓了开源视频 AI 的发展。因此,我们构建了 FineVideo,一个包含 4.3 万个视频、总时长 3.4 千小时的数据集,并附有丰富的描述、叙事细节、场景切分和问答对。
FineVideo 包含高度多样化的视频和元数据集合,使其成为训练模型理解视频内容、训练扩散模型从文本描述生成视频,或使用其结构化数据作为输入训练计算机视觉模型的优质素材。
等等,你还没看过 FineVideo 吗?快通过数据集浏览器页面了解一下吧。

目录
关于这篇博文
在这篇博文中,我们分享了开发 FineVideo 涉及的技术细节和代码:这段旅程始于 YouTube-Commons 中的 190 万个视频,最终得到了 4.4 万个带有详尽标注的视频。
一个好的开始方式是先了解我们整个过程的不同步骤。这些步骤涉及内容筛选、标注和输出结构化。

在接下来的章节中,我们将讨论每个步骤,并提供相关代码的参考链接。如果你更喜欢直接浏览代码,请查看我们在 Github 上的 FineVideo 代码仓库。
首先,让我们看看我们如何获取初始的 YouTube 视频列表,以及如何应用初步的筛选。
构建原始数据集
我们的旅程始于 YouTube-Commons:这是一个收录了在 YouTube 上以 CC-By 许可证共享的视频的音频转录文本的集合。该项目由 PleIAs 创建并维护,作为其语料库收集项目的一部分。
筛选 YouTube-Commons
YouTube Commons 包含多种语言的视频和转录文本,我们最初的任务是将其中的内容缩小到同一种语言。
我们筛选出 YouTube-Commons 中的英语视频,并同时收集相关元数据。通过这次初步筛选,我们收集了 190 万个视频及其隐藏式字幕和元数据。
以下是我们保留的筛选条件和元数据字段的一些详细信息
筛选条件
字段 | 筛选值 | 描述 |
---|---|---|
original_language | en | 英语视频 |
transcription_language | en | 英语字幕 |
元数据字段
点击展开元数据字段
字段 | 描述 |
---|---|
acodec | 音频编解码器 |
age_limit | YouTube 视频的年龄限制 |
categories | YouTube 视频分类 |
channel | YouTube 频道 |
channel_follower_count | 频道订阅用户数 |
channel_id | YouTube 频道标识符 |
character_count | 隐藏式字幕中的字符数 |
comment_count | YouTube 中的评论数 |
description | YouTube 视频描述 |
duration_string | 视频时长,格式为 hh:mm:ss |
许可证 | 视频许可证 |
like_count | YouTube 上的视频点赞数 |
resolution | 视频的像素分辨率,格式为“宽 x 高” |
tags | 与视频关联的 YouTube 自由文本标签 |
text | 隐藏式字幕 |
title | YouTube 视频标题 |
upload_date | YouTube 上传日期 |
vcodec | 视频编解码器 |
video_id | YouTube 视频标识符 |
view_count | YouTube 上的观看次数 |
word_count | 隐藏式字幕中的单词数 |
内容筛选和元数据收集的代码可在此处获取 [链接]
下载视频
在确定了包含 190 万个视频的目标列表后,我们成功下载了 180 万个视频(部分视频被频道所有者移除,或更改了权限)。
我们探索了两种不同的分布式下载方法。
方案 1:Video2dataset
video2dataset 是一个开源项目 [链接],专注于分布式视频下载、转换和打包成不同数据集格式。该项目原生支持 Slurm Workload Manager,因此我们可以在我们的 CPU 集群上运行它。

由于我们所有的集群实例都通过同一个公共 IP 访问互联网,我们为该项目贡献了指定代理的功能以方便视频下载。虽然该功能尚未合并,但您可以通过我们的 PR [链接] 来为 video2dataset 打补丁以使用代理功能。
方案 2:云批量作业
大多数云提供商都支持运行作业,只需定义执行每个作业的实例类型、定义一个队列,并提供一个包含待执行代码的容器即可。
我们使用 Google Cloud 和 AWS 运行了一个定制的 Docker 容器,该容器使用 ytdlp 下载视频和元数据,并将结果推送到 S3。
用于构建 Docker 容器的文件可以在这里找到 [代码]。
我们的结论
虽然 Video2Dataset 在使用代理时功能正常,并允许我们执行额外的处理步骤,但我们能向代理发出的每秒请求数成了瓶颈。这使得我们转向了云批量作业。
保留动态内容
在寻找最佳视频的过程中,我们将选择范围缩小到那些既有视觉动作、又有人以中等至较快语速说话的内容。我们通过词密度筛选和视觉动态性筛选来实现这一点。
词密度筛选
我们把视频中的词密度作为音频动态性的一个代理指标。词密度的定义是
词密度 = 隐藏式字幕中的单词数 / 视频总时长(秒)
通过对不同密度阈值下的内容质量进行抽样和视觉评估,我们决定移除所有词密度低于 0.5 词/秒的视频。
示例
词密度 | 示例 |
---|---|
0.25 | |
0.5 | |
0.75 | |
1.0 |
按词密度筛选和浏览示例的代码可以在这里找到 [链接]
视觉动态性筛选
我们重新利用 FFMPEG 的 Freezedetect 滤镜 来判断视频的动态性。虽然这个滤镜本是设计用来识别视频中的静止部分(连续多个相同的帧),但通过将 noise
参数夸张地设为一个非常高的值,我们也可以识别出运动量很低的片段。
我们没有对整个视频运行 freezedetect,而是按时间段分析视频,并根据被归类为静态的段落数量来判断视频是否为静态。通过人工评估,我们设定了一个阈值:如果 40% 的分析段落运动量过低,则丢弃该视频。
此次筛选后丢弃的一些内容类型
类型 | 示例 |
---|---|
带音乐的静态图片 | |
演示文稿的屏幕录像 | |
高度静态的人物对着镜头讲话 |
用于按视频动态性分类的 DockerFile 和代码可以在这里找到 [链接]
在分析的 180 万个视频中,经过这一步筛选,我们保留了 60 万个动态视频。在这个阶段,我们深入研究视频内容,这对确保数据集的多样性至关重要。
视频分类
为了实现最广泛的内容选择,我们利用隐藏式字幕和 YouTube 元数据对 60 万个筛选后的资产进行了分类。为了更好地控制分类过程,我们创建了一个分类体系,并引导标注过程遵循该体系。
定制分类体系
我们使用 GPT4-o 启动了定制分类体系的构建,并由一位信息科学家进行了审查和调整。该分类体系包含 126 个细分类别,并聚合成多个层级。这种多层级的方法允许 FineVideo 的用户根据其特定用例对数据集进行切片。
该分类体系也提供 JSON 格式 [链接]
在分类体系的初版基础上,我们开始了内容标注,并通过观察内容标注的结果,在信息科学家的帮助下,相应地调整了分类体系。
内容标注
我们使用通过文本生成推理 TGI 提供的 Llama 3.1 70B 对视频进行分类 [代码]。
为了确保答案严格属于我们分类体系中的一个类别,提示词经过了多次迭代。在我们的提示词评估过程中,我们发现,从提示词中移除现有的 YouTube 标签和类别,可以极大地提高结果质量:YouTube 的元数据会使 Llama 3.1 生成的文本偏向 YouTube 提供的某个类别。
prompt_template = """
Given those categories: {leaves}
Classify a youtube video given its closed captioning and some metadata details. RETURN ONLY the selected category and nothing else!
Title: {title}
Description: {description}
Channel: {channel}
Closed Caption: {closed_caption}
"""
分类体系 - 内容标注的反馈循环

使用大语言模型进行内容分类,使得调整分类体系的周期从数月/数年缩短到数小时。此外,在某些情况下,我们专门创建了一些类别来丢弃敏感视频,例如属于 枪支与武器
和 物质使用与毒品
的视频。
贡献描述性元数据
在流程的这个阶段,我们有三个视频级别的元数据来源
- 视频类别(由 Llama 3.1 推断)
- YouTube 元数据(标题、描述)
- 来自 YouTube-Commons 的字幕
为了在视频理解领域做出贡献,我们决定深入研究时间码级别的元数据,例如活动、物体、叙事和剪辑方面。虽然我们曾考虑将人工标注作为主动学习设置的一部分,即由一个或多个模型提出标注,然后由人工进行质检,但正如我们将在下一节中讨论的,我们发现 Gemini 是一个很好的解决方案,尤其是在我们限制了输入视频长度和输出格式的情况下。
长视频与 Gemini 1.5 Pro
我们深入研究了 Gemini 1.5 Pro,迭代我们的提示词并用不同长度的内容进行测试。
考虑到其 100 万 token 的限制(大约相当于 1 小时的视频),我们不得不放弃超过 1 小时的视频。为了克服这种情况,一个想法是加速超过一小时的视频,以便适应 Gemini 的上下文。

虽然在高层面上看起来可行,但当我们开始审视细节时,我们意识到只有视频的前几分钟被准确标注了。
发现长视频质量下降让我们思考:这个问题是否影响了我们其余的视频?通过抽样不同长度的视频并检查标注的视频覆盖范围,我们发现在超过 10 分钟的视频中,质量有所下降。
为了与我们向社区提供高质量数据的目标保持一致,我们放弃了超过 10 分钟的视频。
内容选择
鉴于每小时视频使用 Gemini 标注的成本超过 5 美元,我们无法标注筛选后所有的视频。因此,我们希望确保对所有主题都有良好的覆盖,并寻求在内容多样性、后期预训练/微调任务和预算之间找到一个良好的平衡。我们将这个规模限制设定为 4000 小时视频。
为了从 60 万个视频中筛选出 4000 小时的内容,我们准备了一个算法,该算法平衡了内容类别、用户参与度和频道代表性,以达到目标时长。

算法流程图
内容选择算法的一些关键部分
- 活跃度评分:我们通过加权组合评论数、观看次数和点赞数来计算每个视频的参与度指标。这个分数有助于优先选择那些在观众中反响良好的视频。
- 视频选择:此步骤迭代选择视频以满足目标时长,同时确保多样性。它在高度参与的内容与来自不同类别和频道的代表性之间取得平衡,并使用惩罚系统来避免任何单个频道的过度代表。
- 最终调整:我们调整选择,以尽可能接近目标时长而不超过它。它按时长对选定的视频进行排序,并将它们添加到最终列表中,直到达到最接近目标总时长的总和。
代码可以在代码仓库中找到 [链接]。
使用 Gemini 1.5 Pro 进行标注,并使用 GPT4o 进行结构化输出
为什么需要结构化数据?
我们通过 FineVideo 的目标之一是提供结构化数据,以此赋能我们的社区:如果你正在研究多模态大语言模型,你可以对数据进行切片,并决定哪些类别适合你的预训练或微调组合。如果你更关注计算机视觉,你可以直接使用该数据集来训练分类器,基于 FineVideo 中包含的数值类别,如动态性得分、场景边界或音视频相关性得分。
结构化数据与 Gemini 1.5
Gemini 1.5 Pro 通过提供模式 (schema) 支持基于 JSON 的输出。我们探索了这一功能,但很快发现了两个问题
- 由于我们的模式非常复杂,我们无法将其完全适配到 Gemini 中。
- 当我们尝试使用稍微简单一些的模式时——尽管仍然相当复杂——Gemini 结果的质量大幅下降:大多数场景类型的数据(角色、活动、道具)都丢失了。我们尝试将提示词拆分成多个,并将不同的提示词与模式的不同部分匹配,但收效甚微。
我们观察到的情况与其他研究人员的经历完全一致:添加具体的模式约束可能会降低性能。(让我自由发言?关于格式限制对大语言模型性能影响的研究)。
我们的解决方案是先用 Gemini 1.5 生成自由文本,然后增加一个处理步骤,将 Gemini 的结果与我们的模式对齐。
我们使用的 Gemini 提示词如下
Study the video and provide the following details about the video and the semantic scenes that compose it:
- characterList: a list of characters that appear in the whole video and a visual description that should allow me to identify them just seeing an image of them.
- scenes: a list of the scenes with the following properties:
- start/end timestamps of the scene
- list of all the characters that appear in the scene
- list of all activities and their timestamps
- list of all props and their timestamps
- list of all video editing details and their start/end timestamps. Details include transitions, effects, music as well as suggestions like segments of the scene that could be removed and why
- scene mood with notes on how the visuals, audio and context contribute to it. Use the following taxonomy returning only the name in your answer {"moods":{"Positive":[{"name":"Happy","description":"Feeling joyful, content, or delighted."},{"name":"Excited","description":"Feeling enthusiastic, energetic, or eager."},{"name":"Calm","description":"Feeling peaceful, relaxed, or serene."},{"name":"Grateful","description":"Feeling appreciative or thankful."},{"name":"Proud","description":"Feeling satisfied with one's achievements or the achievements of others."}],"Negative":[{"name":"Sad","description":"Feeling down, unhappy, or sorrowful."},{"name":"Angry","description":"Feeling irritated, frustrated, or furious."},{"name":"Anxious","description":"Feeling nervous, worried, or uneasy."},{"name":"Lonely","description":"Feeling isolated, disconnected, or abandoned."},{"name":"Bored","description":"Feeling uninterested, disengaged, or restless."}],"Neutral":[{"name":"Indifferent","description":"Feeling neither particularly positive nor negative."},{"name":"Content","description":"Feeling satisfied but not overly excited."},{"name":"Curious","description":"Feeling interested or inquisitive without strong emotion."},{"name":"Confused","description":"Feeling uncertain or unclear but without strong negative feelings."},{"name":"Pensive","description":"Feeling thoughtful or reflective without strong emotional engagement."}]}}
- specific mood changing moments inside the scene, report the timestamp and what we transition from/to in any of the dimensions (visual / auditive)
- scene narrative progression and plot development
- specific narrative moments inside the scene. Report the timestamp and what happened
- character interaction and dynamics descriptions and their start/end timestamps
- specific thematic elements and descriptions
- specific relevant happenings to create deeper meanings and subtexts not explicitly stated that contribute to the richness and depth of the content, timestamp and descriptions
- dynamism score of the scene. Score between 0 and 1. 1 is highly dynamic
- audio - visual correlation score. Score between 0 and 1. 0 what we see is not correlated with the speech and 1 is highly correlated
- storylines: a list of the different storylines found and which scenes belong to it.
- Specify where is the climax (scene and timestamp) and if the content is being presented a narrative story, or is it more like a collection of facts or non-narrative information
- if there are scenes not matching storylines, explain how those scenes contribute to the video
- looking at the overall video and the storylines, which segments of the video could be trimmed to make it more dynamic?
- q&a: a list of 5 questions/answers about the video that focus on fine details (objects and or activities), overall story reasoning and mood. Focus on Q&A aspects captured on the audio and the video whenever possible difficult to get only by looking at the transcription.
添加 Instructor
在 Gemini 处理完结果后,我们使用 Instructor 进行解析:这是一个构建在 Pydantic 之上的库,用于根据给定的模式实现结构化输出。请参见下表示例。
Instructor 让我们能够试验不同的模型,将 Gemini 的自由文本转换为我们在 Pydantic 中定义的模式。我们尝试了 Gemini 和 GPT4o,并最终选择了 GPT4o,因为它的成功率更高。
视频 | Gemini 输出 | Instructor 输出 |
---|---|---|
|
|
值得强调的是,Gemini 的内容筛选功能丢弃了一些视频,如果你使用 Gemini,也可能会遇到这种情况。在我们的案例中,鉴于我们目标的内容量,Gemini 筛选丢弃的总内容时长可以忽略不计。
完整的视频标注代码可以在这里找到 [链接]。
精细对齐和异常筛选
在视频标注完成且数据与我们的模式正确对齐后,我们关注数据的时间维度,并确保其与视频对齐:Gemini 1.5 以每秒 1 帧的速度读取视频,而视频的帧率通常为每秒 25 - 29 帧。在我们的精细对齐中,我们确保 Gemini 1.5 提供的场景边界与视频中的正确帧匹配。
我们还利用这种时间对齐来丢弃那些 Gemini 停止提供有效数据、导致视频部分内容被错误标注的情况。需要注意的是,由于我们在流程早期丢弃了所有超过 10 分钟的内容,数据质量差的视频数量可以忽略不计(低于 0.5%)。

视频对齐代码链接在此 [链接]
未来工作
我们目前正在准备用 FineVideo 训练一个多模态大语言模型,并计划在完成后尽快与社区分享模型权重和训练方案。
我们也对 FineVideo 的其他扩展持开放态度,欢迎提出您的想法,告诉我们您希望看到什么!