Edited at

ニューラルネットの重みを実数値遺伝的アルゴリズムで最適化してみる ~その3:TensorFlow対応【失敗】~

前回作ったOpenAI GymをNeuroEvolution(ニューラルネット+実数値遺伝的アルゴリズム)で学習する実装をTensorFlowを使って高速化できないかなと思って試してみました。

結果としては、失敗しました。。。2~4倍の時間がかかるようになりました。

他の人が無駄な実装をしないように、迷走をしないようにメモとして残しておきます。


TensorFlowに関して改めて復習

TensorFlow化するにあたって、引っかかった点を整理します。


テンソル(Tensor)オブジェクトは数値の配列ではない

numpyと同じような計算ができるのごっちゃになりますが、あくまでも操作(オペレーション)を詰め込んだ箱です。


tensor.py

import tensorflow as tf

import numpy as np

def sum(a , b) :
return a + b

#TensorFlowの計算
tfa = tf.constant([1,2])
tfb = tf.constant([10,20])
tfc = tf.constant([100,200])

print('tfa : {0} : <type={1}>'.format(tfa , type(tfa)))
#>tfa : Tensor("Const:0", shape=(2,), dtype=int32) : <type=<class 'tensorflow.python.framework.ops.Tensor'>>

sum_op = sum(tfa , tfb)
print('sum_op : {0} : <type={1}>'.format(sum_op , type(sum_op)))
#>sum_op : Tensor("add:0", shape=(2,), dtype=int32) : <type=<class 'tensorflow.python.framework.ops.Tensor'>>

sum_op = sum(sum_op , tfc)
print('sum_op : {0} : <type={1}>'.format(sum_op , type(sum_op)))
#>sum_op : Tensor("add_1:0", shape=(2,), dtype=int32) : <type=<class 'tensorflow.python.framework.ops.Tensor'>>

sess = tf.Session()
result = sess.run(sum_op)
print('result : {0} : <type={1}>'.format(result , type(result)))
#>result : [111 222] : <type=<class 'numpy.ndarray'>>

#numpyの計算
npa = np.array([1,2])
npb = np.array([10,20])
npc = np.array([100,200])
npsum = sum(npa , npb)
npsum = sum(npsum , npc)
print('npsum : {0} : <type={1}>'.format(npsum , type(npsum)))
#>npsum : [111 222] : <type=<class 'numpy.ndarray'>>


sess.run()でオペレーションを実行することで初めて計算結果が受け取れます。

変数ではないので、テンソル(Tensor)オブジェクトをnumpyの関数に渡すとエラーになります。


TensorError.py

import tensorflow as tf

import numpy as np
#ReLU関数
def relu(x):
return np.maximum(0, x)

npa = np.array([-1,2])
nprelu = relu( npa )
print('nprelu : {0} : <type={1}>'.format(nprelu , type(nprelu)))
#> nprelu : [0 2] : <type=<class 'numpy.ndarray'>>

tfa = tf.constant([-1,2])
relu_op = relu(tfa )

#> TypeError: Using a `tf.Tensor` as a Python `bool` is not allowed. Use `if t is not None:` instead of `if t:` to test if a tensor is defined, and use TensorFlow ops such as tf.cond to execute subgraphs conditioned on the value of a tensor.



オペレーション作成後にTensorFlowの定数を書き換えても反映されない


TensorConstSum.py

import tensorflow as tf

import numpy as np

def sum(a , b) :
return a + b

tfc = tf.constant([1,2])
b = 10
sum_op = sum(tfc , b)

sess = tf.Session()

result = sess.run(sum_op)
print('result : {0} : <type={1}>'.format(result , type(result)))
#>result : [11 12] : <type=<class 'numpy.ndarray'>>

tfc = tf.constant([10,20])
b = 100
result = sess.run(sum_op)
print('result : {0} : <type={1}>'.format(result , type(result)))
#>result : [11 12] : <type=<class 'numpy.ndarray'>>



TensorFlowの変数を更新するにはassign()を使う必要がある


TensorVariableSum.py

import tensorflow as tf

import numpy as np

def sum(a , b) :
return a + b

tfc = tf.constant([1,2])
tfv = tf.Variable(10)

sum_op = sum(tfc , tfv)

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

result = sess.run(sum_op)
print('result : {0} : <type={1}>'.format(result , type(result)))
#>result : [11 12] : <type=<class 'numpy.ndarray'>>

#変数 更新
assign_op = tfv.assign(100)
sess.run(assign_op)
result = sess.run(sum_op)
print('result : {0} : <type={1}>'.format(result , type(result)))
#>result : [101 102] : <type=<class 'numpy.ndarray'>>



NN+実数値GAのTensorFlow対応

OpenAI GymをNN+実数値GAで学習するとき、1個体のシミュレーションの処理フローは以下のようにしていました。

OpenAI Gymはnumpyを使って実装されているので、TensorFlow化できるのは「NNで行動を推論」の部分だけに。。なおかつ、NNの重みとバイアスをTensorFlowの変数で表現すると、個体毎に変数の更新処理が必要になります。


結果

GoogleColaboratoryのGPUモードとCPUモードで速度計測しています。50個体を評価した平均です(時刻取得や出力にかかるコストもあるので、あくまで目安で)

計測区間
TensorFlowなし(GPU)
TensorFlow対応(GPU)
TensorFlow対応(CPU)

重みとバイアスセット
23[μsec]
21963[μsec]
25191[μsec]

200stepのシミュレート
88587[μsec]
255974[μsec]
186580[μsec]

合計
88610[μsec]
277937[μsec]
211771[μsec]

「重みとバイアスセット」に時間がかかるのは想定内ですが、シミュレートにも時間がかかってます。

1stepのシミュレートの内訳です。

計測区間
TensorFlowなし(GPU)
TensorFlow対応(GPU)
TensorFlow対応(CPU)

NNで行動を推論
132[μsec]
926[μsec]
559[μsec]

OpenAI Gymで1stepシミュレート
36[μsec]
53[μsec]
55[μsec]

合計
168[μsec]
979[μsec]
614[μsec]

高速化したかった「NNで行動を推論」自体に時間がかかってしまってます。そして、GPUよりCPUの方が速い。。TensorFlowのチュートリアル を見る限りでは、メモリのコピーがネックになっていそうな感じです。

「OpenAI Gymで1stepシミュレート」で時間がかかるようになってるのは、コンテキストの切り替えとかですかね??


結論

BPを用いた画像認識のように、1回のオペレーション実行で大量のバッチ処理を行うような学習ではない限り、TensorFlowで実装するメリットはなさそうですね。