ニューラルネットワークの仕組みの解説と実装

ニューラルネットワークの視覚的な説明とPythonでの実装(並列処理なし)

Edit

はじめに

この記事ではニューラルネットワークの仕組みを説明して実装したコードを掲載しております。琉球大学の知能情報コースprogramming1で習う知識で実装しています。並列処理をするようには設計していません。 理解度を深めるために問題を各所に掲載しております。回答は最後の方へスクロールダウンすると見つけることができます。

この記事を読むべき人

  • ニューラルネットワークの仕組みがわからないままTensorflowやPytorchなどの機械学習APIを使いたくない人
  • 線形代数が苦手だけどニューラルネットワークに興味ある人(極力、図を使って説明しています)

脳のニューロンの仕組み

実際に脳に存在するニューロンの図

image15.jpg

脳のニューロンはそのニューロンに集まる信号を一つづつ重み付け(信号を大きくしたり小さくしたりする)してその和が閾値(境目となる値)を超えると他のニューロンに信号を発火して、超えないと他のニューロンに信号を発火しない仕組みになっている。

脳のニューロンの仕組み

image1.jpg

脳のニューロンの振る舞いを形式的に表現すると以下のような図になる。

形式ニューロン

image8.jpg

重みと閾値

重みと閾値の計算

ニューロンに送られてくる信号をxとしてそれに対応する重みのベクトルをw(重み=weightのw)、閾値をb(閾値=biasのb)、重み付けされた信号の和から閾値を引いた値をuとすると

image27.jpg

となる。下の表と再度表示する形式ニューロンの対応を確認されたし。

image31.jpg image8.jpg

問題(1_1, 1_2)

1_1. 送られる信号をx = {x1,x2,x3,x4}、重みをw = {{w1},{w2},{w3},{w4}}、閾値をb 、重み付けされた信号の和から閾値を引いた値をuとすると、u = xw-b = x1w1+x2w2+x3w3+x4w4 - b と表すことができるが、 x = {1,-1,2,-2}, w = {{1},{2},{-1},{-2}}, b = 2とするとuはいくらか。

1_2.

image28.jpg

この図の各々の信号(青)と重み(黄色)を参考にuを求めよ、なお赤色の玉の中の数字の-(-3)は閾値が-3で、重み付けされた信号の和から-3を引くという意味とする。

コラム: ニューラルネットワークの学習

ニューラルネットワークは機械学習の一分野であり機械学習は読んで字の如く「機械に学習させること」である。ではニューラルネットワーク、つまりニューロンの網形体はどのように学習をするかというと「重みと閾値」を修正することで学習をする。ちなみに重みを修正するアルゴリズムをオプティマイザーというが閾値も全く同じアルゴリズムを採用する。ちなみに信号を重み付けして和を求めることを数学的な専門用語で線形結合と呼ぶ。

活性化関数

重みと閾値から、重み付けされた信号の和から閾値を引いた値を求める方法を学んだ。ここでは脳のニューロンの発火に相当する活性化関数を学ぶ。

image1.jpg

脳の発火の仕組みを数理的に表現する。重み付けされた信号の和から閾値を引いた値をuとするとuが0以上(=信号の和が閾値より大きい)であれば1(信号を発火する)を返す、0未満(=信号の和が閾値より小さい)であるなら0(信号を発火しない)を返すということになる。uを引数として下記のような定義をする関数のことを活性化関数という。ちなみにこの活性化関数はステップ関数と呼ばれる。

ステップ関数

発火の結果をzとし、z = f(u)としてz(活性化関数の戻り値)の定義を数式で表現すると、

image37.jpg

となる。 ステップ関数を横軸u、縦軸zでグラフで表現すると

image25.jpg

こうなる。

問題(2_1, 2_2)

2_1. uを1_1の答えとし, f(u)をステップ関数という活性化関数で定義し、z = f(u)で発火を表現するとzは1と0どちらか。

2_2. uを1_2の答えとし, f(u)をステップ関数という活性化関数で定義し、z = f(u)で発火を表現するとzは1と0どちらか。

他の活性化関数

ステップ関数の他にも活性化関数が存在する。シグモイド関数、tanh関数、ReLU関数などがある。これら3つの関数に共通する特徴は「微分可能な関数であること」であり、一方、ステップ関数はグラフからわかるとおり連続関数でないので微分可能な関数ではないということになる。活性化関数が微分可能であるということは後に解説する「誤差逆伝播」にとって重要な要因になる。とにかく活性化関数が微分可能でない限り機械学習を行うのは難しいということだけわかって欲しい。理由については後ほど触れる誤差逆伝播にて説明する。これからの学習ではニューラルネットの振る舞いについて活性化関数にステップ関数は用いずに現在主流のReLU関数(活性関数の中でも最も単純で最も強力)を用いて説明する。またこれからは信号の和から閾値を引いた値をuではなくxで表す。

まずシグモイド関数、tanh関数、ReLU関数について説明する。

シグモイド関数

image38.jpg

exp(-x)の部分はeを-x乗するという意味である。 f(x) = 1/{1+e^(-x)} シグモイド関数の取りうる値は0以上1以下( 0 <= f(x) <= 1) ステップ関数も0から1の値を取る。シグモイド関数はその点でステップ関数と共通しているが連続関数で微分可能かどうかという点に置いて違う。

tanh関数

image33.jpg

シグモイド関数と形が似ている。 -1以上1以下の範囲で値を取る。

ReLU関数

image6.jpg

xが0以上のときはそのままxを返し、0未満の時は0を返す。 しばしばf(x) = max(0,x)で表される。max関数は2つの引数のうちどちらか大きい方を返すという関数。

厳密にはxが0未満の値をとる時、ReLU関数は微分可能ではないが便宜上微分値を0と定義することで学習が可能になる。

問題(2_3, 2_4)

2_3. xを1_1の答えとし, f(x)をReLU関数という活性化関数で定義し、z = f(x)で表現する。zを求めよ。

2_4. xを1_2の答えとし, f(x)をReLU関数という活性化関数で定義し、z = f(x)で表現する。zを求めよ。

コラム: なぜ活性化関数が必要か

ニューラルネットワークというのは関数を模倣することができるという性質があるが、活性化関数がない(≒活性化関数が線形である)と線形(グラフにした時に直線となるような性質)の関数しか模倣できない。非線形の関数も模倣することができれば汎用性が上がるという理屈。まとめると活性化関数の主要な条件は「微分可能」と「非線形(グラフにすると曲がってる箇所があること)」の二つである。

入力層、中間層、出力層と損失関数

重みと閾値、そして活性化関数を学んだ。ニューロン一個単位での振る舞いはこれらの知識で再現できるが、ニューロンが層として連なったときの振る舞いや入力層、中間層、出力層それぞれの層の種類とその役割を理解しないとニューラルネットワークは理解できない。

image23.jpg

全体として、上の図では左から右手の層に信号が伝わるように設計されている。

入力層

データを入れて中間層に信号を送る。重み、閾値、活性化関数の概念がない。つまり値は特殊な処理をされずそのまま中間層のニューロンに送られる信号になる。 実際にありえる例としてクレジットカードを申請するある人物のカテゴリに関する3つデータを入力層の出力として扱うとする。 1番目の出力は収入が470万円以上であるか(470万円以上なら1で470万円未満なら0を出力) 2番目の出力はローンがあるかないか(ローンがある場合は1でない場合は0) 3番目の出力は住まいは一軒家かどうか(一軒家の場合は1, 一軒家ではない場合は0) ある人物が収入が500万でローンを組んでおらず一軒家に住んでいる場合は 1番目の出力:1, 2番目の出力:0, 3番目の出力:1 といった具合で入力層の出力が決まる。

image5.jpg

図で見ると、 一番上の入力層のニューロン(白色x)の出力が1番目の出力:1、 真ん中の入力層のニューロン(白色y)の出力が2番目の出力:0、 一番下の入力層のニューロン(白色z)の出力が3番目の出力:1 である。

問題(3_1)

3_1. クレジットカードを申請するある人物のカテゴリに関する3つデータを入力層の出力として扱うとする。

image4.jpg

1番目の出力(x)は収入が470万円以上であるか(470万円以上なら1で470万円未満なら0を出力) 2番目の出力(y)はローンがあるかないか(ローンがある場合は1でない場合は0) 3番目の出力(z)は住まいは一軒家かどうか(一軒家の場合は1, 一軒家ではない場合は0)

クレジットカードを申請する人物はのプロファイルは以下の通り。 男性 現在学生の琉球大学工学部工学科知能情報コース在籍、 20歳、 独身、 好きな食べ物は肉、 好きな音楽はロック、 趣味は散歩、読書、プログラミング、将棋、 住まいは家賃3万4千円のアパート、 ローンなし、 収入は100万弱

この人物のデータを元に入力層の各々のニューロンの出力x, y, zを求めよ。

中間層

中間層のニューロンは前の層から送られた信号を重み付けして和を求めて閾値を引いた値を活性化関数に通して出力を決める。

image5.jpg

中間層のニューロンは青色である。 図のニューラルネットの入力層の各ニューロンの出力から第一中間層であるaとbのニューロンの出力を求める。 入力層からaのニューロンに送られる各々の信号に対応する重みを{{1},{-2},{2}}、aのニューロンの閾値を2、 入力層からbのニューロンに送られる各々の信号に対応する重みを{{-3},{2},{1}}、bのニューロンの閾値を-1、 a,bの活性化関数が共にReLU関数とすると、a,bの出力は a = max(0, {1, 0, 1} × {{1},{-2},{2}} - 2) = max(0, 1) = 1 b = max(0, {1, 0, 1} × {{-3},{2},{1}} - (-1)) = max(0, -1) = 0 となる。 c, d, eの出力もa, bと同じようにして前の層の出力(a = 1とb = 0)とそれぞれのニューロンに対応する重みと閾値と活性化関数を用いて計算すると求められる。

image29.jpg

図では重みや閾値、活性化関数の表現を省略しているが矢印一つ一つに独立した重みが存在し前の層の出力を重み付けして、入力層以外のニューロンの一つ一つに独立した閾値が存在して信号の和から引いているというイメージで捉えるとわかりやすい。慣習的に同じ層に存在しているニューロンの活性化関数は統一して用いる。別に一つ一つ違う活性化関数を用いても理論的に問題なし。

問題(3_2)

3_2.

image24.jpg

図のaとbの出力は1と0である。c, d, eのaとbの出力に対応する重みはそれぞれ c: {{1},{-1}}、d: {{−1},{2}}、e: {{-2},{1}}、 そしてc,d,eの閾値がそれぞれ2,-2,-4(信号の和をそれぞれ2,-2,-4「引く」という意味)で活性化関数がReLU関数であるとする。 c, d, eの出力を求めよ。

出力層と損失関数

出力層と中間層の出力プロセスで唯一違う点は活性化関数である。中間層と出力層のニューロンは共通して前の層の主力を重み付けして信号の和を求めて閾値を引くという計算処理をするが、相違点として中間層ではシグモイド関数、tanh関数、ReLU関数などの活性化関数を用いる一方、出力層の活性化関数は線形関数(出力をそのまま返す≒活性化関数なし)、Softmax関数(後述)などの別の活性化関数を用いる。

ニューラルネットワークの用途は多様に存在し、出力層の活性化関数もその用途によって変化する。

「この人物は何歳であるか」、「この商品の最適な値段は何円か」などという再帰的な問題にもニューラルネットワークは適用できるし(機械学習するのにはハードルが高い印象)、「この盤面で適切な次の一手」、「このカスタマーは自社のクレジットカードを発行するに値する信用できる人物であるかどうか(Yes1(最高ステータス)かYes2(標準ステータス)かNo)」という複数の候補から一つを選ぶという分類問題などにも適応ができる(こちらの使い方を個人的に勧める)。

出力層の活性化関数

式の厳密な形をあまり覚えなくていい。数学的な背景や意味を模索しなくていい。出力と正解の距離感がわかり、導関数が綺麗な形になるように設計されているだけであるため。

線形関数

f(x) = x 信号をそのまま出力する関数、再帰問題に使われる。

Softmax関数

image30.jpg

分類問題に使われる。nは出力層のニューロンの数、xiはi個目のニューロンの信号の和から閾値を引いた数

image16.jpg

Softmax関数の定義を説明する。

図から出力層のニューロンの数が3個だからn=3。 fとgとhに送られた信号の和から閾値を引いた値をそれぞれ3, 0, -1とすると、 Softmax関数の分母はシグマの定義通りに計算するとexp(3)+exp(0)+exp(-1)となる。 (exp(x)はeをx乗するという意味) 最終的な出力は f = exp(3)/{exp(3)+exp(0)+exp(-1)} = 0.93623955187, g = exp(0)/{exp(3)+exp(0)+exp(-1)} = 0.04661262257, h = exp(-1)/{exp(3)+exp(0)+exp(-1)) = 0.01714782554, になる。 ちなみにSoftmax関数を活性化関数とする出力層の出力を全て足し合わせると1である。数式をよく眺めると納得できる。

他にも用途によっていろいろ出力層の活性関数が存在するが、これら2つが有名である。 出力層の活性関数による出力は訓練する時には理想とどれくらい離れているか測る指標になるし、実装時にはそのままその出力を扱えばいい。

問題(3_3)

3_3.

image9.jpg

出力層の一つ前の中間層{c, d, e}の出力が{0, 1, 2}である。 出力層のニューロンf, g, hのc, d, eの出力に対応する重みはそれぞれ f: {{-1},{1},{-2}}, g: {{2},{4},{-1}}, h: {{-2},{-1},{-3}} でf, g, hの閾値はf: -2,g: 1,h: -5で出力層の活性化関数はSoftmax関数である。 f, g, hの出力を求めよ。(電卓などの計算機を用いてよし)

損失関数(あまり覚えなくてもいい)

出力層の出力と正解の出力などを見比べてどれくらい離れているか調べる関数。出力層の活性化関数によって形が変わる。

教師あり学習(正解を元に訓練する方法)では損失関数を求めるには正解の値(教師データという)を用意しなければならない。よってニューラルネットワークで教師あり学習を行うには入力層に入れる「入力データ」と出力層の出力と比較するための「教師データ」の二つをセットで用意しなければならない。

平均二乗誤差

出力層の活性化関数が再帰的な問題を扱う線形関数(ykがそれぞれ線形関数の出力である)の場合使う損失関数は平均二乗誤差(式を全く覚えなくても問題なし)

image36.jpg

1/2がついている理由は微分するときに指数の2とうまい具合で打ち消し合うため。導関数の形や各出力を変数としたときの偏微分の形が重要なのでこの損失関数は忘れても問題ない。

image20.jpg

fがある人物の年齢でgがその人の年収(単位は万円)とする。 fとgの出力を{f, g} = {y1, y2} = {28, 500}、 fの正解が30でgを550 ⇔ 教師データ = {t1, t2} = {30, 550}とすると、 得られる誤差は定義から E = 1/2{(28 - 30)^2 + (500 - 550)^2} = 1252。 この計算プロセスはあまり記憶しなくていい。むしろ意味がないと言ってもいい。f, g の出力を変数とした時の偏微分の形が重要。ちなみに「その人が何歳であるか」、「年収はどれくらいか」などという再帰的な問題は分類問題に帰着(出力層のニューロンを0歳から100歳までの100分用意, 年収が何万円代に乗っているかに分けるなど)して学習させた方が精度がいいと個人的に思う。

Cross-entropy関数

出力層の活性化関数が分類問題を扱う(ykがそれぞれSoftmax関数の出力である)の場合に使う損失関数はCross-entropy関数(教師データの形式の説明以外全く覚えなくても問題なし)

image22.jpg

分類問題というのは複数の候補の中から一つ選ぶという問題で正解値を表すとき、選択すべきものには1で選択すべきでない他の候補は全部0という値をとることになっている。 「このカスタマーは自社のクレジットカードを発行するに値する信用できる人物であるかどうか(Yes1(最高ステータス)かYes2(標準ステータス)かNo)」という分類問題を解くとする。

image4.jpg

図の出力層で表して {f, g, h} = {y1, y2, y3} = {Yes1, Yes2, No}、 教師データを{t1, t2, t3} = {0, 1, 0} ⇔ 「このカスタマーはYes2に属し標準ステータスのクレジットカードを発行できる。」、 出力層のSoftmax関数によって得られる出力を {f, g, h} = {y1, y2, y3} = {0.93623955187, 0.04661262257, 0.01714782554}とすると、 得られる誤差は定義から、 E = -(t1log(y1) + t2log(y2) + t3log(y3)} = 1.33149646163 この計算プロセスもあまり記憶しなくていい。意味がない。f, g, h の出力を変数とした時の偏微分の形が重要。

損失関数に関する問題はなし、覚える必要がほぼ皆無であるため。

コラム: ニューラルネットワークとディープラーニング

ディープラーニングが機械学習の有力な手法として有名になってきている。ディープラーニングの定義は中間層が一つ以上あるニューラルネットワークのことを指す。 理論上、中間層が一つのニューラルネットワーク(三層ニューラルネットワークと呼ばれる)で十分であるが中間層の数を増やすと高度な学習が可能になった。

image4.jpg

中間層の数を増やすと高度な学習が可能になる理由は数理的には説明できない。また最近になってやっとディープラーニング が有力視された理由はハードウェアの性能の向上と中間層の数を増やすことで起きてしまう勾配消失問題(後のコラムで触れる)が改善されたからである。

順伝播(今までの復習)

復習

入力層から中間層、そして出力層に向かって信号を伝播していくこと順伝播という。 ニューロンには重みと閾値、活性化関数が存在する。 入力層: ニューロンに重み、閾値、活性化関数はない。 中間層: ニューロンに重み、閾値、活性化関数(シグモイド関数、tanh関数、ReLU関数)がある。 出力層: ニューロンに重み、閾値、活性化関数(線形関数(再帰問題)、Softmax関数(分類問題))がある。

image4.jpg

コラム: ニューラルネットワークの種類

全結合

image18.jpg

現在このドキュメントで学んでいるニューラルネットワーク、原点にして頂点。これを使わないニューラルネットワークの機械学習モデルは存在しないと言っても良い。

畳み込みニューラルネットワーク

image19.jpg

画像認識、処理と相性がいい。深層学習が注目される火種になった立役者。

再帰型ニューラルネットワーク

image12.jpg

時系列データ処理と相性がいい。チューリング完全=「理論上」汎用性最強。再帰型ニューラルネットワークがLSTMが主流。言語もある種の時系列データなので、再帰型ニューラルネットワークは自然言語処理にも応用可能。

チューリングは数学者の名前である。イミテーションゲームというとても面白い戦争映画の主人公ある。

敵対的生成ネットワーク

image21.jpg

2つのニューラルネットワークを用いて片方は偽物に似せる画像を生成し、もう片方は本物か偽物か見分ける。実際に存在する画像と生成された偽物の画像がどちらかランダムに選ばれて見分ける側に本物か偽物か見分けさせる。生成する側は見分ける側を騙すように見分ける側は偽物を精度良く見分けられるように訓練して切磋琢磨する形で学習する。結果的に見分ける側が本物か偽物かどうか見分けられないほど偽物を生成するニューラルネットワークが学習をしたら完成。これを使って技術者の倫理的に良からぬことをしている人もいるがまあ仕方ない。ちなみにこの偽物が本物を模倣して識別側が見分けるという構図はチューリング・テストと似ていると個人的に思っている。チューリングは人工知能の父と呼ばれる偉大な数学者であり、コンピュータが生まれる前からコンピュータの計算可能性について精巧な考察を行っている。現在のAI技術の筆頭である敵対的生成ネットワークが偶然ながら数学の巨人が遥か昔に考えたゲームと類似していることは個人的に非常に感慨深いと思っている。

他にもいろいろ面白いニューラルネットワークの種類が存在するが主要なのはこんなところ。

誤差逆伝播

ニューラルネットワークの学習

順伝播で出力を求めたあと、その出力と教師データを基にニューラルネットワーク全てのニューロンの重みと閾値を修正する。そのためには損失関数のそれぞれの重みや閾値についての偏微分=勾配を求める必要がある。

損失関数を偏微分する意味

損失関数は誤差を求めることができる。ニューラルネットワークの学習の目的は「誤差を最小化する」ということでそのために損失関数を偏微分して得られる勾配から学習を行う。損失関数を偏微分して得られる勾配は坂の傾斜のようなもので誤差を最小化するためその傾斜=勾配を「降下する」ことでニューラルネットワークの精度が上がっていく仕組み。 ちなみに損失関数の偏微分をすることで得られる情報は足下の勾配=誤差を最小化する方向しかない。わかりやすく例えると「目隠しをされた状態で山のどこか(誤差が大きい場所)にいて、付近の谷(誤差が小さい場所)に行くために足下の傾斜の情報だけで山を降る」ような状況である。

image39.jpg image3.jpg

偏微分とは

数学的な定義は多変数関数(f(x,y)のように引数が二つ以上ある関数)に対し一つの変数のみに関する微分(他の変数を定数として固定する)のことである。 ニューラルネットワークにおける損失関数の偏微分の意義は各ニューロンの重みや閾値をどれくらい修正すれば良いかの指標を求めるためである。

image7.jpg

図のX軸とY軸がニューロンの重みで縦軸が損失の値を表す。ニューラルネットワークの学習の目的は損失をGlobal Minimum(大域的最適解)の位置にすることであり、そのためにはXとYの重みを調節する必要がある。XとYの重みを調節するためにX軸方向、Y軸方向に正負どちらのにどのくらい進むべきかを求める。そしてX,Yについての偏微分をすることでそれが求められる。物理的な表現をすると現時点の足下に定規をX軸方向,Y軸方向に当てたときの傾きを求めて降る方向に移動するイメージ。

誤差逆伝播とは

順伝播とは入力層から出力層にかけてニューロンの出力を伝播することであることであり、その計算過程を学んだ。 誤差逆伝播とは「出力層から、中間層、入力層に渡って」誤差を逆に伝播することである。誤差逆伝播のアルゴリズムは微分の連鎖律を用いる。微分の連鎖律とは合成関数を微分するとき、その導関数がそれぞれの導関数の積で与えられるという関係式のことを指す。連鎖律自体は理解しなくていい。

損失関数の出力層についての偏微分

誤差逆伝播の最初の操作は損失関数を出力層の各ニューロンの出力について偏微分した時の勾配を求めることである。厳密には違うがニュアンス的に出力層の各ニューロンの出力がどれくらい誤差に貢献したか調べる感じである。

image14.jpg

f,g,hは出力層の出力でt1,t2,t3がf,g,hに対応する教師データである。 損失関数を出力層の各ニューロンの出力でそれぞれ偏微分すると、 二乗損失関数でもCross-entropy関数でも、 fのニューロンの出力の勾配: ∂f = f - t1、 gのニューロンの出力の勾配: ∂g = g - t2、 hのニューロンの出力の勾配: ∂h = h - t3、 となる。 f, h, g及びt1, t2, t3は出力層の活性化関数が線形関数なら範囲の制限はないがSoftmax関数なら0から1の値を取るので注意。 これらの損失関数の出力層のニューロンの出力に関する偏微分をして得た勾配を使って前の層のニューロンの出力の勾配を求めて同じようにして入力の層まで伝えていく。その過程でニューロンの重みや閾値を修正してニューラルネットワークは学習をする。

問題(4_1)

image14.jpg

出力層のニューロン(f, g, h)のSoftmax関数による出力はそれぞれ、 f = 0.11419519938, g = 0.84379473448,h = 0.04201006613 である。 これらの出力に対応する教師データの値(t1, t2, t3)はそれぞれ、 f = 1, g = 0 ,h = 0 である。 損失関数を出力層のニューロンによる出力で偏微分したときの勾配(∂f, ∂g, ∂h)を求めよ。

微分の連鎖律のグラフィカル表現

誤差逆伝播は微分の連鎖律を用いるが微分の連鎖律は定義だけ文面で説明されてもわかりづらい。視覚的に微分の連鎖律を理解することで誤差逆伝播の理解も容易くなる。実装を通して理解すると良いので図を見てよく分からなくてもいい。

パターン1 分岐

順計算において変数が分岐する場合 y = x + 5, z = 2x + 1など

image17.jpg

c = a, b = a ∂a = ∂ b + ∂ c

パターン2 加算

順計算において変数が加算される場合 z = x + yなど

image26.jpg

c = a + b ∂a = ∂c ∂b = ∂c

パターン3 乗算

順計算において変数が乗算される場合 z = x・yなど

image13.jpg

c = a・b ∂a = ∂c・b ∂b = ∂c・a

パターン4 関数

順計算において変数が関数を通る場合

image35.jpg

b = f(a) ∂a = ∂b・f'(a)

活性化関数の導関数

微分の連鎖律のパターン4の計算によると関数を変数が通るとき勾配を伝播させる時その関数の導関数を求める必要がある。そして順伝播の際に関数を通す操作は活性化関数に対して行う。つまり、活性化関数の導関数を求める必要がある。これが活性化関数が微分可能な関数でなければいけない理由なのである。

シグモイド関数(=f(x))の導関数

f'(x) = (1-f(x)) f(x) シグモイド関数の導関数はシグモイド関数自身を用いて表現することができる。 つまり、f(x) = a なら f'(x) = (1-a)a として表すことができる。

tanh関数(=f(x))の導関数

tanh関数もシグモイド関数と同様に導関数をtanh関数自身で表現することができる。 f'(x) = 1 - f(x)^2

ReLU関数(=f(x))の導関数

xが0以上のとき、 f'(x) = 1 xが0未満のとき、 f'(x) = 0 ちなみに各活性化関数の導関数を求めるとき活性化関数の出力の値を記録しておく必要がある。

微分の連鎖律の計算

image14.jpg image32.jpg

損失関数の出力層の一つ前の中間層のニューロンcの信号の和に関する偏微分で得られる勾配∂cを求めたい。

損失関数の出力層のニューロンの出力f, c, hに関する勾配はそれぞれ∂f, ∂g, ∂hである。 cの出力に対応するf, g, hのニューロンの重みをそれぞれw11, w21, w31とする。 cの出力は分岐(パターン1)してw11, w21, w31と乗算(パターン3)されて、f, g, hのニューロンに順伝播している。 手順を逆にすると最初に乗算、次に分岐の操作を辿ることで∂cを求めれる。

順計算が乗算(パターン3)のときの逆伝播の操作通りに計算すると得られる勾配はそれぞれ(∂f・w11),( ∂g・w21), (∂h・31)となる。 そしてこれらの値をもとに順計算が分岐(パターン1)のときの逆伝播の操作通りに計算すると得られるcの出力の勾配は、∂c = (∂f・w11) + (∂g・w21) + (∂h・31)となる。

また、ニューラルネットワークの学習には重みと閾値の修正を行うと学んだが、そのためには損失関数をその重みと閾値に関して偏微分したときの勾配を求める必要がある。ニューロンcの前の層の出力に対応する重みw11, w21, w31を修正するために、損失関数のw11, w21, w31に関する偏微分をして勾配∂w11, ∂w21, ∂w31を求めたい。

順計算では重みw11, w21, w31はそれぞれcと乗算(パターン3)されるので勾配は ∂w11 = ∂f・c , ∂w21 = ∂g・c, ∂w31 = ∂h・c となる。勾配∂w11, ∂w21, ∂w31を求めたらそれらを用いて重みw11, w21, w31を修正する。修正の仕方は「重みの修正とオプティマイザー」にて説明する。

ちなみに損失関数のf, g, hのニューロンの閾値b1, b2, b3に関する偏微分で得られる勾配はそれぞれ-∂f, -∂g, -∂hである。また閾値も重みと全く同じ方法で修正する。

image14.jpg image10.jpg

損失関数の中間層のニューロンの出力c, d, eに関する偏微分で得られる勾配を ∂c, ∂,d, ∂eとする。

順計算ではc, d, eは活性化関数を通して(パターン4)出力されている。 一つ前の層のニュートンa, bやそれらのニューロンの出力と乗算する重みや閾値に誤差を伝播する前に∂c, ∂,d, ∂eを加工しなくてはならない。 微分の連鎖律のグラフィカル表現によると関数を通す計算をした場合は勾配とその導関数を掛け合わせる操作をする。つまり、勾配∂c, ∂d, ∂eと活性化関数の導関数とそれぞれ掛け合わせて勾配を伝播させる。 c, d, eは活性化関数の戻り値なので活性化関数の導関数をc', d', e’と表して、誤差をa, bに伝播するときパターン4から勾配はそれぞれ、 ∂c・c', ∂d・d', ∂e・e’となる。これ以降は前の説明と同様に分岐(パターン1)と乗算(パターン3)の順計算を逆に辿り連鎖律に基づいて重みの勾配、前の層のニューロンの出力の勾配を求める。この処理を入力層のニューロンに到達するまで行う。 ちなみに順計算に加法(パターン2)を含むときの勾配の加工方法に関して、加算の計算は順伝播のときにニューロンに集まる信号の和を求めるときに行われるが、逆伝播の際に微分の連鎖律による勾配の特殊な加工はしない。 実はパターン1,3,4の微分の連鎖律の操作とこれから学ぶオプティマイザーのアルゴリズムを用いるとニューラルネットワークは学習ができるように設計されている。

コラム: 勾配消失問題

ニューラルネットワークの中間層の数を増やす、つまりディープラーニングにするとニューラルネットワークの表現力が高まることがあるとわかったが同時に問題が生じることがあった。勾配消失問題である。これはわかりやすく説明すると、勾配を前の層に逆伝播するときに、勾配が小さくなり入力層へ逆伝播される頃には勾配が消失してしまうという問題である。シグモイド関数を例に説明すると、微分の連鎖律パターン4で関数を通して逆伝播するとき関数の導関数を求めて勾配を掛ける必要があり、シグモイド関数(=f(x))の導関数は(1-f(x))f(x)である。シグモイド関数は0から1までの範囲で値をとるからシグモイド関数の導関数は0から0.25の値しかとれないことになる。つまりニューラルネットワークの学習のために最大でも0.25しかない導関数の値を勾配にかけて逆伝播する必要があるのだ。これは中間層の数が多ければ多いほど逆伝播の際に微分の連鎖立体的のパターン4の操作を多くしなければならないので同様に勾配も逆伝播するたびもっと小さくなるのだ。しかし最近は様々な工夫(ReLU関数の誕生、残差ニューラルネットワーク、LSTMなど)により勾配消失問題は改善されてディープラーニングによる精度の高い機械学習が可能になっている。

重みの修正とオプティマイザー

ある重み(または閾値)の勾配∂wを求めたら、wを修正する。勾配を基に重みを修正するアルゴリズムをオプティマイザーと呼ぶ。オプティマイザーにはいろいろ種類があるが古典的なものと最新の二つのオプティマイザーのアルゴリズムを紹介する。

SGD (Stochastic Gradient Descent=確率的勾配降下法)

新しい重みがwt+1, 従来の重みの値がwt,学習率がη

wt+1← wt-η∂w

ηは学習率という定数。学習率は大体0.001ぐらいの値を取る。古典的なオプティマイザーのアルゴリズムだがこれを基として最新のオプティマイザーへと改良する。基本中の基本のオプティマイザー。短所は勾配が0になると学習をやめることであり、それを改善したのが次に紹介するオプティマイザーのアルゴリズムである。勾配が0のとき学習しないのがダメな理由は勾配が0だからといってそこが大域的最適解では無く局所的最適解かもしれないからである。局所的最適解とは一応ある程度は適応しているが大域的最適解と比べるとまだまだな状態みたいなものである。

Adam

勾配の平均と分散を計算して重みの更新に使うオプティマイザー。 実装の練習として論文( https://arxiv.org/pdf/1412.6980.pdf )を読んでみよう。わからなかったら私と一緒に読みながらプログラミングしてみよう。 論文の見るべきところの抜粋

image34.jpg

重みの初期化

重み、閾値を「更新」することでニューラルネットワークは学習をする。また、「更新」をするには最初は必ず初期値の設定をすることが必要である。しかも、初期値を設定するときにはある程度の工夫が必要で結論から書くと平均0、 分散1/√n(nはニューロンの数)のガウス分布に従った乱数を重みの値に設定するべきである。(リンクを参照)「ガウス分布に従った乱数」とは「偏りがあるランダム」のことであり、「平均0、 分散1/√n」とはガウス分布に従って生成する乱数の平均が0で 分散が1/√nということになる。細かいことは気にしなくて良いがとりあえず偏ったランダムな数で設定しないと学習によくない。

image2.jpg

「偏る」とは上の図を見ると山になっている「0」付近の数字が出現しやすく谷になっている数字の部分は出現しにくいという意味である。

pythonの標準ライブラリではrandomというものが存在するが、それは一様(偏っていない)な乱数しか生成できない。(numpyにはあるが標準ライブラリではない)しかし0から1までの間で一様な乱数を二つ(X,Yとする)生成して使うことでガウス分布に従う乱数を生成できる。

image11.jpg

上の式はボックス=ミューラー法と呼ばれる二つの一様な乱数をガウス分布に従う乱数を生成する手法である。 ちなみに上の式で生成できる乱数の平均は0で分散が1である。なので 分散を1/√nにするにはその生成した乱数に1/√nかけるといい。

補足として中間層にReLU関数を使うときに初期値として設定する乱数の分散は√(2/n)にするのがおすすめ

分散を1/√nにしたいならボックスミューラー法で作られた乱数に1/√nをかけるだけでいい。

問題の回答

1_1: -1 1_2: 5

2_1: 0 2_2: 1

2_3: 0 2_4: 5

3_1: x = 0, y = 0, z = 0 3_2: c: 0, d: 1, e: 2 3_3: f = 0.11419519938, g = 0.84379473448,h = 0.04201006613

4_1: ∂f = -0.88580480062, ∂g = 0.84379473448, ∂h = 0.04201006613

Pythonで実装

gitのレポジトリ

並列処理をさせていないのでニューラルネットワークの学習が遅いです。

README.mdを読んでプログラムを実行したり、ソースコードと資料を参考にニューラルネットワークがどのように学習するかを頭の中でシュミレーションしてください。

comments powered by Disqus