Macでgo-tfliteを動かしてみる

## はじめに

[mattn/go-tflite](https://github.com/mattn/go-tflite)というものが出て動かそうとしていたのですが、動くまでに詰まることがとても多かったのでメモ代わりに残します。

### 環境

- macOS Mojave10.14.6
- go1.12.6 darwin/amd64
- [mattn/go-tflite](https://github.com/mattn/go-tflite) commit: d3ce6e235e3b2c4d07b899f05cea4004b08d8c43
- [tensorflow/tensorflow](https://github.com/tensorflow/tensorflow) commit: d1583caf72add28ad997616fe4aa08b5b5181b71

バージョンとかの違いで、変わってしまうかもしれないので
go-tfliteやtensorflowのビルドのバージョンも載せておきます。

### 1. tensorflow liteのビルド

このビルドに関しては[mruby + TensorFlow Liteで画像分類する](https://qiita.com/akuroda/items/e3d509f75713a55ae22e)という記事を参考にしました。
おそらくUbuntuの方は、この記事通りにビルドしてもらったらいいと思います。

まずは `$GOPATH/src/github.com/tensorflow/tensorflow`に[このリポジトリ](https://github.com/tensorflow/tensorflow)をcloneします。
なぜ、`$GOPATH`以下に置くのかというと`go-tflite`の`tflite.go`でライブラリを取得する際に相対パスでライブラリへのパスを指定しているみたいだからです。

次に、`ld: library not found for -lrt`というエラーが出ないように、`tensorflow/lite/tools/make/Makefile`から `-lrt`オプションを消します。(このエラーについてはあまり良くわからなかったので詳しい方がいれば教えて下さい。)

```diff:tensorflow/lite/tools/make/Makefile
        CORE_CC_ALL_SRCS += tensorflow/lite/delegates/nnapi/quant_lstm_sup.cc
        CORE_CC_ALL_SRCS += tensorflow/lite/nnapi/nnapi_implementation.cc
-       LIBS += -lrt
```

次に以下のコマンドを実行します。

```bash
$ ./tensorflow/lite/tools/make/download_dependencies.sh
$ make -f tensorflow/lite/tools/make/Makefile
```

## 2. C experimental APIライブラリのビルド

こちらも上述の記事を参考にさせてもらって以下のように、Makefileを書きました。
`tensorflow/lite/experimental/c`以下で`make`を実行すると、`libtensorflowlite_c.so`というファイルが出来上がります。

```make:tensorflow/lite/experimental/c/Makefile
CXX = clang++
SRCS =  c_api.cc c_api_experimental.cc
OBJS = $(subst .cc,.o,$(subst .cxx,.o,$(subst .cpp,.o,$(SRCS))))
TENSORFLOW_ROOT = ../../../../
CXXFLAGS = -DTF_COMPILE_LIBRARY -I$(TENSORFLOW_ROOT) -I$(TENSORFLOW_ROOT)/tensorflow/lite/tools/make/downloads/flatbuffers/include -fPIC
TARGET = libtensorflowlite_c
OS_ARCH = osx_x86_64
TARGET_SHARED := $(TARGET).so
LDFLAGS += -L$(TENSORFLOW_ROOT)/tensorflow/lite/tools/make/gen/$(OS_ARCH)/lib
LIBS = -ltensorflow-lite

.SUFFIXES: .cpp .cxx .o

all : $(TARGET_SHARED)

$(TARGET_SHARED) : $(OBJS)
	$(CXX) -shared -o $@ $(OBJS) $(LDFLAGS) $(LIBS)
.cxx.o :
	$(CXX) -std=c++14 -c $(CXXFLAGS) -I. $< -o $@
.cpp.o :
	$(CXX) -std=c++14 -c $(CXXFLAGS) -I. $< -o $@
clean :
	rm -f *.o $(TARGET_SHARED)
```

上の`make`で出来た`libtensorflowlite_c.so`を`/usr/local/lib`以下にコピーすると準備完了です。

## go-tfliteをクローンする

`$GOPATH/src/github.com/mattn/go-tflite`に[mattn/go-tflite](https://github.com/mattn/go-tflite)をクローンしてください。
_exampleで何個か遊んでみましょう。

```zsh:$GOPATH/src/github.com/mattn/go-tflite/_example/mnist
$ go run main.go 0.png
# github.com/mattn/go-tflite
./tflite_experimental.go:35:12: warning: expression result unused [-Wunused-value]
0
```

無事mnistの画像認識に成功しています。

```zsh:$GOPATH/src/github.com/mattn/go-tflite/_example/fizzbuzz
$ go run main.go
# github.com/mattn/go-tflite
./tflite_experimental.go:35:12: warning: expression result unused [-Wunused-value]
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
31
32
Fizz
34
Buzz
Fizz
37
38
Fizz
Buzz
41
Fizz
43
44
FizzBuzz
46
47
Fizz
49
Buzz
Fizz
52
53
Fizz
Buzz
56
Fizz
58
59
FizzBuzz
61
62
Fizz
64
Buzz
Fizz
67
68
Fizz
Buzz
71
Fizz
73
74
FizzBuzz
76
77
Fizz
79
Buzz
Fizz
82
83
Fizz
Buzz
86
Fizz
88
89
FizzBuzz
91
92
Fizz
94
Buzz
Fizz
97
98
Fizz
Buzz
```

fizzbuzzも成功しました。

## まとめ

tensorflowのようなツールをGoでも使えるのはすごく嬉しいですね。
こういうツールはPythonが使われることが多いのですが、僕個人としてはGolangでも機械学習する人が増えればいいなあと思います。

go-tfliteは推論にしか使えないようで、まだpythonのように学習から推論までというふうには行きませんが、今後Golangでも学習が出来たらうれしいなあと思います。