- 投稿日:2019-07-28T15:53:58+09:00
WindowsのPython embeddable版でTensorFlowを使う
【内容】
WindowsのPython embeddable版でTensorFlowを使おうとして少しハマったので、解決方法を共有します。
【前提条件】
WindowsのPython embeddable版 + pip がインストールされていることが前提です。
embeddable版のインストール方法は下記の記事を参照してください。
【Windowsで環境を極力汚さずにPythonを動かす方法 (Python embeddable版)】また、TensorFlowをWindowsで動かすためには「Microsoft Visual C++ 2015 再頒布可能パッケージ Update 3」が必要になります。
Python embeddable版を動かすためにも必要ですので、すでにインストールされているはずです。最後に2019年07月28日現在、Windows環境でTensorFlowが対応しているのはPython3.5/3.6/3.7の64bit版のみになります。
なお、本記事で検証したバージョンは以下になります。
- Python 3.5.4 embeddable 64bit
- Python 3.6.7rc2 embeddable 64bit
- Python 3.7.3 embeddable 64bit
- Python 3.7.4 embeddable 64bit
- TensorFlow 1.11.0, 1.13.0, 1.13.1, 1.14.0
【pipを用いたTensorFlowのインストール方法】
Python3.5/3.6/3.7の64bit版では下記のコマンドを実行することでTensorFlowをインストールすることが可能です。
(pythonのインストールフォルダ)\Scripts\pip install tensorflow または (pythonのインストールフォルダ)\Scripts\pip install tensorflow-gpuただし、embeddable版ではエラーが発生して失敗します。
バージョンによって原因と解決方法が異なりますので、それぞれについて解説します。先に投稿した【Windowsで環境を極力汚さずにPythonを動かす方法 (Python embeddable版)】の手順を踏んでいる場合はエラーなくインストールできるはずですので、これ以降読む必要はありません。
【Python 3.5.4 embeddableの場合】
embeddable版はpython本体をzipファイルの中に収めていますが、ある特定モジュールのインストールスクリプトが、このzipファイルの中を参照できないようです。
よって、以下の手順でzipファイルを解凍して、その中身をzipファイルと同じ名前のフォルダに保存することで、問題を回避できます。【対応方法】
- pythonをインストールしたフォルダ内に「python35.zip」というZIPファイルが存在します。 このファイルの名前を「__python35.zip」に変更します。
- pythonをインストールしたフォルダ内に新規フォルダを作成し、名前を「python35.zip」とします。
- 先ほど名前を変更したZIPファイル「__python35.zip」の中身を「python35.zip」フォルダ内に解凍します。
- 最後に
(pythonのインストールフォルダ)\Scripts\pip
コマンドを実行して、TensorFlowをインストールします。【Python 3.6.7rc2 embeddableの場合】(3.7.2、3.7.3、3.7.4でも確認済み)
通常Pythonはカレントディレクトリに存在するPythonファイルをimportできるのですが、embeddable版ではパスが通らずアクセスできない状態になっています。
このため、特定のモジュールのインストールスクリプトが自身のモジュールを参照できずにエラーになってしまいます。
そこで、下記の方法でカレントディレクトリにパスを通すことで、問題を回避します。【対応方法】
- pythonをインストールしたフォルダ内に「current.pth」というファイルを作成します。 (ファイル名は何でも良いですが、拡張子を「.pth」としてください)
- 「current.pth」をメモ帳などで開いて
import sys; sys.path.append('')
と記載して保存します。- 最後に
(pythonのインストールフォルダ)\Scripts\pip
コマンドを実行して、TensorFlowをインストールします。【動作確認】
下記のコマンドを実行して、TensorFlowのバージョンが表示されるか確認します。
(pythonをインストールしたフォルダ)\python -c "import tensorflow as tf; print(tf.__version__)"【結果】
1.14.0【動作確認2】
TensorFlowのチュートリアル(手書き数字識別)を実行します。
mnist_test.pyimport tensorflow as tf mnist = tf.keras.datasets.mnist (x_train, y_train),(x_test, y_test) = mnist.load_data() x_train, x_test = x_train / 255.0, x_test / 255.0 model = tf.keras.models.Sequential([ tf.keras.layers.Flatten(), tf.keras.layers.Dense(512, activation=tf.nn.relu), tf.keras.layers.Dropout(0.2), tf.keras.layers.Dense(10, activation=tf.nn.softmax) ]) model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) model.fit(x_train, y_train, epochs=5) print(model.evaluate(x_test, y_test))【実行】
(pythonをインストールしたフォルダ)\python mnist_test.py【結果 TF_1.11.0 CPU版】
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz 11493376/11490434 [==============================] - 1s 0us/step Epoch 1/5 2018-10-19 11:11:41.986927: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 60000/60000 [==============================] - 14s 228us/step - loss: 0.1996 - acc: 0.9413 Epoch 2/5 60000/60000 [==============================] - 13s 221us/step - loss: 0.0797 - acc: 0.9752 Epoch 3/5 60000/60000 [==============================] - 13s 219us/step - loss: 0.0524 - acc: 0.9834 Epoch 4/5 60000/60000 [==============================] - 13s 218us/step - loss: 0.0371 - acc: 0.9880 Epoch 5/5 60000/60000 [==============================] - 13s 219us/step - loss: 0.0266 - acc: 0.9915 10000/10000 [==============================] - 0s 46us/step [0.08852570028939517, 0.9753]【結果 TF_1.14.0 GPU版】
2019-07-28 15:35:49.126687: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 2019-07-28 15:35:49.135333: I tensorflow/stream_executor/platform/default/dso_loader.cc:42] Successfully opened dynamic library nvcuda.dll 2019-07-28 15:35:49.851931: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1640] Found device 0 with properties: name: GeForce MX150 major: 6 minor: 1 memoryClockRate(GHz): 1.5315 pciBusID: 0000:01:00.0 2019-07-28 15:35:49.860067: I tensorflow/stream_executor/platform/default/dlopen_checker_stub.cc:25] GPU libraries are statically linked, skip dlopen check. 2019-07-28 15:35:49.868413: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1763] Adding visible gpu devices: 0 2019-07-28 15:35:50.629424: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1181] Device interconnect StreamExecutor with strength 1 edge matrix: 2019-07-28 15:35:50.633160: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1187] 0 2019-07-28 15:35:50.635637: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1200] 0: N 2019-07-28 15:35:50.639866: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1326] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 1360 MB memory) -> physical GPU (device: 0, name: GeForce MX150, pci bus id: 0000:01:00.0, compute capability: 6.1) Epoch 1/5 60000/60000 [==============================] - 6s 98us/sample - loss: 0.2195 - acc: 0.9353 Epoch 2/5 60000/60000 [==============================] - 5s 82us/sample - loss: 0.0955 - acc: 0.9705 Epoch 3/5 60000/60000 [==============================] - 5s 81us/sample - loss: 0.0684 - acc: 0.9787 Epoch 4/5 60000/60000 [==============================] - 5s 82us/sample - loss: 0.0524 - acc: 0.9828 Epoch 5/5 60000/60000 [==============================] - 5s 82us/sample - loss: 0.0449 - acc: 0.9851 10000/10000 [==============================] - 0s 48us/sample - loss: 0.0768 - acc: 0.9751 [0.07682957069921541, 0.9751]
- 投稿日:2019-07-28T01:18:43+09:00
[Tensorflow2.0] GraphとEagerで動作するsummaryの出力方法
概要
本記事はTensorflow1系に慣れている方を想定した、Tensorflow2におけるsummaryの使い方の解説です。
Tensorflow 2.0は、Eager Modeという強力な機構が入りましたが、パフォーマンスを改善するためには、@tf.functionによるGraph Modeを併用する必要があります。本記事では、EagerとGraph両方において意図した通りにsummaryを出す方法を解説します。本記事はすべてtensorblow 2.0 beta1で検証を行っています。
Eagerモードでのsummary出力
基本編
もっとも基本的なsummary出力コードは以下のようになります。
import tensorflow as tf writer = tf.summary.create_file_writer(logdir="log") writer.set_as_default() x = tf.Variable(1, name="x") for i in range(100): x.assign_add(1) tf.summary.scalar("x", x, step=i, description="first variable")tf.summary.create_file_writerとset_as_defaultを用いることで、それ以降のsummary出力先を指定することができます。その後、tf.summary.scalarで実行時のxの値を出力します。
TF1系との大きな違いは, scalarの引数にstepが入ったことでしょう。TF1系と異なり、global stepの概念が無くなっているため、個々の命令において何step目の出力か明記する必要があります。
また、descriptionで出力の説明文を入れられるようになったのは面白い変化です。下図のようにTensorboard上でiボタンを押すと説明が見られるようになりました。step指定の省略
ここのsummary.scalarにおいてstep数を明記するのは煩雑ですし、階層が深い関数では何step目かという情報を持っていないことがあります。そこでset_step関数を用いることで、デフォルトのstepを指定することができます。
for i in range(100): x.assign_add(1) tf.summary.experimental.set_step(i) tf.summary.scalar("x", x, description="first variable")set_stepは値ではなく参照を保持するため, ループ内で毎回set_stepする必要はありません。stepを表す変数をnumpy.arrayに
することで、ループ外で一度set_stepするだけでよくなります。global_step = np.array(1, dtype=np.int64) tf.summary.experimental.set_step(global_step) for i in range(100): global_step += 1 x.assign_add(1) tf.summary.scalar("x", x, description="first variable")ただし, global_stepは必ずtf.Variableやnumpy.arrayのようなmutableなオブジェクトを渡してください。もし, global_stepにただの数値をしてしまった場合, tf.summary.scalarはset_step時の値を参照し続けます。以下は不適切な例です。
global_step = 1 tf.summary.experimental.set_step(global_step) for i in range(100): global_step += 1 x.assign_add(1) tf.summary.scalar("x", x, description="first variable")summaryの階層化
summaryが増加した際に、たくさんのsummaryが並んでいると邪魔なので、summaryをいくつかのグループに分割することが可能です。スラッシュ/区切りのsummary名をつけることで、summaryはグループ化されます。
以下のコードではvariablesというグループに複数のsummaryをまとめています。
tf.summary.scalar("variables/x", x, step=i, description="first variable") tf.summary.scalar("variables/y", y, step=i, description="second variable")summary名に階層を入れる代わりに、summary_scopeを用いることも可能です。なお、summary_scopeではだけでなく、これまで通りname_scopeでもsummaryは階層化されます。
with tf.summary.experimental.summary_scope("variables"): tf.summary.scalar("x", x, step=i, description="first variable") tf.summary.scalar("y", y, step=i, description="second variable")周期的にsummaryを出力する
summaryを学習の各iterationで出力すると、ログファイルサイズが膨大になってしまいます。そこで、100 iterationsごとに1度summaryを出力するような措置が必要になります。これをEagerで実現する、最も簡単な方法はif文をscalarの前に置くことです。以下は7 iterationsに1度summaryを出力しています。
x = tf.Variable(1, name="x") global_step = tf.Variable(1, dtype=tf.int64) tf.summary.experimental.set_step(global_step) for i in range(100): global_step.assign_add(1) x.assign_add(1) #if global_step % 7 == 0: if tf.equal(global_step % 7, 0): tf.summary.scalar("x", x, description="first variable")重要なのはglobal_step%7 == 0 ではなく, equalを用いている点です。global_step%7 == 0とした場合, global_step%7はTensorを返す(global_stepがVariableなので)のですが、Tensorと数値の比較は常にFalseとなるため、summaryが一切出力されなくなります。
このif文を用いた方法は直感的であるものの、summaryの数が増えると管理が面倒になります。そこでもっとも現実的な方法は、record_ifを用いる方法です。ソースコードを見ると大体使い方がわかります。
should = lambda : tf.equal(global_step % 7, 0) with tf.summary.record_if(should): for i in range(100): global_step.assign_add(1) x.assign_add(1) tf.summary.scalar("x", x, description="first variable")record_ifに与えた関数が、summaryの呼び出しのたびに評価され、結果がTrueになるとsummaryが出力されます。実はrecord_summaries_every_n_global_stepsという関数を使えばもっと簡潔に記述できるのですが、API一覧に記載されていないため、あまり使わない方が良いと思います。
from tensorflow.python.ops.summary_ops_v2 import record_summaries_every_n_global_steps with record_summaries_every_n_global_steps(7, global_step): for i in range(100): global_step.assign_add(1) x.assign_add(1) tf.summary.scalar("x", x, description="first variable")Graphモードでのsummary出力
Graphモードでのsummary出力も、基本はEagerモードと同様です。以下を実行すると、y のsummaryが記録されます。
@tf.function def process(x): y = x * 10 tf.summary.scalar(name="y", data=y) return y writer = tf.summary.create_file_writer(logdir="log") writer.set_as_default() global_step = tf.Variable(0, name="global_step", dtype=tf.int64) tf.summary.experimental.set_step(global_step) x = tf.Variable(1, name="x", dtype=tf.int64) for i in range(100): x.assign_add(1) global_step.assign_add(1) y = process(x)一見すると、ほぼEagerモードと同様に動作するように見えますが、実はEagerモードにない制約が増えています。global_stepをtf.Variableでなくnp.arrayにした場合、正常に動作しません。np.arrayを用いるとprocess関数中のsummary.scalarのstepは最初にprocessが呼び出されたときの値で固定されてしまいます。
以下のコードは、Graphモードでは正常にsummaryが出力されませんが、tf.functionをコメントアウトしEagerモードに
すると正常に動作する例となっています。@tf.function def process(x): y = x * 10 tf.summary.scalar(name="y", data=y) return y writer = tf.summary.create_file_writer(logdir="log") writer.set_as_default() global_step = np.array(0, dtype=np.int64) tf.summary.experimental.set_step(global_step) x = tf.Variable(1, name="x", dtype=tf.int64) for i in range(100): x.assign_add(1) global_step += 1 y = process(x)Graphモードにおける周期的なsummary出力
周期的なsummary出力でも、Eagerモードと比較してGraphモードでは制約が増えています。以下はEagerモードでの例と同様に、7回に1回summaryを出力するコードです。
@tf.function def process(x): y = x * 10 tf.summary.scalar(name="y", data=y) return y writer = tf.summary.create_file_writer(logdir="log") writer.set_as_default() global_step = tf.Variable(0, name="global_step", dtype=tf.int64) tf.summary.experimental.set_step(global_step) x = tf.Variable(1, name="x", dtype=tf.int64) should = lambda: tf.equal(global_step % 7, 0) with tf.summary.record_if(should): for i in range(100): x.assign_add(1) global_step.assign_add(1) y = process(x)こちらも先ほどと同様、global_stepをnumpy.arrayにすると動作しなくなるため注意してください。
なぜこのように動作するのか
TF1系に慣れていないと上記の動作は少し不可解に思えるかもしれません。このようにglobal_stepの型がnumpyのときとtf.Variableのときとで動作が変化するのは、@tf.functionがついた関数の初回呼び出し時に計算グラフが構築され、以降の関数呼び出しでは構築された計算グラフが実行されるためです。
この計算グラフ構築時に、numpyでglobal_stepを渡すと、計算グラフ中では定数として扱われます。一方でtf.Variableで渡すと、計算グラフの実行のたびに値が評価されます。
まとめ
本記事ではTensorflow2.0でのsummaryの基本的な出し方を解説しました。
TF1系と比較すると難しくなったようにも見えますが、TF1系でもsummary周りはわりと難しかったので、慣れればこれはこれでありだと思います。