本文へスキップ
ひもとくAI

Batch Normalization

レッスン 6/7

Batch Normalization:層ごとに、値の大きさをそろえる

第3章の前処理では、入力の値の大きさを0〜1などにそろえると、学習が安定することを学びました。ところが、ニューラルネットワークの中では、各層の出力が次の層の入力 になります。学習で重みが変わるたびに、次の層に渡る値の平均やばらつきも変わっていきます。深いネットワークでは、層ごとに値が大きくずれていき、学習が難しくなります。

そこで、各層で、ミニバッチごとに値をそろえ直す のが Batch Normalization(バッチ正規化)です。

手順

ミニバッチ(例えば32件)の中で、ニューロンごとに次の計算をします。

  1. 32件の値の平均 μ\mu(ミュー)と、ばらつき(分散)σ2\sigma^2 を求める
  2. それぞれの値から平均を引き、標準偏差で割る(平均0、ばらつき1にそろう)
  3. そろえた値を、学習で決まる2つの数 γ\gamma(ガンマ)と β\beta(ベータ)で、γ×\gamma \times 値 +β+ \beta と調整する
z^=z−μσ2+ε,出力=γz^+β\hat{z} = \frac{z - \mu}{\sqrt{\sigma^2 + \varepsilon}}, \qquad \text{出力} = \gamma \hat{z} + \beta

ε\varepsilon(イプシロン)は、0で割らないように足す、ごく小さな数(0.00001など)です。2は、この章のレッスン2で、多項式の特徴量の大きさをそろえた「標準化」とまったく同じ計算です。3があるのは、「平均0、ばらつき1」が次の層にとって最適とは限らないからで、ちょうどよい平均とばらつきを、学習で決められるようにしています。

例:そろえる前と後

手書き数字のデータの32件を、ある層に通したときの値(3つのニューロン分)です。

ニューロン1ニューロン2ニューロン3
そろえる前の平均2.924.99−0.86-0.86
そろえる前の標準偏差1.181.151.07
そろえた後の平均0.000.000.00
そろえた後の標準偏差1.001.001.00

ニューロンによって平均がばらばらだった値が、どれも平均0、標準偏差1にそろいました。

実験:Batch Normalizationで、深いネットワークが学習できるようになる

手書き数字のデータで、シグモイド関数の隠れ層を6層(各32個)重ねたネットワークを、ミニバッチ32件のSGD(学習率0.1)で20周学習させました。初期値は、前のレッスンのXavier法です。

周損失05101520012
  • Batch Normalizationなし
  • Batch Normalizationあり
周ごとの訓練データの損失。Batch Normalizationなしは、20周のあいだずっと約2.3のまま、ほとんど下がらない。Batch Normalizationありは、1周目の約1.77から順調に下がり、20周目には約0.09になる
20周後の損失テストデータの正解率
Batch Normalizationなし2.3050.096
Batch Normalizationあり0.0860.964

Batch Normalizationなしでは、損失がまったく下がりませんでした。正解率0.096は、10個の数字からでたらめに選んだときの正解率(0.1)と同じくらいで、何も学習できていません。前のレッスンで学んだ勾配消失が起きているためです。Batch Normalizationを入れると、同じネットワークが学習できるようになり、正解率は0.964になりました。

各層の値を平均0、ばらつき1にそろえると、シグモイド関数に入る値が0の近く、つまり傾きが最も大きい(0.25に近い)ところに集まります。そのため、勾配が消えにくくなるのです。

学習するときと、使うとき(推論)の違い

使うときは、1件ずつ予測することもあり、ミニバッチの平均やばらつきを計算できません。そこで、学習の間に、ミニバッチごとの平均とばらつきを少しずつ記録しておき、使うときはその記録を使います。ドロップアウトと同じく、学習するときと使うときで動きが違う仕組みです。

注意:ミニバッチが小さすぎると、不安定になる

実は最初の実験では、訓練データ1347件を32件ずつに分けたとき、各周の最後に余る 3件だけのミニバッチ も使っていました。すると、損失は周ごとに大きく上下し、学習率0.1ではテストの正解率が0.46と0.61(乱数の種を2通り)にとどまりました。学習率0.2では、7周目に約0.33だった損失が、10周目に約3.9まで跳ね上がりました。わずか3件から計算した平均とばらつきは大きくぶれるため、そろえ方が毎回大きく変わってしまうのです。余りを使わないようにすると、上のグラフのとおり安定しました。

Batch Normalizationは、ミニバッチがある程度大きいこと(目安として数十件以上)を前提にした方法です。1件ずつのデータの中でそろえる Layer Normalization もあり、後の章で学ぶTransformer(大規模言語モデルのしくみ)では、こちらが使われています。ほかにも、画像1枚・チャネル1枚ごとにそろえる Instance Normalization、チャネルをいくつかの組に分けて組ごとにそろえる Group Normalization があり、どれも「何の範囲で平均とばらつきを求めるか」が違うだけです。

NumPyでBatch Normalizationを書く、よくある誤解と振り返り

学習するときの計算は、ミニバッチの平均とばらつきを、ニューロンごと(列ごと)に求めるだけです。

import numpy as np

z = np.array([[2.0, 5.0],
              [4.0, 3.0],
              [6.0, 4.0],
              [8.0, 6.0]])                # ミニバッチ4件 × ニューロン2個

mu = z.mean(axis=0)                      # 列ごとの平均 → [5.0, 4.5]
var = z.var(axis=0)                      # 列ごとの分散(ばらつき)→ [5.0, 1.25]
z_hat = (z - mu) / np.sqrt(var + 1e-5)   # 平均0、ばらつき1にそろえる

gamma, beta = 1.0, 0.0                   # 学習で決まる数(最初は1と0)
out = gamma * z_hat + beta
print(z_hat.mean(axis=0), z_hat.std(axis=0))   # → ほぼ [0, 0] と [1, 1]
  • axis=0 は「縦の方向(件数の方向)に計算する」という意味で、列ごと(ニューロンごと)の平均になります。axis を付けないと、表全体の平均を1つ求めてしまいます
  • 分散 var は、標準偏差の2乗です(第3章)
  • PyTorchでは nn.BatchNorm1d(ニューロンの数) や、CNN用の nn.BatchNorm2d(チャネルの数) を、層の間に入れるだけで使えます。学習するときと使うときの切り替えは、ドロップアウトと同じく model.train() と model.eval() です

よくある誤解

  • 「Batch Normalizationは、入力データを一度そろえる前処理と同じ」: 前処理は学習の前に一度だけ行いますが、Batch Normalizationは、学習中に、毎回、各層の中で行います
  • 「使うときも、そのときのミニバッチの平均を使う」: 使うときは、学習中に記録しておいた平均とばらつきを使います。そうしないと、同じ入力でも、一緒に予測するほかのデータによって結果が変わってしまいます
  • 「ミニバッチの大きさに関係なく、同じように効く」: 3件のような小さなミニバッチでは、平均とばらつきがぶれて、学習が不安定になりました

振り返り

  • Batch Normalization: 各層で、ミニバッチごとに値を平均0、ばらつき1にそろえ、学習で決まる γ\gamma、β\beta で調整する
  • 深いネットワークの学習を安定させ、勾配消失を防ぐ。シグモイド関数6層のネットワークが、なしでは何も学習できず、ありでは正解率0.964になった
  • 使うときは、学習中に記録した平均とばらつきを使う。ミニバッチが小さすぎると不安定になる。1件ずつそろえるLayer Normalizationもある

演習

演習を読み込んでいます…