Skip to content

主動學習

按模型不確定性重排 Potato 的標註佇列,包含五種查詢策略、sklearn 分類器、冷啟動處理,以及跨重啟的模型持久化。

主動學習會重排標註佇列,讓標註者先看到模型最沒把握的實例,而不是按語料到達的順序一路標下去。

工作方式

  1. 收集一批初始標註
  2. 在目前已標註的資料上訓練一個分類器
  3. 為未標註池打出不確定性分數
  4. 重排佇列,把最不確定的實例排到前面
  5. 標註這些被提前的實例
  6. 隨著標註積累定期重新訓練

主動學習迴圈:在已標註資料上訓練,按不確定性為未標註池打分,標註最不確定的項目,然後重新訓練把標註精力用在模型學得最多的地方

配置

基本設定

yaml
active_learning:
  enabled: true
  schema_names:
    - sentiment
 
  min_annotations_per_instance: 1
  min_instances_for_training: 20
  update_frequency: 50
  max_instances_to_reorder: 1000

min_instances_for_training 預設是 10,update_frequency 預設是 5。在真實項目裡這兩個預設值都低到值得調高,因為每五條標註就重訓一次,算力花在了一個幾乎沒有變化的模型上。

完整配置

yaml
active_learning:
  enabled: true
 
  schema_names:
    - sentiment
 
  min_annotations_per_instance: 1
  min_instances_for_training: 20
  update_frequency: 50
  max_instances_to_reorder: 1000
 
  # How disagreement between annotators resolves into one training label
  resolution_strategy: majority_vote
 
  # Fraction of the queue left in random order, so the model does not
  # only ever see what it already found interesting
  random_sample_percent: 0.2
 
  classifier:
    name: sklearn.linear_model.LogisticRegression
    hyperparameters:
      C: 1.0
      max_iter: 1000
 
  vectorizer:
    name: sklearn.feature_extraction.text.TfidfVectorizer
    hyperparameters:
      max_features: 5000
      ngram_range: [1, 2]
 
  query_strategy: uncertainty
 
  model_persistence:
    enabled: true
    save_directory: "models/"
    retention_count: 5

巢狀的 classifier、vectorizer 和 model_persistence 塊是文件中的寫法,也是 potato validate --strict 認得的寫法。前兩者的子鍵是 name 和 hyperparameters,第三個的子鍵是 enabled、save_directory 和 retention_count。用了別的子鍵名的塊會被忽略且不報錯,這次執行會悄悄退回預設值。

查詢策略

策略說明
uncertainty模型最沒把握的實例。預設值
diversity與已標註內容最不相似的實例
badgeBatch Active learning by Diverse Gradient Embeddings
baldBayesian Active Learning by Disagreement
hybrid不確定性與多樣性的加權組合
yaml
active_learning:
  query_strategy: uncertainty

無法識別的策略名不會讓啟動失敗,而是帶一條警告退回 uncertainty,所以某個策略看起來沒起作用時,去看日誌。

調節 hybrid 和 bald

hybrid 預設按 70% 不確定性、30% 多樣性來配:

yaml
active_learning:
  query_strategy: hybrid
  hybrid_weights:
    uncertainty: 0.7
    diversity: 0.3

bald 訓練一個小型整合,按整合內部的分歧排序:

yaml
active_learning:
  query_strategy: bald
  bald_params:
    n_estimators: 5
    bootstrap_fraction: 0.8

分類器

分類器用它在 sklearn 裡的匯入路徑來指定。預設是 sklearn.linear_model.LogisticRegression。

分類器路徑適用場景速度
邏輯迴歸sklearn.linear_model.LogisticRegression多數文本任務快
隨機森林sklearn.ensemble.RandomForestClassifier非線性模式中等
SVCsklearn.svm.SVC小資料集慢
多項式樸素貝葉斯sklearn.naive_bayes.MultinomialNB簡單的文本分類非常快

邏輯迴歸,推薦的起點:

yaml
active_learning:
  classifier:
    name: sklearn.linear_model.LogisticRegression
    hyperparameters:
      C: 1.0
      max_iter: 1000

隨機森林,用在判定並不接近線性的時候:

yaml
active_learning:
  classifier:
    name: sklearn.ensemble.RandomForestClassifier
    hyperparameters:
      n_estimators: 100
      max_depth: 10

SVC 必須被明確要求輸出機率,因為不確定性分數依賴機率:

yaml
active_learning:
  classifier:
    name: sklearn.svm.SVC
    hyperparameters:
      kernel: rbf
      probability: true

樸素貝葉斯,用在速度比最後一點精度更重要的時候:

yaml
active_learning:
  classifier:
    name: sklearn.naive_bayes.MultinomialNB
    hyperparameters:
      alpha: 1.0

calibrate_probabilities 預設開啟,這一點要緊:不確定性取樣按預測機率排序,而未經校準的分類器會把自信而錯誤的條目排得像是已經定下來一樣。

向量化器

預設是 sklearn.feature_extraction.text.TfidfVectorizer。

TF-IDF,帶權重的特徵:

yaml
active_learning:
  vectorizer:
    name: sklearn.feature_extraction.text.TfidfVectorizer
    hyperparameters:
      max_features: 5000
      ngram_range: [1, 2]
      stop_words: english

純詞頻計數:

yaml
active_learning:
  vectorizer:
    name: sklearn.feature_extraction.text.CountVectorizer
    hyperparameters:
      max_features: 3000
      ngram_range: [1, 1]

雜湊,用在詞表大到放不下的時候:

yaml
active_learning:
  vectorizer:
    name: sklearn.feature_extraction.text.HashingVectorizer
    hyperparameters:
      n_features: 10000

ngram_range 寫成 YAML 列表,會被轉成 sklearn 需要的元組,所以這裡寫 [1, 2] 就是對的,不需要特別的語法。

冷啟動

還沒有模型的時候也就無所謂不確定,所以第一批實例是在沒有模型的情況下選出來的:

yaml
active_learning:
  cold_start_strategy: random
  cold_start_batch_size: 20

由 LLM 輔助選擇

LLM 可以在冷啟動期間給實例打分,也可以和分類器一起打分:

yaml
active_learning:
  enabled: true
  schema_names:
    - sentiment
 
  llm:
    enabled: true
    endpoint_type: vllm
    base_url: http://localhost:8000/v1
    model: meta-llama/Llama-3.2-3B-Instruct
    confidence_method: verbalized

confidence_method 決定置信度分數怎麼得到,這個選擇要緊,因為挑選實例靠的就是這個分數:

方法工作方式取捨
verbalized模型按 1-10 的尺度自報置信度到處都能用,但在某些模型上不可靠
logprobs由詞元對數機率算出校準最好,需要會返回 logprobs 的端點
consistency同一個實例查詢多次,按幾次結果的一致程度打分哪裡都能用,代價是每個實例 N 次呼叫

端點不返回 logprobs 時,logprobs 會退回自報置信度。

模型持久化

訓練好的模型可以在重啟後留下來,不必從頭重建:

yaml
active_learning:
  model_persistence:
    enabled: true
    save_directory: "models/"
    retention_count: 5

retention_count 預設是 2,限定保留多少個已儲存的模型。

基於資料庫的持久化已經不能配置了。database_enabled 和 database_config 兩個鍵被移除,因為它們背後那一層只是個樁,記錄成功但什麼都不存。執行歷史現在一律會被記錄,不需要配置;配置裡若還留著這兩個鍵,也會被忽略。

多個方案

schema_names 列出哪些方案的標註用來訓練模型。不設定時,預設取項目中可以打標籤的方案:

yaml
annotation_schemes:
  - annotation_type: radio
    name: sentiment
    description: "What is the sentiment?"
    labels: [Positive, Negative, Neutral]
 
  - annotation_type: radio
    name: topic
    description: "What is the topic?"
    labels: [Politics, Sports, Tech, Entertainment]
 
active_learning:
  enabled: true
  schema_names:
    - sentiment
    - topic

完整配置示例

yaml
annotation_task_name: "Sentiment Analysis with Active Learning"
task_dir: "."
port: 8000
 
data_files:
  - "data/reviews.json"
 
item_properties:
  id_key: id
  text_key: text
 
annotation_schemes:
  - annotation_type: radio
    name: sentiment
    description: "What is the sentiment?"
    labels:
      - Positive
      - Negative
      - Neutral
    sequential_key_binding: true
 
active_learning:
  enabled: true
  schema_names:
    - sentiment
 
  min_annotations_per_instance: 1
  min_instances_for_training: 30
  update_frequency: 50
  max_instances_to_reorder: 500
  random_sample_percent: 0.2
 
  classifier:
    name: sklearn.linear_model.LogisticRegression
    hyperparameters:
      C: 1.0
      max_iter: 1000
 
  vectorizer:
    name: sklearn.feature_extraction.text.TfidfVectorizer
    hyperparameters:
      max_features: 5000
      ngram_range: [1, 2]
 
  query_strategy: uncertainty
 
  model_persistence:
    enabled: true
    save_directory: "models/"
    retention_count: 5
 
output_annotation_dir: "output/"
export_annotation_format: jsonl
 
user_config:
  allow_all_users: true

與 AI 輔助組合使用

主動學習決定接下來展示什麼,AI 輔助幫標註者處理螢幕上已經在顯示的內容。兩者互不依賴,可以疊加:

yaml
active_learning:
  enabled: true
  schema_names:
    - sentiment
  min_instances_for_training: 30
 
ai_support:
  enabled: true
  endpoint_type: openai
  ai_config:
    model: gpt-4o-mini
    api_key: ${OPENAI_API_KEY}
    max_tokens: 800
    include:
      all: true

解讀結果時要當心。如果模型給出一個標籤建議、標註者接受了它,而挑出這個實例的又是同一族模型,那最後測到的一致性裡有一部分是模型在跟自己保持一致。

監控

管理員面板會報告模型準確率、訓練輪次數、不確定性的分佈,以及還剩多少實例。訪問它需要管理員金鑰,或者一個帶 view_admin_dashboard 許可權的角色。

實踐建議

先攢夠基線資料

空集上訓練不出任何東西,而用二十條標註訓出來的模型,重排的多半是噪聲:

yaml
active_learning:
  min_instances_for_training: 50

留意類別平衡

不確定性取樣集中在決策邊界附近,會讓標籤分佈偏離語料本身。用來訓練模型沒問題,但把這批標註樣本當作對資料的描述來報告就會誤導人。random_sample_percent 保留一部分無偏的佇列,正是為了這個原因。

不要重訓得太頻繁

yaml
active_learning:
  update_frequency: 100

讓模型跨重啟留存

yaml
active_learning:
  model_persistence:
    enabled: true
    save_directory: "models/"

故障排查

訓練一直不開始

檢查標註量是否達到了 min_instances_for_training,以及每個類別是否至少有一個樣例。只有一個類別的訓練集擬合不出分類器。

重排看起來沒有效果

去日誌裡找有沒有無法識別的 query_strategy,它會悄悄退回 uncertainty。確認 enabled: true,並確認 schema_names 寫的方案確實存在。

池子很大時速度慢

調低 max_instances_to_reorder,調高 update_frequency,或者在詞表非常大時改用 HashingVectorizer。

延伸閱讀

實現細節見原始碼文件。