【Python】TensorFlowでモデルを作る方法

Python
スポンサーリンク

TensorFlowでモデルを作る方法を解説します。

データセット作成

インポート

from sklearn.datasets import load_boston
import numpy as np
import pandas as pd

data = load_boston()
df = pd.DataFrame(data["data"], columns = data["feature_names"])
df["target"] = data["target"]
df

sickit-learnからload_bostonをインポートしました。

辞書型でデータや特徴名が入っているので、取り出してpandasのデータフレームにしましょう。

“CRIM”~”LSTAT”までの特徴から”target”を予測するモデルを作ります。

データ分割

from sklearn.model_selection import KFold
df["fold"] = -1
kf = KFold(n_splits = 5, shuffle = True, random_state = 0)
for fold, (_, valid_idx) in enumerate(kf.split(df)):
    df.loc[valid_idx, "fold"] = fold
print(df["fold"].value_counts())

モデルに学習させるデータと性能を検証するためのデータに分けました。

KFoldはn_splitsの数だけ分割してくれます。

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
for col in [c for c in df.columns if not c in ["CHAS", "target", "fold"]]:
    df[col] = scaler.fit_transform(df[col].values.reshape(-1, 1))
df

StandardScalerで分散を1、平均を0にしておきましょう。

例えば”CRIM”で値が1増えるのと”ZN”で1増えるのとでは、変化の度合いがそれぞれ違いますよね。

なのでスケーリングして、同じ1だけ増えたときの影響度をそろえておきます。

fold = 0

train = df.loc[df["fold"] != fold].reset_index(drop = True)
valid = df.loc[df["fold"] == fold].reset_index(drop = True)
print(train.shape, valid.shape)

各データに0~5までの番号がふられているので、今回は0番を検証用データにします。

5分割しているので、サイズの比率は学習データ:検証データ = 4:1です。

feature_cols = [c for c in df.columns if c not in ["target", "fold"]]
print(feature_cols)

X_train = train[feature_cols].values
X_valid = valid[feature_cols].values
y_train = train["target"].values
y_valid = valid["target"].values

特徴のデータ名を取り出し、Xを特徴、yを目的の値としました。

“target”は予測したい値で”fold”は分割番号をふっただけなので、特徴には入れません。

モデル作成

定義

import tensorflow as tf
import tensorflow.keras.layers as L
import tensorflow.keras.models as M

def get_model(num_features):
    inputs = L.Input(shape = num_features)
    x = L.Dense(units = 16, activation = "relu")(inputs)
    x = L.Dense(units = 32, activation = "relu")(x)
    outputs = L.Dense(units = 1, activation = "linear")(x)

    model = M.Model(inputs = inputs, outputs = outputs)
    model.compile(
        optimizer = tf.keras.optimizers.Adam(learning_rate = 0.01),
        loss = "mse",
    )

    return model
tensorflow:大元のライブラリ
layers:モデルに入れる層を定義できる
models:モデルを定義するときに必要。learning_rateは学習率。

モデルを作成するには、まず層を定義して入力から出力までの流れを組みます。

最初はInput層でデータを受け取ります。

このときサイズ指定が必要なので、特徴の列数を受け取れるようにしましょう。

層の種類は色々ありますが、今回は単純なDense(全結合)だけにしました。

units:ニューロンの数。多いほど複雑な特徴を表現できる。
activation:活性化関数。とりあえずreluでいい。

最終層は出力するだけなのでactivationはlinearで大丈夫です。

最後にModelでコンパイルします。

optimizer:最適化手法。とりあえずAdamでいい。
loss:損失関数。mseは平均二乗誤差。

モデル呼び出し

model = get_model(X_train.shape[1])
model.summary()

関数を呼び出してモデルを作りました。

引数には特徴量の列数を入れています。

summaryで中身を確認できます。

13は今回の特徴の数(列数)で13です。

そこから16⇒32⇒1の順で計算され、実際の値”target”と誤差計算されます。

学習と検証

学習

fitで学習をします。

history = model.fit(
    X_train, y_train,
    validation_data = (X_valid, y_valid),
    epochs = 10,
    batch_size = 16,
)
X_train, y_train:学習データ
validation_data:検証データ
epochs:学習ループ回数
batch_size:データを小分けするサイズ

とりあえず上記だけ渡しておけば学習してくれます。

引数の数はめちゃくちゃ多いので、公式ドキュメントを読んだらいいです。

学習結果はこんな感じ。

lossが学習データ、val_lossが検証データでのmseです。

徐々に下がっているので、学習が進んでいるとわかりますね。

import matplotlib.pyplot as plt
plt.plot(history.history["loss"], label = "train")
plt.plot(history.history["val_loss"], label = "valid")
plt.legend()
plt.show()

hisrotyには学習結果が入っているので、可視化しました。

lossが徐々に下がっているとわかります。

ちなみに、モデルの学習率を0.001に小さくすると以下のようになります。

学習率が小さいのでlossが下がるまでに時間がかかり、下がりきっていません。

ただ時間がかかる分、細かく学習が進んでくれます。

この場合はepochを20とかに増やすといいです。

予測結果

pred = model.predict(X_valid)
print(y_valid.shape, pred.shape)

plt.scatter(y_valid, pred)
plt.plot(range(50), range(50), "red")
plt.xlabel("truth")
plt.ylabel("pred")
plt.show()

予測結果はpredictで取り出せます。

検証データでの予測結果と実際の値をプロットしました。

まぁまぁ直線上にのっていますね。

from sklearn.metrics import mean_squared_error
rmse = np.sqrt(mean_squared_error(y_valid, pred))
print(rmse)

最後にrmse(mseの平方根)を計算しました。

だいたい5くらいかと思います。

層を増やしたりunitsを変えたりして、精度が変わるか見てみるといいですよ。

改善例

いくつか改善方法を解説します。

乱数固定

まず乱数でスコアが変わるので、乱数を固定しておきます。

seed = 0
np.random.seed(seed)
tf.random.set_seed(seed)

再度実行するとRMSEが4.62になりました。

何度実行しても同じ値になればOKです。

これを基準に改善できたか判断しましょう。

unitsを変える

def get_model(num_features):
    inputs = L.Input(shape = num_features)
    x = L.Dense(units = 64, activation = "relu")(inputs)
    x = L.Dense(units = 128, activation = "relu")(x)
    outputs = L.Dense(units = 1, activation = "linear")(x)

    model = M.Model(inputs = inputs, outputs = outputs)
    model.compile(
        optimizer = tf.keras.optimizers.Adam(learning_rate = 0.01),
        loss = "mse",
    )

    return model

例えばunitsの数を増やします。上記では64と128にしました。

unitsが増えるほどモデルの表現力が上がるので、単純に精度改善が見込めます。

悪化しましたね。データが単調なら、少ないunitsで十分なのかもしれません。

層を増やす

def get_model(num_features):
    inputs = L.Input(shape = num_features)
    x = L.Dense(units = 16, activation = "relu")(inputs)
    x = L.Dense(units = 32, activation = "relu")(x)
    x = L.Dense(units = 32, activation = "relu")(x)
    x = L.Dense(units = 32, activation = "relu")(x)
    outputs = L.Dense(units = 1, activation = "linear")(x)

    model = M.Model(inputs = inputs, outputs = outputs)
    model.compile(
        optimizer = tf.keras.optimizers.Adam(learning_rate = 0.01),
        loss = "mse",
    )

    return model

Dense層を増やしました。これも表現力が上がります。

ちょっと良くなりましたね。

グリッドサーチとかoptunaで最適な層数をサーチするといいかもしれません。

活性化関数を変える

def get_model(num_features):
    inputs = L.Input(shape = num_features)
    x = L.Dense(units = 16, activation = "selu")(inputs)
    x = L.Dense(units = 32, activation = "selu")(x)
    x = L.Dense(units = 32, activation = "selu")(x)
    x = L.Dense(units = 32, activation = "selu")(x)
    outputs = L.Dense(units = 1, activation = "linear")(x)

    model = M.Model(inputs = inputs, outputs = outputs)
    model.compile(
        optimizer = tf.keras.optimizers.Adam(learning_rate = 0.01),
        loss = "mse",
    )

    return model

活性化関数は色々あります。SeLUにしてみました。

ちょっと良くなっていますね。

learning_rateを変える

epochs = 20

scheduler = tf.keras.optimizers.schedules.CosineDecay(
    initial_learning_rate = 0.01, decay_steps = epochs,
)

history = model.fit(
    X_train, y_train,
    validation_data = (X_valid, y_valid),
    epochs = epochs,
    batch_size = 16,
    callbacks = [tf.keras.callbacks.LearningRateScheduler(scheduler)],
)

learning_rateをループ回数によって変えるようにしました。

学習が進むにつれてlearning_rateを小さくすることで、lossが極小値に近づくようにできます。

小刻みに学習を進めるのでepochsを20にしました。

またちょっと良くなっていますね。

こんな感じで色々条件を変えてスコアを見てみると面白いですよ。

まとめ

今回はTensorFlowでモデルを作る方法を解説しました。

TensorFlowなら高速で学習を回せるTPUを使用できるので、普段Pytorchを使う方でも勉強しておくと役に立ちますよ。

コメント

タイトルとURLをコピーしました