自训练中变化阈值的影响

Beginner

This tutorial is from open-source community. Access the source code

简介

本实验展示了不同阈值对自训练的影响。加载了breast_cancer数据集,并删除了标签,使得 569 个样本中只有 50 个有标签。在这个数据集上使用不同的阈值拟合了一个SelfTrainingClassifier

虚拟机使用提示

虚拟机启动完成后,点击左上角切换到笔记本标签页,以访问 Jupyter Notebook 进行练习。

有时,你可能需要等待几秒钟让 Jupyter Notebook 完成加载。由于 Jupyter Notebook 的限制,操作的验证无法自动化。

如果你在学习过程中遇到问题,随时向 Labby 提问。课程结束后提供反馈,我们会及时为你解决问题。

导入库

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.svm import SVC
from sklearn.model_selection import StratifiedKFold
from sklearn.semi_supervised import SelfTrainingClassifier
from sklearn.metrics import accuracy_score
from sklearn.utils import shuffle

首先,我们将导入本实验所需的库。

加载数据

X, y = datasets.load_breast_cancer(return_X_y=True)
X, y = shuffle(X, y, random_state=42)
y_true = y.copy()
y[50:] = -1
total_samples = y.shape[0]

加载并打乱breast_cancer数据集。然后我们将真实标签复制到y_true,并从y中移除前 50 个样本之外的所有标签。这将用于模拟半监督学习场景。

定义分类器

base_classifier = SVC(probability=True, gamma=0.001, random_state=42)

我们将基础分类器定义为一个支持向量机(SVM),其伽马值(gamma)设为较低的 0.001。

定义阈值

x_values = np.arange(0.4, 1.05, 0.05)
x_values = np.append(x_values, 0.99999)

我们定义了一个阈值数组,范围从 0.4 到 1,步长为 0.05。然后我们添加了一个非常高的阈值 0.99999,以确保包含一个不会产生任何自标记样本的阈值。

定义用于存储结果的数组

scores = np.empty((x_values.shape[0], n_splits))
amount_labeled = np.empty((x_values.shape[0], n_splits))
amount_iterations = np.empty((x_values.shape[0], n_splits))

我们定义数组来存储实验结果。

不同阈值下的自训练

for i, threshold in enumerate(x_values):
    self_training_clf = SelfTrainingClassifier(base_classifier, threshold=threshold)

    skfolds = StratifiedKFold(n_splits=n_splits)
    for fold, (train_index, test_index) in enumerate(skfolds.split(X, y)):
        X_train = X[train_index]
        y_train = y[train_index]
        X_test = X[test_index]
        y_test = y[test_index]
        y_test_true = y_true[test_index]

        self_training_clf.fit(X_train, y_train)

        amount_labeled[i, fold] = (
            total_samples
            - np.unique(self_training_clf.labeled_iter_, return_counts=True)[1][0]
        )

        amount_iterations[i, fold] = np.max(self_training_clf.labeled_iter_)

        y_pred = self_training_clf.predict(X_test)
        scores[i, fold] = accuracy_score(y_test_true, y_pred)

我们使用基础分类器和 scikit-learn 中的SelfTrainingClassifier类,在不同阈值下进行自训练。我们使用分层 k 折交叉验证将数据拆分为训练集和测试集。然后我们在训练集上拟合自训练分类器,并计算分类器在测试集上的准确率。我们还存储了每个折的标记样本数量和迭代次数。

可视化结果

ax1 = plt.subplot(211)
ax1.errorbar(
    x_values, scores.mean(axis=1), yerr=scores.std(axis=1), capsize=2, color="b"
)
ax1.set_ylabel("Accuracy", color="b")
ax1.tick_params("y", colors="b")

ax2 = ax1.twinx()
ax2.errorbar(
    x_values,
    amount_labeled.mean(axis=1),
    yerr=amount_labeled.std(axis=1),
    capsize=2,
    color="g",
)
ax2.set_ylim(bottom=0)
ax2.set_ylabel("Amount of labeled samples", color="g")
ax2.tick_params("y", colors="g")

ax3 = plt.subplot(212, sharex=ax1)
ax3.errorbar(
    x_values,
    amount_iterations.mean(axis=1),
    yerr=amount_iterations.std(axis=1),
    capsize=2,
    color="b",
)
ax3.set_ylim(bottom=0)
ax3.set_ylabel("Amount of iterations")
ax3.set_xlabel("Threshold")

plt.show()

我们使用 Matplotlib 绘制实验结果。上图展示了分类器在拟合结束时可用的标记样本数量以及分类器的准确率。下图展示了标记样本的最后一次迭代。

总结

在这个实验中,我们学习了如何使用 scikit-learn 在不同阈值下进行自训练。我们发现最优阈值介于非常低和非常高的阈值之间,并且选择合适的阈值可以显著提高准确率。