macOS で「this TensorFlow binary was not compiled to use: AVX2 FMA」エラー

Python3 + tensorflow で `tf.Session()` を実行するとワーニングが表示される。

> 2018-06-18 13:51:51.762563: I tensorflow/core/platform/cpu_feature_guard.cc:140] **Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA**

## TL; DR <sub><sup>（今北産業）</sup></sub>

1. ノープロブレム。**無視しても大丈夫**です。
1. `pip` でインストールした tensorflow のバイナリが、現在の CPU に最適化されたものではなく**汎用（安定版）のバイナリを使っている**というワーニングです。
1. 気になる人は**ワーニングを非表示にする**か、ソースからコンパイルし直します。

---

このメッセージは、次のように **`pip` 経由で TensorFlow のバイナリをインストールしている場合に表示されるメッセージ**です。

```
$ pip install tensorflow
```

エラー（警告）の意味は以下になります。

1. 「GPU を使わず CPU を使う」
2. 「CPU 自体は `AVX2 + FMA` に対応しているが、この `tensorflow` のバイナリでは使えないなり」

**TensorFlow は `AVX2` の代わりに `AVX` を使う**ので（速度や精度が気にならなければ）スルーしても大丈夫です。メッセージ内容や `AVX` `FMA` の詳細については以下の [TS; DR](#ts-dr-%E3%83%AF%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0%E5%86%85%E5%AE%B9%E3%82%92%E7%90%86%E8%A7%A3%E3%81%99%E3%82%8B%E3%82%B3%E3%83%9E%E3%82%B1%E3%83%BC%E3%81%93%E3%81%A8) をご覧ください。

なお、**警告レベルを変えるとワーニングは表示されなくなる**ので、表示が気になる場合は `TF_CPP_MIN_LOG_LEVEL` の値を '2' に設定してください。

```python
>>> import os
>>> os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
```

:::note warn
<font color='red'>**注意**</font>
上記設定で**警告レベルを変えても `AVX2` `FMA` とやらは使われません**。それらを使いたい場合は、別途 [TensorFlow をコンパイルする](https://www.tensorflow.org/install/source)必要あります。

- 「[Build from source](https://www.tensorflow.org/install/source) | Install」@ tensorflow.org
- [Qiita 記事の「{Mac macOS} TensorFlow ビルド ソース コンパイル」の検索結果](https://www.google.co.jp/search?q=site%3Aqiita.com+%7BMac+macOS%7D+TensorFlow+%E3%83%93%E3%83%AB%E3%83%89+%E3%82%BD%E3%83%BC%E3%82%B9+%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB&tbs=qdr:y) @ Google（直近１年以内の更新・作成絞り）
:::

## TS; DR <sub><sup>（ワーニング内容を完全に理解した気になるコマケーこと）</sup></sub>

### `Hello? TensorFlow?`

2018 年の 6 月ごろの話しです。「[Python3 TensorFlow for Mac 環境構築](https://qiita.com/tom_ato/items/bbdf4574b3ecb0048fa1)」の Qiita 記事を読みながら Hello World 写経をしていたのですが、途中 [tensorflow ライブラリの `session` クラス](https://qiita.com/mski_iksm/items/d0ee0bb679f3e8502747#class-tfsession)（`tf.Session()`）を使うところでエラーメッセージが表示されました。

```python:HelloWorld
$ python
Python 3.6.0 (default, Jun 18 2018, 13:35:26) 
[GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.10.25.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import tensorflow as tf
>>>
>>> hello = tf.constant('Hello, TensorFlow!')
>>> sess  = tf.Session()
2018-06-18 13:51:51.762563: I tensorflow/core/platform/cpu_feature_guard.cc:140] Your CPU
 supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
>>>
>>> print(sess.run(hello))
b'Hello, TensorFlow!'
>>>
>>> exit();
```

しかし「`b'Hello, TensorFlow!'`」と、何故か `b` と OK（👍）をもらいながらも "Hello world" は動いたことは動いたので、エラーでなくワーニング（警告）のようです。

ただ、そのメッセージ内容が「おぬし、なんか違うバイナリ使ってね？」っぽい内容なので気になりました。

> Your CPU supports instructions that this TensorFlow binary was not compiled to use: `AVX2` `FMA`

> 🐒 &nbsp; 訳）このコンパイル済み TensorFlow のバイナリでは利用できない次の命令を、実はあなたの CPU では使えちゃったりなんか（サポート）しています：`AVX2` `FMA`

最初メッセージを読んだ時、"`support instructions`" とあるので「なに？指示をサポート？CPU が？」と意味がわかりませんでした。

わかりづらいのですが、"`support instructions`"は「指示をサポート」ではなく「命令に対応」の意味で使われています。

つまり、**"`Your CPU support instructions that ...`" は「あなたの CPU は、次の CPU 命令をサポート（対応）してます」という意味**です。"`Your CPU supports instructions for ...`" であればわかりやすいのに。

その上で、「`AVX2` や `FMA` とやらが使えるのに、使えるようにコンパイルされてないよ」と警告してくれているのです。

よって「貴殿のCPU は`AVX2` `FMA` に対応しているなり」と言うことと「現在ご利用中の TensorFlow バイナリは `AVX2` `FMA` 非サポート版なり」と言うことのようです。

TensorFlow 公式の [Issue](https://github.com/tensorflow/tensorflow/issues?q=is%3Aissue) にもあがっていました。

> This isn't an error, just warnings saying if you build TensorFlow from source it can be faster on your machine.
> （[TensorFlow 公式リポジトリ Issue #8037](https://github.com/tensorflow/tensorflow/issues/8037#issuecomment-283831398) @ GitHub より）
> 
> 訳） こんたエラーじゃらせん。 TensorFlow をソースからビルドすっと速う動作すっよと注意してくれちょっだけじゃ。

では `AVX2` でなくとも、デフォルトで `AVX` で動くのに、なぜわざわざ警告してくれているのでしょうか？

### AVX（[Advanced Vector Extension](https://ja.wikipedia.org/wiki/Streaming_SIMD_Extensions#Intel_AVX)）とは

気になったので「**`AVX` とは何なのか**」と調べてみたところ、[Intel](https://ja.wikipedia.org/wiki/Sandy_Bridge%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3) や [x86 系 AMD のプロセッサ](https://ja.wikipedia.org/wiki/AMD_FX)で使える <strong>[SIMD](https://ja.wikipedia.org/wiki/SIMD) を実行するのに必要な命令を拡張した「命令セットの呼称」</strong>とのこと。

意味がわからないので、さらに調べてみると SIMD とは「１つの命令でベクトルの計算処理を複数のデータにわけて並列で処理するタイプの演算方式」のようです。これを実現するために集めた関数やメソッドのライブラリが AVX のようです。

この AVX により、あれこれと低レベルの CPU 命令を組み合わせて計算していたものが少ない命令で実行できる、ということのようです。つまり「**`AVX` に対応していたら速くなるぞ**」と。「○△□」と打っていたものが「×」で同じことができる、みたいなものでしょうか。

<details><summary>ちなみに「ベクトル」とは「ソートされた数値だけの配列のこと」です。</summary><div>

#### ベクトルとは

ベクトルと聞いて「`sin` `cos` とか使ったり、矢印とか引いちゃったりするアレでしょ？」とか、デザイナーだと「フォトショじゃなくイラレのタイプのアレでしょ？」とか思うかもしれません。

おそらく、<ruby>この用語<rt>ベクトル</rt></ruby>が機械学習の勉強の最初の妨げになっている気がします。

まず、「ベクター」と「ベクトル」もどちらも `vector` です。「ピザ」と「ピッツァ」の違いみたいなものらしいです。

恐れずに言うと、機械学習における**ベクトルとは数値だけで構成された配列データのこと**です。そう例の<ruby>アレ<rt>array</rt></ruby>です。単純に。

```jsonc:JSONのベクトル例1
[
  [ 1, 2, 3 ], // ← これがベクトル。そして機械学習用の 1 データ
  [ 4, 5, 6 ], // ← これがベクトル。そして機械学習用の 1 データ
  ...          // 以下同文
]
```

普通の配列と何が違うかというと、以下の 3 つの条件を網羅していると機械学習の世界では「ベクトル」と呼ぶのです。

1. 数値だけで構成されている
1. 特定の方向に並んで（ソートされて）いる
1. 各値が量を表している

ここで言う「特定の方向」とは、例えば登録順や時間順や軸といったことです。変数 a と b に代入する値を `[3, 10]` とした場合、この 2 つの並び順は重要です。つまり「並び順に意味を持っているもの」です。

次に「各値が量を表している」とは、機械学習の用語で「スカラー」のことです。ゆうて、数値のことです。

数値なのに「スカラー」と名前が付いているのは、我々の世界でいう「型」のようなものです。同じ「12」でも「`int(12)`」「`int64(12)`」「`float32(12)`」では異なるように、一見同じ数値でも性質が違うから名前が付いている（型がある）のです。

「12」だと、ただの整数なのですが「座標の 12」と言われた時点で「0 から 12 移動した量」という意味を持ちます。つまり大きさを表すことになります。

このことからベクトルの各値のことを、大きさを測る「スケール（`scale`）」と同じ語源の、大きさを示す「スカラー（`scalar`）」と呼びます。[英語の発音を聞いてみる](https://dictionary.cambridge.org/pronunciation/english/scalar)と、「`scalar`」のことを「スケイラー」とスケール寄りに発音しているのがわかります。

つまり「スカラー」の集合体を「ベクトル」と呼んでいると考えていいでしょう。

そして、プログラムの世界で言う「多次元配列」にしたものを、機械学習の世界では「テンソル」と呼ぶのです。

テンソルとは、伸びたり縮んだりする筋肉の「張筋」の英語である `tensor` が語源です。機械学習では、配列を多次元配列化（収縮）させた後、元に戻した（展開させた）時に同じ結果になるかを繰り返して精度を上げていくので、筋肉の伸び縮みと見立てた名前が付いているのです。

- 「数値」→「配列」→「多次元配列」
- 「スカラー」→「ベクトル」→「テンソル」

「んだら、ベクトルとかテンソルとか、小難しい名前を使わないで配列や多次元配列でいいじゃん」と思うかもしれません。

それは、コンパイル型のプログラム言語などで「型々うるさい」と感じるのと似ています。「`int(12)` とか `float32(12)` とか宣言しなくても、ぜんぶ `12` でいいじゃん。流れをみて理解しろよ」と。

「型」が大事な理由は、処理速度です。その変数の型（値の種類）が「予めわかっている」のと、「使ってみないとわからない」とでは、予め用意できるメモリ領域や最適化の方法が異なるため、処理速度に大きく影響します。

数学の世界でも、「X」と書かれても「これが数値なのか配列なのかわからん」という状況があります。長ったらしい計算式を最初から読み直すのは大変なので、数学の世界では「𝕏」と書かれた場合は配列型（ベクトル）として扱う、といった型が決まっています。

つまり、同じもの同士が迅速に意思疎通し、正確に伝えるために「型」というものが存在します。

そのため、機械学習を勉強していて小難しい専門用語が出てきても、「上から目線」と捉えずに、ライトノベルで魔法を詠唱しているくらいに考えた方が楽しいです。

「（あ、厨二病が発症したんだな）」と。

しかも、すごいことに、我慢強く詠唱を理解しようとすれば、自分でも使えるようになるのですから。

#### 機械学習用のデータの作り方

例えば、下記のような測定データと判定結果をまとめたデータがあったとします。`hoge`〜`piyo` が測定値で、`result` がその結果です。

```json
[
  { "hoge": 123, "fuga": 456, "piyo": 789, "result": "ok" },
  { "hoge": 987, "fuga": 654, "piyo": 321, "result": "ng" },
  ...
]
```

このデータを、機械学習用のデータに変換したい場合は以下のステップで可能です。

1. 値が文字列である場合は、数値の ID に置き換える。
2. CSV 形式に変換する。
3. 測定データ以外に判定結果（答え）のデータもある場合は２つにわける
4. ヘッダー行を削除し、配列に変換する。

まず、文字列には ID を振り数値に置き換えます。ここでは文字列 "`ok`" を数値の 1、"`ng`" を 0 としました。

```json
[
  { "hoge": 123, "fuga": 456, "piyo": 789, "result": 1 },
  { "hoge": 987, "fuga": 654, "piyo": 321, "result": 0 },
  ...
]
```

そして、このデータを CSV 形式にします。

| hoge | fuga | piyo | result |
| :--: | :--: | :--: | :--: | 
| 123  | 456  | 789  | 1 |
| 987  | 654  | 321  | 0 |
| ...  | ...  | ...  | ... |

上記は、以下と同じと思ってください。

```csv
"hoge","fuga","piyo","result"
123,456,789,1
987,654,321,0
...
```

次に、このデータを「測定データ」と「判定結果」」にわけます。

| hoge | fuga | piyo |
| :--: | :--: | :--: |
| 123  | 456  | 789  |
| 987  | 654  | 321  |
| ...  | ...  | ...  |

| result |
| :--:   |
| 1      |
| 0      |
| ...    |

最後に、CSV からヘッダー行を取り除き、以下のように数値だけの配列になったもの（ベクトルが並んだもの）を機械学習では「学習データ」と呼んでいます。また、ベクトルのうち判定結果のデータを「教師データ」と呼びます。

```python
# 測定データ = 学習データ (train data)
x=[
  [ 123, 456, 789 ],
  [ 987, 654, 321 ],
  ...
]

# 判定結果 = 教師データ (target data)
y=[
  [ 1 ],
  [ 0 ],
  ...
]
```

「ベクトル、ベクトル」と言われると、`for` ループの足し算を「Σ（シグマ）」で表現されるようなビビり感があります。

単純に**時間順だったり登録順だったり、「特定の方向にソートされた、連続した数値の集まり」を「ベクトル」と呼んでいる**のです。

逆に言えば、**ベクトルは数値だけの配列なので並列処理もしやすい**のです。そのため、AVX もしくは AVX2 といった CPU/GPU レベルの並列処理の実装が効果を出すとも言えます。

> <sup>🐒 機械学習の業界も、同業者間（わかっている人同士）では、伝える時に長ったらしくならないように、数学ベースの機械学習[プロトコル](https://qiita.com/KEINOS/items/cb7f629268b90ef6493d)で会話しています。素人でアウェイの私には小難しく感じるのですが「Go に入っては Go に従え」と言う IT 業界のことわざのようにプロトコルを合わせる必要があります。しかし、ルール以前に用語に付いて行くのがたいへん。</sup>

</div></details>

[Word2Vec](https://www.google.co.jp/search?q=site:qiita.com+word2vec%E3%81%A8%E3%81%AF) もそうですが、昨今の機械学習はベクトルの計算をしまくっています。

こういった CPU/GPU 命令レベルの差が最終的な速度に大きく影響を与えるため、**実行ファイルが CPU/GPU の「どの拡張命令をサポートしているか」は重要な要素**なのだと思われます。

そして問題の「[AVX2](https://ja.wikipedia.org/wiki/Streaming_SIMD_Extensions#Intel_AVX2)」は `AVX` の後継で、整数演算命令が 128 ビットから倍の 256 ビットに拡張されたものです。つまり、1 回の処理で扱えるデータ量が増えたぶん<strong>`AVX2` に対応していたら早く処理が終わるぞ</strong>、と。

Intel は [Haswell](https://ja.wikipedia.org/wiki/Haswell%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3)、AMD は [Excavator](https://ja.wikipedia.org/wiki/Excavator_(%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3)) 以降のプロセッサから `AVX2` が使えるようになっているらしいです。

おいどんの MacBookPro は Early 2015 の [2.7 GHz Intel Core i5（5257U）](https://ja.wikipedia.org/wiki/Broadwell%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3#%E3%83%A2%E3%83%90%E3%82%A4%E3%83%AB%E5%90%91%E3%81%91) なので記憶が正しければ Haswell の次の [Broadwell](https://ja.wikipedia.org/wiki/Broadwell%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3) なはず。となると `AVX2` とやらも使えるはずなのですが、肝心の TensorFlow のバイナリが対応していないのでメッセージで教えてくれた、という流れのようです。

```shell-session:サポートしている命令一覧にFMA,AVX,AVX2がいる（[]は筆者）
$ sysctl -a machdep.cpu | grep features
machdep.cpu.features: FPU VME DE PSE TSC MSR PAE MCE CX8 APIC SEP MTRR PGE MCA CMOV PAT PSE36
 CLFSH DS ACPI MMX FXSR SSE SSE2 SS HTT TM PBE SSE3 PCLMULQDQ DTES64 MON DSCPL VMX EST TM2 
 SSSE3 [FMA] CX16 TPR PDCM SSE4.1 SSE4.2 x2APIC MOVBE POPCNT AES PCID XSAVE OSXSAVE SEGLIM64 
 TSCTMR [AVX1.0] RDRAND F16C
machdep.cpu.leaf7_features: RDWRFSGS TSC_THREAD_OFFSET BMI1 [AVX2] SMEP BMI2 ERMS INVPCID 
 FPU_CSDS RDSEED ADX SMAP IPT MDCLEAR IBRS STIBP L1DF SSBD
machdep.cpu.extfeatures: SYSCALL XD 1GBPAGE EM64T LAHF LZCNT PREFETCHW RDTSCP TSCI
```

大抵の GPU はそもそも [SIMD](https://ja.wikipedia.org/wiki/SIMD)（ベクトル演算を並列処理するタイプ）で動いているのですが、Mac の GPU は機種によって大きく異なります。しかも Windows や Linux などの DOS/V 互換機と違い、おいそれと GPU の交換ができません。

そのため TensorFlow を、どの環境でも `pip` で動くようにするためには、GPU でなく CPU、かつ最低限共通する Intel CPU の仕様に対応させたのだと思われます。

つまり、`pip` を使わずに手動で TensorFlow をコンパイル（ソースからビルド）してインストールすればワーニングは表示されないし、速度も速くなる、と。

しかし、どの写経も `pip install` を前提にしているものが多く、いまの初級のおいどんには速度よりも実際に触れることが優先 ~~アップデートも楽~~ なので、**とりあえず動けばいい**ためメッセージは無視することにしました、とさ。

でも、「お客さ〜ん」と毎回言われるのが気になる時は、実行クラスを呼び出す前に、以下のように環境設定を変更して**警告レベルを下げると表示を回避できます**。

```python:警告レベルを下げる例
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
```

```python:HelloWorldのワーニングなし
$ python
Python 3.6.0 (default, Jun 18 2018, 13:35:26) 
[GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.10.25.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import os
>>> import tensorflow as tf
>>>
>>> os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
>>>
>>> hello = tf.constant('Hello, TensorFlow!')
>>> sess  = tf.Session()
>>>
>>> print(sess.run(hello))
b'Hello, TensorFlow!'
>>> 
>>> exit()
```

:ok_hand: Hello TensorFlow! よろしくね

### FMA（fused multiply–add, [融合積和演算](https://ja.wikipedia.org/wiki/%E7%A9%8D%E5%92%8C%E6%BC%94%E7%AE%97)）

FMA も気になったので、ついでに調べてみました。

簡単に言うと「乗算の最終的な誤差を小さくするための命令」です。つまり、掛け算の和を計算する時の Float（浮動小数点数）の値を途中で丸めずに１つの命令で行うのが特徴で、先の AVX2 の命令セットに実装されているものらしいです。つまり、CPU とバイナリが **`FMA` に対応していたら精度が上がるぞ**ということですな。

### TensorFlow のコンパイル

使い方も消化したし、バージョンアップのたびに疲労コンパイるのも大丈夫という方はこちら。

- 「[Build from source](https://www.tensorflow.org/install/source) | Install」@ tensorflow.org
- [「TensorFlow ビルド ソース コンパイル」の検索結果 @ Qiita](https://www.google.co.jp/search?q=site:qiita.com+TensorFlow+%E3%83%93%E3%83%AB%E3%83%89+%E3%82%BD%E3%83%BC%E3%82%B9+%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB&tbs=qdr:y) @ Google


## 検証環境
- macOS HighSierra (OSX 10.13.5)
- `python --version`: 3.6.0
- `pyenv --version`: 1.2.4
- `pip3 list | grep tensorflow`: tensorflow  1.8.0    

## 参考文献

- Issue #8037「[How to compile tensorflow using SSE4.1, SSE4.2, and AVX.](https://github.com/tensorflow/tensorflow/issues/8037)」@ TensorFlow @ GitHub
- 「[Python3 TensorFlow for Mac 環境構築](https://qiita.com/tom_ato/items/bbdf4574b3ecb0048fa1)」@ Qiita
- 「[Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX AVX2](https://stackoverflow.com/questions/47068709/your-cpu-supports-instructions-that-this-tensorflow-binary-was-not-compiled-to-u)」@ StackOverflow
- 「[Tensorflowの関数理解](https://qiita.com/mski_iksm/items/d0ee0bb679f3e8502747)」@ Qiita