Keras

View on TensorFlow.org Run in Google Colab View source on GitHub

Kerasは、深層学習モデルを構築・学習するための高水準APIです。
迅速なプロトタイピングから先端研究、実運用にも使用されており、3つの特徴があります:

  • ユーザーフレンドリー
    一般的なユースケースに最適化したKerasのAPIは、シンプルで統一性があります。誤った使い方をした場合のエラー出力も明快で、どう対応すべきか一目瞭然です。
  • モジュール性
    Kerasのモデルは、設定可能なモジュールをつなぎ合わせて作られます。モジュールのつなぎ方には、ほとんど制約がありません。
  • 拡張性
    簡単にモジュールをカスタマイズできるため、研究の新しいアイデアを試すのに最適です。新しい層、損失関数を自作し、最高水準のモデルを開発しましょう。

tf.keras のインポート

tf.keras は、TensorFlow版 Keras API 仕様 です。 モデルを構築・学習するための高水準APIであり、TensorFlow特有の機能である Eagerモードtf.data パイプライン、 Estimators にも正式に対応しています。 tf.keras は、TensorFlowの柔軟性やパフォーマンスを損ねることなく使いやすさを向上しています。

TensorFlowプログラムの準備として、先ずは tf.keras をインポートしましょう:

!pip install -q pyyaml  # YAML形式でモデルを保存する際に必要です。
from __future__ import absolute_import, division, print_function, unicode_literals

import tensorflow as tf
from tensorflow.keras import layers

print(tf.VERSION)
print(tf.keras.__version__)

tf.keras ではKerasと互換性のあるコードを実行できますが、注意点もあります:

  • 最新リリースのTensorFlowに同梱されている tf.keras のバージョンと、pipインストールした最新の keras のバージョンが同一とは限りません。バージョンは tf.keras.__version__ の出力をご確認ください。
  • モデルの重みを保存する場合、 tf.keras のデフォルトの保存形式は チェックポイント形式です。 HDF5形式にて保存する場合は、 save_format='h5' オプションを指定してください。

単純なモデルの構築

シーケンシャル モデル

Kerasでは、を組み合わせてモデルを構築します。 モデルは通常、複数の層から成るグラフ構造をしています。 最も一般的なモデルは、単純に層を積み重ねる類の tf.keras.Sequential モデルです。

単純な全結合ネットワーク(いわゆる マルチ レイヤー パーセプトロン)を構築してみましょう:

model = tf.keras.Sequential()
# ユニット数が64の全結合層をモデルに追加します:
model.add(layers.Dense(64, activation='relu'))
# 全結合層をもう一つ追加します:
model.add(layers.Dense(64, activation='relu'))
# 出力ユニット数が10のソフトマックス層を追加します:
model.add(layers.Dense(10, activation='softmax'))

層の設定

tf.keras.layers はさまざまな層を提供していますが、共通のコンストラクタ引数があります:

  • activation: 層の活性化関数を設定します。組み込み関数、もしくは呼び出し可能オブジェクトの名前で指定します。デフォルト値は、活性化関数なし。
  • kernel_initializerbias_initializer: 層の重み(カーネルとバイアス)の初期化方式。名前、もしくは呼び出し可能オブジェクトで指定します。デフォルト値は、 "Glorot uniform"
  • kernel_regularizerbias_regularizer:層の重み(カーネルとバイアス)に適用する、L1やL2等の正則化方式。デフォルト値は、正則化なし。

コンストラクタ引数を使って tf.keras.layers.Dense 層をインスタンス化する例を以下に示します:

# シグモイド層を1層作る場合:
layers.Dense(64, activation='sigmoid')
# 別の記法:
layers.Dense(64, activation=tf.sigmoid)

# カーネル行列に係数0,01のL1正則化を課した全結合層:
layers.Dense(64, kernel_regularizer=tf.keras.regularizers.l1(0.01))

# バイアスベクトルに係数0,01のL2正則化を課した全結合層:
layers.Dense(64, bias_regularizer=tf.keras.regularizers.l2(0.01))

# カーネルをランダム直交行列で初期化した全結合層:
layers.Dense(64, kernel_initializer='orthogonal')

# バイアスベクトルを2.0で初期化した全結合層:
layers.Dense(64, bias_initializer=tf.keras.initializers.constant(2.0))

学習と評価

学習の準備

モデルを構築したあとは、compile メソッドを呼んで学習方法を構成します:

model = tf.keras.Sequential([
# ユニット数64の全結合層をモデルに追加する:
layers.Dense(64, activation='relu', input_shape=(32,)),
# もう1層追加する:
layers.Dense(64, activation='relu'),
# 出力ユニット数10のソフトマックス層を追加する:
layers.Dense(10, activation='softmax')])

model.compile(optimizer=tf.train.AdamOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

tf.keras.Model.compile には3つの重要な引数があります:

  • optimizer: このオブジェクトが訓練方式を規定します。 tf.train モジュールから tf.train.AdamOptimizertf.train.RMSPropOptimizertf.train.GradientDescentOptimizer等のオプティマイザ インスタンスを指定します。
  • loss: 最適化の過程で最小化する関数を指定します。平均二乗誤差(mse)やcategorical_crossentropybinary_crossentropy等が好んで使われます。損失関数は名前、もしくは tf.keras.losses モジュールから呼び出し可能オブジェクトとして指定できます。
  • metrics: 学習の監視に使用します。 名前、もしくはtf.keras.metrics モジュールから呼び出し可能オブジェクトとして指定できます。

学習用モデルの構成例を2つ、以下に示します:

# 平均二乗誤差 回帰モデルを構成する。
model.compile(optimizer=tf.train.AdamOptimizer(0.01),
              loss='mse',       # 平均二乗誤差
              metrics=['mae'])  # 平均絶対誤差

# 多クラス分類モデルを構成する。
model.compile(optimizer=tf.train.RMSPropOptimizer(0.01),
              loss=tf.keras.losses.categorical_crossentropy,
              metrics=[tf.keras.metrics.categorical_accuracy])

NumPy データの入力

小規模なデータセットであれば、モデルを学習・評価する際にインメモリの NumPy配列を使いましょう。 モデルは fit メソッドを使って学習データに適合させます。

import numpy as np

def random_one_hot_labels(shape):
  n, n_class = shape
  classes = np.random.randint(0, n_class, n)
  labels = np.zeros((n, n_class))
  labels[np.arange(n), classes] = 1
  return labels

data = np.random.random((1000, 32))
labels = random_one_hot_labels((1000, 10))

model.fit(data, labels, epochs=10, batch_size=32)

tf.keras.Model.fit は3つの重要な引数があります:

  • epochs: エポック は学習の構成単位で、(バッチに分割した)全入力データを一巡したものを1エポックと換算します。
  • batch_size: NumPyデータを渡されたモデルは、データをバッチに分割し、それを順繰りに舐めて学習を行います。一つのバッチに配分するサンプル数を、バッチサイズとして整数で指定します。全サンプル数がバッチサイズで割り切れない場合、最後のバッチだけ小さくなる可能性があることに注意しましょう。
  • validation_data: モデルの試作中に評価データを使って簡単にパフォーマンスを監視したい場合は、この引数に入力とラベルの対を渡すことで、各エポックの最後に推論モードで評価データの損失と評価指標を表示することができます。

validation_data の使用例:

import numpy as np

data = np.random.random((1000, 32))
labels = random_one_hot_labels((1000, 10))

val_data = np.random.random((100, 32))
val_labels = random_one_hot_labels((100, 10))

model.fit(data, labels, epochs=10, batch_size=32,
          validation_data=(val_data, val_labels))

tf.data データセットの入力

大規模なデータセット、もしくは複数デバイスを用いた学習を行う際は Datasets API を使いましょう。 fitメソッドにtf.data.Dataset インスタンスを渡します:

# データセットのインスタンス化の例:
dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32)
dataset = dataset.repeat()

# `fit` にデータセットを渡す際は、`steps_per_epoch` の指定をお忘れなく:
model.fit(dataset, epochs=10, steps_per_epoch=30)

fit メソッドの引数 steps_per_epoch には、1エポックあたりの学習ステップ数を指定します。 Dataset がバッチを生成するため batch_sizeの指定は不要です。

Dataset は評価データにも使えます:

dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32).repeat()

val_dataset = tf.data.Dataset.from_tensor_slices((val_data, val_labels))
val_dataset = val_dataset.batch(32).repeat()

model.fit(dataset, epochs=10, steps_per_epoch=30,
          validation_data=val_dataset,
          validation_steps=3)

評価と推論

tf.keras.Model.evaluatetf.keras.Model.predict メソッドは、NumPyデータとtf.data.Datasetに使えます。

推論モードでデータの損失と評価指標を評価する例を示します:

data = np.random.random((1000, 32))
labels = random_one_hot_labels((1000, 10))

model.evaluate(data, labels, batch_size=32)

model.evaluate(dataset, steps=30)

推論 結果を最終層のNumPy配列として出力する例を示します:

result = model.predict(data, batch_size=32)
print(result.shape)

高度なモデルの構築

Functional API

tf.keras.Sequential モデルは層を積み重ねる単純なつくりであり、あらゆるモデルに対応しているわけではありません。 以下に挙げる複雑な構成のモデルを構築するには Keras functional API を使いましょう:

  • 入力ヘッドが複数あるモデル
  • 出力ヘッドが複数あるモデル
  • 共有層(おなじ層が複数回呼び出される)を含むモデル
  • (残差結合のように)データの流れが分岐するモデル

Functional API を用いたモデル構築の流れ:

  1. 層のインスタンスは呼び出し可能で、テンソルを返します。
  2. 入力テンソルと出力テンソルを使ってtf.keras.Modelインスタンスを定義します。
  3. モデルはSequentialモデルと同様の方法で学習します。

Functional API を使って全結合ネットワークを構築する例を示します:

inputs = tf.keras.Input(shape=(32,))  # プレイスホルダのテンソルを返します。

# 層のインスタンスは呼び出し可能で、テンソルを返します。
x = layers.Dense(64, activation='relu')(inputs)
x = layers.Dense(64, activation='relu')(x)
predictions = layers.Dense(10, activation='softmax')(x)

inputsとoutputsを引数にモデルをインスタンス化します。

model = tf.keras.Model(inputs=inputs, outputs=predictions)

# コンパイル時に学習方法を指定します。
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# 5エポック学習します。
model.fit(data, labels, batch_size=32, epochs=5)

モデルの派生

tf.keras.Model を継承し順伝播を定義することでカスタムモデルを構築できます。 __init__ メソッドにクラス インスタンスの属性として層をつくります。 call メソッドに順伝播を定義します。

順伝播を命令型で記載できるため、モデルの派生は Eagerモード でより威力を発揮します。

キーポイント:目的にあったAPIを選択しましょう。派生モデルは柔軟性を与えてくれますが、その代償にモデルはより複雑になりエラーを起こしやすくなります。目的がFunctional APIで賄えるのであれば、そちらを使いましょう。

tf.keras.Modelを継承して順伝播をカスタマイズした例を以下に示します:

class MyModel(tf.keras.Model):

  def __init__(self, num_classes=10):
    super(MyModel, self).__init__(name='my_model')
    self.num_classes = num_classes
    # 層をここに定義します。
    self.dense_1 = layers.Dense(32, activation='relu')
    self.dense_2 = layers.Dense(num_classes, activation='sigmoid')

  def call(self, inputs):
    # (`__init__`)にてあらかじめ定義した層を使って
    # 順伝播をここに定義します。
    x = self.dense_1(inputs)
    return self.dense_2(x)

  def compute_output_shape(self, input_shape):
    # 派生モデルを使用する場合、
    # このメソッドをオーバーライドすることになります。
    # 派生モデルを使用しない場合、このメソッドは省略可能です。
    shape = tf.TensorShape(input_shape).as_list()
    shape[-1] = self.num_classes
    return tf.TensorShape(shape)

今定義した派生モデルをインスンス化します。

model = MyModel(num_classes=10)

# コンパイル時に学習方法を指定します。
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# 5エポック学習します。
model.fit(data, labels, batch_size=32, epochs=5)

層のカスタマイズ

tf.keras.layers.Layerを継承して層をカスタマイズするには、以下のメソッドを実装します:

  • build: 層の重みを定義します。add_weightメソッドで重みを追加します。
  • call: 順伝播を定義します。
  • compute_output_shape: 入力の形状をもとに出力の形状を算出する方法を指定します。
  • 必須ではありませんが、get_configメソッド と from_config クラスメソッドを実装することで層をシリアライズすることができます。

入力のカーネル行列を matmul (行列乗算)するカスタム層の実装例:

class MyLayer(layers.Layer):

  def __init__(self, output_dim, **kwargs):
    self.output_dim = output_dim
    super(MyLayer, self).__init__(**kwargs)

  def build(self, input_shape):
    shape = tf.TensorShape((input_shape[1], self.output_dim))
    # 学習可能な重みを指定します。
    self.kernel = self.add_weight(name='kernel',
                                  shape=shape,
                                  initializer='uniform',
                                  trainable=True)
    # 最後に`build` メソッドを呼ぶのをお忘れなく。
    super(MyLayer, self).build(input_shape)

  def call(self, inputs):
    return tf.matmul(inputs, self.kernel)

  def compute_output_shape(self, input_shape):
    shape = tf.TensorShape(input_shape).as_list()
    shape[-1] = self.output_dim
    return tf.TensorShape(shape)

  def get_config(self):
    base_config = super(MyLayer, self).get_config()
    base_config['output_dim'] = self.output_dim
    return base_config

  @classmethod
  def from_config(cls, config):
    return cls(**config)

カスタマイズした層を使ってモデルを構築します:

model = tf.keras.Sequential([
    MyLayer(10),
    layers.Activation('softmax')])

# コンパイル時に学習方法を指定します。
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# 5エポック学習します。
model.fit(data, labels, batch_size=32, epochs=5)

コールバック

コールバックは、学習中のモデルの挙動をカスタマイズするためにモデルに渡されるオブジェクトです。 コールバック関数は自作する、もしくは以下に示すtf.keras.callbacksが提供する組み込み関数を利用できます:

  • tf.keras.callbacks.ModelCheckpoint:モデルのチェックポイントを一定間隔で保存します。
  • tf.keras.callbacks.LearningRateScheduler:学習率を動的に変更します。
  • tf.keras.callbacks.EarlyStopping:評価パフォーマンスが向上しなくなったら学習を中断させます。
  • tf.keras.callbacks.TensorBoard: モデルの挙動を TensorBoardで監視します。

tf.keras.callbacks.Callbackを使用するには、モデルの fit メソッドにコールバック関数を渡します:

callbacks = [
  # `val_loss` が2エポック経っても改善しなければ学習を中断させます。
  tf.keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'),
  # TensorBoard用ログを`./logs` ディレクトリに書き込みます。
  tf.keras.callbacks.TensorBoard(log_dir='./logs')
]
model.fit(data, labels, batch_size=32, epochs=5, callbacks=callbacks,
          validation_data=(val_data, val_labels))

保存と復元

重みのみ

tf.keras.Model.save_weightsを使ってモデルの重みの保存やロードを行います。

model = tf.keras.Sequential([
layers.Dense(64, activation='relu', input_shape=(32,)),
layers.Dense(10, activation='softmax')])

model.compile(optimizer=tf.train.AdamOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])
# TensorFlow チェックポイント ファイルに重みを保存します。
model.save_weights('./weights/my_model')

# モデルの状態を復元します。
# 復元対象のモデルと保存されていた重みのモデル構造が同一である必要があります。
model.load_weights('./weights/my_model')

デフォルトでは、モデルの重みは TensorFlow チェックポイント 形式で保存されます。 重みはKerasのHDF5形式でも保存できます(マルチバックエンド実装のKerasではHDF5形式がデフォルト):

# 重みをHDF5形式で保存します。
model.save_weights('my_model.h5', save_format='h5')

# モデルの状態を復元します。
model.load_weights('my_model.h5')

構成のみ

モデルの構成も保存可能です。 モデル構造を重み抜きでシリアライズします。 元のモデルのコードがなくとも、保存された構成で再構築できます。 Kerasがサポートしているシリアライズ形式は、JSONとYAMLです。

# JSON形式にモデルをシリアライズします
json_string = model.to_json()
json_string
import json
import pprint
pprint.pprint(json.loads(json_string))

JSONから(新たに初期化して)モデルを再構築します:

fresh_model = tf.keras.models.model_from_json(json_string)

YAML形式でモデルを保存するには、 TensorFlowをインポートする前に あらかじめpyyamlをインストールしておく必要があります:

yaml_string = model.to_yaml()
print(yaml_string)

YAMLからモデルを再構築します:

fresh_model = tf.keras.models.model_from_yaml(yaml_string)

注意:callメソッド内ににPythonコードでモデル構造を定義するため、派生モデルはシリアライズできません。

モデル全体

モデルの重み、構成からオプティマイザ設定までモデル全体をファイルに保存できます。 そうすることで、元のコードなしに、チェックポイントで保存したときと全く同じ状態から学習を再開できます。

# 層の浅いモデルを構築します。
model = tf.keras.Sequential([
  layers.Dense(64, activation='relu', input_shape=(32,)),
  layers.Dense(10, activation='softmax')
])
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
model.fit(data, labels, batch_size=32, epochs=5)


# HDF5ファイルにモデル全体を保存します。
model.save('my_model.h5')

# 重みとオプティマイザを含む 全く同一のモデルを再構築します。
model = tf.keras.models.load_model('my_model.h5')

Eagerモード

Eagerモード](./eager) は、オペレーションを即時に評価する命令型のプログラミング環境です。 Kerasでは必要ありませんが、tf.kerasでサポートされておりプログラムを検査しデバッグするのに便利です。

すべてのtf.kerasモデル構築用APIは、Eagerモード互換性があります。 Sequential や Functional APIも使用できますが、 Eagerモードは特に派生モデル の構築や 層のカスタマイズに有益です。 (既存の層の組み合わせでモデルを作成するAPIの代わりに) 順伝播をコードで実装する必要があります。

詳しくは Eagerモード ガイド (カスタマイズした学習ループとtf.GradientTapeを使ったKerasモデルの適用事例)をご参照ください。

分散

Estimators

Estimators は分散学習を行うためのAPIです。 実運用に耐えるモデルを巨大なデータセットを用いて分散学習するといった産業利用を目的にすえています。

tf.keras.Modeltf.estimator APIによる学習を行うには、 tf.keras.estimator.model_to_estimatorを使ってKerasモデルを tf.estimator.Estimatorオブジェクトに変換する必要があります。

KerasモデルからEstimatorsを作成するをご参照ください。

model = tf.keras.Sequential([layers.Dense(64, activation='relu', input_shape=(32,)),
                          layers.Dense(10,activation='softmax')])

model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

estimator = tf.keras.estimator.model_to_estimator(model)

注意:Estimator input functionsをデバッグしてデータの検査を行うにはEagerモードで実行してください。

マルチGPU

tf.kerasモデルはtf.contrib.distribute.DistributionStrategyを使用することでマルチGPU上で実行できます。 このAPIを使えば、既存コードをほとんど改変することなく分散学習へ移行できます。

目下、分散方式としてtf.contrib.distribute.MirroredStrategyのみサポートしています。 MirroredStrategy は、シングルマシン上でAllReduce を使った同期学習によりin-grapnレプリケーションを行います。 KerasでDistributionStrategyを使用する場合は、tf.keras.estimator.model_to_estimatorを使って tf.keras.Modeltf.estimator.Estimatorに変換し、Estimatorインスタンスを使って分散学習を行います。

以下の例では、シングルマシンのマルチGPUにtf.keras.Modelを分散します。

まず、単純なモデルを定義します:

model = tf.keras.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10,)))
model.add(layers.Dense(1, activation='sigmoid'))

optimizer = tf.train.GradientDescentOptimizer(0.2)

model.compile(loss='binary_crossentropy', optimizer=optimizer)
model.summary()

入力パイプラインを定義します。input_fn は、複数デバイスにデータを配置するのに使用する tf.data.Dataset を返します。 各デバイスは、入力バッチの一部(デバイス間で均等に分割)を処理します。

def input_fn():
  x = np.random.random((1024, 10))
  y = np.random.randint(2, size=(1024, 1))
  x = tf.cast(x, tf.float32)
  dataset = tf.data.Dataset.from_tensor_slices((x, y))
  dataset = dataset.repeat(10)
  dataset = dataset.batch(32)
  return dataset

次に、 tf.estimator.RunConfigを作成し、 train_distribute 引数にtf.contrib.distribute.MirroredStrategy インスタンスを設定します。MirroredStrategyを作成する際、デバイスの一覧を指定する、もしくは引数でnum_gpus(GPU数)を設定することができます。デフォルトでは、使用可能なすべてのGPUを使用する設定になっています:

strategy = tf.contrib.distribute.MirroredStrategy()
config = tf.estimator.RunConfig(train_distribute=strategy)

Kerasモデルを tf.estimator.Estimator インスタンスへ変換します。

keras_estimator = tf.keras.estimator.model_to_estimator(
  keras_model=model,
  config=config,
  model_dir='/tmp/model_dir')

最後に、input_fnsteps引数を指定して Estimator インスタンスを学習します:

keras_estimator.train(input_fn=input_fn, steps=10)