标注有错误?教你使用置信学习将样本错误标识出来!
前言
置信学习也叫信心学习(Confident Learning ,CL),它是一个新兴的、原则性的框架,用于识别标签错误、描述标签噪声。
网上对于这个置信学习描述也挺少的,主要有这一篇:《数据集中存在错误标注怎么办? 置信学习帮你解决》。它主要介绍了置信学习是什么,有什么用。总体来讲是翻译了国外的一个博客,这里大致讲述一下其内容,有很多晦涩难懂的。
首先抛出了一个惊人的事实,那就是我们常用的ImageNet 中可能至少有 10 万个标签有问题。这是令人吃惊的,因为我们都会认为标注的标签是正确的。但是,对于从事标注的人员来说,这应该是司空见惯的,因为标注本来就存在主观性,至少存在客观歧义性。要知道,目前ImageNet中总共有14197122幅图像,总共分为21841个类别。(私下认为,在2万多个类别情况下,做到了只有1%的错误,这才是应该吃惊的吧。)
不管怎么说,我们使用置信学习,就可以获得这10万个有问题的标签。并且附了下图来展示结果。
上图显示了使用置信学习发现的 2012 ILSVRC ImageNet 训练集中的标签错误的例子。为了便于解释,我们将使用 CL 在 ImageNet 中发现的标签问题分为三类:
多标签图像(蓝色):在图像中有多个标签
本体论问题(绿色):包括“是”或 “有”两种关系,在这些情况下,数据集应该包含其中一类
标签错误(红色):数据集别的类的标签比给定的类标签更适合于某个示例
这样看起来确实很不错,通过置信学习,我们可以去识别出数据集中那些潜在的标注错误,对于进行数据清理和修改是一个非常不错的选择。最重要的是,这个框架适用于几乎所有的模型。(因为找到这些标注的问题完全是通过预测概率和真实标签获得的,与模型无关。)
而后,它给出了一个置信学习的流程图,帮助我们理解置信学习的过程,不过这里我不多赘述,可以直接看原文。
最后讲了一些置信学习的优点和原理。
总的来说,置信学习主要有2个功能,一个功能是用于发现标注错误的标签,另一个功能则是增强模型的性能表现。但是原文没有特别注意如何实现,或者实现的具体内容,我这里重点讲一下如何发现标注错误的标签的具体代码,想看论文原文的同学可以点击《原文》,想看官方指南的可以点击《官方指南》。
如何发现标注错误标签
上面说了这么多好处,最终我们还是要落到实处啊。在官方介绍中,它声称1行代码就可以发现潜在的错误标签,如下所示。
# Compute psx (n x m matrix of predicted probabilities)
# in your favorite framework on your own first, with any classifier.
# Be sure to compute psx in an out-of-sample way (e.g. cross-validation)
# Label errors are ordered by likelihood of being an error.
# First index in the output list is the most likely error.
from cleanlab.pruning import get_noise_indices
ordered_label_errors = get_noise_indices(
s=numpy_array_of_noisy_labels,
psx=numpy_array_of_predicted_probabilities,
sorted_index_method='normalized_margin', # Orders label errors
)
这就是一个被封装好的函数(对啊,不然怎么1行实现所有代码?),可以直接获得标注错误的索引。就是这么简单,你只需要准备好s和psx即可。
这里的S其实就是Y,只不过它为了说明标注中存在错误(Noisy Label
),才使用S,这里的维度是(n,1),即标签的索引即可。而PSX则是使用自己的模型预测出的标签概率分布,它的维度理应是(n,k),其中k为类型数量,n为样本数量,不过要说明的是,在函数的注释里,建议使用3倍以上的交叉验证获得样本。
那我们需要做什么?只需要以下4步:
- 利用我们自己的模型,使用交叉验证的方式,记录下真实标签和模型预测的概率分布(即没有被argmax的预测结果)。
- 将交叉验证的所有结果拼接在一起,使用numpy.save保存为两个文件,如
real.npy
和predict.npy
。 - 将两个文件使用Numpy.load读取,填充到
numpy_array_of_noisy_labels
和numpy_array_of_predicted_probabilities
中。 - 利用上面1行代码进行预测,获得的结果就是所有的可能错误的索引,其可能的正确标签就是概率分布中最大的那个。
这个置信学习的准确程度还是和你的模型性能好坏有关,它们在Imagenet和MNIST上出色的表现完全是因为它们原有模型的性能已经非常好了,例如在MNIST使用的是AlexNet,该模型可以跑出93%的准确性。而如果你使用的模型本身就不是非常厉害的话,那还是挺不可信的(尽管是信心学习)。
结语
嗯?这就完了?没错!好吧,还有个彩蛋,那就是介绍一下如何用它增强你的模型表现吧,只需要下面3行。
from cleanlab.classification import LearningWithNoisyLabels
from sklearn.linear_model import LogisticRegression
# Wrap around any classifier (scikit-learn, PyTorch, TensorFlow, FastText, etc.)
lnl = LearningWithNoisyLabels(clf=LogisticRegression())
lnl.fit(X=X_train_data, s=train_noisy_labels)
# Estimate the predictions you would have gotten by training with *no* label errors.
predicted_test_labels = lnl.predict(X_test)
这里使用的是LogisticRegression()模型,你也可以使用任何你喜欢的模型,只需要实现下面4个方法即可。
from sklearn.base import BaseEstimator
class YourFavoriteModel(BaseEstimator): # Inherits scikit-learn base classifier
'''Let's your model be used by LearningWithNoisyLabels'''
def __init__(self, ):
pass
def fit(self, X, y, sample_weight = None):
pass
def predict(self, X):
pass
def predict_proba(self, X):
pass
def score(self, X, y, sample_weight = None):
pass
# Now you can use `cleanlab.classification.LearningWithNoisyLabels` like this:
from cleanlab.classification import LearningWithNoisyLabels
lnl = LearningWithNoisyLabels(clf=YourFavoriteModel())
lnl.fit(train_data, train_labels_with_errors)
行了,今天的讲解就到这了!下期见!
下一篇: 2018.8.18T1(线段树)