AI(機械学習)やってみた、第3弾です。「ニューラルネットワーク(Neural Network)」を使ったソムリAIを作ってみようと思います。”ニューラルネットワーク”、見るからにAIっぽい名前ですよね。
過去2回では決定木(Decision Tree)・ロジスティック回帰(Logistic Regression)を使ったソムリAI(ワインソムリエAI)を作りました。今回は「ニューラルネットワーク(Neural Network)」という”それっぽい”名前のモデルを使ったソムリAIを作ってみようと思います。
入力はx1・x2という2つで、それぞれの重要度を表す重みであるw1・w2が付いています。入力値に重みを掛けた値を足して、閾値Aを越えたら1を返し、越えなかったら0を返します。閾値(しきいち・いきち)とは「このラインを超えたらONになるぞ」という境界です。
例えば、w1=2, w2=3, A=10のときに、x1=4, x2=2だと14になり閾値を越えるので、y=1(ニューロンが発火した状態)となります。一方で、x1=2, x2=1だと7になるので活性化せずy=0となります。
ある程度の刺激を受けると活性化するという点でニューラルネットワークと相性がよいのです。先ほどのw1x1+w2x2+bの計算結果yをそのままシグモイド関数に突っ込みます。シグモイド関数の出力が0~1という性質を使うと分類の精度など、確率を表現できるのも便利なのです。
まず入力データとなる赤ワイン1600本分のデータを読み込みます。
次に、入力データをアルコール度数などの「モデル作成に使うインプットデータ」と「モデルを検証するための解答データ」であるワインの評価に分割します。
最後に、用意したデータを「モデル作成用」と「精度検証用」に分けます。前回同様、元データの70%をモデル作成用(訓練データ)・残りの30%を精度検証用(検証データ)にします。これで事前準備はおしまいです。
fitというメソッドに訓練データを突っ込んでモデルを作ったら、訓練データでのモデルの精度と、作成したモデルを検証用データに適用した場合の精度を表示させます。ここも前回までと同様です。
訓練データ・検証用データともに約59%の精度が出ています。両者の精度が近しいので、訓練用データに過度に適応しすぎた「過学習(Overfitting)」が起きていないことを示しています。まずまずのモデルです。
※過学習とは訓練データの精度は高いが、検証用データの精度が低い”訓練データに個別最適化された状態”のことです。汎化能力が低いとも言います。
「hidden_layer_sizes」では「各層のパーセプトロンの数と階層数」が指定できます。最初に試した「10, 10」という設定は「それぞれ10個のパーセプトロンを2階層積み重ねたニューラルネットワーク」を示しています。ここを「20, 30, 20」などと変化させて訓練用データ・検証用データそれぞれの精度をチューニングしていきます。
まず、階層数は2層のままで、各層のパーセプトロンの数を20個ずつ(hidden_layer_sizes=(20,20))にしてみます。
お、精度が62%に上がりました。パーセプトロンの数を増やすのは効果的のようです。
次に、パーセプトロンの数を10個のままとし、階層を3階層(hidden_layer_sizes=(10,10,10))にしてみます。
こちらも、精度が61%に上がりました。階層を増やすのも効果がありそうです。
いいとこどりして20個のパーセプトロンを3階層にして見ました(hidden_layer_sizes=(20,20,20))。
精度が60%に低下。。うーん、奥深いですね。
知りたいのは各層のパーセプトロンの数と階層の数であるため、hidden_layer_sizesに手当たり次第パターンを書き出します。最適な値を自動計算してくれるのではなく、自分でチューニングする必要があるようです。全自動というより、半自動なのです。
また、最適化に用いる活性化関数であるActivationについても、オプションに加えています。こうすることで最適化の変数(What)と最適化の方法(How)両面でチューニングしてやろうという算段です。
このようにパラメータを定義し、機械学習で使ったMLPClassifierというニューラルネットワーク関数とともにグリッドサーチ用の関数(GridSearchCV)に放り込みます。(ちなみに、オプションを指定せず突っ込んだら「機械学習が収束しない」旨のエラーが出たので、max_iterという学習回数を指定するオプションに大きめの値(デフォルトは200)を入れ、収束した時点で学習を終えるearly_stoppingという過学習を防ぐオプションをオンにしています。)
かなりバリエーションの多いチューニングだったので時間もそれなりにかかりました(20分くらい?)。最適なhidden_layer_sizesは「100個ずつ3階層+10個の層の計4階層(100,100,100,10)」・活性化関数は「ハイパボリックタンジェント関数」とのことです。ハイパボリックタンジェント関数とはロジスティック関数のようなS字型の関数で、出力値が-1から1の間になるものだそうです(ロジスティック関数の出力は0から1)。
このパラメータを使ってStep 2をもう一度やってみます。
訓練データの精度・検証データの精度共に改善され、検証データについては63%の精度という結果になりました。劇的な改善とはいきませんでしたが、グリッドサーチの結果精度が上がったのでよしとしましょう。
ここまでで、ニューラルネットワーク版のソムリAIができました。多層パーセプトロンを使って人間の脳のように複雑な判定を行うというのはとてもイケているように思えます。
ただし、ニューラルネットワークには欠点もあって、中のパーセプトロンでどのような処理が行われているか(重みとか)が分からないのです。利用者から見ると、「当たるモデルができたものの、中身がブラックボックス」なのです。
◆グリッドサーチで参考にしたサイト◆
概ね60%くらいの確率で当たるモデルができましたが、精度としてはちょっとイマイチかなと思います。また、使用したアルゴリズム(モデル)による差異もあまり出ませんでした。考えられる要因は、
とは言え、ソムリAIを通して機械学習の基本のキくらいは習得できたのかなと思います。全体を通して感じたのは、機械学習用のライブラリを使うだけで素人でも”それらしいモノ”が簡単に作れるという驚きと、うかうかしていると食い扶持がなくなるのではないかという危機感でした。次は機械学習・AIの流行である画像認識に挑戦してみようと思います!(これから夜なべして学ぶので少し時間がかかりそう)
バックナンバー
過去2回では決定木(Decision Tree)・ロジスティック回帰(Logistic Regression)を使ったソムリAI(ワインソムリエAI)を作りました。今回は「ニューラルネットワーク(Neural Network)」という”それっぽい”名前のモデルを使ったソムリAIを作ってみようと思います。
ニューラルネットワークとは
その名の通り「脳」の構造をモデルにしたものです。人間の脳はニューロン(ノード)にシナプスがつながっていて、シナプスから一定以上の刺激を受けるとニューロンが発火(活性化)します。ある力以上でつねられると「痛い」と感じるようなものです。これを機械で再現したのがニューラルネットワークです。パーセプトロン
基本構造
パーセプトロンはニューロンに相当するもので、複数の入力を受けて1つの出力を返します。ニューラルネットワークはパーセプトロンの組合せで作られます。一番シンプルなパーセプトロンはこんな感じ↓です。入力はx1・x2という2つで、それぞれの重要度を表す重みであるw1・w2が付いています。入力値に重みを掛けた値を足して、閾値Aを越えたら1を返し、越えなかったら0を返します。閾値(しきいち・いきち)とは「このラインを超えたらONになるぞ」という境界です。
例えば、w1=2, w2=3, A=10のときに、x1=4, x2=2だと14になり閾値を越えるので、y=1(ニューロンが発火した状態)となります。一方で、x1=2, x2=1だと7になるので活性化せずy=0となります。
バイアスを加えて活性化度合いを制御する
下の図は入力x1, x2に加えてバイアスbを加えています。バイアスはパーセプトロンの入力式にそのまま加算(or減算)されるもので、活性化されやすさを制御する効果があります。先ほどの例においてb=3を組み込むことでx1=2, x2=1のときでも10となりy=1となります(ゲタを履かせる効果があります)。重みwで活性化度合いをある程度制御することもできますが、定数であるバイアスbを使うことでよりシンプルに表現できるのです。活性化関数を組み込む
これまでの例は「閾値を越えたらON・越えなかったらOFF」というスイッチのようなもの(非連続なデジタルなもの)でした。ON/OFFだけでは複雑な機械学習は難しいので、実際のニューラルネットワークではシグモイド関数のような連続的な関数を組み込むことが多いです。シグモイド関数、前回のロジスティック回帰版ソムリAIでも登場しましたね。こんな感じのS字を描く関数で0から1の値を出力します。Pythonを使って簡単に描画できます。#ライブラリimport import numpy as np import matplotlib.pyplot as plt #ロジスティック関数のクラス定義 def logistic_function(x): return 1/ (1+ np.exp(-x)) #数式の定義 #描画 x = np.linspace(-5,5) #xを-5から5まで変化させる plt.plot(x, logistic_function(x)) #ロジスティック関数を描画 plt.xlabel("x") #x軸ラベルの設定 plt.ylabel("y") #y軸ラベルの設定
ある程度の刺激を受けると活性化するという点でニューラルネットワークと相性がよいのです。先ほどのw1x1+w2x2+bの計算結果yをそのままシグモイド関数に突っ込みます。シグモイド関数の出力が0~1という性質を使うと分類の精度など、確率を表現できるのも便利なのです。
単層パーセプトロン
このパーセプトロンを横に複数並べたものを「単層パーセプトロン」と呼びます。下の例では先ほどと同様、2つの入力値x1・x2に対して3つのパーセプトロンを並べています。1つの入力値に対してパーセプトロンが3つあるので、計6本の経路(バイアスからのものも含めると9本)が作られます。また、それぞれの重みも異なります。各パーセプトロンは入力を受けてそれぞれ出力y1, y2, y3を返します。多層パーセプトロン
単層パーセプトロンを積み重ねたもの(絵にすると横に伸ばしたもの)が「多層パーセプトロン」です。層を積み重ねることでより複雑な処理・分類ができるようになります。今回はこの多層パーセプトロン(ニューラルネットワーク)を使ってソムリAIを作ってみようと思います。ソムリAI(ニューラルネットワーク版)を作ってみる
Step 1: 機械学習の前準備(データの整形など)
前準備でやることはこれまでのソムリAIと同じです。簡単にコードだけ再掲します。まず入力データとなる赤ワイン1600本分のデータを読み込みます。
#入力データセットを読み込み import pandas as pd #データハンドリング用ライブラリ呼び出し data = pd.read_csv('winequality-red.csv', encoding='SHIFT-JIS') data.head() #先頭5つを表示して読み込まれていることを確認
次に、入力データをアルコール度数などの「モデル作成に使うインプットデータ」と「モデルを検証するための解答データ」であるワインの評価に分割します。
#機械学習で求める解である「評価」以外の項目をdata_Xに、「評価」をdata_Yに格納 data_X = data.copy() del data_X['評価'] data_Y = data['評価'] data_X.head() #評価欄がないことを表示して確認 data_Y.head() #評価欄のみであることを表示して確認
最後に、用意したデータを「モデル作成用」と「精度検証用」に分けます。前回同様、元データの70%をモデル作成用(訓練データ)・残りの30%を精度検証用(検証データ)にします。これで事前準備はおしまいです。
#入力データセットの70%を訓練データ・30%を検証データに分割 from sklearn.model_selection import train_test_split #機械学習用ライブラリ呼び出し X_train, X_test, Y_train, Y_test = train_test_split(data_X, data_Y,random_state=0, test_size=0.3) print(len(X_train)) #訓練用のデータ数を表示(70%) print(len(X_test)) #検証用のデータ数を表示(30%)
Step 2: ニューラルネットワークを使った機械学習
いよいよニューラルネットワークによるモデル作成です。と言ってもやることはこれまでと同じで、機械学習用のライブラリであるScikitlearnからMLPClassifierというニューラルネットワーク用関数を呼び出し、訓練データを使って機械学習させます。ちなみに、MLPとはMulti Layer Perceptronの略で多層パーセプトロンを示します。fitというメソッドに訓練データを突っ込んでモデルを作ったら、訓練データでのモデルの精度と、作成したモデルを検証用データに適用した場合の精度を表示させます。ここも前回までと同様です。
#ニューラルネットワークによる機械学習 from sklearn.neural_network import MLPClassifier #機械学習用ライブラリからニューラルネットワークを呼び出し mlpModel = MLPClassifier(solver='lbfgs', random_state=0, hidden_layer_sizes=(10,10)) #ニューラルネットワークモデル定義 mlpModel.fit(X_train, Y_train) #機械学習実行 print(metrics.accuracy_score(Y_train, mlpModel.predict(X_train))) #訓練データの精度表示(訓練データ用のモデルでどの程度訓練用データの「評価」を当てられるか) print(metrics.accuracy_score(Y_test, mlpModel.predict(X_test))) #検証用データの精度表示(訓練データ用のモデルでどの程度検証用データの「評価」を当てられるか)
訓練データ・検証用データともに約59%の精度が出ています。両者の精度が近しいので、訓練用データに過度に適応しすぎた「過学習(Overfitting)」が起きていないことを示しています。まずまずのモデルです。
※過学習とは訓練データの精度は高いが、検証用データの精度が低い”訓練データに個別最適化された状態”のことです。汎化能力が低いとも言います。
Step 3: モデルのチューニング(手作業)
ニューラルネットワークの精度は、1つの階層に用意するパーセプトロンの数とパーセプトロンの階層数で決まります。決定木でmax_depthという階層数をいじってモデルの精度をチューニングしたように、ニューラルネットワークにおいても「hidden_layer_sizes」の値を変えることでモデルのチューニングを行うことができます。「hidden_layer_sizes」では「各層のパーセプトロンの数と階層数」が指定できます。最初に試した「10, 10」という設定は「それぞれ10個のパーセプトロンを2階層積み重ねたニューラルネットワーク」を示しています。ここを「20, 30, 20」などと変化させて訓練用データ・検証用データそれぞれの精度をチューニングしていきます。
まず、階層数は2層のままで、各層のパーセプトロンの数を20個ずつ(hidden_layer_sizes=(20,20))にしてみます。
お、精度が62%に上がりました。パーセプトロンの数を増やすのは効果的のようです。
次に、パーセプトロンの数を10個のままとし、階層を3階層(hidden_layer_sizes=(10,10,10))にしてみます。
こちらも、精度が61%に上がりました。階層を増やすのも効果がありそうです。
いいとこどりして20個のパーセプトロンを3階層にして見ました(hidden_layer_sizes=(20,20,20))。
精度が60%に低下。。うーん、奥深いですね。
Step 4: モデルのチューニング(グリッドサーチ)
手作業では限界があるなと思って調べていたら「グリッドサーチ」という方法があることを知りました。一言で言うと「指定した様々なパラメータで機械学習を行って最適な組合せを教えてくれる」ものです。早速やってみます。知りたいのは各層のパーセプトロンの数と階層の数であるため、hidden_layer_sizesに手当たり次第パターンを書き出します。最適な値を自動計算してくれるのではなく、自分でチューニングする必要があるようです。全自動というより、半自動なのです。
また、最適化に用いる活性化関数であるActivationについても、オプションに加えています。こうすることで最適化の変数(What)と最適化の方法(How)両面でチューニングしてやろうという算段です。
#グリッドサーチ用のライブラリを読み込み from sklearn.model_selection import GridSearchCV #シミュレーションするパラメータを定義 parameters = {'hidden_layer_sizes': [(10,10,10),(10,10,10,10),(100,100,10),(100,100,100), (100,100,100,10),(200,200,10),(200,200,100),(200,200,200)], 'activation': ['logistic', 'identity', 'relu', 'tanh']} #グリッドサーチの実行 mlpModel2 = GridSearchCV(MLPClassifier(random_state=0, max_iter=1000, early_stopping=True), parameters) mlpModel2.fit(X_train, Y_train) #ベストな隠れ層を使った機械学習 mlpModel2.best_params_ #ベストな隠れ層の結果表示
かなりバリエーションの多いチューニングだったので時間もそれなりにかかりました(20分くらい?)。最適なhidden_layer_sizesは「100個ずつ3階層+10個の層の計4階層(100,100,100,10)」・活性化関数は「ハイパボリックタンジェント関数」とのことです。ハイパボリックタンジェント関数とはロジスティック関数のようなS字型の関数で、出力値が-1から1の間になるものだそうです(ロジスティック関数の出力は0から1)。
このパラメータを使ってStep 2をもう一度やってみます。
訓練データの精度・検証データの精度共に改善され、検証データについては63%の精度という結果になりました。劇的な改善とはいきませんでしたが、グリッドサーチの結果精度が上がったのでよしとしましょう。
ここまでで、ニューラルネットワーク版のソムリAIができました。多層パーセプトロンを使って人間の脳のように複雑な判定を行うというのはとてもイケているように思えます。
ただし、ニューラルネットワークには欠点もあって、中のパーセプトロンでどのような処理が行われているか(重みとか)が分からないのです。利用者から見ると、「当たるモデルができたものの、中身がブラックボックス」なのです。
◆グリッドサーチで参考にしたサイト◆
まとめ
- ニューラルネットワークは人間の脳をモデル化した多層パーセプトロンであり、AIやっている感が強い。
- パラメータのチューニングにはグリッドサーチのような手法があるものの、人間の「勘と経験」が必要。
- 最適化されたモデルの中身がブラックボックスなのは不便。
ソムリAIの全体総括
決定木・ロジスティック回帰・ニューラルネットワークの3つのソムリAIを作ったので、それぞれのパフォーマンスを並べてみます。概ね60%くらいの確率で当たるモデルができましたが、精度としてはちょっとイマイチかなと思います。また、使用したアルゴリズム(モデル)による差異もあまり出ませんでした。考えられる要因は、
- データの数が少ない
- 例えばワイン10000本のデータなどインプットを増やすと精度が上がるかも。
- データに含まれる変数が少ない
- 変数はアルコール度数や酢酸濃度など「液体としてのワイン」のデータであり、「香り」のようなワインの風味に大きく影響する要素が含まれていない。
とは言え、ソムリAIを通して機械学習の基本のキくらいは習得できたのかなと思います。全体を通して感じたのは、機械学習用のライブラリを使うだけで素人でも”それらしいモノ”が簡単に作れるという驚きと、うかうかしていると食い扶持がなくなるのではないかという危機感でした。次は機械学習・AIの流行である画像認識に挑戦してみようと思います!(これから夜なべして学ぶので少し時間がかかりそう)