主动学习
按模型不确定性重排 Potato 的标注队列,包含五种查询策略、sklearn 分类器、冷启动处理,以及跨重启的模型持久化。
主动学习会重排标注队列,让标注者先看到模型最没把握的实例,而不是按语料到达的顺序一路标下去。
工作方式
- 收集一批初始标注
- 在目前已标注的数据上训练一个分类器
- 为未标注池打出不确定性分数
- 重排队列,把最不确定的实例排到前面
- 标注这些被提前的实例
- 随着标注积累定期重新训练
把标注精力用在模型学得最多的地方
配置
基本设置
active_learning:
enabled: true
schema_names:
- sentiment
min_annotations_per_instance: 1
min_instances_for_training: 20
update_frequency: 50
max_instances_to_reorder: 1000min_instances_for_training 默认是 10,update_frequency 默认是 5。在真实项目里这两个默认值都低到值得调高,因为每五条标注就重训一次,算力花在了一个几乎没有变化的模型上。
完整配置
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 | 与已标注内容最不相似的实例 |
badge | Batch Active learning by Diverse Gradient Embeddings |
bald | Bayesian Active Learning by Disagreement |
hybrid | 不确定性与多样性的加权组合 |
active_learning:
query_strategy: uncertainty无法识别的策略名不会让启动失败,而是带一条警告退回 uncertainty,所以某个策略看起来没起作用时,去看日志。
调节 hybrid 和 bald
hybrid 默认按 70% 不确定性、30% 多样性来配:
active_learning:
query_strategy: hybrid
hybrid_weights:
uncertainty: 0.7
diversity: 0.3bald 训练一个小型集成,按集成内部的分歧排序:
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 | 非线性模式 | 中等 |
| SVC | sklearn.svm.SVC | 小数据集 | 慢 |
| 多项式朴素贝叶斯 | sklearn.naive_bayes.MultinomialNB | 简单的文本分类 | 非常快 |
逻辑回归,推荐的起点:
active_learning:
classifier:
name: sklearn.linear_model.LogisticRegression
hyperparameters:
C: 1.0
max_iter: 1000随机森林,用在判定并不接近线性的时候:
active_learning:
classifier:
name: sklearn.ensemble.RandomForestClassifier
hyperparameters:
n_estimators: 100
max_depth: 10SVC 必须被明确要求输出概率,因为不确定性分数依赖概率:
active_learning:
classifier:
name: sklearn.svm.SVC
hyperparameters:
kernel: rbf
probability: true朴素贝叶斯,用在速度比最后一点精度更重要的时候:
active_learning:
classifier:
name: sklearn.naive_bayes.MultinomialNB
hyperparameters:
alpha: 1.0calibrate_probabilities 默认开启,这一点要紧:不确定性采样按预测概率排序,而未经校准的分类器会把自信而错误的条目排得像是已经定下来一样。
向量化器
默认是 sklearn.feature_extraction.text.TfidfVectorizer。
TF-IDF,带权重的特征:
active_learning:
vectorizer:
name: sklearn.feature_extraction.text.TfidfVectorizer
hyperparameters:
max_features: 5000
ngram_range: [1, 2]
stop_words: english纯词频计数:
active_learning:
vectorizer:
name: sklearn.feature_extraction.text.CountVectorizer
hyperparameters:
max_features: 3000
ngram_range: [1, 1]哈希,用在词表大到放不下的时候:
active_learning:
vectorizer:
name: sklearn.feature_extraction.text.HashingVectorizer
hyperparameters:
n_features: 10000ngram_range 写成 YAML 列表,会被转成 sklearn 需要的元组,所以这里写 [1, 2] 就是对的,不需要特别的语法。
冷启动
还没有模型的时候也就无所谓不确定,所以第一批实例是在没有模型的情况下选出来的:
active_learning:
cold_start_strategy: random
cold_start_batch_size: 20由 LLM 辅助选择
LLM 可以在冷启动期间给实例打分,也可以和分类器一起打分:
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: verbalizedconfidence_method 决定置信度分数怎么得到,这个选择要紧,因为挑选实例靠的就是这个分数:
| 方法 | 工作方式 | 取舍 |
|---|---|---|
verbalized | 模型按 1-10 的尺度自报置信度 | 到处都能用,但在某些模型上不可靠 |
logprobs | 由词元对数概率算出 | 校准最好,需要会返回 logprobs 的端点 |
consistency | 同一个实例查询多次,按几次结果的一致程度打分 | 哪里都能用,代价是每个实例 N 次调用 |
端点不返回 logprobs 时,logprobs 会退回自报置信度。
模型持久化
训练好的模型可以在重启后留下来,不必从头重建:
active_learning:
model_persistence:
enabled: true
save_directory: "models/"
retention_count: 5retention_count 默认是 2,限定保留多少个已保存的模型。
基于数据库的持久化已经不能配置了。database_enabled 和 database_config 两个键被移除,因为它们背后那一层只是个桩,记录成功但什么都不存。运行历史现在一律会被记录,不需要配置;配置里若还留着这两个键,也会被忽略。
多个方案
schema_names 列出哪些方案的标注用来训练模型。不设置时,默认取项目中可以打标签的方案:
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完整配置示例
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 辅助帮标注者处理屏幕上已经在显示的内容。两者互不依赖,可以叠加:
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 权限的角色。
实践建议
先攒够基线数据
空集上训练不出任何东西,而用二十条标注训出来的模型,重排的多半是噪声:
active_learning:
min_instances_for_training: 50留意类别平衡
不确定性采样集中在决策边界附近,会让标签分布偏离语料本身。用来训练模型没问题,但把这批标注样本当作对数据的描述来报告就会误导人。random_sample_percent 保留一部分无偏的队列,正是为了这个原因。
不要重训得太频繁
active_learning:
update_frequency: 100让模型跨重启留存
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。
延伸阅读
实现细节见源码文档。