用你的標註訓練第一個模型
在沒有 GPU 的筆記本上執行 Potato 的示例評論研究,用模擬器標註 120 條評論,訓練 sklearn-text,並檢視模型給出的預標籤。
v2.14.0 新增
本教程在一個示例研究自己的標籤上訓練情感分類器,並把分類器的答案作為預標籤展示給下一位標註者。教程寫給第一次嘗試 Potato 模型訓練的研究負責人,每一步都能在沒有 GPU 的筆記本上執行,除 Potato 本身外無需任何安裝。你要啟動 Potato 的示例評論項目,讓使用者模擬器標註項目 160 條評論中的 120 條,在管理員訓練頁面訓練 sklearn-text,檢視這次執行的留出得分,再以新標註者的身份登入,看到模型的答案。關於迴圈中每個部分的一般作用,請參閱模型訓練概覽。
安裝 Potato 和示例項目
本教程使用 sklearn-text。這個訓練器把每條評論轉換成 TF-IDF 特徵,並擬合一個邏輯迴歸分類器。sklearn-text 無需額外安裝,因為 scikit-learn 是 Potato 的核心依賴之一。示例項目在 Potato 的 GitHub 倉庫裡,而不在 pip 包中,所以要安裝 Potato 並克隆倉庫:
pip install --upgrade potato-annotation
git clone https://github.com/davidjurgens/potato.git
cd potato下面的每條命令都在倉庫根目錄執行。項目資料夾 examples/ai-assisted/model-training/ 中有三個檔案:
config.yaml是研究配置,註釋裡寫著本教程用到的命令。data/reviews.json包含 160 條合成的商品評論,每行一個 JSON 物件,帶有id和text。data/gold.json給出每條評論的正確標籤。模擬器按這個檔案作答。
示例配置
配置定義了一個名為 sentiment 的 radio 方案,標籤為 positive 和 negative,並開啟了模型訓練。下面的程式碼塊是去掉註釋的原始檔案,該檔案無需修改即可通過 python -m potato.validate_cli --strict:
annotation_task_name: "Review Sentiment (model training demo)"
data_files:
- "data/reviews.json"
item_properties:
id_key: "id"
text_key: "text"
task_dir: "."
output_annotation_dir: "annotation_output"
require_password: false
admin_api_key: "demo-admin-key"
num_annotators_per_item: 1
annotation_schemes:
- annotation_type: "radio"
name: "sentiment"
description: "Is this review positive or negative?"
labels:
- "positive"
- "negative"
sequential_key_binding: true
model_training:
enabled: true
writeback:
schemas: ["sentiment"]
include_unlabeled: true
min_confidence: 0.6四項設定決定了教程的走向:
num_annotators_per_item: 1讓每條評論得到一個標籤。三位模擬標註者各標 40 條,就能覆蓋 120 條不同的評論,剩下 40 條未標註的留給模型。require_password: false讓新標註者只憑使用者名稱即可登入。最後一步就用這種方式登入。admin_api_key: "demo-admin-key"用來開啟管理員頁面。在其他人能訪問的伺服器上,請更換這個金鑰。model_training註冊/admin/training路由。writeback塊會把sentiment方案的預測展示給標註者,下文回寫一節會說明其中每個鍵。
在 8000 埠啟動伺服器
用示例配置啟動 Potato:
potato start examples/ai-assisted/model-training/config.yaml -p 8000伺服器會載入這 160 條評論,並在 http://localhost:8000 提供標註頁面。如果你的機器上 8000 埠已被佔用,請給 -p 指定另一個埠,並在下面的每個 URL 裡使用該埠。
用模擬器標註 120 條評論
模型需要人工標籤才能學習。使用者模擬器代替三位標註者。每位模擬標註者按金標準檔案作答,大多數答案是正確的。在第二個終端裡,從倉庫根目錄執行模擬器:
python -m potato.simulator --server http://localhost:8000 --users 3 \
--strategy gold_standard --competence good=1.0 --max-annotations 40 \
--gold-file examples/ai-assisted/model-training/data/gold.json \
--sequential --fast-mode --no-export--strategy gold_standard 讓每位模擬標註者按 gold.json 作答,--competence good=1.0 讓三位標註者都使用模擬器的 good 檔案。good 標註者會抽取一個 0.80 到 0.90 之間的準確率,並以該機率答對每條評論。因此這些標籤含有錯誤,和真實團隊的標籤一樣。--sequential 讓三位標註者依次執行,--fast-mode 去掉作答之間的停頓,--no-export 則跳過模擬器自身結果檔案的寫入。模擬器大約兩秒完成,並以一份摘要收尾。繼續之前,請確認摘要中報告了 Total annotations: 120。你也可以在 http://localhost:8000 手工標註評論。當 train 劃分中已標註的條目少於 10 個時,sklearn-text 拒絕擬合。
用管理員金鑰開啟訓練頁面
訓練頁面需要管理員會話。開啟 http://localhost:8000/admin,在頁面詢問 API 金鑰時輸入 demo-admin-key,然後開啟 http://localhost:8000/admin/training。
在示例項目上完成兩次 sklearn-text 執行後的訓練頁面
頂部的 Start a run 面板包含全部設定。Schema 列出項目唯一的方案 sentiment (radio)。Trainer 列出全部 38 個訓練器,項目無法執行的訓練器會在名稱旁標註原因,例如 "not installed"。頁面選中 sklearn-text,只要 sklearn-text 適用於該方案。Runs on 顯示項目唯一的後端 local (this machine)。
訓練 sklearn-text
保持 Parameters 為空以使用訓練器的預設值,然後點選 Train now。這次執行先把標註合併成每條評論一個標籤,再把 120 條已標註的評論劃分為 train、val 和 test 三個集合,並在單獨的程序中擬合模型。我們的執行用了 1.5 秒,執行結束時面板顯示 "Run finished."。
sklearn-text 預設用 scikit-learn 的 TfidfVectorizer 提取特徵,用 LogisticRegression 作分類器。當 train 劃分中有 50 個或更多條目時,sklearn-text 還會對分類器的置信度做校準。訓練條目少於 1,000 時用 Platt 縮放,達到 1,000 條起用保序迴歸。只有當校準使訓練準確率下降不超過 0.05 時,才保留校準後的模型。校準在這裡很重要,因為回寫步驟會把每個置信度與 min_confidence 比較。
要用指令碼啟動同樣的執行,請在 X-API-Key 請求頭中傳送管理員金鑰:
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
http://localhost:8000/admin/training/api/runs \
-d '{"trainer": "sklearn-text", "schemas": ["sentiment"]}'響應中帶有新的 run_id 和 "started": true。
執行頁面和留出得分
完成的執行會出現在 Runs 表中,附有 train 和 val 的大小以及它的 Held-out score。點選執行 ID,即可開啟 /admin/training/runs/<run_id> 處的執行頁面。執行頁面列出該次執行的狀態、訓練器、後端、劃分大小、劃分種子、留出得分和參數,然後是一張包含每項指標的表以及執行日誌。
預設情況下,一次執行會把已標註條目的 15% 留出作 val,15% 留出作 test,由 split_seed 決定哪些條目落入哪個集合。只要 split_seed 的值不變,每次執行的劃分就保持一致,所以對同一批標籤的兩次執行是在相同的條目上評分的。sklearn-text 執行的留出得分是 val 上的 macro-F1。Macro-F1 對每個標籤(這裡是 positive 和 negative)的 F1 得分取平均。執行還會記錄 test 上的準確率和 macro-F1。train 劃分上的準確率記為 train_accuracy,從不用作得分。在我們的執行中,日誌如下:
[info] Read 76 labelled rows from the train split
[info] Applied sigmoid calibration over 5 folds
[info] Held-out val macro_f1: 0.725Potato 自己的文件報告了同一項目上的一次執行,下表把那次執行與我們的執行並排列出:
| Potato 的文件 | 我們的執行 | |
|---|---|---|
| 已標註評論 | 120 | 120 |
| train、val 和 test 條目數 | 未說明 | 76、19 和 25 |
| 留出得分(val macro-F1) | 0.84 | 0.725 |
| test macro-F1 | 未說明 | 0.826 |
| 已釋出的預標籤 | 80 | 73 |
低於 min_confidence 的預測 | 4 | 11 |
你的數字會與兩列都不同,因為每位模擬標註者隨機抽取一個準確率,並犯下不同的錯誤。在我們的執行中,模擬標籤在 120 條評論中有 97 條與 gold.json 一致,在 19 條 val 評論中有 15 條一致。留出得分衡量的是與你的標註者所給標籤的一致程度,而不是與任何標準答案的一致程度,所以 val 中的標註錯誤會拉低得分。我們的執行釋出的 73 個預標籤全部與 gold.json 相符。在沒有金標準檔案的真實研究裡,看到較低的留出得分時,應先檢查標註者之間的一致性,再去怪模型。
為方案開啟回寫
一次執行的預測只通過回寫送到標註者面前。示例配置已經為 sentiment 方案開啟了回寫。對於你自己的項目,請在配置的 model_training 下加入這個塊,並重啟伺服器:
model_training:
enabled: true
writeback:
schemas: ["sentiment"]
include_unlabeled: true
min_confidence: 0.6writeback.schemas 指定標註者能看到其預測的方案。該列表預設為空,所以開啟訓練後,在你指定方案之前,標註頁面不會有任何變化。include_unlabeled: true 讓每次執行除了對 val 和 test 評論做預測,也對無人標註的評論做預測。沒有 include_unlabeled 時,一次執行只對 val 和 test 評論做預測,而這些評論已經有人工標籤。min_confidence: 0.6 會在模型置信度低於 0.6 時,不把該預測放到標註頁面上。Potato 仍會儲存這個預測。
回寫在每次成功執行之後執行,並跳過該次執行 train 劃分中的所有條目。在我們的執行中,模型對 84 條評論做了預測(19 條 val、25 條 test 和 40 條未標註),其中 11 個預測低於 0.6,執行釋出了其餘 73 個。GET /admin/training/api/status 的 writeback 欄位給出相同的數量,摘要為 "Published 73 prediction(s) as prelabels."。已儲存的預測會在伺服器啟動時再次回寫,所以預標籤在重啟後依然存在。若要從條目上撤下某次執行的預標籤,請傳送 DELETE /admin/training/api/runs/<run_id>/predictions。
新標註者看到的預標籤
在隱私瀏覽視窗中開啟 http://localhost:8000,用一個沒人用過的使用者名稱登入,例如 newannotator。每條已標註的評論都已有它的那一個標籤,所以新標註者被分配到 40 條未標註的評論,我們的執行中進度計數器顯示 0/40。我們的第一條評論是 "The toaster: Disappointed, it stopped working within a week. Much smaller than the photos suggest.",其中 Negative 已被選中,置信度為 0.68。
一條開啟時已選中模型答案的評論,此時標註者還沒有儲存任何內容
標註者只是檢視評論時,預標籤不會被儲存,狀態標籤會一直顯示 "Not labeled",直到標註者儲存答案。標註者更改選擇或點選 Next 時,模型的答案就成為標註者的答案。在我們的執行中,預測低於 min_confidence 的 5 條未標註評論開啟時沒有任何選中項。通過 ai_support.holdout 被排除在機器協助之外的標註者,完全看不到預標籤。預標籤會把標註者拉向模型的答案,所以請等留出得分高到值得信任時再開啟回寫,並用檢測被不加思考接受的預標籤中的檢查,留意那些照單全收所有預標籤的標註者。
後續步驟
在換成你自己的資料之前,同一個項目還支援幾個實驗:
- 更改一個參數。 在 Parameters 中輸入
{"vectorizer_kwargs": {"ngram_range": [1, 2]}}並再次訓練,把詞對加入特徵。我們加入詞對的第二次執行在同一劃分上得到 0.774 的 val macro-F1,併發布了 74 個預標籤。sklearn-text不接受的參數名會被拒絕,並附上該訓練器接受的名稱列表。 - 試試 transformer。
hf-text-classification(pip install "potato-annotation[train-text]")和setfit(pip install "potato-annotation[embeddings]")可以學習同一個方案,訓練器頁面給出了每個訓練器的參數。 - 閉合迴圈。 迴圈中的模型加入了稽核佇列、用
auto_retrain隨標註到來而重新訓練,以及把一次執行的劃分儲存為資料集版本。 - 把訓練移到 GPU 上。 在 GPU 伺服器、SLURM 叢集或 NAT 後的機器上訓練會把同樣的執行傳送到另一臺機器。
- 訓練其他類型的模型。 關於微調 LLM、視覺模型、音訊模型和科學模型的教程,用其他訓練器和方案遵循相同的步驟。
這裡用到的每個鍵都列在模型訓練配置參考中。要開始你自己項目的第一次執行,請把上面的 model_training 塊複製到配置裡,並在 writeback.schemas 下寫上你的方案。
延伸閱讀
- 模型訓練概覽
- 模型訓練配置
- 訓練器
- 使用者模擬器
- 管理員儀表盤
- 主動學習
- Potato 倉庫中的模型訓練參考,其中有完整的
sklearn-text參數、留出得分和回寫各節