こんにちは、サイオステクノロジーの藤井です。
この記事では、ディープラーニングが具体的にどのようなアルゴリズムで動いているかを書いていこうと思います。ディープラーニングを使ったことのない方や、私と同じように動かしたことはあるけどなんで動いているかわからない方が、ディープラーニングを理解する助けになれれば良いなと思っております。今回はディープラーニングの基礎であるニューラルネットワークについて学んだのでその仕組みについて書いていきます。
機械学習の流れ
ニューラルネットワークを含む、機械学習では次のような流れで分類や予測を行います。
- モデルを構築する
- 学習データを用いて学習する
- テストデータや実際のデータを用いて答えを予測する
1については人間が行い、2と3はコンピュータが行います。2を理解するには3がどうなっているのか知らないといけないので、まずは、3の部分を行うときのアルゴリズムを書いていきたいと思います。
パーセプトロンとは
ニューラルネットワークについて説明する前にパーセプトロンについて説明します。パーセプトロンはニューラルネットワークのもととなったモデルです。パーセプトロンを理解することはニューラルネットワークを理解することにつながります。
単純パーセプトロン
単層パーセプトロンの構造は以下の画像のように表せます。
それぞれの青丸をノードといいます。x1,x2は入力の大きさ表します。w1,w2は重みと言って入力をどの程度次の層に伝えるかを表します。それぞれの入力と重みの積の総和を計算し、その総和が閾値より高ければ1を出力し、低ければ0を出力します。以下のように書くことができます。
if x1*w1+x2*w2>border: return 1 else: return 0
この時、閾値borderの符号を逆転させたbを不等号の左に移動させ、以下のように表します。このbをバイアスと呼びます。
if x1*w1+x2*w2+b>0: return 1 else: return 0
重みw1,w2と、バイアスbを調整することで、パーセプトロンでは様々な入力と出力の関係を表現できます。
例えば、w1=0.5,w2=0.5,b=-0.7とすると以下のようにANDゲート、つまりw1,w2がともに1の時のみ1を出力する関係としてふるまいます。
x1 | x2 | x1*w1+x2*w2+b= | 出力 |
0 | 0 | 0*0.5+0*0.5-0.7=-0.7 | 0 |
0 | 1 | 0*0.5+1*0.5-0.7=-0.2 | 0 |
1 | 0 | 1*0.5+0*0.5-0.7=-0.2 | 0 |
1 | 1 | 1*0.5+1*0.5-0.7=0.3 | 1 |
パーセプトロンには、線形分離可能な分類しかできないという問題があります。線形可能とは平面上において直線1本で分けることができるかということです。
パーセプトロンでは層を重ねて多層パーセプトロンを作ることができます。多層パーセプトロンでは、線形分離不可能な分類もすることができます。
層が重なってもそれぞれのノードは単層パーセプトロンと同じ動きをします。ノード2は、x1×w3+x2×w4+b2が0以上なら1を次のノードに伝え、0未満なら0を伝えます。
例えば、w1=0.5,w2=0.5,b1=-0.2,w3=-0.5,w4=-0.5,b2=0.7,w5=0.5,w6=0.5,b3=-0.7の時、線形分離不可能なXORゲート、つまりx1とx2が同じときは0を異なるときは1という分類を表現できます。ノード1の出力をn1、ノード2の出力をn2とします。
x1 | x2 | x1*w1+x2*w2+b1 | n1 | x1*w3+x2*w4+b2 | n2 | n1*w5+n2*w6+b3 | 最後の出力 |
0 | 0 | 0*0.5+0*0.5-0.2 =-0.2 |
0 | 0*-0.5+0*-0.5+0.7 =0.7 |
1 | 0*0.5+1*0.5-0.7 =-0.2 |
0 |
0 | 1 | 0*0.5+1*0.5-0.2 =0.3 |
1 | 0*-0.5+1*-0.5+0.7 =0.2 |
1 | 1*0.5+1*0.5-0.7 =0.3 |
1 |
1 | 0 | 1*0.5+0*0.5-0.2 =0.3 |
1 | 1*-0.5+0*-0.5+0.7 =0.2 |
1 | 1*0.5+1*0.5-0.7 =0.3 |
1 |
1 | 1 | 1*0.5+1*0.5-0.2 =0.8 |
1 | 1*-0.5+1*-0.5+0.7 =-0.3 |
0 | 1*0.5+0*0.5-0.7 =-0.2 |
0 |
ニューラルネットワークとは
ニューラルネットワークはこのような構造をしています。
左のデータを入力する入力層、右の結果を出力する出力層、その間の中間層(隠れ層)からなります。一般的に中間層を2層以上持つニューラルネットワークを使った機械学習をディープラーニングと呼びます。この記事では、話を簡単にするために入力層ノード3個、中間層ノード2個の層が1層、出力層ノード2個のすべて全結合層(すべてのノードがそれぞれ次の層のすべてのノードにつながっている)のニューラルネットワークを使って説明します。
まずは、ノード1に注目してどのような処理を行うか見ていきます。
ニューラルネットワークにもそれぞれの経路に重みが設定されていて、パーセプトロンと同じように、入力と重みの積の総和にバイアスを足します。(x1×w1+x2×w2+x3×w3+b)
パーセプトロンではこの値が0より大きいか小さいかで、0か1を出力していました。この部分の処理を活性化関数といいます。ニューラルネットワークとパーセプトロンの違いは活性化関数の違いです。
活性化関数
パーセプトロンで使用した活性化関数をステップ関数といいます。ステップ関数は入力が0より小さければ0、大きければ1を出力する関数です。
ディープラーニングの隠れ層で使用される活性化関数では、シグモイド関数、tanh関数、ReLU関数などが有名です。例えば、シグモイド関数は以下のように定義されます
Pythonでは以下のように書けます。
import math def sigmoid(x): return 1/(1+math.e**-x)
シグモイド関数を使うと、データが広い範囲に散らばっていても0~1の範囲に収まるように整理することができます。
このような、「入力と重みの積の総和にバイアスを足したもの(x1×w1+x2×w2+x3×w3+b)」を活性化関数によって変換し、次の層のノードに出力する。という処理をそれぞれのノードが左から順に行っていきます。
出力層について
機械学習をもちいて解かれる問題は、大きく分けて「回帰問題」と「分類問題」があります。回帰と分類では出力層で用いる活性化関数が異なります。一般的に回帰問題では恒等関数を、分類問題ではsoftmax関数を使います。
恒等関数は入力されたデータをそのまま出力する関数です。ノードに3.0が入力されたら3.0を、-0.7が入力されたら-0.7を出力します。
softmax関数の定義式はこうです。
softmax関数は、入力を指数関数に通した値を、出力層のすべてノードの入力を指数関数に通した値の和で割ったものです。例えばノード3の入力(n1*w7+n2*w8+b3)が2、ノード4の入力(n1*w9+n2*w10+b4)が3の時、出力はそれぞれ0.269と0.731になります。
softmax関数では出力は必ず0~1の範囲に入り、すべての出力の和が1になります。この性質から、softmax関数の出力をそのクラスに分類される確率と考えることができます。今回の場合、26.9%の確率で上のクラス、73.1%の確率で下のクラスに分類された、つまりこの場合の入力データは下のクラスである確率が高いと考えます。
このように入力されたデータをもとに予測結果を出力する過程を前方伝播といいます。
ニューラルネットワークの学習
次に「2. 学習データを用いて学習する」の部分について説明していきます。ニューラルネットワークでは重みやバイアス(パラメータ)によって予測が当たるかどうかが決まります。学習では、予測の精度が向上するようなパラメータを探していきます。
損失関数(loss)
先ほど精度が向上するパラメータを探すと書きましたが、正確には損失関数の値が小さくなるパラメータを探します。
損失関数とは、正解の値とニューラルネットワークによって計算された予測がどれだけ違っているかを求める関数です。損失関数の値が小さいほど予測が正解に近い、つまり、精度が高くなります。
ここでは交差エントロピー誤差について説明します。交差エントロピー誤差はそれぞれの予測結果の対数をとり、それに正解をかけたものの総和です。
Pythonでは以下のように書けます。
import math def cross_entropy(result,label):#resultは予測結果のリスト、labelは正解のリスト loss=0 for x in range(len(result)): loss+=math.log(result[x])*label[x] return -loss
labelは正解のリストです。分類問題を解くニューラルネットワークでは、正解データをone-hot表現にします。one-hot表現とは、1つの成分が1で残りの成分が全て0であるようなベクトルで表すことです。
例えば、先ほどの、出力層についての例の場合、result=[0.269,0.731]です。
仮に正解が下だった場合、label=[0,1]となり、交差エントロピー誤差はlog(0.269)*0+log(0.731)*1≒0.313となります。
また正解が上だった場合、label=[1,0]となり、交差エントロピー誤差はlog(0.269)*1+log(0.731)*0≒1.313となります。
このように、正解に近いほど損失関数の値は小さくなります。
ニューラルネットワークで損失関数の値を求めるときは、データそれぞれの損失関数の値を求め平均をとります。例えば、学習データが10000個あり、学習データの損失関数の値が知りたいときは10000このデータそれぞれのlossを求め、平均します。
勾配降下法
損失関数は、重みやバイアスの値によって変化するので重みとバイアスを変数とした、関数といえます。今回のモデルの場合、重みが10個、バイアスが4個あるので、変数14個の関数です。
勾配とは、すべての変数の偏微分の集合です。
例えばが正の数だった場合、それは、w1を大きくした時に損失関数の値が大きくなることを意味します。なのでプラスの時はw1を小さく、マイナスの時はw1を大きくすると損失関数の値が小さくなります。
勾配を求める単純なアルゴリズムです。微分の定義に従って微分したいパラメーターを微増させた場合の関数の増加量をパラメータの増加量で割ります。
h=1e-4#微小な数字(1のマイナス4乗) for i in range(len(parameter)):#パラメータの個数だけ繰り返す #もともとの値を保存 tmp_val=parameter[i] #パラメータのi番目を少し増やして損失関数の値を求める parameter[i]=tmp_val+h loss_h=calc_loss() #パラメータのi番目を元に戻して損失関数の値を求める parameter[i]=tmp_val loss=calc_loss #微分した結果をパラメータのi番目に保存 gradient[i]=(loss_h-loss)/h
勾配降下法では、「勾配を求め、勾配の方向にパラメータを動かす」という計算を繰り返します。この時、パラメータをどの程度動かすかを決めるのが学習率です。例えば、学習率が0.1だった場合、w1の勾配が3だったならw1を0.1×3で0.3減らします。学習率が小さすぎると学習がなかなか進まず、学習率が大きすぎるとうまく損失関数の値が最小になるパラメータを見つけることができません
ニューラルネットワークの学習の流れ
1. 勾配を求める
パラメータごとに偏微分を行う
勾配は学習データの損失関数の値が小さくなる方向を示す
2. 重みとバイアスの更新
パラメータを「勾配×学習率」だけ減らす
3. 1と2を繰り返す
損失関数の値が十分小さくなるほど繰り返す
まとめ
今回はニューラルネットワークで学習するための必要最低限な要素についてアルゴリズムを紹介しました。
- ニューラルネットワークで予測を行うアルゴリズム
- ニューラルネットワークの学習を行うアルゴリズム
・パーセプトロンの限界
・活性化関数
・損失関数を最小になるパラメータを探す
・勾配を求める
しかし、今回紹介したアルゴリズムをもとにニューラルネットワークを実装しても、計算量が多いため、よほど小さいモデルとデータ数でない限り現実的な時間で学習することはできないと思います。次回はニューラルネットワークの学習を高速化するための手法とそのアルゴリズムについて書いていこうと思います。
ディープラーニングについて学ぶ上で「ゼロから作るDeep Leaning Pythonで学ぶディープラーニングの理論と実装」を参考にさせていただきました。
- [AI入門] ディープラーニングの仕組み ~その1:ニューラルネットワークってなに?~
- [AI入門] ディープラーニングの仕組み ~その2:学習を高速に行う仕組み~
- [AI入門] ディープラーニングの仕組み ~その3:CNNの仕組み~