半教師ありテキスト分類

Beginner

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

はじめに

この実験では、scikit-learn を使用してテキストデータセットに対して半教師あり分類を行う方法を学びます。半教師あり学習は、ラベル付きデータとラベルなしデータの両方を使ってモデルを学習させる機械学習の一種です。この実験では、半教師ありテキスト分類に Self-Training と LabelSpreading アルゴリズムを使用する方法を説明します。モデルの学習とテストには、20 newsgroups データセットを使用します。

VM のヒント

VM の起動が完了したら、左上隅をクリックして Notebook タブに切り替え、Jupyter Notebook を開いて練習を始めましょう。

時には、Jupyter Notebook の読み込みが完了するまで数秒待つ必要があることがあります。Jupyter Notebook の制限により、操作の検証を自動化することはできません。

学習中に問題が発生した場合は、Labby に質問してください。セッション終了後にフィードバックを提供していただければ、迅速に問題を解決します。

データセットの読み込み

ここでは、20 のトピックに関する約 18,000 のニュースグループ投稿が含まれる 20 newsgroups データセットを使用します。このステップでは、データセットを読み込み、その基本情報をいくつか表示します。

import numpy as np
from sklearn.datasets import fetch_20newsgroups

## 最初の 5 つのカテゴリでデータセットを読み込む
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、出現回数行列に対して単語頻度 - 逆文書頻度(TF-IDF)正規化を適用する 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)),
    ]
)

教師ありモデルの学習と評価

このステップでは、データセットを学習セットとテストセットに分割し、ステップ 2 で作成した教師ありモデルのパイプラインを学習させて評価します。

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(
    "Micro-averaged F1 score on test set: %0.3f"
    % f1_score(y_test, y_pred, average="micro")
)

Self-Training のパイプラインの作成

このステップでは、Self-Training を用いた半教師あり学習のパイプラインを作成します。このパイプラインは教師あり学習のパイプラインと似ていますが、SGDClassifier の代わりに SelfTrainingClassifier を使用します。

from sklearn.semi_supervised import SelfTrainingClassifier

## Self-Training のパイプラインを作成する
st_pipeline = Pipeline(
    [
        ("vect", CountVectorizer(**vectorizer_params)),
        ("tfidf", TfidfTransformer()),
        ("clf", SelfTrainingClassifier(SGDClassifier(**sdg_params), verbose=True)),
    ]
)

Self-Training モデルの学習と評価

このステップでは、ラベル付きデータの 20% を使って Self-Training を行います。ラベル付きデータの 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

## Self-Training のパイプラインを学習させて評価する
st_pipeline.fit(X_train, y_train)
y_pred = st_pipeline.predict(X_test)
print(
    "Micro-averaged F1 score on test set: %0.3f"
    % f1_score(y_test, y_pred, average="micro")
)

LabelSpreading のパイプラインの作成

このステップでは、LabelSpreading を用いた半教師あり学習のパイプラインを作成します。このパイプラインは教師あり学習のパイプラインと似ていますが、SGDClassifier の代わりに LabelSpreading アルゴリズムを使用します。

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

## LabelSpreading のパイプラインを作成する
ls_pipeline = Pipeline(
    [
        ("vect", CountVectorizer(**vectorizer_params)),
        ("tfidf", TfidfTransformer()),
        ("toarray", FunctionTransformer(lambda x: x.toarray())),
        ("clf", LabelSpreading()),
    ]
)

LabelSpreading モデルの学習と評価

このステップでは、ラベル付きデータの 20% を使って LabelSpreading を行います。ラベル付きデータの 20% をランダムに選択し、そのデータでモデルを学習させ、そのモデルを使って残りのラベルなしデータのラベルを予測します。

## LabelSpreading のパイプラインを学習させて評価する
ls_pipeline.fit(X_train, y_train)
y_pred = ls_pipeline.predict(X_test)
print(
    "Micro-averaged F1 score on test set: %0.3f"
    % f1_score(y_test, y_pred, average="micro")
)

まとめ

この実験では、scikit-learn を使ってテキストデータセットに対して半教師あり分類を行う方法を学びました。Self-Training と LabelSpreading アルゴリズムを使用してモデルの学習とテストを行いました。半教師あり学習は、ラベル付きデータが限られている場合に役立ち、ラベルなしデータを組み込むことでモデルの性能を向上させることができます。