Skip to content
教程閱讀約需 11 分鐘

Solo Mode:一個標註者如何標註 10,000 個樣本

Potato Solo Mode 分步教程:先自己標註一個種子集,其餘交給 LLM 標註,再用準確率檢查決定何時停手。

Potato Team

你有 10,000 條產品評論要做情感標註(正面、中性、負面)。僱三名標註者把全部資料標完要花幾週時間和幾千美元。Solo Mode 讓一位領域專家只標註 500-1,000 個實例就拿到相當的品質;其餘交給 LLM,而 LLM 拿不準的那些由人來審。

本教程把整個流程走一遍。


你需要準備什麼

  • Potato 2.3.0 或更高版本,並安裝 LLM 提供商所需的 AI 擴充套件:pip install potato-annotation[ai]
  • 一個 OpenAI 或 Anthropic API 金鑰(用於 LLM 元件)
  • JSONL 格式的資料集
  • 一位懂行的標註者(可以是你自己)

第 1 步:準備資料

建立 data/reviews.jsonl,每行一條評論:

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
annotation_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
 
  # The model that labels. A list, so you can give fallbacks.
  labeling_models:
    - endpoint_type: openai
      model: "gpt-4o"
      api_key: ${OPENAI_API_KEY}
      temperature: 0.1
      max_tokens: 1024
 
  embedding:
    model_name: "all-MiniLM-L6-v2"
 
  uncertainty:
    strategy: direct_confidence
 
  # When you are allowed to stop labeling
  thresholds:
    end_human_annotation_agreement: 0.93
    minimum_validation_sample: 50
    confidence_low: 0.5
    confidence_high: 0.85
    periodic_review_interval: 100
 
  # What decides which instance you see next
  instance_selection:
    low_confidence_weight: 0.30
    disagreement_weight: 0.25
    diversity_weight: 0.20
    llm_predicted_weight: 0.15
    random_weight: 0.10
 
  batches:
    llm_labeling_batch: 25
    max_parallel_labels: 200
 
  labeling_functions:
    enabled: true
    auto_extract: true
    max_functions: 15
    min_confidence: 0.92
    min_coverage: 3
 
  edge_case_rules:
    enabled: true
    auto_extract_on_labeling: true
 
  confusion_analysis:
    enabled: true
    min_instances_for_pattern: 3
    max_patterns: 20
 
  prompt_optimization:
    enabled: true
    target_accuracy: 0.93
 
  refinement_loop:
    enabled: true
    trigger_interval: 50
    min_improvement: 0.02
    max_cycles: 3
 
  state_dir: "solo_state_reviews"
 
# --- 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/"
export_annotation_format:
  - jsonl
  - parquet

第 3 步:啟動伺服器

bash
potato start config.yaml -p 8000

開啟 http://localhost:8000 並登入。Solo Mode 面板出現,顯示項目處於設定狀態,等你審閱它合成出來的提示。


第 4 步:並行標註 -- 你的前 50 個

Potato 用基於嵌入的聚類挑出了 50 條多樣化的評論。它們不是隨機選的,而是儘可能廣地覆蓋你的資料分佈。

逐條標註。這是最要緊的階段,因為種子標籤的品質決定了 LLM 能學到的上限。慢慢來,保持一致。

預計時間: 每個實例 20-30 秒,共 15-25 分鐘。

標夠一定數量之後,LLM 不用你吩咐就會和你並行標註起來。


第 5 步:LLM 追上進度

這個階段自己跑。Potato 把一批 200 個實例連同你的 50 個種子標籤作為少樣本示例一起發給 LLM,然後拿預測結果和 10 個留出的種子標籤比對,估算基線準確率。

面板裡會出現一個進度指示。視 LLM 提供商而定,通常要 1-2 分鐘。

典型結果: 第一次校準時 LLM 的準確率落在 75-85%。這是預料之中的,它還沒學會你的標註風格。


第 6 步:混淆分析

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 標錯的具體實例,這是弄清它怎麼出錯的最快辦法。


第 7 步:回到提示審閱

基於混淆分析,Potato 為 LLM 起草一份最佳化過的指南,並並排展示:一邊是當前的提示,另一邊是它從錯誤模式中提出的具體修改。

例如,Potato 可能建議加上:

"Reviews that describe a product as 'fine', 'okay', or 'decent' without strong emotion should be labeled Neutral, even if they mention buying again."

把建議的修改逐條過一遍,批准、修改或拒絕。你也可以寫進自己的說明。

預計時間: 5-10 分鐘。


第 8 步:標註函式

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)

標註函式以 92% 以上的精確率覆蓋了資料集的 18%。這些實例會被自動標註,把 LLM 和人的精力騰出來對付更難的情況。

你的操作: 把生成的函式看一遍,停用看起來不可靠的。這一步可選,因為 Potato 本來就只保留超過你所配精確率閾值的函式。


第 9 步:主動標註(125-375 個實例)

你的大部分標註工作在這裡。Potato 按上面 instance_selection 裡設定的加權池來挑選實例:

  • low_confidence_weight(0.30):LLM 置信度最低的評論
  • disagreement_weight(0.25):LLM 和標註函式給出不同標籤的評論
  • diversity_weight(0.20):為鋪開嵌入空間而非扎堆而選出的評論
  • llm_predicted_weight(0.15):由 LLM 標註、被拉回來供人工比對的評論
  • random_weight(0.10):用於校準的隨機評論

還有兩個權重,這裡保持預設的 0,所以不起任何作用:edge_case_rule_weight,針對匹配已發現的邊緣案例規則的實例;以及 cartography_weight,針對置信度在各輪之間來回擺動的實例。Potato 會檢查這些權重之和是否為 1.0,不是的話記一條警告,但它仍按寫下的數值使用,所以得你自己把它們湊齊。

你以 25 個為一批標註。每批之後,Potato 更新 LLM 的準確率估計,並決定要不要繼續。

典型軌跡:

  • 第 1-3 批(75 個實例):準確率從 82% 升至 87%
  • 第 4-6 批(150 個實例):準確率達到 90%
  • 第 7-10 批(250 個實例):準確率在 91-92% 趨於平穩

如果一致性達到你設的 end_human_annotation_agreement,Solo Mode 就轉向自主標註。否則它繼續在審閱和最佳化之間迴圈。

預計時間: 總計 45-90 分鐘,取決於需要多少批。


第 10 步:最佳化迴圈

如果主動標註之後準確率仍低於閾值,Potato 再跑一輪最佳化迴圈:

  1. LLM 用更新後的指南和更多少樣本示例重新標註整個資料集
  2. Potato 對照所有人工標籤重新計算準確率
  3. 它找出新的混淆模式
  4. 它再次最佳化指南

這個階段基本不用你動手。你只需要批准指南的改動。

典型結果: 每輪最佳化準確率提高 2-4%。


第 11 步:分歧消解

Potato 呈現最有爭議的實例:LLM、標註函式和最近鄰分析三者給出不同答案的情況。每個實例你會看到:

  • 評論文本
  • LLM 預測和置信度
  • 標註函式投票
  • 3 個最近的已標註示例及其標籤
  • LLM 的思維鏈推理

這些是難啃的情況,你在這裡給出的標籤,比流程中其他地方的分量更重。

預計時間: 100-150 個實例需 20-30 分鐘。


第 12 步:邊緣案例合成

Potato 針對剩下的混淆模式生成合成評論。比如,如果 LLM 在"提到會再買的中性評論"上仍然吃力,它會生成這樣的例子:

"It's an okay product for the price. I might get another one if there's a sale."

你標註這些合成示例,Potato 把它們加進 LLM 的少樣本上下文。

預計時間: 30 個示例需 10-15 分鐘。


第 13 步:定期審閱

到這時 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

一旦準確率連著三批不再變動,Solo Mode 就把剩下的高置信度標籤當作可信的。注意這看的是穩定而不是完美:上面這一輪在 92%、96%、100% 之後停下,是因為數字已經穩住了,不是因為最後一批全對。

預計時間: 15-20 分鐘。


第 14 步:提示最佳化

這個階段自己跑。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 步:最終驗證

Potato 抽出 100 個 LLM 標註的實例交給你稽核。你來標,Potato 把你的標籤和 LLM 的比對。

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

如果 LLM 過了你設的閾值,項目就完成了。如果沒過,它退回主動標註再來一輪 -- 這是最終驗證唯一允許的轉移。

預計時間: 10-15 分鐘。


結果彙總

最後,面板會報告這些標籤各自的來源。Solo Mode 沒有命令列介面 -- 它跑在伺服器裡,由它自己的頁面驅動 -- 所以這份彙總是從執行中的實例裡看到的,不是從終端:

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%。其餘的由 LLM 和標註函式以 94% 以上的準確率處理。


匯出結果

Solo Mode 的寫入走的是和其他項目一樣的儲存,所以標籤也通過普通的匯出器取出:

bash
python -m potato.export -c config.yaml -f jsonl -o ./export/

每行包含標籤及其來源:

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/exports/parquet/annotations.parquet")
print(df["sentiment"].value_counts())

annotations.parquet 是每個實例、每個標註者一行,每個標註方案攤平成自己的一列,所以你按方案名取列,而不是取一個通用的 value 列。


品質保證:混合驗證

要做出版級品質的資料集,加一位標註者來稽核一個樣本。Solo Mode 源文件更詳細地講了各種驗證選項。

Solo Mode 自己沒有驗證配置塊,所以這就是普通的第二遍:把標籤匯出,作為一個普通項目重新送回 Potato,由一位稽核者過 10% 的樣本,再像對待任意兩名標註者那樣計算稽核者與 Solo Mode 標籤之間的一致性。(icl_labeling 確實有一個 verification 塊,但那屬於上下文內標註,不屬於 Solo Mode。)


故障排除

LLM 準確率在閾值以下停滯

  • 增加種子數量: 試 75-100 個種子實例,而不是 50 個
  • 更換 LLM: 試試用 claude-sonnet-4-20250514 代替 GPT-4o(或者反過來)
  • 降低閾值: 如果 93% 達不到,想想 90% 對你的用例是否可以接受
  • 檢查資料: 有些資料集本身就是模糊的。如果人與人之間的一致性也只有 90%,就別指望 LLM 做得更好

主動標註需要太多批次

  • 增大批次: 把 batch_size 從 25 改成 50
  • 調整池權重: 如果升級上來的實例大多來自低置信度池,就降低 low_confidence_weight,提高 disagreement_weight 或 diversity_weight

標註函式覆蓋率低

  • 對於沒有強詞彙訊號的任務(如諷刺檢測、隱含情感),這是正常的
  • 標註函式對顯式的、由關鍵詞驅動的模式效果最好
  • 沒有標註函式,Solo Mode 照樣能用,這些實例改由 LLM 覆蓋

延伸閱讀