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。

延伸阅读

实现细节见源码文档。