Windows 10でTensorFlowをソースからビルドする手順（Pascal,Turing,Ampere世代GPU向け）

# 概要と背景
- この記事は、Microsoft Visual Studioで作成するプログラムからTensorFlow C APIを利用したGPUでの推論処理を可能にするために、Windows 10環境でTensorFlowをソースからビルドし、tensorflow.dllやtensorflow.libを生成するまでの手順をまとめたものです。
- これまで、GeForce GTX 10シリーズ(Pascal世代)や、RTX 20シリーズ(Turing世代)では、[TensorFlow公式の記事](https://www.tensorflow.org/install/source_windows "Windowsでのソースからのビルド")[^1]や、[こちらの記事](https://medium.com/@hippiedev/install-tensorflow-gpu-with-cuda-10-0-and-cudnn-7-4-for-python-on-windows-10-be95629e4f54 "Install Tensorflow GPU with CUDA 10.0 and cudNN 7.4 for Python on Windows 10")[^2]を参考にすることで、TensorFlow 1.xのビルドができましたが、GeForce RTX 30シリーズ(Ampere世代)におけるTensorFlow 1.xの利用が可能かがわからなかったため、その調査とあわせて、どのバージョンの組み合わせであればビルドできるのかを調べました。

# 確認結果
- 結論としては、Windows 10とAmpere世代GPU（CUDA 11）の組合わせでTensorFlow 1.xをビルド&利用する方法は見つからなかったため、TensorFlow 2.4でのビルド方法をまとめました。
- 検証した時点では以下のバージョンの組み合わせで動作することを確認しています。<font color="DarkGray">（PythonやVisual Studioのバージョンなど、これ以外でも動作する組合わせはありますが、一旦確認できたバージョンで記載しています。）</font>

| GPU <br>Architecture | Compute <br>Capability | Python | Visual <br>Studio | CUDA | Bazel | cuDNN | TensorFlow | numpy |
|:-------------|---:|-----:|---:|---:|-----:|----:|---:|------:|
|Ampere        |8.6 | 3.8.10|2019|11.3|4.2.1 |8.2.1|2.4.1 |1.19.3|
|Ampere        |8.6 | 3.7.0|2019|11.2|3.1.0 |8.1.0|2.4 |1.19.3※|
|Ampere        |8.6 | 3.7.0|2019|11.1|3.1.0 |8.0.5|2.4 |1.19.3※|
|Pascal, Turing|~7.5| 3.6.8|2015|10.0|0.17.2|7.4  |1.12|1.18.1 |


※Pythonで`import tensorflow`を行う場合はエラーが出るため、**ビルド終了後に**numpyを1.20.xに上げる必要あり

- <font color="IndianRed">**2021.02.12追記:**<br>本記事作成中に、TensorFlow公式の記事[^1]の「テスト済みのビルド構成」の項目にもtensorflow-2.4.0の行が追加されていました。</font>
- <font color="IndianRed">**2021.11.12追記:**<br>Bazel 4.2.1でビルドするパターンを追加しました。</font>

# ビルド手順
### はじめに
- 本手順は、従来の記事[^2]の流れに則って、その内容を整理&アップデートしていく形で記述していきます。
- Ampere世代向けの手順だけでなく、Pascal, Turing世代向けの手順も備忘録的に残しておきたいため、**Ampere世代の手順（1例）を[A]**、**Pascal, Turing世代の手順を[B]** として、併記しています。

## STEP 0: Pythonをインストールする
#### 0-1. [Pythonの公式サイト](https://www.python.org/downloads/ "Python | Downloads")から以下をダウンロード&インストールする

[A] Python 3.7.0 （おそらく3.6.X〜3.8.Xで可）
[B] Python 3.6.8 （おそらく3.5.X〜3.7.Xで可※）

- <font color="IndianRed">※[B]でPython 3.7.0の場合、STEP 8-2.のbazel buildでエラーになる（2021.02.22追記）</font>
 - <font color="IndianRed">関連情報：https://github.com/tensorflow/tensorflow/issues/20950</font>

## STEP 1: Visual C++ Build Toolsをインストールする

#### 1-1. [A] [Visual Studioのダウンロードサイト](https://visualstudio.microsoft.com/ja/downloads/ "Microsoft | Visual Studio > ダウンロード")から以下をダウンロード&インストールする
・Visual Studio 2019 の Microsoft Visual C++ 再頒布可能パッケージ
・Build Tools for Visual Studio 2019

#### 1-1. [B] [以前のバージョンのVisual Studioのダウンロードサイト](https://visualstudio.microsoft.com/ja/vs/older-downloads/ "Microsoft | Visual Studio > 以前のバージョンのダウンロード")から以下をダウンロード&インストールする
・Microsoft Visual C++ 2015 再頒布可能パッケージ Update 3
・Microsoft Build Tools 2015 Update 3

## STEP 2: CUDA Toolkitをインストールする
#### 2-1. [CUDA Toolkit Archive](https://developer.nvidia.com/cuda-toolkit-archive/ "NVIDIA Developer | CUDA Toolkit Archive")から以下をダウンロード&インストールする
[A] CUDA 11.2.0
[B] CUDA 10.0

## STEP 3: MSYS2をインストールする
Windowsで基本的なLinuxコマンドを使えるようにします。

#### 3-1. [MSYS2の公式サイト](https://www.msys2.org/ "MSYS2")からダウンロード&インストールする

<font color="DarkGray"><details><summary>**【プロキシ設定が必要なネットワーク環境の場合】**</summary><div>
本内容は[こちらの記事](https://nantonaku-shiawase.hatenablog.com/entry/2014/10/11/163342 "msys2 + pacmanをproxy環境で使う")[^3]を引用しています。
<br>
##### 3-1-1. proxy.shの作成
以下ディレクトリに、自身のネットワーク環境のプロキシ情報を記述したproxy.shファイルを新規作成する。
*C:¥msys64¥etc¥profile.d*

```shell:proxy.sh
export http_proxy=http://{user}:{password}@{proxy_host}:{proxy_port}/
export https_proxy=http://{user}:{password}@{proxy_host}:{proxy_port}/
export ftp_proxy=http://{user}:{password}@{proxy_host}:{proxy_port}/
export HTTP_PROXY=http://{user}:{password}@{proxy_host}:{proxy_port}/
export HTTPS_PROXY=http://{user}:{password}@{proxy_host}:{proxy_port}/
export FTP_PROXY=http://{user}:{password}@{proxy_host}:{proxy_port}/
```

なお、{user}に@などの特殊文字を含む場合は、パーセントエンコード（URLエンコード）する。

##### 3-1-2. パッケージ管理システムpacmanでcurlによるダウンロードを有効にする
以下ディレクトリにあるpacman.confを開き、curlに関する以下の１行のコメントアウトを外す。
*C:¥msys64¥etc*

```Config_File:pacman.conf
XferCommand = /usr/bin/curl -C - -f %u > %o
```

***

</font></div></details>


#### 3-2. MSYS2の動作環境を整える
##### 3-2-1. コマンドプロンプトを起動し、以下のコマンドを実行する

```cmd.exe
"C:¥msys64¥msys2_shell.cmd" -use-full-path
```

（Windowsメニューから普通にMSYS2を起動しても問題ない可能性あり）

##### 3-2-2. MSYS2を起動し、pacmanのパッケージを一括アップグレードする

```msys2.exe
pacman -Syu
```
（-Sy: 最新のパッケージデータベースをダウンロードする）
（-Su: インストールしたパッケージをアップグレードする）
このときに署名エラーが出る場合は、以下のファイル内で`SigLevel=Never`と指定すると（強引に）解決できる。
*C:¥msys64¥etc¥pacman.conf*

##### 3-2-3. MSYS2を再起動して以下のコマンドを実行する （[Y]でインストールを完了）

```msys2.exe
pacman -Su
pacman -S git patch unzip zip ed
```

##### <font color="IndianRed">3-2-4. Windows側のPATHを読み込めるようにする (2021.02.22追記)</font>
- <font color="IndianRed">Windowsの *システムのプロパティ＞詳細設定＞環境変数* にて、以下を[新規]で追加する。</font>

|変数            |値     |
|:--------------|:------|
|MSYS2_PATH_TYPE|inherit|


## STEP 4: Bazelをダウンロードする
#### 4-1. bazel.exeをダウンロードするディレクトリを作成し、wgetで取得する

```msys2.exe
cd /c/
mkdir bazel
cd bazel/
```

[A]

```msys2.exe
wget https://github.com/bazelbuild/bazel/releases/download/3.1.0/bazel-3.1.0-windows-x86_64.exe -O bazel.exe
```

[B]

```msys2.exe
wget https://github.com/bazelbuild/bazel/releases/download/0.17.2/bazel-0.17.2-windows-x86_64.exe -O bazel.exe
```

## STEP 5: cuDNNをインストールする
#### 5-1. [cuDNN Archive](https://developer.nvidia.com/rdp/cudnn-archive "NVIDIA Developer | cuDNN Archive")から以下をダウンロードする （NVIDIAアカウントが必要）

[A] cuDNN 8.1.0 （CUDA11.2の場合）
[B] cuDNN 7.4.X （7.4.2など）

#### 5-2. ダウンロードした各種ファイルを、それぞれ以下に配置する
*C:¥Program Files¥NVIDIA GPU Computing Toolkit¥CUDA¥v1X.X¥bin*
*C:¥Program Files¥NVIDIA GPU Computing Toolkit¥CUDA¥v1X.X¥include*
*C:¥Program Files¥NVIDIA GPU Computing Toolkit¥CUDA¥v1X.X¥lib¥x64*


## STEP 6: Pythonの各種パッケージをインストールする
#### 6-1. pipでインストールする
```msys2.exe
pip install pip six numpy wheel mock
pip install keras_applications
pip install keras_preprocessing
```

なお、[A]ではnumpyのバージョンが上がりすぎるとエラーになる可能性があるため、ダウングレードしておく（[参考記事](https://qiita.com/bear_montblanc/items/b4b75dfd77da98076da5 "Qiita | Numpy1.19.4のエラー")[^4]）

```msys2.exe
pip install numpy==1.19.3
```

## STEP 7: TensorFlowをダウンロードし、構成を設定する

#### 7-1. Bazelのパスを通す

```msys2.exe
export PATH=/c/bazel:$PATH
```
（~/.bash_profileに上記を追記しておくと楽）

#### 7-2. TensorFlowのGitリポジトリを取得する

```msys2.exe
cd /d/
git clone https://github.com/tensorflow/tensorflow.git
cd tensorflow/
```

[A]

```msys2.exe
git checkout r2.4
```
<font color="IndianRed">（エラーが出る場合は、2.4.1まで指定する必要あり）(2021.11.12追記)</font>

[B]

```msys2.exe
git checkout r1.12
```

#### 7-3. 構成を指定する

```msys2.exe
python configure.py
```

<details><summary><font color="IndianRed">【MSYS2上でPythonのインタラクティブモードを使えるようにする方法】(2021.02.22追記)</font></summary><div>

##### 7-3-1. winptyをインストールする

```msys2.exe
pacman -S winpty
```

##### 7-3-2. ~/.bash_profileに以下を追記する

```config:.bash_profile
alias python="winpty python"
```

***

</div></details>



いくつか質問されるのでYやNなどを入力していく。

```
[A,B共通]
Please specify the location of python. [Default is C:¥Users¥USERNAME¥AppData¥Local¥Programs¥Python¥Python3X¥python.exe]: {press enter}
Please input the desired Python library path to use.  Default is [C:¥Users¥USERNAME¥AppData¥Local¥Programs¥Python¥Python3X¥lib¥site-packages] {press enter}

[Bのみ]
Do you wish to build TensorFlow with Apache Ignite support? [Y/n]: Y
Do you wish to build TensorFlow with XLA JIT support? [y/N]: N

[A,B共通]
Do you wish to build TensorFlow with ROCm support? [y/N]: N
Do you wish to build TensorFlow with CUDA support? [y/N]: Y

[Bのみ]
Please specify the CUDA SDK version you want to use. [Leave empty to default to CUDA 9.0]: 10.0
Please specify the location where CUDA 10.0 toolkit is installed. Refer to README.md for more details. [Default is C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v10.0]: {press enter}
Please specify the cuDNN version you want to use. [Leave empty to default to cuDNN 7]: 7.4.X.X
Please specify the location where cuDNN 7 library is installed. Refer to README.md for more details. [Default is C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v10.0]: {press enter}
```

Compute Capabilityについて次のように質問されるので、使用する可能性のあるGPUのCompute Capabilityを1つ以上カンマ区切りで入力する。
なお、GPUと値の対応関係は、[NVIDIA Developerのページ](https://developer.nvidia.com/cuda-GPUs "NVIDIA Developer | Your GPU Compute Capability")から確認できる。

```
Please note that each additional compute capability significantly increases your build time and binary size, and that TensorFlow only supports compute capabilities >= 3.5 [Default is: 3.5,7.0]:
```
入力例：
[A] 6.1,7.0,7.5,8.6
[B] 6.1,7.0,7.5

```
Please specify optimization flags to use during compilation when bazel option "--config=opt" is specified [Default is /arch:AVX]: {press enter}
Would you like to override eigen strong inline for some C++ compilation to reduce the compilation time? [Y/n]: Y

[Aのみ]
Would you like to interactively configure ./WORKSPACE for Android builds? [y/N]: N
```


## STEP 7B: ファイルの一部を修正する（[B]のみ）
（half eigenのコンパイルで失敗するバグがあるため）
#### 7B-1. eigen_half.patchをダウンロードする
以下のファイルを取得し、（Rawをコピーして新規にeigen_half.patchファイルを作成しても良い）
https://github.com/amsokol/tensorflow-windows-build-tutorial/blob/master/eigen_half.patch

以下のディレクトリに格納する。
*D:¥tensorflow¥third_party*

#### 7B-2. workspace.bzlを編集する
以下のディレクトリにあるworkspace.bzlをテキストエディタで開き、*eigen_archive*で文字列検索し、該当するブロックの最後に一行を追加する。
*D:¥tensorflow¥tensorflow¥*

```diff:workspace.bzl
…
tf_http_archive(
        name = "eigen_archive",
        build_file = clean_dep("//third_party:eigen.BUILD"),
        sha256 = "",
        strip_prefix = "eigen-eigen-fd6845384b86",
        urls = [
            "https://mirror.bazel.build/bitbucket.org/eigen/eigen/get/fd6845384b86.tar.gz",
            "https://bitbucket.org/eigen/eigen/get/fd6845384b86.tar.gz",
        ],
+       patch_file = clean_dep("//third_party:eigen_half.patch"),
    )
…
```

## STEP 8: BazelでTensorFlowをビルドする

#### 8-1. whlファイルを生成するディレクトリを作成した後、ビルドするディレクトリに移動する

```msys2.exe
cd /c/
mkdir tmp
cd /d/tensorflow/
```

#### 8-2. ビルドファイルを生成する
[A]

```msys2.exe
bazel build --define=no_tensorflow_py_deps=true //tensorflow/tools/pip_package:build_pip_package
```


[B]

```msys2.exe
bazel build --config=opt --config=cuda //tensorflow/tools/pip_package:build_pip_package
```

<details><summary><font color="IndianRed">なんらかのエラーが出た場合</font></summary><div>

<font color="IndianRed">**・以下のようなチェックサムエラーが出た場合 （2021.02.22追記）**</font>

```
ERROR: Analysis of target '//tensorflow/tools/pip_package:build_pip_package' failed; build aborted: no such package '@icu//': java.io.IOException: Error downloading [https://mirror.bazel.build/github.com/unicode-org/icu/archive/release-62-1.tar.gz, https://github.com/unicode-org/icu/archive/release-62-1.tar.gz] to C:/users/USERNAME/_bazel_USERNAME/xv6zejqw/external/icu/release-62-1.tar.gz: Checksum was AAAAA but wanted BBBBB
```

- 値の書き換えを行ってから再度実行する。
 - 具体的には、 */d/tensorflow/third_party/icu/workspace.bzl* をテキストエディタで開き、sha256の値を、"BBBBB"から"AAAAA"に変更する。
 - 関連情報：https://github.com/tensorflow/tensorflow/issues/24135

<font color="IndianRed">**・以下のようなwindows.hが見つからないエラーが出た場合 （2021.02.22追記）**</font>

```
external/grpc/include\grpc/impl/codegen/port_platform.h(52): fatal error C1083: Cannot open include file: 'windows.h': No such file or directory
```

- Microsoft Visual Studio本体をインストールしてやり直す。
 - **やり直し方**：一度~~PCを再起動~~`bazel shutdown`し、以下の2つのディレクトリを削除し、**STEP 7-2.**から再開する。<br>
・*C:\\Users\\USERNAME\\_bazel_USERNAME*<br>
・*D:\\tensorflow*

<font color="IndianRed">**・以下のようなエラーが出た場合 （2021.02.27追記）**</font>

```
ERROR: D:/tensorflow/tensorflow/python/BUILD:276:1: C++ compilation of rule '//tensorflow/python:bfloat16_lib' failed (Exit 2): msvc_wrapper_for_nvcc.bat failed: error executing command
```

- Microsoft Visual StudioとCUDAの噛み合わせが悪い場合に発生？
- Microsoft Visual Studio本体をインストールし、CUDAのインストーラを再度起動して再インストールしてやり直す。（やり直し方は上の項目と同じ）

<font color="IndianRed">**・以下のようなos.symlinkのエラーが出た場合　(2021.11.12追記)**</font>

```
os.symlink(os.path.abspath(from_path), os.path.abspath(to_path))
OSError: symbolic link privilege not held
```

- Python 3.8以上にする必要があり、それでも「クライアントは要求された特権を保有していません。」というOSエラーが出る場合は以下をONにする。
 - *Windowsの設定＞更新とセキュリティ＞開発者向け＞開発者用モード*</font>

<font color="IndianRed">**・以下のようなmodel_analyzer.ccのエラーが出た場合　(2021.11.12追記)**</font>

```
analyzer_wrapper/model_analyzer.cc(166): error C2001: 定数が 2 行目に続いています。
```

- 該当ソースコードの対象行を1行に修正する。

***

</div></details>






#### 8-3. ビルドを実行する （長時間かかる）

```msys2.exe
bash bazel-bin/tensorflow/tools/pip_package/build_pip_package /c/tmp/tensorflow_pkg
```

#### 8-4. TensorFlowをインストールする

```msys2.exe
cd /c/tmp/tensorflow_pkg
pip install tensorflow-X.X.X-cp3X-cp3Xm-win_amd64.whl
```

## STEP 9: TensorFlow C APIを利用するために
#### 9-1. libtensorflowをビルドする

```msys2.exe
cd /d/tensorflow/
bazel build --config opt //tensorflow/tools/lib_package:libtensorflow
```

[A]

- 以下にtensorflow.dllが生成される。
*C:¥msys64¥home¥USERNAME¥_bazel_USERNAME¥26orbg4z¥execroot¥org_tensorflow¥bazel-out¥x64_windows-opt¥bin¥tensorflow*

- tensorflow.libが必要な場合は、同じく生成されるtensorflow.dll.if.libをtensorflow.libにリネームすれば使える様子。

- c_api.hなどのヘッダファイル類は以下に格納されている。
*C:¥msys64¥home¥USERNAME¥_bazel_USERNAME¥26orbg4z¥execroot¥org_tensorflow¥tensorflow¥c*


[B]

- 以下にtensorflow.soが生成される。
*C:¥User¥USERNAME¥_bazel_USERNAME¥26orbg4z¥execroot¥org_tensorflow¥bazel-out¥x64_windows-opt¥bin¥tensorflow*
**（tensorflow.dllが生成されないため、これをlibファイルに変換する必要がある）**

- c_api.hなどのヘッダファイル類は以下に格納されている。
*C:¥User¥USERNAME¥_bazel_USERNAME¥26orbg4z¥execroot¥org_tensorflow¥tensorflow¥c*


## STEP 9B: soファイルをdllファイルに変換する（[B]のみ）
- 本内容は[こちらの記事](https://qiita.com/take-iwiw/items/3caf294726918c3af9e6 "DLLからlibファイルを作る (Windows版 TensorFlow for Cを例に)")[^5]を引用しています。
- ここではVCツール（VS2015 x64 Native Tools Command Promptなど）でコマンドを実行します。


#### 9B-1. soファイルの情報をdumpする
dumpbinコマンドでlibtensorflow.soファイルの情報を書き出す。

```cmd.exe
cd C:\Users\USERNAME\_bazel_USERNAME\26orbg4z\execroot\org_tensorflow\bazel-out\x64_windows-opt\bin\tensorflow\
dumpbin /exports libtensorflow.so > dump.txt
```

dump.txtの中身の例：

```dump.txt
Microsoft (R) COFF/PE Dumper Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file libtensorflow.so

File Type: DLL

  Section contains the following exports for libtensorflow.so

    00000000 characteristics
    5C9C9ABD time date stamp Xxx Xxx XX hh:mm:ss YYYY
        0.00 version
           1 ordinal base
        3078 number of functions
        3078 number of names

    ordinal hint RVA      name

          1    0 0256DD00 ??0?$MaybeStackArray@D$0CI@@icu_62@@AEAA@AEBV01@@Z
          2    1 02E5A0B0 ??0?$MaybeStackArray@D$0CI@@icu_62@@QEAA@$$QEAV01@@Z
          3    2 02E5A110 ??0?$MaybeStackArray@D$0CI@@icu_62@@QEAA@H@Z
          4    3 02E5A190 ??0?$MaybeStackArray@D$0CI@@icu_62@@QEAA@XZ
…以下略
```

#### 9B-2. dumpファイルから関数のシンボル情報を抽出したdefファイルを作成する
dump.txtのnameの列が関数名にあたるので、これを適当な方法で（文字列置換とExcelを駆使しても良い）すべて抽出し、以下のような書式でtensorflow.defとして保存する。

```tensorflow.def
LIBRARY tensorflow
EXPORTS
    ??0?$MaybeStackArray@D$0CI@@icu_62@@AEAA@AEBV01@@Z
    ??0?$MaybeStackArray@D$0CI@@icu_62@@QEAA@$$QEAV01@@Z
    ??0?$MaybeStackArray@D$0CI@@icu_62@@QEAA@H@Z
    ??0?$MaybeStackArray@D$0CI@@icu_62@@QEAA@XZ
    ??0Appendable@icu_62@@QEAA@AEBV01@@Z
…以下略
```

#### 9B-3. libファイルを作成する
VCツールのlib.exeを利用する。

```cmd.exe
lib /def:tensorflow.def /machine:x64
```

#### 9B-4. dllファイルを作成する
dumpしていたファイル（libtensorflow.so）をコピーまたはリネームする。

```cmd.exe
copy libtensorflow.so tensorflow.dll
```

<br>

[^1]: [TensorFlow | インストール > Windowsでのソースからのビルド](https://www.tensorflow.org/install/source_windows "Windowsでのソースからのビルド")
[^2]: [Medium | Install Tensorflow GPU with CUDA 10.0 and cudNN 7.4 for Python on Windows 10](https://medium.com/@hippiedev/install-tensorflow-gpu-with-cuda-10-0-and-cudnn-7-4-for-python-on-windows-10-be95629e4f54 "Install Tensorflow GPU with CUDA 10.0 and cudNN 7.4 for Python on Windows 10")
[^3]: [Hatena Blog | msys2 + pacmanをproxy環境で使う](https://nantonaku-shiawase.hatenablog.com/entry/2014/10/11/163342 "msys2 + pacmanをproxy環境で使う")
[^4]: [Qiita | Numpy1.19.4のエラー RuntimeError: The current Numpy installation fails to pass a sanity check due to a bug in the windows runtime.](https://qiita.com/bear_montblanc/items/b4b75dfd77da98076da5 "Numpy1.19.4のエラー")
[^5]: [Qiita | DLLからlibファイルを作る (Windows版 TensorFlow for Cを例に)](https://qiita.com/take-iwiw/items/3caf294726918c3af9e6 "DLLからlibファイルを作る (Windows版 TensorFlow for Cを例に)")