2019年8月29日木曜日

TensorFlowをVisualStudio2019でビルドしてみた。

GoogleにはWindowsがないみたいで、TensorFlowもWindowsでビルドがほとんどできない状態です。
ほどんどできないというのは、TensorFlowをビルドするのに使うMAKEツールのBazelがWindowsのコンパイラをVisualC++ Build Tool 2015しかサポートしていなく、VisualStudio2017やVisualStudio2019のC++でのビルドができないのです。

Googleが対応してくれないのならば、おじさん意地でもTensorFlowをWindowsのVisualStudioでビルドしてやる。
というわけで、TensorFlowをBazelを使わないでスクラッチビルドできるか試してみました。

まずここをチェック。
https://joe-antognini.github.io/machine-learning/windows-tf-project

次にTensorFlowをスクラッチビルドをするためには外部依存のライブラリもスクラッチビルドする必要があります。

absl
boringssl
double-conversion
eigen
farmhash
fft2d
gemmlowp
giflib
grpc
higwayhash
icu
jpeglib
jsonCPP
LMDB
nsync
protocolbuffers
libpng
re2
snappy
sqlite
zlib

まずこれらのライブラリをVisualStudioでビルドしてます。
20個以上ある。これだけでかなりめんどくさい。

Windows.hのインクルード方法が良くないソースコードがあるので、Windows.hのインクルードが以下のようになっているかチェックしながらビルドしていきます。

#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include<windows.h>
#undef ERROR

いろんなオープンソースをWindowsでビルドしようとすると意味不明なエラーになるのですが、大体上記のwindows.hの読み込みのところを修正すれば90%くらいはエラーが取れます。

ついでに、今回はDLLを作らないので、
 #define HOGE_API __dllexport
 #define HOGE_API __dllimport
など__dll*のところを
 #define HOGE_API
などのように修正します。


一週間かけてこれらのライブラリを全部スクラッチビルド。
g++ *.cc
のように全てのライブラリをmakefileなしでビルドできるようにしました。


次にprotocolbuffersの中にあるツール、protocをビルドし、ensorFlowの中にある.protoファイルを全部コンパイルします。
ツールはもう一つあります。
tensorflowのツールの中にあるproto_textというツールもビルドします。
このツーでも.protoをコンパイル。
これらのツール、protocolbuffersに統一してほしいですね。
さらに.shによって生成される謎のソースコードもあるので、これらも生成します。

これでやっとTensorFlow本体をビルドする準備が整いました。
tensorlow/c
tensorlow/cc
tensorlow/core
の三つのディレクトリをコンパイルしていきます。

VisualStudio2017のC++コンパイラでは、
vector<int> a({1,2})
みたいなの書き方がVisualStudioでコンパイルできないので
vector<int> a{1,2}
みたいに修正します。

libファイルが4Gを超えるとリンカーがハングするので、数百個のファイル毎にlibファイルを生成するように分割してコンパイルしていきます。
libファイルだけで合計10Gバイトもある。

でもどうにかVisualStudio2017やVisualStudio2019でTensorFlow r1.13をビルドで来た。やったー今年の夏はついに念願のTensorflowのWindows対応ができた。
なんかGoogleに勝った気分。
これでIDEでデバッグしながらTensorFlow開発ができる。


------------------------
#include "tensorflow/cc/client/client_session.h"
#include "tensorflow/cc/ops/standard_ops.h"
#include "tensorflow/core/framework/tensor.h"

int main() {
  using namespace tensorflow;
  using namespace tensorflow::ops;
  Scope root = Scope::NewRootScope();
  // Matrix A = [3 2; -1 0]
  auto A = Const(root, { {3.f, 2.f}, {-1.f, 0.f} });
  // Vector b = [3 5]
  auto b = Const(root, { {3.f, 5.f} });
  // v = Ab^T
  auto v = MatMul(root.WithOpName("v"), A, b, MatMul::TransposeB(true));
  std::vector<Tensor> outputs;
  ClientSession session(root);
  // Run and fetch v
  TF_CHECK_OK(session.Run({v}, &outputs));
  // Expect outputs[0] == [19; -3]
  LOG(INFO) << outputs[0].matrix<float>();
  return 0;
}
------------------------


------------------------
7 17
-1 -3
------------------------