![]() |
![]() |
![]() |
![]() |
セットアップ
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
はじめに
このガイドでは、トレーニングと検証に組み込みAPI ( model.fit()
、model.evaluate()
、model.predict()
など) を使用する場合のトレーニング、評価、予測 (推論) モデルについて説明します。
独自のトレーニングステップ関数を指定するときにfit()
を活用することに関心がある場合は、「fit()
で行われる処理のカスタマイズ」をご覧ください 。
独自のトレーニングと評価ループを新規作成することに関心がある場合は、「トレーニングループの新規作成」をご覧ください。
一般的に、組み込みループを使用している場合でも、独自のループを作成している場合でも、モデルのトレーニングと評価は、Sequential モデル、Functional API を使用して構築されたモデル、モデルのサブクラス化により新規作成されたモデルなど、あらゆるタイプの Keras モデルで同じように機能します。
このガイドは、分散トレーニングについては取り上げていません。 分散トレーニングについては、「マルチ GPU と分散トレーニングのガイド」を参照してください。
API の概要:最初のエンドツーエンドの例
モデルの組み込みトレーニングループにデータを渡すときは、NumPy 配列(データが小さく、メモリに収まる場合)またはtf.data Dataset
オブジェクトを使用する必要があります。 以下の例では、オプティマイザ、損失、およびメトリックの使用方法を示すために、MNIST データセットを NumPy 配列として使用します。
次のモデルを見てみましょう(ここでは、Functional API を使用して構築していますが、Sequential モデルまたはサブクラスモデルを使用する場合もあります)。
inputs = keras.Input(shape=(784,), name="digits")
x = layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = layers.Dense(10, activation="softmax", name="predictions")(x)
model = keras.Model(inputs=inputs, outputs=outputs)
一般的なエンドツーエンドのワークフローは以下のとおりです。
- トレーニング
- 元のトレーニングデータから生成されたホールドアウトセットの検証
- テストデータの評価
この例では MNIST データを使用します。
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
# Preprocess the data (these are NumPy arrays)
x_train = x_train.reshape(60000, 784).astype("float32") / 255
x_test = x_test.reshape(10000, 784).astype("float32") / 255
y_train = y_train.astype("float32")
y_test = y_test.astype("float32")
# Reserve 10,000 samples for validation
x_val = x_train[-10000:]
y_val = y_train[-10000:]
x_train = x_train[:-10000]
y_train = y_train[:-10000]
トレーニングの構成(オプティマイザ、損失、メトリック)を指定します。
model.compile(
optimizer=keras.optimizers.RMSprop(), # Optimizer
# Loss function to minimize
loss=keras.losses.SparseCategoricalCrossentropy(),
# List of metrics to monitor
metrics=[keras.metrics.SparseCategoricalAccuracy()],
)
fit()
を呼び出します。これは、データを「batch_size」サイズの「バッチ」にスライスし、指定された数の「エポック」間にデータセット全体を繰り返し反復することによりモデルをトレーニングします。
print("Fit model on training data")
history = model.fit(
x_train,
y_train,
batch_size=64,
epochs=2,
# We pass some validation for
# monitoring validation loss and metrics
# at the end of each epoch
validation_data=(x_val, y_val),
)
Fit model on training data Epoch 1/2 782/782 [==============================] - 3s 3ms/step - loss: 0.5914 - sparse_categorical_accuracy: 0.8324 - val_loss: 0.1876 - val_sparse_categorical_accuracy: 0.9457 Epoch 2/2 782/782 [==============================] - 2s 3ms/step - loss: 0.1693 - sparse_categorical_accuracy: 0.9498 - val_loss: 0.1398 - val_sparse_categorical_accuracy: 0.9579
返された「履歴」オブジェクトは、トレーニング間の損失値とメトリック値の記録を保持します。
history.history
{'loss': [0.3499179184436798, 0.16121159493923187], 'sparse_categorical_accuracy': [0.9002199769020081, 0.9526000022888184], 'val_loss': [0.1876121163368225, 0.13984256982803345], 'val_sparse_categorical_accuracy': [0.9456999897956848, 0.9578999876976013]}
evaluate()
を介してテストデータのモデルを評価します。
# Evaluate the model on the test data using `evaluate`
print("Evaluate on test data")
results = model.evaluate(x_test, y_test, batch_size=128)
print("test loss, test acc:", results)
# Generate predictions (probabilities -- the output of the last layer)
# on new data using `predict`
print("Generate predictions for 3 samples")
predictions = model.predict(x_test[:3])
print("predictions shape:", predictions.shape)
Evaluate on test data 79/79 [==============================] - 0s 2ms/step - loss: 0.1489 - sparse_categorical_accuracy: 0.9536 test loss, test acc: [0.14889934659004211, 0.9535999894142151] Generate predictions for 3 samples predictions shape: (3, 10)
では、このワークフローの各部分を詳しく見ていきましょう。
compile()
メソッド:損失、メトリック、およびオプティマイザを指定する
fit()
を使用してモデルをトレーニングするには、損失関数、オプティマイザ、および必要に応じて監視するいくつかのメトリックを指定する必要があります。
これらをcompile()
メソッドの引数としてモデルに渡します。
model.compile(
optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
loss=keras.losses.SparseCategoricalCrossentropy(),
metrics=[keras.metrics.SparseCategoricalAccuracy()],
)
metrics
引数はリストである必要があります。モデルは任意の数のメトリックを持つことができます。
モデルに複数の出力がある場合、出力ごとに異なる損失とメトリックを指定でき、モデルの総損失に対する各出力の寄与を変調できます。詳細については、「データを多入力多出力モデルに渡す」セクションを参照してください。
デフォルト設定に満足している場合、多くの場合、オプティマイザ、損失、およびメトリックは、ショートカットとして文字列識別子を介して指定できます。
model.compile(
optimizer="rmsprop",
loss="sparse_categorical_crossentropy",
metrics=["sparse_categorical_accuracy"],
)
後で再利用するために、モデルの定義とコンパイルの手順を関数に入れます。本ガイドでは、これらをさまざまな例で数回呼び出します。
def get_uncompiled_model():
inputs = keras.Input(shape=(784,), name="digits")
x = layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = layers.Dense(10, activation="softmax", name="predictions")(x)
model = keras.Model(inputs=inputs, outputs=outputs)
return model
def get_compiled_model():
model = get_uncompiled_model()
model.compile(
optimizer="rmsprop",
loss="sparse_categorical_crossentropy",
metrics=["sparse_categorical_accuracy"],
)
return model
数多くの組み込みオプティマイザ、損失とメトリクスが利用可能
多くの場合、Keras API には、必要とされるものは含まれているので、一般的に、損失、メトリック、オプティマイザを新規作成する必要はありません。
オプティマイザ:
SGD()
(モメンタム有り、モメンタム無し)RMSprop()
Adam()
- その他
損失:
MeanSquaredError()
KLDivergence()
CosineSimilarity()
- その他
メトリック:
AUC()
Precision()
Recall()
- その他
カスタム損失
Keras でカスタム損失を提供する方法は 2 つあります。 最初の例では、入力y_true
およびy_pred
を受け入れる関数を作成します。2 つめの例は、実データと予測の間の平均二乗誤差を計算する損失関数を示しています。
def custom_mean_squared_error(y_true, y_pred):
return tf.math.reduce_mean(tf.square(y_true - y_pred))
model = get_uncompiled_model()
model.compile(optimizer=keras.optimizers.Adam(), loss=custom_mean_squared_error)
# We need to one-hot encode the labels to use MSE
y_train_one_hot = tf.one_hot(y_train, depth=10)
model.fit(x_train, y_train_one_hot, batch_size=64, epochs=1)
782/782 [==============================] - 2s 2ms/step - loss: 0.0276 <tensorflow.python.keras.callbacks.History at 0x7f73187363c8>
y_true
およびy_pred
以外のパラメータを受け取る損失関数が必要な場合は、tf.keras.losses.Loss
クラスをサブクラス化して、次の 2 つのメソッドを実装できます。
__init__(self)
: 損失関数の呼び出し中に渡すパラメータを受け入れるcall(self, y_true, y_pred)
: ターゲット(y_true)とモデル予測(y_pred)を使用してモデルの損失を計算する
平均二乗誤差を使用する際に、0.5 より離れた予測値にペナルティを与えるとします(カテゴリターゲットはワンホットエンコードされ、0 と 1 の間の値を取ると想定します)。これにより、モデルの信頼性が調整され、過剰適合を防ぐのに役立ちます(実際に試してみるまで、その効果はわかりませんが)。
以下のように記述します。
class CustomMSE(keras.losses.Loss):
def __init__(self, regularization_factor=0.1, name="custom_mse"):
super().__init__(name=name)
self.regularization_factor = regularization_factor
def call(self, y_true, y_pred):
mse = tf.math.reduce_mean(tf.square(y_true - y_pred))
reg = tf.math.reduce_mean(tf.square(0.5 - y_pred))
return mse + reg * self.regularization_factor
model = get_uncompiled_model()
model.compile(optimizer=keras.optimizers.Adam(), loss=CustomMSE())
y_train_one_hot = tf.one_hot(y_train, depth=10)
model.fit(x_train, y_train_one_hot, batch_size=64, epochs=1)
782/782 [==============================] - 2s 2ms/step - loss: 0.0498 <tensorflow.python.keras.callbacks.History at 0x7f73185dd400>
カスタムメトリック
API に含まれていないメトリックが必要な場合は、tf.keras.metrics.Metric
クラスをサブクラス化することにより、カスタムメトリックを簡単に作成できます。
__init__(self)
- メトリックの状態変数を作成します。update_state(self, y_true, y_pred, sample_weight=None)
- ターゲット (y_true) とモデル予測 (y_pred) を使用し状態変数を更新します。result(self)
- 状態変数を使用して最終結果を計算します。reset_states(self)
- メトリックの状態を再初期化します。
多くの場合、結果の計算に非常に負荷がかかり、定期的にしか実行されないため、状態の更新(update_state()
)と結果の計算(result()
)は別々に保持されます。
以下は、CategoricalTruePositives
メトリックを実装する方法を示す簡単な例です。これは、特定のクラスに属するものとして正しく分類されたサンプル数を数えます。
class CategoricalTruePositives(keras.metrics.Metric):
def __init__(self, name="categorical_true_positives", **kwargs):
super(CategoricalTruePositives, self).__init__(name=name, **kwargs)
self.true_positives = self.add_weight(name="ctp", initializer="zeros")
def update_state(self, y_true, y_pred, sample_weight=None):
y_pred = tf.reshape(tf.argmax(y_pred, axis=1), shape=(-1, 1))
values = tf.cast(y_true, "int32") == tf.cast(y_pred, "int32")
values = tf.cast(values, "float32")
if sample_weight is not None:
sample_weight = tf.cast(sample_weight, "float32")
values = tf.multiply(values, sample_weight)
self.true_positives.assign_add(tf.reduce_sum(values))
def result(self):
return self.true_positives
def reset_states(self):
# The state of the metric will be reset at the start of each epoch.
self.true_positives.assign(0.0)
model = get_uncompiled_model()
model.compile(
optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
loss=keras.losses.SparseCategoricalCrossentropy(),
metrics=[CategoricalTruePositives()],
)
model.fit(x_train, y_train, batch_size=64, epochs=3)
Epoch 1/3 782/782 [==============================] - 2s 2ms/step - loss: 0.5641 - categorical_true_positives: 22021.2146 Epoch 2/3 782/782 [==============================] - 2s 2ms/step - loss: 0.1653 - categorical_true_positives: 23888.6207 Epoch 3/3 782/782 [==============================] - 2s 2ms/step - loss: 0.1155 - categorical_true_positives: 24183.9796 <tensorflow.python.keras.callbacks.History at 0x7f731847f2b0>
標準のシグネチャに適合しない損失と測定基準の処理
ほとんどの損失とメトリックは、y_true
およびy_pred
から計算できます。この場合、y_pred
はモデルの出力です。例外として、正則化損失ではレイヤーのアクティブ化のみが必要で(この場合はターゲットはありません)、このアクティブ化はモデルの出力ではない場合があります。
このような場合は、カスタムレイヤーの呼び出しメソッド内からself.add_loss(loss_value)
を呼び出すことができます。この方法で追加された損失は、トレーニング時に「メイン」の損失(compile()
に渡されたもの)に追加されます。以下は、アクティビティの正規化を追加する簡単な例です(アクティビティの正規化はすべての Keras レイヤーに組み込まれています。このレイヤーは具体的な例を示すためのものです)。
class ActivityRegularizationLayer(layers.Layer):
def call(self, inputs):
self.add_loss(tf.reduce_sum(inputs) * 0.1)
return inputs # Pass-through layer.
inputs = keras.Input(shape=(784,), name="digits")
x = layers.Dense(64, activation="relu", name="dense_1")(inputs)
# Insert activity regularization as a layer
x = ActivityRegularizationLayer()(x)
x = layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = layers.Dense(10, name="predictions")(x)
model = keras.Model(inputs=inputs, outputs=outputs)
model.compile(
optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
)
# The displayed loss will be much higher than before
# due to the regularization component.
model.fit(x_train, y_train, batch_size=64, epochs=1)
782/782 [==============================] - 2s 2ms/step - loss: 3.5150 <tensorflow.python.keras.callbacks.History at 0x7f73183c7400>
add_metric()
を使用して、メトリック値のロギングに対して同じく実行できます。
class MetricLoggingLayer(layers.Layer):
def call(self, inputs):
# The `aggregation` argument defines
# how to aggregate the per-batch values
# over each epoch:
# in this case we simply average them.
self.add_metric(
keras.backend.std(inputs), name="std_of_activation", aggregation="mean"
)
return inputs # Pass-through layer.
inputs = keras.Input(shape=(784,), name="digits")
x = layers.Dense(64, activation="relu", name="dense_1")(inputs)
# Insert std logging as a layer.
x = MetricLoggingLayer()(x)
x = layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = layers.Dense(10, name="predictions")(x)
model = keras.Model(inputs=inputs, outputs=outputs)
model.compile(
optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),
loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
)
model.fit(x_train, y_train, batch_size=64, epochs=1)
782/782 [==============================] - 2s 2ms/step - loss: 0.5880 - std_of_activation: 0.9576 <tensorflow.python.keras.callbacks.History at 0x7f7318328320>
Functional API では、model.add_loss(loss_tensor)
またはmodel.add_metric(metric_tensor, name, aggregation)
を呼び出せます。
簡単な例を下記に示します。
inputs = keras.Input(shape=(784,), name="digits")
x1 = layers.Dense(64, activation="relu", name="dense_1")(inputs)
x2 = layers.Dense(64, activation="relu", name="dense_2")(x1)
outputs = layers.Dense(10, name="predictions")(x2)
model = keras.Model(inputs=inputs, outputs=outputs)
model.add_loss(tf.reduce_sum(x1) * 0.1)
model.add_metric(keras.backend.std(x1), name="std_of_activation", aggregation="mean")
model.compile(
optimizer=keras.optimizers.RMSprop(1e-3),
loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
)
model.fit(x_train, y_train, batch_size=64, epochs=1)
782/782 [==============================] - 2s 2ms/step - loss: 3.4985 - std_of_activation: 0.0019 <tensorflow.python.keras.callbacks.History at 0x7f73181ac400>
add_loss()
を介して損失を渡すと、モデルにはすでに最小化する損失があるため、損失関数なしでcompile()
を呼び出すことが可能になります。
次のLogisticEndpoint
レイヤーでは、入力としてターゲットとロジットを取り、add_loss()
を介してクロスエントロピー損失を追跡します。また、add_metric()
を介して分類の精度を追跡します。
class LogisticEndpoint(keras.layers.Layer):
def __init__(self, name=None):
super(LogisticEndpoint, self).__init__(name=name)
self.loss_fn = keras.losses.BinaryCrossentropy(from_logits=True)
self.accuracy_fn = keras.metrics.BinaryAccuracy()
def call(self, targets, logits, sample_weights=None):
# Compute the training-time loss value and add it
# to the layer using `self.add_loss()`.
loss = self.loss_fn(targets, logits, sample_weights)
self.add_loss(loss)
# Log accuracy as a metric and add it
# to the layer using `self.add_metric()`.
acc = self.accuracy_fn(targets, logits, sample_weights)
self.add_metric(acc, name="accuracy")
# Return the inference-time prediction tensor (for `.predict()`).
return tf.nn.softmax(logits)
次のように、loss
引数なしでコンパイルされた 2 つの入力(入力データとターゲット)を持つモデルで使用できます。
import numpy as np
inputs = keras.Input(shape=(3,), name="inputs")
targets = keras.Input(shape=(10,), name="targets")
logits = keras.layers.Dense(10)(inputs)
predictions = LogisticEndpoint(name="predictions")(logits, targets)
model = keras.Model(inputs=[inputs, targets], outputs=predictions)
model.compile(optimizer="adam") # No loss argument!
data = {
"inputs": np.random.random((3, 3)),
"targets": np.random.random((3, 10)),
}
model.fit(data)
1/1 [==============================] - 0s 256ms/step - loss: 0.9517 - binary_accuracy: 0.0000e+00 <tensorflow.python.keras.callbacks.History at 0x7f73107d00b8>
多入力モデルのトレーニングの詳細については、「多入力多出力モデルへのデータの受け渡し」をご覧ください。
自動的にホールドアウトセットを別にする
最初に紹介したエンドツーエンドの例では、各エポックの終わりに検証損失と検証メトリックを評価するために、NumPy 配列のタプル(x_val, y_val)
をモデルに渡すためにvalidation_data
引数を使用しました。
もう一つのオプションとして、validation_split
引数は、検証のためにトレーニングデータの一部を自動的に取っておくことを可能にします。 引数値は検証のために取っておかれるデータの割合を表し、 0 より高く 1 より低い数字に設定します。たとえば、validation_split=0.2
は「検証のためにデータの 20% を使用する」ことを意味し、validation_split=0.6
は「検証のためにデータの 60% を使用する」ことを意味します。
任意のシャッフルの前に、fitの呼び出しにより受け取った配列の最後の x% サンプルを取り、検証が計算されます。
NumPy データでトレーニングする場合は、validation_split
のみを使用できることに注意してください。
model = get_compiled_model()
model.fit(x_train, y_train, batch_size=64, validation_split=0.2, epochs=1)
625/625 [==============================] - 2s 3ms/step - loss: 0.6083 - sparse_categorical_accuracy: 0.8308 - val_loss: 0.2223 - val_sparse_categorical_accuracy: 0.9347 <tensorflow.python.keras.callbacks.History at 0x7f7310726160>
tf.data データセットからのトレーニングと評価
前の段落では、損失、メトリクスおよびオプティマイザをどのように扱うかを見ました。そしてデータが Numpy 配列として渡されるとき、fit でvalidation_data
とvalidation_split
引数をどのように使用するかを見ました。
次に、データがtf.data.Dataset
オブジェクトの形式で渡される場合を見てみましょう。
tf.data
API は高速でスケーラブルな方法でデータを読み込んで前処理するためのTensorFlow 2.0 の一連のユティリティです。
Datasets
の作成についての詳細は、tf.data ドキュメントをご覧ください。
Dataset
のインスタンスは、fit()
、evaluate()
、predict()
メソッドに直接的に渡すことができます。
model = get_compiled_model()
# First, let's create a training Dataset instance.
# For the sake of our example, we'll use the same MNIST data as before.
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
# Shuffle and slice the dataset.
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
# Now we get a test dataset.
test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test))
test_dataset = test_dataset.batch(64)
# Since the dataset already takes care of batching,
# we don't pass a `batch_size` argument.
model.fit(train_dataset, epochs=3)
# You can also evaluate or predict on a dataset.
print("Evaluate")
result = model.evaluate(test_dataset)
dict(zip(model.metrics_names, result))
Epoch 1/3 782/782 [==============================] - 2s 2ms/step - loss: 0.5453 - sparse_categorical_accuracy: 0.8548 Epoch 2/3 782/782 [==============================] - 2s 2ms/step - loss: 0.1663 - sparse_categorical_accuracy: 0.9517 Epoch 3/3 782/782 [==============================] - 2s 2ms/step - loss: 0.1194 - sparse_categorical_accuracy: 0.9642 Evaluate 157/157 [==============================] - 0s 2ms/step - loss: 0.1496 - sparse_categorical_accuracy: 0.9588 {'loss': 0.1496019959449768, 'sparse_categorical_accuracy': 0.9588000178337097}
データセットは各エポックの最後にリセットされるため、次のエポックで再利用できます。
このデータセットから特定の数のバッチでのみトレーニングを実行する場合は、steps_per_epoch
引数を渡します。これは、次のエポックに進む前に、このデータセットを使用してモデルが実行するトレーニングステップの数を指定します。
この場合、データセットは各エポックの終わりにリセットされず、次のバッチを取得し続けます。最終的にデータセットのデータは使い果たされます(無限ループのデータセットでない限り)。
model = get_compiled_model()
# Prepare the training dataset
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
# Only use the 100 batches per epoch (that's 64 * 100 samples)
model.fit(train_dataset, epochs=3, steps_per_epoch=100)
Epoch 1/3 100/100 [==============================] - 1s 3ms/step - loss: 1.2545 - sparse_categorical_accuracy: 0.6592 Epoch 2/3 100/100 [==============================] - 0s 2ms/step - loss: 0.3950 - sparse_categorical_accuracy: 0.8874 Epoch 3/3 100/100 [==============================] - 0s 2ms/step - loss: 0.3605 - sparse_categorical_accuracy: 0.8925 <tensorflow.python.keras.callbacks.History at 0x7f73181b6b00>
検証データセットの使用
Dataset
インスタンスをfit()
のvalidation_data
引数として渡すことができます。
model = get_compiled_model()
# Prepare the training dataset
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
# Prepare the validation dataset
val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))
val_dataset = val_dataset.batch(64)
model.fit(train_dataset, epochs=1, validation_data=val_dataset)
782/782 [==============================] - 3s 3ms/step - loss: 0.5333 - sparse_categorical_accuracy: 0.8541 - val_loss: 0.1879 - val_sparse_categorical_accuracy: 0.9448 <tensorflow.python.keras.callbacks.History at 0x7f73102fa898>
各エポックの終わりに、モデルは検証データセットを反復処理し、検証損失と検証メトリックを計算します。
このデータセットから特定の数のバッチでのみ検証を実行する場合は、validation_steps
引数を渡すことができます。これは、検証を中断して次のエポックに進む前に、モデルが検証データセットで実行する必要がある検証ステップの数を指定します。
model = get_compiled_model()
# Prepare the training dataset
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
# Prepare the validation dataset
val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))
val_dataset = val_dataset.batch(64)
model.fit(
train_dataset,
epochs=1,
# Only run validation using the first 10 batches of the dataset
# using the `validation_steps` argument
validation_data=val_dataset,
validation_steps=10,
)
782/782 [==============================] - 2s 2ms/step - loss: 0.5556 - sparse_categorical_accuracy: 0.8489 - val_loss: 0.3248 - val_sparse_categorical_accuracy: 0.9172 <tensorflow.python.keras.callbacks.History at 0x7f731051fe80>
検証データセットは使用するたびにリセットされることに注意してください(エポックごとに常に同じサンプルが評価されます)。
validation_split
引数(トレーニングデータからホールドアウトセットを生成)は、Dataset
オブジェクトからトレーニングする場合はサポートされません。この機能には、データセットのサンプルにインデックスを付ける機能が必要ですが、一般的にDataset
API では不可能です。
サポートされるほかの入力フォーマット
NumPy 配列、eager tensors、TensorFlow Datasets
の他、Pandas データフレームやデータとラベルのバッチを生成する Python ジェネレータを使用して Keras モデルをトレーニングできます。
特に、keras.utils.Sequence
クラスは、Pythonデータジェネレータを構築するためのシンプルなインターフェースを提供します。 これらはマルチプロセッシングに対応し、シャッフルすることができます。
一般的に、以下を使用することをお勧めします。
- NumPy 入力データ - データが小さく、メモリに収まる場合
Dataset
オブジェクト - 大規模なデータセットがあり、分散トレーニングを行う必要がある場合Sequence
オブジェクト - 大規模なデータセットがあり、TensorFlow では実行できない多くのカスタム Python 側の処理を行う必要がある場合(たとえば、データの読み込みや前処理が外部ライブラリに依存している場合)。
keras.utils.Sequence
オブジェクトを入力として使用
keras.utils.Sequence
は、以下の 2 つの重要なプロパティを持つ Python ジェネレータを取得するためにサブクラス化できるユーティリティです。
- マルチプロセッシングで正しく機能する。
- シャッフル可能(
shuffle=True
をfit()
で渡す場合など)。
Sequence
は以下の 2 つのメソッドを実装する必要があります。
__getitem__
__len__
__getitem__
メソッドは完全なバッチを返す必要があります。エポック間でデータセットを変更する場合は、on_epoch_end
を実装できます。
簡単な例を次に示します。
from skimage.io import imread
from skimage.transform import resize
import numpy as np
# Here, `filenames` is list of path to the images
# and `labels` are the associated labels.
class CIFAR10Sequence(Sequence):
def __init__(self, filenames, labels, batch_size):
self.filenames, self.labels = filenames, labels
self.batch_size = batch_size
def __len__(self):
return int(np.ceil(len(self.filenames) / float(self.batch_size)))
def __getitem__(self, idx):
batch_x = self.filenames[idx * self.batch_size:(idx + 1) * self.batch_size]
batch_y = self.labels[idx * self.batch_size:(idx + 1) * self.batch_size]
return np.array([
resize(imread(filename), (200, 200))
for filename in batch_x]), np.array(batch_y)
sequence = CIFAR10Sequence(filenames, labels, batch_size)
model.fit(sequence, epochs=10)
サンプルの重み付けとクラスの重み付けの使用
デフォルト設定では、サンプルの重みはデータセット内の頻度によって決定されます。サンプルの頻度に関係なく、データに重みを付ける方法は2つあります。
- クラスの重み
- サンプルの重み
クラスの重み
クラスに対する重みは、ディクショナリをclass_weight
引数に渡し、Model.fit()
に渡すことで設定されます。このディクショナリは、クラスインデックスを、このクラスに属するサンプルに使用する重みにマップします。
これは、リサンプリングせずにクラスのバランスを取るために使用できます。または、特定のクラスをより重要視するモデルをトレーニングするために使用できます。
たとえば、クラス「0」がデータでクラス「1」として表されるものの半分である場合、Model.fit(..., class_weight={0: 1., 1: 0.5})
を使用できます。
以下は NumPy の例です。クラスの重みまたはサンプルの重みを使用して、クラス#5(MNIST データセットの数字「5」)の正しい分類をより重要視しています。
import numpy as np
class_weight = {
0: 1.0,
1: 1.0,
2: 1.0,
3: 1.0,
4: 1.0,
# Set weight "2" for class "5",
# making this class 2x more important
5: 2.0,
6: 1.0,
7: 1.0,
8: 1.0,
9: 1.0,
}
print("Fit with class weight")
model = get_compiled_model()
model.fit(x_train, y_train, class_weight=class_weight, batch_size=64, epochs=1)
Fit with class weight 782/782 [==============================] - 2s 2ms/step - loss: 0.6365 - sparse_categorical_accuracy: 0.8333 <tensorflow.python.keras.callbacks.History at 0x7f73186ee4e0>
サンプルの重み
きめ細かい制御を必要とする場合、または分類子を構築しない場合は、「サンプルの重み」を使用できます。
- NumPy データからトレーニングする場合:
sample_weight
引数をModel.fit()
に渡します。 tf.data
またはその他のイテレータからトレーニングする場合:(input_batch, label_batch, sample_weight_batch)
タプルを介します。
「サンプルの重み」配列は、バッチ内の各サンプルが総損失を計算する際に必要な重みを指定する数値の配列です。 これは、不均衡な分類の問題(頻繁に使用されないクラスに、より大きな重みを与えるため)でよく使用されます。
使用される重みが 1 と 0 の場合、配列は損失関数のマスクとして使用できます(損失全体に対する特定のサンプルの寄与を完全に破棄します)。
sample_weight = np.ones(shape=(len(y_train),))
sample_weight[y_train == 5] = 2.0
print("Fit with sample weight")
model = get_compiled_model()
model.fit(x_train, y_train, sample_weight=sample_weight, batch_size=64, epochs=1)
Fit with sample weight 782/782 [==============================] - 2s 2ms/step - loss: 0.6445 - sparse_categorical_accuracy: 0.8296 <tensorflow.python.keras.callbacks.History at 0x7f7318328978>
以下は、一致するDataset
の例です。
sample_weight = np.ones(shape=(len(y_train),))
sample_weight[y_train == 5] = 2.0
# Create a Dataset that includes sample weights
# (3rd element in the return tuple).
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train, sample_weight))
# Shuffle and slice the dataset.
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
model = get_compiled_model()
model.fit(train_dataset, epochs=1)
782/782 [==============================] - 2s 3ms/step - loss: 0.6058 - sparse_categorical_accuracy: 0.8424 <tensorflow.python.keras.callbacks.History at 0x7f73185d9630>
マルチ入力、マルチ出力モデルにデータを渡す
前の例では、1つの入力(テンソルの形状(764,)
)と1つの出力(形状の予測テンソル(10,)
)を持つモデルを見ました。では、複数の入力や出力を持つモデルではどうでしょう。
次のモデルを考えてみましょう。形状 (32, 32, 3)
の画像入力((高さ、幅、チャネル)
)、形状 (None, 10)
の時系列入力((timesteps, features)
)があります。モデルは、これらの入力の組み合わせから2つの出力(「スコア」(形状(1,)
)および5つのクラスにわたる確率分布(形状(5,)
)を計算します。
image_input = keras.Input(shape=(32, 32, 3), name="img_input")
timeseries_input = keras.Input(shape=(None, 10), name="ts_input")
x1 = layers.Conv2D(3, 3)(image_input)
x1 = layers.GlobalMaxPooling2D()(x1)
x2 = layers.Conv1D(3, 3)(timeseries_input)
x2 = layers.GlobalMaxPooling1D()(x2)
x = layers.concatenate([x1, x2])
score_output = layers.Dense(1, name="score_output")(x)
class_output = layers.Dense(5, activation="softmax", name="class_output")(x)
model = keras.Model(
inputs=[image_input, timeseries_input], outputs=[score_output, class_output]
)
ここで何が行われているか明確に分かるようにこのモデルをプロットしてみましょう(プロットに表示される形状は、サンプルごとの形状ではなく、バッチの形状であることに注意してください)。
keras.utils.plot_model(model, "multi_input_and_output_model.png", show_shapes=True)
コンパイル時に損失関数をリストとして渡すことにより出力ごとに異なる損失を指定できます。
model.compile(
optimizer=keras.optimizers.RMSprop(1e-3),
loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()],
)
モデルに単一の損失関数のみを渡した場合、同じ損失関数がすべての出力に適用されます(ここでは適切ではありません)。
同様にメトリックの場合:
model.compile(
optimizer=keras.optimizers.RMSprop(1e-3),
loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()],
metrics=[
[
keras.metrics.MeanAbsolutePercentageError(),
keras.metrics.MeanAbsoluteError(),
],
[keras.metrics.CategoricalAccuracy()],
],
)
出力レイヤーに名前を付けたので、dict を介して出力ごとの損失とメトリックを指定することもできます。
model.compile(
optimizer=keras.optimizers.RMSprop(1e-3),
loss={
"score_output": keras.losses.MeanSquaredError(),
"class_output": keras.losses.CategoricalCrossentropy(),
},
metrics={
"score_output": [
keras.metrics.MeanAbsolutePercentageError(),
keras.metrics.MeanAbsoluteError(),
],
"class_output": [keras.metrics.CategoricalAccuracy()],
},
)
3 つ以上の出力がある場合は、明示的な名前とディクショナリを使用することをお勧めします。
loss_weights
引数を使用すると、異なる出力固有の損失に異なる重みを与えることができます(この例でクラス損失の 2 倍の重要性を与えることにより、「スコア」損失を優先する場合など)。
model.compile(
optimizer=keras.optimizers.RMSprop(1e-3),
loss={
"score_output": keras.losses.MeanSquaredError(),
"class_output": keras.losses.CategoricalCrossentropy(),
},
metrics={
"score_output": [
keras.metrics.MeanAbsolutePercentageError(),
keras.metrics.MeanAbsoluteError(),
],
"class_output": [keras.metrics.CategoricalAccuracy()],
},
loss_weights={"score_output": 2.0, "class_output": 1.0},
)
これらの出力が予測用であり、トレーニング用ではない場合、特定の出力の損失を計算しないことを選択することもできます。
# List loss version
model.compile(
optimizer=keras.optimizers.RMSprop(1e-3),
loss=[None, keras.losses.CategoricalCrossentropy()],
)
# Or dict loss version
model.compile(
optimizer=keras.optimizers.RMSprop(1e-3),
loss={"class_output": keras.losses.CategoricalCrossentropy()},
)
fit でマルチ入力またはマルチ出力モデルにデータを渡すとコンパイルで損失関数を指定するのと同じように機能します。NumPy 配列のリスト(損失関数を受け取った出力に 1:1 でマッピング)または出力の名前を NumPy 配列にマッピングする dict を渡すことができます。
model.compile(
optimizer=keras.optimizers.RMSprop(1e-3),
loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()],
)
# Generate dummy NumPy data
img_data = np.random.random_sample(size=(100, 32, 32, 3))
ts_data = np.random.random_sample(size=(100, 20, 10))
score_targets = np.random.random_sample(size=(100, 1))
class_targets = np.random.random_sample(size=(100, 5))
# Fit on lists
model.fit([img_data, ts_data], [score_targets, class_targets], batch_size=32, epochs=1)
# Alternatively, fit on dicts
model.fit(
{"img_input": img_data, "ts_input": ts_data},
{"score_output": score_targets, "class_output": class_targets},
batch_size=32,
epochs=1,
)
4/4 [==============================] - 2s 11ms/step - loss: 4.8111 - score_output_loss: 0.1749 - class_output_loss: 4.6361 4/4 [==============================] - 0s 5ms/step - loss: 4.6573 - score_output_loss: 0.1311 - class_output_loss: 4.5262 <tensorflow.python.keras.callbacks.History at 0x7f7318253320>
以下はDataset
のユースケースです。NumPy 配列と同様に、Dataset
はdicts のタプルを返します。
train_dataset = tf.data.Dataset.from_tensor_slices(
(
{"img_input": img_data, "ts_input": ts_data},
{"score_output": score_targets, "class_output": class_targets},
)
)
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
model.fit(train_dataset, epochs=1)
2/2 [==============================] - 0s 29ms/step - loss: 4.6574 - score_output_loss: 0.1439 - class_output_loss: 4.5136 <tensorflow.python.keras.callbacks.History at 0x7f73180544e0>
コールバックの使用
Keras のコールバックは、トレーニング中の異なる時点(エポックの始め、バッチの終わり、エポックの終わりなど)で呼び出されるオブジェクトで、以下のような動作を実装するために使用できます。
- トレーニング中にさまざまな時点で検証を行う(組み込みのエポックごとの検証だけでなく)
- 定期的に、または特定の精度しきい値を超えたときにモデルをチェックポイントする
- 学習が停滞したときにモデルの学習率を変更する
- 学習が停滞したときにトップレイヤーを微調整する
- トレーニング終了時、または特定のパフォーマンスしきい値を超えたときにメールまたはインスタントメッセージ通知を送信する
- その他
コールバックは、リストとしてfit()
の呼び出しに渡すことができます。
model = get_compiled_model()
callbacks = [
keras.callbacks.EarlyStopping(
# Stop training when `val_loss` is no longer improving
monitor="val_loss",
# "no longer improving" being defined as "no better than 1e-2 less"
min_delta=1e-2,
# "no longer improving" being further defined as "for at least 2 epochs"
patience=2,
verbose=1,
)
]
model.fit(
x_train,
y_train,
epochs=20,
batch_size=64,
callbacks=callbacks,
validation_split=0.2,
)
Epoch 1/20 625/625 [==============================] - 2s 3ms/step - loss: 0.6279 - sparse_categorical_accuracy: 0.8239 - val_loss: 0.2314 - val_sparse_categorical_accuracy: 0.9300 Epoch 2/20 625/625 [==============================] - 2s 2ms/step - loss: 0.1852 - sparse_categorical_accuracy: 0.9474 - val_loss: 0.1916 - val_sparse_categorical_accuracy: 0.9442 Epoch 3/20 625/625 [==============================] - 1s 2ms/step - loss: 0.1267 - sparse_categorical_accuracy: 0.9615 - val_loss: 0.1465 - val_sparse_categorical_accuracy: 0.9547 Epoch 4/20 625/625 [==============================] - 1s 2ms/step - loss: 0.0983 - sparse_categorical_accuracy: 0.9710 - val_loss: 0.1415 - val_sparse_categorical_accuracy: 0.9566 Epoch 5/20 625/625 [==============================] - 2s 2ms/step - loss: 0.0785 - sparse_categorical_accuracy: 0.9750 - val_loss: 0.1422 - val_sparse_categorical_accuracy: 0.9610 Epoch 00005: early stopping <tensorflow.python.keras.callbacks.History at 0x7f733a22b240>
数多くの組み込みコールバックが利用可能
ModelCheckpoint
: 定期的にモデルを保存するEarlyStopping
: トレーニングによって検証指標が改善されなくなったら、トレーニングを停止するTensorBoard
: TensorBoard で視覚化できるモデルログを定期的に記述する(詳細については、「視覚化」セクションを参照)。CSVLogger
: 損失およびメトリックデータを CSV ファイルにストリーミング- その他
完全なリストについてはコールバックのドキュメントを参照してください。
コールバックを記述する
基底クラスkeras.callbacks.Callback
を拡張することにより、カスタムコールバックを作成できます。コールバックは、クラスプロパティself.model
を通じて関連するモデルにアクセスできます。
詳細につきましては、「カスタムコールバックの作成に関する完全なガイド」を参照してください。
以下は、トレーニング時にバッチごとの損失値のリストを保存する簡単な例です。
class LossHistory(keras.callbacks.Callback):
def on_train_begin(self, logs):
self.per_batch_losses = []
def on_batch_end(self, batch, logs):
self.per_batch_losses.append(logs.get("loss"))
モデルのチェックポイント
比較的大きなデータセットでモデルをトレーニングする場合、モデルのチェックポイントを頻繁に保存することが重要です。
ModelCheckpoint
コールバックを使用するのが最も簡単な方法です。
model = get_compiled_model()
callbacks = [
keras.callbacks.ModelCheckpoint(
# Path where to save the model
# The two parameters below mean that we will overwrite
# the current checkpoint if and only if
# the `val_loss` score has improved.
# The saved model name will include the current epoch.
filepath="mymodel_{epoch}",
save_best_only=True, # Only save a model if `val_loss` has improved.
monitor="val_loss",
verbose=1,
)
]
model.fit(
x_train, y_train, epochs=2, batch_size=64, callbacks=callbacks, validation_split=0.2
)
Epoch 1/2 625/625 [==============================] - 2s 3ms/step - loss: 0.6639 - sparse_categorical_accuracy: 0.8130 - val_loss: 0.2323 - val_sparse_categorical_accuracy: 0.9290 Epoch 00001: val_loss improved from inf to 0.23232, saving model to mymodel_1 INFO:tensorflow:Assets written to: mymodel_1/assets Epoch 2/2 625/625 [==============================] - 1s 2ms/step - loss: 0.1887 - sparse_categorical_accuracy: 0.9439 - val_loss: 0.1795 - val_sparse_categorical_accuracy: 0.9448 Epoch 00002: val_loss improved from 0.23232 to 0.17946, saving model to mymodel_2 INFO:tensorflow:Assets written to: mymodel_2/assets <tensorflow.python.keras.callbacks.History at 0x7f73105c5ef0>
ModelCheckpoint
コールバックを使用するとフォールトトレランスを実装できます。フォールトトレランスはトレーニングがランダムに中断された場合に、モデルの最後に保存された状態からトレーニングを再開する機能です。 以下は基本的な例です。
import os
# Prepare a directory to store all the checkpoints.
checkpoint_dir = "./ckpt"
if not os.path.exists(checkpoint_dir):
os.makedirs(checkpoint_dir)
def make_or_restore_model():
# Either restore the latest model, or create a fresh one
# if there is no checkpoint available.
checkpoints = [checkpoint_dir + "/" + name for name in os.listdir(checkpoint_dir)]
if checkpoints:
latest_checkpoint = max(checkpoints, key=os.path.getctime)
print("Restoring from", latest_checkpoint)
return keras.models.load_model(latest_checkpoint)
print("Creating a new model")
return get_compiled_model()
model = make_or_restore_model()
callbacks = [
# This callback saves a SavedModel every 100 batches.
# We include the training loss in the saved model name.
keras.callbacks.ModelCheckpoint(
filepath=checkpoint_dir + "/ckpt-loss={loss:.2f}", save_freq=100
)
]
model.fit(x_train, y_train, epochs=1, callbacks=callbacks)
Creating a new model 98/1563 [>.............................] - ETA: 3s - loss: 1.3702 - sparse_categorical_accuracy: 0.6315INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.90/assets 194/1563 [==>...........................] - ETA: 5s - loss: 1.0702 - sparse_categorical_accuracy: 0.7155INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.65/assets 300/1563 [====>.........................] - ETA: 5s - loss: 0.9046 - sparse_categorical_accuracy: 0.7588INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.56/assets 377/1563 [======>.......................] - ETA: 6s - loss: 0.8287 - sparse_categorical_accuracy: 0.7782INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.50/assets 500/1563 [========>.....................] - ETA: 5s - loss: 0.7439 - sparse_categorical_accuracy: 0.7998INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.46/assets 600/1563 [==========>...................] - ETA: 5s - loss: 0.6936 - sparse_categorical_accuracy: 0.8127INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.43/assets 697/1563 [============>.................] - ETA: 4s - loss: 0.6547 - sparse_categorical_accuracy: 0.8227INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.40/assets 799/1563 [==============>...............] - ETA: 4s - loss: 0.6211 - sparse_categorical_accuracy: 0.8314INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.38/assets 899/1563 [================>.............] - ETA: 3s - loss: 0.5934 - sparse_categorical_accuracy: 0.8385INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.36/assets 998/1563 [==================>...........] - ETA: 3s - loss: 0.5699 - sparse_categorical_accuracy: 0.8445INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.35/assets 1100/1563 [====================>.........] - ETA: 2s - loss: 0.5489 - sparse_categorical_accuracy: 0.8498INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.34/assets 1178/1563 [=====================>........] - ETA: 2s - loss: 0.5347 - sparse_categorical_accuracy: 0.8535INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.33/assets 1297/1563 [=======================>......] - ETA: 1s - loss: 0.5153 - sparse_categorical_accuracy: 0.8585INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.32/assets 1377/1563 [=========================>....] - ETA: 1s - loss: 0.5035 - sparse_categorical_accuracy: 0.8615INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.31/assets 1476/1563 [===========================>..] - ETA: 0s - loss: 0.4902 - sparse_categorical_accuracy: 0.8650INFO:tensorflow:Assets written to: ./ckpt/ckpt-loss=0.30/assets 1563/1563 [==============================] - 10s 6ms/step - loss: 0.4794 - sparse_categorical_accuracy: 0.8677 <tensorflow.python.keras.callbacks.History at 0x7f731809cc50>
また、モデルを保存および復元するための独自のコールバックを記述することもできます。
シリアル化と保存の完全なガイドについては、「モデルの保存とシリアル化のガイド」をご覧ください。
学習率スケジューリングの使用
ディープラーニングモデルをトレーニングするときの一般的なパターンは、トレーニングが進むにつれて徐々に学習を減らすことです。 これは一般に「学習率の減衰」として知られています。
学習減衰スケジュールは、静的(その時点のエポックまたはその時点のバッチインデックスの関数として事前に指定)または動的(モデルの現在の動作、特に検証損失に対応)にすることができます。
オプティマイザにスケジュールを渡す
オプティマイザのlearning_rate
引数としてスケジュールオブジェクトを渡すことで、静的学習率の減衰スケジュールを簡単に使用できます。
initial_learning_rate = 0.1
lr_schedule = keras.optimizers.schedules.ExponentialDecay(
initial_learning_rate, decay_steps=100000, decay_rate=0.96, staircase=True
)
optimizer = keras.optimizers.RMSprop(learning_rate=lr_schedule)
ExponentialDecay
、PiecewiseConstantDecay
、PolynomialDecay
、InverseTimeDecay
などの組み込みスケジュールを利用できます。
コールバックを使用して動的学習率のスケジュールを実装
オプティマイザは検証メトリックにアクセスできないため、これらのスケジュールオブジェクトでは動的学習率のスケジュールを実現できません。(検証の損失が改善されなくなったときに学習率を下げるなど)
ただし、コールバックは、検証メトリックを含むすべてのメトリックにアクセスできます。このパターンでは、コールバックを使用してオプティマイザのその時点の学習率を変更します。これはReduceLROnPlateau
コールバックとして組み込まれています。
トレーニング時の損失と測定基準の視覚化
トレーニング時にモデルを監視する最善の方法は、TensorBoard を使用することです。これは、ローカルで実行できるブラウザベースのアプリケーションで、以下の機能を提供します。
- トレーニングと評価のための損失とメトリクスのライブプロット
- レイヤーアクティベーションのヒストグラムの視覚化(オプション)
Embedding
レイヤーが学習した埋め込みスペースの 3D 視覚化(オプション)
TensorFlow を pip でインストールした場合は、コマンドラインから TensorBoard を起動できます。
tensorboard --logdir=/full_path_to_your_logs
TensorBoardコールバックの使用
TensorBoard
コールバックは、Keras モデルと fit メソッドで TensorBoard を使用する最も簡単な方法です。
最も単純なケースでは、コールバックがログを書き込む場所を指定するだけです。
keras.callbacks.TensorBoard(
log_dir="/full_path_to_your_logs",
histogram_freq=0, # How often to log histogram visualizations
embeddings_freq=0, # How often to log embedding visualizations
update_freq="epoch",
) # How often to write logs (default: once per epoch)
<tensorflow.python.keras.callbacks.TensorBoard at 0x7f73105a65f8>
詳細については、TensorBoard
コールバックのドキュメントを参照してください。