モジュール 08 · 第 6 回

過学習とその対策

学習セットではすべて正解するのに、見たことのないデータではまったく伸びない。これを過学習と呼びます。わざと過学習を起こし、重み減衰、ドロップアウト、早期終了、データを増やすことを試して、実際の結果でどれが最も効くのかを見ます。

  • 約 40 分
  • 難易度:中級
  • 検証:2026-09-14 torch 2.14、scikit-learn 1.9、CPU、乱数シード固定

コードと実行結果は実際に動かしたときのまま載せているため、コメントと出力は中国語です。

前の課のネットワークは学習セットで 100%、テストセットで 97.8% でした。この 2.2 ポイントの差が、ネットワークが「丸暗記」して本当には身につけていない部分です。

差が小さいうちは問題になりません。しかし学習データがとても少なく、モデルが大きいと、ネットワークは学習セットを丸ごと暗記してしまいます。学習セットではすべて正解するのに、新しいデータに替えるとたちまち化けの皮がはがれる。これを過学習(overfitting)と呼びます。この課ではわざと過学習を起こし、よくある対策をいくつか試します。

python overfitting.py

三つのデータ

この課ではデータを三つに分けます。

X_pool, X_rest, y_pool, y_rest = train_test_split(X, y, test_size=0.5, random_state=0)
X_val, X_test, y_val, y_test = train_test_split(X_rest, y_rest, test_size=0.5, random_state=0)
X_small, y_small = X_pool[:100], y_pool[:100]  # 故意只用 100 张
可用于训练的图 898 张(先只用其中 100 张),验证集 449 张,测试集 450 张
模型共 301066 个参数
  • 学習セット:パラメータの更新に使います。
  • 検証セット:学習の途中で様子を見て、設定(学習率をいくつにするか、いつ止めるか)を調整するのに使いますが、パラメータの更新には使いません。
  • テストセット:すべての設定が決まってから、最後に一度だけ見て、最終成績とします。

なぜ検証セットがもう一つ必要なのでしょうか。テストセットの成績を一度見て、それをもとに設定を調整するたびに、テストセットの情報があなたの判断に少しずつ漏れていくからです。何十回も調整した後で選んだ設定は、たまたまこのテストセットに合っているだけかもしれません。ですから調整では検証セットを見て、テストセットは最後に取っておきます。

モデルはわざと大きくしてあります。512 個のニューロンの隠れ層が二つで、パラメータは 30 万個あるのに、画像は 100 枚しか与えません。1 枚あたり 3000 個のパラメータになり、丸暗記するのはたやすいことです。

過学習はどう見えるか

== 什么都不加
  第   1 轮  训练损失 2.2147 准确率 26.0%  |  验证损失 2.2507 准确率 16.5%
  第  10 轮  训练损失 1.3077 准确率 79.0%  |  验证损失 1.5603 准确率 62.6%
  第  25 轮  训练损失 0.1552 准确率 98.0%  |  验证损失 0.4420 准确率 87.3%
  第  50 轮  训练损失 0.0058 准确率 100.0%  |  验证损失 0.3772 准确率 88.4%
  第 100 轮  训练损失 0.0013 准确率 100.0%  |  验证损失 0.4038 准确率 89.8%
  第 200 轮  训练损失 0.0004 准确率 100.0%  |  验证损失 0.4371 准确率 90.0%
  第 300 轮  训练损失 0.0002 准确率 100.0%  |  验证损失 0.4667 准确率 89.8%
  最终:测试集准确率 88.7%,测试损失 0.5089

(1 エポック目の損失 2.21 は、前の課で述べた 2.30 に近い値です。最初、ネットワークは何もわかっていません。)

50 エポック目で、学習セットはすでに 100% 正解し、学習の損失は 0.006 で、その後も 0.0002 まで下がり続けます。ところが検証セットの正答率は 90% 前後で止まり、検証の損失は 50 エポック目の 0.377 から上がり始め、300 エポック目には 0.467 になっています。

二本の曲線が分かれました。学習の損失はまだ下がっているのに、検証の損失は上がっている。これが過学習のしるしです。検証の損失が上がるのは、ネットワークが学習セットにどんどん「自信」を持つようになり、見たことのない画像で間違えるときも自信満々に間違え、交差エントロピーがこの種の誤りを重く罰するからです。

図にすると、おおよそこうなります。

损失
 │ \
 │  \  验证损失
 │   \____/ ̄ ̄ ̄ ̄
 │    \
 │     \_  训练损失
 │         ̄ ̄______
 └──────────────────────── 轮
          ↑
    验证损失最低的地方

いくつかの対策を試す

以下はどれも過学習によく使われる対策です。どれも一か所だけを変え、それ以外はまったく同じにします(同じ乱数シード、同じ 100 枚の画像)。

重み減衰(weight decay):更新のたびに、すべての重みを 0 のほうへ少し引き寄せます。パラメータの値が小さいモデルほど「単純」で、個々のサンプルに過敏に反応しにくいという考え方です。AdamW オプティマイザの weight_decay 引数が引き寄せる強さを決めます。

optimizer = torch.optim.AdamW(model.parameters(), lr=0.001, weight_decay=weight_decay)

ドロップアウト(dropout):学習中、各ステップでランダムに一部のニューロンを「休ませ」ます(出力を 0 にする)。ここでは 0.5 にしているので、各ステップで隠れニューロンの半分が働きません。ネットワークは特定のいくつかのニューロンに頼って特定の画像を覚えることができなくなり、より頑健な規則を学ぶしかなくなります。評価のときは model.eval() でドロップアウトをオフにし、学習のときは model.train() でオンにします。

nn.Linear(64, 512), nn.ReLU(), nn.Dropout(dropout),

早期終了(early stopping):毎エポック検証の損失を見て、最も低かった時点のパラメータを記録しておきます。検証の損失が 20 エポック続けて最低を更新しなければ止め、最も低かった時点に戻ります。

if val_loss < best_val:
    best_val, best_epoch, patience = val_loss, epoch, 0
    best_state = {k: v.clone() for k, v in model.state_dict().items()}
else:
    patience += 1
if early_stop and patience >= 20:  # 验证损失连续 20 轮没有创新低,就停下来
    break

そして最も素朴な方法がもう一つあります。データを増やすことです。学習に使える 898 枚の画像をすべて使います。

結果:少し違う結論

== 权重衰减 weight_decay=0.1
  第  50 轮  训练损失 0.0059 准确率 100.0%  |  验证损失 0.3762 准确率 88.4%
  第 300 轮  训练损失 0.0002 准确率 100.0%  |  验证损失 0.4543 准确率 89.8%
  最终:测试集准确率 88.0%,测试损失 0.5008

== 权重衰减 weight_decay=1.0
  第  50 轮  训练损失 0.0079 准确率 100.0%  |  验证损失 0.3674 准确率 89.1%
  第 300 轮  训练损失 0.0012 准确率 100.0%  |  验证损失 0.4243 准确率 89.3%
  最终:测试集准确率 88.0%,测试损失 0.4574

== dropout 0.5
  第  50 轮  训练损失 0.0561 准确率 100.0%  |  验证损失 0.3784 准确率 88.4%
  第 300 轮  训练损失 0.0003 准确率 100.0%  |  验证损失 0.3879 准确率 90.2%
  最终:测试集准确率 88.2%,测试损失 0.4321

== 早停
  第 55 轮停止:验证损失从第 35 轮之后就没再下降
  最终:测试集准确率 87.3%,测试损失 0.4053

== 什么都不加,但用 898 张图训练
  第   1 轮  训练损失 2.2261 准确率 34.6%  |  验证损失 2.2390 准确率 27.4%
  第  10 轮  训练损失 1.3170 准确率 88.1%  |  验证损失 1.3714 准确率 84.4%
  第  25 轮  训练损失 0.2208 准确率 94.4%  |  验证损失 0.2675 准确率 91.1%
  第  50 轮  训练损失 0.0437 准确率 99.3%  |  验证损失 0.1072 准确率 96.9%
  第 100 轮  训练损失 0.0074 准确率 100.0%  |  验证损失 0.0854 准确率 97.1%
  第 200 轮  训练损失 0.0016 准确率 100.0%  |  验证损失 0.0850 准确率 97.1%
  第 300 轮  训练损失 0.0007 准确率 100.0%  |  验证损失 0.0875 准确率 97.3%
  最终:测试集准确率 96.2%,测试损失 0.1682

测试集汇总:
  什么都不加            准确率 88.7%  损失 0.5089
  权重衰减 0.1         准确率 88.0%  损失 0.5008
  权重衰减 1.0         准确率 88.0%  损失 0.4574
  dropout 0.5      准确率 88.2%  损失 0.4321
  早停               准确率 87.3%  损失 0.4053
  898 张训练数据        准确率 96.2%  损失 0.1682

この結果は多くのチュートリアルの説明と少し違うので、じっくり見る価値があります。

重み減衰、ドロップアウト、早期終了は、どれも正答率を上げなかった。 テストセットの正答率はどれも 87% から 89% の間で、何も加えない場合の 88.7% とほぼ同じ、早期終了はむしろ少し低いくらいです。テスト画像は 450 枚なので、1 ポイントはわずか 4~5 枚で、この程度の差ではどれがよいとも言えません。

テストの損失は下げた。 0.51 から 0.40~0.50 に下がり、早期終了が最もはっきりしています。つまりネットワークが正解する問題は同じままで、ただ「自信満々に間違える」ことがなくなり、出す確率がより当てになるようになったのです。確率をもとに結果を人の確認に回すかどうかを決めるような場面では、これが重要になります。

データを増やすことは、ほかのすべての対策をはるかに上回った。 同じモデルで、同じく何も加えず、学習データを 100 枚から 898 枚に替えただけで、テストの正答率は 88.7% から 96.2% に跳ね上がり、テストの損失は 0.51 から 0.17 に下がりました。検証の損失もはっきりとは上がらなくなり、0.085 付近で安定しています。

どう理解すべきか

これらの正則化の方法(重み減衰、ドロップアウト、早期終了はまとめて正則化と呼ばれます)は役に立たないのではなく、適用範囲があるのです。過学習を和らげることはできますが、データにない情報を生み出すことはできません。100 枚の画像では、数字一つあたり平均 10 枚しかなく、同じ数字のさまざまな書き方をとても見尽くしていません。モデルをどう制約しても、見たことのない書き方を認識することはできないのです。

実際にモデルを学習させるとき、過学習に対処する順番はおおよそ次のとおりです。

  1. まずデータを増やす方法を考える。より多く、より多様なデータは、ほぼ常に最も効果的です。
  2. 必ず検証セットを用意し、それを見張る。学習の損失がどれほどきれいに下がっても、検証の指標が伸びなければ意味がありません。早期終了はほとんどコストがかからないので、既定でオンにしておきます。
  3. それから正則化で微調整する。重み減衰やドロップアウトの強さは検証セットで試して決めるもので、どこでも通用する値はありません。
  4. 最初からモデルを大きくする必要はない。データが少ないときは、少し小さなモデルで十分なことが多いのです。

LLM の学習も同じ理屈です。LLM の正則化の手段は複雑ではなく、本当の鍵は膨大で、多様で、きれいな学習データです。モジュール 09 で小さな GPT を学習させるとき、データが少なすぎると、詩の作り方を学ぶのではなく詩の原文を丸暗記してしまう様子を自分の目で見ることになります。

練習問題

  1. 学習データをそれぞれ 200 枚、400 枚にして、「学習データの量 → テストの正答率」の表を作ってください。正答率はデータの量に応じて均等に上がっていくでしょうか。
  2. モデルの隠れ層を 512 から 32 に変え、100 枚の画像だけで学習させてください。テストの正答率は 30 万パラメータの大きなモデルより高いでしょうか、低いでしょうか。
  3. ドロップアウトを 0.2 と 0.8 に、重み減衰を 0.01 と 5.0 に変えて、検証セットで最もよい組み合わせを探し、最後にテストセットを一度だけ見てください。

確認テスト

1. 学習の過程から、過学習が起きたことをどう判断しますか?

学習の損失はまだ下がっているのに、検証の損失が下がらなくなるか、上がり始めて、二本の曲線が分かれることです。学習セットの正答率が検証セットの正答率よりはるかに高いのも、しるしの一つです。

2. テストセットがすでにあるのに、なぜ検証セットがさらに必要なのですか?

調整では成績を何度も見て設定を変えます。見るのがテストセットだと、何度も調整した後で選んだ設定は、たまたまこのテストセットに合っているだけかもしれず、テストの成績はもう新しいデータでの成績を表さなくなります。ですから調整では検証セットを見て、テストセットは最後に一度だけ見ます。

3. この課の実験で、重み減衰、ドロップアウト、早期終了は正答率を上げなかったのに、データを増やすとなぜ大きく上がったのですか?

正則化の方法は、モデルが学習セットの細部に頼りすぎないよう制約できるだけで、新しい情報を与えることはできません。100 枚の画像では数字一つあたり約 10 枚しかなく、見たことのない書き方がたくさんあり、モデルをどう制約しても認識できません。データを増やせばモデルはより多くの書き方をじかに見られるので、効果がほかの方法をはるかに上回ったのです。この実験でのそれらの方法の働きは、主にテストの損失を下げ、モデルが自信満々に間違えないようにしたことでした。

質問と議論

このレッスンでつまずいたところは、ここで質問してください。他の人の質問に答えるのも歓迎です。

質問で 3 ポイント、回答で 6 ポイント。審査を通過すると公開されます。

議論を読み込んでいます…