TensorFlow 2.0 Beta : Beginner Tutorials : ML 基本 :- 燃費効率 : 回帰 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 06/22/2019
* 本ページは、TensorFlow の本家サイトの TF 2.0 Beta – Beginner Tutorials – ML basics の以下のページを翻訳した上で
適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
ML 基本 :- 燃費効率 : 回帰
回帰問題では、価格や確率のような、連続値の出力を予測することを狙いとします。これを分類問題と対比すると、そこではクラスのリストからクラスを選択することを目的とします (例えば、そこでは写真がりんごかオレンジを含む、どのフルーツが写真にあるかを認識します)。
この notebook は古典的な Auto MPG データセットを使用して 1970s 末と 1980s 初期の自動車の燃費効率を予測するモデルを構築します。これを行なうために、その期間からの多くの自動車の記述を持つモデルを提供します。この記述は次のような属性を含みます : 気筒 (= cylinders), 排気量 (= displacement), 馬力 (= horsepower) そして重量 (= weight)。
このサンプルは tf.kreas API を使用します、詳細は このガイド を見てください。
# Use seaborn for pairplot !pip install -q seaborn
from __future__ import absolute_import, division, print_function, unicode_literals import pathlib import matplotlib.pyplot as plt import pandas as pd import seaborn as sns !pip install -q tensorflow==2.0.0-beta1 import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers print(tf.__version__)
2.0.0-beta1
The Auto MPG データセット
データセットは UCI Machine Learning レポジトリ から利用可能です。
データを得る
最初にデータセットをダウンロードします。
dataset_path = keras.utils.get_file("auto-mpg.data", "http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")
dataset_path
Downloading data from http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data 32768/30286 [================================] - 0s 4us/step '/home/kbuilder/.keras/datasets/auto-mpg.data'
pandas を使用してそれをインポートします。
column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',
'Acceleration', 'Model Year', 'Origin']
raw_dataset = pd.read_csv(dataset_path, names=column_names,
na_values = "?", comment='\t',
sep=" ", skipinitialspace=True)
dataset = raw_dataset.copy()
dataset.tail()
| MPG | Cylinders | Displacement | Horsepower | Weight | Acceleration | Model Year | Origin | |
| 393 | 27.0 | 4 | 140.0 | 86.0 | 2790.0 | 15.6 | 82 | 1 |
| 394 | 44.0 | 4 | 97.0 | 52.0 | 2130.0 | 24.6 | 82 | 2 |
| 395 | 32.0 | 4 | 135.0 | 84.0 | 2295.0 | 11.6 | 82 | 1 |
| 396 | 28.0 | 4 | 120.0 | 79.0 | 2625.0 | 18.6 | 82 | 1 |
| 397 | 31.0 | 4 | 119.0 | 82.0 | 2720.0 | 19.4 | 82 | 1 |
データをクリーンにする
データセットは幾つかの未知の値を含みます。
dataset.isna().sum()
MPG 0 Cylinders 0 Displacement 0 Horsepower 6 Weight 0 Acceleration 0 Model Year 0 Origin 0 dtype: int64
この初期チュートリアルを単純に維持するためにこれらの行は破棄します。
dataset = dataset.dropna()
“Origin” カラムは実際には categorical であり、numeric ではありません。そこでそれを one-hot に変換します :
origin = dataset.pop('Origin')
dataset['USA'] = (origin == 1)*1.0 dataset['Europe'] = (origin == 2)*1.0 dataset['Japan'] = (origin == 3)*1.0 dataset.tail()
| MPG | Cylinders | Displacement | Horsepower | Weight | Acceleration | Model Year | USA | Europe | Japan | |
| 393 | 27.0 | 4 | 140.0 | 86.0 | 2790.0 | 15.6 | 82 | 1.0 | 0.0 | 0.0 |
| 394 | 44.0 | 4 | 97.0 | 52.0 | 2130.0 | 24.6 | 82 | 0.0 | 1.0 | 0.0 |
| 395 | 32.0 | 4 | 135.0 | 84.0 | 2295.0 | 11.6 | 82 | 1.0 | 0.0 | 0.0 |
| 396 | 28.0 | 4 | 120.0 | 79.0 | 2625.0 | 18.6 | 82 | 1.0 | 0.0 | 0.0 |
| 397 | 31.0 | 4 | 119.0 | 82.0 | 2720.0 | 19.4 | 82 | 1.0 | 0.0 | 0.0 |
データを訓練とテストに分割する
さてデータセットを訓練セットとテストセットに分割します。
モデルの最後の評価でテストセットを使用します。
train_dataset = dataset.sample(frac=0.8,random_state=0) test_dataset = dataset.drop(train_dataset.index)
データを調査する
訓練セットからカラムの 2,3 のペアの同時分布を簡単に見てみます。
sns.pairplot(train_dataset[["MPG", "Cylinders", "Displacement", "Weight"]], diag_kind="kde")
<seaborn.axisgrid.PairGrid at 0x7fa0c95859e8>

全体的な統計情報も見てみます :
train_stats = train_dataset.describe()
train_stats.pop("MPG")
train_stats = train_stats.transpose()
train_stats
| count | mean | std | min | 25% | 50% | 75% | max | |
| Cylinders | 314.0 | 5.477707 | 1.699788 | 3.0 | 4.00 | 4.0 | 8.00 | 8.0 |
| Displacement | 314.0 | 195.318471 | 104.331589 | 68.0 | 105.50 | 151.0 | 265.75 | 455.0 |
| Horsepower | 314.0 | 104.869427 | 38.096214 | 46.0 | 76.25 | 94.5 | 128.00 | 225.0 |
| Weight | 314.0 | 2990.251592 | 843.898596 | 1649.0 | 2256.50 | 2822.5 | 3608.00 | 5140.0 |
| Acceleration | 314.0 | 15.559236 | 2.789230 | 8.0 | 13.80 | 15.5 | 17.20 | 24.8 |
| Model Year | 314.0 | 75.898089 | 3.675642 | 70.0 | 73.00 | 76.0 | 79.00 | 82.0 |
| USA | 314.0 | 0.624204 | 0.485101 | 0.0 | 0.00 | 1.0 | 1.00 | 1.0 |
| Europe | 314.0 | 0.178344 | 0.383413 | 0.0 | 0.00 | 0.0 | 0.00 | 1.0 |
| Japan | 314.0 | 0.197452 | 0.398712 | 0.0 | 0.00 | 0.0 | 0.00 | 1.0 |
ラベルから特徴を分ける
特徴からターゲット値、あるいは「ラベル」を分離します。このラベルがモデルを訓練して予測する値です。
train_labels = train_dataset.pop('MPG')
test_labels = test_dataset.pop('MPG')
データを正規化する
上の train_stats ブロックを再度見て各特徴の範囲がどのように異なっているかに注意してください。
異なるスケールと範囲を使用する特徴を正規化することは良い実践です。特徴正規化なしでもモデルは収束するかもしれませんが、それは訓練をより困難にし、そしてそれは結果としてのモデルを入力で使用される単位の選択に依拠させます。
Note: これらの統計情報を訓練データセットだけから意図的に生成しますが、こららの統計情報はまたテストデータセットを正規化するためにも使用されます。テストデータセットを (モデルがその上で訓練されたのと) 同じ分布に射影するためにそれを行なう必要があります。
def norm(x): return (x - train_stats['mean']) / train_stats['std'] normed_train_data = norm(train_dataset) normed_test_data = norm(test_dataset)
この正規化されたデータはモデルを訓練するために使用するものです。
Caution: ここで入力を正規化するために使用された統計情報 (平均と標準偏差) は、先に行なった one-hot エンコーディングと一緒に、モデルに供給されるどのような他のデータにも適用される必要があります。それはテストセットそしてプロダクションでモデルが使用されるときのライブデータを含みます。
モデル
モデルを構築する
私達のモデルを構築しましょう。ここで、2 つの密に結合した隠れ層、そして単一の連続値を返す出力層を持つ Sequential モデルを使用します。モデル構築ステップは関数, build_model にラップされます、何故ならば後で 2 番目のモデルを作成するからです。
def build_model():
model = keras.Sequential([
layers.Dense(64, activation='relu', input_shape=[len(train_dataset.keys())]),
layers.Dense(64, activation='relu'),
layers.Dense(1)
])
optimizer = tf.keras.optimizers.RMSprop(0.001)
model.compile(loss='mse',
optimizer=optimizer,
metrics=['mae', 'mse'])
return model
model = build_model()
モデルを調査する
モデルの単純な記述をプリントするために .summary メソッドを使用します。
model.summary()
Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense (Dense) (None, 64) 640 _________________________________________________________________ dense_1 (Dense) (None, 64) 4160 _________________________________________________________________ dense_2 (Dense) (None, 1) 65 ================================================================= Total params: 4,865 Trainable params: 4,865 Non-trainable params: 0 _________________________________________________________________
さてモデルを試してみましょう。訓練データから 10 サンプルのバッチを取ってその上で model.predict を呼び出します。
example_batch = normed_train_data[:10] example_result = model.predict(example_batch) example_result
array([[ 0.01432403],
[ 0.05611863],
[-0.7658546 ],
[-0.03145953],
[-0.14918138],
[-0.17592657],
[-0.13245817],
[-0.17190717],
[-0.11158038],
[ 0.18116453]], dtype=float32)
それは動作しているようです、そしてそれは期待される shape と type の結果を生成します。
モデルを訓練する
モデルを 1000 エポックの間訓練して訓練と検証精度を history オブジェクトに記録します。
# Display training progress by printing a single dot for each completed epoch
class PrintDot(keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs):
if epoch % 100 == 0: print('')
print('.', end='')
EPOCHS = 1000
history = model.fit(
normed_train_data, train_labels,
epochs=EPOCHS, validation_split = 0.2, verbose=0,
callbacks=[PrintDot()])
.................................................................................................... .................................................................................................... .................................................................................................... .................................................................................................... .................................................................................................... .................................................................................................... .................................................................................................... .................................................................................................... .................................................................................................... ....................................................................................................
history オブジェクトにストアされている統計情報を使用してモデルの訓練進捗を可視化します。
hist = pd.DataFrame(history.history) hist['epoch'] = history.epoch hist.tail()
| loss | mae | mse | val_loss | val_mae | val_mse | epoch | |
| 995 | 3.169360 | 1.137359 | 3.169360 | 8.839724 | 2.252794 | 8.839724 | 995 |
| 996 | 3.343072 | 1.140639 | 3.343073 | 8.830334 | 2.231025 | 8.830334 | 996 |
| 997 | 3.105232 | 1.128542 | 3.105232 | 9.016378 | 2.240795 | 9.016376 | 997 |
| 998 | 3.309495 | 1.193391 | 3.309495 | 8.855472 | 2.234031 | 8.855473 | 998 |
| 999 | 3.440103 | 1.183400 | 3.440103 | 8.744971 | 2.213535 | 8.744972 | 999 |
def plot_history(history):
hist = pd.DataFrame(history.history)
hist['epoch'] = history.epoch
plt.figure()
plt.xlabel('Epoch')
plt.ylabel('Mean Abs Error [MPG]')
plt.plot(hist['epoch'], hist['mae'],
label='Train Error')
plt.plot(hist['epoch'], hist['val_mae'],
label = 'Val Error')
plt.ylim([0,5])
plt.legend()
plt.figure()
plt.xlabel('Epoch')
plt.ylabel('Mean Square Error [$MPG^2$]')
plt.plot(hist['epoch'], hist['mse'],
label='Train Error')
plt.plot(hist['epoch'], hist['val_mse'],
label = 'Val Error')
plt.ylim([0,20])
plt.legend()
plt.show()
plot_history(history)


このグラフはおよそ 100 エポック後には検証エラーについて殆ど改良しない、あるいは劣化さえ示します。検証スコアが改善しないとき訓練を自動的に停止するように model.fit 呼び出しを更新しましょう。EarlyStopping コールバックを使用します、これは総てのエポックのために訓練条件をテストします。エポックの設定された総数が改善を示すことなく経過する場合、訓練を自動的に停止します。
このコールバックについて ここで 更に学習できます。
model = build_model()
# The patience parameter is the amount of epochs to check for improvement
early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)
history = model.fit(normed_train_data, train_labels, epochs=EPOCHS,
validation_split = 0.2, verbose=0, callbacks=[early_stop, PrintDot()])
plot_history(history)
................................................


グラフは検証セット上、平均エラーが通常は +/- 2 MPG まわりであることを示しています。これは良いでしょうか?その決定は貴方に委ねましょう。
モデルを訓練するときに使用しなかった、テスト セットを使用してモデルがどの程度上手く一般化されたかを見てみましょう。これは、現実世界でモデルを使用するときどの程度上手く予測することをモデルに期待できるかを私達に教えてくれます。
loss, mae, mse = model.evaluate(normed_test_data, test_labels, verbose=0)
print("Testing set Mean Abs Error: {:5.2f} MPG".format(mae))
Testing set Mean Abs Error: 1.83 MPG
予測を行なう
最後に、テストセットのデータを使用してMPG 値を予測します :
test_predictions = model.predict(normed_test_data).flatten()
plt.scatter(test_labels, test_predictions)
plt.xlabel('True Values [MPG]')
plt.ylabel('Predictions [MPG]')
plt.axis('equal')
plt.axis('square')
plt.xlim([0,plt.xlim()[1]])
plt.ylim([0,plt.ylim()[1]])
_ = plt.plot([-100, 100], [-100, 100])

私達のモデルは適度に上手く予想しているようです。エラー分布を見てみましょう。
error = test_predictions - test_labels
plt.hist(error, bins = 25)
plt.xlabel("Prediction Error [MPG]")
_ = plt.ylabel("Count")

それは完全にはガウシアン (分布) ではありませんが、それを期待できます、何故ならばサンプルの数が非常に小さいからです。
最後に
この notebook は回帰問題を扱う幾つかのテクニックを紹介しました。
- Mean Squared Error (MSE) は回帰問題のために使用される一般的な損失関数です (異なる損失関数が分類問題に使われます)。
- 同様に、回帰に使用される評価メトリクスは分類とは異なります。一般的な回帰メトリクスは Mean Absolute Error (MAE) です。
- 数値入力データ特徴が異なる範囲の値を持つとき、各特徴は独立的に同じ範囲にスケールされるべきです。
- それほど多くの訓練データがない場合、overfitting を回避するために一つのテクニックは少ない隠れ層を持つ小さいネットワークを選択することです。
- Early stopping は overfitting を回避するための有用なテクニックです。
以上