情弱だけどディープラーニングフレームワークCaffeを使ってみたい


# TL;DR←

下記の２記事を読めば終わり。

## EC2でCUDAを使いたい
http://tleyden.github.io/blog/2014/10/25/cuda-6-dot-5-on-aws-gpu-instance-running-ubuntu-14-dot-04/

## GPUインスタンス上でDocker使ってCaffeしたい
http://tleyden.github.io/blog/2014/10/25/running-caffe-on-aws-gpu-instance-via-docker/

# はじめに


どうも、情弱太郎です。
最近、ディープラーニングという言葉は持て囃されていますね。
昔のビックデータのそれを思い出しました。
その辺のオジサン（sucks）が部下に『ほら、最近パソコンがネコ分かるようになった、あー、あれ、ドープラーニングだっけ？アレでなんかできないの？』って言われたときに対応できるようにしておきたくなったので、ディープラーニングしてみることにしました。

ディープラーニング、いろいろ実装があるみたいです。

最近だと
https://github.com/NervanaSystems/neon
これも良さげですが、使い方が全く分からないです。

なんだかんだでCaffeってのがドキュメントちゃんとしてる風なので、Caffeをやってみることにしました。

http://caffe.berkeleyvision.org/

# CPUで使ってみたい

いきなりGPUでやりたいのはやまやまなんですが事前準備が面倒そうで心がほぼ折れたので簡単そうなCPUから手を付けました。

環境はMac。
この手のソフトは手作りでインストールすると、なんか動かない（情弱力を発揮）のでDockerでCaffeのイメージを使うようにします。
Macなので、boot2docker等の必要なモノは導入済みとします。

## イメージをとってくる

```
docker pull tleyden5iwx/caffe-cpu-master
```

しばらくボーッとする。

## コンテナを起動する

```
docker run -it tleyden5iwx/caffe-cpu-master bin/bash
```

はい、Caffe使えるようになった。
やったね。

# GPUで使ってみたい

環境はMacで。たぶんGPUの関係で使えないはず。
MacProなら自分でビデオカード差し替えればできるんじゃないかな。しらね。

という感じなので、EC2のGPU搭載インスタンスを使いましょう。

## AMIを作る

CaffeをGPUモードで動かすためにはCUDAが必要になります。
GPU用のEC2インスタンスにCUDAをインストールする必要がある、というわけです。

なんか面倒くさそうだし、ハードウェアが絡むとハマりそうですよね（情弱力を発揮）。

なので、CUDAがインストール済みのGPUインスタンスAMIを探すわけです。

http://tleyden.github.io/blog/2014/10/25/cuda-6-dot-5-on-aws-gpu-instance-running-ubuntu-14-dot-04/

あるわけです。
すげー。

このAMIを土台にしてCaffe環境を整えれば良さそうですね。
で、自分でCaffe環境を整えるのは面倒なのでDockerに任せます。

https://registry.hub.docker.com/u/tleyden5iwx/caffe-gpu-master/

あるわけです。
すげー。

さて、CUDAなAMIにはDockerがインストールされていません。

CUDAなAMIのインスタンスを立ててからDockerをインストールする感じでしょうか。
GPUなインスタンスをずっと立てっぱなしにできる富豪ならそれ良いです。

しかし、私は清貧です。
GPUなCaffeを使い終わったらすぐにインスタンスを捨てたいわけです。

なので、CUDAなAMIを継承したCUDA+DockerなAMIを作りましょう。

色々作り方はあると思います。
私は仕事ができないくせに言うことは一人前の意識高い系なので、無駄にPackerというヤツを使ってAMIを作りました。

### PackerでCUDA+DockerなAMIを作ろう

Packerインストールします。

```sh:packerをインストール
brew tap homebrew/binary
brew install packer
```

インストールが終わったら、AMIを構築する手順jsonを書きます。

```json:caffe_gpu_ec2.json
{
  "variables":{
    "aws_access_key": "",
    "aws_secret_key": ""
  },
  "builders": [{
    "type": "amazon-ebs",
    "access_key": "{{user `aws_access_key`}}",
    "secret_key": "{{user `aws_secret_key`}}",
    "region": "us-east-1",
    "source_ami": "ami-2cbf3e44",
    "instance_type": "g2.2xlarge",
    "ssh_username": "ubuntu",
    "spot_price": "0.07",    
    "ami_name": "caffe_gpu {{timestamp}}",
    "launch_block_device_mappings": [
      {
        "device_name": "/dev/sdb",
        "volume_type":"gp2",
        "volume_size": 8,
        "delete_on_termination": true
      },
      {
        "device_name": "/dev/sda1",
        "volume_type":"gp2",
        "volume_size": 20,
        "delete_on_termination": true
      }
    ]
    }],
    "provisioners":[{
      "type":"shell",
      "inline": [
        "sudo apt-get -y update",
        "sudo apt-get install -y python-pip python-dev",
        "sudo wget -qO- https://get.docker.com/ | sh",
        "sudo usermod -aG docker ubuntu",
        "sudo pip install ansible",
        "sudo pip install docker-py"        
      ]
    },
    {
      "type": "ansible-local",
      "playbook_file": "setup.yml"
    }
  ]
}
```

awsのキー系は別ファイルから代入するような感じにしたいので

```json:variables.json
{
    "aws_access_key": "アクセスキー",
    "aws_secret_key": "シークレットキー"
}
```

こんなjsonファイルを作りました。


あー、めっちゃ簡単ですね。

`builders`ってところでインスタンスのスペックやらを指定してます。
スポットインスタンスを利用してAMIを作れたりとPackerはとても便利です。

`provisioners`では`builders`で設定したインスタンスで走らせたいコマンドを指定します。

DockerとAnsible(意識高い)をインストールしたかったので`type:shell`な処理を走らせてます。
で、その後Ansibleを実行したかったので`ansible-local`を走らせてます。
`playbook_file`で指定したyamlファイルの中身が実行されるわけです。

で、setup.ymlはこんな感じ。


```yml:setup.yml
- hosts: all
  user: ubuntu
  tasks:
    - name: "docker pull caffe_gpu"
      sudo: yes
      docker:
        name: caffe_gpu
        image: tleyden5iwx/caffe-gpu-master
        state: present        
```

`docker pull`を実行してるだけです。

普通に`type:shell`でやって良いです。
意識高くなって魔が差した、といったところでしょうか。

これ準備完了です。
packerコマンドでAMIを作ります。

```sh:AMIをビルドする
packer build -var-file=variables.json caffe_gpu_aws.json
```

東京オリンピック構想に想い馳せながらしばらく待っているとAMIができてるはず。

このAMIからインスタンス立ててログインします。
しばらく待ってるとインスタンスできてる。

Docker Machineを使ってる人なら下記のような感じでスポットインスタンスを立てるとよさげ。

```sh:DockerMachineでGPUのスポットインスタンスを立てる
docker-machine create -d amazonec2 --amazonec2-access-key [アクセスキー] --amazonec2-secret-key [シークレットキー] --amazonec2-ami [AMIのID] --amazonec2-vpc-id [VPCのID] --amazonec2-request-spot-instance="true" --amazonec2-spot-price="0.16" --amazonec2-instance-type="g2.2xlarge" —amazonec2-root-size="20" caffe(任意のマシン名)
```

あとは`docker run`すれば終わり、と言いたいところなんですが、きっと動かないはずです。

完全に謎なんですが、nvidiaデバイスが有効化されてないっぽい？

http://tleyden.github.io/blog/2014/10/25/cuda-6-dot-5-on-aws-gpu-instance-running-ubuntu-14-dot-04/
のVerify CUDA is correctly installedってところに書いてるコマンドを実行したら動作しました。
この辺は完全に謎です。

```sh:nvidiaデバイスを有効にするために関係なさそうなコマンドを実行する
cd /usr/local/cuda/samples/1_Utilities/deviceQuery && make && ./deviceQuery
```

で、実際にGPUなCaffeを起動します。


```sh:GPUなCaffeを起動
$ DOCKER_NVIDIA_DEVICES="--device /dev/nvidia0:/dev/nvidia0 --device /dev/nvidiactl:/dev/nvidiactl --device /dev/nvidia-uvm:/dev/nvidia-uvm"

$ sudo docker run -ti $DOCKER_NVIDIA_DEVICES tleyden5iwx/caffe-gpu-master /bin/bash
```

はい、GPUなCaffe使えるようになった。
やったね。

あとは適当にサンプルを動かして悦に浸るなり、自分のデータセットを学習させるなりして分類とかしたら『私はディープラーニング使えます』と、言い張れます。たぶん。