【Sequential】Kerasを使ったニューラルネットワーク|python

こんにちは、青の統計学です。

今回はニューラルネットワークについて解説していきます。

単純パーセプトロン

【機械学習】単回帰分析をpythonで実装してみましょうで少し触れましたが、

目的変数に対して非線形活性化関数(例えばシグモイド関数)を加えると単純パーセプトロン、何層も非活性化関数を重ねたものが多層パーセプトロンになります。

多層パーセプトロンは活性化関数にステップ関数、ニューラルネットワークは活性関数にシグモイド関数を使っているという違いはありますが、

単純パーセプトロンを何層にも重ねて、複数の入力に対して複数の出力を実現させる機能においては等しいです。

Kerasというライブラリを使えば、比較的簡単にニューラルネットワークを使えます。

ただ、中身で何が行われているかを理解しておけば、

・モデルの選択

・パラメーターをどうチューニングするか

・どのモデルとアンサンブルさせるか

などの検討がつきやすくなります。

ニューラルネットワーク(neural network)

多層パーセプトロン(Multilayer perceptron)

単純パーセプトロン。中間層が2層以上だと、深層学習と言います。

まずパーセプトロンとは、n個の入力を受けて、1個の出力を出す「関数」のことです。

決定木でいうノードのようなものです。実際、文献によっては「ノード」や「ユニット」などの呼ばれ方をしています。

そして、この関数のことを「活性化関数」と呼び、場面に応じて使い分けがなされています。

例えば、2値分類ならシグモイド関数で、多クラス分類ならソフトマックス関数などを使います。

入力で与えられた値が等しく中間層(中間層)に渡されていくわけではありません。

重要なのは、「重み」があるということです。

実際には、ある中間層のパーセプトロンへの出力信号は以下のように表すことができます。

$$f(\sum_{i=0}^{n-1}w_{I}*x_{I}+b)$$

b:バイアス

wi:i番目のパーセプトロンからの入力にかけられる重み

xi:i番目のパーセプトロンからの入力信号

f():活性化関数

そして、多層パーセプトロンとは以下のように出力まで複数用意できるモデルです。

そして、各層のパーセプトロン同士は互いに結合しており、図のようなパーセプトロンを全結合型ニューラルネットワークと呼びます。

ニューラルネットワークと多層パーセプトロンの関係がお分かりいただけたでしょうか。

「多層」にすることで何が嬉しいのかというと、Reluなどの活性化関数をいくつかの層に重ね合わせることで、非線形の表現ができ、モデルを柔軟にできるということです。

では、何を機械が学習していくのかというと中間層と出力層へのウェイト(w)です。

この重みを調節することによって、入力したものに対して望む出力(教師データ)に近づけることができます。

学習は勾配降下法という手法を用いて、誤差を出力層から入力層に伝播させていきウェイトを更新していきます。

これを誤差逆伝播法(back propagation)と言います。フィードバックに近い考え方です。

乳がんデータを使った予測(python)

では、scikitlearnの乳がんのデータを使って、予測値を出してみましょう。

乳房塊の微細針吸引物(FNA)のデジタル化画像から計算されており、画像中に存在する細胞核の特徴を捉えたものです。

データセットの中では悪性(malignant)は0、良性(benign)は1で表されており、targetカラムで表されております。

from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
import numpy as np
import pandas as pd
data_breast_cancer = load_breast_cancer()

# Pandasによるデータの表示
df_target = pd.DataFrame(data_breast_cancer["target"], columns=["target"])
df_data = pd.DataFrame(data_breast_cancer["data"], columns=data_breast_cancer["feature_names"])
df = pd.concat([df_target, df_data], axis=1)
df.head()

必要なライブラリをimportしてきて、乳がんデータをデータフレームの型にします。

ちなみにpandasのconcatメソッドは、データフレーム同士を結合することができます。

使い方としては、pd.concat([結合するDataFrame1,結合するDataFrame2],結合する方向)です。

結合する方向は、行の方向(縦)ならaxis=0で列の方向(横)ならaxis=1です。デフォルトはaxis=0なので、axis=0とわざわざ入力する必要はありません。

targetmean radiusmean texturemean perimetermean areamean smoothnessmean compactnessmean concavitymean concave pointsmean symmetryworst radiusworst textureworst perimeterworst areaworst smoothnessworst compactnessworst concavityworst concave pointsworst symmetryworst fractal dimension
0017.9910.38122.801001.00.118400.277600.30010.147100.241925.3817.33184.602019.00.16220.66560.71190.26540.46010.11890
1020.5717.77132.901326.00.084740.078640.08690.070170.181224.9923.41158.801956.00.12380.18660.24160.18600.27500.08902
2019.6921.25130.001203.00.109600.159900.19740.127900.206923.5725.53152.501709.00.14440.42450.45040.24300.36130.08758
3011.4220.3877.58386.10.142500.283900.24140.105200.259714.9126.5098.87567.70.20980.86630.68690.25750.66380.17300
4020.2914.34135.101297.00.100300.132800.19800.104300.180922.5416.67152.201575.00.13740.20500.40000.16250.23640.07678
df.head()

train_test_splitを使い、データフレームを訓練データとテストデータに分割します。

y = df["target"]
X = df.loc[:, "mean radius":]
# 訓練データとテストデータに分ける
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)

#スケーリングの作業
#ロバストZスコアを採用
def robust_z(x):
    from sklearn.preprocessing import robust_scale
    from scipy.stats import norm
    coefficient = norm.ppf(0.75)-norm.ppf(0.25)
    robust_z_score = robust_scale(x)*coefficient
    return robust_z_score
X_train_scaled = robust_z(X_train)
X_test_scaled = robust_z(X_test)

ニューラルネットワークは、ランダムフォレストやGBDT(勾配ブースティング決定木アルゴリズム)とは違って、データの正規化が必要です。

(決定木系のアルゴリズムは、「大きいか小さいか」という分類をするだけなので、特徴量間のスケールの違いは問題になりにくいです)

いくつかやり方はありますが、今回はロバストZスコアを使います。

ロバストZスコアは、平均のかわりに中央値(median)を、標準偏差の代わりに正規四分位範囲を用いた正規化です。

何が、頑健(robust)なのかというと、外れ値や異常値に強いという意味です。

平均や標準偏差は外れ値や異常値の影響を大きく受けるため、ロバストZスコアでは使われていません。

ロバストZスコアを算出する関数を作って、分割したデータを入れておきます。

from keras.layers import Dense, Dropout
from keras.models import Sequential 
from sklearn.metrics import log_loss
from keras.callbacks import EarlyStopping

ようやくモデルを構築していきます。kerasをimportしていきます。

事前にtensorflowをpcにインストールする必要があるので、まだの方は以下を実行してください。

pip install tensorflow

pip uninstall tensorflow

conda install tensorflow

まれに上のやり方でインストールし、kerasをインポートするとkernelが強制終了し、自動起動してまうトラブルがおきます。

その場合は、下のようにtensorflowをアンインストールした後、condaからtensorflowをインストールしてください。

#モデルの構築
model = Sequential()
model.add(Dense(256,activation="relu", input_shape=(X_train_scaled.shape[1],)))
model.add(Dropout(0.2))
model.add(Dense(256,activation="relu"))
model.add(Dropout(0.2))
model.add(Dense(1, activation="sigmoid"))
model.compile(loss="binary_crossentropy",optimizer="adam",metrics="accuracy")

ニューラルネットワークのモデル構築には、functionnalAPIを利用するパターンとSequential()を使うパターンがあり、今回は簡単なSequentail()を使います。

まず、Sequential()でインスタンスを取得し、addで層を追加していきます。

Dropout()

ドロップアウトは、対象となったそうについて、一部のユニットをランダムに存在しないものとみなし誤差逆伝播法によるウェイトの更新を行います。

引数は「ユニットのうち何割をドロップさせるか」であり、0~1を入力します。

嬉しいこと:過学習をおさせる効果がある。

Dense()

層の引数については、以下のようになります。

activation:活性化関数を何するか。sigmoidかreluが一般的

units:出力する次元の数。

input_shape:最初の入力層のみで使います。入力するデータの形を指定しております。

compile()

loss:損失関数のこと。
目的変数が、2値分類ならloss="binary_crossentropy"にし、回帰ならloss="mean_squared_error"とします。

optimeizer:最適化関数
Adagrad、Adam、RMSprop()があります

metrics:評価関数
基本はaccuracyを選択します。

先ほど説明した、勾配降下法の最適化アルゴリズムを設定するのが引数「optimizer」です。

#学習の実行
#バリデーションデータもモデルにわたし、学習の進行と共にスコアがどう変わるかをモニタリングする
batch_size = 128
epochs = 50
early_stopping = EarlyStopping(monitor="val_loss",patience=20,restore_best_weights=True)

history = model.fit(X_train_scaled,y_train,batch_size=batch_size, epochs=epochs, verbose = 1, validation_data  =(X_test_scaled,y_test),callbacks=[early_stopping])

va_pred = model.predict(X_test_scaled)
score = log_loss(y_test,va_pred,eps=1e-7)

では、モデルに値を渡していきます。

流れとしては他の機械学習と同様です。インスタンスの生成→fit→predictです。

エポック(epochs = 50):モデルが学習データセットに対して学習した回数です。

学習ごとに最適化アルゴリズムは損失関数の値を最小化するように重みを調整します。

バッチサイズ(batch_size = 128):最適化アルゴリズムが重みを更新する際に、データを幾つ使用するかを表しています。

callbacksはバッチの処理やエポックごとに指定した処理を走らせることができ、今回はearly_stoppingを代入し、過学習の抑止や時間の節約のためにモデルの改善が止まった時点で学習を止めることができます。

monitor に設定した値が、 patienceの値の回数続けてmin_delta以上改善しないと、学習がストップします。

ちなみに、eps=1e-7とは浮動小数点の演算で発生する演算誤差を防ぐ仕組みです。

epoch(エポック)について深掘り

ニューラルネットワークの学習プロセスは、エポックと呼ばれる複数の反復で構成されています。

最初のエポックでは、まず、重み \(w\) とバイアス \(b\)の値に対してランダムな初期化値を割り当てます。

その後のプロセスは次のようになります。

①既知のラベル値を持つデータ観測の特徴量が入力層に送信されます。 一般に、これらの観測は “バッチ” (“ミニバッチ” と呼ばれることもあります) にグループ化されます。

②次に、ニューロンで関数が適用され、活性化した場合は、出力層が予測を生成するまで結果が次の層に渡されます。

③予測は実際の既知の値と比較され、予測値と真の値の間の差異の量 (損失ですね!) が計算されます。

④結果に基づいて、損失を減らすために重みとバイアスの修正された値が計算されます。これらの調整は、ネットワーク層のニューロンに “逆伝播” されます。

⑤次のエポックでは、修正された重みとバイアスの値を使用してバッチ トレーニングの前方パスを繰り返します。

うまくいけば、損失を減らすことで、モデルの精度を向上させることができます。

#targetを出しているので、0.5以上の確率を1。0.5以下の確率を0としてラベリングする。
nnw = np.where(va_pred > 0.5, 1, 0)

nnw[:5]
→array([[0],
       [1],
       [1],
       [1],
       [1]])

ここは他の機械学習と同じ処理です。

1になる確率が格納されたままでは使えないので、0.5を閾値に2値分類します。

他の教師あり学習モデルについては、以下をご覧ください。

【分類タスク】ロジスティック回帰の使い方|python

【機械学習】決定木の仕組みと実装方法について|python

【XGB】交差検証法を使った勾配ブースティング決定木の実装|python

【python】Ridge(リッジ)回帰で多重共線性を解決する話

【ランダムフォレスト】ブートストラップ法を決定木に応用|python

【判別問題】サポートベクトルマシン(SVM)の仕組み|python

【kaggle】ベイズ最適化とXGBでtitanicの予測問題を解く|python

FOLLOW ME !