M1 Mac にnumpy, matplotlibなどが入らない問題の解消法


はじめに

M1 Macbook Proが登場して意気揚々として購入したものの,普段多用しているnumpyやmatplotlibをpipでインストールしようとしたら入らなかったので,その解決法(暫定)を記しておきます.(02/19/2020)
時間が経てばライブラリ側からこの問題は解消してくれるはずなので,その時に,今まで自分が何をいじったのかが思い出せるように,ここに記しておきたいと思います.

numpy build fail in M1 Big sur 11.1

解消法

A. condaをインストールしてその上に入れる

今回こちらの方法は使いませんが一応紹介します.
かくいう僕も以前まではAnacondaをよく使用していましたが,ライブラリがcondaで入らずに泣く泣くpipを使うたびに下の記事が頭に浮かんで心を痛めますし,何よりGitHubでソースコードを公開する場合はrequirements.txtをつけて pip install を促す場合が多いわけですから,Pythonのパッケージ管理はPython公式が推奨するpipに限定する方がいいと思います(この辺りのベストプラクティスは人それぞれだと思いますが)

ただ,condaの使用を許すのであれば以下の方法が一番簡単であるかと思います.MiniforgeというArm版に対応したcondaのインストーラーを使ってcondaをインストールし,その上にnumpyやmatplotlibをインストールします.詳しくは以下のサイトに譲ります.

B. tensorflow-macosを入れる

tensorflow-macosというMacに最適化されたTensorFlowをインストールすれば,numpyは自動的にインストールすることもできるようです.ただ,これは本来の目的とは異なりますし,如何せんmatplotlibがインストールできない問題は解決しません.

C. Python3.9をインストールする ← これが本命

冒頭で説明したスレッドの回答に以下のようにあります.

First of all numpy support is not there for m1 arm based Macs using python 3.8 version. The method to workaround with this is to update your python version to 3.9. This is the latest version of python and can be a bit unstable. If you want to work with tensorflow then do not update to python 3.9 version as they don't support it and support will come up with version 2.5 (estimated). So, if you don't want to use tensorflow and want to use numpy by using pip install then go ahead install python3.9 and make it your default python interpreter.

I will recommend you to use Homebrew for installation of python 3.9 as it as easy as running a simple command.

現在のところ,Python3.9用のnumpyやmatplotlibはarmアーキテクチャをサポートしてそうなので,Python3.9をインストールしてその上に載せる形で実現しようと思います.

デフォルトで入っているPython
$ python --version # /usr/bin/python
Python 2.7.16
$ python3 --version # /usr/bin/python3
Python 3.8.2

更地の環境は汚したくないので,pyenvを利用してバージョン管理をします(Pythonのバージョン管理とパッケージ管理のベストプラクティスは何なんでしょうね.この記事に説得力があるので個人的には pyenv+venv を採用しています.)

Homebrewのインストール

pyenvはHomebrew経由で入れますが,買ったばかりのMacにはHomebrewが入っていないのでインストールします.
去年の年末までは,Homebrewがまだ対応しきっておらず,arm64とx86_64でバイナリが分かれてしまうことから,下の記事にあるようにシェルのアーキテクチャを切り替えて使用する必要がありました.

しかし,2021年2月現在では公式ページにあるように既にHomebrewのインストーラーはM1 Macをサポートしてくれたので,下記のコマンド一発で入ります(ちなみに今やMacのデフォルトシェルはzshですが,このコマンドの'bin/bash'は気にしなくていいです).

$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

ちなみに,コマンドラインの最後にHomebrewのパスが通っていないと忠告が出ますが,ご丁寧に~/.zprofileに追記すべき文まで書いてくれているので,この指示に従ってパスを通します("YOUR HOME DIRECTORY PATH"はそれぞれ異なります)

command_output
Warning: /opt/homebrew/bin is not in your PATH.
- Add Homebrew to your PATH in (YOUR HOME DIRECTORY PATH)/.zprofiles:
    echo 'eval $(/opt/homebrew/bin/brew shellenv)' >> (YOUR HOME DIRECTORY PATH)/.zprofile
    eval $(/opt/homebrew/bin/brew shellenv)
$ echo 'eval $(/opt/homebrew/bin/brew shellenv)' >> ~/.zprofile
$ source ~/.zprofile
$ brew --version

これでHomebrewが使えるようになりました.

pyenvのインストール

pyenvの仕組みやインストール方法は公式ページに載っているので,この通りにインストールします.
3行目はpyenvの自動起動のコマンドです.もし今後何かのソフトウェアと競合する場合は ~/.zshrc からこの文をコメントアウトすればいいでしょう.

$ brew update
$ brew install pyenv
$ echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n  eval "$(pyenv init -)"\nfi' >> ~/.zshrc
$ exec "$SHELL" # シェルの再起動
$ pyenv --version

Python3.9のインストール

さあ,いよいよPython3.9をインストールします.
その前に以下のパッケージのインストールが推奨されているので,従っておきます.

# optional, but recommended:
$ brew install openssl readline sqlite3 xz zlib

pyenv install --listでインストールできるPythonのバージョンを確認できるので,インストールできるPython3.9系を確認しておきます(minicondaもインストールできるんですね,面白い)

$ pyenv install --list | grep 3.9
 3.9.0
 3.9-dev
 3.9.1
 miniconda-3.9.1
 miniconda3-3.9.1
$ pyenv install 3.9.1 # 3.9.1をインストール
$ pyenv rehash # 新たなバージョンをインストールしたあとはこれをしないとshimsコマンドが再構成されない 

適当なフォルダ内で試してみます.

$ pyenv local 3.9.1
$ python version
3.9.1 (set by (CURRENT DIR)/.python-version
$ python -m venv .venv
$ source .venv/bin/activate
$ pip instal numpy matplotlib pandas scikit-learn

が,matplotlibだけが入らない... 結局matplotlibはPython3.9でもサポートされていないじゃないか...

根本の問題は

numpy

そうこうしている内に,numpyはM1 Macでもサポートされたようです.よって,Python3.8でもPython3.9でもpipでインストールできます.

matplotlib

とあるように,Homebrewでlibjpegというパッケージをインストールする必要があったようです.こうすると,Python3.9系でもPython3.8系でもmatplotlibが入るようになります.

$ brew install libjpeg

pyenv導入しなくてもよかったですね.ただいずれにせよHomebrewは必要ですし,pyenvの+venvはベストプラクティスな気がするので,上記のステップも無駄ではないでしょう.

pandas, scikit-learn

pandasやscikit-learnが入らないのは,インストーラがarm64というアーキテクチャ上では動かないからだそうです.よって上の記事にあるようにiTermを開く前に"Open Using Rosetta"にチェックを入れてから起動するようにし(もちろん一度iTermはquitしてから再起動する),pipでインストールすると入るようになります.(M1 Macbook Pro 2020では,arm64とx86_86を切り替えて使えるようになっています.)

もしくはzsh上では以下のコマンドで変更できるようです(@hiro-551010 さん,コメントありがとうございました)

$ arch -arm64 zsh # arm64に変更
$ arch -x86_64 zsh # x86_64に変更

ちなみに現在のシェルがどのアーキテクチャを使っているかはuname -mというコマンドで確認できます.

$ uname -m # Open Using Rosetta にチェックを入れずに起動した場合
arm64
$ uname -m # Open Using Rosetta にチェックを入れて起動した場合
x86_64

しかし,x86_64上でインストールしたpandasやscikit-learnは,もちろんx86_64上で実行しなくてはなりません.アーキテクチャをx86_64にした状態でインストールした後,arm64に戻してシェルを起動すると,その状態では使えないということです.

(参考) ARMとx86/x64(Intel, AMD)のCPU、アーキテクチャの違い、シェア、性能比較、アーキテクチャ、エンディアン

追記(4/14/2021)

この回答によると,pandasは以下のようにソースコードからインストールすると入るらしいです.(若干のWarningが出ましたが基本的な機能は入りました.)

$ pip install numpy cython
$ git clone https://github.com/pandas-dev/pandas.git
$ cd pandas
$ python3 setup.py install

numpy, matplotlib, pandas, scikit-learnを全て同時に使いたいときは

pandas, scikit-learnはx86_64上でしかインストールできませんし,実行できません.対してnumpy, matplotlibはarm64上でもx86_64上でも動きます.よってこれらを同時に使いたい場合は,インストール時も実行時もアーキテクチャをx86_64に統一しましょう.
現在のところはプロジェクトやディレクトリ単位で,どちらのアーキテクチャ上で行うかを逐一自分で覚えておきながら実行するということになりそうです.

まとめ

とりあえずは上記の解決方法で,それぞれのライブラリをimportすることはできそうです.numpyのインストーラは仕様変更してくれたみたいですし,そのうちにあらゆるライブラリがM1チップの上でシームレスに動くようになってくれるでしょう.その時を早く待ちたいものです.

余談: 問題3.8系がpyenvで入らない

pyenvで色々なバージョンのpythonを入れようとしたとき,3.9.1は入ったのだが,3.8系が入らないという問題があったが,この方法を用いたら入った

$ CFLAGS="-I$(brew --prefix openssl)/include -I$(brew --prefix bzip2)/include -I$(brew --prefix readline)/include -I$(xcrun --show-sdk-path)/usr/include" LDFLAGS="-L$(brew --prefix openssl)/lib -L$(brew --prefix readline)/lib -L$(brew --prefix zlib)/lib -L$(brew --prefix bzip2)/lib" pyenv install --patch 3.8.6 <<(curl -sSL https://raw.githubusercontent.com/Homebrew/formula-patches/113aa84/python/3.8.3.patch\?full_index\=1)