ホーム » 「TensorFlow Programmer’s Guide」タグがついた投稿

タグアーカイブ: TensorFlow Programmer’s Guide

TensorFlow : Guide : TensorBoard : ヒストグラム・ダッシュボード

TensorFlow : Guide : TensorBoard: ヒストグラム・ダッシュボード (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/15/2018

* TensorFlow 1.9 でドキュメント構成が変わりましたので調整しました。
* 本ページは、TensorFlow の本家サイト Guide – TensorBoard – Histogram Dashboard を翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

 

本文

TensorBoard ヒストグラム・ダッシュボードは貴方の TensorFlow グラフのある Tensor の分布が時間とともにどのように変化するかを示します。異なるポイントで tensor の多くのヒストグラムの可視化を揃えて見せることによりこれを遂行します。

 

基本サンプル

単純なケースから始めましょう: 正規分布に従う変数、ここで平均 (= mean) は時間と共にシフトします。TensorFlow は op tf.random_normal を持ち、これはこの目的のために完璧です。TensorBoard では通常のことですが、summary op を使用してデータを取り込みます、この場合には、‘tf.summary.histogram’ です。

ここにコード・スニペットがあります、これは時間につれて分布の平均が増加するような正規分布に従うデータを含む何某かのヒストグラム要約を生成します。

import tensorflow as tf

k = tf.placeholder(tf.float32)

# Make a normal distribution, with a shifting mean
mean_moving_normal = tf.random_normal(shape=[1000], mean=(5*k), stddev=1)
# Record that distribution into a histogram summary
tf.summary.histogram("normal/moving_mean", mean_moving_normal)

# Setup a session and summary writer
sess = tf.Session()
writer = tf.summary.FileWriter("/tmp/histogram_example")

summaries = tf.summary.merge_all()

# Setup a loop and write the summaries to disk
N = 400
for step in range(N):
  k_val = step/float(N)
  summ = sess.run(summaries, feed_dict={k: k_val})
  writer.add_summary(summ, global_step=step)

コードを実行すれば、コマンド行を通してデータを TensorBoard にロードすることができます :

tensorboard --logdir=/tmp/histogram_example

TensorBoard が動作していれば、それを Chrome か Firefox へロードしてヒストグラム・ダッシュボードにナビゲートします。それから正規分布に従うヒストグラム可視化を見ることができます。

 
tf.summary.histogram は任意のサイズと shape の Tensor を取り、それを幅とカウントを持つ多くのビンから成るヒストグラム・データ構造に圧縮 (= compress) します。例えば、数字 [0.5, 1.1, 1.3, 2.2, 2.9, 2.99] をビンに構造化することを望むとします。3つのビンを作れるでしょう :

  • 0 から 1 の総てを含むビン (それは 1 要素, 0.5 を含むでしょう)、
  • 1 から 2 の総てを含むビン (それは 2 要素, 1.1 と 1.3 を含むでしょう)、
  • 2 から 3 の総てを含むビン (それは 3 要素: 2.2, 2.9 と 2.99 を含むでしょう)。

TensorFlow はビンを作成するために同様のアプローチを使用しますが、この例とは違って、それは整数値ビンを作成しません。巨大な、疎なデータセットに対して、それは数千のビンという結果になるかもしれません。代わりに、ビンは指数関数的に分布して、多くのビンは 0 に近くそして非常に巨大な数のための比較的少ないビンがあります。けれども、指数関数的に分布したビンの可視化はトリッキーです ; もし高さがカウントをエンコードするために使用される場合、より広いビンは更にスペースを取ります、それらが同じ要素数を持つ場合でさえも。逆に、領域でカウントをエンコードすると高さの比較が不可能になります。代わりに、ヒストグラムは一様なビンに データをリサンプリング します。これはある場合には不幸な人工物に繋がるかもしれません。

ヒストグラム・ビジュアライザで各スライスはシングル・ヒストグラムを示します。スライスはステップで構成されます ; より古いスライス (e.g. ステップ 0) は更に “後ろ (= back)” でより暗く、一方でより新しいスライス (e.g. step 400) は最前面に近く、色の点でより明るいです。右側の y-軸はステップ数を示します。

より詳細な情報を持つツールチップを見るにはヒストグラム上でマウスカーソルを移動することができます。例えば、次の画像ではタイムステップ 176 のヒストグラムは 177 要素を持ち 2.25 を中心とするビンを持つことを見て取れます。

 
また、ヒストグラム・スライスはステップカウントや時間で常に等分に分けられるわけではないことに注意してください。これは TensorBoard は (メモリをセーブするため) 総てのヒストグラムのサブセットを保持するのに reservoir (貯水池) サンプリング を使用するからです。reservoir サンプリングは総てのサンプルが (含まれるために) 等しい尤度を持つことを保証しますが、それはランダム化されたアルゴリズムですから、選択されるサンプルは一定のステップでは発生しません。

 

オーバーレイ・モード

ダッシュボードの左側に histogram mode を “offset” から “overlay” へのトグルを可能にするコントロールがあります :

 
“offset” モードでは、ビジュアリゼーションは 45 度回転して、個々のヒストグラム・スライスは時間で広がるのではなく、代わりに総ては同じ y-軸上にプロットされます。

 
今では、各スライスはチャート上で分離したラインで、y-軸は各バケット内の項目カウントを示します。より暗い線はより古く、より早いステップで、より明るい線はより最近で、後のステップです。再度、何某かの追加情報を見るためにチャート上でマウスカーソルを移動できます。

一般的に、overlay ビジュアリゼーションは異なるヒストグラムのカウントを直接比較することを望む場合に有用です。

 

マルチモーダル分布

ヒストグラム・ダッシュボードはマルチモーダル分布の可視化について素晴らしいです。2 つの異なる正規分布からの出力を結合して単純なバイモーダル (二峰性) 分布を構築してみましょう。コードはこのようなものです :

import tensorflow as tf

k = tf.placeholder(tf.float32)

# Make a normal distribution, with a shifting mean
mean_moving_normal = tf.random_normal(shape=[1000], mean=(5*k), stddev=1)
# Record that distribution into a histogram summary
tf.summary.histogram("normal/moving_mean", mean_moving_normal)

# Make a normal distribution with shrinking variance
variance_shrinking_normal = tf.random_normal(shape=[1000], mean=0, stddev=1-(k))
# Record that distribution too
tf.summary.histogram("normal/shrinking_variance", variance_shrinking_normal)

# Let's combine both of those distributions into one dataset
normal_combined = tf.concat([mean_moving_normal, variance_shrinking_normal], 0)
# We add another histogram summary to record the combined distribution
tf.summary.histogram("normal/bimodal", normal_combined)

summaries = tf.summary.merge_all()

# Setup a session and summary writer
sess = tf.Session()
writer = tf.summary.FileWriter("/tmp/histogram_example")

# Setup a loop and write the summaries to disk
N = 400
for step in range(N):
  k_val = step/float(N)
  summ = sess.run(summaries, feed_dict={k: k_val})
  writer.add_summary(summ, global_step=step)

貴方は既に上の例から “移動平均” 正規分布を覚えているでしょう。今 “shrinking variance (分散)” 分布もまた持ちます。それらは並んで、このように見えます :

それらを結合するとき、分岐する、バイモーダル構造を明瞭に示すチャートを得ます :

 

更に幾つかの分布

楽しみのために、更に幾つかの分布を生成して可視化し、そしてそれら総てを一つのチャートに結合してみましょう。ここに使用するコードがあります :

import tensorflow as tf

k = tf.placeholder(tf.float32)

# Make a normal distribution, with a shifting mean
mean_moving_normal = tf.random_normal(shape=[1000], mean=(5*k), stddev=1)
# Record that distribution into a histogram summary
tf.summary.histogram("normal/moving_mean", mean_moving_normal)

# Make a normal distribution with shrinking variance
variance_shrinking_normal = tf.random_normal(shape=[1000], mean=0, stddev=1-(k))
# Record that distribution too
tf.summary.histogram("normal/shrinking_variance", variance_shrinking_normal)

# Let's combine both of those distributions into one dataset
normal_combined = tf.concat([mean_moving_normal, variance_shrinking_normal], 0)
# We add another histogram summary to record the combined distribution
tf.summary.histogram("normal/bimodal", normal_combined)

# Add a gamma distribution
gamma = tf.random_gamma(shape=[1000], alpha=k)
tf.summary.histogram("gamma", gamma)

# And a poisson distribution
poisson = tf.random_poisson(shape=[1000], lam=k)
tf.summary.histogram("poisson", poisson)

# And a uniform distribution
uniform = tf.random_uniform(shape=[1000], maxval=k*10)
tf.summary.histogram("uniform", uniform)

# Finally, combine everything together!
all_distributions = [mean_moving_normal, variance_shrinking_normal,
                     gamma, poisson, uniform]
all_combined = tf.concat(all_distributions, 0)
tf.summary.histogram("all_combined", all_combined)

summaries = tf.summary.merge_all()

# Setup a session and summary writer
sess = tf.Session()
writer = tf.summary.FileWriter("/tmp/histogram_example")

# Setup a loop and write the summaries to disk
N = 400
for step in range(N):
  k_val = step/float(N)
  summ = sess.run(summaries, feed_dict={k: k_val})
  writer.add_summary(summ, global_step=step)

 

ガンマ分布

一様分布

ポアソン分布

ポアソン分布は整数に渡り定義されます。そのため、生成される総ての値は完全な整数です。ヒストグラム圧縮はデータを浮動小数点ビンに移動し、ビジュアリゼーションは完全なスパイク (尖った波形) ではなく整数値に渡って小さいバンプ (凹凸) を示すことになります。

All Together Now

最後に、データ総てを一つの奇妙に見える (= funny-looking) カーブに結合することができます。

 

以上

TensorFlow : Guide : 高位 API : Keras (tf.keras)

TensorFlow : Guide : 高位 API : Keras (tf.keras) (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/13/2018 (1.9.0)

* TensorFlow 1.9.0 で tf.keras のドキュメントが追加されましたので翻訳しておきます。
* 本ページは、TensorFlow 本家サイトの Guide – High Level APIs – Keras を翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、適宜、追加改変している場合もあります。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

本文

Keras は深層学習モデルを構築してビルドするための高位 API です。それは、次の 3 つの主要な優位点を持ち高速なプロトタイピング、先進的な研究、そしてプロダクションのために使用されます。

  • ユーザ・フレンドリー
    Keras は一般的なユースケースのために最適化された単純で、首尾一貫したインターフェイスを持ちます。それはユーザ・エラーのために明瞭でアクション可能なフィードバックを提供します。

  • モジュール式で組み立て可能
    Keras モデルは構成可能なビルディングブロックを一緒に接続することにより作成され、殆ど制限はありません。

  • 簡単に拡張
    研究のための新しいアイデアを表わすためにカスタム・ビルディングブロックを書いてください。新しい層、損失関数を作成し、最先端技術のモデルを開発してください。

 

tf.keras をインポートする

tf.kerasKeras API 仕様 の TensorFlow 実装です。これはモデルを構築して訓練するための高位 API で、eager execution, tf.data pipelines, そして Estimators のような、TensorFlow-特有の機能のためのファーストクラス・サポートを含みます。tf.keras は柔軟性とパフォーマンスを犠牲にすることなく TensorFlow をより簡単に使用可能にします。

始めるためには、貴方の TensorFlow プログラムのセットアップの一部として tf.keras をインポートします :

import tensorflow as tf
from tensorflow import keras

tf.keras は任意の Keras 互換コードを実行できますが、以下に留意してください :

  • 最新の TensorFlow リリースの tf.keras バージョンは PyPI からの最新の keras バージョンと同じではないかもしれません。tf.keras.version (訳注: 7/13 現在リンク切れ) を確認してください。
  • モデルの重みをセーブするとき、tf.keras はチェックポイント・フォーマットをデフォルトにしています。HDF5 を使用するためには save_format=’h5′ を渡してください。

 

単純なモデルを構築する

Sequential モデル

Keras では、モデルを構築するために層を組み立てます。モデルは (通常は) 層のグラフです。モデルの最も一般的なタイプは層のスタック : tf.keras.Sequential です。

単純な、完全結合ネットワーク(i.e. 多層パーセプトロン) を構築するためには :

model = keras.Sequential()
# Adds a densely-connected layer with 64 units to the model:
model.add(keras.layers.Dense(64, activation='relu'))
# Add another:
model.add(keras.layers.Dense(64, activation='relu'))
# Add a softmax layer with 10 output units:
model.add(keras.layers.Dense(10, activation='softmax'))

 

層を設定する

幾つかの一般的なコンストラクタ・パラメータを持つ、利用可能な tf.keras.layers は多くあります :

  • activation: 層のための活性化関数を設定します。このパラメータは組み込み関数の名前か呼び出し可能なオブジェクトとして指定されます。デフォルトでは、活性は適用されません。
  • kernel_initializerbias_initializer: 層の重み (カーネルとバイアス) を作成する初期化スキームです。このパラメータは名前か呼び出し可能なオブジェクトです。これは “Glorot uniform” initializer をデフォルトとします。
  • kernel_regularizerbias_regularizer: L1 または L2 正則化のような、層の重み (カーネルとバイアス) に適用する正則化スキームです。デフォルトでは、正則化は適用されません。

次は tf.keras.layers.Dense 層をコンストラクタ引数を使用してインスタンス化します :

# Create a sigmoid layer:
layers.Dense(64, activation='sigmoid')
# Or:
layers.Dense(64, activation=tf.sigmoid)

# A linear layer with L1 regularization of factor 0.01 applied to the kernel matrix:
layers.Dense(64, kernel_regularizer=keras.regularizers.l1(0.01))
# A linear layer with L2 regularization of factor 0.01 applied to the bias vector:
layers.Dense(64, bias_regularizer=keras.regularizers.l2(0.01))

# A linear layer with a kernel initialized to a random orthogonal matrix:
layers.Dense(64, kernel_initializer='orthogonal')
# A linear layer with a bias vector initialized to 2.0s:
layers.Dense(64, bias_initializer=keras.initializers.constant(2.0))

 

訓練と評価

訓練のセットアップ

モデルがコンストラクトされた後、その学習プロセスを compile メソッドを呼び出すことにより設定します :

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

tf.keras.Model.compile は 3 つの重要な引数を取ります :

  • optimizer: このオブジェクトは訓練手続きを指定します。AdamOptimizer, RMSPropOptimizer, または GradientDescentOptimizer のような、tf.train モジュールからの optimizer インスタンスをそれに渡します。
  • loss: 最適化の間に最小化する関数です。一般的な選択はmean square error (mse), categorical_crossentropy, そして binary_crossentropy を含みます。損失関数は名前か tf.keras.losses からの呼び出し可能なオブジェクトを渡すことで指定されます。
  • metrics: 訓練を監視するために使用されます。これらは文字列名か tf.keras.metrics モジュールからの呼び出し可能 (オブジェクト) です。

以下は訓練のためにモデルを設定する 2, 3 のサンプルを示します :

# Configure a model for mean-squared error regression.
model.compile(optimizer=tf.train.AdamOptimizer(0.01),
              loss='mse',       # mean squared error
              metrics=['mae'])  # mean absolute error

# Configure a model for categorical classification.
model.compile(optimizer=tf.train.RMSPropOptimizer(0.01),
              loss=keras.losses.categorical_crossentropy,
              metrics=[keras.metrics.categorical_accuracy])

 

NumPy データの入力

小さいデータセットに対しては、モデルを訓練して評価するためにインメモリ NumPy 配列を使用します。モデルは fit メソッドを使用して訓練データに “fit” します :

import numpy as np

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

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

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

  • epochs: 訓練はエポックに構造化されます。1 つのエポックは入力データ全体に渡る 1 回の反復です (これはより小さいバッチ内で遂行されます)。
  • batch_size: NumPy データが渡されたとき、モデルはデータをより小さいバッチにスライスして (薄切りにして) 訓練の間これらのバッチに渡り反復します。この整数は各バッチのサイズを指定します。サンプル総数がバッチサイズで割り切れない場合は最後のバッチはより小さいかもしれないことに留意してください。
  • validation_data: モデルをプロトタイピングするとき、何某かの検証データ上でそのパフォーマンスを容易に監視することを望みます。この引数 — 入力とラベルのタプル — を渡すと、各エポックの最後に、モデルが渡されたデータに対して推論モードで損失とメトリクスを示すことを可能にします。

ここに validation_data を使用するサンプルがあります :

import numpy as np

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

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

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

 

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

巨大なデータセットやマルチデバイス訓練にスケールするためには Datasets API を使用します。tf.data.Dataset インスタンスを fit メソッドに渡します :

# Instantiates a toy dataset instance:
dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32)
dataset = dataset.repeat()

# Don't forget to specify `steps_per_epoch` when calling `fit` on a dataset.
model.fit(dataset, epochs=10, steps_per_epoch=30)

ここで、fit メソッドは steps_per_epoch 引数を使用します — これはモデルが次のエポックに移る前に実行する訓練ステップ数です。Dataset がデータのバッチを yield するので、このスニペットは batch_size を必要としません。

Datasets はまた検証のためにも使用できます :

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 を使用できます。

提供されるデータのために推論モード損失とメトリクスを評価するには :

model.evaluate(x, y, batch_size=32)

model.evaluate(dataset, steps=30)

そして提供されるデータのために最後の層の出力を推論で予測するためには、NumPy 配列として :

model.predict(x, batch_size=32)

model.predict(dataset, steps=30)

 

上級モデルを構築する

Functional API

tf.keras.Sequential は層の単純なスタックで、任意のモデルを表わすことはできません。次のような複雑なモデル・トポロジーを構築するためには Keras functional API を使用します :

  • マルチ入力モデル、
  • マルチ出力モデル、
  • 共有層を持つモデル (同じ層が何回か呼ばれます)、
  • 非シーケンシャル・データフローを持つモデル (e.g. 残差接続)。

functional API によるモデル構築はこのように作業します :

  1. 層インスタンスは呼び出し可能で tensor を返します。
  2. 入力 tensor と出力 tensor は tf.keras.Model インスタンスを定義するために使用されます。
  3. このモデルは丁度 Sequential モデルのように訓練されます。

次のサンプルは単純な、完全結合ネットワークを構築するために functional API を使用します :

inputs = keras.Input(shape=(32,))  # Returns a placeholder tensor

# A layer instance is callable on a tensor, and returns a tensor.
x = keras.layers.Dense(64, activation='relu')(inputs)
x = keras.layers.Dense(64, activation='relu')(x)
predictions = keras.layers.Dense(10, activation='softmax')(x)

# Instantiate the model given inputs and outputs.
model = keras.Model(inputs=inputs, outputs=predictions)

# The compile step specifies the training configuration.
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Trains for 5 epochs
model.fit(data, labels, batch_size=32, epochs=5)

 

Model サブクラス化

tf.keras.Model をサブクラス化して貴方自身の forward パスを定義して完全にカスタマイズ可能なモデルを構築します。__init__ メソッドで層を作成してそれらをクラス・インスタンスの属性として設定します。call メソッドで forward パスを定義します。

Model サブクラ化は eager execution が有効なときに特に有用です、何故ならば forward パスが命令的に書けるからです。

Key Point: ジョブのために適切な API を使用してください。model サブクラス化が柔軟性を提供する一方で、それは大きな複雑さとユーザエラーのより多くの機会という代償を伴います。可能であれば、functional API を選択してください。

次のサンプルはカスタム forward パスを使用してサブクラス化した tf.keras.Model を示します :

class MyModel(keras.Model):

  def __init__(self, num_classes=10):
    super(MyModel, self).__init__(name='my_model')
    self.num_classes = num_classes
    # Define your layers here.
    self.dense_1 = keras.layers.Dense(32, activation='relu')
    self.dense_2 = keras.layers.Dense(num_classes, activation='sigmoid')

  def call(self, inputs):
    # Define your forward pass here,
    # using layers you previously defined (in `__init__`).
    x = self.dense_1(inputs)
    return self.dense_2(x)

  def compute_output_shape(self, input_shape):
    # You need to override this function if you want to use the subclassed model
    # as part of a functional-style model.
    # Otherwise, this method is optional.
    shape = tf.TensorShape(input_shape).as_list()
    shape[-1] = self.num_classes
    return tf.TensorShape(shape)


# Instantiates the subclassed model.
model = MyModel(num_classes=10)

# The compile step specifies the training configuration.
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Trains for 5 epochs.
model.fit(data, labels, batch_size=32, epochs=5)

 

カスタム層

tf.keras.layers.Layer のサブクラス化と次のメソッドの実装でカスタム層を作成します :

  • build: 層の重みを作成します。add_weight メソッドで重みを追加します。
  • call: forward パスを定義します。
  • compute_output_shape: 入力 shape が与えられたとき層の出力 shape をどのように計算するかを指定します。
  • オプションで、get_config メソッドと from_config クラスメソッドを実装することにより層はシリアライズできます。

入力のカーネル行列による matmul を実装するカスタム層のサンプルがここにあります :

class MyLayer(keras.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))
    # Create a trainable weight variable for this layer.
    self.kernel = self.add_weight(name='kernel',
                                  shape=shape,
                                  initializer='uniform',
                                  trainable=True)
    # Be sure to call this at the end
    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

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


# Create a model using the custom layer
model = keras.Sequential([MyLayer(10),
                          keras.layers.Activation('softmax')])

# The compile step specifies the training configuration
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Trains for 5 epochs.
model.fit(data, targets, batch_size=32, epochs=5)

 

Callbacks

callback は訓練の間の (モデルの) 挙動をカスタマイズして拡張するためにモデルに渡されるオブジェクトです。貴方自身のカスタム callback を書く、あるいは以下を含む組み込み tf.keras.callbacks を使用することができます :

tf.keras.callbacks.Callback を使用するためには、モデルの fit メソッドにそれを渡します :

callbacks = [
  # Interrupt training if `val_loss` stops improving for over 2 epochs
  keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'),
  # Write TensorBoard logs to `./logs` directory
  keras.callbacks.TensorBoard(log_dir='./logs')
]
model.fit(data, labels, batch_size=32, epochs=5, callbacks=callbacks,
          validation_data=(val_data, val_targets))

 

セーブとリストア

Weights (重み) only

tf.keras.Model.save_weights を使用してモデルの重みをセーブしてロードします :

# Save weights to a TensorFlow Checkpoint file
model.save_weights('./my_model')

# Restore the model's state,
# this requires a model with the same architecture.
model.load_weights('my_model')

デフォルトでは、これはモデルの重みを TensorFlow チェックポイント・ファイルフォーマットでセーブします。重みはまた Keras HDF5 フォーマットでもセーブできます (Keras のマルチ・バックエンド実装のためのデフォルトです) :

# Save weights to a HDF5 file
model.save_weights('my_model.h5', save_format='h5')

# Restore the model's state
model.load_weights('my_model.h5')

 

Configuration only

モデルの configuration をセーブできます — これはモデル・アーキテクチャを重みなしでシリアライズします。セーブされた configuration は (元のモデルを定義するコードなしでさえも) 同じモデルを再作成して初期化することができます。Keras は JSON と YAML シリアライゼーション・フォーマットをサポートします :

# Serialize a model to JSON format
json_string = model.to_json()

# Recreate the model (freshly initialized)
fresh_model = keras.models.from_json(json_string)

# Serializes a model to YAML format
yaml_string = model.to_yaml()

# Recreate the model
fresh_model = keras.models.from_yaml(yaml_string)

Caution: サブクラス化されたモデルはシリアライズ可能ではありません、何故ならばそれらのアーキテクチャは call メソッドの本体内で Python コードで定義されるからです。

 

モデル全体

モデル全体は、重み値、モデルの configuration、そして optimizer の configuration さえも含むファイルにセーブできます。これはモデルをチェックポイントして後で— 正確に同じ状態から — 元のコードへのアクセスなしに訓練を再開することを可能にします。

# Create a trivial model
model = keras.Sequential([
  keras.layers.Dense(10, activation='softmax', input_shape=(32,)),
  keras.layers.Dense(10, activation='softmax')
])
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
model.fit(data, targets, batch_size=32, epochs=5)


# Save entire model to a HDF5 file
model.save('my_model.h5')

# Recreate the exact same model, including weights and optimizer.
model = keras.models.load_model('my_model.h5')

 

Eager execution

Eager execution は演算を直ちに評価する命令的なプログラミング環境です。これは Keras のために必要ではありませんが、tf.keras によりサポートされて貴方のプログラムを調査してデバッグするために有用です。

総ての tf.keras モデル構築 API は eager execution と互換です。そして Sequential and functional API が使用できる一方で、eager execution は特にモデル・サブクラス化とカスタム層の構築に利益を与えます forward パスをコードとして書くことを要求する API です (既存の層を組み立てることによりモデルを作成する API の代わりに)。

カスタム訓練ループと tf.GradientTape で Keras モデルを使用するサンプルのための eager execution ガイド を見てください。

 

分散

Estimators

Estimators API は分散環境のためのモデルを訓練するために使用されます。これは巨大なデータセット上での分散訓練のような、プロダクションのためのモデルをエクスポートできる industry ユースケースを対象としています。

tf.keras.Model は tf.keras.estimator.model_to_estimatortf.estimator.Estimator オブジェクトにモデルを変換することにより tf.estimator API で訓練できます。Keras モデルから Estimator を作成する を見てください。

model = keras.Sequential([layers.Dense(10,activation='softmax'),
                          layers.Dense(10,activation='softmax')])

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

estimator = keras.estimator.model_to_estimator(model)

Note: Estimator 入力関数 をデバッグしてデータを調査するためには eager execution を有効にしてください。

 

マルチ GPU

tf.keras モデルは tf.contrib.distribute.DistributionStrategy を使用して
マルチ GPU 上で実行できます。この API はマルチ GPU 上の分散訓練を既存のコードへの殆ど変更なしに提供します。

現在、tf.contrib.distribute.MirroredStrategy が唯一サポートされる分散ストラテジーです。MirroredStrategy はシングル・マシン上で all-reduce を使用して同期訓練で in-graph レプリケーションを行ないます。Keras で DistributionStrategy を使用するためには、tf.keras.Model を tf.keras.estimator.model_to_estimatortf.estimator.Estimator に変換し、それから estimator を訓練します。

次のサンプルはシングルマシン上のマルチ GPU に渡り tf.keras.Model を分散します。

最初に、単純なモデルを定義します :

model = keras.Sequential()
model.add(keras.layers.Dense(16, activation='relu', input_shape=(10,)))
model.add(keras.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 を作成して tf.contrib.distribute.MirroredStrategy インスタンスに train_distribute 引数を設定します。MirroredStrategy を作成するとき、デバイスのリストを指定するか num_gpus 引数を設定することができます。デフォルトでは総ての利用可能な GPU を使用します、次のようにです :

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

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

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

最後に、input_fn と steps 引数を提供することにより Estimator インスタンスを訓練します :

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

 
以上



TensorFlow : Guide : Debugging : TensorFlow デバッガー

TensorFlow : Guide : Debugging TensorFlow デバッガー (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
更新日時 : 07/14/2018
作成日時 : 05/04/2018

* TensorFlow 1.9 でドキュメント構成が変わりましたので調整しました。
* 本ページは、TensorFlow 本家サイトの Guide – Debugging – TensorFlow Debugger を翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、適宜、追加改変している場合もあります。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

序文

TensorFlow デバッガー (tfdbg) は TensorFlow のために特化されたデバッガーです。それは訓練と推論の間に動作している TensorFlow グラフの内部構造と状態を見せてくれます。TensorFlow の計算グラフ・パラダイムゆえに、Python の pdb のような一般目的のデバッガーではそれはデバッグすることが困難です。

NOTE: このガイドは tfdbg のコマンドライン・インターフェイス (CLI) にフォーカスしています。tfdbg のグラフィカル・ユーザ・インターフェイス (GUI) i.e., TensorBoard デバッガー Plugin をどのように使用するかのガイドについては、その README を訪ねてください。

このチュートリアルは TensorFlow モデル開発で頻繁に遭遇するタイプのバグ、naninf の出現をデバッグするためにどのように tfdbg CLI を使用するかを示します。次のサンプルは TensorFlow の低位 Session API を使用するユーザのためのものです。このドキュメントの後のセクションは高位 API、つまり tf-learn Esitomator と Experiment とともに tfdbg をどのように使用するかを記述します。そのような問題を観測するために、デバッガーなしで次のコマンドを実行します (ソースコードは ここで 見つかります) :

python -m tensorflow.python.debug.examples.debug_mnist

このコードは MNIST 数字画像認識のための単純なニューラルネットワークを訓練します。accuracy は最初の訓練ステップ後にわずかばかり上がり、しかしそれから低い (ほぼ偶然) レベルで抜け出せなくなることに気がつくでしょう :

Accuracy at step 0: 0.1113
Accuracy at step 1: 0.3183
Accuracy at step 2: 0.098
Accuracy at step 3: 0.098
Accuracy at step 4: 0.098

何が間違っていたのかと考えながら、貴方は訓練グラフのあるノードが inf と nan のような悪い数値を生成したことを疑います、何故ならばこれはこのタイプの訓練の失敗の一般的な原因であるからです。この問題をデバッグしてこの数値問題が最初に浮上した正確なグラフノードの位置を示すために tfdbg を使用しましょう。

 

TensorFlow Session を tfdbg でラップする

私達のサンプルで tfdbg のためのサポートを追加するために、必要な総てのことは次のコード行を追加して Session オブジェクトを debugger ラッパーでラップすることです。このコードは既に debug_mnist.py に追加されていますので、コマンドラインで –debug フラグにより tfdbg CLI をアクティベートできます。

# Let your BUILD target depend on "//tensorflow/python/debug:debug_py"
# (You don't need to worry about the BUILD dependency if you are using a pip
#  install of open-source TensorFlow.)
from tensorflow.python import debug as tf_debug

sess = tf_debug.LocalCLIDebugWrapperSession(sess)

このラッパーは Session と同じインターフェイスを持ちますので、デバッギングの有効化はコードへの他の変更は必要としません。ラッパーは追加の特徴を提供します、以下を含みます :

  • Session.run() 呼び出しの前後に CLI を起動し、貴方に実行を制御してグラフの内部状態を調査させます。
  • tensor 値のための特別なフィルタを登録して、問題の診断を容易にすることを可能にします。

このサンプルでは、私達は既に tfdbg.has_inf_or_nan と呼ばれる tensor フィルタを登録していて、これは任意の中間 tensor に任意の nan か inf 値があるかを単純に決定します (tensor は Session.run() 呼び出しの入力や出力ではなく、入力から出力に繋がるパス内にあります)。

Note: 貴方自身のカスタム・フィルタを書くこともできます。追加の情報のためには DebugDumpDir.find() の API 文書 を見てください。

 

tfdbg でモデル訓練をデバッグする

モデルを再度訓練してみましょう、但し今回は –debug フラグとともにです :

python -m tensorflow.python.debug.examples.debug_mnist --debug

debug ラッパー session は最初の Session.run() 呼び出しを実行しようとするとき貴方にプロンプトを出します、取得された tensor と feed 辞書に関する情報をスクリーンに表示するとともに。

 
これが run-start CLI として言及したものです。それは何かを実行する前に現在の Session.run 呼び出しへの供給 (= feeds) と取得 (= fetches) をリストします。

スクリーンサイズが全体的にメッセージ内容を表示するために小さ過ぎる場合には、それをリサイズすることができます。

スクリーン出力をナビゲートするために PageUp / PageDown / Home / End キーを使用します。それらのキーが欠落しているキーボードの殆どの上では Fn + Up / Fn + Down / Fn + Right / Fn + Left が機能するでしょう。

コマンド・プロンプトで run コマンド (or 単に r) を入力します :

tfdbg> run

run コマンドは tfdbg に次の Session.run() 呼び出し (これは test データセットを使用してモデル精度を計算します) の最後まで実行させます。tfdbg は総ての中間 tensor をダンプするために runtime Graph を増強します。実行終了後、tfdbg は総てのダンプされた tensor 値を run-end CLI で表示します。例えば :

 
tensor のこのリストは run を実行した後でコマンド lt を実行することによっても得られます。

tfdbg CLI Frequently-Used コマンド

tfdbg> プロンプトで次のコマンドを試してください (tensorflow/python/debug/examples/debug_mnist.py のコードを参照しています) :

コマンド シンタックス or オプション 説明
lt ダンプされた tensor をリストします。 lt
-n <name_pattern> 与えられた正規表現パターンにマッチする名前を持つダンプされた tensor をリストします。 lt -n Softmax.*
-t <op_pattern> 与えられた正規表現パターンにマッチする op タイプを持つダンプされた tensor をリストします。 lt -t MatMul
-f <filter_name> 登録された tensor フィルタを通過する tensor のみをリストします。 lt -f has_inf_or_nan
-s <sort_key> 与えられた sort_key により出力をソートします、その可能な値は timestamp (default), dump_size, op_type そして tensor_name です。 lt -s dump_size
-r 逆順でのソート。 lt -r -s dump_size
pt ダンプされた tensor の値をプリントします。
pt <tensor> tensor 値をプリントします。 pt hidden/Relu:0
pt <tensor>[slicing] numpy-スタイルの配列スライシングを使用して、tensor の部分配列をプリントします。 pt hidden/Relu:0[0:50,:]
-a ellipsis を使用することなく、より巨大な tensor の全体をプリントします。(巨大な tensor のためには長時間かかるかもしれません。) pt -a hidden/Relu:0[0:50,:]
-r <range> 指定された数字範囲に入る要素をハイライトします。複数の範囲が連結して使用できます。 pt hidden/Relu:0 -a -r [[-inf,-1],[1,inf]]
-n <number> 指定された 0-ベースのダンプ番号に対応するダンプをプリントします。複数のダンプを持つ tensor のために必要です。 pt -n 0 hidden/Relu:0
-s tensor の数値の要約を含みます (Boolean と int* と float* のような数値型を持つ non-empty tensor にのみ適用可能です) pt -s hidden/Relu:0[0:50,:]
-w (多分スライスされている) tensor の値を numpy.save() を使用して Numpy ファイルに書きます。 pt -s hidden/Relu:0 -w /tmp/relu.npy
@[coordinates] pt 出力の指定された要素にナビゲートします。 @[10,0] or @10,0
/regex 与えられた正規表現に対して less-スタイル検索。 /inf
/ 検索された正規表現にマッチする次の行にスクロールします。 /
pf Session.run への feed_dict の値をプリントします。
pf <feed_tensor_name> feed の値をプリントします。また pf コマンドは -a, -r と -s フラグも持つことに注意してください (下にリストされません)、これは pt の同一の名前のフラグと同じシンタックスとセマンティクスを持ちます。 pf input_xs:0
eval 任意の Python と numpy 式を評価します。
eval <expression> np として利用可能な numpy による、Python / numpy 式を評価して、バッククォートで囲まれた tensor 名をデバッグします。 eval “np.matmul((
`output/Identity:0` / `Softmax:0`).T, `Softmax:0`)”
-a それを全体として i.e., ellipsis を使用することなく、巨大なサイズの評価結果をプリントします。 eval -a ‘np.sum(`Softmax:0`, axis=1)’
-w 評価の結果を numpy.save() を使用して Numpy ファイルに書きます。 eval -a ‘np.sum(`Softmax:0`, axis=1)’ -w /tmp/softmax_sum.npy
ni ノード情報を表示します。
-a 出力にノード属性を含みます。 ni -a hidden/Relu
-d ノードから利用可能な debug ダンプをリストします。 ni -d hidden/Relu
-t ノード作成の Python スタックトレースを表示します。 ni -t hidden/Relu
li ノードへの入力をリストします。
-r ノードへの入力をリストします、再帰的に (入力ツリー)。 li -r hidden/Relu:0
-d -r モード下で再帰の深さを制限します。 li -r -d 3 hidden/Relu:0
-c 制御入力を含めます。 li -c -r hidden/Relu:0
-t 入力ノードの op タイプを示します。 li -t -r hidden/Relu:0
lo ノードの出力受取者をリストします。
-r ノードの出力受取者をリストします、再帰的に (出力ツリー)。 lo -r hidden/Relu:0
-d <max_depth> -r モード下で再帰の深さを制限します。 lo -r -d 3 hidden/Relu:0
-c 制御エッジ経由の受取者を含めます。 lo -c -r hidden/Relu:0
-t 受取者ノードの op タイプを示します。 lo -t -r hidden/Relu:0
ls ノード作成に伴う Python ソースファイルをリストします。
-p <path_pattern> 出力を与えられた正規表現パス・パターンにマッチするソースファイルに制限します。 ls -p .*debug_mnist.*
-n 出力を与えられた正規表現パターンにマッチするノード名に制限します。 ls -n Softmax.*
ps Python ソースファイルをプリントします。
ps <file_path> (もしあれば、行の) 各々で作成されたノードでアノテートされた行とともに、与えられた Python ソースファイル source.py をプリントします。 ps /path/to/source.py
-t デフォルトのノードの代わりに、Tensor に関するアノテーションを遂行します。 ps -t /path/to/source.py
-b <line_number> 与えられたラインから始まる source.py をアノテートします。 ps -b 30 /path/to/source.py
-m <max_elements> 各行に対するアノテーションの要素数を制限します。 ps -m 100 /path/to/source.py
run 次の Session.run() まで進めます。 run
-n デバッギングなしで次の Session.run を通して実行します、そしてそれ以後の run の直前に CLI に落ちます。 run -n
-t <T> デバッギングなしで Session.run を T – 1 回実行し、続いてデバッギングとともに run します。それからデバッグされた run 直後に CLI に落ちます run -t 10
-f <filter_name> 任意の中間 tensor が指定された Tensor フィルタをトリガーするまで Session.run を実行し続けます (フィルタに True を返させます)。 run -f has_inf_or_nan
–node_name_filter <pattern> 次の Session.run を実行し、与えられた正規表現パターンにマッチする名前を持つノードだけを監視します。 run –node_name_filter Softmax.*
–op_type_filter <pattern> 次の Session.run を実行し、与えられた正規表現パターンにマッチする op タイプを持つノードだけを監視します。 run –op_type_filter Variable.*
–tensor_dtype_filter <pattern> 次の Session.run を実行し、与えられた正規表現パターンにマッチするデータ型 (dtypes) を持つ Tensor だけをダンプします。 run –tensor_dtype_filter int.*
-p profiling モードで次の Session.run 呼び出しを実行します。 run -p
ri fetches と feeds を含む、現在の実行についての情報を表示します。 ri
config 永続的な TFDBG UI configuration を設定するか表示します。
set config 項目の値を設定します: {graph_recursion_depth, mouse_mode}。 config set graph_recursion_depth 3
show 現在の永続的 UI configuration を表示します。 config show
help 一般的なヘルプ情報をプリントします。 help
help <command> 与えられたコマンドに対するヘルプをプリントします。 help lt

貴方がコマンドを入力するたびに、新しいスクリーン出力が現れることに注意してください。これはブラウザの web ページに幾分類似しています。これらのスクリーンの間を CLI の左上隅近くの <– and –> テキスト矢印をクリックすることによりナビゲートできます。

 

tfdbg CLI の他の特徴

上でリストされたコマンドに加えて、tfdbg CLI は次の追加の特徴を提供します :

  • 以前の tfdbg コマンドを通してナビゲートするために、2, 3 のキャラクタのタイプに続いて Up または Down 矢印キーを続けます。tfdbg はそれらのキャラクタで始まるコマンドの履歴を示します。
  • スクリーン出力の履歴を通してナビゲートするためには、次のいずれかを行ないます :
    • prev と next コマンドを使用します。
    • スクリーンの左上隅近くのアンダーラインがされた <– と –> リンクをクリックします。
  • コマンドと幾つかのコマンド引数の Tab 補完。
  • スクリーン出力をスクリーンの代わりにファイルにリダイレクトするためには、コマンドを bash-スタイルのリダイレクションで終了します。例えば、次のコマンドは pt コマンドの出力を /tmp/xent_value_slices.txt ファイルにリダイレクトします :
    tfdbg> pt cross_entropy/Log:0[:, 0:10] > /tmp/xent_value_slices.txt
    

 

nan と inif を見つける

この最初の Session.run() 呼び出しでは、問題のある数値は発生していません。コマンド run かその簡略記法 r を使用して次の run に進むことができます。

TIP: run か r を繰り返し入力すれば、シーケンシャルな方法で Session.run() 呼び出しを通して移動することができるでしょう。

一度に多くの Session.run() 呼び出しを進むためには -t フラグを使用することもできます、例えば :

tfdbg> run -t 10

run-end UI で run を繰り返し入力して総ての Session.run() 呼び出しの後に (例えば、上のテーブルで示された pt コマンドを使用して) nan と inf を手動で探す代わりに、グラフで最初の nan や inf 値が現れるまで run-start や run-end プロンプトで停止することなしにデバッガーに繰り返し Session.run() 呼び出しを実行させるために次のコマンドを使用できます。これは幾つかの手続型言語デバッガーの条件ブレークポイントに類似しています :

tfdbg> run -f has_inf_or_nan

NOTE: 前のコマンドは正しく動作します、何故ならばラップされた session が作成されたときに has_inf_or_nan という名前の tensor フィルタが貴方のために登録されているからです。このフィルタは (前に説明したように) nan と inf を検出します。もし貴方が任意の他のフィルタを登録した場合には、任意の tensor が (フィルタに True を返させて) そのフィルタをトリガーするまで tfdbg を実行させるために “run -f” を使用できます。

def my_filter_callable(datum, tensor):
  # A filter that detects zero-valued scalars.
  return len(tensor.shape) == 0 and tensor == 0.0

sess.add_tensor_filter('my_filter', my_filter_callable)

そして tfdbg run-start プロンプトで貴方のフィルタがトリガーされるまで実行します :

tfdbg> run -f my_filter

add_tensor_filter() で使用される想定されるシグネチャと predicate Callable の返り値についての更なる情報については この API ドキュメント を見てください。

 
スクリーン・ディスプレイが最初の行で示すように、has_inf_or_nan フィルタは 4 番目の Session.run() 呼び出しの間に最初にトリガーされます : グラフ上の Adam optimizer forward-backward 訓練パスです。この run では、(総計 95 から) 36 の中間 tensor が nan か inf 値を含みます。これらの tensor は、左側に表示されるそれらのタイムスタンプとともに時系列順にリストされます。リストのトップでは、悪い数値が最初に現れた最初の tensor を見ることができます : cross_entropy/Log:0。

tensor の値を見るためには、アンダーラインがつけられた tensor 名 cross_entropy/Log:0 をクリックするか同等のコマンドを入力します :

tfdbg> pt cross_entropy/Log:0

少しスクロール・ダウンすると貴方は幾つかの散らばった inf 値に気がつくでしょう。もし inf と nan のインスタンスを目で見つけるのが難しいのであれば、regex 検索を遂行して出力をハイライトするために次のコマンドを使用できます :

tfdbg> /inf

あるいは、代わりに :

tfdbg> /(inf|nan)

tenosr の数値の型の素早い要約を得るために -s か –numeric_summary コマンドを使用することもできます :

tfdbg> pt -s cross_entropy/Log:0

要約から、cross_entropy/Log:0 tensor の 1000 要素の幾つかが -inf (負の無限大) であることが見て取れます。

何故これらの無限大が出現したのでしょう?更にデバッグするためには、トップのアンダーラインがつけられた node_info メニュー項目をクリックするか同値の node_info (ni) コマンドを入力することによりノード cross_entropy/Log について更なる情報を表示します :

tfdbg> ni cross_entropy/Log

 
このノードが op タイプ Log を持ちそしてその入力がノード Softmax であることを見て取れます。入力 tensor をより密接に見るために次のコマンドを実行します :

tfdbg> pt Softmax:0

入力 tensor の値を調査して、ゼロを探します :

tfdbg> /0\.000

実際にゼロがあります。今では悪い数値の源はゼロの log を取るノード cross_entropy/Log であることが明らかです。Python ソースコード内の犯人を見つけ出すために、ノードの構築のトレースバックを示すために ni コマンドの -t フラグを使用します :

tfdbg> ni -t cross_entropy/Log

スクリーンのトップの “node_info” をクリックすれば、tfdbg はノードの構築のトレースバックを自動的に示します。

トレースバックから、op は次の行で構築されたことが見てとれます : debug_mnist.py :

diff = y_ * tf.log(y)

tfdbg は Tensor と ops を Python ソースファイルの行にトレースバックすることを容易にする特徴を持ちます。それらから作成された ops や Tensor を持つ Python ファイルの行にアノテートすることができます。この特徴を使用するためには、単純に ni -t <op_name> コマンドのスタック・トレース出力のアンダーラインがつけられた行番号をクリックするか、ps /path/to/source.py のように ps (or print_source) コマンドを使用します。例えば、次のスクリーンショットは ps コマンドの出力を示します。

 

問題を修正する

問題を修正するためには、debug_mnist.py を編集して、元の行を :

diff = -(y_ * tf.log(y))

組み込みの、softmax cross-entropy の数値的に安定な実装に変更します :

diff = tf.losses.sparse_softmax_cross_entropy(labels=y_, logits=logits)

次のように –debug フラグで再実行します :

python -m tensorflow.python.debug.examples.debug_mnist --debug

tfdbg> プロンプトで、次のコマンドを入力します :

run -f has_inf_or_nan

どの tensor も nan や inf 値を含むとフラグされておらず、そして精度が今では硬直するのではなく上がり続けることを確認してください。成功です!

 

tf-learn Estimator と Experiment をデバッグする

このセクションでは Estimator と Experiment API を使用する TensorFlow プログラムをどのようにデバッグするかについて説明します。これらの API により提供される便利さの一部はそれらが Session を内部的に管理することです。これは前のセクションで記述された LocalCLIDebugWrapperSession を適用できなくなります。幸い、tfdbg により提供される特別なフックを使用してそれらを依然としてデバッグできます。

 

tf.contrib.learn Estimator をデバッグする

現在、tfdbg は tf-learn Estimator の fit() evaluate() メソッドをデバッグできます。Estimator.fit() をデバッグするためには、LocalCLIDebugHook を作成してそれを monitors 引数に供給します。例えば :

# First, let your BUILD target depend on "//tensorflow/python/debug:debug_py"
# (You don't need to worry about the BUILD dependency if you are using a pip
#  install of open-source TensorFlow.)
from tensorflow.python import debug as tf_debug

# Create a LocalCLIDebugHook and use it as a monitor when calling fit().
hooks = [tf_debug.LocalCLIDebugHook()]

classifier.fit(x=training_set.data,
               y=training_set.target,
               steps=1000,
               monitors=hooks)

Estimator.evaluate() をデバッグするためには、次のサンプル内のように、フックを hooks パラメータに割り当てます :

accuracy_score = classifier.evaluate(x=test_set.data,
                                     y=test_set.target,
                                     hooks=hooks)["accuracy"]

tf-learn のアイリス・チュートリアル に基づく、debug_tflearn_iris.py は Estimator で tfdbg をどのように使用するかの完全なサンプルを含みます。このサンプルを実行するためには、次を行ないます :

python -m tensorflow.python.debug.examples.debug_tflearn_iris --debug

 

tf.contrib.learn Experiment をデバッグする

Experiment は Estimator よりもより高位の tf.contrib.learn の構成要素です。それはモデルを訓練して評価するための単一のインターフェイスを提供します。Experiment オブジェクトへの train() と evaluate() 呼び出しをデバッグするためには、そのコンストラクタを呼び出すときキーワード引数 train_monitors と eval_hooks をそれぞれ使用できます。例えば :

# First, let your BUILD target depend on "//tensorflow/python/debug:debug_py"
# (You don't need to worry about the BUILD dependency if you are using a pip
#  install of open-source TensorFlow.)
from tensorflow.python import debug as tf_debug

hooks = [tf_debug.LocalCLIDebugHook()]

ex = experiment.Experiment(classifier,
                           train_input_fn=iris_input_fn,
                           eval_input_fn=iris_input_fn,
                           train_steps=FLAGS.train_steps,
                           eval_delay_secs=0,
                           eval_steps=1,
                           train_monitors=hooks,
                           eval_hooks=hooks)

ex.train()
accuracy_score = ex.evaluate()["accuracy"]

Experiment モードで debug_tflearn_iris サンプルを構築して実行するためには、次を行ないます :

python -m tensorflow.python.debug.examples.debug_tflearn_iris \
    --use_experiment --debug

LocalCLIDebugHook はまた fetches と feed_dict と他の状態の関数として、異なる Session.run() 呼び出し上でどの Tensor を監視するかを柔軟に指定するために使用できる watch_fn を configure することを可能にします。更なる詳細のためにはこの API doc を見てください。

 

TFDBG で keras モデルをデバッグする

Keras で TFDBG を使用するためには、Keras バックエンドに TFDBG-wrapped Session オブジェクトを使用させます。例えば、CLI ラッパーを使用するには :

import tensorflow as tf
from keras import backend as keras_backend
from tensorflow.python import debug as tf_debug

keras_backend.set_session(tf_debug.LocalCLIDebugWrapperSession(tf.Session()))

# Define your keras model, called "model".
model.fit(...)  # This will break into the TFDBG CLI.

 

TFDBG で tf-slim をデバッグする

TFDBG は tf-slim による訓練と評価のデバッギングをサポートします。下で詳述されるように、訓練と評価は少し異なるデバッギング・ワークフローを必要とします。

 

tf-slim 内で訓練をデバッグする

訓練プロセスをデバッグするには、LocalCLIDebugWrapperSession を slim.learning.train() の session_wrapper に提供します。例えば :

import tensorflow as tf
from tensorflow.python import debug as tf_debug

# ... Code that creates the graph and the train_op ...
tf.contrib.slim.learning.train(
    train_op,
    logdir,
    number_of_steps=10,
    session_wrapper=tf_debug.LocalCLIDebugWrapperSession)

 

tf-slim 内で評価をデバッグする

評価プロセスをデバッグするには、LocalCLIDebugHook を slim.evaluation.evaluate_once() の hooks 引数に提供します。例えば :

import tensorflow as tf
from tensorflow.python import debug as tf_debug

# ... Code that creates the graph and the eval and final ops ...
tf.contrib.slim.evaluation.evaluate_once(
    '',
    checkpoint_path,
    logdir,
    eval_op=my_eval_op,
    final_op=my_value_op,
    hooks=[tf_debug.LocalCLIDebugHook()])

 

リモートで動作する Session のオフライン・デバッギング

しばしば、貴方のモデルはアクセスする端末を持たないリモート・マシンやプロセス上で動作します。そのようなケースでモデル・デバッギングを遂行するために、tfdbg の offline_analyzer バイナリを使用できます (下で説明されます)。それはダンプされたデータ・ディレクトリ上で動作します。これは低位 Session API とより高位な Estimator と Experiment API の両者に対して行なうことができます。

 

リモート tf.Session をデバッグする

python で tf.Session API と直接的に相互作用する場合には、メソッド tfdbg.watch_graph を使用して、それと一緒に Session.run() メソッドを呼び出すことができる RunOptions proto を configure することができます。これは、Session.run() 呼び出しが発生するとき、(より遅いパフォーマンスという代償を払って) 中間 tensor とランタイム・グラフが貴方が選択した共有ストレージ位置にダンプされることを引き起こすでしょう。例えば :

from tensorflow.python import debug as tf_debug

# ... Code where your session and graph are set up...

run_options = tf.RunOptions()
tf_debug.watch_graph(
      run_options,
      session.graph,
      debug_urls=["file:///shared/storage/location/tfdbg_dumps_1"])
# Be sure to specify different directories for different run() calls.

session.run(fetches, feed_dict=feeds, options=run_options)

後で、アクセスする端末を持つ環境で(例えば、上のコードで指定された共有ストレージ位置にアクセスできるローカル・コンピュータ)、tfdbg の offline_analyzer バイナリを使用して共有ストレージ上のダンプ・ディレクトリ内のデータをロードして調査することができます。例えば :

python -m tensorflow.python.debug.cli.offline_analyzer \
    --dump_dir=/shared/storage/location/tfdbg_dumps_1

Session ラッパー DumpingDebugWrapperSession はオフラインで解析できるファイルシステム・ダンプを生成するためのより容易で柔軟な方法を提供します。それを使用するためには、単に貴方の session を tf_debug.DumpingDebugWrapperSession でラップします。例えば :

# Let your BUILD target depend on "//tensorflow/python/debug:debug_py
# (You don't need to worry about the BUILD dependency if you are using a pip
#  install of open-source TensorFlow.)
from tensorflow.python import debug as tf_debug

sess = tf_debug.DumpingDebugWrapperSession(
    sess, "/shared/storage/location/tfdbg_dumps_1/", watch_fn=my_watch_fn)

watch_fn 引数は、run() 呼び出しへの fetches と feed_dict と他の状態の関数として、異なる Session.run() 呼び出し上で何の Tensor を監視するかを configure することを可能にする Callable を受け取ります。

 

C++ と他の言語

モデル・コードが C++ か他の言語で書かれている場合でもまた、オフラインで調査できるうデバッグ・ダンプを生成するためにRunOptions の debug_options フィールドを変更できます。より詳細のためには proto 定義 を見てください。

 

リモートで動作する tf-learn Estimator と Experiment をデバッグする

リモートの TensorFlow サーバが Estimator を実行する場合、非対話的な DumpingDebugHook を使用できます。例えば :

# Let your BUILD target depend on "//tensorflow/python/debug:debug_py
# (You don't need to worry about the BUILD dependency if you are using a pip
#  install of open-source TensorFlow.)
from tensorflow.python import debug as tf_debug

hooks = [tf_debug.DumpingDebugHook("/shared/storage/location/tfdbg_dumps_1")]

それからこのフックはこのドキュメントの前の方で説明された LocalCLIDebugHook サンプルと同じ用法で使用できます。Estimator か Experiment の訓練 and/or 評価が発生するとき、tfdbg は次の名前パターンを持つディレクトリを作成します :
/shared/storage/location/tfdbg_dumps_1/run_<epoch_timestamp_microsec>_<uuid>。
各ディレクトリは fit() か evaluate() 呼び出しの基礎となる Session.run() 呼び出しに対応します。tfdbg で提供される offline_analyzer を使用してこれらのディレクトリをロードしてそれらをオフライン流儀でコマンドライン・インターフェイスで調査することができます。例えば :

python -m tensorflow.python.debug.cli.offline_analyzer \
    --dump_dir="/shared/storage/location/tfdbg_dumps_1/run_<epoch_timestamp_microsec>_<uuid>"

 

Frequently Asked Questions

Q: lt 出力kの左側のタイムスタンプは非デバッギング session の実際のパフォーマンスを反映していますか?

A: No. デバッガーは中間 tensor の値を記録するためにグラフに追加の特殊な目的のデバッグ・ノードを挿入しています。これらのノードはグラフ実行を遅くします。貴方のモデルを profile することに興味があれば、以下をチェックしてください :

  1. tfdbg の profiling モード: tfdbg> run -p。
  2. TensorFlow のための tfprof と他の profiling ツール。

 
Q: Bazel の Session に対してどのように tfdbg をリンクしますか?何故 “ImportError: cannot import name debug” のようなエラーを見るのでしょう?

A: 貴方の BUILD ルールで、dependencies を宣言してください : “//tensorflow:tensorflow_py” 及び “//tensorflow/python/debug:debug_py”。最初のはデバッガー・サポートなしでさえも TensorFlow を使用するために include する依存です ; 2 番目がデバッガーを有効にします。それから貴方の Python ファイルで、以下を追加します :

from tensorflow.python import debug as tf_debug

# Then wrap your TensorFlow Session with the local-CLI wrapper.
sess = tf_debug.LocalCLIDebugWrapperSession(sess)

 
Q: tfdbg は shape ミスマッチのようなランタイム・エラーをデバッグするのに役立ちますか?

A: Yes. tfdbg は実行時に ops により生成されたエラーをインターセプトして CLI のユーザに何某かのデバッグ手順とともにエラーを提示します。サンプルを見てください :

# Debugging shape mismatch during matrix multiplication.
python -m tensorflow.python.debug.examples.debug_errors \
    --error shape_mismatch --debug

# Debugging uninitialized variable.
python -m tensorflow.python.debug.examples.debug_errors \
    --error uninitialized_variable --debug

 
Q: tfdbg-wrapped Session や Hook をどのようにメインスレッドからだけデバッグモードで実行させますか?

A: これは一般的なユースケースで、そこでは Session オブジェクトはマルチスレッドから同時に使用されます。典型的には、子スレッドは enqueue 演算を実行するようなバックグラウンド・タスクをケアします。しばしば、メインスレッドのみをデバッグすることを望むでしょう (あるいは頻度は下がるでしょうが、子スレッドの一つだけ)。このタイプの thread-selective デバッギングを遂行するために LocalCLIDebugWrapperSession の thread_name_filter キーワード引数を使用できます。例えば、メインスレッドだけからデバッグするためには、次のように wrapped Session を構築します :

sess = tf_debug.LocalCLIDebugWrapperSession(sess, thread_name_filter="MainThread$")

上のサンプルは Python のメインスレッドがデフォルト名 MainThread を持つという事実に依拠しています。

 
Q: 私がデバッグしているモデルは非常に巨大です。tfdbg によりダンプされたデータは私のディスクの空き容量を一杯にします。どうしたら良いでしょう?

A: 次のような状況のどれでもこの問題に遭遇するかもしれません :

  • 多くの中間 tensor を持つモデル
  • 非常に巨大な中間 tensor
  • 多くの tf.while_loop iteration

3つの可能なワークアラウンドあるいは解法があります :

  • LocalCLIDebugWrapperSession と LocalCLIDebugHook のコンストラクタは tfdbg がデバッグデータをダンプするパスを指定するためのキーワード引数、dump_root を提供します。tfdbg がより大きな空き容量を持つディスク上にデバッグデータをダンプさせるためにそれを使用できます。例えば :
    # For LocalCLIDebugWrapperSession
    sess = tf_debug.LocalCLIDebugWrapperSession(dump_root="/with/lots/of/space")
    
    # For LocalCLIDebugHook
    hooks = [tf_debug.LocalCLIDebugHook(dump_root="/with/lots/of/space")]
    

    dump_root によりポイントされているディレクトリが空か存在しないことを確認してください。tfdbg は抜ける前にダンプ・ディレクトリをクリーンアップします。

  • 実行の間に使用されるバッチサイズを減じます。
  • グラフの特定のノードだけを監視するために tfdbg の run コマンドのフィルタリング・オプションを使用します。例えば :
    tfdbg> run --node_name_filter .*hidden.* 
    tfdbg> run --op_type_filter Variable.* 
    tfdbg> run --tensor_dtype_filter int.*
    

    上の最初のコマンドは名前が正規表現パターン .*hidden.* にマッチするノードだけを監視します。2番目のコマンドは名前がパターン Variable.* にマッチする演算のみを監視します。3番目のものは dtype がパターン int.* (e.g., int32) にマッチする tensor だけを監視します。

 
Q: tfdbg CLI で何故テキストを選択できないのでしょう?

A: これは tfdbg CLI がデフォルトで端末のマウス・イベントを有効にするためです。mouse-マスク モードは、テキスト選択を含むデフォルトの端末相互作用を override します。コマンド mouse off か m off を使用してテキスト選択を再有効にすることができます。

 
Q: 次のようなコードをデバッグするとき tfdbg CLI はダンプされた tensor を何故表示しないのでしょう?

a = tf.ones([10], name="a")
b = tf.add(a, a, name="b")
sess = tf.Session()
sess = tf_debug.LocalCLIDebugWrapperSession(sess)
sess.run(b)

A: ダンプされたデータを見ない理由は実行される TensorFlow グラフの総てのノードが TensorFlow ランタイムにより constant-folded であるからです。このサンプルでは、a は constant tensor です ; 従って取得される tensor b もまた事実上 constant tensor です。TensorFlow グラフ最適化はグラフの将来的な実行をスピードアップするために a と b を含むグラフを単一のノードに折り畳みます、これが何故 tfdbg がどのような中間 tensor ダンプも生成しないかです。けれども、次のサンプル内のように a が tf.Variable であるならば、

import numpy as np

a = tf.Variable(np.ones[10], name="a")
b = tf.add(a, a, name="b")
sess = tf.Session()
sess.run(tf.global_variables_initializer())
sess = tf_debug.LocalCLIDebugWrapperSession(sess)
sess.run(b)

constant-folding は発生せずに tfdbg は中間 tensor ダンプを表示するはずです。

 
Q: 望まない無限大や NaN を生成するモデルをデバッグしています。しかし私のモデルでは、完全に正常な条件下でさえも出力 tensor 内で無限大や NaN を生成することが分かっている幾つかのノードがあります。run -f has_inf_or_nan アクションの間にそれらをどのようにスキップできるでしょう?

A: –filter_exclude_node_names (-fenn for short) フラグを使用してください。例えば、モデルが正しく振舞っているか否かに無関係に無限大や NaN を生成する、正規表現 .*Sqrt.* にマッチする名前を持つノードを持つことを知っているのであれば、コマンド run -f has_inf_or_nan -fenn .*Sqrt.* で infinity/NaN-finding run からノードを除外できます。

 
Q: tfdbg のための GUI はありますか?

A: Yes, TensorBoard Debugger Plugin は tfdbg の GUI です。それは、計算グラフの精査、tensor 値のリアルタイム可視化、tensor への継続と条件ブレークポイント、そして tensor のそれらのグラフ構築ソースコードへの結び付けをブラウザ環境で提供します。始めるためには、その README を訪ねてください。

 
以上



TensorFlow : Guide : Accelerators : TPU を利用する

TensorFlow : Guide : Accelerators : TPU を利用する (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
更新日時 : 07/14/2018
作成日時 : 04/23/2018

* TensorFlow 1.9 でドキュメント構成が変わりましたので調整しました。
* 本ページは、TensorFlow 本家サイトの Guide – Accelerators – Using TPUs を翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、適宜、追加改変している場合もあります。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

序文

このドキュメントは Cloud TPU を効果的に活用するために必要な主要な TensorFlow API をウォークスルーし、通常の TensorFlow 使用方法と TPU の使用方法の間の違いをハイライトします。

このドキュメントは次のようなユーザを対象にしています :

  • TensorFlow の Estimator と Dataset API に精通している。
  • 既存のモデルを使用して多分 Cloud TPU を試したことがある
  • サンプル TPU モデル [1] [2] (訳注: リンク切れ) のコードを多分大まかに読んだことがある。
  • 既存の Estimator モデルを Cloud TPU 上で実行するためにポートすることに興味がある。

 

TPUEstimator

Estimator は TensorFlow のモデル・レベルの抽象です。標準的な Estimator は CPU と GPU 上でモデルを駆動できます。TPU 上でモデルを駆動するためには tf.contrib.tpu.TPUEstimator を使用しなければなりません。

TPUEstimator クラスは Estimator クラスとは幾分異なります。

CPU/GPU の両者あるいは Cloud TPU 上で動作可能なモデルを維持する最も単純な方法はモデルの推論フェイズ (入力から予測) を model_fn の外側で定義することです。それから Estimator setup と moderl_fn の別の実装を維持します、両者はこの推論ステップをラップします。このパターンのサンプルのために tensorflow/models の mnist.py と mnist_tpu.py 実装を比較します。

 

TPUEstimator をローカルで実行する

標準的な Estimator を作成するためには、コンストラクタを呼び出し、それに model_fn に渡します、例えば :

my_estimator = tf.estimator.Estimator(
  model_fn=my_model_fn)

貴方のローカルマシン上で tf.contrib.tpu.TPUEstimator を使用するために必要な変更は比較的少ないです。コンストラクタは2つの追加引数を要求します。use_tpu 引数を False に設定して、 config 引数として tf.contrib.tpu.RunConfig を渡すべきです、下で示されるように :

my_tpu_estimator = tf.contrib.tpu.TPUEstimator(
    model_fn=my_model_fn,
    config=tf.contrib.tpu.RunConfig()
    use_tpu=False)

丁度この単純な変更が TPUEstimator をローカルで実行することを可能にします。コマンドライン・フラグを次のように設定することにより、サンプル TPU モデルの大半はこのローカルモードで実行可能です :

$> python mnist_tpu.py --use_tpu=false --master=''

Note: この use_tpu=False 引数は TPUEstimator API を試すために有用です。それは完全な TPU 互換テストであることを意味しません。TPUEstimator のモデルのローカルでの成功的に実行することはそれが TPU 上で動作することを保証しません。

 

tpu.RunConfig を構築する

ローカル訓練のためにはデフォルト RunConfig は十分である一方で、これらの設定は実際の利用では無視できません。

Cloud TPU を使用するために切り替え可能な RunConfig のためのより典型的なセットアップは次のようなものです :

import tempfile
import subprocess

class FLAGS(object):
  use_tpu=False
  tpu_name=None
  # Use a local temporary path for the `model_dir`
  model_dir = tempfile.mkdtemp()
  # Number of training steps to run on the Cloud TPU before returning control.
  iterations = 50
  # A single Cloud TPU has 8 shards.
  num_shards = 8

if FLAGS.use_tpu:
    my_project_name = subprocess.check_output([
        'gcloud','config','get-value','project'])
    my_zone = subprocess.check_output([
        'gcloud','config','get-value','compute/zone'])
    cluster_resolver = tf.contrib.cluster_resolver.TPUClusterResolver(
            tpu_names=[FLAGS.tpu_name],
            zone=my_zone,
            project=my_project)
    master = tpu_cluster_resolver.get_master()
else:
    master = ''

my_tpu_run_config = tf.contrib.tpu.RunConfig(
    master=master,
    evaluation_master=master,
    model_dir=FLAGS.model_dir,
    session_config=tf.ConfigProto(
        allow_soft_placement=True, log_device_placement=True),
    tpu_config=tf.contrib.tpu.TPUConfig(FLAGS.iterations,
                                        FLAGS.num_shards),
)

それから tf.contrib.tpu.RunConfig をコンストラクタに渡さなければなりません :

my_tpu_estimator = tf.contrib.tpu.TPUEstimator(
    model_fn=my_model_fn,
    config = my_tpu_run_config,
    use_tpu=FLAGS.use_tpu)

典型的には FLAGS はコマンドライン引数で設定されます。ローカルでの訓練から Cloud TPU 上の訓練に切り替えるためには次が必要です :

  • FLAGS.use_tpu を True に設定する。
  • FLAGS.tpu_name を設定します、tf.contrib.cluster_resolver.TPUClusterResolver がそれを見つけられるようにです。
  • FLAGS.model_dir を Google Cloud Storage bucket url (gs://) に設定します。

 

Optimizer

cloud TPU 上で訓練するとき、optimizer を tf.contrib.tpu.CrossShardOptimizer でラップしなければなりません、これは
勾配を集めて結果を各シャード (各 TPU コア) にブロードキャストするために allreduce (訳注: 集団通信アルゴリズムの一つ) を使用します。

CrossShardOptimizer はローカル訓練と互換ではありません。そのため、同じコードをローカルと Cloud TPU 上の両者で実行するためには、次のような行を追加します :

optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
if FLAGS.use_tpu:
  optimizer = tf.contrib.tpu.CrossShardOptimizer(optimizer)

貴方のモデル・コードでグローバル FLAGS 変数を回避することを好む場合に、一つのアプローチは optimizer を Estimator の params の一つとして設定することです :

my_tpu_estimator = tf.contrib.tpu.TPUEstimator(
    model_fn=my_model_fn,
    config = my_tpu_run_config,
    use_tpu=FLAGS.use_tpu,
    params={'optimizer':optimizer})

 

Model 関数

このセクションは貴方が model 関数 (model_fn()) に (それを) TPUEstimator と互換にするために行わなければならない変更について詳述します。 

 

静的 shape

通常の使用の間は TensorFlow は各 tf.Tensor の shape をグラフ構築の間に決定することを試みます。実行の間任意の未知の shape 次元は動的に決定されます。

Cloud TPU 上で実行するために TensorFlow モデルは XLA を使用してコンパイルされます。XLA はコンパイル時に shape を決定するために同様のシステムを使用します。XLA は総ての tensor 次元がコンパイル時に静的に定義されることを要求します。総ての shape は定数として評価され、そして外部データ、あるいは変数やランダム数ジェネレータのようなステートフルな演算に依拠してはなりません。

 

Summaries

貴方のモデルから tf.summary の任意の使用を除去します。

TensorBoard summaries は貴方のモデルの内側を見る偉大な方法です。基本的な summaries の最小セットは TPUEstimator により model_dir のイベントファイルに自動的に記録されます。けれども、Cloud TPU 上で訓練するときカスタム summaries は現在サポートされていません。そのため TPUEstimator が依然としてローカルで summaries とともに動作しても、TPU 上で使用されればそれは失敗するでしょう。

 

Metrics

stand-alone metric_fn で貴方の評価メトリクス辞書を構築します。

評価メトリクスはモデル訓練の本質的なパートです。これらは Cloud TPU 上で完全にサポートされますが、僅かばかり異なるシンタクスを伴います。

標準的な tf.metrics は2つの tensor を返します。最初はメトリック値の実行平均を返し、その一方で2番目は実行平均を更新してこのバッチのための値を返します :

running_average, current_batch = tf.metrics.accuracy(labels, predictions)

標準的な Estimator ではこれらのペアの辞書を作成して、それを EstimatorSpec の一部として返します。

my_metrics = {'accuracy': tf.metrics.accuracy(labels, predictions)}

return tf.estimator.EstimatorSpec(
  ...
  eval_metric_ops=my_metrics
)

TPUEstimator では、代わりに関数 (これはメトリクス辞書を返します) と引数 tensor のリストを渡します、以下で示されるようにです :

def my_metric_fn(labels, predictions):
   return {'accuracy': tf.metrics.accuracy(labels, predictions)}

return tf.contrib.tpu.TPUEstimatorSpec(
  ...
  eval_metrics=(my_metric_fn, [labels, predictions])
)

 

TPUEstimatorSpec を使用する

TPUEstimatorSpec は hooks をサポートしません、そして幾つかのフィールドについては関数ラッパーが必要です。

Estimator の model_fn は EstimatorSpec を返さなければなりません。EstimatorSpec は、Estimator が相互作用する必要があるかもしれないモデルの総ての tf.Tensor を含む名前付けされたフィールドの単純な構造です。

TPUEstimators は tf.contrib.tpu.TPUEstimatorSpec を使用します。それと標準的な tf.estimator.EstimatorSpec の間には 2, 3 の違いがあります :

  • eval_metric_ops は metrics_fn にラップされなければなりません、このフィールドは eval_metrics に名前変更されます (上を参照)。
  • hooks は非サポートですので、これらのフィールドは省略されます。
  • もし使用されるのであれば、scaffold もまた関数にラップされなければなりません。このフィールドは scaffold_fn に名前変更されます。

Scaffold と Hooks はより進んだ利用のためで、典型的には省略可能です。

 

入力関数

入力関数はそれらが Cloud TPU 自身ではなく、ホストコンピュータで動作するとき殆ど変更なしで動作します。このセクションは2つの必要な調整を説明します。

 

Params 引数

標準的な Estimator のための input_fn は params 引数を含むことができます ; TPUEstimator のための input_fn は params 引数を含まなければなりません。これは、estimator に入力ストリームの各レプリカのためのバッチサイズを設定することを可能にするために必要です。TPUEstimator のための input_fn のための最小限のシグネチャは :

def my_input_fn(params):
  pass

ここで params[‘batch-size’] はバッチサイズを含みます。

 

静的 shape とバッチサイズ

貴方の input_fn により生成された入力パイプラインは CPU で動作します。そのためそれは大抵は XLA/TPU 環境により課せられる自由で厳密な静的 shape 要件です。一つの要件は貴方の入力パイプラインから TPU に供給されるデータのバッチは、標準的な TensorFlow shape 推論アルゴリズムにより決定される静的 shape を持つことです。中間的な tensor は動的 shape を自由に持ちます。もし shape 推論が失敗して、しかし shape が知られている場合には、tf.set_shape() を使用して正しい shape を課すことができます。

下のサンプルでは shape 推論アルゴリズムが失敗していますが、set_shape を使用して正されています :

>>> x = tf.zeros(tf.constant([1,2,3])+1)
>>> x.shape

TensorShape([Dimension(None), Dimension(None), Dimension(None)])

>>> x.set_shape([2,3,4])

多くの場合バッチサイズは唯一の未知の次元です。

tf.data を使用する、典型的な入力パイプラインは通常な固定サイズのバッチを生成します。けれども、有限の Dataset の最後のバッチは典型的にはより小さく、丁度残りの要素を含みます。Dataset はそれ自身の長さや有限性は知りませんので、標準的な batch メソッドは総てのバッチがそれ自身の上で固定サイズ・バッチを持つかは決定できません :

>>> params = {'batch_size':32}
>>> ds = tf.data.Dataset.from_tensors([0, 1, 2])
>>> ds = ds.repeat().batch(params['batch-size'])
>>> ds

<BatchDataset shapes: (?, 3), types: tf.int32>

最も簡単な fix は次のように tf.contrib.data.batch_and_drop_remainderapply することです :

>>> params = {'batch_size':32}
>>> ds = tf.data.Dataset.from_tensors([0, 1, 2])
>>> ds = ds.repeat().apply(
...     tf.contrib.data.batch_and_drop_remainder(params['batch-size']))
>>> ds

 <_RestructuredDataset shapes: (32, 3), types: tf.int32>

このアプローチの一つの欠点は、その名前が意味するように、このバッチ処理 method は dataset の最後で任意の端数のバッチを投げ捨てます。これは訓練のために使用される dataset を無限に反復するためには良いのですが、もし貴方が正確な数のエポックのために訓練することを望む場合には問題になるかもしれません。正確に 1-エポックの評価を遂行するためには貴方自身の tf.metrics を作成するときにバッチの長さを手動でパディングし、パディング要素をゼロ重みを持つように設定することによりこれを回避することができます。

 

Dataset

Cloud TPU を使用するとき tf.data.Dataset API の効率的な使用は重要です、何故ならばデータを十分に迅速に供給できない場合には Cloud TPU を使用することは不可能だからです。dataset パフォーマンスの詳細については Input Pipeline Performance Guide を見てください。

(tf.data.Dataset.from_tensor_slice か他の in-graph データを使用する) 最も単純な実験を別にすれば TPUEstimator の Dataset により読まれるデータファイルの総ては Google Cloud Storage Bucket にストアする必要があるでしょう。

殆どのユースケースについて、貴方のデータを TFRecord フォーマットに変換してそれを読むために tf.data.TFRecordDataset を使用することを推奨します。けれどもこれは厳しい要件ではなくて好ましいのであれば他の dataset リーダー (FixedLengthRecordDataset か TextLineDataset) を使用できます。

tf.data.Dataset.cache を使用して小さい dataset は全体をメモリにロードすることができます。

使用されるデータ・フォーマットにかかわらず、100 MB のオーダー上の 巨大なファイルを使用する ことが強く推奨されます。これはこのネットワーク化された設定において特に重要です、というのはファイルを開くオーバーヘッドが本質的により高いからです。

使用されるリーダーのタイプにかかわらず、コンストラクタへの buffer_size 引数を使用してバッファリングを有効にすることもまた重要です。この引数はバイトで指定されます。必要なときにデータが利用可能であるように最小限 2, 3 MB (buffer_size=8*1024*1024) が推奨されます。

TPU-demos レポジトリは imagenet データセットをダウンロードしてそれを適切なフォーマットに変換するための スクリプト (訳注: リンク切れ) を含みます。これはレポジトリに含まれる imagenet モデル (訳注: リンク切れ) と一緒にこれらのベストプラクティスを示します。

 
以上



TensorFlow : Guide : Accelerators : GPU を利用する

TensorFlow : Guide : Accelerators : GPU を利用する (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
更新日時 : 07/14/2018
作成日時 : 04/21/2018

* TensorFlow 1.9 でドキュメント構成が変わりましたので調整しました。
* GPU の利用方法については、以前は Tutorials に含まれていましたが、内容が改定されて Programmer’s Guide に移されましたので再翻訳致しました。
* 本ページは、TensorFlow 本家サイトの Guide – Accelerators – Using GPUs を翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、適宜、追加改変している場合もあります。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

サポートされるデバイス

典型的なシステムでは、複数の計算デバイスがあります。TensorFlow では、サポートされるデバイスのタイプは CPU と GPU です。これらは文字列として表されます。例えば :

“/cpu:0”: 貴方のマシンの CPU。
“/gpu:0”: 貴方のマシンの GPU、もし一つあれば。
“/gpu:1”: 貴方のマシンの2つ目の GPU、etc.

もし TensorFlow 演算が CPU と GPU 両方の実装を持つならば、演算がデバイスに割り当てられる時 GPU デバイスに優先順位が与えられます。例えば、matmul は CPU と GPU kernel を持ちます。cpu:0 と gpu:0 を持つシステム上、matmul を実行するために gpu:0 が選択されます。

 

デバイス割り当てをロギングする

どのデバイスが演算とテンソルに割り当てられたかを見つけ出すためには、セッションを log_device_placement 構成オプションを True にして作成します。

# Creates a graph.
a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
c = tf.matmul(a, b)
# Creates a session with log_device_placement set to True.
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
# Runs the op.
print(sess.run(c))

次のような出力が見れるはずです :

Device mapping:
/job:localhost/replica:0/task:0/device:GPU:0 -> device: 0, name: Tesla K40c, pci bus
id: 0000:05:00.0
b: /job:localhost/replica:0/task:0/device:GPU:0
a: /job:localhost/replica:0/task:0/device:GPU:0
MatMul: /job:localhost/replica:0/task:0/device:GPU:0
[[ 22.  28.]
 [ 49.  64.]]

 

手動のデバイス割り当て

もし貴方が特定の演算を自動的に選択されたものの代わりに貴方の選択したデバイス上で実行させたいのであれば、コンテキスト内で全ての演算が同じデバイス割り当てを持つようなデバイスコンテキストを作成するために tf.device が使用できます。

# Creates a graph.
with tf.device('/cpu:0'):
  a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
  b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
c = tf.matmul(a, b)
# Creates a session with log_device_placement set to True.
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
# Runs the op.
print(sess.run(c))

今 a と b が cpu:0 に割り当てられたことが見れるでしょう。MatMul 演算についてはデバイスが明示的に指定されていませんので、TensorFlow ランタイムはその演算と利用可能なデバイス (この例では gpu:0) をベースに一つを選択して必要であればデバイス間で自動的に tensor をコピーするでしょう。

Device mapping:
/job:localhost/replica:0/task:0/device:GPU:0 -> device: 0, name: Tesla K40c, pci bus
id: 0000:05:00.0
b: /job:localhost/replica:0/task:0/cpu:0
a: /job:localhost/replica:0/task:0/cpu:0
MatMul: /job:localhost/replica:0/task:0/device:GPU:0
[[ 22.  28.]
 [ 49.  64.]]

 

GPU メモリの増大を可能にする

デフォルトでは、(CUDA_VISIBLE_DEVICES 下の) 総ての可視の GPU の GPU メモリの殆ど総てをプロセスにマップします。これは メモリ断片化 を削減してデバイスの比較的貴重な GPU メモリ・リソースをより効率的に使用するために遂行されます。

ある場合にはプロセスにとって利用可能なメモリのサブセットを割り当てるだけ、またはプロセスにより必要とされるに従いメモリ消費量を増大するだけが望ましいです。TensorFlow はこれを制御するために Session 上の2つの Config オプションを提供します。

最初は allow_growth オプションで、これはランタイム割り当てをベースにした GPU メモリだけを割り当てることを試みます : それは非常に少ないメモリを割り当てることから初めて、Session が実行されてより多くの GPU メモリが必要になるとき、TensorFlow プロセスにより必要とされる GPU メモリ領域を拡張します。メモリを解放しないことに注意してください、何故ならばそれは更に悪いメモリ断片化にさえ繋がるからです。このオプションを有効にするには、次によって ConfigProto のオプションを設定します :

config = tf.ConfigProto()
config.gpu_options.allow_growth = True
session = tf.Session(config=config, ...)

2番目の方法は per_process_gpu_memory_fraction で、これは各可視の GPU が割り当てられるべきメモリの全体的な総量の分数を決定します。例えば、次によって TensorFlow に合計メモリの 40 % だけを割り当てることを伝えることができます :

config = tf.ConfigProto()
config.gpu_options.per_process_gpu_memory_fraction = 0.4
session = tf.Session(config=config, ...)

TensorFlow プロセスで利用可能な GPU メモリの総量を真に縛ることを望む場合にこれは有用です。

 

マルチ-GPU システムで単一の GPU を使う

システムに1つ以上の GPU を持つならば、最小の ID を持つ GPU がデフォルトで選択されるでしょう。異なる GPU 上で実行したいのであれば、明示的に好みの選択を指定する必要があります :

# Creates a graph.
with tf.device('/device:GPU:2'):
  a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
  b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
  c = tf.matmul(a, b)
# Creates a session with log_device_placement set to True.
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
# Runs the op.
print(sess.run(c))

もし貴方が指定したデバイスが存在しないのであれば、InvalidArgumentError を得るでしょう :

InvalidArgumentError: Invalid argument: Cannot assign a device to node 'b':
Could not satisfy explicit device specification '/device:GPU:2'
   [[Node: b = Const[dtype=DT_FLOAT, value=Tensor, _device="/device:GPU:2"]()]]

もし貴方が TensorFlow に指定した一つが存在しない場合に演算を実行させるために存在しサポートされているデバイスを自動的に選択させたいのであれば、セッションを作成時の構成オプションにおいて allow_soft_placement を True に設定することができます。

# Creates a graph.
with tf.device('/device:GPU:2'):
  a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
  b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
  c = tf.matmul(a, b)
# Creates a session with allow_soft_placement and log_device_placement set
# to True.
sess = tf.Session(config=tf.ConfigProto(
      allow_soft_placement=True, log_device_placement=True))
# Runs the op.
print(sess.run(c))

 

複数の GPU を使用する

もし貴方が TensorFlow を複数の GPU 上で実行したいのであれば、各タワーが異なる GPU に割り当てられる、マルチ・タワー流儀でモデルを構築することができます。例えば :

# Creates a graph.
c = []
for d in ['/device:GPU:2', '/device:GPU:3']:
  with tf.device(d):
    a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3])
    b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2])
    c.append(tf.matmul(a, b))
with tf.device('/cpu:0'):
  sum = tf.add_n(c)
# Creates a session with log_device_placement set to True.
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
# Runs the op.
print(sess.run(sum))

貴方は次のような出力を見るでしょう。

Device mapping:
/job:localhost/replica:0/task:0/device:GPU:0 -> device: 0, name: Tesla K20m, pci bus
id: 0000:02:00.0
/job:localhost/replica:0/task:0/device:GPU:1 -> device: 1, name: Tesla K20m, pci bus
id: 0000:03:00.0
/job:localhost/replica:0/task:0/device:GPU:2 -> device: 2, name: Tesla K20m, pci bus
id: 0000:83:00.0
/job:localhost/replica:0/task:0/device:GPU:3 -> device: 3, name: Tesla K20m, pci bus
id: 0000:84:00.0
Const_3: /job:localhost/replica:0/task:0/device:GPU:3
Const_2: /job:localhost/replica:0/task:0/device:GPU:3
MatMul_1: /job:localhost/replica:0/task:0/device:GPU:3
Const_1: /job:localhost/replica:0/task:0/device:GPU:2
Const: /job:localhost/replica:0/task:0/device:GPU:2
MatMul: /job:localhost/replica:0/task:0/device:GPU:2
AddN: /job:localhost/replica:0/task:0/cpu:0
[[  44.   56.]
 [  98.  128.]]

cifar 10 チュートリアル は複数の GPU でどのように訓練するかを示す良い例です。

 
以上


TensorFlow : Guide : 低位 API : イントロダクション

TensorFlow : Guide : 低位 API – イントロダクション (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
更新日時 : 07/14/2018
作成日時 : 04/20/2018

* TensorFlow 1.9 でドキュメント構成が変わりましたので調整しました。
* 本ページは、TensorFlow 本家サイトの Guide – Low Level APIs – Introduction を翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、適宜、追加改変している場合もあります。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

本文

このガイドは貴方が低位 TensorFlow API (TensorFlow Core) のプログラミングを始めるにあたり、以下をどのように遂行するかを示します :

  • 貴方自身の TensorFlow プログラム (tf.Graph) と TensorFlow ランタイム (tf.Session) を管理します。Estimator にそれらを管理することを依拠する代わりにです。
  • tf.Session を使用して TensorFlow 演算を実行します。
  • この低位環境内で高位レベル・コンポーネント (dataset, layer, と feature_columns) を使用します。
  • Estimator により提供される ものを使用する代わりに、貴方自身の訓練ループを構築します。

可能なときにはより高位な API を使用してモデルを構築することを推奨します。TensorFlow Core を知ることは次の理由で大切です :

  • 低位 TensorFlow 演算を直接的に使用可能なとき実験とデバッギングは両者ともよりストレートフォワードです。
  • それは、より高位な API を使用するとき物事が内部的にどのように動作するかのメンタルモデルを貴方に与えます。

 

セットアップ

このガイドを最大限に活用するためには、次を知るべきです :

  • Python でどのようにプログラムするか。
  • 少なくとも配列について少々。
  • 理想的には、機械学習について何某か。

気軽に Python を起動してこのウォークスルーに沿ってフォローしてください。貴方の Python 環境をセットアップするためには次の行を実行してください :

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np
import tensorflow as tf

 

Tensor 値

TensorFlow のデータの中心的なユニットは tensor です。tensor は任意の数の次元の配列に shape されたプリミティブ値のセットから成ります。tensor の rank はその次元数で、その一方でその shape は各次元に沿った配列の長さを指定する整数のタプルです。tensor 値の幾つかのサンプルがあります :

3. # a rank 0 tensor; a scalar with shape [],
[1., 2., 3.] # a rank 1 tensor; a vector with shape [3]
[[1., 2., 3.], [4., 5., 6.]] # a rank 2 tensor; a matrix with shape [2, 3]
[[[1., 2., 3.]], [[7., 8., 9.]]] # a rank 3 tensor with shape [2, 1, 3]

TensorFlow は tensor 値を表わすために numpy 配列を使用しています。

 

TensorFlow Core ウォークスルー

貴方は TensorFlow Core プログラムを2つの具体的なセクションから成るものと考えて良いかもしれません :

  1. 計算グラフを構築する ( tf.Graph )。
  2. 計算グラフを実行する ( tf.Session )。

 

グラフ

計算グラフはグラフに配置された TensorFlow 演算のシリーズです。グラフは2つのタイプのオブジェクトから成ります。

  • 演算 (または “ops”): グラフのノード。演算は tensor を消費して生成する計算を記述します。
  • Tensor: グラフのエッジです。これらはグラフを通して流れるであろう値を表します。殆どの TensorFlow 関数は tf.Tensor を返します。

Important: tf.Tensor は値を持ちません、それらは計算グラフの要素への単なるハンドルです。

単純な計算グラフを構築しましょう。最も基本的な演算は constant です。演算を構築する Python 関数は入力として tensor 値を取ります。結果としての演算は入力を取りません。実行するとき、それはコンストラクタに渡された値を出力します。次のように2つの浮動小数点 constant a と b を作成できます :

a = tf.constant(3.0, dtype=tf.float32)
b = tf.constant(4.0) # also tf.float32 implicitly
total = a + b
print(a)
print(b)
print(total)

print ステートメントは以下を生成します :

Tensor("Const:0", shape=(), dtype=float32)
Tensor("Const_1:0", shape=(), dtype=float32)
Tensor("add:0", shape=(), dtype=float32)

tensor の print は値 3.0, 4.0 と 7.0 を貴方が期待するようには出力しないことに気がつくでしょう。上のステートメントは計算グラフを構築するだけです。これらの tf.Tensor オブジェクトは実行される演算の結果を表わすだけです。

グラフの各演算には一意の名前が与えられます。この名前は Python でオブジェクトに割り当てられる名前からは独立です。tensor はそれらを生成する演算に由来して命名され出力 index が続きます、上の “add:0” のように。

 

TensorBoard

TensorFlow は TensorBoard と呼ばれるユティリティを提供します。TensorBoard の多くの機能の一つは計算グラフを可視化することです。2, 3 の単純なコマンドでこれを容易に行なうことができます。

最初に次のように計算グラフを TensorBoard summary ファイルにセーブします :

writer = tf.summary.FileWriter('.')
writer.add_graph(tf.get_default_graph())

これは現在のディレクトリで次のフォーマットの名前を持つイベントファイルを生成するでしょう :

events.out.tfevents.{timestamp}.{hostname}

今、新しい端末で、次のシェル・コマンドで TensorBaord を起動します :

tensorboard --logdir .

それからブラウザで TensorBoard の グラフ・ページ (http://localhost:6006/#graphs) をオープンすれば、次に類似したグラフを見るはずです :

TensorBoard のグラフ可視化ツールについての詳細は TensorBoard: グラフ視覚化 を見てください。

 

Session

tensor を評価するために、tf.Session オブジェクトをインスタンス化します、非公式には session として知られています。session は TensorFlow ランタイムの状態をカプセル化して、TensorFlow 演算を実行します。tf.Graph が .py ファイルのようなものであれば、tf.Session は python executable のようなものです。

次のコードは tf.Session オブジェクトを作成してから上で作成した total tensor を評価するためにその run メソッドを起動します :

sess = tf.Session()
print(sess.run(total))

Session.run でノードの出力を要求するとき、TensorFlow はグラフを通してバックトラックして要求された出力ノードに入力を提供する総てのノードを実行します。従って、これは 7.0 の期待された値をプリントします :

7.0

複数の tensor を tf.Session.run に渡すこともできます。次のサンプルのように、run メソッドはタプルか辞書の任意の組み合わせを透過的に処理します :

print(sess.run({'ab':(a, b), 'total':total}))

これは同じレイアウトの構造で結果を返します :

{'total': 7.0, 'ab': (3.0, 4.0)}

tf.Session.run への呼び出しの間、任意の tf.Tensor は単一の値を持つだけです。例えば、次のコードは tf.Tensor を生成するために tf.random_uniform を呼び出します、これはランダムな ( [0, 1) の値を持つ) 3-要素ベクトルを生成します :

vec = tf.random_uniform(shape=(3,))
out1 = vec + 1
out2 = vec + 2
print(sess.run(vec))
print(sess.run(vec))
print(sess.run((out1, out2)))

結果は run の各呼び出しで異なるランダムな値を示しますが、単一の run の間には一貫した値です (out1 と out2 は同じランダム入力を受け取ります) :

[ 0.52917576  0.64076328  0.68353939]
[ 0.66192627  0.89126778  0.06254101]
(
  array([ 1.88408756,  1.87149239,  1.84057522], dtype=float32),
  array([ 2.88408756,  2.87149239,  2.84057522], dtype=float32)
)

幾つかの TensorFlow 関数は tf.Tensor の代わりに tf.Operation を返します。演算上の run の呼び出しの結果は None です。値を取得するためではなく、副作用を引き起こすために演算を実行します。このサンプルは初期化、そして後で示される訓練 ops を含みます。

 

Feeding

そのままでは、グラフは特に面白くはありません、何故ならばそれは常に結果 constant を生成するからです。グラフは placeholder として知られる外部入力を受け取ってパラメータ化されます。placeholder は関数引数のように、後で値を提供するという約束です。

x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
z = x + y

前の3つの行は少しばかり関数のようで、そこでは2つの入力パラメータ (x と y) を定義してからそれらの上での演算を定義しています。このグラフを、placeholder に具体的な値を供給するために run メソッド の feed_dict 引数を使用して複数の入力で評価できます :

print(sess.run(z, feed_dict={x: 3, y: 4.5}))
print(sess.run(z, feed_dict={x: [1, 3], y: [2, 4]}))

これは次の出力の結果になります :

7.5
[ 3.  7.]

feed_dict 引数はグラフの任意の tensor を上書きするためにも使用できます。placeholder と他の tf.Tensor の唯一の違いはそれらに値が供給されない場合には placeholder はエラーを投げることです。

 

Dataset

placeholder は単純な実験のために動作しますが、Dataset はデータをモデルにストリームする好ましい方法です。

Dataset から実行可能な tf.Tensor を得るためには最初にそれを tf.data.Iterator に変換して、それから Iterator の get_next メソッドを呼びださなければなりません。

Iterator を作成する最も単純な方法は make_one_shot_iterator メソッドによります。例えば、次のコードで next_item tensor は各 run 呼び出しで my_data 配列からの row を返します :

my_data = [
    [0, 1,],
    [2, 3,],
    [4, 5,],
    [6, 7,],
]
slices = tf.data.Dataset.from_tensor_slices(my_data)
next_item = slices.make_one_shot_iterator().get_next()

データ・ストリームの最後に達すると Dataset に OutOfRangeError を投げさせます。例えば、次のコードは読むためのデータがなくなるまで next_item を読みます :

while True:
  try:
    print(sess.run(next_item))
  except tf.errors.OutOfRangeError:
    break

Dataset がステートフル演算に依拠する場合にはそれを使用する前に iterator を初期化する必要があるかもしれません、下で示されるように :

r = tf.random_normal([10,3])
dataset = tf.data.Dataset.from_tensor_slices(r)
iterator = dataset.make_initializable_iterator()
next_row = iterator.get_next()

sess.run(iterator.initializer)
while True:
  try:
    print(sess.run(next_row))
  except tf.errors.OutOfRangeError:
    break

Dataset と Iterator のより詳細のためには データをインポートする を見てください。

 

訓練可能なモデルは同じ入力で新しい出力を得るためにグラフで値を変更しなければなりません。 はグラフに訓練可能なパラメータを追加するための好ましい方法です。

層は変数とその上で作用する演算の両者を一緒にパッケージ化します。例えば 密結合層 は各出力に対して総ての入力に渡る重み付けられた総計を遂行してオプションの 活性化関数 を適用します。接続重みとバイアスは層オブジェクトにより管理されます。

 

層を作成する

次のコードは Dense 層を作成します、これは入力ベクトルのバッチを取り、そしてそれぞれのために単一の出力値を生成します。入力に層を適用するには、層をそれが関数であるかのように呼び出します。例えば :

x = tf.placeholder(tf.float32, shape=[None, 3])
linear_model = tf.layers.Dense(units=1)
y = linear_model(x)

層はその内部変数のサイズを決定するために入力を調査します。そのためここで層が正しいサイズの重み行列を構築できるように x placeholder の shape を設定しなければなりません出力, y の計算を定義した今、計算を実行する前にケアする必要がある一つの更なる詳細があります。

 

層を初期化する

層は、使用可能となる前に初期化されなければならない変数を含みます。変数を個々に初期化することが可能である一方で、次のように TensorFlow グラフの変数総てを簡単に初期化することが可能です :

init = tf.global_variables_initializer()
sess.run(init)

Important:
tf.global_variables_initializer の呼び出しは TensorFlow 演算へのハンドルを作成して返すだけです。その op はそれを tf.Session.run で実行するとき総てのグローバル変数を初期化するでしょう。global_variables_initializer は initializer が作成されたときにグラフに存在した変数を初期化するだけであることにも注意してください。そのため initializer はグラフ構築の間に追加される最後のものの一つであるべきです。

 

層を実行する

層が初期化された今、linear_model の出力 tensor を (任意の他の tensor で行えるように) 評価することができます。例えば、次のコードは :

print(sess.run(y, {x: [[1, 2, 3],[4, 5, 6]]}))

次のような 2-要素出力ベクトルを生成するでしょう :

[[-3.41378999]
 [-9.14999008]]

 

層関数ショートカット

(tf.layers.Dense のような) 各層クラスの各々について TensorFlow はまた (tf.layers.dense のような) ショートカット関数もまた提供します。唯一の違いはショートカット関数バージョンは単一の呼び出しで層を作成して実行します。例えば、次のコードは前のバージョンと同値です :

x = tf.placeholder(tf.float32, shape=[None, 3])
y = tf.layers.dense(x, units=1)

init = tf.global_variables_initializer()
sess.run(init)

print(sess.run(y, {x: [[1, 2, 3], [4, 5, 6]]}))

便利である一方で、このアプローチは tf.layers.Layer オブジェクトへのアクセスは許容されません。これは内省とデバッギングをより困難にして、層の再利用は不可能です。

 

特徴カラム

特徴カラムを伴う実験を行なう最も簡単な方法は tf.feature_column.input_layer 関数を使用することです。この関数は入力として dense カラム を受け取るだけですので、カテゴリカル・カラムの結果を見るためにはそれを tf.feature_column.indicator_column にラップしなければなりません。例えば :

features = {
    'sales' : [[5], [10], [8], [9]],
    'department': ['sports', 'sports', 'gardening', 'gardening']}

department_column = tf.feature_column.categorical_column_with_vocabulary_list(
        'department', ['sports', 'gardening'])
department_column = tf.feature_column.indicator_column(department_column)

columns = [
    tf.feature_column.numeric_column('sales'),
    department_column
]

inputs = tf.feature_column.input_layer(features, columns)

入力 tensor の実行は特徴をベクトルのバッチへと解析します。

特徴カラムは層のように内部状態を持つことができますので、それらはしばしば初期化を必要とします。カテゴリカル・カラムは 検索テーブル を内部的に使用してそれらは別の初期化 op, tf.tables_initializer を必要とします。

var_init = tf.global_variables_initializer()
table_init = tf.tables_initializer()
sess = tf.Session()
sess.run((var_init, table_init))

ひとたび内部状態が初期化されれば任意の他の tf.Tensor のように inputs を実行できます :

print(sess.run(inputs))

これは特徴カラムが入力ベクトルをどのようにパックしたかを示し、最初の2つのインデックスとして one-hot “department” を3番目として “sales” を持ちます。

[[  1.   0.   5.]
 [  1.   0.  10.]
 [  0.   1.   8.]
 [  0.   1.   9.]]

 

訓練

core TensorFlow の基本に慣れた今、小さな回帰モデルを手動で訓練してみましょう。

 

データを定義する

最初にある入力, x と個々の入力のための期待される出力, y_true を定義しましょう :

x = tf.constant([[1], [2], [3], [4]], dtype=tf.float32)
y_true = tf.constant([[0], [-1], [-2], [-3]], dtype=tf.float32)

 

モデルを定義する

次に、1 出力を持つ、単純な線形モデルを構築します :

linear_model = tf.layers.Dense(units=1)

y_pred = linear_model(x)

次のように予測を評価できます :

sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)

print(sess.run(y_pred))

モデルはまだ訓練されていませんので、4 つの「予測」値は非常に良くはありません。ここに私達が得たものがあります ; 貴方自身の出力は殆ど確実に異なるでしょう :

[[ 0.02631879]
 [ 0.05263758]
 [ 0.07895637]
 [ 0.10527515]]

 

損失

モデルを最適化するためには、最初に損失を定義する必要があります。mean square error, 回帰問題に対する標準的な損失を使用します。

より低位の数学演算でこれを手動で行える一方で、tf.losses モジュールは一般的な損失関数のセットを提供します。mean square error を計算するために次のようにそれを使用できます :

loss = tf.losses.mean_squared_error(labels=y_true, predictions=y_pred)

print(sess.run(loss))

これは次のような、損失値を生成します :

2.23962

 

訓練

TensorFlow は標準的な最適化アルゴリズムを実装する optimizer を提供します。これらは tf.train.Optimizer のサブクラスとして実装されています。それらは損失を最小化するために各変数を徐々に変更します。最も単純な最適化アルゴリズムは tf.train.GradientDescentOptimizer により実装される、勾配降下 です。それはその変数に関する損失の導関数の大きさに従って各変数を変更します。例えば :

optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)

このコードは最適化に必要なグラフ・コンポーネントを構築し、そして訓練演算を返します。実行時、訓練 op はグラフの変数を更新するでしょう。貴方はそれを次のように実行できます :

for i in range(100):
  _, loss_value = sess.run((train, loss))
  print(loss_value)

訓練は op で、tensor ではありませんから、実行時にそれは値を返しません。訓練の間に損失の進捗を見るために、同時に loss tensor を実行して、次のように出力を生成します :

1.35659
1.00412
0.759167
0.588829
0.470264
0.387626
0.329918
0.289511
0.261112
0.241046
...

 

完全なプログラム

x = tf.constant([[1], [2], [3], [4]], dtype=tf.float32)
y_true = tf.constant([[0], [-1], [-2], [-3]], dtype=tf.float32)

linear_model = tf.layers.Dense(units=1)

y_pred = linear_model(x)
loss = tf.losses.mean_squared_error(labels=y_true, predictions=y_pred)

optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)

init = tf.global_variables_initializer()

sess = tf.Session()
sess.run(init)
for i in range(100):
  _, loss_value = sess.run((train, loss))
  print(loss_value)

print(sess.run(y_pred))
 

以上



TensorFlow : Guide : 高位 API : データをインポートする

TensorFlow : Guide : 高位 API : データをインポートする (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
更新日時 : 4/10/2019 (1.13.1); 10/30 (1.11.0), 07/13/2018 (1.9.0)
作成日時 : 04/16/2018

* TensorFlow 1.9 でドキュメント構成が変わりましたので調整しました。
* 本ページでは tf.data モジュールによるデータのインポートについて説明されています。
* 本ページは、TensorFlow 本家サイトの Programmer’s Guide – Importing Data を翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、適宜、追加改変している場合もあります。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

本文

tf.data API は、単純で、再利用可能なピースから複雑な入力パイプラインを構築することを可能にします。例えば、画像モデルのためのパイプラインは分散ファイルシステムのファイルからデータを集め、各画像にランダムな摂動 (= perturbation、ずれ) を適用し、そしてランダムに選択された画像を訓練のためのバッチにマージするかもしません。テキスト・モデルのためのパイプラインは生テキスト・データからシンボルを抽出し、それらを検索テーブルを持つ埋め込み認識装置へと変換し、そして異なる長さのシークエンスをまとめてバッチ処理することを伴うかもしれません。tf.data API は巨大な総量のデータ、異なるデータフォーマット、そして複雑な変換を処理することを容易にします。

tf.data API は TensorFlow に2つの抽象を導入します :

  • tf.data.Dataset は要素のシークエンスを表わし、そこでは各要素は一つまたはそれ以上の Tensor オブジェクトを含みます。例えば画像パイプラインでは、(一つの) 要素は画像データとラベルを表わす Tensor のペアを持つ単一の訓練サンプルかもしれません。データセットを作成するために2つの異なる方法があります :
    • ソース (e.g. Dataset.from_tensor_slices()) の作成は一つまたそれ以上の tf.Tensor オブジェクトからデータセットを構築します。
    • 変換 (= transformation) (e.g. Dataset.batch()) の適用は一つまたはそれ以上の tf.data.Dataset オブジェクトから dataset を構築します。
  • tf.data.Iterator はデータセットから要素を抽出するための主要な方法を提供します。Iterator.get_next() で返される演算は実行されるときにデータセットの次の要素を生成し、典型的には入力パイプライン・コードと貴方のモデルの間のインターフェイスとして動作します。最も単純な iterator は “one-shot iterator” で、これは特定のデータセットに関連してそれを通して一度だけ iterate します。より洗練された利用のためには、Iterator.initializer 演算は異なるデータセットを持つ iterator を再初期化してパラメータ化します、その結果、例えば、同じプログラム内で訓練と検証データに渡り複数回 iterate できます。

 

基本メカニズム

(Programmer’s) ガイドのこのセクションは異なる種類の Dataset と Iterator オブジェクトの作成の基本と、それらからどのようにしてデータを抽出するかについて記述します。

入力パイプラインを開始するためには、ソースを定義しなければなりません。例えば、メモリ内の何某かの Tensor から Dataset を構築するために、tf.data.Dataset.from_tensors() か tf.data.Dataset.from_tensor_slices() を利用できます。あるいは、入力データが推奨される TFRecord フォーマットでディスク上にあるのであれば、tf.data.TFRecordDataset を構築できます。

Dataset オブジェクトをひとたび持てば、tf.data.Dataset オブジェクト上のメソッド呼び出しをチェインしてそれを新しい Dataset に変換することができます。例えば、(各要素に関数を適用するための) Dataset.map() のような要素毎の変換、そして Dataset.batch() のような複数要素変換を適用することができます。変換の完全なリストのためには tf.data.Dataset のドキュメントを見てください。

Dataset から値を消費する最も一般的な方法は、(例えば、Dataset.make_one_shot_iterator() を呼び出すことで) 一度にデータセットの一つの要素へのアクセスを提供する iterator オブジェクトを作成することです。tf.data.Iterator は2つの演算を提供します : Iterator.initializer, これは iterator の状態を (再) 初期化することを可能にします ; そして Iterator.get_next(), これは記号的に次の要素に相当する tf.Tensor オブジェクトを返します。貴方のユースケースに依拠して、異なるタイプの iterator を選択できます、そしてそのオプションは下で概説されます。

 

Dataset 構造

dataset は各々が同じ構造を持つ要素から成ります。(一つの) 要素は、コンポーネントと呼ばれる一つまたはそれ以上の tf.Tensor オブジェクトから成ります。各コンポーネントは要素の型を tensor で表わす tf.DType と、各要素の (多分部分的に指定された) 静的 shape を表わす tf.TensorShape を持ちます。Dataset.output_types と Dataset.output_shapes プロパティは dataset 要素の各コンポーネントの推論型と shape を探究することを可能にします。これらのプロパティのネストされた構造は要素の構造へとマップされ、これは単一の tensor、tensor のタプル, または tensor のネストされたタプルが取れます。例えば :

dataset1 = tf.data.Dataset.from_tensor_slices(tf.random_uniform([4, 10]))
print(dataset1.output_types)  # ==> "tf.float32"
print(dataset1.output_shapes)  # ==> "(10,)"

dataset2 = tf.data.Dataset.from_tensor_slices(
   (tf.random_uniform([4]),
    tf.random_uniform([4, 100], maxval=100, dtype=tf.int32)))
print(dataset2.output_types)  # ==> "(tf.float32, tf.int32)"
print(dataset2.output_shapes)  # ==> "((), (100,))"

dataset3 = tf.data.Dataset.zip((dataset1, dataset2))
print(dataset3.output_types)  # ==> (tf.float32, (tf.float32, tf.int32))
print(dataset3.output_shapes)  # ==> "(10, ((), (100,)))"

要素の各コンポーネントに名前を与えることはしばしば便利です、例えばそれらが訓練サンプルの異なる特徴を表わす場合です。タプルに加えて、Dataset の単一の要素を表わすために collections.namedtuple か文字列を tensor にマップする辞書を使用することができます。

dataset = tf.data.Dataset.from_tensor_slices(
   {"a": tf.random_uniform([4]),
    "b": tf.random_uniform([4, 100], maxval=100, dtype=tf.int32)})
print(dataset.output_types)  # ==> "{'a': tf.float32, 'b': tf.int32}"
print(dataset.output_shapes)  # ==> "{'a': (), 'b': (100,)}"

Dataset 変換は任意の構造の dataset をサポートします。各要素に関数を適用する、Dataset.map(), Dataset.flat_map(), と Dataset.filter() 変換を使用するとき、要素構造は関数の引数を決定します :

dataset1 = dataset1.map(lambda x: ...)

dataset2 = dataset2.flat_map(lambda x, y: ...)

# Note: Argument destructuring is not available in Python 3.
dataset3 = dataset3.filter(lambda x, (y, z): ...)

 

iterator を作成する

ひとたび貴方の入力データを表わす Dataset を構築したのであれば、次のステップはその dataset から要素にアクセスするための Iterator を作成することです。tf.data API は現在次の iterator をサポートします、洗練レベルを上げながら :

  • one-shot,
  • initializable,
  • reinitializable, そして
  • feedable.

one-shot iterator は iterator の最も単純な形式で、これは dataset を通して一度だけ iterate することをサポートするだけで、明示的な初期化は必要ありません。one-shot iterator は既存の queue-ベースの入力パイプラインがサポートする殆ど総てのケースを扱いますが、それらはパラメータ化はサポートしません。Dataset.range() のサンプルを使用しています :

dataset = tf.data.Dataset.range(100)
iterator = dataset.make_one_shot_iterator()
next_element = iterator.get_next()

for i in range(100):
  value = sess.run(next_element)
  assert i == value

Note: 現在、one-shot iterator は Estimator と共に簡単に利用可能な唯一のタイプです。

initializable iterator はそれを使用する前に明示的な iterator.initializer を実行することを要求します。不便さと引き換えに、それは (iterator を初期化するときに供給可能な) 一つまたはそれ以上の tf.placeholder() tensor を使用して dataset の定義をパラメータ化します。Dataset.range() サンプルで続けます :

max_value = tf.placeholder(tf.int64, shape=[])
dataset = tf.data.Dataset.range(max_value)
iterator = dataset.make_initializable_iterator()
next_element = iterator.get_next()

# Initialize an iterator over a dataset with 10 elements.
sess.run(iterator.initializer, feed_dict={max_value: 10})
for i in range(10):
  value = sess.run(next_element)
  assert i == value

# Initialize the same iterator over a dataset with 100 elements.
sess.run(iterator.initializer, feed_dict={max_value: 100})
for i in range(100):
  value = sess.run(next_element)
  assert i == value

reinitializable iterator は複数の異なる Dataset オブジェクトか初期化できます。例えば、貴方は、一般化を改善するために入力画像にランダム摂動を使用する訓練入力パイプラインと変更されていないデータ上の予測を評価する検証入力パイプラインを持つかもしれません。これらのパイプラインは典型的には同じ構造を持つ (i.e. 各コンポーネントに対して同じタイプと互換な shape) 異なる Dataset オブジェクトを使用するでしょう。

# Define training and validation datasets with the same structure.
training_dataset = tf.data.Dataset.range(100).map(
    lambda x: x + tf.random_uniform([], -10, 10, tf.int64))
validation_dataset = tf.data.Dataset.range(50)

# A reinitializable iterator is defined by its structure. We could use the
# `output_types` and `output_shapes` properties of either `training_dataset`
# or `validation_dataset` here, because they are compatible.
iterator = tf.data.Iterator.from_structure(training_dataset.output_types,
                                           training_dataset.output_shapes)
next_element = iterator.get_next()

training_init_op = iterator.make_initializer(training_dataset)
validation_init_op = iterator.make_initializer(validation_dataset)

# Run 20 epochs in which the training dataset is traversed, followed by the
# validation dataset.
for _ in range(20):
  # Initialize an iterator over the training dataset.
  sess.run(training_init_op)
  for _ in range(100):
    sess.run(next_element)

  # Initialize an iterator over the validation dataset.
  sess.run(validation_init_op)
  for _ in range(50):
    sess.run(next_element)

feedable iterator は、良く知られた feed_dict メカニズムを通して tf.Session.run への各呼び出しにおいてどの iterator を使用するかを選択するために tf.placeholder と一緒に利用可能です。それは reinitializable iterator と同じ機能を提供しますが、iterator 間で切り替えるときに dataset の最初から iterator を初期化することを要求しません。例えば、上からの同じ訓練と検証サンプルを使用して、2つの dataset 間で切り替えることを可能にする feedable iterator を定義するために tf.data.Iterator.from_string_handle を使用できます :

# Define training and validation datasets with the same structure.
training_dataset = tf.data.Dataset.range(100).map(
    lambda x: x + tf.random_uniform([], -10, 10, tf.int64)).repeat()
validation_dataset = tf.data.Dataset.range(50)

# A feedable iterator is defined by a handle placeholder and its structure. We
# could use the `output_types` and `output_shapes` properties of either
# `training_dataset` or `validation_dataset` here, because they have
# identical structure.
handle = tf.placeholder(tf.string, shape=[])
iterator = tf.data.Iterator.from_string_handle(
    handle, training_dataset.output_types, training_dataset.output_shapes)
next_element = iterator.get_next()

# You can use feedable iterators with a variety of different kinds of iterator
# (such as one-shot and initializable iterators).
training_iterator = training_dataset.make_one_shot_iterator()
validation_iterator = validation_dataset.make_initializable_iterator()

# The `Iterator.string_handle()` method returns a tensor that can be evaluated
# and used to feed the `handle` placeholder.
training_handle = sess.run(training_iterator.string_handle())
validation_handle = sess.run(validation_iterator.string_handle())

# Loop forever, alternating between training and validation.
while True:
  # Run 200 steps using the training dataset. Note that the training dataset is
  # infinite, and we resume from where we left off in the previous `while` loop
  # iteration.
  for _ in range(200):
    sess.run(next_element, feed_dict={handle: training_handle})

  # Run one pass over the validation dataset.
  sess.run(validation_iterator.initializer)
  for _ in range(50):
    sess.run(next_element, feed_dict={handle: validation_handle})

 

iterator からの値を消費する

Iterator.get_next() メソッドは iterator のシンボリックな次の要素に相当する一つまたはそれ以上の tf.Tensor オブジェクトを返します。これらの tensor が評価されるたびに、それらは基礎となる dataset の次の要素の値を取ります。(TensorFlow の他のステートフル・オブジェクトのように、Iterator.get_next() の呼び出しは iterator を直ちには進めません。代わりに貴方は TensorFlow 式の返された tf.Tensor オブジェクトを使用して、そして次の要素を取り iterator を進めるためにその式の結果を tf.Session.run() に渡さなければなりません。)

iterator が dataset の終わりに達した場合は、Iterator.get_next() 演算の実行は tf.errors.OutOfRangeError を上げるでしょう。このポイント以後、iterator は利用負荷の状態にあり、それを更に使用することを望む場合にはそれをサイド初期化しなければなりません。

dataset = tf.data.Dataset.range(5)
iterator = dataset.make_initializable_iterator()
next_element = iterator.get_next()

# Typically `result` will be the output of a model, or an optimizer's
# training operation.
result = tf.add(next_element, next_element)

sess.run(iterator.initializer)
print(sess.run(result))  # ==> "0"
print(sess.run(result))  # ==> "2"
print(sess.run(result))  # ==> "4"
print(sess.run(result))  # ==> "6"
print(sess.run(result))  # ==> "8"
try:
  sess.run(result)
except tf.errors.OutOfRangeError:
  print("End of dataset")  # ==> "End of dataset"

一般的なパターンは「訓練ループ」を try-except ブロックでラップします :

sess.run(iterator.initializer)
while True:
  try:
    sess.run(result)
  except tf.errors.OutOfRangeError:
    break

もし dataset の各要素がネストされた構造を持つのであれば、Iterator.get_next() の返し値は同じネストされた構造内の一つまたはそれ以上の tf.Tensor オブジェクトになるでしょう :

dataset1 = tf.data.Dataset.from_tensor_slices(tf.random_uniform([4, 10]))
dataset2 = tf.data.Dataset.from_tensor_slices((tf.random_uniform([4]), tf.random_uniform([4, 100])))
dataset3 = tf.data.Dataset.zip((dataset1, dataset2))

iterator = dataset3.make_initializable_iterator()

sess.run(iterator.initializer)
next1, (next2, next3) = iterator.get_next()

next1, next2, と next3 は (Iterator.get_next() で作成された) 同じ op/ノードにより生成された tensor であることに注意してください。従って、これらの tensor のどれかを評価すれば総てのコンポーネントのために iterator を進めるでしょう。iterator の典型的な消費者は単一の式で総てのコンポーネントを含むでしょう。

 

iterator 状態をセーブする

tf.contrib.data.make_saveable_from_iterator 関数は iterator からSaveableObject を作成します、これは iterator の (そして、効果的に、入力パイプライン全体の) 現在の状態をセーブしてリストアするために使用できます。こうして作成されたセーブ可能なオブジェクトは tf.Variable と同じ方法でセーブしてリストアするために tf.train.Saver 変数リストか tf.GraphKeys.SAVEABLE_OBJECT コレクションに追加されます。変数をどのようにセーブしてリストアするかについての詳細は セーブとリストア を参照してください。

# Create saveable object from iterator.
saveable = tf.contrib.data.make_saveable_from_iterator(iterator)

# Save the iterator state by adding it to the saveable objects collection.
tf.add_to_collection(tf.GraphKeys.SAVEABLE_OBJECTS, saveable)
saver = tf.train.Saver()

with tf.Session() as sess:

  if should_checkpoint:
    saver.save(path_to_checkpoint)

# Restore the iterator state.
with tf.Session() as sess:
  saver.restore(sess, path_to_checkpoint)

 

入力データを読む

NumPy 配列を消費する

貴方の入力データ総てがメモリにおさまる場合には、それらから Dataset を作成する最も単純な方法はそれらを tf.Tensor オブジェクトに変換して Dataset.from_tensor_slices() を使用することです。

# Load the training data into two NumPy arrays, for example using `np.load()`.
with np.load("/var/data/training_data.npy") as data:
  features = data["features"]
  labels = data["labels"]

# Assume that each row of `features` corresponds to the same row as `labels`.
assert features.shape[0] == labels.shape[0]

dataset = tf.data.Dataset.from_tensor_slices((features, labels))

上のコード・スニペットは features と labels 配列を TensorFlow グラフ内に tf.constant() 演算として埋め込むことに注意してください。これは小さなデータセットに対しては上手く動作しますが、メモリを浪費して — 何故ならば配列の内容が複数回コピーされるので — そして tf.GraphDef protocol buffer の 2 GB 制限に達するかもしれません。

代わりに、tf.placeholder() tensor の観点から Dataset を定義して、そして dataset に渡り Iterator を初期化するとき NumPy 配列を供給することができます。

# Load the training data into two NumPy arrays, for example using `np.load()`.
with np.load("/var/data/training_data.npy") as data:
  features = data["features"]
  labels = data["labels"]

# Assume that each row of `features` corresponds to the same row as `labels`.
assert features.shape[0] == labels.shape[0]

features_placeholder = tf.placeholder(features.dtype, features.shape)
labels_placeholder = tf.placeholder(labels.dtype, labels.shape)

dataset = tf.data.Dataset.from_tensor_slices((features_placeholder, labels_placeholder))
# [Other transformations on `dataset`...]
dataset = ...
iterator = dataset.make_initializable_iterator()

sess.run(iterator.initializer, feed_dict={features_placeholder: features,
                                          labels_placeholder: labels})

 

TFRecord データを消費する

tf.data API は、メモリにおさまらない巨大なデータセットを処理できるように様々なフォーマットをサポートしています。例えば、TFRecord ファイル・フォーマットは単純なレコード指向バイナリ・フォーマットで、多くの TensorFlow アプリケーションが訓練データのために使用します。tf.data.TFRecordDataset クラスは一つまたはそれ以上の TFRecord ファイルの内容に渡って入力パイプラインの一部として流れることを可能にします。

# Creates a dataset that reads all of the examples from two files.
filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)

TFRecordDataset initializer への filenames 引数は文字列、文字列のリスト、あるいは文字列の tf.Tensor のいずれかです。従って訓練と検証目的の2セットのファイルを持つ場合には、filenames を表わすために tf.placeholder(tf.string) を使用して適切な filenames から iterator を初期化することができます :

filenames = tf.placeholder(tf.string, shape=[None])
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...)  # Parse the record into tensors.
dataset = dataset.repeat()  # Repeat the input indefinitely.
dataset = dataset.batch(32)
iterator = dataset.make_initializable_iterator()

# You can feed the initializer with the appropriate filenames for the current
# phase of execution, e.g. training vs. validation.

# Initialize `iterator` with training data.
training_filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
sess.run(iterator.initializer, feed_dict={filenames: training_filenames})

# Initialize `iterator` with validation data.
validation_filenames = ["/var/data/validation1.tfrecord", ...]
sess.run(iterator.initializer, feed_dict={filenames: validation_filenames})

 

テキストデータを消費する

多くの dataset は一つまたはそれ以上のテキストファイルとして分散されます。tf.data.TextLineDataset は一つまたはそれ以上のテキストファイルから行を抽出するための簡単な方法を提供します。一つまたはそれ以上の filenames が与えられた場合、TextLineDataset はそれらのファイルの行毎の一つの文字列値の要素を生成するでしょう。TFRecordDataset のように、TextLineDataset は tf.Tensor として filenames を受け取り、tf.placeholder(tf.string) を渡すことによりそれをパラメータ化できます。

filenames = ["/var/data/file1.txt", "/var/data/file2.txt"]
dataset = tf.data.TextLineDataset(filenames)

デフォルトでは、TextLineDataset は各ファイルの総ての行を生成しますが、これは望ましくはないかもしれません、例えばファイルがヘッダ行で始まったり、コメントを含む場合です。これらの行は Dataset.skip() と Dataset.filter() 変換を使用して除去できます。これらの変換を各ファイルに別々に適用するために、各ファイルのためにネストされた Dataset を作成するために Dataset.flat_map() を使用します。

filenames = ["/var/data/file1.txt", "/var/data/file2.txt"]

dataset = tf.data.Dataset.from_tensor_slices(filenames)

# Use `Dataset.flat_map()` to transform each file as a separate nested dataset,
# and then concatenate their contents sequentially into a single "flat" dataset.
# * Skip the first line (header row).
# * Filter out lines beginning with "#" (comments).
dataset = dataset.flat_map(
    lambda filename: (
        tf.data.TextLineDataset(filename)
        .skip(1)
        .filter(lambda line: tf.not_equal(tf.substr(line, 0, 1), "#"))))

 

Dataset.map() でデータを前処理する

Dataset.map(f) 変換は与えられた関数 f を入力 dataset の各要素に適用することにより新しい dataset を生成します。それは map() 関数 をベースとし、これは関数型プログラミング言語において一般にリスト (そして他の構造) に適用されます。関数 f は入力の単一要素を表わす tf.Tensor オブジェクトを取り、新しい dataset の単一の要素を表わす tf.Tensor オブジェクトを返します。その実装は一つの要素を他のひとつに変換するために標準的な TensorFlow 演算を使用します。

このセクションは Dataset.map() をどのように使用するかの一般的なサンプルをカバーします。

 

tf.Example protocol buffer メッセージを解析する

多くの入力パイプラインは (例えば、tf.python_io.TFRecordWriter を使用して書かれた) TFRecord フォーマットファイルから tf.train.Example protocol buffer メッセージを抽出します。各 tf.train.Example レコードは一つまたそれ以上の “features” を含み、入力パイプラインは典型的にはこれらの features を tensor に変換します。

# Transforms a scalar string `example_proto` into a pair of a scalar string and
# a scalar integer, representing an image and its label, respectively.
def _parse_function(example_proto):
  features = {"image": tf.FixedLenFeature((), tf.string, default_value=""),
              "label": tf.FixedLenFeature((), tf.int32, default_value=0)}
  parsed_features = tf.parse_single_example(example_proto, features)
  return parsed_features["image"], parsed_features["label"]

# Creates a dataset that reads all of the examples from two files, and extracts
# the image and label features.
filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(_parse_function)

 

画像データをデコードしてそれをリサイズする

現実世界の画像データ上でニューラルネットワークを訓練するとき、異なるサイズの画像をそれらが固定サイズにバッチ処理されるように共通サイズに変換することはしばしば必要です。

# Reads an image from a file, decodes it into a dense tensor, and resizes it
# to a fixed shape.
def _parse_function(filename, label):
  image_string = tf.read_file(filename)
  image_decoded = tf.image.decode_image(image_string)
  image_resized = tf.image.resize_images(image_decoded, [28, 28])
  return image_resized, label

# A vector of filenames.
filenames = tf.constant(["/var/data/image1.jpg", "/var/data/image2.jpg", ...])

# `labels[i]` is the label for the image in `filenames[i].
labels = tf.constant([0, 37, ...])

dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
dataset = dataset.map(_parse_function)

 

任意の Python ロジックを tf.py_func() で適用する

パフォーマンス的な理由で、可能なときはいつでも貴方のデータの前処理に TensorFlow 演算を使用することを勧めます。けれども、貴方の入力データを解析するときに外部の Python ライブラリに頼むことは時々有用です。それを行なうためには、Dataset.map() 変換内で tf.py_func() 演算を呼び起こします。

import cv2

# Use a custom OpenCV function to read the image, instead of the standard
# TensorFlow `tf.read_file()` operation.
def _read_py_function(filename, label):
  image_decoded = cv2.imread(filename.decode(), cv2.IMREAD_GRAYSCALE)
  return image_decoded, label

# Use standard TensorFlow operations to resize the image to a fixed shape.
def _resize_function(image_decoded, label):
  image_decoded.set_shape([None, None, None])
  image_resized = tf.image.resize_images(image_decoded, [28, 28])
  return image_resized, label

filenames = ["/var/data/image1.jpg", "/var/data/image2.jpg", ...]
labels = [0, 37, 29, 1, ...]

dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
dataset = dataset.map(
    lambda filename, label: tuple(tf.py_func(
        _read_py_function, [filename, label], [tf.uint8, label.dtype])))
dataset = dataset.map(_resize_function)

 

dataset 要素をバッチ処理する

単純なバッチ処理

バッチ処理の最も単純な形式は dataset の n 連続的な要素を単一の要素にスタックすることです。Dataset.batch() 変換は正確にこれを行ないます、tf.stack(0 演算子と同じ制約を伴い、要素の各コンポーネントに適用されます : i.e. 各コンポーネント i について、総ての要素は正確な同じ shape の tensor を持たなければなりません。

inc_dataset = tf.data.Dataset.range(100)
dec_dataset = tf.data.Dataset.range(0, -100, -1)
dataset = tf.data.Dataset.zip((inc_dataset, dec_dataset))
batched_dataset = dataset.batch(4)

iterator = batched_dataset.make_one_shot_iterator()
next_element = iterator.get_next()

print(sess.run(next_element))  # ==> ([0, 1, 2,   3],   [ 0, -1,  -2,  -3])
print(sess.run(next_element))  # ==> ([4, 5, 6,   7],   [-4, -5,  -6,  -7])
print(sess.run(next_element))  # ==> ([8, 9, 10, 11],   [-8, -9, -10, -11])

 

パディングを持つ tensor をバッチ処理

上のレシピは同じサイズを持つ tensor 総てのためには動作します。けれども、多くのモデル (e.g. シークエンス・モデル) は変化するサイズを持てる入力データ (e.g. 異なる長さのシークエンス) で動作します。このケースを処理するために、Dataset.padded_batch() 変換は、(そこでパッドされるかもしれない) 一つまたはそれ以上に次元を指定することにより異なる shape の tensor をバッチ処理することを可能にします。

dataset = tf.data.Dataset.range(100)
dataset = dataset.map(lambda x: tf.fill([tf.cast(x, tf.int32)], x))
dataset = dataset.padded_batch(4, padded_shapes=[None])

iterator = dataset.make_one_shot_iterator()
next_element = iterator.get_next()

print(sess.run(next_element))  # ==> [[0, 0, 0], [1, 0, 0], [2, 2, 0], [3, 3, 3]]
print(sess.run(next_element))  # ==> [[4, 4, 4, 4, 0, 0, 0],
                               #      [5, 5, 5, 5, 5, 0, 0],
                               #      [6, 6, 6, 6, 6, 6, 0],
                               #      [7, 7, 7, 7, 7, 7, 7]]

Dataset.padded_batch() 変換は各コンポーネントの各次元に対する異なるパディングを設定することを可能にし、そしてそれは (上のサンプルでは None で表わさている) 可変長または定数長かもしれません。デフォルトが 0 のパディング値を override することもまた可能です。

 

訓練ワークフロー

複数エポックを処理する

tf.data API は同じデータの複数のエポックを処理するために2つの主要な方法を提供します。

複数のエポックで dataset に渡り iterate するための最も単純な方法は Dataset.repeat() 変換を使用することです。例えば、入力を 10 エポック繰り返す dataset を作成するためには :

filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...)
dataset = dataset.repeat(10)
dataset = dataset.batch(32)

Dataset.repeat() 変換を引数なしで適用することは入力を無期限に繰り返すでしょう。Dataset.repeat() は一つのエポックの終わりと次のエポックの最初をシグナルすることなくその引数を結合します。

各エポックの終わりでシグナルを受け取ることを望むのであれば、dataset の終わりに tf.errors.OutOfRangeError を catch する訓練ループを書くことができます。そのポイントで貴方は各エポックのための統計情報 (e.g., 検証エラー) を収集することができます。

filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...)
dataset = dataset.batch(32)
iterator = dataset.make_initializable_iterator()
next_element = iterator.get_next()

# Compute for 100 epochs.
for _ in range(100):
  sess.run(iterator.initializer)
  while True:
    try:
      sess.run(next_element)
    except tf.errors.OutOfRangeError:
      break

  # [Perform end-of-epoch calculations here.]

 

入力データをランダムにシャッフルする

Dataset.shuffle() 変換は tf.RandomShuffleQueue に類似したアルゴリズムを使用して入力 dataset をランダムにシャッフルします : それは固定長バッファを保持してバッファから次の要素を一様にランダムに選択します。

filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...)
dataset = dataset.shuffle(buffer_size=10000)
dataset = dataset.batch(32)
dataset = dataset.repeat()

 

高位 API を使用する

tf.train.MonitoredTrainingSession API は分散設定で TensorFlow を実行する多くの局面を単純化します。MonitoredTrainingSession は訓練が完了したことをシグナルするために tf.errors.OutOfRangeError を使用し、それを tf.data API で使用するためには、Dataset.make_one_shot_iterator() を使用することを推奨します。例えば :

filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...)
dataset = dataset.shuffle(buffer_size=10000)
dataset = dataset.batch(32)
dataset = dataset.repeat(num_epochs)
iterator = dataset.make_one_shot_iterator()

next_example, next_label = iterator.get_next()
loss = model_function(next_example, next_label)

training_op = tf.train.AdagradOptimizer(...).minimize(loss)

with tf.train.MonitoredTrainingSession(...) as sess:
  while not sess.should_stop():
    sess.run(training_op)

tf.estimator.Estimator の input_fn で Dataset を使用するためにもまた、Dataset.make_one_shot_iterator() を使用することを推奨します。例えば :

def dataset_input_fn():
  filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
  dataset = tf.data.TFRecordDataset(filenames)

  # Use `tf.parse_single_example()` to extract data from a `tf.Example`
  # protocol buffer, and perform any additional per-record preprocessing.
  def parser(record):
    keys_to_features = {
        "image_data": tf.FixedLenFeature((), tf.string, default_value=""),
        "date_time": tf.FixedLenFeature((), tf.int64, default_value=""),
        "label": tf.FixedLenFeature((), tf.int64,
                                    default_value=tf.zeros([], dtype=tf.int64)),
    }
    parsed = tf.parse_single_example(record, keys_to_features)

    # Perform additional preprocessing on the parsed data.
    image = tf.image.decode_jpeg(parsed["image_data"])
    image = tf.reshape(image, [299, 299, 1])
    label = tf.cast(parsed["label"], tf.int32)

    return {"image_data": image, "date_time": parsed["date_time"]}, label

  # Use `Dataset.map()` to build a pair of a feature dictionary and a label
  # tensor for each example.
  dataset = dataset.map(parser)
  dataset = dataset.shuffle(buffer_size=10000)
  dataset = dataset.batch(32)
  dataset = dataset.repeat(num_epochs)
  iterator = dataset.make_one_shot_iterator()

  # `features` is a dictionary in which each value is a batch of values for
  # that feature; `labels` is a batch of labels.
  features, labels = iterator.get_next()
  return features, labels
 

以上



TensorFlow : Guide : 高位 API : Eager Execution

TensorFlow : Guide : 高位 API : Eager Execution (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
更新日時 : 04/11/2019 (1.13.1); 07/14/2018 (1.9.0)
作成日時 : 04/08/2018

* TensorFlow 1.9 でドキュメント構成が変わりましたので調整しました。また本ページは改訂されましたので再翻訳しました。
* 本ページは、TensorFlow 本家サイトの Guide – Hight Level APIs – Eager Execution を翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、適宜、追加改変している場合もあります。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

本文

TensorFlow の eager execution は命令型プログラミング環境で、グラフを構築することなく演算を直ちに評価します : 演算は後で実行する計算グラフを構築する代わりに具体的な値を返します。これは TensorFlow を始めてモデルをデバッグすることを容易にして、そしてまたそれはボイラープレートを削減します。このガイドに沿ってフォローするためには、対話的 python インタープリタで下のコード・サンプルを実行してください。

Eager execution は研究や実験のための柔軟な機械学習プラットフォームで、以下を提供します :

  • 直感的なインターフェイス — 貴方のコードを自然に構造化して Python データ構造を使用します。小さなモデルと小さなデータ上で迅速に反復します。
  • より簡単なデバッギング — 実行中のモデルを調査して変更をテストするために ops を直接的に呼び出します。即時のエラー報告のために標準的な Python デバッギング・ツールを使用します。
  • 自然な制御フロー — グラフ制御フローの代わりに Python 制御フローを使用し、動的モデルの仕様を単純化します。

Eager execution は殆どの TensorFlow 演算と GPU アクセラレーションをサポートします。eager execution で動作するサンプルのコレクションについては: tensorflow/contrib/eager/python/examples を見てください。

Note: 幾つかのモデルでは eager execution が有効であると増大したオーバーヘッドを経験するかもしれません。パフォーマンス改善は進行中ですが、問題が見つかる場合にはバグをファイルしてベンチマークを共有してください。

 

セットアップと基本的な使用方法

TensorFlow の最新版にアップグレードします :

$ pip install --upgrade tensorflow

eager execution を開始するには、プログラムかコンソール・セッションの最初で tf.enable_eager_execution() を追加します。この演算をプログラムが呼び出す他のモジュールには追加しないでください。

from __future__ import absolute_import, division, print_function

import tensorflow as tf

tf.enable_eager_execution()

今では貴方は TensorFlow 演算を実行できて結果は直ちに返ります :

tf.executing_eagerly()        # => True

x = [[2.]]
m = tf.matmul(x, x)
print("hello, {}".format(m))  # => "hello, [[4.]]"

eager execution を有効にすると TensorFlow 演算がどのように挙動するかを変更します — 今ではそれらは即時に評価して値を Python に返します。tf.Tensor オブジェクトは計算グラフのノードへのシンボリックなハンドルの代わりに具体的な値を参照します。セッション内で構築して後で実行する計算グラフはありませんので、print() やデバッガーを使用して結果を調査することは容易です。tensor 値の評価、プリンティング、そしてチェックは勾配を計算するためのフローを壊しません。

Eager execution は NumPy と共に素晴らしく動作します。NumPy 演算は tf.Tensor 引数を受け取ります。TensorFlow 数学演算 は Python オブジェクトと NumPy 配列を tf.Tensor オブジェクトに変換します。tf.Tensor.numpy メソッドはオブジェクトの値を NumPy ndarray として返します。

a = tf.constant([[1, 2],
                 [3, 4]])
print(a)
# => tf.Tensor([[1 2]
#               [3 4]], shape=(2, 2), dtype=int32)

# Broadcasting support
b = tf.add(a, 1)
print(b)
# => tf.Tensor([[2 3]
#               [4 5]], shape=(2, 2), dtype=int32)

# Operator overloading is supported
print(a * b)
# => tf.Tensor([[ 2  6]
#               [12 20]], shape=(2, 2), dtype=int32)

# Use NumPy values
import numpy as np

c = np.multiply(a, b)
print(c)
# => [[ 2  6]
#     [12 20]]

# Obtain numpy value from a tensor:
print(a.numpy())
# => [[1 2]
#     [3 4]]

tf.contrib.eager モジュールは eager とグラフ実行環境の両者で利用可能なシンボルを含みそしてグラフと一緒に動作するコードを書く (訳注: 後述) ために有用です :

tfe = tf.contrib.eager

 

動的制御フロー

eager execution の主なメリットはモデルが実行されているときにホスト言語の総ての機能が利用可能であることです。そのため例えば、fizzbuzz を書くことも容易です :

def fizzbuzz(max_num):
  counter = tf.constant(0)
  max_num = tf.convert_to_tensor(max_num)
  for num in range(max_num.numpy()):
    num = tf.constant(num)
    if int(num % 3) == 0 and int(num % 5) == 0:
      print('FizzBuzz')
    elif int(num % 3) == 0:
      print('Fizz')
    elif int(num % 5) == 0:
      print('Buzz')
    else:
      print(num)
    counter += 1
  return counter

これは tensor 値に依拠する条件節を持ちこれらの値を実行時にプリントします。

 

モデルを構築する

多くの機械学習モデルは構成する層により表わされます。TensorFlow を eager execution とともに使用するとき貴方自身の層を書くか tf.keras.layers パッケージで提供される層を使用することができます。

層を表わすために任意の Python オブジェクトを使用できる一方で、TensorFlow は便利な基底クラスとして tf.keras.layers.Layer を持ちます。貴方自身の層を実装するためにそれから継承してください :

class MySimpleLayer(tf.keras.layers.Layer):
  def __init__(self, output_units):
    super(MySimpleLayer, self).__init__()
    self.output_units = output_units

  def build(self, input_shape):
    # The build method gets called the first time your layer is used.
    # Creating variables on build() allows you to make their shape depend
    # on the input shape and hence removes the need for the user to specify
    # full shapes. It is possible to create variables during __init__() if
    # you already know their full shapes.
    self.kernel = self.add_variable(
      "kernel", [input_shape[-1], self.output_units])

  def call(self, input):
    # Override call() instead of __call__ so we can perform some bookkeeping.
    return tf.matmul(input, self.kernel)

上の MySimpleLayer の代わりに tf.keras.layers.Dense 層を使用します、何故ならばそれはその機能のスーパーセットを持つからです (それはまたバイアスを加算します)。

層をモデルに構成するときモデルを表わすために層の線形スタックである tf.keras.Sequential を使用することができます。基本モデルのために使用することは容易です :

model = tf.keras.Sequential([
  tf.keras.layers.Dense(10, input_shape=(784,)),  # must declare input shape
  tf.keras.layers.Dense(10)
])

他の方法として、tf.keras.Model から継承することによりクラスでモデルを体系化します。これは層のためのコンテナでそれ自身が層で、tf.keras.Model オブジェクトが他の tf.keras.Model オブジェクトを含むことを可能にします。

次のサンプルは標準的な MNIST 手書き数字を分類する多層モデルを作成します。それは eager execution 環境で訓練可能なグラフを構築する optimizer と層 API を示します。

class MNISTModel(tf.keras.Model):
  def __init__(self):
    super(MNISTModel, self).__init__()
    self.dense1 = tf.keras.layers.Dense(units=10)
    self.dense2 = tf.keras.layers.Dense(units=10)

  def call(self, input):
    """Run the model."""
    result = self.dense1(input)
    result = self.dense2(result)
    result = self.dense2(result)  # reuse variables from dense2 layer
    return result

model = MNISTModel()

tf.keras.Model クラスのために入力 shape を設定する必要はありません、何故ならば最初に入力が層に渡されるときにパラメータが設定されるからです

tf.keras.layers クラスはそれら自身のモデル変数を作成して含み、それらは層オブジェクトのライフタイムに結び付けられます。層変数を共有するためには、それらのオブジェクトを共有します。

 

Eager 訓練

勾配を計算する

自動微分 はニューラルネットワークを訓練するための バックプロパゲーション のような機械学習アルゴリズムを実装するために有用です。eager execution の間は、後で勾配を計算するための演算を追跡するために tf.GradientTape を使用します。

tfe.GradientTape は追跡しないとき最大限のパフォーマンスを提供するためのオプトインな機能です。異なる演算が各呼び出しの間に発生しますので、総ての forward パス演算は「テープ」に記録されます。勾配を計算するために、テープを反対に再生してから破棄します。特定の一つの tf.GradientTape は 1 つの勾配を計算するだけです ; 続く呼び出しはランタイム・エラーを投げます。

w = tf.Variable([[1.0]])
with tf.GradientTape() as tape:
  loss = w * w

grad = tape.gradient(loss, w)
print(grad)  # => tf.Tensor([[ 2.]], shape=(1, 1), dtype=float32)
tf.Tensor([[2.]], shape=(1, 1), dtype=float32)

 

モデルを訓練する

次のサンプルは標準的な MNIST 手書き数字を分類する多層モデルを作成します。それは eager execution 環境で訓練可能なグラフを構築するために optimizer と層 API を実演します。

# Fetch and format the mnist data
(mnist_images, mnist_labels), _ = tf.keras.datasets.mnist.load_data()

dataset = tf.data.Dataset.from_tensor_slices(
  (tf.cast(mnist_images[...,tf.newaxis]/255, tf.float32),
   tf.cast(mnist_labels,tf.int64)))
dataset = dataset.shuffle(1000).batch(32)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
# Build the model
mnist_model = tf.keras.Sequential([
  tf.keras.layers.Conv2D(16,[3,3], activation='relu'),
  tf.keras.layers.Conv2D(16,[3,3], activation='relu'),
  tf.keras.layers.GlobalAveragePooling2D(),
  tf.keras.layers.Dense(10)
])

訓練なしでさえも、eager executeion でモデルを呼び出して出力を調査してください :

for images,labels in dataset.take(1):
  print("Logits: ", mnist_model(images[0:1]).numpy())
Logits:  [[-0.00147692 -0.02905408  0.04325635  0.03817059 -0.02543205 -0.01521162
   0.02900162 -0.03181015  0.05013638  0.07374214]]

keras モデルが (fit メソッドを使用する) 訓練ループを持つ一方で、貴方は時には更なるカスタマイズを必要とします。ここに、eager で実装された訓練ループのサンプルがあります :

optimizer = tf.train.AdamOptimizer()

loss_history = []
for (batch, (images, labels)) in enumerate(dataset.take(400)):
  if batch % 10 == 0:
    print('.', end='')
  with tf.GradientTape() as tape:
    logits = mnist_model(images, training=True)
    loss_value = tf.losses.sparse_softmax_cross_entropy(labels, logits)

  loss_history.append(loss_value.numpy())
  grads = tape.gradient(loss_value, mnist_model.trainable_variables)
  optimizer.apply_gradients(zip(grads, mnist_model.trainable_variables),
                            global_step=tf.train.get_or_create_global_step())
........................................
import matplotlib.pyplot as plt

plt.plot(loss_history)
plt.xlabel('Batch #')
plt.ylabel('Loss [entropy]')

 

Variable と optimizer

tf.Variable オブジェクトは、自動微分をより容易にするために訓練の間にアクセスされるミュータブルな tf.Tensor 値をストアします。モデルのパラメータはクラス内に変数としてカプセル化できます。

モデル・パラメータは tf.Variable を tf.GradientTape と共に使用することでより良くカプセル化できます。例えば、上の自動微分サンプルは次のように書き換えることができます :

class Model(tf.keras.Model):
  def __init__(self):
    super(Model, self).__init__()
    self.W = tf.Variable(5., name='weight')
    self.B = tf.Variable(10., name='bias')
  def call(self, inputs):
    return inputs * self.W + self.B

# A toy dataset of points around 3 * x + 2
NUM_EXAMPLES = 2000
training_inputs = tf.random_normal([NUM_EXAMPLES])
noise = tf.random_normal([NUM_EXAMPLES])
training_outputs = training_inputs * 3 + 2 + noise

# The loss function to be optimized
def loss(model, inputs, targets):
  error = model(inputs) - targets
  return tf.reduce_mean(tf.square(error))

def grad(model, inputs, targets):
  with tf.GradientTape() as tape:
    loss_value = loss(model, inputs, targets)
  return tape.gradient(loss_value, [model.W, model.B])

# Define:
# 1. A model.
# 2. Derivatives of a loss function with respect to model parameters.
# 3. A strategy for updating the variables based on the derivatives.
model = Model()
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)

print("Initial loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))

# Training loop
for i in range(300):
  grads = grad(model, training_inputs, training_outputs)
  optimizer.apply_gradients(zip(grads, [model.W, model.B]),
                            global_step=tf.train.get_or_create_global_step())
  if i % 20 == 0:
    print("Loss at step {:03d}: {:.3f}".format(i, loss(model, training_inputs, training_outputs)))

print("Final loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))
print("W = {}, B = {}".format(model.W.numpy(), model.B.numpy()))
Initial loss: 68.132
Loss at step 000: 65.523
Loss at step 020: 30.228
Loss at step 040: 14.266
Loss at step 060: 7.039
Loss at step 080: 3.763
Loss at step 100: 2.276
Loss at step 120: 1.600
Loss at step 140: 1.293
Loss at step 160: 1.153
Loss at step 180: 1.089
Loss at step 200: 1.060
Loss at step 220: 1.047
Loss at step 240: 1.041
Loss at step 260: 1.038
Loss at step 280: 1.037
Final loss: 1.036
W = 3.0057950019836426, B = 1.9924943447113037

 

eager execution の間に状態のためにオブジェクトを使用する

グラフ実行では、(変数のような) プログラム状態はグローバルなコレクションにストアされてそれらのライフタイムは tf.Session オブジェクトで管理されます。対照的に、eager execuction の間は状態オブジェクトのライフタイムはそれらの対応する Python オブジェクトのライフタイムにより決定されます。

 

Variables はオブジェクト

eager execution の間、variable はオブジェクトへの最後の参照が除去されるまで存続し、それから削除されます。

if tf.test.is_gpu_available():
  with tf.device("gpu:0"):
    v = tf.Variable(tf.random_normal([1000, 1000]))
    v = None  # v no longer takes up GPU memory

 

オブジェクト・ベースのセービング

tf.train.Checkpoint は tf.Variable をチェックポイントへ/からセーブしてリストアできます :

x = tf.Variable(10.)
checkpoint = tf.train.Checkpoint(x=x)
x.assign(2.)   # Assign a new value to the variables and save.
checkpoint_path = './ckpt/'
checkpoint.save('./ckpt/')
'./ckpt/-1'
x.assign(11.)  # Change the variable after saving.

# Restore values from the checkpoint
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_path))

print(x)  # => 2.0
<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=2.0>

モデルをセーブしてロードするためには、隠れ変数を必要とすることなく、tf.train.Checkpoint がオブジェクトの内部状態をストアします、モデル、optimizer, そしてグローバルステップの状態を記録するためには、それらを tf.train.Checkpoint に渡します :

import os
import tempfile

model = tf.keras.Sequential([
  tf.keras.layers.Conv2D(16,[3,3], activation='relu'),
  tf.keras.layers.GlobalAveragePooling2D(),
  tf.keras.layers.Dense(10)
])
optimizer = tf.train.AdamOptimizer(learning_rate=0.001)
checkpoint_dir = tempfile.mkdtemp()
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
root = tf.train.Checkpoint(optimizer=optimizer,
                           model=model,
                           optimizer_step=tf.train.get_or_create_global_step())

root.save(checkpoint_prefix)
root.restore(tf.train.latest_checkpoint(checkpoint_dir))
<tensorflow.python.training.checkpointable.util.CheckpointLoadStatus at 0x7fe652194198>

 

オブジェクト指向メトリクス

tfe.metrics はオブジェクトとしてストアされます。新しいデータを callable に渡すことでメトリクスを更新して、そして tfe.metrics.result メソッドを使用して結果を取得します、例えば :

m = tfe.metrics.Mean("loss")
m(0)
m(5)
m.result()  # => 2.5
m([8, 9])
m.result()  # => 5.5
<tf.Tensor: id=68185, shape=(), dtype=float64, numpy=5.5>

 

Summaries と TensorBoard

TensorBoard はモデル訓練プロセスを理解し、デバッグして最適化するための可視化ツールです。それはプログラムを実行する間に書かれる要約イベントを使用します。

tf.contrib.summary は eager とグラフ実行環境の両者で互換です。tf.contrib.summary.scalar のような要約演算はモデル構築の間に挿入されます。例えば、 100 グローバル・ステップ毎に一度要約を記録するためには :

writer = tf.contrib.summary.create_file_writer(logdir)
global_step=tf.train.get_or_create_global_step()  # return global step var

writer.set_as_default()

for _ in range(iterations):
  global_step.assign_add(1)
  # Must include a record_summaries method
  with tf.contrib.summary.record_summaries_every_n_global_steps(100):
    # your model code goes here
    tf.contrib.summary.scalar('loss', loss)
     ...

 

上級者のための自動微分トピック

動的モデル

tfe.GradientTape はまた動的モデルでも利用できます。バックトラックする直線探索 (= line search) アルゴリズムのこのサンプルは、複雑な制御フローにもかかわらず、普通の NumPy コードのように見えます。勾配があり微分可能であることを除けばです :

def line_search_step(fn, init_x, rate=1.0):
  with tf.GradientTape() as tape:
    # Variables are automatically recorded, but manually watch a tensor
    tape.watch(init_x)
    value = fn(init_x)
  grad = tape.gradient(value, init_x)
  grad_norm = tf.reduce_sum(grad * grad)
  init_value = value
  while value > init_value - rate * grad_norm:
    x = init_x - rate * grad
    value = fn(x)
    rate /= 2.0
  return x, value

 

勾配を計算するための追加の関数

tfe.GradientTape は勾配を計算するためのパワフルなインターフェイスですが、自動微分のために利用可能なもう一つの Autograd-スタイル API があります。これらの関数は tfe.Variable なしで tensor と勾配関数だけで、数学コードを書く場合に有用です :

  • tfe.gradients_function — 入力関数パラメータのその引数に関する導関数を計算する関数を返します。入力関数パラメータはスカラー値を返さなければなりません。返された関数が呼び起こされたとき、それは tf.Tensor オブジェクトのリストを返します : 入力関数の各引数のために一つの要素です。興味のある任意のものは関数パラメータとして渡されなければなりませんので、これはもし多くの訓練可能なパラメータへの依存がある場合にはこれは扱いにくくなります。
  • tfe.value_and_gradients_function — tfe.gradients_function と同様ですが、返された関数が呼び起こされるとき、それは入力関数のその引数に関する導関数のリストに加えて入力関数からの値を返します。

次のサンプルでは、tfe.gradients_function は引数として square 関数を取り square のその入力に関する偏微分を計算する関数を返します。square の 3 における導関数を計算するために、grad(3.0) は 6 を返します。

def square(x):
  return tf.multiply(x, x)

grad = tfe.gradients_function(square)

square(3.)  # => 9.0
grad(3.)    # => [6.0]

# The second-order derivative of square:
gradgrad = tfe.gradients_function(lambda x: grad(x)[0])
gradgrad(3.)  # => [2.0]

# The third-order derivative is None:
gradgradgrad = tfe.gradients_function(lambda x: gradgrad(x)[0])
gradgradgrad(3.)  # => [None]


# With flow control:
def abs(x):
  return x if x > 0. else -x

grad = tfe.gradients_function(abs)

grad(3.)   # => [1.0]
grad(-3.)  # => [-1.0]

 

カスタム勾配

カスタム勾配は eager とグラフ実行で勾配を override するための簡単な方法です。forward 関数内では、勾配は入力、出力、または中間結果に関する勾配を定義します。例えば、backward パスで勾配のノルムをクリップするための簡単な方法がここにあります :

@tf.custom_gradient
def clip_gradient_by_norm(x, norm):
  y = tf.identity(x)
  def grad_fn(dresult):
    return [tf.clip_by_norm(dresult, norm), None]
  return y, grad_fn

カスタム勾配は演算のシークエンスのための数値的安定な勾配を提供するために一般に使用されます :

def log1pexp(x):
  return tf.log(1 + tf.exp(x))
grad_log1pexp = tfe.gradients_function(log1pexp)

# The gradient computation works fine at x = 0.
grad_log1pexp(0.)  # => [0.5]

# However, x = 100 fails because of numerical instability.
grad_log1pexp(100.)  # => [nan]

ここで、log1pexp 関数はカスタム勾配で解析的に単純化できます。下の実装は forward パスで計算された tf.exp(x) のための値を再利用しています — 冗長な計算を除去することでそれをより効率的にしています :

@tf.custom_gradient
def log1pexp(x):
  e = tf.exp(x)
  def grad(dy):
    return dy * (1 - 1 / (1 + e))
  return tf.log(1 + e), grad

grad_log1pexp = tfe.gradients_function(log1pexp)

# As before, the gradient computation works fine at x = 0.
grad_log1pexp(0.)  # => [0.5]

# And the gradient computation also works at x = 100.
grad_log1pexp(100.)  # => [1.0]

 

パフォーマンス

eager execution の間は計算は GPU へと自動的にはオフロードされません。計算が動作する位置について制御することを望む場合にはそれを tf.device(‘/gpu:0’) ブロックで囲むことができます (または CPU の同等のもの) :

import time

def measure(x, steps):
  # TensorFlow initializes a GPU the first time it's used, exclude from timing.
  tf.matmul(x, x)
  start = time.time()
  for i in range(steps):
    x = tf.matmul(x, x)
    _ = x.numpy()  # Make sure to execute op and not just enqueue it
  end = time.time()
  return end - start

shape = (1000, 1000)
steps = 200
print("Time to multiply a {} matrix by itself {} times:".format(shape, steps))

# Run on CPU:
with tf.device("/cpu:0"):
  print("CPU: {} secs".format(measure(tf.random_normal(shape), steps)))

# Run on GPU, if available:
if tfe.num_gpus() > 0:
  with tf.device("/gpu:0"):
    print("GPU: {} secs".format(measure(tf.random_normal(shape), steps)))
else:
  print("GPU: not found")

出力 (正確な数はハードウェアに依存します) :

Time to multiply a (1000, 1000) matrix by itself 200 times:
CPU: 4.614904403686523 secs
GPU: 0.5581181049346924 secs

tf.Tensor オブジェクトはその演算を実行するために異なるデバイスへとコピーできます :

x = tf.random_normal([10, 10])

x_gpu0 = x.gpu()
x_cpu = x.cpu()

_ = tf.matmul(x_cpu, x_cpu)    # Runs on CPU
_ = tf.matmul(x_gpu0, x_gpu0)  # Runs on GPU:0

if tfe.num_gpus() > 1:
  x_gpu1 = x.gpu(1)
  _ = tf.matmul(x_gpu1, x_gpu1)  # Runs on GPU:1

 

ベンチマーク

GPU 上の ResNet50 訓練のような計算が重いモデルについては、eager execution パフォーマンスはグラフ実行に匹敵します。しかしこのギャップはより少ない計算を持つモデルのためにはより大きくなって多くの小さい演算を持つモデルのためにホットコード・パスを最適化するために行われなければならないワークがあります。

 

グラフで作業する

eager execution は開発とデバッギングをより対話的にする一方で、TensorFlow グラフ実行は分散訓練、パフォーマンス最適化、そしてプロダクション配備のための優位点があります。けれども、グラフコードを書くことは標準的な Python コードを書くこととは異なり、デバッグすることがより困難に感じるかもしれません。

グラフ構築モデルをビルドして訓練するためには、Python プログラムは最初に計算を表わすグラフをビルドして、それから C++ ベースのランタイム上で実行のためのグラフを送るために Session.run を呼び起こします。これは以下を提供します :

  • 静的 autodiff を使用する自動微分。
  • プラットフォーム独立なサーバへの単純な配備。
  • グラフ・ベースの最適化 (共通部分式除去, 定数畳み込み, etc.)。
  • コンパイルとカーネル・フュージョン。
  • 自動的な分散とレプリケーション (ノードを分散システム上に配置)。

eager execution のために書かれるコードの配備はより困難です : モデルからグラフを生成するにせよ、あるいは Python ランタイムとコードをサーバ上で直接的に実行するにせよです。

 

互換コードを書く

eager execution のために書かれた同じコードはまたグラフ実行の間にはグラフを構築するでしょう。単純に同じコードを新しい Python セッションで実行することでこれを遂行します、そこでは eager execution は有効ではありません。

殆どの TensorFlow 演算は eager execution の間に動作しますが、しかし留意すべき幾つかのことはあります :

  • 入力前処理のために queue の代わりに tf.data を使用します。それはより高速で簡単です。
  • オブジェクト指向層 API を使用します — tf.keras.layers と tf.keras.Model のような — 何故ならばそれらは変数のためのストレージを明示的に持つからです。
  • 殆どのモデル・コードは eager とグラフ実行の間で同じに動作しますが、例外があります。(例えば、入力に基づく計算を変更する Python 制御フローを使用する動的モデル。)
  • ひとたび tf.enable_eager_execution で eager execution が有効にされれば、それは無効にはできません。グラフ実行に戻るためには新しい Python セッションを開始します。

eager execution とグラフ実行の両者のためにコードを書くのが最善です。これは、グラフ実行の分散パフォーマンスの恩恵とともに eager の対話的実験とデバッグしやすさを貴方に与えます。

eager execution で書き、デバッグし、そして反復し、それからプロダクション配備のためにモデルグラフをインポートします。モデル変数をセーブしてリストアするためには tfe.Checkpoint を使用します、これは eager とグラフ実行環境の間での移動を可能にします。tensorflow/contrib/eager/python/examples のサンプルを見てください。

 

グラフ環境で eager execution を使用する

選択的に TensorFlow グラフ環境で tfe.py_func を使用して eager execution を有効にします。これは tf.enable_eager_execution() が呼び出されていないときに使用されます。

def my_py_func(x):
  x = tf.matmul(x, x)  # You can use tf ops
  print(x)  # but it's eager!
  return x

with tf.Session() as sess:
  x = tf.placeholder(dtype=tf.float32)
  # Call eager function in graph!
  pf = tfe.py_func(my_py_func, [x], tf.float32)
  sess.run(pf, feed_dict={x: [[2.0]]})  # [[4.0]]

 
以上



TensorFlow : Guide : Estimators : カスタム Estimator の作成

TensorFlow : Guide : Estimators : カスタム Estimator の作成 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
更新日時 : 07/14/2018
作成日時 : 03/28/2018

* TensorFlow 1.9 でドキュメント構成が変わりましたので調整しました。
* 本ページは、TensorFlow の本家サイトの Guide – Estimators – Creating Custom Estimators を翻訳した上で適宜、補足説明したものです:

* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

このドキュメントはカスタム Estimator を紹介します。特に、このドキュメントはアイリス問題を解く際に pre-made Estimator DNNClassifier の挙動を模倣するカスタム Estimator をどのように作成するかを示します。アイリス問題の詳細は Pre-Made Estimators の章を見てください。

サンプル・コードをダウンロードしてアクセスするためには次の2つのコマンドを起動してください :

git clone https://github.com/tensorflow/models/
cd models/samples/core/get_started

このドキュメントでは custom_estimator.py を見ていきます。次のコマンドでそれを実行できます :

python custom_estimator.py

もし待ち遠しく感じるのであれば、custom_estimator.py を (同じディレクトリにある) premade_estimator.py と自由に比較・対比してください。

 

Pre-made vs. カスタム

次の図が示すように、pre-made Estimator は tf.estimator.Estimator 基底クラスのサブクラスで、一方でカスタム Estimator は tf.estimator.Estimator のインスタンスです :


Pre-made とカスタム Estimators は総て Estimator です。

Pre-made Estimator は完全に焼けて (= baked) います。けれども時には、Estimator の挙動についてより制御する必要があります。そこがカスタム Estimator が役に立つところです。殆どどのようなことを行なうためにもカスタム Estimator を作成できます。もし貴方が隠れ層にある普通ではない流儀で接続されることを望むのであれば、カスタム Estimator を書きましょう。もし貴方のモデルのために独特な メトリック を計算することを望むのであれば、カスタム Estimator を書きましょう。基本的には、貴方の特定の問題のために Estimator が最適化されることを望むのであれば、カスタム Estimator を書きましょう。

モデル関数 (または model_fn) は ML アルゴリズムを実装します。pre-made Estimator とカスタム Estimator で作業する間の唯一の違いは :

  • pre-made Estimator では、貴方のために誰かが既にモデル関数を書いています。
  • カスタム Estimator では、貴方がモデル関数を書かなければなりません。

貴方のモデル関数は、あらゆる種類の隠れ層とメトリクスを定義して、広い範囲のアルゴリズムを実装できるでしょう。入力関数のように、総てのモデル関数は入力パラメータの標準的なグループを受け取りそして出力値の標準的なグループを返さなければなりません。丁度入力関数が Dataset API を利用できるように、モデル関数は Layers API と Metrics API を利用できます。

アイリス問題をカスタム Estimator でどのように解くかを見てみましょう。簡単な覚書として — ここに模倣しようとしているアイリス・モデルの体系があります :


アイリスの私達の実装は4つの特徴、2つの隠れ層そしてロジット出力層を含みます。

 

入力関数を書く

私達のカスタム Estimator 実装は iris_data.py からの pre-made Estimator 実装 と同じ入力関数を仕様します。すなわち :

def train_input_fn(features, labels, batch_size):
    """An input function for training"""
    # Convert the inputs to a Dataset.
    dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))

    # Shuffle, repeat, and batch the examples.
    dataset = dataset.shuffle(1000).repeat().batch(batch_size)

    # Return the read end of the pipeline.
    return dataset.make_one_shot_iterator().get_next()

この入力関数は(features, labels) ペアのバッチを生成する入力パイプラインを構築します、そこでは特徴は辞書特徴です。

 

特徴カラムを作成する

Premade Estimators特徴カラム の章で詳述されましたように、モデルが各々の特徴をどのように使用するべきかを指定するために貴方のモデルの特徴カラムを定義しなければなりません。pre-made Estimator かカスタム Estimator のいずれかで作業するにせよ、貴方は同じ流儀で特徴カラムを定義します。

次のコードは各々の入力特徴のために単純な numeric_column を作成します、これは入力特徴の値はモデルへの入力として直接使用されるべきであることを示しています :

# Feature columns describe how to use the input.
my_feature_columns = []
for key in train_x.keys():
    my_feature_columns.append(tf.feature_column.numeric_column(key=key))

 

モデル関数を書く

私達が使用するモデル関数は次の呼び出しシグネチャ (= call signature) を持ちます :

def my_model_fn(
   features, # This is batch_features from input_fn
   labels,   # This is batch_labels from input_fn
   mode,     # An instance of tf.estimator.ModeKeys
   params):  # Additional configuration

最初の2つの引数は入力関数から返される特徴とラベルのバッチです ; つまり、features と labels は貴方のモデルが使用するデータへのハンドルです。mode 引数は呼び出し元が training, predicting, あるいは evaluation を要求しているかを示します。

呼び出し元は params を Estimator のコンストラクタに渡すこともできます。コンストラクタに渡された任意の params は model_fn 上に順番に渡されます。custom_estimator.py では次の行群が estimator を作成してモデルを構成するために params を設定します。この構成ステップは Getting Started with TensorFlowtf.estimator.DNNClassifier をどのように構成したかに類似しています。

classifier = tf.estimator.Estimator(
    model_fn=my_model,
    params={
        'feature_columns': my_feature_columns,
        # Two hidden layers of 10 nodes each.
        'hidden_units': [10, 10],
        # The model must choose between 3 classes.
        'n_classes': 3,
    })

典型的なモデル関数を実装するためには、次を行わなければなりません :

  • モデルを定義する。
  • 3つの異なるモードの各々のために追加の計算を指定します :
    • 予測する
    • 評価する
    • 訓練する

 

モデルを定義する

基本的な深層ニューラルネットワーク・モデルは次の3つのセクションを定義しなければなりません :

 

入力層を定義する

model_fn の最初の行は特徴辞書と feature_columns を貴方のモデルのための入力に変換するために tf.feature_column.input_layer を呼び出します、次のようにです :

    # Use `input_layer` to apply the feature columns.
    net = tf.feature_column.input_layer(features, params['feature_columns'])

前の行は貴方の特徴カラムで定義された変換を適用し、モデルの入力層を作成します。

 

隠れ層

もし貴方が深層ニューラルネットワークを作成している場合には、一つまたはそれ以上の隠れ層を定義しなければなりません。Layers API は、convolutional, pooling, と dropout 層を含む、総てのタイプの隠れ層を定義するための関数の豊富なセットを提供します。アイリスのためには、隠れ層を作成するために単に tf.layers.dense を呼び出します、これは params[‘hidden_layers’] で定義された次元を持ちます。dense 層では各ノードは前の層の総てのノードに接続されます。ここに関連するコードがあります :

    # Build the hidden layers, sized according to the 'hidden_units' param.
    for units in params['hidden_units']:
        net = tf.layers.dense(net, units=units, activation=tf.nn.relu)
  • units パラメータは与えられた層の出力ニューロンの数を定義します。
  • activation パラメータは 活性化関数 — この場合は Relu を定義します。

ここでの変数 net はネットワークの現在のトップ層を表します。最初の iteration の間、net は入力層を表します。各ループ iteration で tf.layers.dense は新しい層を作成して、これは、変数 net を使用して、その入力として前の層の出力を取ります。

2つの隠れ層を作成後、私達のネットワークは次のように見えます。単純化のため、図は各層の総てのユニットを表してはいません。

tf.layers.dense は、多数の正則化パラメータを設定するための機能を含む多くの追加の能力を提供することに注意してください。けれども単純化のために、他のパラメータのデフォルト値を単に受容します。

 

出力層

更にもう一度 tf.layers.dense を呼び出すことにより出力層を定義します、今回は活性化関数なしでです :

    # Compute logits (1 per class).
    logits = tf.layers.dense(net, params['n_classes'], activation=None)

ここで、net は最後の隠れ層を表します。従って、層の完全な層は今では次のように接続されています :


最後の隠れ層は出力層に流れ込みます。

出力層を定義するとき、units パラメータは出力の数を指定します。それで、units を params[‘n_classes’] に設定することで、モデルはクラス毎に一つの出力値を生成します。出力ベクトルの各要素は、それぞれアイリスの関連するクラス: セトサ, バージカラー, バージニカのために計算された、スコア、あるいは「ロジット」を含むでしょう。

後で、これらのロジットは tf.nn.softmax 関数により確率へと変換されるでしょう。

 

訓練、評価、そして予測を実装する

モデル関数を作成する最後のステップは予測、評価、そして訓練を実装する分岐コードを書くことです。

モデル関数は誰かが Estimator の train, evaluate, または predict メソッドを呼び出すときいつでも起動されます。モデル関数のためのシグネチャはこのように見えることを思い出してください :

def my_model_fn(
   features, # This is batch_features from input_fn
   labels,   # This is batch_labels from input_fn
   mode,     # An instance of tf.estimator.ModeKeys, see below
   params):  # Additional configuration

3番目の引数, mode に注目しましょう。次のテーブルが示すように、誰かが train, evaluate, または predict を呼び出すとき、Estimator フレームワークは次のように設定された mode パラメータとともに貴方のモデル関数を起動します :

Estimator メソッド

Estimator モード

train()

ModeKeys.TRAIN

evaluate()

ModeKeys.EVAL

predict()

ModeKeys.PREDICT

例えば、classifier と命名したオブジェクトを生成するためにカスタム Estimator をインスタンス化することを仮定します。それから次の呼び出しを行ないます :

classifier = tf.estimator.Estimator(...)
classifier.train(input_fn=lambda: my_input_fn(FILE_TRAIN, True, 500))

それから Estimator フレームワークは貴方のモデル関数を ModeKeys.TRAIN に設定された mode とともに呼び出します。

貴方のモデル関数は3つ総ての mode 値を処理するためのコードを提供しなければなりません。各 mode 値のために、貴方のコードは tf.estimator.EstimatorSpec のインスタンスを返さなければなりません、これは呼び出し元が必要とする情報を含みます。各モードを考察してみましょう。

 

予測する

Estimator の predict メソッドが呼び出されたとき、model_fn は mode = ModeKeys.PREDICT を受け取ります。この場合、モデル関数は予測を含む tf.estimator.EstimatorSpec を返さなければなりません。

モデルは予測を行なう前に訓練されていなければなりません。訓練されたモデルは、Estimator をインスタンス化したときに初期化された model_dir ディレクトリ内でディスク上にストアされます。

このモデルのために予測を生成するコードは次のようなものです :

# Compute predictions.
predicted_classes = tf.argmax(logits, 1)
if mode == tf.estimator.ModeKeys.PREDICT:
    predictions = {
        'class_ids': predicted_classes[:, tf.newaxis],
        'probabilities': tf.nn.softmax(logits),
        'logits': logits,
    }
    return tf.estimator.EstimatorSpec(mode, predictions=predictions)

prediction 辞書は prediction モードで実行するとき貴方のモデルが返すもの総てを含みます。

predictions は次の3つのキー/値ペアを保持します :

  • class_ids はこのサンプルに対するもっとも尤度の高い種のモデルの予測を表わすクラス id (0, 1, または 2) を保持します。
  • probabilities は3つの確率 (このサンプルでは、0.02, 0.95, と 0.03) を保持します。
  • logits は生のロジット値 (このサンプルでは、-1.3, 2.6, と -0.9) を保持します。

その辞書を tf.estimator.EstimatorSpec の predictions パラメータを通して呼び出し元に返します。Estimator の predict メソッドはこれらの辞書を生成します。

 

損失を計算する

訓練評価 の両者のためにモデルの損失を計算する必要があります。これは最適化される 目的 (= objective) です。

tf.losses.sparse_softmax_cross_entropy を呼び出すことにより損失を計算できます。この関数により返される値は最も低く、およそ 0 で、(インデックス・ラベルにおける) 正しいクラスの確率は 1.0 近くです。正しいクラスの確率が減少するとき返される損失値は徐々により大きくなります。

この関数はバッチ全体に渡る平均を返します。

# Compute loss.
loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)

 

評価する

Estimator の evaluate メソッドが呼び出されたとき、model_fn は mode = ModeKeys.EVAL を受け取ります。この場合、モデル関数は、モデルの損失とオプションで一つまたはそれ以上のメトリクスを含む tf.estimator.EstimatorSpec を返さなければなりません。

メトリクスを返すのははオプションですが、多くのカスタム Estimator は少なくとも一つのメトリックを返します。TensorFlow は一般的なメトリクスを計算するための Metrics モジュール tf.metrics を提供します。簡潔さのために、私達は accuracy を返すだけです。tf.metrics.accuracy 関数は私達の予測を真の値に対して、つまり、入力関数により提供されるラベルに対して比較します。tf.metrics.accuracy 関数はラベルと予測が同じ shape を持つことを要求します。ここに tf.metrics.accuracy への呼び出しがあります :

# Compute evaluation metrics.
accuracy = tf.metrics.accuracy(labels=labels,
                               predictions=predicted_classes,
                               name='acc_op')

評価のために返される EstimatorSpec は典型的には次の情報を含みます :

  • loss, これはモデルの損失です。
  • eval_metric_ops、これはオプションのメトリクスの辞書です。

さて、ただ一つのメトリックを含む辞書を作成します。他のメトリクスを計算する場合には、同じ辞書に追加のキー/値ペアとしてそれらを追加します。それから、tf.estimator.EstimatorSpec の eval_metric_ops 引数に辞書を渡します。ここにコードがあります :

metrics = {'accuracy': accuracy}
tf.summary.scalar('accuracy', accuracy[1])

if mode == tf.estimator.ModeKeys.EVAL:
    return tf.estimator.EstimatorSpec(
        mode, loss=loss, eval_metric_ops=metrics)

tf.summary.scalar は TRAIN と EVAL モードの両者において accuracy を TensorBoard で利用可能にします。(More on this later).

 

訓練する

Estimator の train メソッドが呼び出されるとき、model_fn は mode = ModeKeys.TRAIN で呼び出されます。この場合、モデル関数は損失と訓練演算を含む EstimatorSpec を返さなければなりません。

訓練演算の構築は optimizer を必要とするでしょう。私達は tf.train.AdagradOptimizer を使用します、何故ならばデフォルトで Adagrad をまた使用している DNNClassifier を模倣しているからです。tf.train パッケージは多くの他の optimizer を提供します — それらで自由に実験してください。

ここに optimizer を構築するコードがあります :

optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)

次に、先に計算した損失上で optimizer の minimize メソッドを使用して訓練演算を構築します。

minimize メソッドはまた global_step パラメータを取ります。TensorFlow は (訓練実行をいつ終わらせるかを知るために) 処理された訓練ステップ数をカウントするためにこのパラメータを使用します。更に、global_step は TensorBoard グラフが正しく動作するために欠くことができません。単純に tf.train.get_global_step を呼び出して結果を minimize の global_step 引数に渡します。

ここにモデルを訓練するためのコードがあります :

train_op = optimizer.minimize(loss, global_step=tf.train.get_global_step())

訓練のために返される EstimatorSpec は次のフィールド集合を持たなければなりません :

  • loss, これは損失関数の値を含みます。
  • train_op, これは訓練ステップを実行します。

ここに EstimatorSpec を呼び出すコードがあります :

return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)

モデル関数は今では完成しています。

 

カスタム Estimator

Estimator 基底クラスを通してカスタム Estimator を次のようにインスタンス化します :

    # Build 2 hidden layer DNN with 10, 10 units respectively.
    classifier = tf.estimator.Estimator(
        model_fn=my_model,
        params={
            'feature_columns': my_feature_columns,
            # Two hidden layers of 10 nodes each.
            'hidden_units': [10, 10],
            # The model must choose between 3 classes.
            'n_classes': 3,
        })

ここで params 辞書は DNNClassifier の key-word 引数と同じ目的に役立ちます ; つまり、params 辞書は model_fn のコードを変更することなしに貴方の Estimator を構成することを可能にします。

私達の Estimator を使用して訓練、評価して予測を生成するための残りのコードは Premade Estimators の章と同様です。
例えば、次の行はモデルを訓練します :

# Train the Model.
classifier.train(
    input_fn=lambda:iris_data.train_input_fn(train_x, train_y, args.batch_size),
    steps=args.train_steps)

 

TensorBoard

貴方のカスタム Estimator のための訓練結果を TensorBoard で見ることができます。このレポートを見るためには、次のように TensorBoard をコマンドラインから開始します :

# Replace PATH with the actual path passed as model_dir
tensorboard --logdir=PATH

それから、http://localhost:6006 にブラウズすることで TensorBoard をオープンします。

総ての pre-made Estimators は TensorBoard への多くの情報を自動的にロギングします。けれども、カスタム Estimator では、TensorBoard は一つのデフォルト・ログと貴方が TensorBoard にロギングするように明示的に伝えた情報を提供するだけです。貴方が単に作成しただけのカスタム Estimator については、TensorBoard は次を生成します :


TensorBoard は3つのグラフを表示します。

簡単に言えば、3つのグラフが貴方に伝えるものがここにあります :

  • global_step/sec: パフォーマンス・インジケータで、モデルが訓練されるときにどれだけのバッチが毎秒処理されたか (勾配更新) を示します。
  • loss: レポートされる損失です。
  • accuracy: 精度は次の2行により記録されます :
  • eval_metric_ops={‘my_accuracy’: accuracy}), 評価の間。
  • tf.summary.scalar(‘accuracy’, accuracy[1]), 訓練の間。

これらの tensorboard グラフは、貴方の optimizer の minimize メソッドに global_step を渡すことが重要である多くの理由の一つです。それなしではモデルはこれらのグラフのために x-軸を記録できません。

my_accuracy と loss グラフにおいて以下に注意してください :

  • オレンジ色の線は訓練を表します。
  • 青色のドットは評価を表します。

訓練の間、要約 (オレンジの線) はバッチが処理されるにつれて定期的に記録され、それはそれが何故 x-軸範囲に渡るグラフになるかです。

対照的に、評価は evaluate への各呼び出しのためにグラフ上の単一ポイントを生成します。このポイントは evaluation 呼び出し全体に渡る平均を含みます。これはグラフ上で幅を持ちません、何故ならばそれは (単一のチェックポイントから) 特定の訓練ステップにおいてモデル状態から全体的に評価されるからです。

下の図で示されるように、レポーティングを見ることができ、また左側のコントロールを使用して選択的に無効/有効にすることができます。

 

以上



TensorFlow : Guide : Estimators : Estimator のためのデータセット

TensorFlow : Guide : Estimators : Estimator のためのデータセット (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
更新日時 : 07/14/2018
作成日時 : 03/18/2018

* TensorFlow 1.9 でドキュメント構成が変わりましたので調整しました。
* 本ページは、TensorFlow の本家サイトの Guide – Datasets for Estimators を翻訳した上で適宜、補足説明したものです:

* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

tf.data モジュールはクラスのコレクションを含み、これらは簡単にデータをロードし、それを操作し、それを貴方のモデルに運ぶことを可能にします。このドキュメントは2つの単純なサンプルを一通り説明することで API を紹介します :

  • numpy 配列から in-memory データを読む。
  • csv ファイルから行を読む。

 

基本入力

配列からスライスを取ることは tf.data で始めるために最も単純な方法です。

Premade Estimator の章ではデータを Estimator にパイプするために iris_data.py からの次の train_input_fn を記述しています :

def train_input_fn(features, labels, batch_size):
    """An input function for training"""
    # Convert the inputs to a Dataset.
    dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))

    # Shuffle, repeat, and batch the examples.
    dataset = dataset.shuffle(1000).repeat().batch(batch_size)

    # Build the Iterator, and return the read end of the pipeline.
    return dataset.make_one_shot_iterator().get_next()

これをより密接に見てみましょう。

 

引数

この関数は3つの引数を想定しています。
「配列」を想定する引数は numpy.array で配列に変換できるどのようなものでも殆ど受け取ることができます。

一つの例外は タプル で、これは Datasets のために特別な意味を持ちます。

  • features: 生の入力特徴を含む {‘feature_name’:array} 辞書 (または DataFrame) です。
  • labels : 各サンプルのための ラベル を含む配列です。
  • batch_size : 望まれるバッチサイズを示す整数。

premade_estimator.py では iris_data.load_data() 関数を使用してアイリス・データを回収します。貴方はそれを実行して、次のように結果をアンパックすることができます :

import iris_data

# Fetch the data
train, test = iris_data.load_data()
features, labels = train

それからこれに類似した行でこのデータを入力関数に渡しました :

batch_size=100
iris_data.train_input_fn(features, labels, batch_size)

train_input_fn() を一通り見てみましょう。

 

スライス

最も単純なケースでは、tf.data.Dataset.from_tensor_slices 関数は配列を取り配列のスライスを表わす tf.data.Dataset を返します。例えば、mnist 訓練データ を含む配列は shape (60000, 28, 28) を持ちます。これを from_tensor_slices に渡すと 60000 スライスを含む Dataset オブジェクトを返し、各々の一つは 28×28 画像です。

Dataset を返すコードは次のようなものです :

train, test = tf.keras.datasets.mnist.load_data()
mnist_x, mnist_y = train

mnist_ds = tf.data.Dataset.from_tensor_slices(mnist_x)
print(mnist_ds)

これは次の行をプリントし、dataset の項目の shape を示します。dataset はそれが幾つの項目を含むかは知らないことに注意してください。

<TensorSliceDataset shapes: (28,28), types: tf.uint8>

上の dataset は単純な配列のコレクションを表しますが、dataset はこれよりも遥かによりパワフルです。dataset は辞書かタプルのネストされた組み合わせを透過的に処理します。例えば、次のように features が標準的な辞書であることを確かなものとしてから、配列の辞書を辞書の dataset に変換することができます :

dataset = tf.data.Dataset.from_tensor_slices(dict(features))
print(dataset)
<TensorSliceDataset

  shapes: {
    SepalLength: (), PetalWidth: (),
    PetalLength: (), SepalWidth: ()},

  types: {
      SepalLength: tf.float64, PetalWidth: tf.float64,
      PetalLength: tf.float64, SepalWidth: tf.float64}
>

ここで Dataset が構造化された要素を含むとき、Dataset の shape と型は同じ構造を持つことを見ることができます。この dataset は スカラー の辞書を含み、総てが tf.float64 型です。

train_input_fn の最初の行は同じ機能を使用しますが、構造の他のレベルも追加します。それは (features, labels) ペアを含む dataset を作成します。

次のコードは label が int64 型のスカラーであることを示します。

# Convert the inputs to a Dataset.
dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))
print(dataset)
<TensorSliceDataset
    shapes: (
        {
          SepalLength: (), PetalWidth: (),
          PetalLength: (), SepalWidth: ()},
        ()),

    types: (
        {
          SepalLength: tf.float64, PetalWidth: tf.float64,
          PetalLength: tf.float64, SepalWidth: tf.float64},
        tf.int64)>

 

操作

現在 Dataset はデータに渡り固定された順序で一度だけ iterate します、そして一度に単一の要素を生成するだけです。訓練のために使用可能となる前に更なる処理を必要とします。幸い、tf.data.Dataset クラスは訓練のためのデータをより良い準備をするためのメソッドを提供します。入力関数の次の行はこれらのメソッドの幾つかを活用します :

# Shuffle, repeat, and batch the examples.
dataset = dataset.shuffle(1000).repeat().batch(batch_size)

shuffle メソッドは項目をシャッフルするために (それらが通過するとき) 固定長のバッファを使用します。Dataset のサンプル数よりも大きい buffer_size の設定はデータが完全にシャッフルされることを確かなものにします。アイリス・データセットは 150 サンプルを含むだけです。

repeat メソッドは Dataset をそれが最後に達したとき再スタートさせます。エポック数を制限するためには、count 引数を設定します。

batch メソッドはバッチを作成するために、多数のサンプルを集めてそれらをスタックします。これはそれらの shape に次元を追加します。新しい次元は最初の次元として追加されます。次のコードは (前からの) MNIST Dataset 上で batch メソッドを使用します。これは (28, 28) 画像のスタックを表わす 3D 配列を含む Dataset になります :

print(mnist_ds.batch(100))
<BatchDataset
  shapes: (?, 28, 28),
  types: tf.uint8>

最後のバッチがより少ない要素を持つでしょうから dataset は未知のバッチサイズを持つことに注意してください。train_input_fn では、バッチ化の後 Dataset は要素の 1D ベクトルを含みます、そこでは各スカラーは以前は :

print(dataset)
<TensorSliceDataset
    shapes: (
        {
          SepalLength: (?,), PetalWidth: (?,),
          PetalLength: (?,), SepalWidth: (?,)},
        (?,)),

    types: (
        {
          SepalLength: tf.float64, PetalWidth: tf.float64,
          PetalLength: tf.float64, SepalWidth: tf.float64},
        tf.int64)>

 

Return

総ての Estimator の train, evaluate, と predict メソッドは tensorflow テンソル を含む (features, label) ペアを返す入力関数を必要とします。train_input_fn は Dataset を想定されるフォーマットに変換するために次の行を使用します :

# Build the Iterator, and return the read end of the pipeline.
features_result, labels_result = dataset.make_one_shot_iterator().get_next()

結果は TensorFlow テンソル の構造です、Dataset の項目のレイアウトに適合します。これらのオブジェクトが何かそしてそれらとどのように作業するかへのイントロダクションのためには、Introduction を見てください。

print((features_result, labels_result))
({
    'SepalLength': ,
    'PetalWidth': ,
    'PetalLength': ,
    'SepalWidth': },
Tensor("IteratorGetNext_1:4", shape=(?,), dtype=int64))

 

CSV ファイルを読む

Dataset クラスのための最も一般的な現実世界のユースケースはディスク上のファイルからデータをストリームします。tf.data モジュールは様々なファイル・リーダーを含みます。Dataset を利用した csv ファイルからのアイリス・データセットの解析がどのようなものか見てみましょう。

iris_data.maybe_download 関数への次の呼び出しは必要であればデータをダウンロードし、結果としてのファイルのパス名を返します :

import iris_data
train_path, test_path = iris_data.maybe_download()

iris_data.csv_input_fn 関数は Dataset を使用して csv ファイルを解析する代わりの実装を含みます。

ローカル・ファイルから読む Estimator 互換の入力関数をどのように構築するかを見てみましょう。

 

Dataset を構築する

一度にファイルの一行を読むための TextLineDataset オブジェクトを構築することから始めます。それから、サンプルではなくヘッダを含む、ファイルの最初の行をスキップするために skip メソッドを呼び出します :

ds = tf.data.TextLineDataset(train_path).skip(1)

 

csv 行パーサーを構築する

結局のところは必要な (features, label) ペアを生成するためには dataset の各行を解析する必要があるでしょう。

単一行を解析するための関数を構築することから始めます。

次の iris_data.parse_line 関数は tf.decode_csv と幾つかの単純な python コードを使用してこのタスクを成し遂げます :

必要な (features, label) ペアを生成するためには dataset の各行を解析しなければなりません。次の _parse_line 関数は単一行をその特徴とラベルに解析するために tf.decode_csv を呼び出します。Estimator はその特徴が辞書として表わされることを要求しますので、その辞書を構築するために Python の組み込み辞書と zip 関数に依拠します。特徴名はその辞書のキーです。それから特徴辞書から label フィールドを取り除くために辞書の pop メソッドを呼び出します :

# Metadata describing the text columns
COLUMNS = ['SepalLength', 'SepalWidth',
           'PetalLength', 'PetalWidth',
           'label']
FIELD_DEFAULTS = [[0.0], [0.0], [0.0], [0.0], [0]]
def _parse_line(line):
    # Decode the line into its fields
    fields = tf.decode_csv(line, FIELD_DEFAULTS)

    # Pack the result into a dictionary
    features = dict(zip(COLUMNS,fields))

    # Separate the label from the features
    label = features.pop('label')

    return features, label

 

行を解析する

Dataset は (モデルにパイプされている) データを操作するために多くのメソッドを持ちます。最も頻繁に使用されるメソッドは map で、これは Dataset の各要素に変換を適用します。

map メソッドは map_func 引数を取ります、これは Dataset の各項目がどのように変換されるべきかを記述します。


map メソッドは Dataset の各項目を変換するために `map_func` を適用します。

行を解析するためにはそれらが csv ファイルからストリームされるとき、_parse_line 関数を map メソッドに渡します :

ds = ds.map(_parse_line)
print(ds)
<MapDataset
shapes: (
    {SepalLength: (), PetalWidth: (), ...},
    ()),
types: (
    {SepalLength: tf.float32, PetalWidth: tf.float32, ...},
    tf.int32)>

今では単純なスカラー文字列の代わりに、dataset は (features, label) ペアを含みます。

iris_data.csv_input_fn 関数の残りは、基本入力のセクションでカバーされた iris_data.train_input_fn と同一です。

 

試してみましょう

この関数は iris_data.train_input_fn の置き換えとして使用できます。それは次のように estimator に供給するために使用されます :

train_path, test_path = iris_data.maybe_download()

# All the inputs are numeric
feature_columns = [
    tf.feature_column.numeric_column(name)
    for name in iris_data.CSV_COLUMN_NAMES[:-1]]

# Build the estimator
est = tf.estimator.LinearClassifier(feature_columns,
                                    n_classes=3)
# Train the estimator
batch_size = 100
est.train(
    steps=1000,
    input_fn=lambda : iris_data.csv_input_fn(train_path, batch_size))

Estimator は input_fn が引数を取らないことを想定しています。この制限を回避するために、引数を捕捉するために lambda を使用して想定するインターフェイスを提供します。

 

以上



AI導入支援 #2 ウェビナー

スモールスタートを可能としたAI導入支援   Vol.2
[無料 WEB セミナー] [詳細]
「画像認識 AI PoC スターターパック」の紹介
既に AI 技術を実ビジネスで活用し、成果を上げている日本企業も多く存在しており、競争優位なビジネスを展開しております。
しかしながら AI を導入したくとも PoC (概念実証) だけでも高額な費用がかかり取組めていない企業も少なくないようです。A I導入時には欠かせない PoC を手軽にしかも短期間で認知度を確認可能とするサービの紹介と共に、AI 技術の特性と具体的な導入プロセスに加え運用時のポイントについても解説いたします。
日時:2021年10月13日(水)
会場:WEBセミナー
共催:クラスキャット、日本FLOW(株)
後援:働き方改革推進コンソーシアム
参加費: 無料 (事前登録制)
人工知能開発支援
◆ クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
(株)クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com