TensorFlowを始めてみることにした。
チュートリアルとしてMNIST等はすでに先人の日本語訳があるため、見当たらなかったGetting Started With TensorFlow (https://www.tensorflow.org/get_started/get_started) (2017/3/11現在)を訳すことにした。
間違い等、ご指摘下さい。
#Getting Started With TensorFlow
このガイドであなたはTensorFlowでのプログラミングの準備ができます。このガイドを使う前に、TensorFlowをインストールして下さい。このガイドを最大限活用するには以下のことを知っている必要があります。
- Pythonでのプログラムの仕方
- 配列(arrays)について最低限少しでも知っていること
- 理想的には何かしら機械学習について。もし少し、または全く知らなかったとしても、その時はこれがあなたの読むべき最初のガイドとなるでしょう。
TensorFlowは複数のAPIを提供します。最も低レベルのAPI(TensorFlow Core)はあなたに完全なプログラミングの制御(control)を与えます。機械学習研究者やモデルに対する細かいレベルの制御を必要とするその他方々にはTensorFlow Coreをおすすめします。より高いレベルのAPIは通常TensorFlow Coreよりも習得や使用が簡単です。加えて、これらの高レベルAPIは繰り返しのタスクを簡単にし、より異なるユーザ間でより一貫性のあるようにします。tf.contrib.learnのような高レベルAPIはデータセットや、推定器(estimators)や、訓練と推論(inference)を管理することを助けます。TensorFlowの高レベルAPIのうちいくつか(それらのメソッド名にはcontrib
が含まれています)は開発中のものです。contrib
メソッドの中には次のTensorFlowのリリースで変更されたり廃止されるものがあるかもしれません。
このガイドはTensorFlow Coreから始めます。その後で、同じモデルをtf.contrib.learnで実装する方法を説明します。TensorFlow Coreの原理を知っていることはよりコンパクトな高レベルAPIを使うときに内部的に物事がどのように動作するかについてのとてもよいメンタルモデルをもたらします。
#Tensors
TensorFlowのデータの中心単位(central unit)はテンソルです。テンソルは任意の次元数の配列(array)の形をしたprimitive valuesの組(set)から成ります。
3 # ランク0のテンソル; これは shapeが[]であるスカラーです。
[1. ,2., 3.] # ランク1のテンソル; これは shapeが[3]のベクトルです。
[[1., 2., 3.], [4., 5., 6.]] # ランク2のテンソル; shape [2, 3]の行列です。
[[[1., 2., 3.]], [[7., 8., 9.]]] # shape [2, 1, 3]の、ランク3のテンソル
##TensorFlow Core tutorial
TensorFlowをインポートする
TensorFlowインポートのの標準的な記述は下記です:
import tensorflow as tf
これによりPythonがすべてのTensorFlowのクラス、メソッド、シンボルにアクセスできるように成ります。本ドキュメントの大半はすでにこれを実行していることを前提しています。
###計算グラフ
あなたはTensorFlow Coreプログラムは2つの分離したセクションから構成されると考えるかもしれません。
- 計算グラフを構築する
- 計算グラフを実行する
計算グラフは複数ノードのグラフに配置された一連のTensorFlowの操作です。(A computational graph is a series of TensorFlow operations arranged into a graph of nodes.)簡単な計算グラフを構築してみましょう。それぞれのノードは入力としてゼロまたはそれ以上のテンソルを受け取り、ひとつのテンソルを出力します。ノードの一つの型は定数(constant)です。すべてのTensorFlowの定数は共通に、入力を取らず、その内部に保存された値を出力します。2つの浮動小数テンソルnode1
とnode2
を以下のように作成できます:
node1 = tf.constant(3.0, tf.float32)
node2 = tf.constant(4.0) # 暗黙に tf.float32 となる
print(node1, node2)
最後の print 文の結果はこのようになります
Tensor("Const:0", shape=(), dtype=float32) Tensor("Const_1:0", shape=(), dtype=float32)
ノードをprintしても期待するような3.0
や4.0
の値を出力しないことに注意して下さい。その代わり、それらは評価された(evaluated)時に、3.0や4.0をそれぞれ出力するようなノードです。ノードを適切に評価するために、計算グラフを**セッション(session)**の中で計算しなければなりません。セッションはTensorFlowランタイムの制御と状態をカプセル化します。
次のコードはSession
オブジェクトを作成し、その後node1
とnode2
を評価するのに計算グラフを十分走らせるためにそのrun
メソッドを呼び出します。あるセッションで下記のように計算グラフを実行すると:
sess = tf.Session()
print(sess.run([node1, node2]))
期待通りの3.0と4.0の値を見ることができます:
[3.0, 4.0]
Tensor
ノードとオペレーション(operations)(オペレーションもノードです。)を結合することでより複雑な計算を構築することができます。
たとえば、下記のように2つの定数ノードを加算して新しいグラフを作ることができます:
node3 = tf.add(node1, node2)
print("node3: ", node3)
print("sess.run(node3): ",sess.run(node3))
最後の2つのprint文は以下を出力します。
node3: Tensor("Add_2:0", shape=(), dtype=float32)
sess.run(node3): 7.0
TensorFlowはTensorBoardという計算グラフの画像を表示することができるユーティリティを提供します。これはTensorBoardがグラフを視覚化する方法を示すスクリーンショットです:
このままでは、このグラフはいつでも同じ結果を出力するだけなので特に面白くありません。グラフは**プレースホルダ(placeholders)**として知られるように外部の入力を受け入れるためにパラメータ化できます。プレースホルダは値を後から供給するための予言です。
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b # + provides a shortcut for tf.add(a, b)
先に上げた3行は関数またはラムダのようなものです。2つの入力パラメータ(aとb)を定義して、その後それらの操作(operation)を定義します。このグラフは複数の入力で評価できます。具体的な値をこれらのプレースホルダに提供するTensorを特定するために、feed_dictパラメータを使うことによって。
print(sess.run(adder_node, {a: 3, b:4.5}))
print(sess.run(adder_node, {a: [1,3], b: [2, 4]}))
出力結果は
7.5
[ 3. 7.]
TensorBoardでは、グラフはこのように見えます:
別の操作を加えることによりもっと複雑な計算グラフを作ることができます。たとえば、
add_and_triple = adder_node * 3.
print(sess.run(add_and_triple, {a: 3, b:4.5}))
出力結果は
22.5
先の計算グラフはTensorBoardでは次のように見えます:
機械学習においては通常、上記の一つのような曖昧な入力を取ることのできるモデルが欲しいものです。モデルを学習可能(trainable)にするため、同じ入力に対して新たな出力を得ることができるようにグラフを修正出来る必要があります。Variablesは学習可能なパラメータをグラフに追加することを可能にします。それらは型(type)と初期値で構成されます:
W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b
定数はtf.constant
を呼んだ時に初期化され、その値は変わることはありません。対象的に、変数(variables)はtf.Variable
を呼んでも初期化されません。TensorFlowプログラムですべての変数を初期化するためには明示的に以下のような特別な操作をしなければなりません:
init = tf.global_variables_initializer()
sess.run(init)
init
がすべてのグローバル変数を初期化するTensorFlow sub-graphのハンドルであると認識することは重要です。sess.run
を呼ぶまで、それらの変数は初期化されません。
x
はプレースホルダなので、次のようにいくつかの値x
についてlinear_model
を同時に評価できます:
print(sess.run(linear_model, {x:[1,2,3,4]}))
出力は
[ 0. 0.30000001 0.60000002 0.90000004]
私達はモデルを作成しましたが、それがどのくらい良いかを知りません。モデルを訓練データで評価するために、望みの値を提供するy
プレースホルダが必要であり、誤差関数を書く必要があります。
誤差関数は現在のモデルが提供されたデータからどのくらい離れているかを測定します。線形回帰には標準誤差モデル(standard loss model)を使うことにしますが、それは現在のモデルと提供されたデータ間の誤差(deltas)の2乗を加算したものです。linear_model-y
はそれぞれの要素がサンプルの誤差(example's error delta)に対応しているベクトルを作成します。それらの誤差を2乗するためにtf.square
を呼び出します。その後、tf.reduce_sum
を使って、すべてのサンプルの誤差を抽出するひとつのスカラーを作るためにすべての2乗誤差を合計します:
y = tf.placeholder(tf.float32)
squared_deltas = tf.square(linear_model - y)
loss = tf.reduce_sum(squared_deltas)
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]}))
誤差値は
23.66
W
とb
に対して完璧な値である-1と1を手動で再度割り当てることこれを改善することができます。変数はtf.Variable
に提供される値へと初期化されますが、tf.assign
のような操作で変更されます。たとえば、W=-1
とb=1
は私達のモデルに最適なパラメータです。下記に従ってW
とb
を変更できます:
fixW = tf.assign(W, [-1.])
fixb = tf.assign(b, [1.])
sess.run([fixW, fixb])
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]}))
最後のprintは誤差がゼロとなったことを示します。
0.0
W
とb
の「完璧な」値を推測しましたが、機械学習の全体(whole point)は正しいモデルパラメータを自動的に発見することです。
これをどうやって達成するか次のセクションで見ていきます。
tf.train API
機械学習の完全な議論はこのチュートリアルの範囲を超えています。しかし、TensorFlowはそれぞれの変数を誤差関数を最小化するためにゆっくりと変更する**オプティマイザ(optimizers)を提供します。最も簡単なオプティマイザは勾配降下法(gradient descent)**です。それは誤差関数のそれぞれの変数に関する微分の大きさによってそれぞれの変数を修正します。一般に、記号的な微分を手動で計算することは退屈ですしエラーを起こしやすいです。結果的に、TensorFlowはただtf.gradients
関数を使ってモデルの記述を与えるだけで自動で微分を計算できます。簡単に言うと、オプティマイザが通常これをあなたの代わりに行います。たとえば、
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
sess.run(init) # reset values to incorrect defaults.
for i in range(1000):
sess.run(train, {x:[1,2,3,4], y:[0,-1,-2,-3]})
print(sess.run([W, b]))
最後のモデルパラメータは:
[array([-0.9999969], dtype=float32), array([ 0.99999082], dtype=float32)]
実際に機械学習ができました!この簡単な線形回帰は多くのTensorFlow coreのコードを必要としませんが、モデルにもっと複雑なモデルとメソッドを供給するにはもっと多くのコードが必要です。したがって、TensorFlowは共通のパターンや構造、機能についてより高次の抽象化を提供します。それらの抽象化の使い方は次のセクションで学びます。
####完全なプログラム
学習可能な線形回帰モデルの完全版を下記に示します:
import numpy as np
import tensorflow as tf
# モデルパラメータ
W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
# モデルの入力と出力
x = tf.placeholder(tf.float32)
linear_model = W * x + b
y = tf.placeholder(tf.float32)
# 誤差
loss = tf.reduce_sum(tf.square(linear_model - y)) # sum of the squares
# オプティマイザ
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
# 訓練データ
x_train = [1,2,3,4]
y_train = [0,-1,-2,-3]
# 訓練ループ
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) # reset values to wrong
for i in range(1000):
sess.run(train, {x:x_train, y:y_train})
# 訓練の精度を評価します
curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x:x_train, y:y_train})
print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_loss))
実行すると下記のようになります:
W: [-0.9999969] b: [ 0.99999082] loss: 5.69997e-11
このもっと複雑なプログラムはTensorBoardで可視化できます
tf.contrib.learn
tf.contrib.learn
は機械学習の動作をシンプル化する高レベルのTensorFlowライブラリです。下記を含んでいます。
- 訓練ループを実行する
- 評価ループを実行する
- データセットを管理する
- 供給(feeding)を管理する
tf.contrib.learn
は多くの共通モデルを定義しています。
####基本的な使い方
tf.contrib.learn
でシンプルな線形回帰プログラムがどのくらい簡単になるか見て下さい:
import tensorflow as tf
# NumPyはデータを読み込み、操作、前処理するためによく使われます。
import numpy as np
# 特徴のリストを宣言します。今はひとつの実数の特徴しか使いません。もっと複雑で便利な
# 多くの他のタイプの列があります
features = [tf.contrib.layers.real_valued_column("x", dimension=1)]
# 推定器(estimator)は訓練(フィッティング)と評価(推定)するためのフロントエンドです。
# 線形回帰、ロジスティック回帰、線形分類、ロジスティック分類、
# そして多くのニューラルネットワーク分類器や回帰のように
# 多くのすでに定義された型があります。
# 次のコードで線形分類の推定器が使用できます。
estimator = tf.contrib.learn.LinearRegressor(feature_columns=features)
# TensorFlowはデータセットを読み込んでセットアップするための多くのヘルパーメソッドを含んでいます。
# ここでは`numpy_input_fn`を使います。
# 必要なデータのバッチ(num_epochs)数と
# それぞれのバッチがどのくらい大きくなるかを関数に与えます。
x = np.array([1., 2., 3., 4.])
y = np.array([0., -1., -2., -3.])
input_fn = tf.contrib.learn.io.numpy_input_fn({"x":x}, y, batch_size=4,
num_epochs=1000)
# `fit`メソッドを呼んで訓練データセットを与えることで
# 1000回の訓練ステップを起動できます。
estimator.fit(input_fn=input_fn, steps=1000)
# ここで今回のモデルがどのくらい良いかを評価します。実際の例では
# 過学習を防ぐために分割検証(separate validation)を使ってデータセットをテストしたいものです。
estimator.evaluate(input_fn=input_fn)
実行すると、結果はこのようになります
{'global_step': 1000, 'loss': 1.9650059e-11}
####カスタムモデル
tf.contrib.learn
は定義済みモデルの中にあなたを閉じ込めておくわけではありません。TensorFlowの中に組み込まれていないカスタムモデルを作りたいとしましょう。tf.contrib.learn
のデータセットや、データ提供(feeding)、訓練等の高レベルの抽象化をそのまま保持することができます。説明のために、より低いレベルのTensorFlow APIに関する知識を使ってLinearRegressor
と等しい独自のモデルの作り方を示します。
tf.contrib.learn
で動作するカスタムモデルを定義するには、tf.contrib.learn.Estimator
を使います。
実際のところ、 tf.contrib.learn.LinearRegressor
は tf.contrib.learn.Estimator
のサブクラスです。Estimator
をサブクラス化する代わりに、tf.contrib.learn
に自身がどのように予測や訓練数、誤差を評価するのかを伝える関数 model_fn
に単に Estimator
を提供することにします。コードは次のようになります:
import numpy as np
import tensorflow as tf
# 特徴のリストを宣言します。今はひとつの実数の特徴しか使いません
def model(features, labels, mode):
# 線形モデルを構築して値を予測します
W = tf.get_variable("W", [1], dtype=tf.float64)
b = tf.get_variable("b", [1], dtype=tf.float64)
y = W*features['x'] + b
# 誤差 サブグラフ(sub-graph)
loss = tf.reduce_sum(tf.square(y - labels))
# 訓練 サブグラフ(sub-graph)
global_step = tf.train.get_global_step()
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = tf.group(optimizer.minimize(loss),
tf.assign_add(global_step, 1))
# ModelFnOpsは適切な機能性(funcrionality)のために
# 今まで作ったサブグラフを結合します
return tf.contrib.learn.ModelFnOps(
mode=mode, predictions=y,
loss=loss,
train_op=train)
estimator = tf.contrib.learn.Estimator(model_fn=model)
# データセットの定義
x = np.array([1., 2., 3., 4.])
y = np.array([0., -1., -2., -3.])
input_fn = tf.contrib.learn.io.numpy_input_fn({"x": x}, y, 4, num_epochs=1000)
# 訓練
estimator.fit(input_fn=input_fn, steps=1000)
# モデルを評価します
print(estimator.evaluate(input_fn=input_fn, steps=10))
実行結果は次のようになります
{'loss': 5.9819476e-11, 'global_step': 1000}
カスタムしたmodel()
関数の内容が低レベルAPIから手動でモデルを訓練するループととても似通っていることに注意して下さい。
##次のステップ
TensorFlowの役に立つ基礎知識はわかりました。もっと学べるようにいくつかのチュートリアルを用意しています。もし機械学習の初学者ならMNIST for beginnersを、そうでないならDeep MNIST for expertsをご覧ください。
特に断りがある場合を除き、このページの内容はCreative Commons Attribution 3.0 License、コードはApache 2.0 Licenseのもとでライセンスされます。詳細はSite Policiesをご覧下さい。
JavaはOracle社とその関連会社の登録商標です。
最終更新日 2017年3月8日