macOS に最適化された TensorFlow を Apple M1 チップ 環境で import するまでの Tips

## 更新履歴

- 2020/11/26 見出しの階層が誤っていたのを修正し、また、見出しの一部を変更しました。

## 概要

- "Mac-optimized TensorFlow and TensorFlow Addons" (https://github.com/apple/tensorflow_macos) を MacBook Air with M1 で動かすための試行錯誤
- Intel アーキテクチャの MacBook Pro (Early 2015) から環境を移行した上で、 ARM ベース アーキテクチャに合わせるための方法の模索
- (Mac-optimized) TensorFlow の実行結果は含まず、ひとまず import するところまで

## 背景

日本時間の 2020/11/11 03:00 から行われた Apple Event で詳細が公開された、Apple の新型チップ「M1」。
発表の中で、具体的なソフトウェアの名称として「TensorFlow」の名前が挙がっていたので気になっていたのですが、その後、Apple の GitHub Organization から "Mac-optimized TensorFlow and TensorFlow Addons" (https://github.com/apple/tensorflow_macos) が公開されました。プレリリースのドライバーで、[Apple の `ML Compute` framework](https://developer.apple.com/documentation/mlcompute) を使っている、とのことです。

ちょうど MacBook Pro (Early 2015) からのリプレースを考えていたため、M1 チップの載った MacBook Air を購入し、いろいろと動作を試してみることにしました。[^1]

[^1]: 2020/11/11 (JST) に発表・予約開始された、というのが印象的で、購入に踏み切ったというのもあります。ゲーム・アニメ等が苦手ではない方で、お手すきのお時間がありましたら「アイドルマスター 趣味 プログラミング 誕生日」でご検索ください。今回は「開発」というほどのことはしておりませんが、こうした点で「アイマス駆動開発」（参考： https://www.slideshare.net/treby/imas-driven-developemnt ） の影響を受けたものと言えるかもしれません。

`tensorflow_macos` を動かす上で、Intel アーキテクチャの Mac から ARM ベースのアーキテクチャの Mac への移行で引っかかるところがいくつかありましたので、記録として残せればと思います。[^2]

申し遅れましたが、私は @forest1988 と申します。
Qiita への投稿は今回が初めてとなります。至らない部分が多々あるかと思いますが、ご笑覧いただき、その上で少しでもお役に立つ部分があれば嬉しく思います。

試行錯誤の過程を書いているために無駄に遠回りになっている部分や、私の勘違いから誤謬が含まれている可能性があります。
お気付きの点がありましたら、ご指摘をいただけますと大変幸いです。

[^2]: なお、 Intel アーキテクチャの Mac での動作については Qiita 記事「appleがmac用tensorflowを出したようなのでintelのMacBookairにインストールしてみる」(https://qiita.com/notfolder/items/b27cc00bd77eb1587832) があり、参考にさせていただきました。お礼申し上げます。

## 環境

- MacBook Air
    - Apple M1 チップ (8コアCPU, 8コアGPU)
    - macOS 11.0.1 (Big Sur)
- MacBook Pro (Early 2015) から「移行アシスタント」によりデータ移行済み

環境を移行したことで、初期状態の macOS 11 (Big Sur) とは変わっているところがあります（たとえば、terminal の shell が zsh ではなく、macOS Catalina 以前から使っている bash のままになっているなど）。

そのため、データの移行などをせずにまっさらな状態で Big Sur with M1 を使われる方や、Windows マシンなどから移行される方、そもそも作っていた環境がまるで違う方とは、
同様に実行しても異なる結果になる可能性が十分にあることを予めお断りしておきます。

## まず試して失敗したこと

MacBook Pro で作っていた python の環境がそのままコピーできていたため、まずはそれで動かすことを試みました。
具体的には、pyenv で anaconda3-2020.07 を入れていた状態です。 Python の version は 3.8.3 でした。

リンクを辿り、 https://github.com/apple/tensorflow_macos/releases にある `tensorflow_macos-0.1alpha0.tar.gz` をダウンロードしてきます。

GitHub のリポジトリにあるのはダウンロードとインストール用のスクリプトですが、このプレリリースの圧縮ファイルの中には（スクリプトでダウンロードできる）最適化された TensorFlow などが含まれているため、331.6MBほどのサイズになっています。

まずは、Python 3.8 の venv を使って `tensorflow_macos` 用の仮想環境を作ります。

pyenv の anaconda にさらに venv を重ねるとおかしくならないだろうか……という不安はあったのですが、とりあえず試してみることに（後述するように、それ以前の問題だったわけですが）。

```bash
$ /bin/bash install_venv.sh --prompt --python /path/to/anaconda3-2020.7/bin/python
```

を実行し、途中のプロンプトはデフォルト設定で進めました。[^3]
プロンプトで指定した場所に `tensorflow_macos_venv` ディレクトリが作成され、ここにある `/bin/activate`  を使うことで作られた仮想環境に入ることができます。[^4]

[^3]: macOS Catalina からは zsh がデフォルトになっているはずなので、敢えて bash を使っているのに何か理由があるのかは興味があるところです。

[^4]: `$PS1` の設定で表示できるホスト名やユーザー名などは `primary_prompt_string` に、 tensorflow_macos_venv を置いた場所は `/path/to/` で置き換えています。ご自身の環境に合わせて読み替えていただければと存じます。特に前者について、以下の記事を参考にさせていただきました。お礼申し上げます：「[Mac] ターミナルの$前の出力内容をカスタマイズする」(https://www.yoheim.net/blog.php?q=20140309) , 「プロンプトの確認や設定 - Pocketstudio.jp Linux Wiki」(http://pocketstudio.jp/linux/?%A5%D7%A5%ED%A5%F3%A5%D7%A5%C8%A4%CE%B3%CE%C7%A7%A4%E4%C0%DF%C4%EA)  

```bash
$ . /path/to/tensorflow_macos_venv/bin/activate
(tensorflow_macos_venv) (base) primary_prompt_string$ python
Python 3.8.3 (default, Jul  2 2020, 11:26:31)
[Clang 10.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
```

[tensorflow_macos のリポジトリ](https://github.com/apple/tensorflow_macos) の README.md には、CPU と GPU を選択するためのコードスニペットが書かれているので、まずはこれを試してみることにしました。

```python
# Import mlcompute module to use the optional set_mlc_device API for device selection with ML Compute.
from tensorflow.python.compiler.mlcompute import mlcompute
```

これを、先ほど対話モードで立ち上げた python で試してみます。

```python
>>> # Import mlcompute module to use the optional set_mlc_device API for device selection with ML Compute.
>>> from tensorflow.python.compiler.mlcompute import mlcompute
Traceback (most recent call last):
  File "/path/to/tensorflow_macos_venv/lib/python3.8/site-packages/tensorflow/python/pywrap_tensorflow.py", line 64, in <module>
    from tensorflow.python._pywrap_tensorflow_internal import *
ImportError: dlopen(/path/to/tensorflow_macos_venv/lib/python3.8/site-packages/tensorflow/python/_pywrap_tensorflow_internal.so, 6): no suitable image found.  Did find:
	/path/to/tensorflow_macos_venv/lib/python3.8/site-packages/tensorflow/python/_pywrap_tensorflow_internal.so: mach-o, but wrong architecture
	/path/to/tensorflow_macos_venv/lib/python3.8/site-packages/tensorflow/python/_pywrap_tensorflow_internal.so: mach-o, but wrong architecture

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/tensorflow_macos_venv/lib/python3.8/site-packages/tensorflow/__init__.py", line 41, in <module>
    from tensorflow.python.tools import module_util as _module_util
  File "/path/to/tensorflow_macos_venv/lib/python3.8/site-packages/tensorflow/python/__init__.py", line 39, in <module>
    from tensorflow.python import pywrap_tensorflow as _pywrap_tensorflow
  File "/path/to/tensorflow_macos_venv/lib/python3.8/site-packages/tensorflow/python/pywrap_tensorflow.py", line 83, in <module>
    raise ImportError(msg)
ImportError: Traceback (most recent call last):
  File "/path/to/tensorflow_macos_venv/lib/python3.8/site-packages/tensorflow/python/pywrap_tensorflow.py", line 64, in <module>
    from tensorflow.python._pywrap_tensorflow_internal import *
ImportError: dlopen(/path/to/tensorflow_macos_venv/lib/python3.8/site-packages/tensorflow/python/_pywrap_tensorflow_internal.so, 6): no suitable image found.  Did find:
	/path/to/tensorflow_macos_venv/lib/python3.8/site-packages/tensorflow/python/_pywrap_tensorflow_internal.so: mach-o, but wrong architecture
	/path/to/tensorflow_macos_venv/lib/python3.8/site-packages/tensorflow/python/_pywrap_tensorflow_internal.so: mach-o, but wrong architecture


Failed to load the native TensorFlow runtime.

See https://www.tensorflow.org/install/errors

for some common reasons and solutions.  Include the entire stack trace
above this error message when asking for help.
```

それならばと思い、もう一方のインストール方法を試してみることにしました。
[リポジトリの README.md の INSTALLATION の項目](https://github.com/apple/tensorflow_macos#installation) を見て、
次のスクリプトを動かしました。

```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/apple/tensorflow_macos/master/scripts/download_and_install.sh)"
```

やはり同様のエラーが起き、ここで改めてエラーメッセージをしっかり確認して、 `wrong architecture` というところに目が留まりました。

使っていた anaconda が Intel のアーキテクチャである x86-64 対応のものであったのが原因なのではないか、と考え、 Python の環境を整え直すことにしました。

## Homebrew での Python のインストール（今回はうまくいっていません）

まずは、 `pyenv` や `anaconda` に関する PATH の設定を `~/.bash_profile` から削除し、 terminal を再起動しました。これで、移行元のマシンからコピーした pyenv, anaconda を参照しなくなるはずです。

その上で、既に入っていた Homebrew で Python の入れ直しを試したところ、以下のエラーに遭遇しました。

```bash
Error: Cannot install in Homebrew on ARM processor in Intel default prefix (/usr/local)!
Please create a new installation in /opt/homebrew using one of the
"Alternative Installs" from:
  https://docs.brew.sh/Installation
You can migrate your previously installed formula list with:
  brew bundle dump
```

["Alternative Installs"](https://docs.brew.sh/Installation) を読み、以下のように Homebrew を入れ直します。

```bash
$ mkdir homebrew && curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C homebrew
$ mv homebrew/ /opt/
```

`~/.bashrc` に以下を追記して、

```bash
export PATH=/opt/homebrew/bin/:$PATH
```

`source` を使って反映します。

```bash
$ source ~/.bashrc
```

次に、`brew bundle dump` で install 済みの package の list を作ります。[^5]

[^5]: 既に入っていた Homebrew で行う必要があるので、新しい Homebrew を入れる前に実行したほうが、フルパス指定をする面倒がなくて良かったかと思います。

```bash
$ /usr/local/Homebrew/bin/brew bundle dump
```

この結果、`Brewfile` として以下のようなファイルが生成されました。

```txt
tap "cartr/qt4"
tap "homebrew/bundle"
...
<略>
...
tap "sanemat/font"
brew "automake"
brew "python@3.9"
brew "python@3.8"
...
<略>
...
brew "sanemat/font/ricty"
```

これを以下のように反映します。

```bash
$ brew tap Homebrew/bundle
$ touch Brewfile
$ brew bundle --file Brewfile
```

しかし、ここで以下のような warning が発生し、いくつかの package の install に失敗しました。

```txt
Warning: You are running macOS on a arm64 CPU architecture.
We do not provide support for this (yet).
Reinstall Homebrew under Rosetta 2 until we support it.
You will encounter build failures with some formulae.
Please create pull requests instead of asking for help on Homebrew's GitHub,
Twitter or any other official channels. You are responsible for resolving
any issues you experience while you are running this
unsupported configuration.
```

Python を入れることはできていましたが、先と同様の問題を生じそうだったため、これはアンインストールして、次に進みます。

## System の Python を試す

[REQUIREMENTS](https://github.com/apple/tensorflow_macos#requirements) に Xcode Command Line Tools の使用について書かれていたので、

```bash
$ xcode-select --install
```

で Xcode Command Line Tools を入れました。 [^6]
これで Python の問題が解決する……かと思いきや、環境を移行した関係か、なぜか Python 3.9 が動いてしまいます。
これでは、Python 3.8 を要件とする `tensorflow_macos` は動かない……と焦ったのですが、これには二つの原因がありました。

- Homebrew でのアンインストールに失敗していた。
- 環境移行のためか、Python 3.9 が `/usr/local/bin/python3` に残っていた。

どうやら、原因の一つは、Homebrew で install した Python がうまくアンインストールできなかったことのようです。
`which python` で確認したところ、 `/opt/homebrew/bin//python3` が参照されていました。 [^7]
実行した際に表示される日付などから見ても、上述のインストール時のものと思われます。

再度アンインストールを試みると、

```bash
$ brew uninstall python3
Error: Refusing to uninstall /opt/homebrew/Cellar/python@3.9/3.9.0_2
because it is required by itstool, libxml2 and sphinx-doc, which are currently installed.
You can override this and force removal with:
  brew uninstall --ignore-dependencies python3
```

と Error が出るので、この指示に従ってアンインストールを行います。

```bash
$ brew uninstall --ignore-dependencies python3
Uninstalling /opt/homebrew/Cellar/python@3.9/3.9.0_2... (9,224 files, 137.5MB)
```

これで、 `which python3` が `/usr/local/bin/python3` を指してくれるようになりました。

しかし、これも Python 3.9.0 になっています。

```bash
$ /usr/local/bin/python3
Python 3.9.0 (default, Nov  4 2020, 22:18:28)
[Clang 12.0.0 (clang-1200.0.32.21)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 
```

ここで、 Homebrew の warning を思い返してみると、

```txt
Error: Cannot install in Homebrew on ARM processor in Intel default prefix (/usr/local)!
```

と書いてありました。
これまでは特に詳しく考えずに使っていたのですが、`/usr/local` が Intel の default prefix であり、 ARM processor に対応していないということは、
この `/usr/local/bin` にある python はどこから来たものなのでしょうか？

おそらく、環境移行をする前に Homebrew で入れていた Python が残っているのではないか、と思われます。
そこで、移行前に入れてあった Homebrew を使ってアンインストールを試みます。

```bash
$ /usr/local/Homebrew/bin/brew uninstall python3
```

先と同様の依存関係のエラーが出るので、 `--ignore-dependencies` を付けて再度実行します。

```bash
$ /usr/local/Homebrew/bin/brew uninstall --ignore-dependencies python3
```

今回はアンインストールに成功し、`which python3` が `/usr/bin/python3` を指してくれるようになりました。

```bash
$ which python3
/usr/bin/python3
$ python3
Python 3.8.2 (default, Oct  2 2020, 10:45:41)
[Clang 12.0.0 (clang-1200.0.32.27)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
```

これで、ようやく Python 環境の準備が整ったはずです！

[^6]: Xcode Command Line Tools のインストールについては Qiita 記事：「MacでXcodeと一緒にpython3をインストールする」(https://qiita.com/todotani/items/73877deea8773c316694) を参考にさせていただきました。お礼申し上げます。

[^7]: "//" になっているのは誤記ではなく、実際にそのように表示されていました。 `export PATH=/opt/homebrew/bin/:$PATH` の際に、 `bin` の後に `/` を付けたのが良くなかったのだと思われます。

## 再度、README に従って実行

``` bash
$ /bin/bash install_venv.sh --prompt --python=/usr/bin/python3
```

何の問題もなく、すっきりとインストールできました。

```bash
$ . "/path/to/tensorflow_macos_venv/bin/activate"
(tensorflow_macos_venv) primary_prompt_string$ which python
/path/to/tensorflow_macos_venv/bin/python
(tensorflow_macos_venv) primary_prompt_string$ python
Python 3.8.2 (default, Oct  2 2020, 10:45:41)
[Clang 12.0.0 (clang-1200.0.32.27)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> # Import mlcompute module to use the optional set_mlc_device API for device selection with ML Compute.
>>> from tensorflow.python.compiler.mlcompute import mlcompute
>>>
```

今回はエラーなく import ができました！
では、 CPU と GPU の使用についてはどうでしょうか。

```python
>>> # Select CPU device.
>>> mlcompute.set_mlc_device(device_name='cpu')
>>> # Select GPU device.
>>> mlcompute.set_mlc_device(device_name='gpu')
WARNING:tensorflow:Eager mode on GPU is extremely slow. Consider to use CPU instead
```

GPU については Warning が出ていますが、ひとまず CPU と GPU の指定は可能だと考えて良さそうです。

`Eager mode on GPU is extremely slow` とのことで、 TensorFlow 2.0 で default になっている Eager mode (Define-by-Run) ではなく、TensorFlow 1系の Define-and-Run での実行が前提になっているのでしょうか。

改めてリポジトリの README.md を見ると `Please note that in eager mode, ML Compute will use the CPU.` と書かれていますので、上記では Warning と共に強制的に CPU に切り替わっている可能性があります。

"Mac-optimized TensorFlow and TensorFlow Addons" の真価を試すには、 Eager mode ではないコードを用意する必要がありそうです。

## まとめ

"Mac-optimized TensorFlow and TensorFlow Addons" (https://github.com/apple/tensorflow_macos) を MacBook Air with M1 で動かすことを目標として、
Intel プロセッサ の Mac から M1 搭載の Mac にデータ移行をした場合の Python 環境構築で引っ掛かった部分についてまとめてみました。

詰まりどころとしては、移行元の環境で作っていた Python の環境を消去、ないしは参照しないように設定し直す必要があるところでしょうか。
アーキテクチャが違うので、考えてみれば当たり前のことなのですが、移行元でせっかく作っていた環境をなるべく利用できないか……と考えてしまったこともあり、思いのほか手こずってしまいました。

Homebrew が現時点で ARM ベース のアーキテクチャに対応していないということもありましたが、 `We do not provide support for this (yet).` という書き方をしていることからしても、これについては将来的に解消されるものではないかと思います。
その他のソフトウェアについても M1 対応が進んでいくものと思われますので、Intel プロセッサ搭載 Mac から M1 搭載 Mac への移行はどんどん楽になっていくものと期待されます。
この記事については、あくまで2020年11月24日現在の状況での試みを記録したものであり、近いうちに（もしかしたら今日のうちにでも）、状況の変化する可能性があります。

そもそも `tensorflow_macos` もプレリリースのものですし、正式リリース版がどのようなものになるのかを楽しみにしたいと思います。

（以上）