ニューラルネットワークの構造理解
上記の記事のコードを用いて、ニューラルネットワークではどのような操作が行われているのかを学習する。
理解が及んでいない部分もあるため、間違っている点のご指摘や、より有効なコードのアドバイス等ありましたら、ご教授願います。
必要ライブラリの読み込み
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import datetime, os, random
import seaborn as sns
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.utils import Sequence
%load_ext tensorboard
from google.colab import auth
from oauth2client.client import GoogleCredentials
import gspread
学習用データの準備
学習データ用のファイルdataesetを用意する。
datesetのデータをランダムな順番に並べ替え、そのうち8割を訓練用のデータセットに、残りをテスト用のデータセットにする。
frac=0.8 は8割抜きだし
random_state=0 はデータをランダムにする。=0でなくても、数値はなんでも良い。
train_dataset = dataset.sample(frac=0.8,random_state=0)
test_dataset = dataset.drop(train_dataset.index)
ラベル
train_datesetの中のbmiの列を削除し、bmiという名のラベルをつける。
train_datesetの中のlife_expectancyの列を削除、life_expectancyという名のラベルをつける。
そして、このラベルをtrain_labelsに入れる。
テストデータセットの方も同様。
POPは削除
つまり、ここでは、 train_dateの中から各行を削除し、その削除した行をtrain_labelに入れている。
train_labels = pd.DataFrame.from_dict({
'bmi': train_dataset.pop('bmi'),
'life_expectancy': train_dataset.pop('life_expectancy')
})
train_dataset.pop('age')
test_labels = pd.DataFrame.from_dict({
'bmi': test_dataset.pop('bmi'),
'life_expectancy': test_dataset.pop('life_expectancy')
})
テストデータの中からageの行を削除
test_dataset.pop('age')
学習用データの正規化
describeでデータフレームの型がpandas.DataFrameであることを確認
→データフレームがpandas.DataFrameでないと、後述のtransposeなどが使用できない
train_stats = train_dataset.describe()
transposeは行と列を入れ替える
train_stats = train_stats.transpose()
norm(x)はxを正規化する関数
train_datasetを正規化したものをnormed_train_dataにいれ、test_datasetを正規化したものをnormed_test_dataに入れる。
def norm(x):
return (x - train_stats['mean']) / train_stats['std']
normed_train_data = norm(train_dataset)
normed_test_data = norm(test_dataset)
normed_train_data.sample(5)
❓return (x - train_stats['mean']) / train_stats['std']
はtrain_statsでいいの?
モデル
モデルの定義
Denseで新しいレイヤーを追加。Denseの引数(64, activation='relu')は、64は中間層のノードの数。activationは活性化関数名。
Dropoutは一定割合のノードを不活性化させることで過学習の防止を図るもの。
y = layers.Dense(256, activation='sigmoid')(x)
入力xに対する中間層をyと定義。
def build_model():
inputs = layers.Input(shape=(len(train_dataset.keys()),))
x = layers.Dense(64, activation='relu')(inputs)
y = layers.Dense(256, activation='sigmoid')(x)
y = layers.Dropout(0.2)(y)
y = layers.Dense(64, activation='relu')(y)
y = layers.Flatten()(y)
y = layers.Dense(10, activation='relu')(y)
bmi = layers.Dense(1, name='bmi')(y)
z = layers.Dense(256, activation='tanh')(x)
z = layers.Dropout(0.4)(z)
z = layers.Dense(64, activation='relu')(z)
life_expectancy = layers.Dense(1, name='life_expectancy')(z)
model = keras.Model(inputs=inputs, outputs=[bmi, life_expectancy], name='Health')
model.compile(
optimizer=tf.keras.optimizers.RMSprop(0.001),
loss_weights=[1., 0.8],
loss=['mse', 'huber'],
metrics=['mae', 'mse'])
return model
上記のプログラムでは、yの中間層を持つニューラルネットワーク(入力に対してbmiを予想する)と、zの中間層を持つニューラルネットワーク(入力に対してlife_expectancyを予想する)の二つを生成している。それぞれのニューラルネットワークは、それぞれ異なる入力をもとに生成されている。
model = keras.Model(inputs=inputs, outputs=[bmi, life_expectancy], name='Health')
入力をもとにbmiとlife_expectancyを出力するモデルを作成。このモデル名をHealthとしている。
model = build_model()
model.summary()
built_model関数で定義したモデルをmodelとする
訓練
★用語★
エポック = 1つの訓練データを何回繰り返して学習させるか
モデルを1000エポック訓練し、TensorBoardに記録。
エポックの終わりに呼び出され、エポックが終わるごとにドットを一つ出力することで学習の進捗を表示
→学習中に下部に出力される学習進行状況のバーのこと
class PrintDot(keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs):
if epoch % 100 == 0: print('')
print('.', end='')
logdir = os.path.join("logs", datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))
tensorboard_callback = tf.keras.callbacks.TensorBoard(logdir, histogram_freq=1)
EPOCHS = 1000
fit()で訓練の実行を行う。
引数でバッチサイズやエポック数などを指定。
下記のコードでは、normed_train_dataを入力として、 [train_labels.bmi, train_labels.life_expectancy]を出力としている。
model.fit(
normed_train_data,
[train_labels.bmi, train_labels.life_expectancy],
epochs=EPOCHS,
validation_split=0.1,
verbose=0,
callbacks=[tensorboard_callback, PrintDot()])
引数verboseは表示される学習状況の出力のモード。デフォルトはverbose=1で、エポックごとのログがプログレスバーの進行状況とともに示される。verbose=2はプログレスバーなし(エポックごとのログはあり)、verbose=0は出力が一切なし。
引数callbacksは、訓練中(エポック終了時など)に呼び出したい関数をリストで指定。
Tensorboardで可視化
%tensorboard --logdir logs
テストデータで予測
テストデータを使用した予測を行い、結果を出力
test_predictions = model.predict(normed_test_data)
result = normed_test_data.copy()
result['height'] = dataset['height']
result['weight'] = dataset['weight']
result['sex'] = dataset['sex'].map(lambda x: 'female' if x == 0 else 'male')
result['age'] = dataset['age']
result['bmi'] = dataset['bmi']
result['life_expectancy'] = dataset['life_expectancy']
result['predict_BMI'] = test_predictions[0]
result['diff_BMI'] = round(result['predict_BMI']) - result['bmi']
result['predict_LE'] = test_predictions[1]
result['diff_LE'] = round(result['predict_LE']) - result['life_expectancy']
result.reindex(columns=['height', 'weight', 'bmi', 'predict_BMI', 'diff_BMI', 'sex', 'age', 'life_expectancy', 'predict_LE', 'diff_LE']).sort_values('diff_LE')
ここでは、入力データと予想データを並べて表示するコードを書いている。
result['bmi'] = dataset['bmi']では入力データをそのまま表示。
result['predict_BMI'] = test_predictions[0]では、予想したBMIの値を表示。
result['diff_BMI'] = round(result['predict_BMI']) - result['bmi']では、実際のBMI(入力データ)と予測したbmiのデータの差異を表示している。