半教師あり学習とは?教師あり・弱教師あり学習との違いと代表的な手法をPythonで試す
半教師あり学習とは、正解ラベルの付いた少量のデータと、ラベルの付いていない大量のデータをいっしょに使って学習する機械学習の方法です。教師あり学習(全部にラベルが必要)と教師なし学習(ラベルを使わない)の中間にあたり、ラベル付けに手間やお金がかかる場面で使われます。
この記事では、教師あり学習・教師なし学習・弱教師あり学習との違いを整理したうえで、代表的な手法(自己訓練・ラベル伝播・一貫性正則化)の考え方を説明し、Pythonのscikit-learnで「ラベルが5%しかない手書き数字データ」を実際に学習させた結果を示します。コードはPython 3.12・scikit-learn 1.9.1で実行しました(2026年10月15日確認)。
半教師あり学習とは
機械学習のモデルに「この画像は猫」「このメールは迷惑メール」といった正解(ラベル)を教えて学ばせるのが教師あり学習です。ところが実際には、データそのものは大量に集まっても、1件ずつ正解を付ける作業が追いつかないことがよくあります。画像なら人が見て判定し、医療や法律の文書なら専門家の判断が必要になるからです。
半教師あり学習は、この「ラベルが足りない」状況のための方法です。scikit-learnの公式ドキュメントは、訓練データの一部のサンプルにラベルが付いていない状況を半教師あり学習と呼び、ラベルなしデータを使ってデータの分布の形をよりよくとらえ、新しいサンプルへの当てはまりを良くすることが目的だと説明しています。また、ラベル付きの点がごく少なく、ラベルなしの点が大量にあるときに効果を発揮しうるとしています(scikit-learn User Guide 1.14. Semi-supervised learning)。
教師あり・教師なし・弱教師ありとの違い
| 学習の種類 | 使うデータ | 主な目的・例 |
|---|---|---|
| 教師あり学習 | すべてにラベルが付いたデータ | 分類(迷惑メール判定)や回帰(価格の予測) |
| 教師なし学習 | ラベルのないデータだけ | クラスタリング(顧客のグループ分け)、次元削減 |
| 半教師あり学習 | 少量のラベル付き+大量のラベルなし | 教師あり学習と同じ予測を、少ないラベルで行う |
| 弱教師あり学習 | 不正確・おおまか・ノイズを含むラベル | キーワードのルールや既存のデータベースから自動で付けた粗いラベルで学習する |
| 能動学習(アクティブラーニング) | ラベル付けする対象をモデルが選ぶ | 人がラベルを付けるデータを効率よく選ぶ |
半教師あり学習と弱教師あり学習は名前が似ていますが、着眼点が違います。スタンフォード大学AI研究所のブログ記事(Weak Supervision: A New Programming Paradigm for Machine Learning)は、半教師あり学習を「少量のラベル付きデータと、はるかに多いラベルなしデータの両方を使う」もの、弱教師あり学習を「専門家から、より大まかな、あるいはノイズの多い形で知識をもらって使う」ものとして区別しています。前者はラベルの量が少ない問題、後者はラベルの質が粗い問題への対処といえます。
教師あり学習の基本は教師あり学習とは?、教師なし学習は教師なし学習とは?教師あり学習との違いと代表的な手法をPythonで試す、ラベルを付けるデータの選び方はアクティブラーニングとは?で解説しています。
なぜラベルなしデータが役に立つのか(前提となる仮定)
ラベルのないデータには正解が書かれていないのに、なぜ学習の助けになるのでしょうか。それは「データの並び方そのもの」が手がかりになるからです。ただし、手がかりになるのは次のような仮定がデータに当てはまる場合に限られます。scikit-learnのドキュメントも、半教師ありの手法で性能を上げるにはデータの分布について仮定が必要だと注意しています。
- なめらかさの仮定:近くにあるデータは同じラベルを持ちやすい
- クラスタの仮定:同じクラスのデータはかたまりを作り、クラスの境目はデータがまばらなところにある
- 多様体の仮定:高次元のデータも、実際にはもっと低い次元の構造の上に並んでいる
たとえば手書きの「1」と「7」は、ラベルがなくても形の近いもの同士でかたまりを作ります。少数のラベル付きデータで「このかたまりは1」と分かれば、同じかたまりのラベルなしデータも1だろうと推測できます。逆に、データがかたまりを作らない問題や、ラベルなしデータが別の種類のデータを多く含む場合には、効果が出ないこともあります。
代表的な手法
自己訓練(Self-Training・疑似ラベル)
最もわかりやすい方法です。
- ラベル付きデータだけでモデルを作る
- そのモデルでラベルなしデータを予測する
- 予測の確信度が高いものに、予測結果を仮のラベル(疑似ラベル)として付ける
- 疑似ラベル付きのデータも加えて学習し直し、2〜4をくり返す
scikit-learnのSelfTrainingClassifierはこの方法の実装で、Yarowskyのアルゴリズム(1995年)に基づくと説明されています。確率を出せる分類器(predict_probaを持つもの)なら何でも中に入れられ、確信度のしきい値(threshold、既定値0.75)か、確信度の高い順にk_best件を選ぶかで疑似ラベルを付けます(SelfTrainingClassifier)。ドキュメントは、しきい値を使う場合は予測確率がよく較正(キャリブレーション)された分類器を使うべきだと注意しています。
ラベル伝播(Label Propagation・Label Spreading)
すべてのデータ(ラベルあり・なし)を点とし、似ている点同士を線で結んだグラフを作って、ラベルを持つ点から近くの点へラベルを「しみ出させる」方法です。scikit-learnにはLabelPropagationとLabelSpreadingの2つがあり、後者は正則化のはたらきを持つ損失関数を最小化するため、ノイズに強いことが多いとされています。似ている度合いの計算にはRBFカーネルとkNNカーネルが選べ、RBFはデータ数が多いと巨大な行列になって時間がかかるため、大きなデータではkNNのほうがメモリを節約できると説明されています。
一貫性正則化と、深層学習での組み合わせ
画像を少し回転させたりノイズを加えたりしても、モデルの予測は変わらないはずです。この性質を利用して、ラベルなしデータに加工を加えても予測が一貫するように学習させるのが一貫性正則化です。2020年にNeurIPSで発表されたFixMatchは、弱く加工した画像の予測で確信度が高いものだけを疑似ラベルにし、同じ画像を強く加工したものでその疑似ラベルを当てるよう学習する、という疑似ラベルと一貫性正則化の組み合わせです。論文では、画像分類のベンチマークCIFAR-10でラベル250枚のとき正解率94.93%、1クラス4枚(計40枚)のとき88.61%と報告されています(FixMatch: Simplifying Semi-Supervised Learning with Consistency and Confidence)。
Pythonで試す:ラベル5%の手書き数字を学習する
scikit-learnに付属している手書き数字データ(8×8ピクセル、1,797枚)を使います。訓練用1,257枚のうち約5%だけにラベルを残し、残りはラベルなしにします。scikit-learnではラベルなしを-1で表す決まりです。次の3つを、同じテスト用データ(540枚)の正解率で比べます。
- ラベル付きの63枚だけで学習したロジスティック回帰(教師あり学習)
- 同じロジスティック回帰を
SelfTrainingClassifierに入れた自己訓練 LabelSpreading(kNNカーネル)によるラベル拡散
import numpy as np
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.semi_supervised import SelfTrainingClassifier, LabelSpreading
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
X, y = load_digits(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=0, stratify=y)
# 訓練データのうち 5% だけラベルを残し、残りは -1(ラベルなし)にする
rng = np.random.RandomState(0)
y_semi = y_train.copy()
unlabeled = rng.rand(len(y_train)) > 0.05
y_semi[unlabeled] = -1
print("訓練データ:", len(y_train), "件 / ラベルあり:", (y_semi != -1).sum(), "件")
def base():
return make_pipeline(StandardScaler(), LogisticRegression(max_iter=2000))
# 1) ラベルありデータだけで学習(教師あり学習)
sup = base().fit(X_train[~unlabeled], y_train[~unlabeled])
print("教師あり(ラベルありだけ):", round(sup.score(X_test, y_test), 3))
# 2) 自己訓練(Self-Training)
st = SelfTrainingClassifier(base(), threshold=0.9, max_iter=None).fit(X_train, y_semi)
print("自己訓練:", round(st.score(X_test, y_test), 3),
"/ 疑似ラベルを付けた件数:", int((st.labeled_iter_ > 0).sum()))
# 3) ラベル拡散(LabelSpreading)
ls = LabelSpreading(kernel="knn", n_neighbors=7).fit(X_train, y_semi)
print("ラベル拡散:", round(ls.score(X_test, y_test), 3))
# 参考: 全部にラベルがある場合(上限の目安)
full = base().fit(X_train, y_train)
print("参考・全件ラベルあり:", round(full.score(X_test, y_test), 3))
実行結果:
訓練データ: 1257 件 / ラベルあり: 63 件
教師あり(ラベルありだけ): 0.817
自己訓練: 0.848 / 疑似ラベルを付けた件数: 992
ラベル拡散: 0.909
参考・全件ラベルあり: 0.972
ラベル付き63枚だけの教師あり学習が0.817だったのに対し、自己訓練は0.848、ラベル拡散は0.909まで上がりました。参考として載せた「全1,257枚にラベルがある場合」の0.972には届きませんが、ラベル付けの量を約20分の1にしても、ラベルなしデータを使うことで差を縮められています。自己訓練では992枚に疑似ラベルが付きましたが、その中には誤ったラベルも含まれるため、伸びはラベル拡散より小さくなりました。
ラベルの割合を変えて、教師あり学習のみとラベル拡散を比べたのが次の結果です。
import numpy as np
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.semi_supervised import LabelSpreading
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
X, y = load_digits(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=0, stratify=y)
print("ラベルの割合 ラベル数 教師ありのみ ラベル拡散")
for rate in [0.02, 0.05, 0.10, 0.30]:
rng = np.random.RandomState(0)
labeled = rng.rand(len(y_train)) < rate
y_semi = np.where(labeled, y_train, -1)
sup = make_pipeline(StandardScaler(), LogisticRegression(max_iter=2000))
sup.fit(X_train[labeled], y_train[labeled])
ls = LabelSpreading(kernel="knn", n_neighbors=7).fit(X_train, y_semi)
print(f"{rate:>8.0%} {labeled.sum():>8d} {sup.score(X_test, y_test):>12.3f} {ls.score(X_test, y_test):>10.3f}")
実行結果:
ラベルの割合 ラベル数 教師ありのみ ラベル拡散
2% 30 0.681 0.843
5% 63 0.817 0.909
10% 124 0.869 0.954
30% 391 0.935 0.967
ラベルが少ないほど差が大きく、ラベルが30%あるとほとんど差がなくなっています。半教師あり学習は「ラベルがとても少ないとき」に効きやすいことが、この小さな実験からも読み取れます。ただしこれは、形の近い数字同士がかたまりを作りやすいデータでの結果で、どんなデータでも同じように上がるわけではありません。
scikit-learnの基本的な使い方(fit・predict・score)はscikit-learnの使い方入門で解説しています。
半教師あり学習の注意点
間違った疑似ラベルが積み重なる
自己訓練では、最初のモデルが間違えた予測が疑似ラベルとして学習データに入り、次のモデルがその間違いをさらに信じ込む、という悪循環が起こりえます。しきい値を高めにする、くり返し回数(max_iter)を制限する、疑似ラベルの一部を人が確認する、といった対策を取ります。
ラベルなしデータに「別物」が混ざると悪化する
NeurIPS 2018で発表された評価研究(Realistic Evaluation of Deep Semi-Supervised Learning Algorithms)は、ラベルなしデータに分類対象以外のクラスの例が含まれていると性能が大きく下がりうること、またラベルなしデータを使わない単純な比較対象の性能が過小に報告されがちであることを指摘しています。ラベルなしデータは「同じ種類のデータ」であることを確かめてから使い、効果は必ず教師あり学習のみの結果と比べて判断しましょう。
評価用のデータには本物のラベルが必要
学習に使うラベルは少なくて済んでも、性能を測るテスト用データには正しいラベルが必要です。上の実験でも、テスト用の540枚には本物のラベルを使っています。ラベル付けの予算は「学習用」と「評価用」に分けて考える必要があります。
どんなときに使うか
- データは大量にあるが、ラベル付けに専門家の判断や長い時間が必要(画像の検査、文書の分類、音声の書き起こしなど)
- ラベル付きデータが少なく、教師あり学習だけでは精度が足りない
- ラベルなしデータが、予測したい対象と同じ種類・同じ条件で集められている
反対に、ラベルを十分に用意できるなら、まず教師あり学習で試すのが基本です。また、事前に学習済みのモデルを少量のデータで調整する転移学習も、ラベルが少ないときの有力な選択肢です(転移学習とは?)。
よくある質問
Q. 半教師あり学習とは簡単に言うと何ですか?
正解ラベルの付いた少量のデータと、ラベルの付いていない大量のデータをいっしょに使って学習する機械学習の方法です。教師あり学習と教師なし学習の中間にあたり、ラベル付けのコストを抑えたいときに使われます。
Q. 教師あり学習との違いは何ですか?
教師あり学習は学習に使うすべてのデータに正解ラベルが必要です。半教師あり学習は一部のデータにだけラベルがあればよく、ラベルのないデータの並び方も手がかりにして学習します。予測する内容(分類など)は同じです。
Q. 弱教師あり学習との違いは何ですか?
半教師あり学習はラベルの量が少ない状況への対処で、ラベル自体は正確であることを前提にします。弱教師あり学習は、ルールや既存のデータベースから自動で付けたような、不正確で大まかなラベルを使って学習する方法です。
Q. Pythonで試すにはどうすればよいですか?
scikit-learnのsklearn.semi_supervisedにSelfTrainingClassifier、LabelPropagation、LabelSpreadingがあります。ラベルのないデータの目的変数を-1にして、ラベル付きデータとまとめてfitに渡します。
Q. 半教師あり学習を使えば必ず精度が上がりますか?
上がるとは限りません。データが同じクラスごとにかたまりを作るといった仮定が成り立たない場合や、ラベルなしデータに対象外の種類のデータが混ざっている場合は、効果がなかったり悪化したりします。ラベル付きデータだけで学習した結果と必ず比べてください。
機械学習とディープラーニングを基礎から順に身につけたい方は、テックジムのAIエンジニア養成コースをご覧ください。データ分析の入門から推薦システムづくりまでを16章で学び、章ごとに実務での使いどころ(想定案件)が示されています。
あわせて読みたい
土台になる仕組みは機械学習とは?仕組み・種類・できることを簡単に解説|Pythonで動く例つき、業務での使われ方はAIエージェントとは?で整理しています。




