Caffe CPU-only on macOS Sierra

# はじめに
Caffe を macOS Sierra 上でビルドしたときのメモ．


## Caffe とは
> Caffe is a deep learning framework made with expression, speed, and modularity in mind. It is developed by the Berkeley Vision and Learning Center (BVLC) and by community contributors.
引用元: http://caffe.berkeleyvision.org/

- C++ライブラリとして提供されている．
- インターフェイスはCommand Line, Python, Matlabが選べる．
- GPGPUへの対応がデフォルトで備わっている．

# 環境

## Mac

- MacBook Air (13-inch, Mid 2012)
- macOS Sierra Version 10.12.1

## Python

- [pyenv](https://github.com/yyuu/pyenv) 1.0.3
- Anaconda2-4.1.1 (Python2.7.12)

Python環境はpyenvを用いてインストールしたAnacondaの上で構築してある．pyenvが何かを公式リポジトリから引用すると：

> * Let you **change the global Python version** on a per-user basis.
* Provide support for **per-project Python versions**.
* Allow you to **override the Python version** with an environment
  variable.
* Search commands from **multiple versions of Python at a time**.
  This may be helpful to test across Python versions with [tox](https://pypi.python.org/pypi/tox).

というもの．ここではPython2.7の最新バージョンをインストール．
環境変数`$PYENV_ROOT`と`$PYENV_ROOT/shims`へのパスは設定済であることを想定．

## Caffe

- Master branch
- Commit: 473f143f9422e7fc66e9590da6b2a1bb88e50b2f
- Date: Nov 18 16:29:35 2016



# 構築
基本的な手順は公式ホームページ (http://caffe.berkeleyvision.org/install_osx.html) を参考にした．ただ和訳しただけの部分もある．
まず、適当なディレクトリに移動して公式リポジトリからCaffeのソースコードをクローンする．

```bash
$ git clone https://github.com/BVLC/caffe
```

クローンしたリポジトリを参照できるように環境変数`$CAFFE_ROOT`を設定する．自分では`$HOME/.bashrc`ファイルに次の書き込んだ．

```bash
export $CAFFE_ROOT="$HOME/caffe"
```

## 依存パッケージのインストール
Homebrewを使う．

```bash
$ brew update
$ brew install -v snappy leveldb gflags glog szip lmdb 
$ brew tap homebrew/science
$ brew install hdf5 opencv openblas
```

hdf5、opencv、openblasはR言語などがあるhomebrew-sienceリポジトリからインストールする．必要があれば、`tap`をしてHomebrewにformulaeを追加する．
公式サイトでは、Anacondaを利用した場合には次のような変更が必要だと書いてある．まず`brew edit opencv`を行い、下記に**似ている**箇所を**完全一致**するようにする．

```
-DPYTHON_LIBRARY=#{py_prefix}/lib/libpython2.7.dylib
-DPYTHON_INCLUDE_DIR=#{py_prefix}/include/python2.7
```

ここからのパッケージのインストールは、CaffeをPythonインターフェイスで利用しない場合と利用する場合で異なる．

#### Caffe without Python

```bash
$ brew install protobuf boost
```

#### Caffe with Python

```bash
$ brew install --build-from-source --with-python -v protobuf
$ brew install --build-from-source -v boost boost-python
```

boostのインストールではかなり時間がかかった(25分ほど)．

pyenvを利用するとシステム(`/usr/local/bin/`などにあるもの)のPythonを用いないので、そのままではHomebrewでインストールしたPythonモジュールが利用できない．そこでpyenvのAnacondaでも同じPythonモジュールを利用できるようする．色々と方法はあると思うが、ここでは単純に最新のモジュールをパッケージ管理システム(conda、pip、etc.)を使ってインストールする．
まずcondaやpipそのものをアップデートする．

```bash
$ conda update anaconda
$ conda update conda
$ pip install --upgrade pip
```

つづいて必要なモジュールをインストールする．自分の環境ではhdf5とprotobufが必要だった(一度失敗してやり直した)．condaだとCaffeに必要なprotobuf3.0がインストールできず、pipだとHomebrewでインストールした最新のhdf5とバージョンが一致しないため別々に入れる．

```bash
$ conda install hdf5
$ pip install protobuf
```

またこれとは別にPythonモジュールをインストールする必要がある．`$CAFFE_ROOT/python/requirements.txt`に必要なモジュールとそのバージョンが記述してあるので、そのファイルに従ってインストールする．


最後に環境変数`$PYTHONPATH`に`$CAFFE_ROOT/python`に加える．これによってPythonインタプリタ内で`import caffe`が使えるようになる．

```bash
export PYTHONPATH="$CAFFE_ROOT/python:$PYTHONPATH"
```

ここまでがビルドの準備である．

## Caffeのビルドの準備

まず`$CAFFE_ROOT`に移動すると、Makefileの設定の雛形である`Makefile.config.example`があるので、これを同じ場所にコピーして新しく`Makefile.config`を作成．

```bash
$ cp Makefile.config.example Makefile.config
```

好きなエディタでこの`Makefile.config`を自分の環境用に書き換える．ここミスすると当然ビルドできないので、しっかりファイルを読みつつ変更する．自分の環境では、

```Makefile:Makefile.config.example
# ...

# CPU-only switch (uncomment to build without GPU support).
# CPU_ONLY := 1

# ...

# BLAS choice:
# atlas for ATLAS (default)
# mkl for MKL
# open for OpenBlas
BLAS := atlas
# Custom (MKL/ATLAS/OpenBLAS) include and lib directories.
# Leave commented to accept the defaults for your choice of BLAS
# (which should work)!
# BLAS_INCLUDE := /path/to/your/blas
# BLAS_LIB := /path/to/your/blas

# Homebrew puts openblas in a directory that is not on the standard search path
# BLAS_INCLUDE := $(shell brew --prefix openblas)/include
# BLAS_LIB := $(shell brew --prefix openblas)/lib

# ...

# NOTE: this is required only if you will compile the python interface.
# We need to be able to find Python.h and numpy/arrayobject.h.
PYTHON_INCLUDE := /usr/include/python2.7 \
		/usr/lib/python2.7/dist-packages/numpy/core/include
# Anaconda Python distribution is quite popular. Include path:
# Verify anaconda location, sometimes it's in root.
# ANACONDA_HOME := $(HOME)/anaconda
# PYTHON_INCLUDE := $(ANACONDA_HOME)/include \
		# $(ANACONDA_HOME)/include/python2.7 \
		# $(ANACONDA_HOME)/lib/python2.7/site-packages/numpy/core/include \

# ...

# We need to be able to find libpythonX.X.so or .dylib.
PYTHON_LIB := /usr/lib
# PYTHON_LIB := $(ANACONDA_HOME)/lib

# ...

# Uncomment to support layers written in Python (will link against Python libs)
# WITH_PYTHON_LAYER := 1

# ...
```

これを、以下のように変更した．

```Makefile:Makefile.config
# ...

# CPU-only switch (uncomment to build without GPU support).
CPU_ONLY := 1

# ...

# BLAS choice:
# atlas for ATLAS (default)
# mkl for MKL
# open for OpenBlas
# BLAS := atlas
# Custom (MKL/ATLAS/OpenBLAS) include and lib directories.
# Leave commented to accept the defaults for your choice of BLAS
# (which should work)!
# BLAS_INCLUDE := /path/to/your/blas
# BLAS_LIB := /path/to/your/blas

# Homebrew puts openblas in a directory that is not on the standard search path
BLAS_INCLUDE := $(shell brew --prefix openblas)/include
BLAS_LIB := $(shell brew --prefix openblas)/lib

# ...

# NOTE: this is required only if you will compile the python interface.
# We need to be able to find Python.h and numpy/arrayobject.h.
# PYTHON_INCLUDE := /usr/include/python2.7 \
#		/usr/lib/python2.7/dist-packages/numpy/core/include
# Anaconda Python distribution is quite popular. Include path:
# Verify anaconda location, sometimes it's in root.
ANACONDA_HOME := $(PYENV_ROOT)/versions/anaconda2-4.1.1
PYTHON_INCLUDE := $(ANACONDA_HOME)/include \
	$(ANACONDA_HOME)/include/python2.7 \
	$(ANACONDA_HOME)/lib/python2.7/site-packages/numpy/core/include \

# ...

# We need to be able to find libpythonX.X.so or .dylib.
# PYTHON_LIB := /usr/lib
PYTHON_LIB := $(ANACONDA_HOME)/lib

# ...

# Uncomment to support layers written in Python (will link against Python libs)
WITH_PYTHON_LAYER := 1

# ...
```


## Caffe のビルド
ここまでの準備ができたら、ビルドを行う．`-j`オプションによって並列数を指定できる．CPUコア数と同じにすると良い．

```
$ make all -j4
$ make test -j4
$ make runtest
```
ここで次にような警告が出るが、問題ない(らしい)のでとりあえず放置して大丈夫．

```
warning: unused typedef 'INVALID_REQUESTED_LOG_SEVERITY' [-Wunused-local-typedef]

clang: warning: argument unused during compilation: '-pthread'
```

すべて実行してエラーが出ず、`make runtest`で

```
...

[----------] Global test environment tear-down
[==========] 1098 tests from 150 test cases ran. (67696 ms total)
[  PASSED  ] 1098 tests.
```
のようにすべてのテストをパスすればビルドは成功．

Caffe with Pythonのビルドは簡単で

```bash
$ make pytest
$ make pycaffe
```

と実行し、エラーが出なけれ完了である．
あとはCaffeの公式ホームページにあるチュートリアルなどを実際に試し、成功したら、自分の作りたいモデルで学習できるはず．


## 手こずった箇所

Caffe with Pythonのインストールにおいて`make pytest`を、もしくは`make pytest`を実行せずに`make pycaffe`を実行した後に`import caffe`を、試すとエラーが出てPythonインタプリタがクラッシュしてしまった．

```bash
>>> 
>>> import caffe
python(88481,0x7fffcb73d3c0) malloc: *** malloc_zone_unregister() failed for 0x7fffcb733000
/Users/name/.pyenv/versions/anaconda2-4.1.1/lib/python2.7/site-packages/matplotlib/font_manager.py:273: UserWarning: Matplotlib is building the font cache using fc-list. This may take a moment.
  warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')
Illegal instruction: 4
```

どうやらmacOS Sierraで生じるものらしく公式リポジトリのイシュー[malloc error on macOS Sierra 10.12 #4783](https://github.com/BVLC/caffe/issues/4783)にも同じものがある．ひとまず、Pythonで使うときは、

```Python
import matplotlib.pyplot
import caffe
```

といったように`matplotlib.pyplot`をインポートしてから実行すればクラッシュすることはなくなった．しかし根本的な解決にはなっていない様子．macOS SierraとLevelDBの互換性の問題らしいが、原因がわからず(そんなに頑張って調べてはいない)気持ちが悪い．

#さいごに
Caffe CPU-onlyをmacOS Sierra上でインストールしたが、C++で使う、もしくはGoogle Proto BufferでDNNモデルを定義するぐらいなら問題にはならないので、インストールして学習してみてはいかがだろうか．