NeuralTalk2で画像にキャプショニングしてみる

## NeuralTalk2とは？

Deep Learningを応用した画像認識や画像処理については、今や割と身近なものになってきている。ちょっとした処理であれば、ありもののサービスやライブラリを使って出来てしまうので、あとはアイデア次第で、いろいろなものに使えると思う。最近では、GoogleのCloud Vision APIが公開されたことなんかも話題になっている。

ということで、今回取り上げる[NeuralTalk2](https://github.com/karpathy/neuraltalk2)も、そんなDeepを使った画像認識から、写っているものについてのキャプションをつけることができる実装の一つだ。作者はGoogleなどでもインターンとして働いたこともある、スタンフォード大学の博士過程学生のAndrej Karpathyという人だ。この人の名前で検索すると、Deepを使った画像認識系の論文もいくつか出てくる。というのと、ブログが参考になるので、この手の技術を追っかけてみたい人はフォローしておくとよいかもしれない。

- スタンフォード大での公式サイト：http://cs.stanford.edu/people/karpathy/
- ブログ：http://karpathy.github.io/

ちょっと前にイケてるセルフィーとイケてないセルフィーを識別するモデルを作って、ネット上で話題にもなっていた。

[NeuralTalk2](https://github.com/karpathy/neuraltalk2)は最初に作られた[NeuralTalk](https://github.com/karpathy/neuraltalk)の改良版で、実装がPythonと[Caffe](http://caffe.berkeleyvision.org/)によるものから、[Lua](http://www.lua.org/)と[Torch](http://torch.ch/)に変更されて、処理速度などが上がっている（らしい）。また、認識、キャプションの作成ともRNNによるモデル（正確にはCNN+RNNのはず）で行うようになっていて、キャプションをつけてみるだけなら、学習済みのモデルからすぐに使えるようになっている。

ここでは、理論の説明はいったん置いておいて、[NeuralTalk2](https://github.com/karpathy/neuraltalk2)を利用できるよう、手順にしたがって環境構築し、キャプションを付けられるところまでやってみる。

## 実行環境を作ってみる

この手の実装の例にもれず、[NeuralTalk2](https://github.com/karpathy/neuraltalk2)もGPUを利用することで、処理が高速化できるようになっているため、AWSのGPUインスタンス上に環境を作ってみる。ここでは、事前にCUDA Toolkitをインストールしてある状態から始める。CUDAのインストール等については[公式サイト](https://developer.nvidia.com/cuda-zone)に譲るとして、その他のインストールを行う。AWSのインスタンスについては、以下の通り。

- OS：Ubuntu14.04
- インスタンスタイプ：g2.2xlarge
- リージョン：バージニア北部

事前にインストールしておいたCUDAのバージョンは7.0となっている。一旦デフォルトで入っているCUDAを全てパージした上で、バージョン7.0を入れ直した。最新は7.5だが、AWSで環境を作る場合、対応ドライバの関係で7.0の方が安定していそうなので、いったん7.0にしている。

また、Pythonを一部利用することがあるが、こちらもインストール済みとしておく。

### NeuralTalk2自体のクローン
まずはNeuralTalk2自体を手元に落としてくる必要がある。普通にGithubのリポジトリからクローンしてくるだけだ。

```shell-session:NeuralTalk2のリポジトリをクローン
$ git clone https://github.com/karpathy/neuraltalk2.git
```

### Torchと関連するLuaのモジュールのインストール

認識をかけていくところまでに必要なTorchとTorchでの実装に必要なLuaのモジュールをインストールする。

Torchについては、依存するライブラリのインストールなど事前に行えるスクリプトを実行し、その後Githubのリポジトリから最新のソースをクローンしてインストールスクリプトを実行という流れになっている。ホームディレクトリで以下の通り行う。

```shell-session:Torchのインストール
$ curl -s https://raw.githubusercontent.com/torch/ezinstall/master/install-deps | bash
$ git clone https://github.com/torch/distro.git ~/torch --recursive
$ cd ~/torch; 
$ ./install.sh
$ source ~/.bashrc
```
インストールの最後にTorchのパスが.bashrcに追加されるので、sourceコマンドでパスを有効にしている。Torchがちゃんとインストールできたかは、Torchのインタラクティブセッションを実行してみれば分かる。以下のようにTorchのロゴが表示されればインストールは正常に行われている。

```shell-session:Torchのインタラクティブセッションの実行
$ th
 
  ______             __   |  Torch7                                         
 /_  __/__  ________/ /   |  Scientific computing for Lua. 
  / / / _ \/ __/ __/ _ \  |  Type ? for help                                
 /_/  \___/_/  \__/_//_/  |  https://github.com/torch         
                          |  http://torch.ch                  

th> 
```
Torchがインストールできたら、Torchでの処理用にニューラル系と画像処理系のLuaのライブラリをインストールする。LuaのライブラリはLuaのパッケージマネージャである[LuaRocks](https://luarocks.org/)で行うことができる。インストールするのは以下の３つで、luarocksコマンドのインストールオプションで行う。LuaとLuaRocksは上記の依存関係を含めたTorchのインストール手順の中で使える状態になっている。

```shell-session:Luaライブラリのインストール
$ luarocks install nn
$ luarocks install nngraph 
$ luarocks install image 
```
次に内部でJSONをLuaで扱うためのcjsonのライブラリもインストールしておく。こちらはソースコードからビルドしてインストールする。

```shell-session:Luaのcjsonライブラリのインストール
$ wget http://www.kyne.com.au/~mark/software/download/lua-cjson-2.1.0.tar.gz
$ tar xzvf lua-cjson-2.1.0.tar.gz
$ cd lua-cjson-2.1.0/
$ luarocks make
```
最後にGPUを利用するので、CUDA関連のライブラリをインストールする。利用するライブラリは以下の二つ。

```shell-session:CUDA関連のLuaライブラリ
$ luarocks install cutorch
$ luarocks install cunn
```
CUDA関連も基本はこれでOKなのだが、NVIDIAのDNN用ライブラリであるcuDNNを使う場合は、cuDNNのバージョン3.0以上とcudnn.torchを入れる必要がある。後ほど使う学習済みのモデルが利用しているので、ここでは入れておくことにする。まずはNVIDIAの[サイト](https://developer.nvidia.com/cuDNN)からcuDNNをダウンロードしてきて、任意の場所に置き、共用ライブラリパスに追加しておく。その後、cudnn.torchのリポジトリをクローンしてきて、ビルドしておく。cuDNNはv4.0のrcが最新だが、試してみたところv4.0だとエラーが出るため、v3.0をダウンロードしておく必要がある。

```shell-session:cuDNNのダウンロードとパス追加
$ export LD_LIBRARY_PATH=/home/ubuntu/libs/cuda/lib64:$LD_LIBRARY_PATH
$ git clone https://github.com/soumith/cudnn.torch.git
$ cd cudnn.torch
$ luarocks make cudnn-scm-1.rockspec
```

ここまでの環境構築で、学習済みのモデルを利用して画像を評価し、キャプションをつけるまではできるようになるはず。

### 学習を行う場合のライブラリの追加
これまでの環境構築でとりあえずキャプションをつけるところまではできるはずだが、一応自分でファインチューニングしたり、新たにモデルを学習するために必要なライブラリもインストールしておく。

一つはCaffeで学習したモデルをTorchで利用するのに必要なloadcaffeをインストールする。loadcaffe自体はluarocksでインストールできるが、loadcaffeを利用するにはProtocol Buffersのライブラリが必要になるので、こちらは別にインストールしておく。Caffeを同一の環境にすでにインストール済みの場合はすでに入っているはずだが、そうでない場合はloadcaffeのインストールとともに以下の通り。

```shell-session:loadcaffeのインストール
$ sudo apt-get install libprotobuf-dev protobuf-compiler
$ luarocks install loadcaffe
```
あとは内部で前処理のデータ保持に利用されている[HDF5](https://www.hdfgroup.org/HDF5/)形式のファイルを扱うためのライブラリを入れる。ちなみにHDF5は大規模な科学計算などでも用いられる構造化データの形式の一つだ。入れる必要があるのはTorch用のライブラリであるtorch-hdf5とPythonでHDF5形式のファイルを扱うためのライブラリであるh5pyだ。またそれらのビルドや内部で参照されているライブラリも最初にインストールしておく。

```shell-session:HDF5関連のライブラリのインストール
$ sudo apt-get install libhdf5-serial-dev hdf5-tools
$ git clone git@github.com:deepmind/torch-hdf5.git
$ cd torch-hdf5/
$ luarocks make hdf5-0-0.rockspec LIBHDF5_LIBDIR="/usr/lib/x86_64-linux-gnu/"
$ pip install h5py
```
これで一応の環境構築はできているはず。ということで、実際に使ってみる。

## キャプショニングの実行
それでは、実際にキャプション付を実行してみる。ここでは学習済みのモデルを使って実行する。提供されている学習済みのモデルは、マイクロソフトが公開しているデータセットである[COCO](http://mscoco.org/)を学習させたものだ。任意のディレクトリにダウンロードして解凍しておく。

```shell-session:COCO学習済みモデルのダウンロード
$ wget http://cs.stanford.edu/people/karpathy/neuraltalk2/checkpoint_v1.zip
$ unzip checkpoint_v1.zip
```

あとはキャプションを付けたい画像も任意のディレクトリに保存しておき、先ほどクローンしてきたNeuralTalk2の中に含まれているeval.luaを実行する。試しにロイヤリティフリーの画像素材から以下の画像を持ってきて実行してみた。

![a1180_015158_m.jpg](https://qiita-image-store.s3.amazonaws.com/0/5315/1163a7e1-7d43-96f6-c991-f85898b7e75a.jpeg)
*写真素材　足成よりダウンロード http://www.ashinari.com/*

男性が自転車に乗っているというようなキャプションが付けばOKのはずである。

```shell-session:実行結果
$ th eval.lua -model ~/data/model/model_id1-501-1448236541.t7 -image_folder ~/data/images -num_images -1
DataLoaderRaw found 1 images
constructing clones inside the LanguageModel
cp "/home/ubuntu/data/images/a1180_015158_m.jpg" vis/imgs/img1.jpg
image 1: a man riding a bike down a street
evaluating performance... 0/-1 (0.000000)
loss:   nan
```
実行はクローンしてきたNeuralTalk2のディレクトリに移動して行っている。num_imagesというオプションは処理するイメージ数を指定しているが、-1で実行すると、指定のディレクトリ下の全ての画像を処理する。この他バッチサイズもbatch_sizeオプションで指定できるが、ここではデフォルトのままで実行している。画像数が多く、処理速度を上げたい場合は指定するとよい。

キャプションの結果はimage 1:の部分に表示されている。「a man riding a bike down a street」となっており、この画像については、正しいキャプションが付けられた。NeuralTalkのキャプションは単なる単語でつくのではなく、このように短文で説明がつくような形になる。

キャプションはJSON形式でvisというディレクトリの中にも保存されており、こちらは簡易のWebインターフェイスで見ることもできる。こちらを見るにはvisディレクトリに移動して、PythonのsimpleHttpServerを実行すればよい。デフォルトだとポート8000番で起動するので、プラウザからアクセスして表示してみる。

```shell-session:実行結果のWeb表示
$ cd vis
$ python -m SimpleHTTPServer
```
![neuraltalk_web.png](https://qiita-image-store.s3.amazonaws.com/0/5315/5785581c-9708-16b6-13fa-6ca612ec82ac.png)

## この後どうする？
NeuralTalk2を動かしてみるところまで、ざっくりやってみた。提供されている学習済みのモデルを使うだけでも、かなりよい感じにキャプションを付けてくれそうで、いろいろ応用を考えてみようと思っているところ。

Karpathyさんご本人のデモだったと思うが、動画のストリームで映っている範囲のキャプション付けをしているのを見たような記憶があるので、そちらもちょっとやってみようと考えている。

ということで、興味のある方はちょっと触ってみてはいかがだろうか？