ニューラルネットワークとは?
人間は、アクセス可能な情報の中から、驚くほど高い精度でパターンを識別する能力を持っています。車や自転車を見れば、それが何であるかをすぐに認識することができます。これは、車や自転車がどのような形をしていて、どのような特徴があるのかを長い時間をかけて学習してきたためです。人工ニューラルネットワークは、人間の神経系に似た複雑なアーキテクチャによって、人間の学習能力を模倣することを意図した計算システムである。
この記事では、ニューラルネットワークとは何か、ニューラルネットワークが通過する計算ステップは何か(その背後にある複雑な数学には立ち入らない)、そしてPython用の一般的なAIライブラリであるScikit-Learnを使用してどのように実装できるかを簡単に説明します。
ヒトの神経系
人間の神経系は、数十億個の神経細胞から構成されている。これらのニューロンは、感覚器から受け取った入力をまとめて処理し、情報を加工し、その入力に反応して何をすべきかを決定する。人間の神経系における典型的なニューロンは、樹状突起、核、軸索の3つの主要な部分からなる。神経細胞に渡された情報は、樹状突起で受け取られます。核はこの情報を処理する役割を担っています。ニューロンの出力は、軸索を介して他のニューロンに伝えられ、軸索はネットワークのさらに下の他のニューロンの樹状突起に接続される。
パーセプトロン
人工ニューラルネットワークは、人間の神経回路網の構造にヒントを得て作られたものです。最も単純なニューラルネットワークは、下図のように1つのニューロンだけで構成され、パーセプトロンと呼ばれます。
パーセプトロンは、1つの入力層と1つのニューロンを持ちます。入力層は樹状突起の役割を果たし、入力を受け取る役割を果たします。入力層のノードの数は、入力データセットの特徴の数に等しい。各入力は重み(これは通常何らかのランダムな値で初期化される)と掛け合わされ、その結果が足し合わされる。その和は活性化関数に渡される。パーセプトロンの活性化関数は、人間の神経系ニューロンの核に似ている。情報を処理し、出力を得る。パーセプトロンの場合、この出力が最終的な結果となる。しかし、多層パーセプトロンの場合、前の層のニューロンからの出力が、次の層のニューロンへの入力となる。
人工ニューラルネットワーク(多層パーセプトロン)
単層パーセプトロンがどのようなものかわかったところで、今度は多層パーセプトロン、あるいは一般に人工ニューラルネットワークと呼ばれるものに話を広げます。単層パーセプトロンは、データが「n」次元(「n」はデータセットの特徴の数)に線形分離可能であれば、簡単な問題を解くことができます。しかし、非線形に分離可能なデータでは、単層パーセプトロンの精度は著しく低下する。一方、多層パーセプトロンは、非線形分離可能なデータに対して効率的に作用することができる。
多層パーセプトロンは、一般に人工ニューラルネットワークと呼ばれ、複数のニューロンをネットワーク状に接続したものである。人工ニューラルネットワークは、入力層、1つ以上の隠れ層、出力層から構成される。下図のようなイメージです。
ニューラルネットワークは、2つのフェーズで実行される。フィードフォワードとバックプロパゲーションである。
フィードフォワード
以下は、フィードフォワードの段階で実行される手順である。
- 入力層で受け取った値は重みと掛け合わされる。入力と重みの和には、ヌル値を避けるためにバイアスが加えられる。
-
- 第1隠れ層の各ニューロンは、重みとバイアスによって入力層から異なる値を受け取る。ニューロンは、入力層から受け取った値を演算する活性化関数を持つ。活性化関数には、ステップ関数、シグモイド関数、relu関数、tanh関数など、さまざまな種類があります。経験則では、隠れ層のニューロンにはrelu関数、出力層のニューロンにはsigmoid関数が使われる。
-
- 第1隠れ層のニューロンからの出力は、第2隠れ層の重みと掛け合わされ、その結果は合計され、次の層のニューロンへと渡される。このプロセスは外層に到達するまで続けられる。外層で計算された値が、このアルゴリズムの実際の出力となる。
フィードフォワード段階はこれら3つのステップで構成される。しかし、予測された出力がすぐに正しいとは限らず、間違っている場合もあるので、それを修正する必要がある。学習アルゴリズムの目的は、できるだけ正確な予測をすることである。そして、この予測結果を改善するために、ニューラルネットワークは逆伝播の段階を経ることになる。逆伝播の間、異なるニューロンの重みは、望ましい出力と予測された出力との差ができるだけ小さくなるように更新されます。
バックプロパゲーション
バックプロパゲーションフェーズは、以下のステップで構成されています。
- 誤差は、予測された出力と希望する出力との差を定量化することで算出される。この差は「損失」と呼ばれ、差を計算するために使用される関数は「損失関数」と呼ばれる。損失関数には、平均二乗誤差やクロスエントロピー関数など、さまざまな種類がある。ニューラルネットワークは教師あり学習アルゴリズムであり、与えられた入力のセットに対して望ましい出力を必要とし、それがデータから学習することを可能にするものであることを忘れないでください。
- 誤差を計算したら、次はその誤差を最小にすることだ。そのために、すべての重みとバイアスに関して誤差関数の偏導関数が計算される。これをグラディエント・ディセントという。この導関数は誤差関数の傾きを求めるのに使用できる。傾きが正であれば、重みの値を小さくし、傾きが負であれば重みの値を大きくすることができる。これにより、全体の誤差を小さくすることができる。この誤差を小さくするための関数を最適化関数と呼ぶ。
このフィードフォワードとバックプロパゲーションの1サイクルを1「エポック」と呼びます。このプロセスは、妥当な精度が得られるまで続けられます。妥当な精度の基準はありません。理想的には100%の精度を目指しますが、これはどんな些細なデータセットでも達成するのは極めて困難です。多くの場合、90%以上の精度は許容範囲と考えられるが、実際にはユースケースに依存する。
Scikit-Learnでニューラルネットワークを実装する。
ニューラルネットワークとは何か、そしてシンプルで密に接続されたニューラルネットワークを構築するために必要な様々なステップを理解することができました。このセクションでは、与えられたアヤメ科の植物が属するクラスを予測するシンプルなニューラルネットワークを構築することを試みます。PythonのScikit-Learnライブラリを使用して、この分類タスクを実行するニューラルネットを作成します。Scikit-Learnライブラリのダウンロードとインストール方法は、http://scikit-learn.org/stable/install.html で入手可能です。
注:このチュートリアルで提供されるスクリプトは、Python Jupyter ノートブックで実行され、テストされています。
データセット
このチュートリアルで使用するデータセットは、https://archive.ics.uci.edu/ml/datasets/iris で公開されている、人気の高い Iris データセットです。このデータセットの詳細は、前述のリンクから入手可能です。
さっそくコードを見てみましょう。最初のステップは、このデータセットをプログラムにインポートすることです。そのために、Pythonのpandasライブラリを使用します。
以下のコマンドを実行して、虹彩データセットをPythonのデータフレームにロードしてください。
import pandas as pd
# Location of dataset
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
# Assign colum names to the dataset
names = ['sepal-length', 'sepal-width', 'petal-length', 'petal-width', 'Class']
# Read dataset to pandas dataframe
irisdata = pd.read_csv(url, names=names)
上記のスクリプトは、虹彩データをダウンロードし、データセットのカラムに ‘sepal-length’, ‘sepal-width’, ‘petal-length’, ‘petal-width’, ‘Class’ という名前を割り当て、 irisdata
データフレームにロードしているだけです。
このデータセットが実際にどのようなものかを見るには、以下のコマンドを実行してください。
irisdata.head()
上記のスクリプトを実行すると、以下のようにデータセットの最初の5行が表示される。
| | sepal-length | sepal-width | petal-length | petal-width | Class
| 0| 5.1| 3.5| 1.4| 0.2| アイリス・セトーサ
| 1|4.9|3.0|1.4|0.2| アイリス・セトサ
| 2|4.7|3.2|1.3|0.2|アイリス・セトサ
| 3|4.6|3.1|1.5|0.2| アイリスセトサ
| 4|5.0|3.6|1.4|0.2|アイリスセトーサ||||||etc.
前処理
このデータセットには5つのカラムがあることがわかる。タスクは、萼片の長さ、萼片の幅、花弁の長さ、花弁の幅(最初の4列)に基づいて、虹彩植物が属するクラス(5列目の値)を予測することである。次のステップは、データセットを属性とラベルに分割することである。次のスクリプトを実行する。
# Assign data from first four columns to X variable
X = irisdata.iloc[:, 0:4]
# Assign data from first fifth columns to y variable
y = irisdata.select_dtypes(include=[object])
y` がどのようなものかを見るには、次のコードを実行する。
y.head()
クラス | |
---|---|
0|アイリス・セトサ|-|1|アイリス・セトサ | |
1 | アイリス-セトーサ(Iris-setosa) |
2|アイリスセトーサ | |
3|アイリス-セトーサ|| |
y系列の値がカテゴリーであることがわかります。しかし、ニューラルネットワークは数値データに対してより効果的に働きます。次のタスクは、これらのカテゴリ値を数値に変換することです。しかし、まず最初に
y` シリーズの中にユニークな値がいくつあるか見てみましょう。次のスクリプトを実行する。
y.Class.unique()
出力
array(['Iris-setosa', 'Iris-versicolor', 'Iris-virginica'], dtype=object)
Iris-setosa’、’Iris-versicolor’、’Iris-virginica’の3つのユニークなクラスがあります。これらのカテゴリ値を数値に変換してみよう。そのためにScikit-Learnの LabelEncoder
クラスを使用する。
以下のスクリプトを実行する。
from sklearn import preprocessing
le = preprocessing.LabelEncoder()
y = y.apply(le.fit_transform)
ここで、もう一度 y
シリーズのユニークな値を調べると、次のような結果になります。
array([0, 1, 2], dtype=int64)
カテゴリ値が0,1,2といった数値にエンコードされていることがわかる。
電車内テスト分割
オーバーフィッティングを避けるため、データセットをトレーニング用とテスト用に分割する。トレーニングデータはニューラルネットワークのトレーニングに使用し、テストデータはニューラルネットワークのパフォーマンスを評価するために使用します。これは、ニューラルネットワークを今まで見たことのない(=トレーニングされた)データで評価するため、オーバーフィッティングの問題を解決するのに役立ちます。
トレーニング分割とテスト分割を作成するには、以下のスクリプトを実行します。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.20)
上記のスクリプトは、データセットの80%をトレーニングセット、残りの20%をテストデータに分割しています。
機能拡張
実際の予測を行う前に、すべての特徴を均一に評価できるよう、特徴のスケーリングを行うことは常に良い習慣である。特徴量のスケーリングはトレーニングデータに対してのみ行われ、テストデータに対しては行われない。これは、実世界ではデータはスケーリングされず、ニューラルネットワークの最終的な目的は実世界のデータで予測を行うことだからです。そのため、テストデータもできるだけ実データに近づけるようにします。
以下のスクリプトは特徴量のスケーリングを行うものである。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)
学習と予測
さて、いよいよお待ちかね、実際に予測を行うニューラルネットワークを学習させる時がやってきました。そのためには、以下のスクリプトを実行します。
from sklearn.neural_network import MLPClassifier
mlp = MLPClassifier(hidden_layer_sizes=(10, 10, 10), max_iter=1000)
mlp.fit(X_train, y_train.values.ravel())
Scikit-Learnを使えば、この3行のコードでニューラルネットワークを作成することができ、足で稼いだ作業の多くを処理してくれます。上のスクリプトで何が起こっているのか見てみましょう。まず、sklearn.neural_network
ライブラリから MLPClassifier
クラスをインポートしています。2行目では、このクラスが2つのパラメータで初期化されています。
最初のパラメータ hidden_layer_sizes
は隠れ層の大きさを設定するためのものです。このスクリプトでは、それぞれ10ノードからなる3つのレイヤーを作成します。ニューラルネットワークの層とノードの数を選択する標準的な公式はなく、手元の問題によってかなり異なる。一番良い方法は、いろいろな組み合わせを試して、何が一番うまくいくかを見ることです。
MLPClassifier` の2番目のパラメータは、ニューラルネットワークに実行させたい反復回数、つまりエポックを指定します。1つのエポックはフィードフォワードとバックプロパゲーションの1サイクルの組み合わせであることを忘れないでください。
デフォルトでは、「relu」活性化関数と「adam」コスト・オプティマイザが使用されます。しかし、これらの関数はそれぞれ activation
と solver
パラメータで変更することができます。
3 行目では、fit
関数を用いて学習データ(X_train
と y_train
)を用いてアルゴリズムを学習させる。
最後のステップは、テストデータに対して予測を行うことである。そのためには、以下のスクリプトを実行する。
predictions = mlp.predict(X_test)
アルゴリズムの評価
私たちはアルゴリズムを作成し、テストデータセットでいくつかの予測を行いました。今度は、我々のアルゴリズムがどの程度うまく機能しているかを評価する番である。アルゴリズムを評価するために、最もよく使われるメトリクスは混同行列、精度、再現性、そしてf1スコアです。これらのスコアを求めるには sklearn.metrics
ライブラリの confusion_matrix
と classification_report
メソッドが役に立ちます。以下のスクリプトは、我々のアルゴリズムの評価レポートを生成する。
from sklearn.metrics import classification_report, confusion_matrix
print(confusion_matrix(y_test,predictions))
print(classification_report(y_test,predictions))
上記のコードは以下の結果を生成します。
[[11 0 0]
0 8 0]
0 1 10]]
precision recall f1-score support
0 1.00 1.00 1.00 11
1 0.89 1.00 0.94 8
2 1.00 0.91 0.95 11
avg / total 0.97 0.97 0.97 30
混同行列から、私たちのニューラルネットワークは、私たちがネットワークをテストした30個の植物のうち、1個しか間違って分類していないことがわかります。また、f1スコアは0.97であり、150のインスタンスしか学習しなかったことを考えると、非常に良い結果です。
train_test_split` はランダムにデータをトレーニングセットとテストセットに分割するので、私たちのネットワークは同じデータでトレーニング/テストされていないかもしれないので、あなたの結果はこれらと少し異なるかもしれません。しかし、全体として、あなたのデータセットでも90%以上の精度があるはずです。
まとめ
この記事では、ニューラルネットワークとは何かについて簡単に説明し、虹彩データセットで学習させた非常にシンプルなニューラルネットワークの作成方法について説明しました。隠れ層の数、活性化関数、学習分割とテスト分割の大きさなどを弄って、今回紹介した以上の結果が得られるかどうか試してみることをお勧めします。