機械学習MACHINE LEARNING

疑似ラベルとは?ラベルのないデータをモデルの予測で学習に使う方法

疑似ラベル(pseudo-label)は、正解ラベルの付いていないデータに対して、学習済みのモデルが出した予測を正解の代わりに付けたものである。疑似ラベルを付けたデータを学習データに足してモデルを学習し直す方法を、疑似ラベル学習、または自己学習(self-training)と呼ぶ。深層学習では Lee が 2013 年に手法として整理し、その後、画像分類の研究で改良が続いている。

ラベルのないデータを使う理由

教師あり学習は、入力と正解ラベルの組からモデルを学習する。精度はラベル付きデータの量に左右されるが、ラベルを付ける作業には人手がかかる。医療画像のように専門家でなければラベルを付けられない分野では、画像は大量にあるのにラベルは少ししか無い、という状況が普通に起きる。

ラベル付きデータとラベルなしデータの両方を使って学習する方式を、半教師あり学習と呼ぶ。疑似ラベル学習は、その中で最も単純な方法の一つである。追加のモデルも特別な損失関数も要らず、通常の教師あり学習の手順をそのまま使える。

基本の手順

疑似ラベル学習は、次の手順で行う。

  1. ラベル付きデータだけでモデルを学習する。
  2. そのモデルで、ラベルなしデータを予測する。
  3. 予測をラベルとして扱う。これが疑似ラベルである。
  4. ラベル付きデータと疑似ラベル付きデータを合わせて、モデルを学習する。

手順 3 のラベルの作り方には二通りある。予測確率が最大のクラスを一つ選んで正解とする方法をハードラベルと呼び、予測確率の分布をそのまま正解として使う方法をソフトラベルと呼ぶ。Lee の論文はハードラベルを使った。

Lee の方法では、手順 1 から 4 を別々の段階に分けず、一つの学習の中で同時に進める。重みを更新するたびにラベルなしデータの疑似ラベルを作り直し、損失関数は次の形にする。

L=Llabeled+α(t) LpseudoL = L_{\text{labeled}} + \alpha(t)\, L_{\text{pseudo}}

LlabeledL_{\text{labeled}} はラベル付きデータの損失、LpseudoL_{\text{pseudo}} は疑似ラベル付きデータの損失である。α(t)\alpha(t) は疑似ラベルの損失にかける重みで、学習の進み具合 tt に応じて変える。論文の設定では、最初の 100 エポックは 0、そこから 600 エポックまで直線的に増やし、以後は 3 で固定した。

α(t)\alpha(t) を 0 から始めるのは、学習の初期のモデルは予測が不正確で、その予測から作った疑似ラベルも誤りが多いからである。誤りの多い疑似ラベルを最初から大きな重みで学習すると、モデルは誤った方向に進む。ラベル付きデータである程度学習してから、疑似ラベルの影響を徐々に強める。

自分の予測を学習して精度が上がる理由

モデル自身の予測を正解として学習しても、新しい情報は増えないように見える。それでも精度が上がる理由を、Lee の論文はエントロピー正則化で説明した。

ハードラベルで学習すると、モデルはラベルなしデータに対して、どれか一つのクラスに高い確率を出すようになる。これは、予測のエントロピー(予測確率がクラス間にどれだけ散らばっているか)を下げることに相当する。予測のエントロピーが下がると、クラスの境界は、データが密集している場所を避けて、データがまばらな場所を通るようになる。

データがまばらな場所に境界を引くとよい理由は、クラスター仮定にある。クラスター仮定とは、「同じクラスのデータは特徴空間で固まって分布し、クラスの境目ではデータが少ない」という仮定である。ラベル付きデータが少ないと、境界をどこに引くかの手がかりが足りない。ラベルなしデータは、正解こそ分からないが、データがどこに固まっているかを教えてくれる。疑似ラベル学習は、この情報を境界の位置に反映させる。

論文は、手書き数字のデータセット MNIST で効果を確かめた。ラベル付きデータを 100 件から 3,000 件に絞り、検証用の 1,000 件を除いた残りをラベルなしデータとして使った結果が次の表である。

ラベル付きデータの件数ラベル付きデータだけで学習疑似ラベルを追加
10021.89%16.15%
6008.57%5.03%
1,0006.59%4.30%
3,0003.72%2.80%

数値はテストデータでの誤り率で、低いほどよい。どの件数でも、疑似ラベルを追加したほうが誤り率が下がっている。

誤った疑似ラベルで悪化する条件

疑似ラベルは予測なので、誤りを含む。誤った疑似ラベルを正解として学習すると、モデルはその誤った予測に対する確信を強める。確信が強まった予測は次の周でも疑似ラベルになり、同じ誤りが固定されていく。Arazo らはこの現象を確証バイアスと呼び、単純な疑似ラベル学習は誤った疑似ラベルに過学習することを実験で示した。

Arazo らは、ラベル付きデータがラベルなしデータよりはるかに少ない場合について、損失の大部分を疑似ラベルの項が占め、正しいラベルに合わせることより疑似ラベルに合わせることのほうがはるかに重く扱われる、と説明している。対策は、疑似ラベルの質を上げるか、誤りの影響を弱めるかのどちらかになる。

対策内容出所
重みを徐々に上げる学習の初期は疑似ラベルの損失の重みを 0 にし、後から増やすLee(2013)
確信度で選ぶ予測確率がしきい値を超えたデータだけに疑似ラベルを付けるSohn ら(2020)
ミニバッチ内のラベル付きデータを確保する1 回の更新に使うデータの中に、ラベル付きデータを一定数以上入れるArazo ら(2020)
mixup を使う2 件のデータとラベルを混ぜ合わせて学習し、特定の予測への過度な確信を抑えるArazo ら(2020)
教師と生徒を分ける疑似ラベルを作るモデルと、それを学習するモデルを別にするXie ら(2020)

確信度のしきい値で選ぶ

疑似ラベルの誤りを減らす直接的な方法は、モデルが自信を持っている予測だけを採用することである。FixMatch(Sohn ら、2020)は、この選別とデータ拡張を組み合わせた。

FixMatch の処理の流れ(FixMatch の論文の図 1 をもとに作成)
FixMatch の処理の流れ(FixMatch の論文の図 1 をもとに作成)

FixMatch は、1 枚のラベルなし画像から 2 種類の画像を作る。一つは左右反転や平行移動だけを加えた弱い拡張の画像、もう一つは色や形を大きく変えた強い拡張の画像である。弱い拡張の画像をモデルに入力し、最大の予測確率がしきい値を超えた場合だけ、そのクラスを疑似ラベルにする。次に、強い拡張の画像に対する予測が、その疑似ラベルに一致するようにモデルを学習する。しきい値を超えなかった画像は、その回の学習には使わない。論文はしきい値を 0.95 とした。

論文は、この組み合わせを入れ替える実験も行った。疑似ラベルを強い拡張の画像から作ると、学習は早い段階で発散した。疑似ラベルを拡張なしの画像から作ると、モデルは疑似ラベルに過学習した。学習に使う側を弱い拡張に替えると、正解率は 45% まで上がったあと安定せず、12% まで下がった。

この方法で、画像分類のデータセット CIFAR-10 では、ラベル付き画像 250 枚で正解率 94.93%、40 枚(1 クラスあたり 4 枚)で 88.61% を達成した。

教師モデルと生徒モデルで繰り返す

疑似ラベルを作るモデルと学習するモデルを分け、役割を交代させながら繰り返す方法もある。Noisy Student Training(Xie ら、2020)がその代表である。

Noisy Student Training の流れ(Noisy Student Training の論文の図 1 をもとに作成)
Noisy Student Training の流れ(Noisy Student Training の論文の図 1 をもとに作成)

まず、ラベル付きデータで教師モデルを学習する。次に、教師モデルでラベルなしデータに疑似ラベルを付ける。続いて、ラベル付きデータと疑似ラベル付きデータを合わせて、生徒モデルを学習する。最後に、学習した生徒モデルを新しい教師モデルにして、疑似ラベルを付ける工程に戻る。

この方法には、生徒モデルが教師モデルを上回るための工夫が二つある。一つ目は、生徒モデルを教師モデルと同じか、より大きいモデルにすることである。論文は、生徒モデルが増えた学習データからよりよく学べるようにするため、と説明している。二つ目は、生徒モデルの学習にだけノイズを加えることである。ノイズとは、データ拡張、ドロップアウト(学習中にモデルの一部をランダムに無効にする処理)、確率的深さ(学習中に層をランダムに飛ばす処理)を指す。教師モデルはノイズなしの状態で疑似ラベルを作り、生徒モデルはノイズで条件を悪くされた状態で同じラベルを出すことを求められる。

論文は、ラベル付き画像 130 万枚の ImageNet に、ラベルなし画像 3 億枚を加えて学習し、ImageNet の正解率 88.4% を達成した。この値は、弱いラベルの付いた画像 35 億枚を使う当時の最高精度のモデルを 2.0 ポイント上回る。

ハードラベルとソフトラベルの比較も行われた。どちらもうまく働いたが、学習データの分布から外れたラベルなし画像では、ソフトラベルのほうがわずかに精度が高かった。

小さなデータでの実行例

基本の手順を、scikit-learn の手書き数字のデータ(8×8 画素、1,797 件)で実行した。500 件をテストデータに取り分け、残りのうち 50 件だけをラベル付きデータ、1,247 件をラベルなしデータとして扱う。モデルはロジスティック回帰である。疑似ラベルを付ける対象を、予測確率のしきい値を変えて選び、テストデータでの正解率を比べる。

python
import numpy as np
from sklearn.datasets import load_digits
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

LABELED = 50
THRESHOLDS = [0.0, 0.5, 0.7, 0.9, 0.95]
SEEDS = range(10)


def fit(x, y):
    return LogisticRegression(max_iter=2000).fit(x, y)


def run(seed):
    x, y = load_digits(return_X_y=True)
    x = x / 16.0
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=500, random_state=seed, stratify=y)
    x_labeled, x_unlabeled, y_labeled, y_hidden = train_test_split(
        x_train, y_train, train_size=LABELED, random_state=seed, stratify=y_train
    )

    # 手順 1: ラベル付きデータだけで学習する。
    teacher = fit(x_labeled, y_labeled)

    # 手順 2 と 3: ラベルなしデータを予測し、予測をラベルとして扱う。
    probabilities = teacher.predict_proba(x_unlabeled)
    pseudo_labels = teacher.classes_[probabilities.argmax(axis=1)]
    confidence = probabilities.max(axis=1)

    rows = {"labeled only": (0, 0, teacher.score(x_test, y_test))}
    for threshold in THRESHOLDS:
        selected = confidence >= threshold
        # 手順 4: ラベル付きデータと、選んだ疑似ラベル付きデータを合わせて学習する。
        student = fit(
            np.vstack([x_labeled, x_unlabeled[selected]]), np.concatenate([y_labeled, pseudo_labels[selected]])
        )
        wrong = (pseudo_labels[selected] != y_hidden[selected]).sum()
        rows[f"threshold {threshold}"] = (selected.sum(), wrong, student.score(x_test, y_test))
    return rows


results = [run(seed) for seed in SEEDS]
for name in results[0]:
    selected, wrong, accuracy = np.mean([r[name] for r in results], axis=0)
    print(f"{name:16s} {selected:9.1f} {wrong:7.1f} {accuracy:14.3f}")

このコードが行う処理を図にすると、次のようになる。

実行例の処理の流れ(Noisy Student Training の論文のアルゴリズム 1 をもとに、上のコードのデータの件数としきい値による選別を書き添えて作成)
実行例の処理の流れ(Noisy Student Training の論文のアルゴリズム 1 をもとに、上のコードのデータの件数としきい値による選別を書き添えて作成)

論文のアルゴリズム 1 にある、生徒モデルにノイズを加える処理と、生徒モデルを教師モデルにして繰り返す処理は、この実行例では行っていない。

データの分け方を 10 通りに変えて実行し、平均を取った結果が次の表である。

学習に使ったデータ疑似ラベルの件数うち誤りの件数テストデータでの正解率
ラベル付きデータのみ0086.3%
疑似ラベルをすべて追加1,247.0179.587.6%
予測確率 0.5 以上だけ追加750.617.887.3%
予測確率 0.7 以上だけ追加295.32.584.5%
予測確率 0.9 以上だけ追加0.20.086.2%
予測確率 0.95 以上だけ追加0.00.086.3%

疑似ラベルをすべて追加すると、1,247 件のうち 179.5 件が誤りでも、正解率は 1.3 ポイント上がった。しきい値を上げると誤りは減るが、使える件数も減る。FixMatch の論文が使った 0.95 では、このモデルの予測確率が届かず、1 件も選ばれなかった。しきい値 0.7 では、誤りが 2.5 件まで減ったのに、正解率はラベル付きデータのみの場合より下がった。この実行例では、しきい値で選ぶことによる改善は見られなかった。FixMatch はしきい値を強いデータ拡張と組み合わせており、この実行例はデータ拡張を使っていない。

検証データとの関係

疑似ラベル学習を使うときは、検証データの扱いに注意が要る。検証データは、モデルが見たことのないデータでの精度を測るために取り分けたデータである。

教師モデルの学習に検証データが含まれていると、教師モデルは検証データの正解を覚えた状態で疑似ラベルを作る。その疑似ラベルで学習した生徒モデルには、検証データの正解の情報が間接的に伝わる。この生徒モデルを検証データで評価すると、精度が実際より高く出る。

これを避けるには、検証データを、生徒モデルの学習だけでなく、教師モデルの学習からも外す。疑似ラベルの工程を何周繰り返す場合でも、どの周のどのモデルにも検証データを使わない。

出所