はじめに
本稿では積層自己符号化器(Stacked Autoencoder)の解説・実装を行います。
目標は、モデルを動かすことに加えて、次の問いを説明できるようになることです。
- 入力を再現する学習によって、どのような表現が得られるのか
- 誤差から各重みの勾配をどう計算するのか
- 自己符号化器を積み重ねて、どのように分類へ利用するのか
- 事前学習によって、学習結果はどう変わるのか
ニューラルネットワークに必要な数式は、実装と対応づけながら説明します。ニューラルネットに関する基礎的な事項を理解していることを前提とします。また、実装についてはC言語で行います。自分が普段Cを使うので慣れているからです…
記事を書きなれていないため、読みにくいかもしれません。申し訳ないです。
自己符号化器とは
自己符号化器(Autoencoder、以下AE)は、入力したデータを再構成するように学習するニューラルネットワークです。
たとえば、手書き数字の画像を入力すると、同じ画像に近いものを出力するように学習します。このとき、正解として使うのは「画像に書かれた数字のラベル」ではなく、入力画像そのものです。
AEは、主に二つの部分から構成されます。
- エンコーダ: 入力を内部表現へ変換する
- デコーダ: 内部表現から入力を再構成する
入力を$\boldsymbol{x}$、内部表現を$\boldsymbol{z}$、再構成した出力を$\boldsymbol{x}_{\mathrm{re}}$ とすると、
\boldsymbol{z}=f_{\theta}(\boldsymbol{x}),
\boldsymbol{x}_{\mathrm{re}}=g_{\phi}(\boldsymbol{z})
と表せます。$\theta$と$\phi$は、それぞれ一つの数値ではなく、ネットワークが持つ複数の重みやバイアスをまとめて表しています。$f_{\theta}$という表記は、「パラメータ$\theta$によって変換の仕方が決まる関数$f$」を意味します。
処理の流れは、次のようになります。
図の直後
| 記号 | 意味 |
|---|---|
| $\boldsymbol{x}\in\mathbb{R}^{d}$ | 入力データ |
| $\boldsymbol{z}\in\mathbb{R}^{h}$ | エンコーダが生成する内部表現 |
| $\boldsymbol{x}_{\mathrm{re}}\in\mathbb{R}^{d}$ | デコーダが再構成した出力 |
| $f_{\theta}$ | 入力を内部表現へ変換する関数(エンコーダ) |
| $g_{\phi}$ | 内部表現から入力を再構成する関数(デコーダ) |
| $\theta$ | エンコーダの学習パラメータ(重み・バイアス)の集合 |
| $\phi$ | デコーダの学習パラメータ(重み・バイアス)の集合 |
ニューラルネットワークとして表す
具体例として、エンコーダとデコーダがそれぞれ一つの全結合層からなるAEを考えます。
\begin{aligned}
\boldsymbol{z}
&=f_{\theta}(\boldsymbol{x})
=\sigma_{\mathrm{enc}}\left(
W_{\mathrm{enc}}\boldsymbol{x}+\boldsymbol{b}_{\mathrm{enc}}
\right),\\
\boldsymbol{x}_{\mathrm{re}}
&=g_{\phi}(\boldsymbol{z})
=\sigma_{\mathrm{dec}}\left(
W_{\mathrm{dec}}\boldsymbol{z}+\boldsymbol{b}_{\mathrm{dec}}
\right).
\end{aligned}
ここで、$W$は重み行列、$\boldsymbol{b}$はバイアスベクトル、$\sigma$は活性化関数です。添字の$\mathrm{enc}$と$\mathrm{dec}$は、それぞれエンコーダとデコーダを区別しています。活性化関数は、この式ではベクトルの各成分に適用するものとします。
この場合、学習するパラメータは
\theta=\{W_{\mathrm{enc}},\boldsymbol{b}_{\mathrm{enc}}\},
\qquad
\phi=\{W_{\mathrm{dec}},\boldsymbol{b}_{\mathrm{dec}}\}
です。入力が$d$次元、内部表現が$h$次元なら、それぞれの形状は次のようになります。
| パラメータ | 形状 |
|---|---|
| $W_{\mathrm{enc}}$ | $h\times d$ |
| $\boldsymbol{b}_{\mathrm{enc}}$ | $h$次元 |
| $W_{\mathrm{dec}}$ | $d\times h$ |
| $\boldsymbol{b}_{\mathrm{dec}}$ | $d$次元 |
たとえば、28×28画素の画像を784次元のベクトルとして入力し、128次元の内部表現に変換する場合、ネットワークは次の構成になります。
入力よりも中間層の次元を小さくすることで、情報の通り道を絞ります。このような部分をボトルネックと呼びます。ネットワークは、その制約のもとで入力をできるだけ再現するように学習します。そのため、中間層には、入力データの特徴を捉えた、再構成に役立つ表現を得られることが期待できます。
なお、内部表現$\boldsymbol{z}$は、入力ごとに計算される値です。一方、$\theta$と$\phi$は、多くの訓練データを使って更新するパラメータです。この二つは区別して考える必要があります。
エンコーダとデコーダをまとめると、AE全体は
\boldsymbol{x}_{\mathrm{re}}
=g_{\phi}\left(f_{\theta}(\boldsymbol{x})\right)
という合成関数になります。
損失関数
AEは、再構成した出力 $\boldsymbol{x}_{\mathrm{re}}$ が、入力 $\boldsymbol{x}$ に近づくように学習します。その「違い」を数値で表すものが損失関数です。損失関数はデータの特性に合わせて適切なものを用います。
今回は、入力と出力の各成分の差を二乗して足し合わせた、二乗誤差を用います。
\ell(\boldsymbol{x},\boldsymbol{x}_{\mathrm{re}})
=
\frac{1}{2}
\sum_{j=1}^{d}
\left(x_{\mathrm{re},j}-x_j\right)^2
ここで、$d$ は入力の次元、$x_j$ と $x_{\mathrm{re},j}$ は、それぞれ入力と再構成した出力の第 $j$ 成分です。画像の場合は、対応する画素(ピクセル)の値を比較することになります。
この損失は常に0以上で、入力と出力のすべての成分が一致すると0になります。先頭の $1/2$ は、後で微分するときに二乗から生じる係数2を打ち消し、式を簡潔にするために付けています。
なお、ここでは成分ごとの二乗誤差を合計しています。成分数 $d$ で割る平均二乗誤差(MSE)とは定数倍の違いがあります。
訓練データ全体の目的関数
一つの入力を再構成できるだけでは、さまざまなデータに対応できません。そこで、複数の訓練データに対する損失の平均を考えます。
訓練データを $\boldsymbol{x}^{(1)},\ldots,\boldsymbol{x}^{(N)}$ とすると、最小化する目的関数は次のようになります。上付きの $(i)$ は、$i$番目のデータであることを表します。
J(\theta,\phi)
=
\frac{1}{N}
\sum_{i=1}^{N}
\ell\left(
\boldsymbol{x}^{(i)},
g_{\phi}\left(f_{\theta}(\boldsymbol{x}^{(i)})\right)
\right)
AEの学習では、この $J(\theta,\phi)$ が小さくなるように、エンコーダのパラメータ $\theta$ とデコーダのパラメータ $\phi$ を更新します。
ここで正解として使っているのは、入力 $\boldsymbol{x}^{(i)}$ 自身です。手書き数字の画像であれば、「何の数字か」というラベルを使わずに、画像の再構成を学習できます。
ただし、訓練データに対する損失が小さいだけでは、未知のデータも再構成できるとは限りません。そのため、実験では学習に使っていないデータに対しても再構成誤差を確認します。
おわりに
今回は、自己符号化器の構成を整理しました。AEは、エンコーダで入力を内部表現に変換し、デコーダで再構成します。そして、入力と再構成の誤差が小さくなるように、両者の重みとバイアスを学習します。
次回は、実装の準備として、一つの層の数式と、ベクトル・行列の配列表現の対応づけを行います。