ディープラーニングのフレームワークの中で、世界で一番使われているtensorflowの使い方を調べてみました。
見ているだけで基本的な使い方が頭に入ってくるものを目指して、できるだけ細かく(冗長に?)コードを書いているので、初めてtensorflowを使う人向けになればいいな、と願っております。
動機
今まで機会があるごとにchainerやkerasを触ってきたのですが、
kerasに頼らずtensorflow使いたい...!
ということでネット上の転がってる記事を参考に勉強を始めました。
ちなみにtensorflowを使おうと思ったきっかけは主に2つです。
・遺伝アルゴリズムを使ってネットワークを進化させてみたい!(とある教授の実験を再現したい)
・日本語記事の豊富なchainerばっかり使ってると「いかにも日本人らしい」ってなりそう...
tensorflowのとっつきにくさ
まずtensorflowがとっかかりづらい(実は嬉しさでもある)のは、
計算グラフ(フロー)を作成 → 計算を実行
という手順を踏まないといけない部分です。
普通のpythonコードだと
a = 10
b = a + 10
c = b + 10
の各行を実行すると、
aに値が入る → bに値が入る → cに値が入る
ってなりますよね。
じゃあtensorflowだとどうなの、について見ていきましょう。
基本的な計算
まずインポートは以下のように。
import numpy as np
import tensorflow as tf
定数
以下を実行してみてください。
const = tf.constant(10) # 定数を定義
with tf.Session() as sess:
result_const = sess.run(const)
print(result_const) # 10がプリントされる
定数を定義している箇所は
const = tf.constant(10)
ですが、ここではまだ
「constに定数オブジェクトを割り当てる」
という「グラフ」を定義しただけなので、constに10という値は代入されません。
sess.run(const)
で初めて値の代入・計算が実行されます。
返り値はconstの値です。
今の例だとやっていることが単純すぎて逆によくわからないかもしれないので、足し算をしてみましょう。
const_1 = tf.constant(10)
const_2 = tf.constant(30)
added = const_1 + const_2
with tf.Session() as sess:
result = sess.run(added)
print(result) # 10+30の結果である40がプリントされる
最初の3行で
(const_1, const_2) → added
という計算グラフを作成しています。
そして、
sess.run(added)
の返り値は"added"の値そのものになります。
注目すべきは、
sess.run(added)
を実行すると、addedに繋がるまでの2つの計算グラフ、すなわち
「const_1に値を代入」「const_2に値を代入」の両方が自動で実行されることです。
つまり、以下のような書き方をする必要はないということです。
const_1 = tf.constant(10)
const_2 = tf.constant(30)
with tf.Session() as sess:
result_const_1 = sess.run(const_1)
result_const_2 = sess.run(const_2)
result = result_const_1 + result_const_2
print(result)
ただし、const_1とconst_2の値を別の計算グラフで計算したい時には、上のような書き方をする必要があるでしょう。
変数
変数は"tf.Variable( )"で定義しますが、定数の時と同じように実行すると怒られます。
これ、知らないとちょっと焦ります
val = tf.Variable(10) # 変数を定義
with tf.Session() as sess:
result = sess.run(val)
print(result)
tf.Variable( )を使う場合は、初期化してあげる必要があるそうです。
val = tf.Variable(10) # 変数を定義
with tf.Session() as sess:
sess.run(tf.global_variables_initializer()) # ここが必要!
result = sess.run(val)
print(result)
プレースホルダー
「実行するまで値がわからない」変数を扱う時の格納庫的な役割をしてくれます。
tf.constantやtf.Variableと違って、データの型を指定する必要があります。
holder = tf.placeholder(dtype=tf.float32) # 今はまだ値がわからない
const = tf.constant(10, dtype=tf.float32)
added = holder + const
with tf.Session() as sess:
the_value = 40 # この時点で初めて値がわかった!
result = sess.run(added, feed_dict={holder:the_value}) # holderにthe_valueの値を代入してaddedを計算
print(result)
"sess.run( )"時に辞書の形で実際の値を代入してあげます。
注意点
今まで触れませんでしたが、同じデータ型同士でないと計算ができないので、今のように型を明示的に書くことを心がける必要がありそうです。
代入
pythonでの
var = 100
def main():
return var + 1
for i in range(3):
var = main()
print(var)
のように、変数の値をどんどんアップデートする処理をtensorflowで実現したいのですが、Sessionの中で
tf_variable = 101 # tf_variable : tf.Variableオブジェクト
のように値を代入することはできません。
値の代入には
tf.assign("アップデートする変数", "代入する値")
を使います。
var = tf.Variable(100, dtype=tf.int32) # この変数をアップデートしていく
holder = tf.placeholder(dtype=tf.int32) # アップデートする値を代入する用
update = tf.assign(var, holder) # 値をアップデートする処理
def main():
return var + 1
with tf.Session() as sess:
sess.run(tf.global_variables_initializer()) # 変数を初期化
for i in range(3):
result = sess.run(main()) # 1を加えた値を取得
sess.run(update, feed_dict={holder:result}) # update関数を使って、varの値をresultの値にアップデート
print(result) # 101 102 103 の順番でプリントされる
以上を踏まえて
一般的な書き方?
普通は関数などを定義してコードをわかりやすくするので、足し算なら以下のような書き方になるのではないでしょうか。
def main():
x = tf.constant(10)
y = tf.constant(30)
return x + y
with tf.Session() as sess:
result = sess.run(main())
print(result) # 40
はたまた
def add():
x = tf.constant(10)
y = tf.constant(30)
return x + y
z = add() + tf.constant(100)
with tf.Session() as sess:
result = sess.run(z)
print(result) # 140
最後のコードの計算グラフをtensorboardを使って可視化するとこうなりました。
ちゃんと1つの計算グラフになっていますね!
ただし、気をつけなければいけないのは、以下を実行すると怒られることです。
def main():
val_1 = tf.Variable(10, dtype=tf.float32)
val_2 = tf.Variable(20, dtype=tf.float32)
return val_1 * val_2
with tf.Session() as sess:
sess.run(tf.global_variables_initializer()) # 変数を初期化
result = sess.run(main()) # ここで怒られる!
何が問題かというと、main( )を定義しただけではval_1, val_2が定義されたことにならないので、変数を初期化してるつもりができていないことです。
なので、変数はできるだけ関数の外で定義してあげると混乱しにくいと思います。
配列
numpyの配列と同じように計算できます。
def mul(x, y):
return x * y
val_1 = tf.Variable([10, 20], dtype=tf.float32)
val_2 = tf.Variable([30, 40], dtype=tf.float32)
result = mul(val_1, val_2)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer()) # 変数を初期化
result = sess.run(result)
print(result) # [ 300. 800.]
最後に
とりあえず今回は以上で。
次は全結合層をメインにしたネットワーク作って、遺伝アルゴリズムで進化させてみたいと思います。
誤差逆伝播使わないとtensorflowが泣く?
そんなことは知ったこっちゃありません。