Chainer 以外

ずいぶん下書きで眠っていたので、書いている途中ですが公開します。


Chainer が流行っていますので、それ以外についてまとめます。
個人的には、Python使いであれば Keras、Java/Scala な方は DeepLearning4J が使いやすいと思います。


# Keras

[Keras](http://keras.io/) は Theano ベースのライブラリです。
あまりメジャーではありませんが、活発に開発が進められており、設計がシンプルで、かなり自由度が高く、また非常に使いやすいため、とてもおすすめです。

## Keras のインストール

pip でインストールすることが出来ます。

```bash
$ pip install keras 
```

最新版を使いたい場合は以下のようにします。

```bash
$ pip install git+https://github.com/fchollet/keras.git
```

Keras は Theano を通じて GPU を利用することも出来ます。
Theano で GPU を使う設定となっている場合は、特別な設定なしに GPU を利用できます。

## Keras で MNIST

Keras では、モデルに対応する `keras.models.Sequential` にレイヤを追加していくことでネットワークを構築します。以下は、いわゆるMLPで MNIST データを10クラス分類する例ですが、非常に簡単であることがわかると思います。

```py
from sklearn import datasets, preprocessing
from keras import models, layers
import numpy as np

train_size = 60000
dropout = 0.2
batch_size = 128
nb_epoch = 100

# データセットの準備
mnist = datasets.fetch_mldata('MNIST original')
x = mnist.data.astype(float)
x /= 255 # [0,1] に正規化
y = preprocessing.LabelBinarizer().fit_transform(mnist.target) # 0 -1 変数に変換する

# 訓練用とテスト要に分割
x_train, x_test = np.split(x, [train_size])
y_train, y_test = np.split(y, [train_size])

# ネットワーク構築
model = models.Sequential()
model.add(layers.core.Dense(28*28, 128, activation='relu'))
model.add(layers.core.Dropout(dropout))
model.add(layers.core.Dense(128, 128, activation='relu'))
model.add(layers.core.Dropout(dropout))
model.add(layers.core.Dense(128, 10, activation='softmax'))

# モデルをコンパイル
model.compile(loss='categorical_crossentropy', optimizer='adam')

# 学習
model.fit(x_train, y_train, batch_size=batch_size, nb_epoch=nb_epoch, show_accuracy=True, validation_data=[x_test, y_test])

# 予測
model.predict(x_test)
``` 

# Caffe

[Caffe](http://caffe.berkeleyvision.org/) は言わずと知れた深層学習ライブラリです。C++で記述されており、非常に高速です。ただし、ネットワーク記述等は専用の設定ファイルで行うため、慣れるのに時間がかかりますし、基本的に Convolutional Network のためのライブラリのため、その他の用途に使うには工夫が必要です。
ただし、最近は NVIDIA から DIGITS というラッパが公開されており、GUIの操作で Caffe を操作することができるようになっているため、限定的な用途ならば使いやすいといっても良いかと思います。

## Caffe のインストール

Caffe は依存ライブラリが多く、インストールも若干厄介です。ここでは OSX でのCPUモードでのインストール方法を紹介します。(GPUモードでのインストールについては若干複雑なため、別の機会に紹介します)

### 依存ライブラリのインストール

homebrew を用いて、依存ライブラリをインストールします。

```bash
$ brew tap homebrew/science
$ brew install protobuf glog gflags
$ brew install hdf5 leveldb snappy lmdb
$ brew install boost
$ brew install opencv
``` 

### Caffe ソースコードの取得

github よりソースコードをダウンロードします。

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

### コアライブラリのビルド

Caffe をビルドするには、 Makefile.config が必要です。このファイルは、 Makefile.config.example を適宜修正して作成します。

```bash
$ cd caffe
$ cp Makefile.config{.example,}
$ vi Makefile.config
```

Makefile.config の CPU_ONLY のコメントアウトを外します。

```
# CPU-only switch (uncomment to build without GPU support).
CPU_ONLY := 1 # コメントアウトを外す
```

後は make すれば、ひとまず動きます。

```
$ make
```

Makefile.config では、 blas や gpu 利用の有無など、様々な設定ができますが、まずはここで示したようなシンプルな設定でコンパイルが通ることを確認し、必要に応じて徐々に設定を変更していくのがよいでしょう。例えば標準の atlas ではなく高速な openblas を使うには、

```bash
$ brew install openblas
```

の後、Makefile.config の BLAS を open に、 BLAS_INCLUDE、 BLAS_LIB のコメントアウトを削除します。

```
# BLAS choice:
# atlas for ATLAS (default)
# mkl for MKL
# open for OpenBlas
# BLAS := atlas
BLAS := open
# 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
```

最後に make しなおせば OK です。

```bash
$ make clean
$ make
```

### pycaffe のビルド

caffe を python から利用するには、 pycaffe のビルドが必要です。
pycaffe にはboost-python が必要ですが、単純に HomeBrew でインストールすると、以下のようなエラーがでてしまいます。

```
caffe Fatal Python error: PyThreadState_Get: no current thread
``` 

このエラーを解決するには、boost-python をソースからビルドします。

```bash
$ brew --build-from-source --fresh -vd boost-python
```

その他の依存ライブラリもインストールします。

```bash
$ cd python
$ pip install -r requirements.txt
```

Makefile.config は PYTHON_INCLUDE と PYTHON_LIB の修正が必要です。
それぞれ、環境に合わせて設定してください。私の場合は、以下のようにPythonを実行してパスを取得するようにしていますが、パスをベタ書きしてしまったほうが安全かもしれません。

```
...
PYTHON_INCLUDE := $(dir $(shell python -c 'import os; print(os.__file__).replace("lib", "include")'))
PYTHON_INCLUDE += $(dir $(shell python -c 'import numpy.core; print(numpy.core.__file__)'))/include
...
PYTHON_LIB := $(dir $(dir $(shell python -c 'import os; print(os.__file__)')))
```

pyenv や virtualenv を利用していてエラーが出る場合は、 libboost_python.dylib や _caffe.so が適切なPython共有ライブラリ (libpythonx.x.dylib) を見ているか確認して下さい。確認には otool が使えます。

```bash
$ otool -L /usr/local/Cellar/boost-python/1.57.0/lib/libboost_python.dylib
/usr/local/Cellar/boost-python/1.57.0/lib/libboost_python.dylib:
	/usr/local/lib/libboost_python.dylib (compatibility version 0.0.0, current version 0.0.0)
	/Users/xxx/.pyenv/versions/2.7.9/lib/libpython2.7.dylib (compatibility version 2.7.0, current version 2.7.0)
	/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
```

## Caffe で MNIST

### MNISTデータのダンロード・変換

Caffe には、MNIST をダウンロードし、Caffeの取り扱える形式に変換するスクリプトが含まれていますので、今回はそちらを利用します。
以下のコマンドは caffe ディレクトリ直下で実行します。

```bash
$ data/mnist/get_mnist.sh
$ examples/mnist/create_mnist.sh
```
### 設定ファイル作成

ネットワークの設定や、学習方法の設定は以下のように設定します。内容は何となく分かるかと思いますが、きちんとした理解には[オフィシャルのチュートリアル](http://caffe.berkeleyvision.org/tutorial/)や[東邦大学・山内先生のページ](http://pepper.is.sci.toho-u.ac.jp/index.php?%A5%CE%A1%BC%A5%C8%2FCaffe%A4%CE%A5%E2%A5%B8%A5%E5%A1%BC%A5%EB%B5%AD%BD%D2%C8%B4%BF%E8%A1%CAcaffe.prototxt%A1%CB) が役に立ちます。

注意しないといけないのは、ネットワークの設定ファイルの weight_filter です。ここでネットワークの重みの初期化方法を設定しているのですが、こちらをきちんと指定しないと収束しません。

mnist_solver.prototxt (学習方法の設定)

```
net: "mnist.prototxt" # ネットワークの設定ファイル
test_iter: 100 # テストでの繰り返し(batch)回数
test_interval: 1000 # テストの間隔
test_compute_loss: true
base_lr: 0.01 # 学習率の初期値
lr_policy: "inv" # 学習率を小さくするポリシー
display: 100 # 画面に情報を表示する間隔
max_iter: 65000 # 繰り返し(batch)回数
solver_type: 2 # 最適化手法 (2: AdaGrad)
solver_mode: CPU # CPU もしくは GPU
```

mnist.prototxt (学習時のネットワーク設定)

```
name: "MNIST"
layer {
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    scale: 0.00390625
  }
  data_param {
    source: "examples/mnist/mnist_train_lmdb"
    batch_size: 128 
    backend: LMDB
  }
}
layer {
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
  transform_param {
    scale: 0.00390625
  }
  data_param {
    source: "examples/mnist/mnist_test_lmdb"
    batch_size: 128
    backend: LMDB
  }
}
layer {
  name: "ip1"
  type: "InnerProduct"
  bottom: "data"
  top: "ip1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 128 
    weight_filler {
      type: "xavier"
    }
  }
}
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "ip1"
  top: "ip1"
}
layer {
  name: "drop1"
  type: "Dropout"
  bottom: "ip1"
  top: "ip1"
  dropout_param {
    dropout_ratio: 0.25
  }
}
layer {
  name: "ip2"
  type: "InnerProduct"
  bottom: "ip1"
  top: "ip2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 128
    weight_filler {
      type: "xavier"
    }
  }
}
layer {
  name: "relu2"
  type: "ReLU"
  bottom: "ip2"
  top: "ip2"
}
layer {
  name: "drop2"
  type: "Dropout"
  bottom: "ip2"
  top: "ip2"
  dropout_param {
    dropout_ratio: 0.25
  }
} 
layer {
  name: "ip3"
  type: "InnerProduct"
  bottom: "ip2"
  top: "ip3"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 10
    weight_filler {
      type: "xavier"
    }
  }
}
layer {
  name: "accuracy"
  type: "Accuracy"
  bottom: "ip3"
  bottom: "label"
  top: "accuracy"
  include {
    phase: TEST
  }
}
layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "ip3"
  bottom: "label"
  top: "loss"
}
```

mnist_deploy.prototxt (予測時のネットワーク設定)
基本的に mnisit.prototxt と同じですが、 Data レイヤーがなくなって、代わりに input を指定しています。また、出力も accuracy がなくなり、 SoftmaxWithLoss が Softmax にかわっています。

```
name: "MNIST"
input: "data"
input_dim: 10 # バッチサイズ
input_dim: 1 # 画像チャネル数(モノクロなので1)
input_dim: 28 # 画像サイズ(y)
input_dim: 28 # 画像サイズ(x)
layer {
  name: "ip1"
  type: "InnerProduct"
  bottom: "data"
  top: "ip1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 128 
    weight_filler {
      type: "xavier"
    }
  }
}
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "ip1"
  top: "ip1"
}
layer {
  name: "drop1"
  type: "Dropout"
  bottom: "ip1"
  top: "ip1"
  dropout_param {
    dropout_ratio: 0.25
  }
}
layer {
  name: "ip2"
  type: "InnerProduct"
  bottom: "ip1"
  top: "ip2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 128
    weight_filler {
      type: "xavier"
    }
  }
}
layer {
  name: "relu2"
  type: "ReLU"
  bottom: "ip2"
  top: "ip2"
}
layer {
  name: "drop2"
  type: "Dropout"
  bottom: "ip2"
  top: "ip2"
  dropout_param {
    dropout_ratio: 0.25
  }
} 
layer {
  name: "ip3"
  type: "InnerProduct"
  bottom: "ip2"
  top: "ip3"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 10
    weight_filler {
      type: "xavier"
    }
  }
}
layer {
  name: "prob"
  type: "Softmax"
  bottom: "ip3"
  top: "prob"
}
```


### 学習

caffe コマンドを使って学習します。caffeコマンドは build/tools/ 以下にあります。

```bash
$ build/tools/caffe train -solver mnist_solver.prototxt
```

学習結果は、デフォルトではカレントディレクトリに *.caffemodel というファイル名で保存されます。

```bash
$ ls *.caffemodel
_iter_65000.caffemodel
```

### 予測

ここでは pycaffe を使って予測します。

```py
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import struct

import caffe
import numpy as np

MNIST_DATA_PATH = 'data/mnist/t10k-images-idx3-ubyte'
PROTO_PATH = 'mnist_deploy.prototxt'
MODEL_PATH = '_iter_65000.caffemodel'


def load_image(fp):
    header = fp.read(16)
    contents = fp.read()
    magic, n_images, row, col = struct.unpack('>4i', header)
    assert(magic == 2051)
    images = np.array(
        struct.unpack('>%dB' % len(contents), contents),
        dtype=np.float64).reshape(n_images, row, col, 1)
    return images/256 # 値が [0, 1] となるよう変換


with open(MNIST_DATA_PATH) as i_:
    images = load_image(i_)

net = caffe.Classifier(
    PROTO_PATH,
    MODEL_PATH,
    image_dims=images.shape[1:]
)

net.predict(images)
```

ふうっ、といった感じですね。


# DeepLearning4J

DeepLearning4J は Skymind社が提供する、 Java製の深層学習ライブラリです。mavenリポジトリにも登録されていて環境構築は非常に簡単です。

## DeepLearning4J のインストール

maven が入っていることを前提とします。まずはプロジェクトを作成しましょう。

```bash
$ mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart
```

途中で groupId や artifactId などを聞かれるので、適当に答えます。(例: groupId: mygroup artifactId: dl4jtest)
maven コマンドが終了すると、プロジェクトディレクトリができるているので、そちらに移動し、 pom.xml を編集します。

```bash
$ cd dl4jtest
$ vi pom.xml
```

```xml

```

# Mocha

[Mocha](https://github.com/pluskid/Mocha.jl) は高速な科学計算言語 [Julia](http://julialang.org/) の DeepLearning 用パッケージです。
Julia がインストールされていれば、Mocha のインストールは簡単で、

```jl
Pkg.add("Mocha")
```

とするだけです。最新版を使いたい場合は

```jl
Pkg.clone("https://github.com/pluskid/Mocha.jl.git")
```

とします。

### 学習

```jl
use_gpu = true

if use_gpu
  ENV["MOCHA_USE_CUDA"] = "true"
else
  ENV["MOCHA_USE_NATIVE_EXT"] = "true"
  ENV["OMP_NUM_THREADS"] = 1
  blas_set_num_threads(1)
end

using Mocha
srand(12345678)

data_layer  = AsyncHDF5DataLayer(name="train-data", source="data/train.txt", batch_size=64, shuffle=true)
conv_layer  = ConvolutionLayer(name="conv1", n_filter=20, kernel=(5,5), bottoms=[:data], tops=[:conv])
pool_layer  = PoolingLayer(name="pool1", kernel=(2,2), stride=(2,2), bottoms=[:conv], tops=[:pool])
conv2_layer = ConvolutionLayer(name="conv2", n_filter=50, kernel=(5,5), bottoms=[:pool], tops=[:conv2])
pool2_layer = PoolingLayer(name="pool2", kernel=(2,2), stride=(2,2), bottoms=[:conv2], tops=[:pool2])
fc1_layer   = InnerProductLayer(name="ip1", output_dim=500, neuron=Neurons.ReLU(), bottoms=[:pool2], tops=[:ip1])
fc2_layer   = InnerProductLayer(name="ip2", output_dim=10, bottoms=[:ip1], tops=[:ip2])
loss_layer  = SoftmaxLossLayer(name="loss", bottoms=[:ip2,:label])

backend = use_gpu ? GPUBackend() : CPUBackend()
init(backend)

common_layers = [conv_layer, pool_layer, conv2_layer, pool2_layer, fc1_layer, fc2_layer]
net = Net("MNIST-train", backend, [data_layer, common_layers..., loss_layer])

exp_dir = "snapshots$(use_gpu ? "-gpu" : "-cpu")"

params = SolverParameters(max_iter=10000, regu_coef=0.0005,
    mom_policy=MomPolicy.Fixed(0.9),
    lr_policy=LRPolicy.Inv(0.01, 0.0001, 0.75),
    load_from=exp_dir)
solver = SGD(params)

setup_coffee_lounge(solver, save_into="$exp_dir/statistics.jld", every_n_iter=1000)

# report training progress every 100 iterations
add_coffee_break(solver, TrainingSummary(), every_n_iter=100)

# save snapshots every 5000 iterations
add_coffee_break(solver, Snapshot(exp_dir), every_n_iter=5000)

# show performance on test data every 1000 iterations
data_layer_test = HDF5DataLayer(name="test-data", source="data/test.txt", batch_size=100)
acc_layer = AccuracyLayer(name="test-accuracy", bottoms=[:ip2, :label])
test_net = Net("MNIST-test", backend, [data_layer_test, common_layers..., acc_layer])
add_coffee_break(solver, ValidationPerformance(test_net), every_n_iter=1000)

solve(solver, net)

#Profile.init(int(1e8), 0.001)
#@profile solve(solver, net)
#open("profile.txt", "w") do out
#  Profile.print(out)
#end

destroy(net)
destroy(test_net)
shutdown(backend)
```