半监督文本分类

Beginner

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

简介

在本实验中,你将学习如何使用 scikit-learn 对文本数据集进行半监督分类。半监督学习是一种机器学习类型,其中模型在有标签和无标签的数据上进行训练。本实验将介绍如何使用自训练和标签传播算法进行半监督文本分类。我们将使用 20 新闻组数据集来训练和测试我们的模型。

虚拟机提示

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

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

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

加载数据集

我们将使用 20 新闻组数据集,该数据集包含约 18,000 篇关于 20 个主题的新闻组文章。在这一步中,我们将加载数据集并打印出有关它的一些基本信息。

import numpy as np
from sklearn.datasets import fetch_20newsgroups

## 加载前五个类别的数据集
data = fetch_20newsgroups(
    subset="train",
    categories=[
        "alt.atheism",
        "comp.graphics",
        "comp.os.ms-windows.misc",
        "comp.sys.ibm.pc.hardware",
        "comp.sys.mac.hardware",
    ],
)

## 打印有关数据集的信息
print("%d documents" % len(data.filenames))
print("%d categories" % len(data.target_names))

创建监督学习管道

在这一步中,我们将创建一个用于监督学习的管道。该管道将由一个 CountVectorizer(用于将文本数据转换为词元计数矩阵)、一个 TfidfTransformer(用于对计数矩阵应用词频 - 逆文档频率归一化)和一个 SGDClassifier(用于训练模型)组成。

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.linear_model import SGDClassifier
from sklearn.pipeline import Pipeline

## SGDClassifier 的参数
sdg_params = dict(alpha=1e-5, penalty="l2", loss="log_loss")

## CountVectorizer 的参数
vectorizer_params = dict(ngram_range=(1, 2), min_df=5, max_df=0.8)

## 创建管道
pipeline = Pipeline(
    [
        ("vect", CountVectorizer(**vectorizer_params)),
        ("tfidf", TfidfTransformer()),
        ("clf", SGDClassifier(**sdg_params)),
    ]
)

训练和评估监督模型

在这一步中,我们将把数据集拆分为训练集和测试集,然后训练并评估我们在第二步中创建的监督模型管道。

from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score

## 将数据集拆分为训练集和测试集
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(X, y)

## 训练并评估监督模型管道
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
print(
    "测试集上的微平均 F1 分数:%0.3f"
    % f1_score(y_test, y_pred, average="micro")
)

创建自训练管道

在这一步中,我们将创建一个使用自训练的半监督学习管道。该管道将与监督管道类似,但我们将使用 SelfTrainingClassifier 而不是 SGDClassifier。

from sklearn.semi_supervised import SelfTrainingClassifier

## 创建自训练管道
st_pipeline = Pipeline(
    [
        ("vect", CountVectorizer(**vectorizer_params)),
        ("tfidf", TfidfTransformer()),
        ("clf", SelfTrainingClassifier(SGDClassifier(**sdg_params), verbose=True)),
    ]
)

训练和评估自训练模型

在这一步中,我们将对 20% 的已标注数据使用自训练方法。我们将随机选择 20% 的已标注数据,在这些数据上训练模型,然后使用该模型为其余未标注数据预测标签。

import numpy as np

## 选择 20% 的训练数据
y_mask = np.random.rand(len(y_train)) < 0.2
X_20, y_20 = map(
    list, zip(*((x, y) for x, y, m in zip(X_train, y_train, y_mask) if m))
)

## 将未掩码的子集设置为未标注
y_train[~y_mask] = -1

## 训练并评估自训练管道
st_pipeline.fit(X_train, y_train)
y_pred = st_pipeline.predict(X_test)
print(
    "测试集上的微平均 F1 分数:%0.3f"
    % f1_score(y_test, y_pred, average="micro")
)

创建标签传播管道

在这一步中,我们将创建一个使用标签传播的半监督学习管道。该管道将与监督管道类似,但我们将使用标签传播算法代替 SGDClassifier。

from sklearn.semi_supervised import LabelSpreading
from sklearn.preprocessing import FunctionTransformer

## 创建标签传播管道
ls_pipeline = Pipeline(
    [
        ("vect", CountVectorizer(**vectorizer_params)),
        ("tfidf", TfidfTransformer()),
        ("toarray", FunctionTransformer(lambda x: x.toarray())),
        ("clf", LabelSpreading()),
    ]
)

训练和评估标签传播模型

在这一步中,我们将对 20% 的已标注数据使用标签传播方法。我们将随机选择 20% 的已标注数据,在这些数据上训练模型,然后使用该模型为其余未标注数据预测标签。

## 训练并评估标签传播管道
ls_pipeline.fit(X_train, y_train)
y_pred = ls_pipeline.predict(X_test)
print(
    "测试集上的微平均 F1 分数:%0.3f"
    % f1_score(y_test, y_pred, average="micro")
)

总结

在这个实验中,我们学习了如何使用 scikit-learn 对文本数据集进行半监督分类。我们使用了自训练和标签传播算法来训练和测试我们的模型。当可用的标注数据量有限时,半监督学习会很有用,并且它可以通过纳入未标注数据来帮助提高模型的性能。