在诸多实际问题中,未标记数据大量而已标记数据稀缺。由于人类专家的参与,标签的获取通常昂贵。因此,通过少量的标签实例来训练准确的预测模型至关重要。
主动学习的目的即是:
通过只查询最有价值的实例来减少人类专家在机器学习系统中注释实例的比例,并已成功地应用到各种实际任务中。
原文链接~~
声明:此文为笔者学习Alipy包使用的历程,绝无半点侵权之意。
常规的主动学习过程中,示例查询和模型更新迭代进行。以下是一个具体的例子:
上述过程伪代码如下:
输入:有标签的训练集(仅有少量实例) L L L
while:
查询算法选择一批被认为有价值的无标签数据
oracle根据其已有知识提供一些有监督的信息
新的有标签实例添加到标记集 L L L
更新模型
直到满足特定的停止标准(如,有限的查询数量或有限的成本预算)。
主动学习的研究方向之一是查询策略。学习者将根据特定的查询策略选择一些未标记的数据,并从oracle中查询其标签。在主动学习文献中,各种策略从不同方面进行评估,以此判断实例对模型改进的有用性。
那如何评估不同算法的性能呢?
通过构造学习曲线的方式直观有效,如将查询次数与准确率的关系进行绘制。对于不同的查询策略,由于会选择不同的数据进行查询,将会产生不同的学习曲线。
Alipy提供了一个基于模块的主动学习框架,其目标是用各种工具功能支持实验实现。这些工具是以低耦合的方式设计的,以便用户可以根据自己的习惯来编程实验项目。支持的模块如下:
序列 | 模块名 | 功用 |
---|---|---|
1 | alipy.data_manipulate | 数据划分 |
2 | alipy.query_strategy | 数据查询策略 |
3 | alipy.index.IndexCollection | 索引管理 |
4 | alipy.metric | 模型性能计算 |
5 | alipy.experiment.state/alipy.experiment.state_io | 保存每次查询后的中间结果并从断点恢复程序 |
6 | alipy.experiment.stopping_criteria | 获取停止标准 |
7 | alipy.experiment.experiment_analyser | 可视化实验结果 |
8 | alipy.oracle | 提供清洁、噪音或者代价敏感的oracle |
9 | alipy.utils.multi_thread | 多次实验 |
支持以下算法:
序列 | 算法类型 | 具体算法 |
---|---|---|
1 | 实例选择 | Uncertainty (SIGIR 1994) , Graph Density (CVPR 2012) , QUIRE (TPAMI 2014)、 |
SPAL (AAAI 2019) , Query By Committee (ICML 1998) , Random | ||
BMDR (KDD 2013) , LAL (NIPS 2017) , Expected Error Reduction (ICML 2001)… | ||
2 | 多标签数据 | AUDI (ICDM 2013) , QUIRE (TPAMI 2014) , Random |
MMC (KDD 2009) , Adaptive (IJCAI 2013) … | ||
3 | 特征查询 | AFASMC (KDD 2018) , Stability (ICDM 2013) , Random |
4 | 不同代价 | AL with Different Costs : HALC (IJCAI 2018) , Random |
Cost performance… | ||
5 | 噪声oracle | AL with Noisy Oracles : CEAL (IJCAI 2017) , IEthresh (KDD 2009) |
All , Random… | ||
6 | 特殊查询类型 | AURO (IJCAI 2015) … |
7 | 大规模任务 | Subsampling… |
ToolBox是一个提供所有可用工具类的类,以下说明大致功用。
程序清单1-1: 初始化工具箱
from sklearn.datasets import *
from alipy import ToolBox
import warnings
warnings.filterwarnings("ignore") #忽略警告
def test():
data_set, label_set = load_iris(return_X_y=True) #示例:导入iris数据集;返回实例集合与相应标签
print("The data set:", data_set)
print("The label set", label_set)
alibox = ToolBox(X=data_set, y=label_set, query_type='AllLabels', saving_path='.')
if __name__ == '__main__':
test()
运行结果(注:载入boston数据时会出现未知错误):
...
[5.9 3. 5.1 1.8]]
The label set [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2]
引入的sklearn.datasets自带以下数据集(当然可以自行导入数据集):
数据集名称 | 导入命令 | 用途 |
---|---|---|
鸢尾花数据集 | load_iris() | 用于分类或聚类任务的数据集 |
手写数字数据集 | load_digits() | 用于分类任务或者降维任务的数据集 |
乳腺癌数据集 | load_barest_cancer() | 用于二分类任务的数据集 |
糖尿病数据集 | load_diabetes() | 用于回归任务的数据集 |
波士顿房价数据集 | load_boston() | 用于回归任务的数据集 |
体能训练数据集 | load-linnerud() | 用于多变量回归任务的数据集 |
查询类型有下:
AllLabels | PartLabels | Features |
---|
Alipy默认模型为logistic回归模型,相应的获取、训练、测试方法如下:
程序清单1-2: 获取默认模型及预测
from sklearn.datasets import *
from alipy import ToolBox
import warnings
warnings.filterwarnings("ignore") #忽略警告
def test():
data_set, label_set = load_iris(return_X_y=True) #示例:导入iris数据集;返回实例集合与相应标签
# print("The data set:", data_set)
# print("The label set", label_set)
alibox = ToolBox(X=data_set, y=label_set, query_type='AllLabels', saving_path='.') #一般使用‘AllLabels’
de_model = alibox.get_default_model() #获取默认模型
de_model.fit(data_set, label_set) #传入数据及标签
pred = de_model.predict(data_set) #标签预测
print(pred)
pred = de_model.predict_proba(data_set) #获取概率输出
# print(pred)
if __name__ == '__main__':
test()
运行结果:
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 2 1 1 1
1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2]
ToolBox中有两种获取划分数据的方式:
1)使用alibox.split_AL()并指定参数来划分,即:
train_ind, test_ind, label_ind, unlabel_ind = alibox.split_AL(test_ratio=0.3, initial_label_rate=0.1, split_count=1)
参数说明:
参数名称 | 意义 |
---|---|
test_ratio | 测试集比例 |
initial_label_rate | 测试集中带标签实例的比例 |
split_count | 划分次数 |
train_ind | 训练集 |
test_ind | 测试集 |
label_ind | 训练集中带标签实例 |
unlabel_ind | 训练集中不带标签实例 |
2)方式2与方式1是类似的,需要注意的是返回值均为索引:
alibox.split_AL(test_ratio=0.3, initial_label_rate=0.1, split_count=10)
train_0, test_0, label_0, unlabel_0 = alibox.get_split(round=0)
train_1, test_1, label_1, unlabel_1 = alibox.get_split(round=1)
alipy.index.IndexCollection是一个管理索引的工具类,可通过以下方式创建和使用该类:
程序清单1-3: IndexCollection类使用示例
from sklearn.datasets import *
from alipy import ToolBox
import warnings
warnings.filterwarnings("ignore") #忽略警告
def test1():
data_set, label_set = load_iris(return_X_y=True)
alibox = ToolBox(X=data_set, y=label_set, query_type='AllLabels', saving_path='.')
a = [1, 2, 3]
a_ind = alibox.IndexCollection(a) #这里的a可以是训练集索引或者其他
print("index:", a_ind.index) #索引输出
a_ind.add(10) #添加单个索引;注:索引不会重复
print("add:", a_ind)
a_ind.discard(1) #删除已有索引
print("discard", a_ind)
a_ind.update([4, 5, 10]) #添加多个元素
print("update", a_ind)
a_ind.difference_update([4, 10]) #删除多个元素
print("difference_update", a_ind)
运行结果:
index: [1, 2, 3]
add: [1, 2, 3, 10]
discard [2, 3, 10]
update [2, 3, 10, 4, 5]
difference_update [2, 3, 5]
1)获取clean oracle
clean_oracle = alibox.get_clean_oracle(query_by_example=False, cost_mat=None)
如按特征查询:query_by_example=True。
提供一个索引或一个索引列表,以便查询标签和相应代价(标签为初始化数据集所对应的标签):
label, cost = clean_oracle.query_by_index([1])
如果在初始化时没有指定代价,则默认为1,否则可以设置代价矩阵;代价矩阵的形状应与标签矩阵的形状相同,以便进行代价敏感查询。
2)获取 repository
alibox.get_repository(round=0, instance_flag=False)
round即与之前split_count相对应。当然,若要保存当前所选实例的特征向量则设置:instance_flag=True。
程序清单1-4: clean oracle类使用示例
from sklearn.datasets import *
from alipy import ToolBox
import warnings
warnings.filterwarnings("ignore") #忽略警告
def test2():
data_set, label_set = load_iris(return_X_y=True) #示例:导入iris数据集;返回实例集合与相应标签
alibox = ToolBox(X=data_set, y=label_set, query_type='AllLabels', saving_path='.') #一般使用‘AllLabels’
clean_oracle = alibox.get_clean_oracle(query_by_example=False, cost_mat=None)
label, cost = clean_oracle.query_by_index([1])
print("label:", label)
print("cost:", cost)
if __name__ == '__main__':
test2()
运行结果:
label: [0]
cost: [1]
alipy.experiment.StateIO用于保存或者载入中间结果,几个重要功能如下:
1)以文件形式保存中间结果;
2)在任意次迭代中恢复工作台(标签集和非标签集);
3)程序意外退出时从断点恢复程序;
4)输出主动学习过程:当前迭代次数、当前平均性能、当前代价等。
对于每一次round,可如下保存:
saver = alibox.get_stateio(round=1) #1作为示例
可设置相应参数如下:
st = alibox.State(select_index=select_ind, performance=accuracy, cost=cost, queried_label=queried_label) #此处并为给出select_ind等的定义
saver.add_state(st) #添加参数
saver.save() #保存至文件,默认保存于当前文件夹
Alipy支持的查询策略如下:
序号 | 策略名称 |
---|---|
1 | QueryInstanceQBC |
2 | QueryInstanceUncertainty |
3 | QueryRandom |
4 | QureyExpectedErrorReduction |
5 | QueryInstanceGraphDensity |
6 | QueryInstanceQUIRE |
具体使用如下:
QBCStrategy = alibox.get_query_strategy(strategy_name='QueryInstanceQBC')
注意:QueryInstanceGraphDensity及QueryInstanceQUIRE需要额外指定参数!
支持性能计算方法如下:
序号 | 性能计算方法名 |
---|---|
1 | accuracy_score |
2 | roc_auc_score |
3 | get_fps_tps_thresholds |
4 | hamming_loss |
5 | one_error |
6 | coverage_error |
7 | label_ranking_loss |
8 | label_ranking_average_precision_score |
一个具体的例子如下:
程序清单1-5: 性能计算示例
from sklearn.datasets import *
from alipy import ToolBox
import warnings
warnings.filterwarnings("ignore") #忽略警告
def test3():
data_set, label_set = load_iris(return_X_y=True) #示例:导入iris数据集;返回实例集合与相应标签
alibox = ToolBox(X=data_set, y=label_set, query_type='AllLabels', saving_path='.') #一般使用‘AllLabels’
alibox.split_AL(test_ratio=0.3, initial_label_rate=0.1, split_count=10)
de_model = alibox.get_default_model()
de_model.fit(data_set, label_set)
pred = de_model.predict(data_set)
"""新增"""
acc = alibox.calc_performance_metric(y_true=label_set, y_pred=pred, performance_metric='accuracy_score')
print("accuracy score:", acc)
if __name__ == '__main__':
test3()
运行结果:
accuracy score: 0.96
常用停止标准如下:
序号 | 命令名 | 代表意义 |
---|---|---|
1 | None | 默认:没有未标记实例 |
2 | num_of_queries | 查询数量 |
3 | cost_limit | 代价限制 |
4 | percent_of_unlabel | 未标记实例比例 |
5 | time_limit | 时间限制 |
一个示例如下:
stopping_criterion = alibox.get_stopping_criterion(stopping_criteria='time_limit', value=1)
for i in range(10):
print(i)
while not stopping_criterion.is_stop():
pass
一个示例如下:
analyser = alibox.get_experiment_analyser(x_axis='time_limit') #设置x轴名称
analyser.add_method(method_name='QBC', method_result=QBC_result)
analyser.plot_learning_curves() #绘制
一个示例如下:
alibox.save()
alibox = ToolBox.load('./al_settings.pkl')
alipy.experiment.AlExperiment是一个封装了各种工具并实现了主循环的类。以下为一个具体示例:
程序清单1-6: 一次简单的实验
from sklearn.datasets import load_iris
from alipy.experiment.al_experiment import AlExperiment
import warnings
warnings.filterwarnings("ignore") #忽略警告
def test5():
data_set, label_set = load_iris(return_X_y=True)
al = AlExperiment(data_set, label_set, stopping_criteria='num_of_queries', stopping_value=50)
al.split_AL()
al.set_query_strategy(strategy="QueryInstanceUncertainty", measure='least_confident')
al.set_performance_metric('accuracy_score')
al.start_query(multi_thread=True) #开启多线程
al.plot_learning_curve()
if __name__ == '__main__':
test5()
运行结果:
| 7 | 50 | 00:00:04 | 0.909 ± 0.12 |
| 8 | 50 | 00:00:04 | 0.957 ± 0.04 |
| 9 | 50 | 00:00:04 | 0.698 ± 0.10 |
+-------+-------------------+-------------+--------------------------+
+--------------------------+-------------------+---------------------------+--------------+------------+
| Methods | number_of_queries | number_of_different_split | performance | batch_size |
+--------------------------+-------------------+---------------------------+--------------+------------+
| QueryInstanceUncertainty | 50 | 10 | 0.843 ± 0.08 | 1 |
+--------------------------+-------------------+---------------------------+--------------+------------+
接下来详细讲解具体工具类的用途。
alipy.data_manipulate用于管理实验数据。