「前の経験は新しい挑戦の助けになる」を転移学習の実験で検証したら、そう単純ではなかった
約14分で読めます

「前の経験は新しい挑戦の助けになる」を転移学習の実験で検証したら、そう単純ではなかった


機械学習で**転移学習(transfer learning)**という手法がある。あるタスクで学習させたモデルの重みを、別の関連したタスクに再利用する手法だ。ゼロから学習するより少ないデータで、より速く、より良い性能に到達できるとされている。

これは人生の話としてもよく聞く。前職での経験は転職先でも活きる、ある分野で身につけたスキルは畑違いの新しい挑戦でも土台になる、といった話だ。直感的にも納得できる話だし、実際そういう場面は多いのだろうと思う。

ただ、それがどんな場合でも成り立つのかは、実はあまり自明ではない。前の経験の積み方によっては、新しい状況に合わない形に凝り固まってしまって、むしろ足かせになることもあるのではないか。今回はこの疑問を、手書き数字認識の転移学習で実際に検証してみた。結論から言うと、当初期待していたのとはかなり違う結果になった。

実験設定: 0〜4で事前学習し、5〜9に転移する

scikit-learnに含まれるload_digitsデータセット(8×8ピクセルの手書き数字画像、1797枚)を使う。数字を0〜4と5〜9の2グループに分け、0〜4を事前学習用のソースタスク、5〜9をデータの少ない状態で学習させるターゲットタスクとして扱う。

from sklearn.datasets import load_digits

digits = load_digits()
X_all = digits.data / 16.0   # 8x8=64次元、0-1に正規化
y_all = digits.target

SOURCE_CLASSES = [0, 1, 2, 3, 4]
TARGET_CLASSES = [5, 6, 7, 8, 9]

ネットワークは64次元入力 → 隠れ層64ユニット(ReLU) → 5クラスsoftmaxの、順伝播・逆伝播ともに手書きの単層MLP。最適化はAdamを自前で実装した。

まず0〜4の751枚でこのネットワークを400エポック学習させる。

source train n=751, source test n=150
source final train/test acc: 1.0, 0.98

訓練精度100%、テスト精度98%まで到達した、十分に学習の進んだ「ソースモデル」ができた。

5〜9のターゲットタスクでは、クラスあたりの訓練データ数を1・2・3・5・10・20・40件と変えながら、3通りの条件を比較する。

  • スクラッチ: 全パラメータをランダム初期化し、そのままターゲットデータだけで学習
  • 転移(特徴量凍結): 隠れ層の重みをソースモデルからそのままコピーして固定し、出力層だけを学習(線形プローブ)
  • 転移(fine-tune): 隠れ層の重みをソースモデルからコピーした状態を初期値にして、全層を学習

「特徴量凍結」は、ソースタスクで学んだ特徴表現がそのままターゲットタスクにも使えるかを見るための条件、「fine-tune」は、その特徴表現を出発点にしつつターゲットタスク用に調整し直す、より一般的な転移学習の使い方だ。

結果: 転移が勝つと思っていたら、スクラッチが勝った

40回の独立試行を平均した結果が次のグラフだ。

クラスあたりの訓練データ数(横軸、対数スケール)に対する5クラス分類のテスト精度(縦軸)を、スクラッチ(赤)・転移特徴量凍結(緑)・転移fine-tune(青)の3本の折れ線グラフで示す。データ数が少ないほど3本の差は大きく、スクラッチが常に最も高く、特徴量凍結が常に最も低い。データ数が増えるにつれて3本は収束していく

n_per_class=  1  scratch=0.6865  frozen=0.5637  finetune=0.6497
n_per_class=  2  scratch=0.7989  frozen=0.6761  finetune=0.7663
n_per_class=  3  scratch=0.8523  frozen=0.7394  finetune=0.8235
n_per_class=  5  scratch=0.8884  frozen=0.7979  finetune=0.8701
n_per_class= 10  scratch=0.9380  frozen=0.8745  finetune=0.9312
n_per_class= 20  scratch=0.9590  frozen=0.9188  finetune=0.9562
n_per_class= 40  scratch=0.9667  frozen=0.9409  finetune=0.9672

正直なところ、この結果は予想していなかった。データが少ないほど転移学習が効くはずだと思っていたのに、どのデータ数でもスクラッチが一番良く、特に転移(特徴量凍結)は終始最下位だった。fine-tuneはスクラッチにかなり近いところまでは来るが、上回ることは(データが十分に増えるn=40まで)一度もない。

念のため、単発の学習曲線でも確認しておく。クラスあたりたった1件(訓練データ計5件)だけを使い、300エポックの学習過程を追ったのが次のアニメーションだ。

クラスあたり1件(訓練データ計5件)だけで学習したときの、スクラッチ(赤)と転移fine-tune(青)のテスト精度の推移を示すアニメーション。エポックが進むにつれて赤い線がすぐに0.75付近まで上昇して安定するのに対し、青い線はそれよりゆっくり上昇し0.7弱で頭打ちになる。赤が終始青を上回り続ける

さらに、別途30回の試行を平均して「学習速度」そのものも比較してみた。クラスあたり1件の場合、エポックごとのテスト精度は次のようになる。

epoch=   5  scratch=0.6184  finetune=0.5067
epoch=  10  scratch=0.6369  finetune=0.5627
epoch=  20  scratch=0.6407  finetune=0.5645
epoch=  50  scratch=0.6407  finetune=0.5929
epoch= 100  scratch=0.6439  finetune=0.6037
epoch= 300  scratch=0.6473  finetune=0.6167

「転移学習は最終精度こそ並ぶが、収束は速い」という可能性も考えていたが、それも外れた。学習の最初期(5エポック時点)からスクラッチの方が上で、その差は最後まで縮まらない。今回の実験では、転移は精度でも速度でも、ただの一度もスクラッチに勝てなかった。

ただし、スクラッチの優位性そのものは、データが増えるにつれてはっきり縮んでいく。

クラスあたりの訓練データ数(横軸、対数スケール)に対する、fine-tune転移の精度からスクラッチの精度を引いた差(縦軸)を示す折れ線グラフ。データ数が少ないほど差は−0.037付近まで大きくマイナスに開き、データ数が増えるにつれて0に近づき、n=40でほぼゼロになる

n=1では-0.037あった差が、n=40ではほぼゼロまで縮んでいる。これは前回の過学習の記事で見た「データが増えるほど、外部からの制約(正則化)の必要性が下がっていく」という形とよく似ている。あちらは「制約の効果が縮む」話だったが、今回は逆に「スクラッチという“制約のなさ”の優位性が縮む」形になっている。どちらも、データ(経験)が少ない局面ほど、何らかの外部要因(制約であれ転移であれ)の影響が大きく出て、データが増えるほどモデル自身の学習能力に収束していく、という共通のパターンに見える。

なぜ勝てなかったのか: 「死んだユニット」を調べる

これはさすがに意外すぎたので、原因を掘り下げることにした。まず疑ったのは、ソースタスク用に学習し切った重みが、ReLUユニットを「死なせて」いるのではないか、という点だ。

ReLUは入力が負になると出力も勾配も常にゼロになる。学習の過程でその状態に落ち込むと、そのユニットは二度と復活しない(死んだReLU、dying ReLUと呼ばれる現象)。ソースタスクへの適合を追求した結果、5〜9のデータに対しては使い物にならないユニットが増えているのではないか、という仮説だ。

事前学習済みの重みと、ランダム初期化の重みそれぞれについて、5〜9のデータを流したときの様子を比較した。

pretrained: abs_mean=0.2644  dead_frac=0.1406  active_rate=0.7137
random:     abs_mean=0.1393  dead_frac=0.0156  active_rate=0.5363

事前学習済みの重みは、64ユニット中14.1%(9ユニット)がターゲットデータに対して完全に死んでいた。ランダム初期化では1.6%(1ユニット)だけだったのと比べると、9倍近い差がある。重みの絶対値の平均も事前学習済みの方が約1.9倍大きく、学習を通じて特定のパターンに強く適合した分、極端な値に振れたユニットが増えていることが伺える。

隠れ層の重みを8×8の画像として可視化してみると、これも見た目でうっすら分かる。

digits 0-4で事前学習した隠れ層のうち32ユニット分の重みを8x8のヒートマップとして並べたもの。多くはピクセル単位でノイズのような赤青のまだら模様になっており、人間が見て分かるような線や輪郭の検出器にはなっていない。一部のパネルはほぼ均一な薄い色で、ほとんど情報を持っていないように見える

畳み込み層と違い、全結合層をピクセルに直接つないでいるので、CNNのようなきれいなエッジ検出器にはならず、全体的にノイズっぽいまだら模様になる。ただその中でも、ほぼ一様な色で情報量が乏しく見えるパネルがいくつか混ざっていて、感覚的には「死んでいる、あるいは死にかけているユニット」の見た目と一致している。

念のため、「ソースタスクを学習させすぎたから死んだユニットが増えたのでは」という仮説も検証した。事前学習を20・50・100・200・400エポックで打ち切り、それぞれから転移した場合の死んだユニットの割合を比べた。

stop_epoch=  20  dead_frac=0.1406
stop_epoch=  50  dead_frac=0.1250
stop_epoch= 100  dead_frac=0.1250
stop_epoch= 200  dead_frac=0.1250
stop_epoch= 400  dead_frac=0.1250

意外なことに、死んだユニットの割合は学習の初期(20エポック時点)でほぼ決まってしまい、その後300エポック以上学習を続けても、ほとんど変わらなかった。「学習させすぎ」が原因ではなく、この学習率・初期化の組み合わせでは、ごく初期の勾配更新で一部のユニットが不可逆的に死ぬ、という方が実態に近いようだ。事前学習を早めに打ち切っても、転移後の精度が目に見えて改善することもなかった。

転移側の「不利」だけが理由ではなさそう

もう一つ気づいたのは、そもそも今回のターゲットタスク自体が、スクラッチにとって簡単すぎたのではないか、という点だ。クラスあたりわずか1件、訓練データ合計5件だけで、ランダム初期化のスクラッチモデルはテスト精度68.65%に到達している(偶然の20%よりずっと高い)。

数字の8×8ピクセル画像は、クラス間の見た目の違いがそれなりにはっきりしているので、64ユニットという比較的広い隠れ層を持つランダム初期化のネットワークでも、たった5枚の例からそこそこの精度を引き出せてしまう。いわゆる「ランダム特徴量」でもある程度戦えてしまうタスクだったということだ。この場合、転移学習が用意する「頭の中の地図」の出番はそもそも少ない。

過去の型に頼るべきか、まっさらで臨むべきか

ここまでの実験を振り返ると、当初の直感(前の経験は新しい挑戦の土台になる)を単純に裏付ける結果にはならなかった。むしろ逆で、ソースタスクに適合しきった重みは、一部のユニットを「死なせて」しまっており、まったく違うタスクに対しては、まっさらなランダム初期化よりも使える情報量が少なかった。しかも、この現象は学習のごく初期に発生し、後から学習を打ち切っても取り消せない。

これは人生の話としても、素直に「前の経験は役立つ」と言い切れない場面があることを示しているように思う。一つの環境やルールに強く適合しすぎると、そこで身についた思考の一部が、まったく別の環境では「死んだユニット」のように反応しなくなっているのかもしれない。しかも厄介なのは、それが適合の初期段階で決まってしまい、その後どれだけ長くその環境にいたかとはあまり関係がない、という点だ。「染まりすぎた」タイミングは、案外早くに訪れる。

一方で、新しい環境そのものが、まっさらな状態からでも十分に学べるくらいシンプルなら、過去の経験という下駄を履かなくても、素の柔軟性だけで十分に太刀打ちできる。今回のネットワークが、たった5枚の画像から68.65%まで到達できたのがその例だ。経験という「地図」は、状況が複雑で情報が少ないときにこそ真価を発揮するのであって、素の対応力だけで十分な場面にまで無理やり持ち込む必要はない。

もちろんこれは64ユニットの小さなMLPと、8×8ピクセルの数字という小規模な実験の結果であって、「転移学習は役に立たない」という一般的な結論を導くものではない。大規模な画像認識モデルや言語モデルの事前学習が実際に大きな効果を上げていることは、他の多くの研究で示されている通りだ。ただ、それが自動的に、無条件に得られる恩恵ではないことは、今回の実験からも実感できた。転移が効くかどうかは、ソースとターゲットが本当にどれだけ近いか、そしてソース側の学習がどれだけ特定の状況に「染まって」しまっているかに、思っていた以上に左右されるのだと思う。

まとめ

  • 手書き数字0-4で事前学習した重みを5-9の分類に転移させたところ、精度・収束速度のどちらでも、スクラッチ(ランダム初期化)を一度も上回れなかった
  • 原因を調べると、事前学習済みの重みはターゲットデータに対して64ユニット中14.1%が完全に「死んで」いた(ランダム初期化では1.6%)。この死んだユニットの割合は学習のごく初期(20エポック以内)でほぼ決まり、その後学習を続けても事前学習を早めに打ち切っても、ほとんど変化しなかった
  • 隠れ層の重みを可視化しても、人間が見て分かるようなきれいな特徴検出器にはなっておらず、ノイズっぽいまだら模様と、ほぼ情報を持たない一部のユニットが混在していた
  • ターゲットタスク自体が、クラスあたり1件・合計5件のデータからでもスクラッチで68.65%に到達できるくらい学習しやすく、転移学習の「頭の中の地図」の出番自体が小さかった可能性もある
  • 過去の経験(事前学習)は、それだけで自動的に新しい挑戦の助けになるわけではない。適合の仕方によっては一部が使い物にならなくなっていることがあり、しかもそれは早い段階で決まってしまう