记录你的标注数据集:数据声明、数据表,以及无文档数据为何会迅速失效
一份没有文档的标注数据集既难以信任,也容易被误用。这是一份关于数据声明、数据表和发布检查清单的指南,并介绍 Potato 如何替你记录下其中大部分内容。
你完成了标注,导出了标签,交出一个文件。半年后有人拿它训练了一个模型,得到一个奇怪的结果,却分不清问题出在他们的模型还是你的数据,因为没人写下过是谁做的标注、数据是怎么采样的,以及这些标签本应意味着什么。标签活得比让它可被理解的语境还久。这种事时时都在发生,而它几乎完全可以靠文档来避免,那份文档你本可以趁项目还新鲜的时候就写下来。
一个数据集的可信程度,不会超过它的文档。一份数据声明会记下数据构建理由、语言及其说话者、标注员及其人口统计信息、标注指南,以及预期用途,好让下游用户判断这些标签会如何泛化、又在哪里不会泛化。把它和数据一起写,而不是事后补,其中大部分内容其实就从你已经跑过的标注项目里自然落下。 这篇文章讲的是该记录什么,以及 Potato 的配置文件已经替你捕获了其中相当一部分。
无文档数据的代价
有两个问题会冒出来,而且两个到后面都很昂贵。
第一个是不可复现。没有采样方法、标注指南的版本和标注员群体,谁都无法重建你的数据集,也无法解释一处与它对不上的差异。数据成了一个黑箱,人们要么盲目信任它,要么直接弃用。
第二个是隐藏的偏差。一个用来自狭窄标注员群体的标签训练出来的模型,会继承那个群体的盲点;而如果这个群体从未被记录,偏差就是隐形的,直到它在生产环境里显形。这正是那些文档框架被发明出来所要防止的失败:让一个数据集的「由谁做、怎么做」变得可读,好让偏差在上线之前就能被看见。
一份数据声明涵盖什么
数据声明(Bender 和 Friedman,2018)是 NLP 领域的专门答案,是一套用来刻画语言数据集的模式,好让用户理解结果可能如何泛化、以及数据带有哪些偏差。值得写下来的几个部分:
- 数据构建理由。 数据集里有什么、为什么,包括数据是如何采样的。从某一个 subreddit 里挑出来的样本,和一份有代表性的抽取不是同一个数据集,而理由就是你把这一点说清楚的地方。
- 语言变体。 具体的语言和方言,而不只是「英语」。基于某一种变体建成的模型,可能在另一种变体上失灵。
- 说话者人口统计信息。 是谁产出了源文本。
- 标注员人口统计信息。 是谁产出了标签。在主观任务上这是决定性的,因为标注员群体的构成塑造了标签,这正是一开始就采集人口统计信息的全部理由。
- 标注指南。 这些标签是在什么样的指示下产出的。同一个标签名,在不同标注指南下意味着不同的东西。
- 预期用途。 这个数据集是做什么用的,以及同样有用的一点,它不该做什么用。
一份数据声明记下由谁做、怎么做,好让下游用户判断这些标签在哪里泛化
数据表与模型卡
有两个相邻的框架把这件事补全。面向数据集的数据表(Gebru 等人)从电子工业借来了这个想法,那里每个元件出厂时都附带一张数据表:每个数据集出厂时也都应附带一份文档,涵盖它的动机、构成、采集过程、推荐用途和维护方式。数据声明是面向语言的那个表亲;数据表是通用版本,两者高度重叠。
数据的下游坐着模型卡(Mitchell 等人,2019),它记录一个训练好的模型的预期用途,以及它拆分到不同人口统计群体和其他群体上的表现。这三者构成一条链:数据表或数据声明记录数据,模型卡记录基于它建成的东西,而前者里那一栏标注员人口统计信息,正是让后者里分群评估可被理解的东西。把标注记录好,你就已经走完了一份站得住脚的模型卡的一半路。
一份发布检查清单
在发布之前,确认你能回答:
- 数据是怎么采样的,从哪里采的?
- 这是哪种语言变体,源文本是谁写的?
- 是谁做的标注,多少人,这个群体的人口统计构成是什么样的?
- 他们遵循的是什么标注指南,哪个版本?
- 分歧是怎么处理的,是聚合成一个黄金标签,还是保留成一个分布?无论哪种,都报告一致性。
- 这个数据集是做什么用的,又不该被用来做什么?
如果某个问题没有答案,那就是一个要在发布之前、而不是之后补上的缺口。
在 Potato 里怎么做
在 Potato 里跑标注的好处在于,数据声明里的大部分内容已经作为项目产物存在了。你不必从一张白纸开始写文档。
配置文件本身就是文档。这份 YAML 记录了标注方案、标签集和任务结构,所以数据声明里「标签是什么、又是怎么定义的」那一部分,就和数据一起被版本控制起来。你写进标注指南里的说明和规范,逐字就是那一栏指南。
人口统计信息已经采集好了。如果你跑过一个试点阶段,标注员的人口统计信息就按标注员分别存好了。聚合成分布之后,绝不用个体记录,它们就是那一栏标注员人口统计信息,可以直接粘贴进去。
导出会带着元数据。Potato 的导出格式在每个标签上都保留了标注员和时间戳,所以来源出处会随数据一起走,而不是在导出这一步被剥掉:
{
"id": "doc_001",
"annotations": { "sentiment": "positive" },
"annotator": "user_1",
"timestamp": "2024-01-15T10:30:00Z"
}当你发布到 Hub 时,把生成数据集卡片作为导出的一部分,就像导出到 HuggingFace 的讲解所展示的那样,再用你已经有的配置文件、标注指南和试点阶段的人口统计信息去填它的各个栏目。文档不再是一个单独的写作项目,而成了标注项目的最后一步。
接下来读什么
- 《负责任地采集标注员的人口统计信息》,讲如何把标注员人口统计信息这一栏做对。
- 《分歧是信号,不是噪声》,讲如何记录你处理分歧的方式。
- 《撰写有效的标注指南》,它同时充当数据声明里那一栏标注指南。
- 《为机器学习导出标注》,讲如何把标签及其元数据干净地导出来。