Help us understand the problem. What is going on with this article?

Tensorflow 1.5のEager Executionを触ってみた

More than 1 year has passed since last update.

はじめに

2018年1月26日にTensorflow 1.5が公開されました。いや〜ほんとに早いですね。Tensorflowはディープラーニング関連のフレームワークで一番開発が早いですね。やはりGoogle先生の力か。。。もはやデファクト・スタンダードと言ってもいいのではないでしょうか?

ただ初心者の方にとっては少々敷居が高いようです (私自身Tensorflowを素で扱うのは苦手です。)。これはグラフの考え方もあるのかな。。。個人的に、このバージョンからEager executionが実装されたので、その敷居が下がったのではないかと感じています。つまり、(Eager Executionを使う限り) 明示的にグラフと流す値を区別する必要がなくなりました。普段いつもPythonで書いているようにTensorflowも書けばディープラーニングの計算を行うことができます。この記事では、その御利益をわかってもらうためにいくつかのサンプルコードを考えてみることにします。

注意
この記事の内容はTensorflowにおける各種演算まとめで追記したものとほとんど同じですが、新しい記事としてまとめることにしました。

Eager executionについて

Tensorflowによらず、ディープラーニング全体の根底にある考え方として、ネットワーク構造と与える値は別々に考えるというものがあります。つまり、最終的にどういう結果が得られるかというのは、ネットワーク構造どういう値(の集合)を考えるかに依存します。(もちろん最適化法とかいろいろ自由度もありますが、ここではそういう細かいことは省きます。このあたりはポテチ製造から学ぶTensorflowの考え方をご参考ください)

これまでは「値をグラフに流す」ときにrun.Session()を用いて書く必要がありましたが、Eager Executionを用いる限り、その必要がありません。Eager Execution: An imperative, define-by-run interface to TensorFlowではEager Executionのメリットして以下を挙げています。

  • Pythonのツールを用いたデバッグが容易になる。(あとで紹介するようにNumpy型の値を取り出せます。)
  • Pythonの構文を用いた処理が可能
  • 自分で実装したGradient等が利用可能
  • ほとんどすべてのTensorflowの操作が利用可能

ここではわかり易い例として、加算と値の代入のコードを通して触れてみたいと思います。詳しくは記事下の参考文献よりどうぞ。

例1: 加算

これまでTensorflowで定数同士を加算するには

tf_add_non_egex.py
import tensorflow as tf

const1 = tf.constant(2)
const2 = tf.constant(3)
add_op = tf.add(const1, const2) # 2 + 3
with tf.Session() as sess:
    result = sess.run(add_op)
    print(result)
# 5

と書く必要がありました。計算させたい変数に対してsess.runを実行する必要がありました。このようにすることで、変数add_opに値2, 3を流して計算させていました。一方、Eager Executionを使うようにコードを書き換えてみると、

tf_add_with_egex.py
import tensorflow as tf
import tensorflow.contrib.eager as tfe

tfe.enable_eager_execution()

const1 = tf.constant(2)
const2 = tf.constant(3)
add_op = tf.add(const1, const2) # 2 + 3
print(add_op)
# tf.Tensor(5, shape=(), dtype=int32)

となります。with文がなくなった代わりに、add_opprint文で表示させればよくなりました。ここでの注意点としてはまず、tfe.enable_eager_execution()は必ずプログラムの最初に書くようにします。 (Jupyter上で実行する際は、カーネルにメモリーが残っていると

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-2-a35e1dbe978d> in <module>()
      2 import tensorflow.contrib.eager as tfe
      3 
----> 4 tfe.enable_eager_execution()
      5 
      6 const1 = tf.constant(2)

/usr/local/lib/python3.6/site-packages/tensorflow/python/framework/ops.py in enable_eager_execution(config, device_policy)
   4892   else:
   4893     raise ValueError(
-> 4894         "tfe.enable_eager_execution has to be called at program startup.")
   4895 
   4896 

ValueError: tfe.enable_eager_execution has to be called at program startup.

というエラーが出るので(例)、カーネル再起動は必須です。

ちなみに出力結果の型を見てみると、

type(add_op)
# EagerTensor

となっています。そこでためしにこの値に3をたしてみると、

add_op + 3
# <tf.Tensor: id=5, shape=(), dtype=int32, numpy=8>

となり、numpyの値としては正しく計算されています。この値を取り出したいときは、numpy()メソッドを使います。つまり

add_op2 = add_op + 3
print(add_op2.numpy())
# 8

とすることで、値を取り出せます。ここで型は

type(add_op2)
# numpy.int32

となっているので、通常の四則演算が可能になっています。

例2: 値の代入

同様にして、値の代入のコード

tf_substitute_non_egex.py
import tensorflow as tf

# see https://www.tensorflow.org/api_guides/python/state_ops
print("\n変数\n=====================")

w = tf.Variable(tf.zeros([3, 2])) #変数(行列)wの宣言. 初期値はゼロ行列

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    print("w = %s" % sess.run(w))
    sess.run(tf.assign(w, tf.ones([3, 2])))

    wn = sess.run(w)
    print("w = %s" % wn)

を書き換えてみると

tf_substitute_with_egex.py
import tensorflow as tf
import tensorflow.contrib.eager as tfe

tfe.enable_eager_execution()

# see https://www.tensorflow.org/api_guides/python/state_ops
print("\n変数\n=====================")

w = tfe.Variable(tf.zeros([3, 2])) #変数(行列)wの宣言. 初期値はゼロ行列

tf.global_variables_initializer()

print("w = %s" % w)
tf.assign(w, tf.ones([3, 2]))

wn = w
print("w = %s" % wn)

# 変数
# =====================
# w = <tf.Variable 'Variable:0' shape=(3, 2) dtype=float32, numpy=
# array([[0., 0.],
#        [0., 0.],
#        [0., 0.]], dtype=float32)>
# w = <tf.Variable 'Variable:0' shape=(3, 2) dtype=float32, numpy=
# array([[1., 1.],
#        [1., 1.],
#        [1., 1.]], dtype=float32)>

となります。ここで変数を宣言するときにtfe.Variableを使うことに注意します。(余談ですが、Eager Executionを使うときはtf.Placeholderは使えません。incompatibleエラーがでます。) それ以外はwith文を全部取り除くことで、先ほどと同様に書き換えることができます。値をNumpy型で取り出したいときは

print(w.numpy()

# array([[1., 1.],
#       [1., 1.],
#       [1., 1.]], dtype=float32)

(wnも同様)とすればでき、numpy()メソッドを使えばNumpy arrayとして取り出し、Numpyの四則演算を行うことができます。

参考文献

hiroyuki827
SDET 興味ある言語: Python, JavaScript その他興味: メインフレーム, Zowe, VSCode Plugin Development Certified Jenkins Engineer取得 *おことわり* Qiitaでの投稿内容は私自身の見解であり、所属会社の立場、戦略、意見を代表するものではありません。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした