TensorFlow : Guide : 低位 API – イントロダクション (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
更新日時 : 07/14/2018
作成日時 : 04/20/2018
* TensorFlow 1.9 でドキュメント構成が変わりましたので調整しました。
* 本ページは、TensorFlow 本家サイトの Guide – Low Level APIs – Introduction を翻訳した上で
適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、適宜、追加改変している場合もあります。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- お住まいの地域に関係なく 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つの具体的なセクションから成るものと考えて良いかもしれません :
- 計算グラフを構築する ( tf.Graph )。
- 計算グラフを実行する ( 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))
以上