20190323のTensorFlowに関する記事は3件です。

Tensorflow 2.0を試してみる

 はじめに

正式リリース前ですがTensorflow 2.0少しだけ試してみます。
ソースコード

Tensorflow 2.0で変わること

2.0で大きく変わることは

  • Eager executionがdefaultになる
  • 重複したAPIを統一(tf.layerstf.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.py
from 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.py
class 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.py
class 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)

KerasTrainerEagerTrainerと全く同じ学習をするように書いています。
シンプルなモデルだと圧倒的にコードの量が少なくできるのでkerasの偉大さがわかります。

Training

main.py
import 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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

基本的な流れ

  1. モデルの freeze を行う (Python)
  2. 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_phasefalse を指定する必要があるようです。

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;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

参考にしたサイト

基本的な流れ

  1. Keras + TensorFlow を用いてモデルを作成する (Python)
  2. モデルをロードして学習を行う (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.pbmodel.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 されたモデルを使用した推論についてはまた次回。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む