線形回帰モデルについて解説する(2)

土曜日, 8月 03, 2019

機械学習 数学

t f B! P L
第2回の今回は、もうひとつの最適解の求め方、勾配法についてです。


解析解との比較


前回記事では、損失関数\( L(\theta) \)を重みパラメータ\( \theta \)で微分した値が0になる点を、損失関数の最小値(=最適解)と見なしていました。

誤差は0にはならないが、0にならないにしてもできるだけ小さい値がいいということですね。
だから損失関数の最小値を求めます。

そして、関数の微分=0となる点を求めることが最小値を求める標準的な手法でした。

平均二乗誤差(MSE)を損失関数とした線形回帰(最小二乗回帰)の最小値は解析的に求めることができます。

「解析的に求まる」というのは、中学高校でやった方程式のように、式変形で解(ここでは、損失関数を最小にする重みパラメータ\( \theta \))が求まる、という意味です。

これで通用する場合は問題ないのですが、世の中には解析的に解けない方程式がたくさんありますので、損失関数の微分=0という方程式が必ず解けるかどうかは保証されていません。

また、前回記事で述べたように行列積や逆行列の計算には、高い計算コスト(計算量)がかかります。
そのような場合は、この記事で紹介する勾配法を使って解くことができます。


損失関数の微分


前回やや天下り的に示した損失関数の微分について考えてみます。

勾配法の詳細は後述しますが、勾配法であっても損失関数の微分値は求める必要があります。

その後のアルゴリズムは異なり、損失関数の微分値=0を解くステップを行う代わりに、別の処理をします。

解析的に解けないといったケースや、計算量が膨大といったケースで効果を発揮します。

話を損失関数の微分に戻しましょう。

前回示した損失関数の微分式はこちらになります。

\( \frac{\partial L(\theta)}{\partial \theta} \)

では、これを詳しく見ていきましょう。

まず、\( L(\theta) \)ですが、こちらは結論から申しますと、スカラー値になります。

前回記事では、損失関数を平均二乗誤差(MSE)として以下のような定義式を載せています。

\( L(\theta) = \frac{1}{2} \| X \theta - y \|^2 \)

実は、この中身である\( X \theta - y \)の部分は、\( n \)次元ベクトルになります。
ここでは、データ点の数を\( n \)としています。

このベクトルの\( i \)番目の要素は以下のように表されます。

\( \sum_{j=1}^{d} \theta_j x_{i,j} - y_i \)

データ点\( x_i, y_i \)について、\( y_i \)とそれを近似した\( f(x_i) = \sum_{j=1}^{d} \theta_j x_{i,j} \)との差をとっており、近似誤差であることが直感的に理解できます。

そしてこの値はスカラー値です。

つまり、損失関数は各要素がスカラー値になっている1次元ベクトルの絶対値です。

1次元ベクトルの絶対値ということは、各要素の2乗を合計したものなので、損失関数自体もスカラー値になります。

ちなみに全体を書き下すと以下のようになります。

\( L(\theta) = \frac{1}{2} \sum_{i=1}^{n} ( (\sum_{j=1}^{d} \theta_j x_{i, j}) - y_i ) ^2 \)

次に、\( \theta \)ですが、これは説明変数\( x \)の各要素に対応する重みパラメータなので、\( \theta = (\theta_1, \cdots, \theta_d)\)でした。

つまりスカラー値をベクトルで微分していることになります。

スカラー値をベクトルで微分するとどうなるのでしょうか。
これはベクトルになると決まっています。

具体的には、スカラー値をベクトルの各要素でそれぞれ微分した値を要素に持つベクトルになります。

したがって

\( \frac{\partial L(\theta)}{\partial \theta} = (\frac{\partial L(\theta_1)}{\partial \theta_1} , \cdots ,\frac{\partial L(\theta_d)}{\partial \theta_d} ) \)

となります。
このベクトルの次元数は\( d \)で、微分するパラメータである\( \theta \)の次元数に一致します。
右辺は偏微分といって、微分する変数以外の変数を定数と見なすタイプの微分です。

例えば、\( \theta_1 \)で偏微分する場合は、それ以外の\( \theta_2, \cdots, \theta_d \)は定数として見なされます。


例として\( \frac{\partial L(\theta_1)}{\partial \theta_1} \)を計算してみましょう。
微分には線形性があるので、足し算でつながっている項同士は個別に微分計算を適用できます。

また偏微分なので、今回は\( \theta_1 \)を含まない項の微分はすべて0になります。

残念なことに\( L(\theta) \)はすべてのデータ点の誤差について\( \theta_1 \)を含んでいるため、すべての項を微分していく必要がありますが、実際には対称性があるのでひとまず\( i \)番目のデータ点の誤差を微分します。

\( \frac{\partial}{\partial \theta_1} (\frac{1}{2} ( (\sum_{j=1}^{d} \theta_j x_{i, j}) - y_i ) ^2) \)

こちらはスカラー値に対するスカラー値の微分なので、高校で習った微分と同じです。

つまり1次、2次関数および合成関数の微分です。
計算結果は以下のようになります。

\( ( (\sum_{j=1}^{d} \theta_j x_{i, j}) - y_i) x_{i, 1} \)

まず2次関数の微分によって2乗が外れ、中身はそのまま残ります。

2は係数として出てくるので、元からあった\( \frac{1}{2} \)との積で消えます。こいつこのためにいたんですね。

また合成関数の微分になるので、中身(\( \theta_1 \))に関する1次間数の微分で、\( \theta_1 \)と掛け算になっていた\( x_{i,1} \)のみが残るという仕組みです。

ここでやっと偏微分の恩恵に預かれました。

これをすべてのデータ点について計算したものを書き下すと

\( \frac{\partial L(\theta_1)}{\partial \theta_1} = \sum_{i=1}^{n}( (\sum_{j=1}^{d} \theta_j x_{i, j}) - y_i) x_{i, 1} \)

となります。

ここがめちゃくちゃ重要なんですが、これってよく見るとすべてのデータ点の誤差ベクトル

\( X\theta -y = (\sum_{j=1}^{d} \theta_j x_{1, j} - y_1 , \cdots, \sum_{j=1}^{d} \theta_j x_{n, j} - y_n) \)

と各説明変数\( x_i \)について、その1次元目の値(\(x_i\)自体は\(d\)次元のベクトル)からなるベクトル

\( (x_{1,1}, \cdots, x_{n,1}) \)

との内積になります。

なので、まとめて書くことができそうです。

実際に、各\( \theta \)について微分したベクトルは、以下のように表すことができます。

\( \frac{\partial L(\theta)}{\partial \theta} = X^T (X \theta - y) \)

これが前回記事で天下り的に示した損失関数の微分式になります。

\( X^T \)は説明変数\( x \)の\( j \)次元目の値からなる行にもつ行列、\( X \theta - y \)は列ベクトルなので、これらの積は上記議論で導出した内積からなるベクトルに一致することがわかります。

勾配法


さて、長かったですが、損失関数に関する議論は終わりでいよいよ勾配法の説明に入ります。

まず、勾配法のアルゴリズムを示します。

  1. \( \frac{\partial L(\theta)}{\partial \theta} \)を求める
  2. \( \theta = \theta - \epsilon \frac{\partial L(\theta)}{\partial \theta} \)で\( \theta \)を更新
  3. 指定回数更新 or 更新前後に\( \theta \)の変化量が一定以下になったら終了、終了しない場合は1へ戻る

非常にシンプルなアルゴリズムであることがわかります。

順番に説明していきます。

すでに述べたとおり、勾配法においても損失関数の微分は必要なので、微分しています。
計算の詳細は前節で述べたものになります。

次に\( \theta \)の更新式です。これが、というかこれしかないんですが、ここが勾配法のキモになります。

何をしているのかというと、現状の\( \theta \)の値から損失関数の微分値を差し引いて、\( \theta \)を更新しています。

\( \epsilon \)はlearning rateと呼ばれるハイパーパラメータで、ニューラルネットワークを使っている人には馴染みが深いと思います。

これはスカラーの定数であり、とりあえず\( \epsilon = 0.001 \)とかそのくらいの値と思っていいです。
そのまま微分値を引くと影響が大きすぎるので、調整のために導入している変数です。

そして、あらかじめ指定した回数分更新が終わるか、もしくは更新前後の変化があらかじめ指定した値を下回ったら終了になります。

特に後者に関しては、損失関数の微分値\( \frac{\partial L(\theta)}{\partial \theta} \)が0に近いということなので、解析解で求めたかったことが達成できている状態に近いと言えます。

理解が難しいのは、第2ステップの更新の部分だと思います。
なぜ、このようなシンプルな動作で最適解が得られるのでしょうか。

これは、高校数学の微分に立ち返ってみるとよくわかります。
そもそも微分値は、その地点\(theta\)における、損失関数の傾きを表しています。

この傾きの正負は、\( \theta \)を増加させた場合に損失関数の値がどうなるか、に対応しています。

そのため、傾きが正の場合は、\( \theta \)を増加させると損失関数が増大し、逆に負の場合は、\( \theta \)を増加させると損失関数が減少します。

ここでやりたいことは、損失関数の最小値にたどり着くことなので、傾き=正なら\(\theta\)を減少させる方向、負なら\(\theta\)を増加させる方向にずらしていけば、最小値(くぼみ)に向かって進んでいけるはずです。

なので、傾きが正のときは、今の\( \theta \)の値からこの微分値を引くことで、上り坂の逆方向(下方向)に進んでもらっているわけです。

負のときは、更新式の定義式から係数マイナスと損失関数の微分値自体のマイナスが打ち消し合いプラスになるので、\(\theta\)は増加方向に動きます。

勾配法とは、このようにして最適解のくぼみ(損失関数の最小値)に向けて、解パラメータを少しずつずらしていく手法と理解できます。


この方法の素晴らしいところは、微分値=0の解析解が求まらなくても、簡単な足し算引き算のみで解析解を求められることです。

また、1回の更新においては、\( \theta \)の次元数\(d\)回だけ足し算をすればいいので、計算量が\( O(d) \)で済みます。

もちろんイテレーションが必要なのでその分、結局計算量はかさみますが、逆行列の計算が入る解析解の計算よりもメモリ効率の面では圧倒的に優れています。


最急降下法と確率的勾配降下法


どっちもかっこいい名前です。

これまで説明した勾配法はやや広い概念であり、様々なテクニックによる拡張があります。

その中で代表的なものが、最急降下法確率的勾配降下法(stochastic gradient descent, SGD)です。


上記で説明した勾配法がほぼ最急降下法にあたります。

じゃあ、確率的勾配降下法は何が違うのかというと、損失関数自体の計算にすべてのデータ点を用いるか否かです。

最急降下法では、すべてのデータ点に関する誤差を計算して和をとっていますが、確率的勾配降下法では、データ点の集合からランダムにとってきたいくつかのデータ点のみの誤差を足した損失関数を使用します。
このあたりが確率的と呼ばれる所以です。

すべてのデータ点から計算した損失関数(本来の損失関数)を、一部のデータ点から計算した損失関数で代用しているわけですね。

損失関数の節で書いたとおり、損失関数の微分値はパラメータ\( \theta \)の次元数\( d \)のベクトルなので、データ点の数\( n \)は勾配法のアルゴリズムに一切影響ないんですね。

そのため、損失関数の計算に使うデータ点の数は何個でも大丈夫なことになります。
損失関数の計算自体にも計算コストがかかるので、一部だけで済むのであればそれに越したことはないです。

ニューラルネットワークの文脈では、最急降下法をバッチ学習、一部のデータ点を使う確率的勾配降下法をミニバッチ学習、1つのデータ点のみを使う確率的勾配降下法をオンライン学習と呼んでおり、計算コストと性能を総合するとミニバッチ学習がもっとも優れているとされています。


ニューラルネットワークとの関係


ニューラルネットワークの重みパラメータの最適化にもSGDが使われています。

最小二乗回帰のようなシンプルなモデルでは、解析的に解けないケースは少ないので、そこまで勾配法の有難みが身にしみてこないのですが、NNの重みパラメータのようにとても解析的には解けないケースでは特に威力を発揮します。

さらっと大事なことを言いますが、入力層と出力層のみからなるNNで、損失関数を平均二乗誤差とした場合、その解として得られる重みパラメータは、重回帰(最小二乗回帰)を勾配法で解いたときに得られる重みパラメータと一致します。

NNはこれにさらに隠れ層を追加し、活性化関数による非線形変換を挟むことで、より複雑な関数を近似する表現力を実現しています。

それはともかくとして、線形回帰モデルってNNとこんな共通点があるんだ〜ってことを一番言いたかったです。

このあたりの話をしっかり抑えておくと、機械学習分野に広く通用する基礎が固まります。


今日のベストプラクティス


重回帰∈ニューラルネットワーク

QooQ