主动学习基础:不确定性采样、查询策略与设定

各位同学,今天我们来聊聊主动学习的核心。说白了,就是让模型学会「主动提问」。

我刚开始做这个方向时,总觉得有点玄乎。模型还能自己挑数据学?后来踩过几次坑才明白——这玩意儿本质上是个「效率游戏」。你想想看,标注数据那么贵,能不能让模型自己挑最有价值的样本去学?

一、不确定性采样:模型最「困惑」的地方

不确定性采样,是主动学习里最直观的策略。核心思想很简单:模型对哪些样本最没把握,就优先学哪些

我个人习惯把不确定性分成三类来理解:

  • 熵(Entropy):信息论里的概念,衡量混乱程度。模型输出概率分布越均匀,熵越大,说明它越不确定。
  • 边际(Margin):看最高概率和第二高概率的差值。差值越小,说明模型在两个类别间摇摆不定。
  • 置信度(Confidence):直接看最高概率值。值越低,模型越没信心。

举个例子,假设模型要对一张图片做三分类:

样本 类别A概率 类别B概率 类别C概率 边际 置信度
样本1 0.95 0.03 0.02 0.28 0.92 0.95
样本2 0.40 0.35 0.25 1.08 0.05 0.40
样本3 0.60 0.20 0.20 0.95 0.40 0.60

看到没?样本2的熵最高、边际最小、置信度最低。三种指标都指向同一个结论——这个样本最值得标注

核心原则:不确定性采样不是选「模型已经会的」,而是选「模型最不会的」。

二、查询策略:怎么选?选多少?

策略这东西,说白了就是「怎么挑样本」的规则。我见过不少新手一上来就堆复杂策略,结果效果还不如简单的。嗯,这里要注意:先跑通基线,再谈优化

2.1 不确定性采样策略

刚才提到的三种指标,都可以直接作为查询策略:

  • 熵采样:选熵最大的前K个样本。适合多分类任务。
  • 边际采样:选边际最小的前K个样本。对二分类特别有效。
  • 置信度采样:选置信度最低的前K个样本。计算最简单,但容易受异常值影响。

我在项目中遇到过一个问题:用置信度采样时,模型对某些「噪声样本」特别没信心,结果选了一堆垃圾数据回来。后来我加了多样性约束,才把效果拉回来。

2.2 池化设定 vs 流式设定

这两个概念,决定了你的主动学习系统怎么「喂数据」。

特性 池化设定 流式设定
数据来源 预先收集好的未标注池 实时到达的数据流
查询时机 每轮从池中批量选 每个样本到达时决定是否标注
典型场景 图像分类、文本分类 推荐系统、实时监控
计算开销 每轮需评估整个池 每个样本单独评估

我个人更推荐新手从池化设定开始。为什么?因为好调试。你可以把整个未标注池存下来,反复实验不同的查询策略。流式设定虽然更贴近真实场景,但一旦策略选错,数据就流走了,想追都追不回来。

小技巧:做池化设定时,建议每轮选5%-10%的池子大小。选太多,模型学不到新东西;选太少,迭代太慢。

三、知识体系总览

下面这张图,是我自己总结的主动学习核心逻辑。你看一遍,应该能对整体框架有个清晰印象。

主动学习核心逻辑框架 未标注数据池 大量无标签样本 查询策略 熵 / 边际 / 置信度 人工标注 专家标注最有价值样本 模型训练 用标注数据更新模型 性能评估 循环迭代 图例: 数据流 迭代反馈

四、代码实战:一个最小实现

光说不练假把式。下面这个代码片段,是我常用的不确定性采样实现。你直接拿去改改就能用。

import numpy as np

def uncertainty_sampling(probs, strategy='entropy', k=10):
    """
    probs: 模型输出的概率矩阵,shape=(n_samples, n_classes)
    strategy: 'entropy' | 'margin' | 'confidence'
    k: 要选择的样本数
    """
    if strategy == 'entropy':
        # 熵:-sum(p * log(p))
        scores = -np.sum(probs * np.log(probs + 1e-10), axis=1)
    elif strategy == 'margin':
        # 边际:最高概率 - 第二高概率
        sorted_probs = np.sort(probs, axis=1)
        scores = sorted_probs[:, -1] - sorted_probs[:, -2]
        # 边际越小越不确定,取负号
        scores = -scores
    elif strategy == 'confidence':
        # 置信度:最高概率
        scores = -np.max(probs, axis=1)
    else:
        raise ValueError(f"Unknown strategy: {strategy}")
    
    # 选分数最高的k个样本
    selected_indices = np.argsort(scores)[-k:]
    return selected_indices

# 使用示例
probs = np.array([
    [0.95, 0.03, 0.02],
    [0.40, 0.35, 0.25],
    [0.60, 0.20, 0.20]
])
indices = uncertainty_sampling(probs, strategy='entropy', k=2)
print(f"选中的样本索引: {indices}")
# 输出: [1 2]  —— 样本1和样本2最不确定

避坑指南:我曾经在熵的计算里忘了加 1e-10,结果 log(0) 直接报错。这种小细节,跑一次线上就记住了。

五、实际项目中的选择建议

说了这么多,到底该用哪种策略?我根据经验给个参考:

  • 二分类任务:边际采样效果最好,计算也快。
  • 多分类任务:熵采样更稳定,但计算稍慢。
  • 类别极度不平衡:置信度采样 + 类别平衡约束,防止模型只挑多数类样本。
  • 流式设定:用阈值法,置信度低于某个阈值就标注。阈值设0.6-0.7比较安全。

嗯,最后提醒一句:没有银弹。我见过有人在图像任务上用熵采样效果爆炸,换到文本任务上就拉胯。多试几种策略,用验证集跑一轮,哪个好就用哪个。


公众号:蓝海资料掘金营,微信deep3321