Skip to content
Guides1 min read

記錄你的標註資料集:資料宣告、資料表,以及無文件資料為何會迅速失效

一份沒有文件的標註資料集既難以信任,也容易被誤用。這是一份關於資料宣告、資料表和釋出檢查清單的指南,並介紹 Potato 如何替你記錄下其中大部分內容。

Potato Team

你完成了標註,匯出了標籤,交出一個檔案。半年後有人拿它訓練了一個模型,得到一個奇怪的結果,卻分不清問題出在他們的模型還是你的資料,因為沒人寫下過是誰做的標註、資料是怎麼取樣的,以及這些標籤本應意味著什麼。標籤活得比讓它可被理解的語境還久。這種事時時都在發生,而它幾乎完全可以靠文件來避免,那份文件你本可以趁項目還新鮮的時候就寫下來。

一個數據集的可信程度,不會超過它的文件。一份資料宣告會記下資料構建理由、語言及其說話者、標註員及其人口統計資訊、標註指南,以及預期用途,好讓下游使用者判斷這些標籤會如何泛化、又在哪裡不會泛化。把它和資料一起寫,而不是事後補,其中大部分內容其實就從你已經跑過的標註項目裡自然落下。 這篇文章講的是該記錄什麼,以及 Potato 的配置檔案已經替你捕獲了其中相當一部分。

無文件資料的代價

有兩個問題會冒出來,而且兩個到後面都很昂貴。

第一個是不可復現。沒有采樣方法、標註指南的版本和標註員群體,誰都無法重建你的資料集,也無法解釋一處與它對不上的差異。資料成了一個黑箱,人們要麼盲目信任它,要麼直接棄用。

第二個是隱藏的偏差。一個用來自狹窄標註員群體的標籤訓練出來的模型,會繼承那個群體的盲點;而如果這個群體從未被記錄,偏差就是隱形的,直到它在生產環境裡顯形。這正是那些文件框架被髮明出來所要防止的失敗:讓一個數據集的「由誰做、怎麼做」變得可讀,好讓偏差在上線之前就能被看見。

一份資料宣告涵蓋什麼

資料宣告(Bender 和 Friedman,2018)是 NLP 領域的專門答案,是一套用來刻畫語言資料集的模式,好讓使用者理解結果可能如何泛化、以及資料帶有哪些偏差。值得寫下來的幾個部分:

  • 資料構建理由。 資料集裡有什麼、為什麼,包括資料是如何取樣的。從某一個 subreddit 裡挑出來的樣本,和一份有代表性的抽取不是同一個資料集,而理由就是你把這一點說清楚的地方。
  • 語言變體。 具體的語言和方言,而不只是「英語」。基於某一種變體建成的模型,可能在另一種變體上失靈。
  • 說話者人口統計資訊。 是誰產出了源文本。
  • 標註員人口統計資訊。 是誰產出了標籤。在主觀任務上這是決定性的,因為標註員群體的構成塑造了標籤,這正是一開始就採集人口統計資訊的全部理由。
  • 標註指南。 這些標籤是在什麼樣的指示下產出的。同一個標籤名,在不同標註指南下意味著不同的東西。
  • 預期用途。 這個資料集是做什麼用的,以及同樣有用的一點,它不該做什麼用。

一份資料宣告的解剖:一張文件卡片,分設資料構建理由、語言變體、說話者人口統計資訊、標註員人口統計資訊、標註指南和預期用途幾個欄目,每一欄都回答一個下游使用者否則只能靠猜的問題。一份資料宣告記下由誰做、怎麼做,好讓下游使用者判斷這些標籤在哪裡泛化

資料表與模型卡

有兩個相鄰的框架把這件事補全。面向資料集的資料表(Gebru 等人)從電子工業借來了這個想法,那裡每個元件出廠時都附帶一張資料表:每個資料集出廠時也都應附帶一份文件,涵蓋它的動機、構成、採集過程、推薦用途和維護方式。資料宣告是面向語言的那個表親;資料表是通用版本,兩者高度重疊。

資料的下游坐著模型卡(Mitchell 等人,2019),它記錄一個訓練好的模型的預期用途,以及它拆分到不同人口統計群體和其他群體上的表現。這三者構成一條鏈:資料表或資料宣告記錄資料,模型卡記錄基於它建成的東西,而前者裡那一欄標註員人口統計資訊,正是讓後者裡分群評估可被理解的東西。把標註記錄好,你就已經走完了一份站得住腳的模型卡的一半路。

一份釋出檢查清單

在釋出之前,確認你能回答:

  • 資料是怎麼取樣的,從哪裡採的?
  • 這是哪種語言變體,源文本是誰寫的?
  • 是誰做的標註,多少人,這個群體的人口統計構成是什麼樣的?
  • 他們遵循的是什麼標註指南,哪個版本?
  • 分歧是怎麼處理的,是聚合成一個黃金標籤,還是保留成一個分佈?無論哪種,都報告一致性。
  • 這個資料集是做什麼用的,又不該被用來做什麼?

如果某個問題沒有答案,那就是一個要在釋出之前、而不是之後補上的缺口。

在 Potato 裡怎麼做

在 Potato 裡跑標註的好處在於,資料聲明裡的大部分內容已經作為項目產物存在了。你不必從一張白紙開始寫文件。

配置檔案本身就是文件。這份 YAML 記錄了標註方案、標籤集和任務結構,所以資料聲明裡「標籤是什麼、又是怎麼定義的」那一部分,就和資料一起被版本控制起來。你寫進標註指南裡的說明和規範,逐字就是那一欄指南。

人口統計資訊已經採集好了。如果你跑過一個試點階段,標註員的人口統計資訊就按標註員分別存好了。聚合成分佈之後,絕不用個體記錄,它們就是那一欄標註員人口統計資訊,可以直接貼上進去。

匯出會帶著後設資料。Potato 的匯出格式在每個標籤上都保留了標註員和時間戳,所以來源出處會隨資料一起走,而不是在匯出這一步被剝掉:

json
{
  "id": "doc_001",
  "annotations": { "sentiment": "positive" },
  "annotator": "user_1",
  "timestamp": "2024-01-15T10:30:00Z"
}

當你釋出到 Hub 時,把生成資料集卡片作為匯出的一部分,就像匯出到 HuggingFace 的講解所展示的那樣,再用你已經有的配置檔案、標註指南和試點階段的人口統計資訊去填它的各個欄目。文件不再是一個單獨的寫作項目,而成了標註項目的最後一步。

接下來讀什麼