TensorFlowの基本

Basic Usage を読んで理解した内容を忘れないように、なるべくわかりやすい説明で書いておきます。これだけ知っていれば TensorFlow の大まかな使い方はわかるはず。(ほぼ翻訳みたいなもの)

TensorFlowを使うにあたって

  • 演算をグラフによって表す
  • Session によってグラフ (=演算) を実行する
  • データをテンソルで表す
  • Variable で演算の状態を維持する
  • フィードやフェッチを使って、任意のノードにデータを出し入れする

というのが TensorFlow です。

概要

TensorFlow は演算をグラフで表現するプログラミングシステムで、このグラフは複数のノードで構成されています。

グラフ中のノードは op (operation の略称) といい、 データを保持、演算結果を出力します。このときのデータは 0 もしくは Tensor です。

ここでいう Tensor とは、多次元配列を指します。

TensorFlow では、どんな計算も Session を用いて実行します。Session は Device 上で動かすことができます。Device の存在は CPU, GPU など様々なデバイスに対応できるということを示しているのでしょう。

演算グラフ

TensorFlow のプログラムは、以下の二つの段階で構成されています。

  • グラフを組み立てる構築フェーズ
  • ノードで演算を実行するために Session を扱う実行フェーズ

例えば、ニューラルネットワークを表現し、学習させるためにグラフを組み立てるのが構築フェーズ。実際に学習させるのが実行フェーズです。

プログラムには Python, C, C++ が使われていますが、ライブラリの充実度から Python のほうが簡単に扱えます。

グラフを構築する

入力がないノードで始まり、その出力を演算を実行する他のノードに渡すようにして、グラフを構築します。

op コンストラクタはノードの出力としてオブジェクトを返し、それを入力として他のノードに渡すこともできます。

Python ライブラリにはデフォルトでグラフが用意されています。その扱いの詳細は Graph class documentation を参照してください。

import tensorflow as tf

matrix1 = tf.constant([[3., 3.]])
matrix2 = tf.constant([[2.],[2.]])

product = tf.matmul(matrix1, matrix2)

このグラフには constant() op 二つと matmul() op 一つ、計三つのノードが含まれています。

今の状態はこんな感じです。

\[
matrix1 = \left[
\begin{array}{r}
3 & 3
\end{array}
\right]
\]

\[
matrix2 = \left[
\begin{array}{rr}
2 \\
2 \\
\end{array}
\right]
\]

\[
product = \left[
\begin{array}{r}
3 & 3
\end{array}
\right]
\cdot
\left[
\begin{array}{rr}
2 \\
2 \\
\end{array}
\right]
\]

二つの constant() op が出力を matmul() op に渡すようにしてグラフが作られました。

Session でグラフの演算をする

構築したグラフに従い、Session を用いて演算を実行します。

sess = tf.Session()

result = sess.run(product)
print(result)
# ==> [[ 12.]]

sess.close()

run() メソッドを呼び出すと自動的に並列処理を行い、演算結果を numpy の ndarray オブジェクトで返します。

Session の処理が終わったら close() します。with 構文を使えばもっと簡単に書けます。

with tf.Session() as sess:
  result = sess.run([product])
  print(result)

Device を使うと演算に利用する CPU, GPU も選択可能ですが、今回は GPU 使わないので省きます。

また、分散処理 (distributed session) について、TensorFlow の強みの一つは分散コンピューティングに対応していることだと思いますが、そこまで手が回らないのでこれも省略します。

インタラクティブな使い方

IPython のような対話的に実行できる環境なら Session の代わりに InteractiveSession を使うことができます。

import tensorflow as tf
sess = tf.InteractiveSession()

x = tf.Variable([1.0, 2.0])
a = tf.constant([3.0, 3.0])

x.initializer.run()

sub = tf.sub(x, a)
print(sub.eval())
# ==> [-2. -1.]

sess.close()

InteractiveSession では Session を保持するための変数が必要ありません。先ほどの product がないという意味でしょう。

Tensor

Tensor (テンソル) は、TensorFlow プログラム上で使われるデータ型です。扱われるデータはすべて Tensor であり、ノード間を渡すことができるのも Tensor だけです。

Tensor は多次元配列というもので、スカラーやベクトルもこれに含まれます。

次元呼称
0Scalar[1]
1Vector[1, 2]
2Matrix[[1, 2], [3, 4]]
33-Tensor[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
NN-Tensor

TensorFlow では、この次元を Rank と呼んでいます。さほど難しい説明ではないので、詳しく知りたい人は Tensor Ranks, Shapes, and Types へ。

Variable

変数はグラフの演算中に値を保持しておくためのものです。

state = tf.Variable(0, name="counter")

one = tf.constant(1)
#new_valueはstatusに1を足した値
new_value = tf.add(state, one)
#stateにnew_valueの値を代入
update = tf.assign(state, new_value)

init_op = tf.global_variables_initializer()

with tf.Session() as sess:
  sess.run(init_op)
  print(sess.run(state))
  for _ in range(3):
    sess.run(update)
    print(sess.run(state))

# output:
# 0
# 1
# 2
# 3

7 行目までがグラフの構築フェーズ、その後が演算の実行フェーズです。

state は Variable に指定してあり、init_op で初期化されて 0 の状態です。(最初の output)

その後 for 構文で update が三回実行されています。その間に state は 1 ずつ増加しているので、ノード間の演算で値を保持していることが分かります。

この仕組みのおかげで、例えば、ニューラルネットワークの学習中に学習率を保持しつつも更新し、使用し続けることができます。

Fetch

出力の取得は Session で run() メソッドを呼び出して実行し、Tensor を渡すという仕組みになっています。複数の出力を取得することも可能です。

input1 = tf.constant([3.0])
input2 = tf.constant([2.0])
input3 = tf.constant([5.0])
intermed = tf.add(input2, input3)
mul = tf.mul(input1, intermed)

with tf.Session() as sess:
  result = sess.run([mul, intermed])
  print(result)

# output:
# [array([ 21.], dtype=float32), array([ 7.], dtype=float32)]

Feed

先ほどまでの例では、定数や変数に指定した Tensor の値を予め決めていましたが、フィードを使って任意の操作に代入することも可能です。

フィードは run() メソッドに直接適用する形で使います。適用された run() メソッドのみで有効です。

input1 = tf.placeholder(tf.float32)
input2 = tf.placeholder(tf.float32)
output = tf.mul(input1, input2)

with tf.Session() as sess:
  print(sess.run([output], feed_dict={input1:[7.], input2:[2.]}))

# output:
# [array([ 14.], dtype=float32)]

placeholder に指定していた Tensor にフィードを適用し忘れるとエラーが発生します。

最後に

夕方に書き始めたはずがいつの間にか日付を跨いでいるんですが、それなりに理解が進んだ気がします。(翻訳のレベルには目を瞑る)

公式のドキュメントが充実しているので助かりますね。