見出し画像

画像データの正規化と標準化

ディープラーニングで画像データを扱う際に正規化や標準化を行います。なぜこれのデータ変換が必要なのかと思ったことはありませんか。おまじないのように毎回行われるこれらの作業の意味を解説します。

最初にモノクロやRGBなどの画像データの構造について簡単に解説します。その後に正規化と標準化の方法の説明をし、どのように学習や推論で役に立つのかを解説します。

では、さっそく始めましょう。


モノクロ画像のデータ構造

単色の画像は、モノクロ(Monochrome)と呼ばれます。モノクロ画像のデータは色の濃淡(強さ、Intensity)の値を2次元に配列したものです。

モノクロとは単色を意味するので色自体の指定はありません。つまり、どんな色としても解釈ができます。ただし、モノクロは黒から白へのグラデーション(色の変化)として表現されることが多いです。それをグレースケール(Grayscale)と呼びます。

グレースケール

また、画像上の各位置を画素あるいはピクセル(Pixel、Picture Elementと呼びます。ピクセルは画像データの最小要素です。モノクロ画像のデータではピクセルを指定すればその位置における色の濃淡値がわかります。

例えば、グレースケール画像では各ピクセル位置の値は白の強さを表します。データ型としては、8ビット(1バイト)がよく使われ、0から255までの整数値になります。0が完全な黒で、255が完全な白になります。全部で256段階の濃淡を表現できます。

2次元に配列された濃淡値

つまり、モノクロの画像データは2次元に配列されたピクセルの濃淡値です。この画像データを$${\mathcal{M}}$$とすると、行と列(縦と横)で指定したピクセルの濃淡値は、以下のように表現できます。

$$
\mathcal{M}[ 行, 列 ] \rightarrow 濃淡値
$$

行と列は0ベースのインデックスを使うので、2行3列目の濃淡値は$${\mathcal{M}[1, 2]}$$と指定します。

このような2次元のデータ構造をチャンネルと呼びます。この表現を使うと「モノクロ画像のデータ構造は1つのチャンネルを持つ」と簡潔に言い換えられます。

RGB画像のデータ構造

カラー画像を表現する手法で代表的なのはRGBです。R、G、Bの各文字はそれぞれ赤(Red)、緑(Green)、青(Blue)の3色の頭文字からきています。赤と緑と青の混ぜ合わせでさまざまな色を表現します。

RGBは3つのチャンネルを持ちます。各チャンネルで赤、緑、青の濃淡をそれぞれ表現します。つまり、上記のモノクロ画像のようなピクセルの2次元配列が赤、緑、青の各チャンネル対して存在し、それぞれの色の強さを表現しています。

RGB

8ビットのデータ型を使うのであれば、各色で256の濃淡を表現できるので、3つのチャンネルを合わせると$${256 \times 256 \times 256 =16,777,216}$$色の組み合わせを各ピクセル位置に表現できます。8ビットが3つあるので24ビットカラーとも呼ばれます。

これまでチャンネル毎にデータが分かれている前提で話を進めてきましたが、実際にはRGB画像のデータ構造にはチャンネル・ファースト(Channel First)チャンネル・ラスト(Channel Last)の2つの方式があります。

画像ソース

チャンネル・ファーストでは、チャンネルと行と列を指定して濃淡値を得ます。以下のようにチャンネルを最初に指定する3次元配列で表現できます。

$$
\mathcal{M}[ チャンネル, 行, 列 ] \rightarrow 濃淡値
$$

例えば、赤・緑・青の各チャンネルのインデックスを$${0,1,2}$$とすれば、赤のチャンネルの2行3列目の濃淡値は$${\mathcal{M}[0, 1, 2]}$$と指定します。

チャンネル・ラストでは、チャンネルを最後に指定します。

$$
\mathcal{M}[ 行, 列, チャンネル] \rightarrow 濃淡値
$$

よって、赤のチャンネルの2行3列目の濃淡値は$${\mathcal{M}[1, 2, 0]}$$と指定します。

また、ディープラーニングで画像を扱う場合はモノクロでもチャンネルを指定する形式で扱います。そうすればモノクロもRGBもチャンネルの数が異なるだけで同じ形式で扱えるからです。

例えば、モノクロ画像をチャンネル・ファーストで表現した場合、2行3列目の濃淡値は$${\mathcal{M}[0, 1, 2]}$$と指定します。ここでのチャンネル0は赤ではありません。モノクロ画像にある唯一のチャンネルであり、色の指定ではありません。同じく2行3列目をチェンネル・ラストで指定するならば$${\mathcal{M}[1, 2, 0]}$$となります。

チャンネル・ファーストかチャンネル・ラストかは使うソフトウェアによって異なるので注意してください。ちなみに、深層学習用のライブラリであるPyTorchはチャネル・ファースト、TensorFlowはチャンネル・ラストをそれぞれデフォルトで使います。

なお、RGB画像のデータ構造がチャンネル・ラストである場合、ピクセル位置を指定して3色の値を得ることもできます。

$$
\mathcal{M}[ 行, 列 ] \rightarrow [ 赤の濃淡値、緑の濃淡値、青の濃淡値 ]
$$

これはチャンネル・ラストでは3色の濃淡値がメモリ上に連続で格納されているからです。

画像ソース

ただし、ソフトウェアの機能としてメモリ上の構造に限らず、チャンネル・ファーストあるいはチェンネル・ラストによるデータへのアクセスを可能にすることもできます。よって必ずしもデータの格納構造とアクセス方法が一致するとは限りません。

以上で画像データの構造の話が終わったので、正規化標準化の解説をする準備が出来ました。

画像データの正規化

正規化(Normalization)とはデータの値を0から1の範囲に収めることです。

例えば、画像データは0から255の値をとりますが、それを0から1の範囲へと変換します。やり方は簡単で、単に画像データの各チャンネル内の値を 255で割るだけです。

例として、画像データ$${\mathcal{M}}$$を正規化したものを下記にチャンネル・ファーストで表記しています。

$$
\text{正規化}(\mathcal{M})[ チャンネル, 行, 列 ] = \dfrac{\mathcal{M}[ チャンネル, 行, 列 ]}{255}
$$

もちろん画像データだけでなく、他のデータでも正規化は可能です。一般に、正規化はデータの最大値と最小値を使って以下のように計算します。

$$
\text{正規化}(x_i) = \dfrac{x_i - \min(X)}{ \max(X) - \min(X)} \\
 \\
X = \{x_1, x_2, \dots, x_N\}
$$

$${X}$$はデータの集合を意味し、値として$${x_1, x_2, \dots, x_N}$$を含みます。$${x_i}$$は集合内の各値になります。$${N}$$は集合内のデータの数です。なお、$${\min(X)}$$と$${\max(X)}$$は集合内のデータの最小値と最大値をそれぞれあらわしています。

これをMin-Max正規化(Min-Max Normalization)とも呼びますが、単に正規化と言う場合はMin-Max正規化を指すことが多いです。ただし、バッチ正規化(Batch Normalization)などでは正規化の意味が異なるので注意が必要です。

この記事では単に「正規化」を「Min-Max正規化」の意味で使います。

以上、正規化された値は必ず0から1の間に収まります。なお、上記の画像データの正規化は最小値が0であるため式が簡略化されています。

さて、データセット内の画像データを正規化する利点はなんでしょうか。

正規化を行うと何が嬉しいのか

正規化された値は小数点を含む浮動小数点型(フロート型)になります。画像データは整数型で収納されていることが多いのですが、ディープラーニングではフロート型を主に使うのであらかじめフロート型に変換しておくと都合が良いです。しかし、これは正規化を行わなくともできることなので、正規化による主要な利点ではありません。

正規化の利点は、データ値が0から1という小さい範囲に収まることから生まれます。これを理解するために、まずは正規化を施さない画像データでニューラルネットワークを訓練する場合を想像してみましょう。

ニューラルネットワークのパラメータは重みやバイアスを含みます。もっとも単純な1変数の計算で見てみると次のような式になります。

$$
y = w x + b
$$

ここで、$${w}$$と$${b}$$がそれぞれ重みとバイアスです。重み$${w}$$による出力$${y}$$の変化率を偏微分で表すと、それが入力値$${x}$$によって決まるのがわかります。

$$
\frac{\partial y}{\partial w} = x
$$

要するに重み$${w}$$を増やしたときに出力$${y}$$がどの程度増えるのかは入力値$${x}$$の大きさによるということです。仮に$${y}$$が損失値だとすると$${y}$$の値を減らすように重み$${w}$$を更新するには$${y}$$が増加するのとは逆方向に$${w}$$を調整する必要があります。

$$
w \leftarrow w - \lambda \ \frac{\partial y}{\partial w}
$$

$${\lambda}$$(ラムダ、Lambda)は学習率で一度に行う更新の大きさを調節します。$${ \frac{\partial y}{\partial w} = x}$$なので置き換えると、

$$
w \leftarrow w - \lambda \ x
$$

となり、パラメータの更新は学習率と入力値で決まります。正規化なしの画像データでは入力値の範囲が0から255と大きいので入力値によって重みの更新の幅が大きく異なることになります。つまり、大きな値を持つピクセルが小さな値を持つピクセルに比べて学習に対して大幅に重視されてしまう可能性があります。

もちろん、ニューラルネットワークでは多数の重みとバイアスがあり、また活性化関数も絡んでくるので、ある重みの変化が最終的な損失値の増減に与える影響は上記のような単純に計算できるものではありません。

しかし、入力値が重みの更新に影響を与えることは確かです。

つまり、大きな入力値は関連する重みを大きく変更してしまう可能性があります。そうなると学習が安定しにくくなります。なぜなら、重みの値を更新するたびに、その値が大きく変動するからです。

特に画像データは0から255の値を取るため、入力値によって重みの更新に大きな差が生まれます。入力値が1と200のケースを比べれば(学習率を無視すると)重みから1を引くのか200を引くのかの差があるわけです。重みの更新のされ方によっては勾配消失(パラメータの変更率がどんどん小さくなる)や勾配爆発(パラメータがどんどん大きくなる)が生じる可能性もあります。場合によっては重みの値が永遠に上下を繰り返すことも考えられます。いずれにせよ学習が安定しない可能性が高くなります。

もちろん学習率を小さくすれば入力値の大きさによるパラメータの更新を小さく抑えることはできます。しかし、入力値によってパラメータ更新の幅にかなりの差が生じる可能性があることに変わりはありません。

また、学習率が極端に小さいと、小さい入力値ではパラメータがほとんど更新されない可能性もあります。つまり、学習の速度を単に遅くしてしまいます。小さい学習率でも長く訓練を続けることで、パラメータの調整がうまくいき学習が成功する可能性はありますが、やはりデータに大きく左右されると考えられます。

ではパラメータの初期化で重みの値を最初から小さくしてはどうでしょうか。これによって仮に最終的な損失値が小さくなったとしたら重みの変化率の値も小さく抑えられるかもしれません。しかし、そうだとしても重みの初期値を極端に制限すると訓練において別の悪影響が生じる可能性が高くなります。なぜならニューラルネットワークは重みの値にある程度のバラツキがあることによってデータの値をより強調したり影響を抑えたりしてデータの特徴量を抽出するからです。重みがすべて小さかったらあまり役に立たないでしょう。

いずれにせよ、学習率やパラメータの初期化を上記のようにいじりすぎるのはさまざまな弊害を伴うので避けるべきです。

もちろん学習率やパラメータの初期化は大事な設定であり、調節する努力や工夫はどんな訓練でもある程度必要です。しかし、入力データの大きさによる弊害を減らしたいのであれば、入力データ自体を直接に調整をした方が効率が良いです。

その一つの方法として、データセット全体に正規化を施すことが有効であることが多くの実験からわかっています。正規化によって入力データの大きさが0から1の間に収まり、また入力値同士の差に大きな開きがなくなります。より連続な値になった入力値で訓練すれば、入力値による更新の幅の差も小さくなるのでパラメータの更新がスムーズになり学習が迅速に進むことが期待できます。

ただし、データセットによってはもともと正規化されていたり、それに近い状態になっているのであれば正規化の必要はありません。よく知られたデータセットでない場合は、データセットの中身を観察してから正規化を施すかどうか決めたり、正規化なしで実験してみて効果を確かめたりすることが推奨されます。

また、データセットによっては標準化を施した方が良い場合もあります。特に画像データでは標準化を行うのが有効であることが多いです。

画像データの標準化

標準化(Standardization)ではデータの分布を平均値が0で標準偏差が1になるようにします。

以下は標準化の一般的な計算式になります。

$$
\begin{align*}
\text{平均値}\ \mu &= \frac{1}{N} \sum\limits_{i=1}^{N} x_i \\
 \\
\text{分散}\ \sigma^2 &= \frac{1}{N} \sum\limits_{i=1}^{N}(x_i - \mu)^2 \\
 \\
\text{標準偏差}\ \sigma &= \sqrt{\sigma^2} \\
 \\
\text{標準化}(x_i) &= \frac{x_i - \mu}{\sigma}
\end{align*}
$$

画像データの標準化の計算はチャンネル毎に行います。つまり、赤のチャンネル、緑のチャンネル、青のチャンネルは別々に標準化されます。各チャンネルのデータで平均値と標準偏差を計算した後に、平均0で分散1になるように標準化をします。なお、$${N}$$はチャンネル内のピクセルの数になります。

チャンネル・ファースト形式で画像データ$${\mathcal{M}}$$の標準化を表記すると以下のようになります。

$$
\text{標準化}(\mathcal{M})[ チャンネル, 行, 列 ] = \dfrac{\mathcal{M}[ チャンネル, 行, 列 ] - \mu^{(チャンネル)}}{\sigma^{(チャンネル)}} \\
$$

$$
\begin{align*}
\mu^{(チャンネル)} &= \text{平均}(\mathcal{M}[ チャンネル ]) \\
 \\
\sigma^{(チャンネル)} &= \text{標準偏差}(\mathcal{M}[ チャンネル ])
\end{align*}
$$

標準化ではデータの分布が平均値0の周りに標準偏差1という小さい分散をもつことになり、平均的な値の範囲が限定されます。よって、正規化と同様に入力値がより連続するので学習がよりスムーズになる効果が期待されます。

しかし、正規化ではなく標準化を行う利点は何でしょうか。特に画像データにおいてなぜ有効なのでしょうか。

標準化を行うと何が嬉しいのか

画像データのデータセットでは、さまざまな画像が含まれており値のバリエーションがあります。さもなければデータセット自体を見直す必要がありますが、ここではその必要がないものを想定しています。

これらの豊富な画像データによってニューラルネットワークはさまざまな画像の特徴に対応できるように学習します。その際、入力値が平均に近い場合とそうでない場合とでは、後者の方がより画像の特徴を表している可能性があります。

よって、平均値を0、分散を1にする標準化では、平均的な特徴が0あるいはその周りの絶対値が小さい値として入力されるので、パラメータの更新への影響が小さくなります。逆に特徴的な入力値は0から離れる値でありパラメータの更新への影響が比較的大きくなります。これを特徴と捉えるのか、ノイズとして無視するようになるのかはニューラルネットワーク次第ですが、少なくとも平均的な値との区別は容易になっています。

また画像データはデータセットによって平均的な値が異なるので、平均値を考慮した標準化の方が正規化よりも有効である可能性があります。正規化では平均値は全く考慮されません。

直感的な説明をすると、画像データの標準化によって平均よりも濃い(値が大きい)あるいは薄い(値が小さい)ピクセルには画像の特徴が表れる傾向があり、それが重みの更新に相対的により大きく影響することによって学習の手助けになります。

さらに、入力値の平均が0に変換されるということは、0を境として、つまり正の値か負の値かによって、平均より上か下かの判断が容易になります。もちろん、バイアスの調整が行われればもとの平均が0でなくとも同様の効果を得ることができる可能性はあります。しかし、バイアスは0に初期化することが多いので最初から入力値を0中心にしておく方が効率が良いでしょう。むしろ、バイアスの更新はその後の活性化関数への入力値を調整するためであり、次の層へ送るシグナルの強さを調節するために行われると考える方が自然です。

例えば、シグモイド(Sigmoid)は0を境に50%の上下が決まります。また、ReLU(Rectified Linear Unit)は0を境に値を通過させるかどうかを決めます。よって、初めから0を中心にデータを変換しておくのは学習の手助けになります。その上で、訓練によるバイアスの調節が加わればどの特徴をより多く次の層へ伝えるかというコントロールが行えます。

これらの利点により、画像データの標準化を行うと、ニューラルネットワークの学習がより効率的に行えることが期待されます。

最後に注意点について述べます

以上の考察は、あくまでガイドライン的な意味合いとして捉えてください。データの分布はデータセットに特有なので常に正規化や標準化が有効とは限りません。

特に自分で集めたデータセットを使う場合などは、まずデータセットそのものの分布をチェックして適度なバラツキがあることを確認したり、必要ならもっとデータを集めるという作業を行いましょう。

その上で、正規化や標準化でのデータの調整を試したり、あるいはやらなかったりして学習の収束速度を比較すれば、どのデータ変換を使うべきかのヒントになるでしょう。また、異なるデータ変換を試したりなどいろんなことを実験してみると発見があるかもしれません。

もちろん、よく知られたモデルを転移学習などで使う場合は、そのモデルが訓練されたときに使われた手法でデータを変換する必要があります。例えば、イメージネット(ImageNetのデータセットを使って訓練された画像分類モデルなどは標準化を使うケースが多いです。また、そのためのチャンネルごと平均値と標準偏差は公開されているものを使います。

なお、訓練で使ったデータセットと実践で使うデータセットがまったく異なる分布に従っているならば、正規化も標準化もほとんど役に立たない可能性があります。これは正規化や標準化だけに限ったことではありません。よって訓練用のデータセットは想定される対象の画像をできるだけ幅広く大量に集める必要があります。


いいなと思ったら応援しよう!