- 投稿日:2019-03-23T14:50:41+09:00
 
Tensorflow 2.0を試してみる
はじめに
正式リリース前ですがTensorflow 2.0少しだけ試してみます。
ソースコードTensorflow 2.0で変わること
2.0で大きく変わることは
- Eager executionがdefaultになる
 - 重複したAPIを統一(
 tf.layersとtf.keras.layersなど)- Contribが一掃される
 で、一番の目玉は「Eager executionがdefaultになる」だと思います。
今回はEager executionとkeras実装での学習を比較してみます。
基本的な実装は公式のチュートリアルを参考にしています。Eager executionとは
Tensorflowはdefine and runという方式で動くライブラリでしたが
Eager executionでは、pytorchやchainerのようにdefine by runで実行されます。
計算グラフを定義しながら実行するdefine by runではモデルを柔軟に定義することができるためRNNなどの実装では好まれていますし、最近のpytorchの勢いからして今後の主流になっていきそうです。やること
- Tensorflow 2.0.0-alpha0 を使ってみる
 - Mnistのサンプルコードを動かす
 - Eager executionとkeras実装で学習を比較する
 実行環境
- tensorflow-datasets==1.0.1
 - tensorflow==2.0.0-alpha0
 モデルの定義
trainer.pyfrom tensorflow.keras.layers import Dense, Flatten, Conv2D from tensorflow.keras import Model class MyModel(Model): def __init__(self): super(MyModel, self).__init__() self.conv1 = Conv2D(32, 3, activation='relu') self.flatten = Flatten() self.d1 = Dense(128, activation='relu') self.d2 = Dense(10, activation='softmax') def call(self, x): x = self.conv1(x) x = self.flatten(x) x = self.d1(x) return self.d2(x)モデルは簡単なCNNです。kerasのModelクラスを継承してモデルを定義することができるようになったみたいです。(kerasの少し前のバージョンからできたみたいですが全然気づかなかった)
pytorchやchainerを使ったことがある人は馴染みのある書き方で、個人的にもわかりやすいと思うのでこの書き方に慣れた方がいいと思います。Trainer
EagerTrainer
trainer.pyclass EagerTrainer(object): def __init__(self): self.model = MyModel() self.loss_object = tf.keras.losses.SparseCategoricalCrossentropy() self.optimizer = tf.keras.optimizers.Adam() self.train_loss = tf.keras.metrics.Mean(name='train_loss') self.train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy') self.test_loss = tf.keras.metrics.Mean(name='test_loss') self.test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy') @tf.function def train_step(self, image, label): with tf.GradientTape() as tape: predictions = self.model(image) # 順伝播の計算 loss = self.loss_object(label, predictions) # lossの計算 gradients = tape.gradient(loss, self.model.trainable_variables) # 勾配の計算 self.optimizer.apply_gradients(zip(gradients, self.model.trainable_variables)) # パラメータの更新 self.train_loss(loss) self.train_accuracy(label, predictions) @tf.function def test_step(self, image, label): predictions = self.model(image) t_loss = self.loss_object(label, predictions) self.test_loss(t_loss) self.test_accuracy(label, predictions) def train(self, epochs, training_data, test_data): template = 'Epoch {}, Loss: {:.5f}, Accuracy: {:.5f}, Test Loss: {:.5f}, Test Accuracy: {:.5f}, elapsed_time {:.5f}' for epoch in range(epochs): start = time.time() for image, label in tqdm(training_data): self.train_step(image, label) elapsed_time = time.time() - start for test_image, test_label in test_data: self.test_step(test_image, test_label) print(template.format(epoch + 1, self.train_loss.result(), self.train_accuracy.result() * 100, self.test_loss.result(), self.test_accuracy.result() * 100, elapsed_time))
EagerTrainerはEager executionのtrainerです。
コードを見るとだいたいどんな操作をしているかわかると思います。KerasTrainer
trainer.pyclass KerasTrainer(object): def __init__(self): self.model = MyModel() self.model.compile(optimizer=tf.keras.optimizers.Adam(), loss=tf.keras.losses.SparseCategoricalCrossentropy(), metrics=['accuracy']) def train(self, epochs, training_data, test_data): self.model.fit(training_data, epochs=epochs, validation_data=test_data)
KerasTrainerはEagerTrainerと全く同じ学習をするように書いています。
シンプルなモデルだと圧倒的にコードの量が少なくできるのでkerasの偉大さがわかります。Training
main.pyimport argparse import tensorflow as tf import tensorflow_datasets as tfds from trainer import EagerTrainer, KerasTrainer def convert_types(image, label): image = tf.cast(image, tf.float32) image /= 255 return image, label def main(): parser = argparse.ArgumentParser(description='Train Example') parser.add_argument('--trainer', type=str, default='eager') args = parser.parse_args() dataset, info = tfds.load('mnist', with_info=True, as_supervised=True) mnist_train, mnist_test = dataset['train'], dataset['test'] mnist_train = mnist_train.map(convert_types).shuffle(10000).batch(32) mnist_test = mnist_test.map(convert_types).batch(32) if args.trainer.lower() == 'eager': trainer = EagerTrainer() else: trainer = KerasTrainer() trainer.train(epochs=5, training_data=mnist_train, test_data=mnist_test) if __name__ == '__main__': main()
--trainerのオプションでどちらのTrainerを使うか選べるようにしています。
実際に実行してみるとCPU上では若干Eager executionの方が速い結果になりました。
Tensorflow 1系ではEager executionだと激おそになるという噂がありましたが、改善されているかもしれません。(GPUでもっと実用的なモデルを動かしてみないとわかりませんが)実行結果(Eager execution)
$ python main.py 2019-03-23 14:27:42.698962: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA 0it [00:00, ?it/s]2019-03-23 14:27:43.063703: W ./tensorflow/core/framework/model.h:202] Encountered a stop event that was not preceded by a start event. 1875it [00:34, 54.75it/s] Epoch 1, Loss: 0.13644, Accuracy: 95.91666, Test Loss: 0.06534, Test Accuracy: 97.75000, elapsed_time 34.25085 0it [00:00, ?it/s]2019-03-23 14:28:19.570435: W ./tensorflow/core/framework/model.h:202] Encountered a stop event that was not preceded by a start event. 1875it [00:32, 56.85it/s] Epoch 2, Loss: 0.08982, Accuracy: 97.28416, Test Loss: 0.06335, Test Accuracy: 97.89000, elapsed_time 32.98253 0it [00:00, ?it/s]2019-03-23 14:28:54.483842: W ./tensorflow/core/framework/model.h:202] Encountered a stop event that was not preceded by a start event. 1875it [00:34, 75.77it/s] Epoch 3, Loss: 0.06759, Accuracy: 97.94389, Test Loss: 0.06064, Test Accuracy: 98.03667, elapsed_time 34.02746 0it [00:00, ?it/s]2019-03-23 14:29:30.609697: W ./tensorflow/core/framework/model.h:202] Encountered a stop event that was not preceded by a start event. 1875it [00:34, 54.66it/s] Epoch 4, Loss: 0.05404, Accuracy: 98.35125, Test Loss: 0.05985, Test Accuracy: 98.14000, elapsed_time 34.30574 0it [00:00, ?it/s]2019-03-23 14:30:06.853807: W ./tensorflow/core/framework/model.h:202] Encountered a stop event that was not preceded by a start event. 1875it [00:32, 70.69it/s] Epoch 5, Loss: 0.04531, Accuracy: 98.61067, Test Loss: 0.05936, Test Accuracy: 98.18999, elapsed_time 32.76600実行結果(keras)
$ python main.py --trainer keras 2019-03-23 14:35:22.782574: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA Epoch 1/5 2019-03-23 14:35:23.047235: W ./tensorflow/core/framework/model.h:202] Encountered a stop event that was not preceded by a start event. 1875/1875 [==============================] - 49s 26ms/step - loss: 0.1304 - accuracy: 0.9271 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00 Epoch 2/5 2019-03-23 14:36:12.005254: W ./tensorflow/core/framework/model.h:202] Encountered a stop event that was not preceded by a start event. 1875/1875 [==============================] - 42s 23ms/step - loss: 0.0415 - accuracy: 0.9861 - val_loss: 0.0530 - val_accuracy: 0.9828 Epoch 3/5 2019-03-23 14:36:54.346357: W ./tensorflow/core/framework/model.h:202] Encountered a stop event that was not preceded by a start event. 1875/1875 [==============================] - 42s 22ms/step - loss: 0.0219 - accuracy: 0.9927 - val_loss: 0.0632 - val_accuracy: 0.9811 Epoch 4/5 2019-03-23 14:37:36.479987: W ./tensorflow/core/framework/model.h:202] Encountered a stop event that was not preceded by a start event. 1875/1875 [==============================] - 39s 21ms/step - loss: 0.0124 - accuracy: 0.9959 - val_loss: 0.0633 - val_accuracy: 0.9826 Epoch 5/5 2019-03-23 14:38:15.134248: W ./tensorflow/core/framework/model.h:202] Encountered a stop event that was not preceded by a start event. 1875/1875 [==============================] - 39s 21ms/step - loss: 0.0089 - accuracy: 0.9966 - val_loss: 0.0665 - val_accuracy: 0.9836
- 投稿日:2019-03-23T08:06:15+09:00
 
C++ で TensorFlow の推論処理を実行する
C++ で TensorFlow の推論処理を実行する
はじめに
前回 は Python で作成したモデルに対して C++ で学習を行うところまで説明しました。
今回は,学習したモデルの freeze と推論処理の実行を行いたいと思います。今回作成したコードは前回同様 github に置いてあるので、詳細はこちらをご確認ください。
実行環境
(前回と同じです)
- Windows
 - Python 3.6
 - keras 2.2.4
 - tensorflow 1.10.0
 - Visual Studio 2015
 なお、Python と C++ で使用する TensorFlow のバージョンは揃えていないとエラーが発生する場合があるようです。
C++ 用の tensorflow.dll はこちらのサイトからダウンロードしました。
- GitHub - fo40225/tensorflow-windows-wheel: Tensorflow prebuilt binary for Windows基本的な流れ
- モデルの freeze を行う (Python)
 - freeze されたモデルを使用して推論を行う (C++)
 モデルの freeze を行う
前回作成した
model.metaと checkpoint を読み込み、tf.graph_util.convert_variables_to_constants()を使用してモデルの freeze を行います。output_node_names = ['output/Softmax'] with tf.Session() as sess: # Restore the graph saver = tf.train.import_meta_graph("model.meta") # Load weights saver.restore(sess, './checkpoints/model.ckpt') # Freeze the graph frozen_graph_def = tf.graph_util.convert_variables_to_constants( sess, sess.graph_def, output_node_names ) # Save the frozen graph with open('frozen_graph.pb', 'wb') as f: f.write(frozen_graph_def.SerializeToString())C++ で推論を行う
Freeze されたモデルを読み込みます。
const string graph_def_filename = "frozen_graph.pb"; // Setup global state for TensorFlow. tensorflow::port::InitMain(argv[0], &argc, &argv); // Load a frozen model tensorflow::GraphDef graph_def; TF_CHECK_OK(tensorflow::ReadBinaryProto(tensorflow::Env::Default(), graph_def_filename, &graph_def));読み込んだモデルを指定して Session を作成します。
// Create a session std::unique_ptr<tensorflow::Session> session(tensorflow::NewSession(tensorflow::SessionOptions())); TF_CHECK_OK(session->Create(graph_def));評価用の画像データとラベルデータを読み込み、推論を実行します。
auto test_x = read_training_file("MNIST_data/t10k-images.idx3-ubyte"); auto test_y = read_label_file("MNIST_data/t10k-labels.idx1-ubyte"); predict(session, test_x, test_y);推論の処理は以下の通りです。
"input" に (画像ファイル数, 784) の Tensor を指定し、 "output/Softmax" の出力を取得しています。
モデルで "Dropout" を使用している場合は、keras_learning_phaseにfalseを指定する必要があるようです。void predict(const std::unique_ptr<tensorflow::Session>& session, const vector<vector<float>>& batch, const vector<float>& labels) { // Create an input data tensorflow::Tensor lp(tensorflow::DT_BOOL, tensorflow::TensorShape({})); lp.flat<bool>().setZero(); vector<std::pair<string, tensorflow::Tensor>> inputs = { {"input", MakeTensor(batch)}, {"batch_normalization_1/keras_learning_phase", lp} }; std::vector<tensorflow::Tensor> out_tensors; // Predict TF_CHECK_OK(session->Run(inputs, {"output/Softmax"}, {}, &out_tensors)); }"output/Softmax" からは (入力画像ファイル数, 10) の Tensor が取得できます。
得られた Tensor から精度を計算する処理は以下の通りです。int hits = 0; for (auto tensor : out_tensors) { auto items = tensor.shaped<float, 2>({static_cast<int>(batch.size()), 10}); for (int i = 0; i < batch.size(); i++) { int arg_max = 0; float val_max = items(i, 0); for (int j = 0; j < 10; j++) { if (items(i, j) > val_max) { arg_max = j; val_max = items(i, j); } } if (arg_max == labels[i]) { hits++; } } } std::cout << "Accuracy: " << hits / (float)batch.size() << std::endl;
- 投稿日:2019-03-23T07:44:59+09:00
 
Keras/TensorFlow で作成したモデルの学習を C++ で行う
Keras/TensorFlow で作成したモデルの学習を C++ で行う
はじめに
Python で Keras/TensorFlow を使って初期状態のモデルの作成を行い,C++ を使ってそのモデルの学習を行ってみたいと思います。
想定されるケースは,レアだとは思いますが,僅かでも早く学習を行いたい場合や,学習用のマシンに Python の環境をインストールすることが困難な場合を想定しています。
今回作成したコードは github に置いてあるので,詳細はこちらをご確認ください。
実行環境
- Windows
 - Python 3.6
 - keras 2.2.4
 - tensorflow 1.10.0 (*)
 - Visual Studio 2015
 (*) Python と C++ で使用する TensorFlow のバージョンは揃えていないとエラーが発生する場合があるようです。
C++ 用の tensorflow.dll はこちらのサイトからダウンロードしました。
- GitHub - fo40225/tensorflow-windows-wheel: Tensorflow prebuilt binary for Windows参考にしたサイト
基本的な流れ
- Keras + TensorFlow を用いてモデルを作成する (Python)
 - モデルをロードして学習を行う (C++)
 Computation Graph を作成する (Python)
まず、Keras + TensorFlow で Computation Graph を作成します。
ここでは入力層の名前を "input", 出力層の名前を "output", 学習用の正解データの入力を "target" としています。この名前は後ほど C++ で学習を行う際に必要になります。num_layer = 3 input_dim = 784 n_hidden = 200 num_classes = 10 dropout = 0.2 # モデル作成 model = Sequential() # 入力層 model.add(InputLayer(input_shape=(input_dim,), name='input')) model.add(Dense(n_hidden, kernel_initializer=weight_variable)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Dropout(dropout)) # 中間層 for i in range(num_layer): model.add(Dense(n_hidden, kernel_initializer=weight_variable)) model.add(BatchNormalization()) model.add(Activation('relu')) model.add(Dropout(dropout)) # 出力層 model.add(Dense(num_classes, kernel_initializer=weight_variable)) model.add(Activation('softmax', name='output')) model.summary() x = tf.placeholder(tf.float32, shape=[None, input_dim], name='image') y_ = tf.placeholder(tf.float32, shape=[None, num_classes], name='target') y = model(x) loss = tf.losses.mean_squared_error(y, y_) optimizer = tf.train.AdamOptimizer()train_op = optimizer.minimize(loss, name='train') init = tf.global_variables_initializer() saver_def = tf.train.Saver().as_saver_def()次に
model.pbとmodel.metaというファイル名でモデルを出力します(*)。model.pbは C++ で学習を行う際に使用します。
model.metaは Python でモデルを freeze する際に使用します。(*) C++ で
model.metaを読み込む、もしくは Python でmodel.pbを読み込むことができればどちらか一方の出力でよいはずですが,方法が見つからず断念しました。# .meta の出力 saver = tf.train.Saver() saver.export_meta_graph('model.meta') # .pb の出力 with open('graph.pb', 'wb') as f: f.write(tf.get_default_graph().as_graph_def().SerializeToString())C++ で学習を行う
まず、
model.pbの読み込みを行い、session を起動します。const string graph_def_filename = "model.pb"; // Setup global state for TensorFlow. tensorflow::port::InitMain(argv[0], &argc, &argv); tensorflow::GraphDef graph_def; TF_CHECK_OK(tensorflow::ReadBinaryProto(tensorflow::Env::Default(), graph_def_filename, &graph_def)); std::unique_ptr<tensorflow::Session> session(tensorflow::NewSession(tensorflow::SessionOptions())); TF_CHECK_OK(session->Create(graph_def));次に checkpoint 用のフォルダを指定し,フォルダ内に既存の checkpoint があればそれを読み込みます。無ければ session はクリアされます。
const string checkpoint_dir = "./checkpoints"; const string checkpoint_prefix = checkpoint_dir + "/checkpoint"; if (directory_exists(checkpoint_dir)) { std::cout << "Restoring model weights from checkpoint\n"; tensorflow::Tensor t(tensorflow::DT_STRING, tensorflow::TensorShape()); t.scalar<string>()() = checkpoint_prefix; TF_CHECK_OK(session->Run({{"save/Const", t}}, {}, {"save/restore_all"}, nullptr)); } else { std::cout << "Initializing model weights\n"; TF_CHECK_OK(session->Run({}, {}, {"init"}, nullptr)); }学習用の画像データと正解ラベルを読み込み,モデルの学習を回します。
auto train_x = read_training_file("MNIST_data/train-images.idx3-ubyte"); auto train_y = read_label_file("MNIST_data/train-labels.idx1-ubyte"); for (int i = 0; i < 20; ++i) { std::cout << "Epoch: " << i << std::endl; run_train_step(session, train_x, train_y); }TensorFlow を呼び出して学習を行う処理は以下のようになります。
"image" には (画像データ数, 784) の Tensor を, "target" には (画像データ数, 10) の Tensor を指定しています。"image" と "target" は Computation Graph を作成するときに placeholder で指定した名前となります。
void run_train_step(const std::unique_ptr<tensorflow::Session>& session, const std::vector<vector<float>>& input_batch, const std::vector<float>& target_batch) { auto train_y = to_one_hot(target_batch); vector<std::pair<string, tensorflow::Tensor>> inputs = { {"image", MakeTensor(input_batch)}, {"target", MakeTargetTensor(train_y)} }; TF_CHECK_OK(session->Run(inputs, {}, {"train"}, nullptr)); }std::vector から Tensor への変換は以下のように行っています。
// 画像データの変換 tensorflow::Tensor MakeTensor(const std::vector<vector<float>>& batch) { tensorflow::Tensor t(tensorflow::DT_FLOAT, tensorflow::TensorShape({(int)batch.size(), 784})); auto dst = t.flat<float>().data(); for (auto img : batch) { std::copy_n(img.begin(), 784, dst); dst += 784; } return t; } // 正解データの変換 tensorflow::Tensor MakeTargetTensor(const std::vector<vector<float>>& batch) { tensorflow::Tensor t(tensorflow::DT_FLOAT, tensorflow::TensorShape({(int)batch.size(), 10})); auto dst = t.flat<float>().data(); for (auto target : batch) { std::copy_n(target.begin(), 10, dst); dst += 10; } return t; }最後に学習結果の checkpoint を保存します。
tensorflow::Tensor t(tensorflow::DT_STRING, tensorflow::TensorShape()); t.scalar<string>()() = checkpoint_prefix; TF_CHECK_OK(session->Run({{"save/Const", t}}, {}, {"save/control_dependency"}, nullptr));今回はこれで終了です。
github のコードでは学習の前後で推論を行っているので,実行すると checkpoints ディレクトリが無い初期状態だと精度が 10% 前後,学習後だと精度が 90% 前後になることが確認できると思います。モデルの freeze と freeze されたモデルを使用した推論についてはまた次回。