自分のアノテーションで最初のモデルを学習する
Potatoのサンプルレビュー調査をGPUなしのノートPCで動かし、シミュレーターで120件のレビューにラベルを付け、sklearn-textを学習して、モデルの事前ラベルを確認します。
v2.14.0の新機能
このチュートリアルでは、サンプル調査自身のラベルで感情分類器を学習し、その分類器の答えを次のアノテーターに事前ラベルとして見せます。対象は、Potatoのモデル学習を初めて試す研究の担当者で、すべての手順がGPUなしのノートPCで動き、Potato自体以外のインストールは必要ありません。Potatoのサンプルレビュープロジェクトを起動し、ユーザーシミュレーターにプロジェクトの160件のレビューのうち120件へラベルを付けさせ、管理者用のTrainingページからsklearn-textを学習し、実行のホールドアウトスコアを読み、新しいアノテーターとしてログインしてモデルの答えを確認します。ループの各部分が一般に何をするかは、モデル学習の概要を参照してください。
Potatoとサンプルプロジェクトのインストール
このチュートリアルでは、各レビューをTF-IDF特徴量に変換してロジスティック回帰の分類器を当てはめるトレーナー、sklearn-textを使います。scikit-learnはPotatoの中核の依存パッケージなので、sklearn-textに追加のインストールは要りません。サンプルプロジェクトはpipパッケージではなくPotatoのGitHubリポジトリにあるため、Potatoをインストールしてリポジトリをクローンします。
pip install --upgrade potato-annotation
git clone https://github.com/davidjurgens/potato.git
cd potato以下のコマンドはすべてリポジトリのルートから実行します。プロジェクトフォルダーexamples/ai-assisted/model-training/には3つのファイルがあります。
- **
config.yaml**は研究の設定ファイルで、このチュートリアルのコマンドがコメントに書かれています。 - **
data/reviews.json**には160件の合成の商品レビューが、1行に1つのJSONオブジェクトとして入っており、各オブジェクトはidとtextを持ちます。 - **
data/gold.json**は、すべてのレビューの正解ラベルを持ちます。シミュレーターはこのファイルから答えます。
サンプルの設定
この設定は、ラベルがpositiveとnegativeのradioスキームをsentimentという名前で1つ定義し、モデル学習を有効にします。次のブロックは配布されているファイルからコメントを除いたもので、このファイルは変更せずに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このチュートリアルを形作る設定は4つあります。
- **
num_annotators_per_item: 1**は、各レビューにラベルを1つ付けます。40件ずつ担当する3人のシミュレートされたアノテーターが、異なる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件のレビューにラベルを付ける
モデルが学習するには、人手のラベルが必要です。ユーザーシミュレーターが、3人のアノテーターの代わりをします。シミュレートされたアノテーターはそれぞれ正解ファイルから答え、ほとんどの答えは正解になります。2つ目のターミナルで、リポジトリのルートからシミュレーターを実行します。
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は、3人全員にシミュレーターのgoodプロファイルを割り当てます。goodのアノテーターは0.80から0.90の間で正解率を引き、その確率で各レビューに正しく答えます。そのため、実際のチームのラベルと同じように、ラベルには誤りが含まれます。--sequentialは3人のアノテーターを1人ずつ順に動かし、--fast-modeは答えの間の待ち時間をなくし、--no-exportはシミュレーター自身の結果ファイルの書き出しを省きます。シミュレーターは約2秒で終了し、サマリーを出力します。先に進む前に、サマリーがTotal annotations: 120と報告していることを確認してください。http://localhost:8000でレビューに手作業でラベルを付けることもできます。sklearn-textは、train分割のラベル付き項目が10件に満たないと、学習を拒否します。
管理者キーでTrainingページを開く
Trainingページには、管理者セッションが必要です。http://localhost:8000/adminを開き、ページがAPIキーを求めたらdemo-admin-keyを入力して、http://localhost:8000/admin/trainingを開きます。
サンプルプロジェクトでsklearn-textを2回実行したあとのTrainingページ
上部のStart a runパネルに、設定のすべてがあります。Schemaには、プロジェクトの唯一のスキームであるsentiment (radio)が並びます。Trainerには38のトレーナーがすべて並び、プロジェクトで実行できないトレーナーには、「not installed」のような理由が名前の横に表示されます。sklearn-textがスキームに合う場合、ページはsklearn-textを選んだ状態にします。Runs onには、プロジェクトの唯一のバックエンドであるlocal (this machine)が表示されます。
sklearn-textを学習する
トレーナーの既定値を使うにはParametersを空のままにして、Train nowをクリックします。実行は、アノテーションをレビューごとに1つのラベルに統合し、ラベル付きの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が含まれます。
実行ページとホールドアウトスコア
完了した実行は、trainとvalのサイズ、Held-out scoreとともにRunsテーブルに表示されます。実行IDをクリックすると、/admin/training/runs/<run_id>の実行ページが開きます。実行ページには、実行の状態、トレーナー、バックエンド、分割のサイズ、分割のシード、ホールドアウトスコア、パラメーターが並び、続いてすべての指標の表と実行のログがあります。
実行は、既定でラベル付き項目の15%をval用に、15%をtest用にホールドアウトし、どの項目をどちらに入れるかはsplit_seedが決めます。split_seedの値が変わらない限り分割は実行ごとに同じなので、同じラベルでの2回の実行は、同じ項目でスコアが測られます。sklearn-textの実行のホールドアウトスコアは、valのマクロF1です。マクロF1は、ここではpositiveとnegativeである各ラベルのF1スコアを平均したものです。実行はtestの正解率とマクロ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自身のドキュメントは同じプロジェクトでの実行を1つ報告しており、次の表にその実行と私たちの実行を並べます。
| Potatoのドキュメント | 私たちの実行 | |
|---|---|---|
| ラベル付きレビュー | 120 | 120 |
| train、val、testの項目数 | 記載なし | 76、19、25 |
| ホールドアウトスコア(valのマクロF1) | 0.84 | 0.725 |
| testのマクロF1 | 記載なし | 0.826 |
| 公開された事前ラベル | 80 | 73 |
min_confidence未満の予測 | 4 | 11 |
シミュレートされた各アノテーターは正解率をランダムに引いて異なる誤りをするため、お手元の数値はどちらの列とも異なります。私たちの実行では、シミュレートされたラベルは、120件のレビューのうち97件、valの19件のレビューのうち15件でgold.jsonと一致しました。ホールドアウトスコアが測るのは、正解ファイルとの一致ではなく、アノテーターのラベルとの一致なので、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件のレビュー(valの19件、testの25件、ラベルなしの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など、誰も使っていないユーザー名でログインします。ラベル付きのレビューにはすでにラベルが1つあるので、新しいアノテーターにはラベルなしの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]}}と入力して再び学習すると、単語の組が特徴量に加わります。単語の組を加えた2回目の実行は、同じ分割でvalのマクロF1が0.774になり、74件の事前ラベルを公開しました。sklearn-textが受け取らないパラメーター名は、トレーナーが受け取る名前の一覧とともに拒否されます。 - トランスフォーマーを試す。
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にスキーム名を指定してください。