半监督学习是指那些试图同时利用有标签和无标签训练数据的算法。
半监督学习算法与只能从有标签训练数据中学习的有监督学习算法不同。
半监督学习的一个流行方法是创建一个连接训练数据集中样本的图,并通过图的边传播已知标签,为无标签样本进行标注。这种半监督学习方法的一个例子是用于分类预测建模的标签传播算法。
在本教程中,您将了解如何将标签传播算法应用于半监督学习分类数据集。
完成本教程后,您将了解:
- 了解标签传播半监督学习算法的工作原理。
- 如何开发半监督分类数据集并使用有监督学习算法建立性能基线。
- 如何开发和评估标签传播算法,并使用模型输出来训练有监督学习算法。
让我们开始吧。

使用标签传播的半监督学习
照片由 TheBluesDude 拍摄,部分权利保留。
教程概述
本教程分为三个部分;它们是:
- 标签传播算法
- 半监督分类数据集
- 用于半监督学习的标签传播
标签传播算法
标签传播是一种半监督学习算法。
该算法由 Xiaojin Zhu 和 Zoubin Ghahramani 于 2002 年的技术报告《Learning From Labeled And Unlabeled Data With Label Propagation》提出。
该算法的直观思路是创建一个图,根据样本(行)之间的距离(例如 欧几里得距离)将它们连接起来。然后,图中的节点会根据图中邻近样本的标签或标签分布,获得软标签或标签分布。
许多半监督学习算法依赖于由有标签和无标签样本共同诱导的数据几何形状,以改进仅使用有标签数据进行学习的有监督方法。这种几何形状可以自然地表示为经验图 g = (V,E),其中节点 V = {1,…,n} 代表训练数据,边 E 代表它们之间的相似性。
— 第 193 页,《Semi-Supervised Learning》,2006 年。
传播是指标签被分配到图中的节点,并通过图的边传播到连接的节点,这种迭代的性质。
这个过程有时被称为标签传播,因为它将标签从(固定的)有标签顶点“传播”到所有无标签顶点。
— 第 48 页,《Introduction to Semi-Supervised Learning》,2009 年。
该过程会重复固定次数的迭代,以加强分配给无标签样本的标签。
从标记有已知标签(1 或 -1)的节点 1、2、…、l 和标记为 0 的节点 l + 1、…、n 开始,每个节点开始将其标签传播给其邻居,然后重复该过程直至收敛。
— 第 194 页,《Semi-Supervised Learning》,2006 年。
现在我们对标签传播算法有了了解,让我们看看如何在项目中使用它。首先,我们必须定义一个半监督分类数据集。
半监督分类数据集
在本节中,我们将定义一个半监督学习数据集,并在此数据集上建立性能基线。
首先,我们可以使用 make_classification() 函数 定义一个合成分类数据集。
我们将定义一个具有两个类别(二分类)、两个输入变量和 1000 个样本的数据集。
1 2 3 |
... # 定义数据集 X, y = make_classification(n_samples=1000, n_features=2, n_informative=2, n_redundant=0, random_state=1) |
接下来,我们将数据集分成训练集和测试集,比例为 50/50(例如,每部分 500 行)。
1 2 3 |
... # 分割成训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.50, random_state=1, stratify=y) |
最后,我们将训练数据集再次分成两半,一部分将有标签,另一部分我们将假装它没有标签。
1 2 3 |
... # 将训练集分为有标签和无标签 X_train_lab, X_test_unlab, y_train_lab, y_test_unlab = train_test_split(X_train, y_train, test_size=0.50, random_state=1, stratify=y_train) |
总而言之,准备半监督学习数据集的完整示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 准备半监督学习数据集 from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split # 定义数据集 X, y = make_classification(n_samples=1000, n_features=2, n_informative=2, n_redundant=0, random_state=1) # 分割成训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.50, random_state=1, stratify=y) # 将训练集分为有标签和无标签 X_train_lab, X_test_unlab, y_train_lab, y_test_unlab = train_test_split(X_train, y_train, test_size=0.50, random_state=1, stratify=y_train) # 总结训练集大小 print('有标签训练集:', X_train_lab.shape, y_train_lab.shape) print('无标签训练集:', X_test_unlab.shape, y_test_unlab.shape) # 总结测试集大小 print('测试集:', X_test.shape, y_test.shape) |
运行示例会准备数据集,然后总结这三个部分的形状。
结果证实我们有一个包含 500 行的测试数据集,一个包含 250 行的有标签训练数据集,以及 250 行的无标签数据。
1 2 3 |
有标签训练集: (250, 2) (250,) 无标签训练集: (250, 2) (250,) 测试集: (500, 2) (500,) |
有监督学习算法将只有 250 行数据用于训练模型。
半监督学习算法将拥有这 250 个有标签行以及 250 个无标签行,这些无标签行可以通过多种方式用于改进有标签训练数据集。
接下来,我们可以使用有监督学习算法在半监督学习数据集上建立性能基线,该算法仅根据有标签训练数据进行拟合。
这一点很重要,因为我们期望半监督学习算法的性能优于仅根据有标签数据拟合的有监督学习算法。如果不是这种情况,那么半监督学习算法就没有效果。
在本例中,我们将使用逻辑回归算法,该算法基于有标签训练数据集的标签进行拟合。
1 2 3 4 5 |
... # 定义模型 model = LogisticRegression() # 在有标签数据集上拟合模型 model.fit(X_train_lab, y_train_lab) |
然后可以使用该模型对整个保留的测试数据集进行预测,并使用分类准确率进行评估。
1 2 3 4 5 6 7 |
... # 对保留的测试集进行预测 yhat = model.predict(X_test) # 计算测试集的分数 score = accuracy_score(y_test, yhat) # 总结分数 print('准确率: %.3f' % (score*100)) |
总而言之,在半监督学习数据集上评估有监督学习算法的完整示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# 在半监督学习数据集上的基线性能 from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score 从 sklearn.线性模型 导入 LogisticRegression # 定义数据集 X, y = make_classification(n_samples=1000, n_features=2, n_informative=2, n_redundant=0, random_state=1) # 分割成训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.50, random_state=1, stratify=y) # 将训练集分为有标签和无标签 X_train_lab, X_test_unlab, y_train_lab, y_test_unlab = train_test_split(X_train, y_train, test_size=0.50, random_state=1, stratify=y_train) # 定义模型 model = LogisticRegression() # 在有标签数据集上拟合模型 model.fit(X_train_lab, y_train_lab) # 对保留的测试集进行预测 yhat = model.predict(X_test) # 计算测试集的分数 score = accuracy_score(y_test, yhat) # 总结分数 print('准确率: %.3f' % (score*100)) |
运行算法会根据有标签训练数据集拟合模型,并在保留数据集上进行评估,然后打印分类准确率。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。请考虑运行示例几次并比较平均结果。
在本例中,我们可以看到该算法实现的分类准确率约为 84.8%。
我们期望一个有效的半监督学习算法比这获得更高的准确率。
1 |
准确率: 84.800 |
接下来,让我们探讨如何将标签传播算法应用于该数据集。
用于半监督学习的标签传播
标签传播算法可以通过 scikit-learn Python 机器学习库中的 LabelPropagation 类 来使用。
该模型可以像任何其他分类模型一样,通过调用 `fit()` 函数来拟合,并通过 `predict()` 函数为新数据进行预测。
1 2 3 4 5 6 7 |
... # 定义模型 model = LabelPropagation() # 在训练数据集上拟合模型 model.fit(..., ...) # 对保留的测试集进行预测 yhat = model.predict(...) |
重要的是,提供给 `fit()` 函数的训练数据集必须包含整数编码的有标签样本(与正常情况一样),以及标记为 -1 的无标签样本。
然后,模型将在拟合模型的过程中为无标签样本确定标签。
模型拟合后,训练数据集中的有标签和无标签数据的估计标签可以通过 `LabelPropagation` 类的 “`transduction_`” 属性获得。
1 2 3 |
... # 获取整个训练数据集数据的标签 tran_labels = model.transduction_ |
现在我们了解了如何在 scikit-learn 中使用标签传播算法,让我们看看如何将其应用于我们的半监督学习数据集。
首先,我们必须准备训练数据集。
我们可以将训练数据集的输入数据连接成一个数组。
1 2 3 |
... # 创建训练数据集输入 X_train_mixed = concatenate((X_train_lab, X_test_unlab)) |
然后,我们可以为训练数据集中的每个无标签样本创建一个值为 -1 的列表。
1 2 3 |
... # 为无标签数据创建“无标签” nolabel = [-1 for _ in range(len(y_test_unlab))] |
然后可以将此列表与训练数据集的标签部分中的标签连接起来,以匹配训练数据集的输入数组。
1 2 3 |
... # 重组训练数据集标签 y_train_mixed = concatenate((y_train_lab, nolabel)) |
我们现在可以对整个训练数据集训练 `LabelPropagation` 模型。
1 2 3 4 5 |
... # 定义模型 model = LabelPropagation() # 在训练数据集上拟合模型 model.fit(X_train_mixed, y_train_mixed) |
接下来,我们可以使用该模型对保留数据集进行预测,并使用分类准确率评估模型。
1 2 3 4 5 6 7 |
... # 对保留的测试集进行预测 yhat = model.predict(X_test) # 计算测试集的分数 score = accuracy_score(y_test, yhat) # 总结分数 print('准确率: %.3f' % (score*100)) |
总而言之,在半监督学习数据集上评估标签传播的完整示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# 在半监督学习数据集上评估标签传播 from numpy import concatenate from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score from sklearn.semi_supervised import LabelPropagation # 定义数据集 X, y = make_classification(n_samples=1000, n_features=2, n_informative=2, n_redundant=0, random_state=1) # 分割成训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.50, random_state=1, stratify=y) # 将训练集分为有标签和无标签 X_train_lab, X_test_unlab, y_train_lab, y_test_unlab = train_test_split(X_train, y_train, test_size=0.50, random_state=1, stratify=y_train) # 创建训练数据集输入 X_train_mixed = concatenate((X_train_lab, X_test_unlab)) # 为无标签数据创建“无标签” nolabel = [-1 for _ in range(len(y_test_unlab))] # 重组训练数据集标签 y_train_mixed = concatenate((y_train_lab, nolabel)) # 定义模型 model = LabelPropagation() # 在训练数据集上拟合模型 model.fit(X_train_mixed, y_train_mixed) # 对保留的测试集进行预测 yhat = model.predict(X_test) # 计算测试集的分数 score = accuracy_score(y_test, yhat) # 总结分数 print('准确率: %.3f' % (score*100)) |
运行算法会根据整个训练数据集拟合模型,然后在保留数据集上进行评估,并打印分类准确率。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。请考虑运行示例几次并比较平均结果。
在本例中,我们可以看到标签传播模型在半监督学习数据集上实现了约 85.6% 的分类准确率,这略高于仅在有标签训练数据集上拟合的逻辑回归(准确率为约 84.8%)。
1 |
准确率: 85.600 |
到目前为止,一切顺利。
我们可以使用的另一种方法是采用半监督模型的估计标签,然后拟合一个有监督学习模型。
回想一下,我们可以通过以下方式从标签传播模型中检索整个训练数据集的标签。
1 2 3 |
... # 获取整个训练数据集数据的标签 tran_labels = model.transduction_ |
然后,我们可以将这些标签与所有输入数据一起用于训练和评估有监督学习算法,例如逻辑回归模型。
希望的是,在整个训练数据集上拟合的有监督学习模型将获得比半监督学习模型本身更好的性能。
1 2 3 4 5 6 7 8 9 10 11 |
... # 定义有监督学习模型 model2 = LogisticRegression() # 在整个训练数据集上拟合有监督学习模型 model2.fit(X_train_mixed, tran_labels) # 对保留的测试集进行预测 yhat = model2.predict(X_test) # 计算测试集的分数 score = accuracy_score(y_test, yhat) # 总结分数 print('准确率: %.3f' % (score*100)) |
总而言之,使用估计的训练集标签来训练和评估有监督学习模型的完整示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# 评估在标签传播上拟合的逻辑回归用于半监督学习 from numpy import concatenate from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score from sklearn.semi_supervised import LabelPropagation 从 sklearn.线性模型 导入 LogisticRegression # 定义数据集 X, y = make_classification(n_samples=1000, n_features=2, n_informative=2, n_redundant=0, random_state=1) # 分割成训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.50, random_state=1, stratify=y) # 将训练集分为有标签和无标签 X_train_lab, X_test_unlab, y_train_lab, y_test_unlab = train_test_split(X_train, y_train, test_size=0.50, random_state=1, stratify=y_train) # 创建训练数据集输入 X_train_mixed = concatenate((X_train_lab, X_test_unlab)) # 为无标签数据创建“无标签” nolabel = [-1 for _ in range(len(y_test_unlab))] # 重组训练数据集标签 y_train_mixed = concatenate((y_train_lab, nolabel)) # 定义模型 model = LabelPropagation() # 在训练数据集上拟合模型 model.fit(X_train_mixed, y_train_mixed) # 获取整个训练数据集数据的标签 tran_labels = model.transduction_ # 定义有监督学习模型 model2 = LogisticRegression() # 在整个训练数据集上拟合有监督学习模型 model2.fit(X_train_mixed, tran_labels) # 对保留的测试集进行预测 yhat = model2.predict(X_test) # 计算测试集的分数 score = accuracy_score(y_test, yhat) # 总结分数 print('准确率: %.3f' % (score*100)) |
运行算法会根据整个训练数据集拟合半监督模型,然后根据推断的标签在整个训练数据集上拟合有监督学习模型,并在保留数据集上进行评估,打印分类准确率。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。请考虑运行示例几次并比较平均结果。
在本例中,我们可以看到这种分层方法(先是半监督模型,然后是有监督模型)在保留数据集上实现了约 86.2% 的分类准确率,甚至优于单独使用的半监督学习(准确率为约 85.6%)。
1 |
准确率: 86.200 |
您能否通过调整 LabelPropagation 模型的超参数来获得更好的结果?
请在下面的评论中告诉我您的发现。
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
书籍
- 半监督学习导论, 2009.
- 第 11 章:标签传播和二次准则,《Semi-Supervised Learning》,2006 年。
论文
- 从有标签和无标签数据中进行标签传播学习, 2002.
API
- sklearn.semi_supervised.LabelPropagation API.
- 第 1.14 节。半监督学习,Scikit-Learn 用户指南.
- sklearn.model_selection.train_test_split API.
- sklearn.linear_model.LogisticRegression API.
- sklearn.datasets.make_classification API.
文章
总结
在本教程中,您了解了如何将标签传播算法应用于半监督学习分类数据集。
具体来说,你学到了:
- 了解标签传播半监督学习算法的工作原理。
- 如何开发半监督分类数据集并使用有监督学习算法建立性能基线。
- 如何开发和评估标签传播算法,并使用模型输出来训练有监督学习算法。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
非常感谢您的贡献,Jason。
我喜欢你的博客,也很乐意向你学习!
继续保持出色的工作。你太棒了!
致以最美好的祝愿和新年快乐
不客气,感谢您的支持和美言!
嘿 Jason,新年快乐!
我发现这篇文章是对半监督学习的一个很好的初步了解,它确实是一个令人兴奋的领域!我想问一下,您是否会考虑制作一个关于它的系列,比如从头开始创建一些算法,然后借助 sklearn 来实际处理数据集?我确实有您列出的关于 SSL 的两本书,但我希望有一些实际的编码教程来将那些相当密集的理论付诸实践。另外,您对基于图的(深度)机器学习有什么看法?我对此也很感兴趣,因为我对计算生物学很感兴趣,而计算生物学确实经常使用图。您对此也有一些看法,并打算写几篇帖子吗?
此致!
Model2 – 看起来您创建了一个对无标签数据进行分类的分类器,然后将其预测用于保留集……那么为什么不创建两个逻辑回归呢?
Model2 是根据半监督学习算法分配给训练数据集的标签来拟合的。
谢谢,也祝您新年快乐!
是的,我还有几篇关于该主题的教程已经写好并安排发布。
好建议,我想深入研究并从头开始编写算法。谢谢。
Jason,
我必须感谢您花时间和精力制作所有这些非常有趣的 بابەت。
继续保持出色的工作。新年快乐!
此致
不客气!
一如既往,清晰的阐述,Jason。
在已经有一个模型的情况下再添加一个分类模型,似乎只会增加错误。这感觉就像二次制作蛋糕……尽管在真正的蛋糕领域,提拉米苏就是这样做的,而且这肯定是一件好事。
理论上,为什么我们期望一个分类模型(这里是逻辑模型)能提高准确率?
实践上,您知道有任何文献证明这一点吗?
谢谢!
谢谢。
不确定是否同意。在转换后的训练集上拟合的模型提供了另一种解释数据的方式。
一如既往,进行评估并与替代方案进行比较,并使用最适合您的特定数据集+模型的方案。
感谢您的回答。
在一个真实的数据集上进行了尝试。传播了大约 10% 的有标签观测值到其余部分。传播的标签准确率约为 45%。
在传播的标签上添加了一个 CNN。它在传播标签上的性能非常好——但当然这并不相关。那是回声室;二次制作的蛋糕。
但是 CNN 在原始的约 10% 有标签观测值上的性能准确率超过 80%。
哇。太棒了。
很酷,干得好!
感谢分享您的成功。
如何对回归进行半监督学习?
它通常用于分类,但也有用于回归的算法。
抱歉,我没有例子,也许“进一步阅读”部分的一些资源会有帮助。
感谢 Jason 撰写了关于标签传播的精炼文章。
还有哪些其他半监督学习技术?sci-kit learn 链接(您提供的)也提到了自训练——您似乎在上面的教程中使用了(与标签传播一起)——还有其他的吗?
不客气。
好问题,这可能是个不错的起点
https://en.wikipedia.org/wiki/Semi-supervised_learning
嗨 Jason
很棒的文章。在 sklearn 文档中,它并没有说明使用 -1 来标记无标签数据。您从哪里获得此信息?我尝试了其他一些随机值,正值和负值,而 -1 的效果最好。
另外,如果标记目标(y_train_lab)中碰巧存在一些 -1,会发生什么?
谢谢。
Jon
谢谢。
来自文档:(无标签点标记为 -1)
在此页面上
https://scikit-learn.cn/stable/modules/generated/sklearn.semi_supervised.LabelPropagation.html
嗨,Jason,
对半监督学习的非常好的阐释。请问您在多标签分类方面有使用半监督学习的经验吗?我知道 scikit-multilearn 是一个用于多标签分类的优秀包,但我无法弄清楚如何将其与 sklearn 的自学习分类器结合。很想听听您的意见!
谢谢!
手头没有,抱歉。
Jason,这篇文章写得很棒,谢谢分享!
在我阅读这个方法的过程中,我开始思考
这与以下做法有什么区别?
a. 构建一个普通的分类器,将其应用于未标记数据
b. 过滤出最高精度(例如,精度>90%)的前10%。
c. 将最高精度的结果重新注入到数据集中,从而扩展标记数据集
d. 再次进行分类,即回到a。
感觉这与您上面描述的方法有些相似,还是我遗漏了什么?
有趣的是,我们应该能够通过比较这两种方法来测试这一点
此外,通过我描述的方法,我们可以使用所有典型的算法
Alexandre,你好……不客气!你的理解和建议的步骤是正确的。