Skip to content

能動学習

Potatoのアノテーションキューをモデルの不確実性で並べ替えます。5つのクエリ戦略、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です。どちらも実際のプロジェクトでは引き上げる価値があるほど低い値です。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が認識するのもこの形式です。最初の2つのサブキーはnameとhyperparameters、3つ目のサブキーはenabled、save_directory、retention_countです。これ以外のサブキー名を使ったブロックは警告なしに無視され、実行は黙ってデフォルトに戻ります。

クエリ戦略

戦略説明
uncertaintyモデルの確信度が最も低いインスタンス。デフォルト
diversityすでにアノテーションされたものと最も似ていないインスタンス
badgeBatch Active learning by Diverse Gradient Embeddings
baldBayesian Active Learning by Disagreement
hybriduncertaintyとdiversityの重み付き組み合わせ
yaml
active_learning:
  query_strategy: uncertainty

認識できない戦略名は起動を失敗させず、警告を出してuncertaintyにフォールバックします。戦略が効いていないように見える場合はログを確認してください。

HybridとBALDの調整

hybridのデフォルトはuncertainty 70%、diversity 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です。

分類器パス適した用途速度
Logistic Regressionsklearn.linear_model.LogisticRegressionほとんどのテキストタスク高速
Random Forestsklearn.ensemble.RandomForestClassifier非線形なパターン中程度
SVCsklearn.svm.SVC小規模データセット低速
Multinomial NBsklearn.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

精度の最後の1ポイントより速度が重要な場合のナイーブベイズ:

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はverbalizedの確信度にフォールバックします。

モデルの永続化

訓練済みモデルは、ゼロから作り直すのではなく再起動後も保持できます:

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を持つロールが必要です。

ベストプラクティス

まずベースラインを集める

空の集合では何も訓練できず、20件のアノテーションで訓練したモデルはほとんどノイズを並べ替えるだけです:

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に達しているか、すべてのクラスに少なくとも1つの例があるかを確認してください。1クラスしかない訓練セットでは分類器を学習できません。

並べ替えが効いていないように見える

ログで未知のquery_strategyがないか確認してください。未知の戦略は黙ってuncertaintyにフォールバックします。enabled: trueになっていること、schema_namesが実際に存在するスキームを指していることも確認してください。

プールが大きいと遅い

max_instances_to_reorderを減らすか、update_frequencyを増やすか、非常に大きな語彙にはHashingVectorizerに切り替えてください。

関連情報

実装の詳細については、ソースドキュメントを参照してください。