Skip to content
Tutorials5 min read

Solo Mode:1人のアノテーターで10,000件のラベリングを行う方法

PotatoのSolo Modeを使用して、人間とLLMの協調で大規模データセットを効率的にラベリングし、アノテーションコストを最大90%削減するステップバイステップチュートリアル。

Potato Team·

Solo Mode:1人のアノテーターで10,000件のラベリングを行う方法

10,000件の商品レビューをセンチメント(Positive、Neutral、Negative)でラベリングする必要があります。3人のアノテーターを雇ってすべてにラベルを付けるには、数週間と数千ドルのコストがかかります。Solo Modeを使えば、1人のドメインエキスパートが500〜1,000件のインスタンスにのみラベルを付けることで同等の品質を達成でき、LLMが残りを処理します -- 人間はLLMが不確かなすべての判断をレビューします。

このチュートリアルでは、プロセス全体をエンドツーエンドで説明します。


必要なもの

  • Solo Modeエクストラ付きのPotato 2.3.0+:pip install potato-annotation[solo]
  • OpenAIまたはAnthropicのAPIキー(LLMコンポーネント用)
  • JSONL形式のデータセット
  • 1人の知識豊富なアノテーター(あなた自身でも可)

ステップ1:データの準備

data/reviews.jsonlを作成し、1行に1レビューを記載:

json
{"id": "rev_001", "text": "Absolutely love this product! Best purchase I've made all year.", "source": "amazon"}
{"id": "rev_002", "text": "It works fine. Nothing special but gets the job done.", "source": "amazon"}
{"id": "rev_003", "text": "Broke after two weeks. Complete waste of money.", "source": "amazon"}
{"id": "rev_004", "text": "The quality is decent for the price point. I might buy again.", "source": "amazon"}
{"id": "rev_005", "text": "Arrived damaged and customer service was unhelpful.", "source": "amazon"}

このチュートリアルでは、このファイルに10,000件のレビューが含まれているとします。


ステップ2:設定の作成

config.yamlを作成してください:

yaml
task_name: "Product Review Sentiment (Solo Mode)"
task_dir: "."
 
data_files:
  - "data/reviews.jsonl"
 
item_properties:
  id_key: id
  text_key: text
 
# --- Solo Mode Configuration ---
solo_mode:
  enabled: true
 
  llm:
    endpoint_type: openai
    model: "gpt-4o"
    api_key: ${OPENAI_API_KEY}
    temperature: 0.1
    max_tokens: 64
 
  # Quality targets
  seed_count: 50
  accuracy_threshold: 0.93
  confidence_threshold: 0.85
 
  # Phase-specific settings
  phases:
    seed:
      count: 50
      selection: diversity
      embedding_model: "all-MiniLM-L6-v2"
 
    calibration:
      batch_size: 200
      holdout_fraction: 0.2
 
    labeling_functions:
      enabled: true
      max_functions: 15
      min_precision: 0.92
      min_coverage: 0.01
 
    active_labeling:
      batch_size: 25
      strategy: hybrid
      max_batches: 15
 
    refinement_loop:
      max_iterations: 3
      improvement_threshold: 0.02
 
    disagreement_exploration:
      max_instances: 150
      show_llm_reasoning: true
      show_nearest_neighbors: 3
 
    edge_case_synthesis:
      enabled: true
      count: 30
 
    confidence_escalation:
      escalation_budget: 150
      batch_size: 25
      stop_when_stable: true
 
    prompt_optimization:
      enabled: true
      candidates: 8
      metric: f1_macro
 
    final_validation:
      sample_size: 100
      min_accuracy: 0.93
 
  # Instance prioritization
  prioritization:
    pools:
      - name: uncertain
        weight: 0.30
      - name: disagreement
        weight: 0.25
      - name: boundary
        weight: 0.20
      - name: novel
        weight: 0.10
      - name: error_pattern
        weight: 0.10
      - name: random
        weight: 0.05
 
# --- Annotation Schema ---
annotation_schemes:
  - annotation_type: radio
    name: sentiment
    description: "What is the overall sentiment of this review?"
    labels:
      - "Positive"
      - "Neutral"
      - "Negative"
    label_requirement:
      required: true
    sequential_key_binding: true
 
output_annotation_dir: "output/"
output_annotation_format: "jsonl"
 
parquet_export:
  enabled: true
  output_dir: "output/parquet/"

ステップ3:サーバーの起動

bash
potato start config.yaml -p 8000

http://localhost:8000を開いてログインしてください。Solo Modeダッシュボードが表示され、フェーズ1:シードアノテーションにいることが示されます。


ステップ4:フェーズ1 -- シードアノテーション(50件)

Potatoは埋め込みベースのクラスタリングを使用して50件の多様なレビューを選択しています。これらはランダムではなく、データ分布のカバレッジを最大化するように選ばれています。

各レビューにラベルを付けてください。これは最も重要なフェーズです -- シードラベルの品質がLLMの学習効果を決定します。時間をかけて一貫性を保ってください。

所要時間の目安: 1インスタンスあたり20〜30秒で15〜25分。

50件目のインスタンスが完了すると、Potatoは自動的にフェーズ2に進みます。


ステップ5:フェーズ2 -- 初回LLMキャリブレーション

このフェーズは自動的に実行されます。Potatoはあなたの50件のシードラベルをfew-shot例として、200件のインスタンスのバッチをLLMに送信します。その後、10件のホールドアウトシードラベルに対するLLMの予測を比較して、ベースライン精度を推定します。

ダッシュボードに進捗インジケーターが表示されます。LLMプロバイダーによりますが、通常1〜2分かかります。

典型的な結果: LLMは最初のキャリブレーションで75〜85%の精度を達成します。これは想定通りです -- LLMはまだあなたの具体的なアノテーションスタイルを学習していません。


ステップ6:フェーズ3 -- 混同分析

Potatoは、LLMがあなたのラベルと不一致する箇所を示す混同行列を表示します。典型的な出力:

text
Confusion Analysis (Round 1)
============================
Overall Accuracy: 0.82 (target: 0.93)

Top Confusion Pairs:
  Neutral -> Positive:  14 instances (7.0%)
  Negative -> Neutral:   9 instances (4.5%)
  Positive -> Neutral:   4 instances (2.0%)

これはLLMの主な弱点を示しています:中立的なレビューをポジティブに格上げする傾向があります。これはよくあることです -- LLMはしばしばポジティブなセンチメントに偏りがちです。

あなたのアクション: 混同ペアを確認してください。各ペアをクリックして、LLMが間違えた具体的なインスタンスを確認します。これによりLLMの失敗モードを理解できます。


ステップ7:フェーズ4 -- ガイドライン改善

混同分析に基づいて、PotatoはLLM用の改善されたガイドラインを生成します。並べて表示されます:

  • 現在のガイドライン: LLMに使用された初期プロンプト
  • 提案された編集: エラーパターンに基づいてLLMが提案する具体的な変更

例えば、Potatoは以下を追加することを提案するかもしれません:

「製品を『fine』、『okay』、『decent』と表現し、強い感情を伴わないレビューは、再購入に言及していてもNeutralとラベル付けすべきです。」

提案された各編集を確認してください。各項目を承認、修正、または拒否できます。独自の明確化を追加することもできます。

所要時間の目安: 5〜10分。


ステップ8:フェーズ5 -- ラベリング関数生成

Potatoはシードラベルのパターンからプログラマティックなラベリング関数を生成します。これらは簡単なケースを処理する高速で決定論的なルールです:

text
Generated Labeling Functions:
  LF1: Strong positive words (love, amazing, best, excellent)
       Precision: 0.97, Coverage: 0.06
  LF2: Strong negative words (terrible, awful, worst, waste)
       Precision: 0.95, Coverage: 0.04
  LF3: Exclamation + positive adjective
       Precision: 0.94, Coverage: 0.03
  LF4: Return/refund mention + negative context
       Precision: 0.92, Coverage: 0.02
  ...
  Total coverage: 0.18 (1,800 of 10,000 instances)

ラベリング関数はデータセットの18%を92%以上の精度でカバーします。これらのインスタンスは自動的にラベル付けされ、LLMと人間の労力をより難しいケースに集中させます。

あなたのアクション: 生成された関数を確認してください。信頼できないものは無効にしてください。これはオプションです -- Potatoは設定された精度閾値を超える関数のみを保持します。


ステップ9:フェーズ6 -- アクティブラベリング(125〜375件)

これがメインの人間ラベリングフェーズです。Potatoは6プール優先順位付けシステムを使用してインスタンスを選択します:

  • Uncertain(30%):LLMの信頼度が85%未満のレビュー
  • Disagreement(25%):LLMとラベリング関数が異なるラベルを付けるレビュー
  • Boundary(20%):埋め込み空間の決定境界付近のレビュー
  • Novel(10%):これまでラベル付けしたものと異なるレビュー
  • Error pattern(10%):既知の混同パターンに一致するレビュー(例:ぬるま湯的ポジティブ)
  • Random(5%):キャリブレーション用のランダムレビュー

25件のバッチでラベル付けします。各バッチ後、PotatoはLLMの精度推定を更新し、続行するかどうかを判断します。

典型的な推移:

  • バッチ1-3(75件):精度が82%から87%に上昇
  • バッチ4-6(150件):精度が90%に到達
  • バッチ7-10(250件):精度が91〜92%で停滞

精度が93%(設定閾値)に達すると、Solo Modeはフェーズ10にスキップします。そうでなければフェーズ7に進みます。

所要時間の目安: 必要なバッチ数に応じて合計45〜90分。


ステップ10:フェーズ7 -- 自動改善ループ

アクティブラベリング後も精度が閾値を下回る場合、Potatoは改善ループの別のラウンドを実行します:

  1. LLMが更新されたガイドラインとより多くのfew-shot例でデータセット全体を再ラベリング
  2. すべての人間ラベルに対して精度を再計算
  3. 新しい混同パターンを特定
  4. ガイドラインを再度改善

このフェーズはほぼ自動的です。ガイドラインの変更を承認するだけで済みます。

典型的な結果: 改善ラウンドごとに精度が2〜4%向上。


ステップ11:フェーズ8 -- 不一致探索

Potatoは最も論争的なインスタンスを提示します:LLM、ラベリング関数、最近傍分析がすべて異なる答えを出すケースです。各インスタンスについて以下が表示されます:

  • レビューテキスト
  • LLMの予測と信頼度
  • ラベリング関数の投票
  • ラベル付き済みの3つの最近傍例
  • LLMの思考過程の推論

これらは本当に難しいケースです。ここでのあなたのラベルは、プロセス全体のどのアノテーションよりも最も高い限界価値を持ちます。

所要時間の目安: 100〜150件で20〜30分。


ステップ12:フェーズ9 -- エッジケース合成

Potatoは、残りの混同パターンをターゲットとした合成レビューを生成します。例えば、LLMが「再購入に言及する中立的なレビュー」にまだ苦戦している場合、以下のような例を生成します:

「値段の割にはまあまあの製品です。セールがあればもう1つ買うかもしれません。」

これらの合成例にラベルを付けると、LLMのfew-shotコンテキストに追加されます。

所要時間の目安: 30件で10〜15分。


ステップ13:フェーズ10 -- カスケード信頼度エスカレーション

LLMはデータセットの大部分にラベルを付け終わっています。Potatoはすべてのラベル付き済みインスタンスを信頼度でランク付けし、最も信頼度の低いものを25件のバッチであなたに送信します。

text
Confidence Escalation Progress:
  Batch 1: 25 instances, 23/25 correct (92%)
  Batch 2: 25 instances, 24/25 correct (96%)
  Batch 3: 25 instances, 25/25 correct (100%)
  -> Stopping: last 3 batches stable

LLMがすべて正解した3つの連続バッチが確認されると、Solo Modeは残りの高信頼度ラベルが信頼できると判断します。

所要時間の目安: 15〜20分。


ステップ14:フェーズ11 -- プロンプト最適化

このフェーズは自動的に実行されます。Potatoは8つのプロンプトバリアントを試し、蓄積された人間ラベルに対して最も高いF1スコアのものを選択します:

text
Prompt Optimization Results:
  Variant 1 (direct, 5 examples):     F1=0.91
  Variant 2 (CoT, 5 examples):        F1=0.93
  Variant 3 (direct, 10 examples):    F1=0.92
  Variant 4 (CoT, 10 examples):       F1=0.94  <-- selected
  Variant 5 (direct, 15 examples):    F1=0.92
  Variant 6 (CoT, 15 examples):       F1=0.93
  Variant 7 (self-consistency, 5x):   F1=0.94
  Variant 8 (self-consistency, 10x):  F1=0.94

最良のプロンプトが最終再ラベリングパスに使用されます。


ステップ15:フェーズ12 -- 最終検証

Potatoは100件のランダムなLLMラベル付きインスタンスを選択してレビューします。あなたがラベルを付け、PotatoはLLMのラベルと比較します。

text
Final Validation:
  Reviewed: 100 instances
  LLM correct: 94/100 (94%)
  Threshold: 93%
  -> PASSED

LLMの精度が閾値を満たせば、データセットは完了です。満たさない場合、Solo Modeはフェーズ6に戻り、アクティブラベリングの別のラウンドを実行します。

所要時間の目安: 10〜15分。


結果サマリー

全12フェーズを実行した後、最終統計を確認してください:

bash
python -m potato.solo status --config config.yaml
text
Solo Mode Complete
==================
Dataset: 10,000 instances
Total human labels: 612
  Seed: 50
  Active labeling: 275
  Disagreement exploration: 137
  Edge case synthesis: 30
  Confidence escalation: 75
  Final validation: 45

LLM labels: 8,200 (accuracy: 94.1%)
LF labels: 1,800 (precision: 95.3%)
Unlabeled: 0

Final label distribution:
  Positive: 4,823 (48.2%)
  Neutral:  3,011 (30.1%)
  Negative: 2,166 (21.7%)

Total human time: ~3.5 hours
Estimated multi-annotator cost (3x): ~$4,500
Solo Mode cost: ~$450 (API fees) + ~$175 (annotator time)
Savings: ~88%

人間は10,000件中612件(6.1%)にラベルを付けました。LLMとラベリング関数が94%以上の精度で残りを処理しました。


結果のエクスポート

最終ラベル付きデータセットをエクスポート:

bash
python -m potato.solo export --config config.yaml --output final_labels.jsonl

各行にはラベルとそのソースが含まれます:

json
{"id": "rev_001", "sentiment": "Positive", "source": "human", "confidence": 1.0}
{"id": "rev_002", "sentiment": "Neutral", "source": "llm", "confidence": 0.91}
{"id": "rev_003", "sentiment": "Negative", "source": "labeling_function", "confidence": 0.97}

Parquetエクスポートの場合:

python
import pandas as pd
df = pd.read_parquet("output/parquet/annotations.parquet")
print(df["value"].value_counts())

品質保証:ハイブリッド検証

出版品質のデータセットの場合、サンプルのレビューのために2人目のアノテーターを追加してください:

yaml
solo_mode:
  verification:
    enabled: true
    sample_fraction: 0.10
    annotator: "reviewer_1"

これにより1,000件のランダムインスタンスが2人目のアノテーターに割り当てられます。Solo Modeのラベルとレビュアーのラベル間のアノテーター間一致度を計算できます。


トラブルシューティング

LLMの精度が閾値以下で停滞する

  • シード数を増やす: 50件の代わりに75〜100件のシードインスタンスを試す
  • LLMを切り替える: GPT-4oの代わりにclaude-sonnet-4-20250514を試す(またはその逆)
  • 閾値を下げる: 93%が達成不可能な場合、90%がユースケースに許容可能かどうかを検討する
  • データを確認する: 一部のデータセットは本質的に曖昧です。人間-人間の一致度が90%にしかならない場合、LLMにそれ以上を期待しないでください

フェーズ6のバッチ数が多すぎる

  • バッチサイズを増やす: batch_sizeを25から50に変更
  • プール重みを調整する: エスカレーションされたインスタンスのほとんどが「uncertain」プールからの場合、その重みを減らし「disagreement」と「error_pattern」を増やす

ラベリング関数のカバレッジが低い

  • これは強い語彙的シグナルのないタスク(例:皮肉検出、暗黙的センチメント)では正常です
  • ラベリング関数は明示的なキーワード駆動パターンに最も効果的です
  • Solo Modeはラベリング関数なしでも機能します -- LLMがその分を補います

参考資料