Windows10でTensorflow2のC++ライブラリビルドが出来た

 前に一度試して、ビルド条件がとても面倒くさそうなので放置していたが、下の記事を参考にして試したところ、Windows10においてTensorflow2のC++ライブラリビルドに成功した。
qiita.com
 めちゃくちゃ勘違いしていたのだが、WSL2などは使わずにMSYSで出来る。

  • MSYSでのCUDA対応ビルドが2.10以前でしかできないとかいう謎仕様がある。それより上のバージョンではWSL2を使えとある。めんどー

 上の記事では、著者のkHz氏が試して成功したものしかリストに挙げられていないが、それ以外の組み合わせでも出来る様だ。

 今回筆者が試した組み合わせは以下の通り。

・Wndows 10 64bit
Visual Studio 2019
Python 3.7.0(Anaconda)
・bazel 3.1.0
・CUDA 11.2
・cuDNN 8.1.0.77
・MSYS2
・Tensorflow 2.4
GPU:RTX 2070 super

 今回の試行で少し躓いた所と言えば、参考にした記事の8.4にある様に、ビルドされたtensorflowのwhlをpipでインストールする際に少しだけバージョンの差異による依存関係でエラーが出た事ぐらいで、エラーが出たパッケージのバージョンをpipで調整してやることで簡単に回避でき、無事C++ライブラリ(dllとlibとヘッダファイル等)が生成された。
 また、Anacondaで環境を作りPythonをインストールした場合にはpython.exeへのパスが通っていない事がほとんどだと思うので、MSYSでpython.exeへのパスをPATHに追加でexportしてやることを忘れないように。
 kHz氏の記事ではTuring世代のGPUでTensorflow1をビルドしたという結果だけを対応表の様に記述しているが、Turing世代のGPUでもTensorflow2はちゃんとビルドできる様だ。
 Ampere世代GPUを使う場合Tensorflow1がビルドできないと言っているがそうなのだろうか。

 しかし作ったはいいものの、本記事作成時点ではまだ使用していない。
 これからも使わなそう・・・

 以前失敗した試行を記録した自分の記事を見返すと、Googleの死ぬほどわかりづらいドキュメントを忠実に追って試したのだが駄目だった様だ・・・
 ツールやライブラリのバージョンの依存関係が原因かもしれないが、今回の手順と結構違う事は確か。

TensorFlow C++ライブラリのWindows環境におけるビルド

 Windowsで汎用的なディープラーニング系のC++ライブラリを選ぼうとすると、導入の簡単さではPyTorchが上だが、一応TensorFlowのC++ライブラリの導入にもなれておかないとなぁということでやってみた。多分使わないと思うが。
 下記内容は基本的な流れだけであって、読者の要求に適合するとは限らない上に、ビルド中に書いているのでかなり無責任である事は述べておこう。

  • 依存関係:Windows 10/11、TensorFlow 2.11、CUDA SDK 11.2、cuDNN 8.1、python 3.7、MSYS2、Visual Studio 2019、Bazel5.3
  • 基本的な流れはここを参照pythonパッケージのビルドとインストールは無視している
  • CUDA SDKとcuDNNをインストール。cuDNNはCUDA SDKインストールディレクトリ内に統合(コピペ)
  • Bazelのバイナリをダウンロードし、下記環境変数PATHに登録する予定のディレクトリにコピー
    • 今回はtensorflow2.11が指定するバージョンである5.3を使用
  • Visual Studio 2019をインストール(2019用ビルドツールと再頒布可能パッケージにチェック)
  • pythonをインストールし、pythonで必要なパッケージをインストール
    • tensorflowのpythonパッケージのビルドを行わない場合、依存パッケージはいらないかもしれないが、python自体は無いとbazelでのビルド時にエラーが出る
    • bazelがなにをやっているのかわからないので入れておくのが無難
pip install -U six numpy wheel packaging
pip install -U keras_preprocessing --no-deps
  • MSYS2をインストールし、以下の手続きを実行
#ロングパス対策
fsutil 8dot3name set 0

#必要なパッケージをインストール
pacman -S git patch unzip
git clone https://github.com/tensorflow/tensorflow.git
cd tensorflow
git checkout r2.11
./configure
#configureでは設定のためにいくつか質問をされるが、デフォルトで良いだろう。mROCを有効にするとCUDAが使えなくなるらしいので注意
# .bazelrcに以下の記述を追加。ロングパスを解決するためのものらしい。
startup --output_user_root=C:/tmp
startup --windows_enable_symlinks
build --enable_runfiles
#bazelコマンドに渡す引数とMSYSターミナルのパス解釈の問題を回避するために下記のコマンドを実行
export MSYS_NO_PATHCONV=1
export MSYS2_ARG_CONV_EXCL="*"
#PATHを設定し、bazelとpythonを実行できるようにする
#URIの区切り文字の書き方がWindowsユーザーには独特だが、まぁ分かるだろう
export PATH="/c/dev/bin:$PATH"
export PATH="/c/Program Files/WindowsApps/PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0:$PATH"
#GPUサポートのためにCUDAとcuDNNのパスを通す
#当たり前だがtensorflowのバージョンに適合するCUDA SDKとcuDNNをインストールしておく必要がある。cuDNNのbin等はv11.2/bin以下にコピー済みという前提
export PATH="/c/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v11.2/bin:$PATH"
export PATH="/c/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v11.2/extras/CUPTI/libx64:$PATH"
#Visual Studio 2019ビルドツールが存在するディレクトリへのパスを通す
#他にもVC関連の環境変数設定があるが省略可能([https://bazel.build/configure/windows#build-c:title])
export BAZEL_VC="/C/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC"
#ビルドとインクルードファイルのインストール
#DLL等のみをビルドするオプション等があるらしい
bazel build tensorflow:tensorflow_cc.dll
bazel build tensorflow:install_headers

 ビルドが正常に開始されるとCPUコアをフルに使ったビルドが始まり、OSの動きもカクカクに。
 しかし、やはりGoogleのドキュメントは行間を読みまくらないと使い物にならない作りだ。
 まだビルドに成功していないが、下に失敗例を記載しておく。

  • 失敗例
    • 大量の未解決のシンボルが出てビルド失敗。./configureしてなかったり、ロングパス問題を解決していない場合に起きるとか起きないとか
tensorflow_cc.dll.if.exp : error LNK2001: 外部シンボル "public: class std::unique_ptr<class tensorflow::data::DatasetBase,struct tsl::core::RefCountDeleter> && __cdecl tsl::StatusOr<class std::unique_ptr<class tensorflow::data::DatasetBase,struct tsl::core::RefCountDeleter> >::value(void)&& " (?value@?$StatusOr@V?$unique_ptr@VDatasetBase@data@tensorflow@@URefCountDeleter@core@tsl@@@std@@@tsl@@QEHAA$$QEAV?$unique_ptr@VDatasetBase@data@tensorflow@@URefCountDeleter@core@tsl@@@std@@XZ) は未解決です
tensorflow_cc.dll.if.exp : error LNK2001: 外部シンボル "public: class google::protobuf::RepeatedPtrField<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > const & __cdecl tensorflow::BytesList::value(void)const " (?value@BytesList@tensorflow@@QEBAAEBV?$RepeatedPtrField@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@protobuf@google@@XZ) は未解決です
tensorflow_cc.dll.if.exp : error LNK2001: 外部シンボル "public: class google::protobuf::RepeatedField<__int64> const & __cdecl tensorflow::Int64List::value(void)const " (?value@Int64List@tensorflow@@QEBAAEBV?$RepeatedField@_J@protobuf@google@@XZ) は未解決です
tensorflow_cc.dll.if.exp : error LNK2001: 外部シンボル "public: virtual class std::basic_string_view<char,struct std::char_traits<char> > __cdecl tsl::table::Block::Iter::value(void)const " (?value@Iter@Block@table@tsl@@UEBA?AV?$basic_string_view@DU?$char_traits@D@std@@@std@@XZ) は未解決です
tensorflow_cc.dll.if.exp : error LNK2001: 外部シンボル "public: class google::protobuf::RepeatedPtrField<class tensorflow::VariantTensorDataProto> const & __cdecl tensorflow::TensorProto::variant_val(void)const " (?variant_val@TensorProto@tensorflow@@QEBAAEBV?$RepeatedPtrField@VVariantTensorDataProto@tensorflow@@@protobuf@google@@XZ) は未解決です
bazel-out\x64_windows-opt\bin\tensorflow\tensorflow_cc.dll : fatal error LNK1120: 11907 件の未解決の外部参照

 色々修正しつつビルドを数回試みたが、DLLをリンクする段階で失敗する。
 MSYSではビルドが失敗するのでVS2019 Developer Command Promptからビルドを試みるとWSL2でビルドしないとCUDAを有効にしてやらないと言われる。
 いやーほんと、面倒なヤツ。とにかくWindowsに合わせるつもりは無いらしい。
 C言語用ライブラリのC++ラッパーはいくつか有るので、このあたりとPythonを組み合わせるのがWindowsでは最適か。
 C++のみで行うならPyTorch一択になるだろう。
 用途を限定するならDlibなどもある。これは画像認識用途で開発された経緯のせいか、リカレントニューラルネットワークインターフェイスが用意されていないものの、基本的なものから高度な分類や近似を行えるモデルが簡単に使えるようになっていて良い。コア機能を流用すればリカレントも実装できるとのことだが、あまり使いやすいとは言えないらしい。NLPライブラリであるMITIEがDlibのコア機能を利用して作られていたりする。
 しかしPyTorchのC++チームにはもっと頑張ってほしい。Python版Torchの記述に合わせることに拘るあまり、ドキュメントやチュートリアルで其のことばかりに言及している。C++はそれ自体に良い記述方法が有るので、Pythonしか使わない人たちに合わせる必要はない。

Visual Studio 2022にはまだまだ問題点が有る

 色々良くなったVisual Studio 2022なのだが、リリースから結構経つものの、無駄に残念になっているところなどが有り、もう少し様子見するしかない。
 Visual Studio 2019ではPythonシンタックスハイライト設定機能が正常に働いていたのに、2022ではコンフィグ画面でなぜかできなくなってしまっている。Webでも同様の問題に遭遇している人がいる。
 それでもVisual Studio 2017のPython環境よりは良くなっている。
 VS2017では、python環境にインストールされたパッケージを解析してIntellisenseデータベースを作成する際にフリーズし、どうしようもなくなるという致命的な不具合があり、PythonIDEとしては適さなかった。
 運良くそのような問題に遭遇しない場合もあったが、VSの解析器とパッケージのソースの相性の問題なのか、突然遭遇し、Intellisenseが使えなくなっていた。
 VS2022では一応其のようなことはなく、仮想Python環境でスムーズにパッケージのインストールからIntellisenseデータベースの解析まで行ってくれるようだ。しかし、未知の不具合はあるかもしれない。
 VS2019から、Intellisense解析中のプログレスバーがなくなり、いつの間にかIntellisenseが機能するように仕様が変わっている。
 其の様な改善にもかかわらず、上記の様ないらぬところで不具合を生み出している2022なので、まだまだ不安であり、2019を使う。

【知らなかった】プログラミング特有の用語「linting」

 Visual Studioのドキュメントを読んでいたら、lintingという謎の用語に遭遇。
 辞書では名詞の意味しか無く、動名詞として使う意味がわからなかったのだが、Weblioには一応掲載されていた。

名詞不可算名詞
1リント 《リンネル (linen) の片側を起毛した柔らかい布; 今は湿布用など》.
2(繰り綿の)綿くず; (布などのほつれた)糸くず,けば.

 面白いのは、名詞としての用法しかないのに、動名詞のlintingのページでは動詞としての「lint」へのリンクが作成されているところだ。単純に矛盾なのでは。

vcpkgでwordnet

 vcpkgでwordnetがサポートされていたので入れてみたが、wordnet.libがインストールされるパスは、ライブラリの参照先として認識されておらず、プロジェクトのプロパティに参照先としてその場所を指定するといろいろ不都合なので、libファイルだけを取り出して別の場所に置いて参照するなどという事態に。
 C:\vcpkg\installed\x64-windows\libなどにインストールされるが、このパスをプロジェクトで直接指定すると、なぜかcpr.libなどと競合した。
 環境はVC15。

vcpkgでインストールしたパッケージとerror LNK2001

 vcpkgでtripletをx64-windowsにしてインストールしたライブラリを使用してビルドした際に、error LNK2001、が出る場合。
 vcpkg/triplet以下にあるx64-windows.cmakeへ以下の様に記述することで解決する公算が高い。

set(VCPKG_PLATFORM_TOOLSET v141)
set(VCPKG_DEP_INFO_OVERRIDE_VARS v141)

 Visual Studioツールセットバージョンやvcpkgが標準で使用する識別式なるものの上書きを行っている。https://github.com/microsoft/vcpkg/blob/master/docs/maintainers/manifest-files.md#supports
 古いバージョンのvcpkgではこの問題が発生すらしない場合があるので、アップデートした後に戸惑う人は全世界でかなり多いだろう。

vcpkgにおけるboostのアンインストール

.\vcpkg.exe --triplet=x64-windows remove boost-uninstall --recurse

 PCにVS141以降しかインストールされて無くてもv140のバイナリしかインストールされない。tripletでツールセットバージョンv141を設定しても駄目。
 v140とVS141はバイナリ互換性があるので普段は問題ないが、visual C++ 2017でboost serializationを使ったプロジェクトをビルドした時、何故かv141のインポートライブラリ(.lib)を要求されるエラーを回避できないというケースに遭遇。
 設定を確認しまくったが意味不明。
 同じソリューション内の別プロジェクトは問題なし。なぜ?
 プロジェクトを作り直せば解決すると思うが、できればなにが問題なのか特定したい。しかし人柱的な作業になりそうなのでスルー。
 VS17(2022、v143)では問題なし。
 boostはバイナリをインストールしてパスを通せば回避できるので、こちらのほうが無難かも。但し、vcpkgでサポートされている別のパッケージが要求するboostのバージョンと合わせておかないと問題が発生する場合あり。
 ポータブルに見えて地雷が多いvcpkg。