14 Deep Learning 計算グラフ

「馬鹿よ貴方は」のネタで、算数の問題がある。
例えば、以下のようである。

問 太郎君はスーパーでリンゴを2個、みかんを3個買いました。リンゴは1個100円、みかんは1個150円です。消費税が10%かかるものとして、支払う金額を求めなさい。

この問は「金額の数値」を問うているが、何故問うているかは不明である。
たとえば、教室で算数の時間で先生が問いを発しているならわかる。生徒に計算能力をつけるため、国の施策に従い、給料をえるために発している問いである。

しかし、「馬鹿よ貴方は」のファラオさんが、いきなりこの問いを発すると途端に訳が分からなくなる。
太郎君は何歳なのか? スーパーでフルーツだけ買って帰ることがあるのか? 金額を求めなさいと言われているのは、おそらくレジの人であろう。
レジの人はバーコードを打つだけだろうし、手計算や暗算などはしないだろう。ではレジのコンピュータに向かってレジの人が求めなさいといっているのか?
音声認識システムのレジをわざわざ作る必要はまったくないので、このレジの人は独語癖があると考えられる。しかもなぜレジの人は、太郎君だと名前を知っているのか?
そもそも日本円で消費税が10%とは未来の話をしているのか?などと、掘り下げるといくらでもできるが、問い自体に意味はない。
これが人生に意味がないというハイデガーやニーチェのニヒリズムや仏教の空と同一であるとするのはいささか強引に思われるかもしれないが、ほとんど同義だと私は考える。

誤差伝播法で使用する計算グラフで問いを解くと下図のようになる。

分かりやすい問題を難しくすることは、このように容易であるが、問題を簡潔にするのは難しい。

今回は簡単なグラフの説明なので、余談が多めであった。

13 Deep Learning 学習アルゴリズムの実装

ニューラルネットワークの学習は以下の4つの手順で行う。

ステップ1 ミニバッチ

訓練データの中からランダムに一部のデータを選びだす。その選ばれたデータをミニバッチと言い、ここでは、このミニバッチの損失関数の値を減らすことを目的とする。

ステップ2 勾配の算出

ミニバッチの損失関数を減らすために、各重みパラメータの勾配をもとめる。勾配は、損失関数の値を最も減らす方向を示す。

ステップ3  パラメータの更新

重みパラメータを勾配方向に微小量だけ更新する。

繰り返す

ステップ1,2,3を繰り返す。

ここで使用するデータはミニバッチとして無作為に選ばれたデータを使用していることから、確率的勾配降下法(stochastic gradient descent)と呼ばれる。
省略してSGDと呼ばれることもある。

13.1  2層ニューラルネットワークのクラス

2層(隠れ層が1層)のネットワークを一つのクラスとして実装する。

実装は以下。

13.2 ミニバッチ学習の実装

下図のグラフが出力される。

12.2 ニューラルネットワークに対する勾配

今更ながら、外部ファイルの関数を使用する方法を見つけたので記しておく。

他のファイルの中で定義された関数は、import文で読み込んでから使います。たとえば、「myhelper.py」というPythonコードの中で定義されたmyfunc1という関数を使いたい場合は

と書くことで使うことができます。構文は「from [ファイル名] import [関数名]」です。

他のコードの中で定義された関数をまとめてimportしたい場合は

とすることで一括でimportできます。ただ、このように何をimportするのかを明示しない場合には名前が衝突してしまう可能性もあるので注意が必要です。

ファイル全体をimportする場合には、fromは使わず、importの対象に直接ファイル名を指定する次のような使い方も可能です。

引用元:LIFE WITH PYTHON Python関数の使い方

ニューラルネットワークの学習においても、勾配を求める必要がある。ここで言う勾配は、重みパラメータに関する損失関数の勾配である。たとえば、形状が 2 × 3 (2行3列) の重みWだけを持つニューラルネットワークがあり、損失関数をLで表す場合を考えると、この場合、勾配はδL/δW と表すことができる。実際に数式で表すと次のようになる。

\begin{equation}\begin{array}{c} W = \left( \begin{array}{ccc} w_{11} & w_{21} & w_{31} \\ w_{12} & w_{22} & w_{32} \end{array} \right) \\
\frac{\partial L}{\partial W} = \left( \begin{array}{ccc} \frac{\partial L}{\partial w_{11}} & \frac{\partial L}{\partial w_{21}} & \frac{\partial L}{\partial w_{31}}\\
\frac{\partial L}{\partial w_{12}} & \frac{\partial L}{\partial w_{22}} & \frac{\partial L}{\partial w_{32}} \end{array} \right) \tag{12.2} \end{array}
\end{equation}

δL/δWの各要素は、それぞれの要素に関する偏微分から構成される。
まず、重みのWを初期化し、入力xと重みWの内積predict()の出力から損失関数の値を求める。

実装すると以下のようになり、重みの勾配dWは次のように出力される。

12.1 Deep Learning 勾配降下法

12.1 勾配法

機械学習の問題の多くは、学習の最適なパラメータを探索する。ニューラルネットワークも同様に最適なパラメータ(重みとバイアス)を学習時に見つけなければならない。ここで、最適なパラメータというのは、損失関数が最小値をとるときのパラメータの値である。損失関数は複雑なので、勾配をうまく利用して、関数の最小値を探そうというのが勾配法である。注意点は勾配が指す先が本当に関数の最小値なのかどうか、実際、複雑な関数においては、最小値ではない場合がほとんどである。

勾配法は現在の場所から勾配方向に、一定の距離だけ移動し、さらに勾配を求め、勾配方向へ一定の距離を進むことを繰り返す。このようにして、関数の値を減らすのが勾配法(gradient method)である。一般的にニューラルネットワークでは勾配降下法(gradient descent method)と呼ばれる。(ディープラーニングにおいては、勾配降下法と誤差逆伝播法(後述)の理解が必須であるため、章をたくさん使って説明している。)

勾配法を数式で表すと、次の式(12.1)のように書くことができる。
\begin{equation} \begin{array}{cc} x_0 = x_0 – \eta \frac{\partial f}{\partial x_0} \\ x_1 = x_1 – \eta \frac{\partial f}{\partial x_1} \tag{12.1} \end{array} \end{equation}

式(12.1)のηはへ更新料を表し、学習率(learning rate)と呼ばれる。一回の学習で、どれだけ学習すべきか、すなわち、どれだけパラメータを更新するかを決めるのが学習率である。
式(12.1)は一回の更新式を示しており、この処理を繰り返し行う。つまり、この処理ごとに変数の値を更新していき、処理を何度か繰り返すことによって、徐々に関数の値を減らしていく。
ここでは、変数が2つの場合を示しているが、変数の数が増えても、同じような式—それぞれぞれの変数の偏微分の値—によって更新されることになる。
学習率の値は0.01や0.001など、前もって何らかの値に決める必要がある。
学習率は一般的に大きすぎても、小さすぎても「良い場所」にたどり着くことができない。
勾配降下法をPythonで実装すると、下記のようになる。

勾配を求めるnumerical_gradient()は以下のようであった。

f(x0,x1) = x0^2 + x1^2 を勾配法で求めてみる。

出力が微小な値(-6.111..e-10, 8,184e-10)に対して、真の最小値は(0,0)であるため、勾配法を実装した関数が正しく動作していることが確認できる。

勾配法による学習のプロセスは下図のように表される。

原点が最も低い場所であるが、徐々に近づいていることがわかる。

学習率は小さすぎても、大きすぎてもよい結果にならないと述べたが、具体例を下図に示す。

この結果が示す通り、学習率が大きすぎると、大きな値へと発散し、小さすぎると、ほとんど更新されずに終わる。学習率のようなパラメータはハイパーパラメータと言い、カッコいい名前に反して、人力で探す必要がある手動パラメータである。

11 Deep Learning 勾配

11.1 偏微分

続いて、変数が2つある微分を行う。次式がまずあるとする。

\[f(x0,x1) = x_0^2 + x_1^2 \tag{11.1}\]

Pythonでは次のように実装される。

 

ここでは、引数にNumPy配列が入力されることを想定する。
この関数をグラフに描画すると下図のようになる。

式(11.1)の微分を求めるとき、注意すべきは変数が2つあるということである。このため、「どの変数に対しての微分か」ということ、つまり、x0とx1のどちらの変数に対しての微分かということを区別する必要がある。ここで扱うような複数の変数からなる関数の微分を偏微分という。 偏微分を数式で表すとδf/δx0、δf/δx1のようにかける。

x0=3、x1=4のときのx0に対する偏微分δf/δx0は

つぎのようにPythonで実装される。

x0=3、x1=4のときのx1に対する偏微分δf/δx1は

つぎのようにPythonで実装される。

解析的な偏微分の解は定数項は0になるため、 δf/δx0 =2×0 = 6 δf/δx1=2×1=8
であるが 、上記の実装の出力結果とほぼ一致している。

偏微分は、1変数の微分と同じで、ある場所の傾きを求めます。ただし、偏微分の場合、複数ある変数の中でターゲットとする変数を一つに絞り、他の変数はある値に固定する。上の例の実装では、ターゲットとする変数以外を特定の値に固定するために、新しい関数を定義した。そして、その新しく定義した関数に対して、これまで使用した数値微分の関数を適用して、偏微分を求めた。

11.2  勾配

勾配は、のちに説明する、勾配降下法、誤差逆伝播法の勾配確認に必要な、ニューラルネットワークの重要な概念である。微分や偏微分の説明は過去を懐かしむためではない。

先の例ではx0とx1の偏微分の計算を変数ごとに計算したが、それではまとめて計算したいとする。(x0,x1)の両方の偏微分をまとめて、(δf/δx0, δf/δx1)として計算することを考える。この(δf/δx0, δf/δx1)のように、すべての変数の偏微分をベクトルとしてまとめたものを勾配(gradient)という。例えば、次のように実装される。

勾配が何を表すかというと、下図を参照されるとわかりやすい。

勾配は上図のように、一点に集中する場合もあれば、実際の山の起伏のようにそうならない場合もある。各地点において低くなる方向を指す。正確に言うと、勾配が示す方向(ベクトル)は、各場所において、関数の値を最も減らす方向である。