🤖

frugally-deep で Keras の mnist モデルを試してみた。

2021/10/02に公開

はじめに

https://zenn.dev/mattn/articles/e871dab58be002

今回は FizzBuzz という簡単な例でしたが、パフォーマンスも悪くないので、画像データの様な大きい入力データを扱う事もそれほど苦ではないと思います。興味のある方はチャレンジしてみては如何でしょうか。

誰もやりそうにないので自分でやった。

まずはモデルを作ろう

どこにでもある mnist の学習と HDF5 を吐き出すコード。

import 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(input_shape=(28, 28)),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10, activation='softmax')
])

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=5)
model.save('mnist_model.h5')

そしてこれまたヘッダーオンリーで画像の読み込みやリサイズが出来て、パブリックドメインなライブラリ STB を使って画像を処理。

https://github.com/nothings/stb

#include <iostream>
#include <fdeep/fdeep.hpp>
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include <stb/stb_image.h>
#include <stb/stb_image_resize.h>

int
main(int argc, char *argv[]) {
  const auto model = fdeep::load_model("mnist_model.json", true, fdeep::dev_null_logger);

  for (auto i = 1; i < argc; i++) {
    const char* filename = argv[i];
    int x = 0, y = 0, n = 0;
    float* img_data = stbi_loadf(filename, &x, &y, &n, STBI_grey);
    float* resized_image = (float*)std::malloc(28 * 28 * sizeof(float));
    stbir_resize_float(img_data, x, y, 0, resized_image, 28, 28, 0, 1); 

    const auto result = model.predict(
        {fdeep::tensor(fdeep::tensor_shape(
              static_cast<std::size_t>(28),
              static_cast<std::size_t>(28)),
        std::vector<float>(resized_image, resized_image+28*28*1))});
    //std::cout << fdeep::show_tensors(result) << std::endl;
    auto v = result[0].to_vector();
    auto it = std::max_element(v.begin(), v.end());
    std::cout << std::distance(v.begin(), it) << std::endl;

    stbi_image_free(img_data);
    stbi_image_free(resized_image);
  }
}

一応 Makefile も

SRCS = \
	mnist.cxx

OBJS = $(subst .cxx,.o,$(SRCS))

CXX = clang++
CXXFLAGS = -std=c++20 \
	-I c:/dev/frugally-deep/include \
	-I c:/dev/FunctionalPlus/include \
	-I c:/dev/nlohmann_json/include \
	-I c:/msys64/mingw64/include/eigen3
LIBS = 
TARGET = mnist
ifeq ($(OS),Windows_NT)
TARGET := $(TARGET).exe
endif

.SUFFIXES: .cxx .o

all : $(TARGET)

$(TARGET) : $(OBJS)
	$(CXX) -o $@ $(OBJS) $(LIBS)

.cxx.o :
	$(CXX) -c $(CXXFLAGS) -I. $< -o $@

clean :
	rm -f *.o $(TARGET)

ためそう

食わせる画像はこんな感じ。

コマンド引数に画像ファイルを食わせる。

イイカンジ。ちなみにちょっと計ってみた所、AMD Ryzen 5 のノート PC で秒間 200 枚くらいの画像を推論できる様です。

おわりに

良い感じです。結構パフォーマンスが良い事が分かったので、暇があれば OpenCV で取り込んだ画像でリアルタイム推論するサンプル書いても楽しいんじゃないでしょうか。興味のある方はどうぞ。(自分でやるフラグ)

Discussion