2014年9月16日

プログラミング

Caffeで手軽に画像分類

  • このエントリーをはてなブックマークに追加

こんにちは。システム統括本部 データソリューション本部の宮崎です。

最近ディープラーニングと呼ばれる技術の話題を耳にすることが増えてきました。
この記事ではディープラーニングの手法を実装し画像認識系の用途で便利に使えるCaffeというツールの使い方を紹介します。

Caffeの概要

Caffeは、C++で実装されGPUに対応した高速なディープラーニングのライブラリです。
大規模画像認識のコンテストILSVRCで2012年にトップとなった畳込みニューラルネットワークの画像分類モデル[1]がすぐに利用できるようになっています。

Caffeは、カリフォルニア大学バークレー校のコンピュータビジョンおよび機械学習に関する研究センターであるBVLCが中心となって開発しているOSSです。
ヤフージャパンは2014年6月から同センターのスポンサーになっており、Caffeの開発を含めたセンターの研究の支援を行っています。

インストール

Caffeのインストール手順は公式ドキュメントに詳しく書かれています。
Caffeは呼び出しているライブラリの最近のバージョンの機能に依存していることがしばしばあるので、ドキュメント通りの構成でのインストールが無難です。
CaffeはCUDA対応GPUを利用できますが、GPUの機種やドライバのバージョンに依存した問題がいくつか報告されています。
CPUのみで使うこともできます。

なお、本記事はCaffe v0.9999, CUDA6.0, OpenBLAS, Pythonバインディングの構成で執筆しています。

リファレンスモデルでの分類

それでは、さっそく画像を分類してみましょう。

CaffeはILSVRC2012データセットで学習済みのリファレンスモデルを配布しているので、それを使ってすぐに分類を実行することができます。
(※Caffeのリファレンスモデルは非商用ライセンスで配布されています)。

準備

リファレンスモデルと関連ファイルを、以下のスクリプトを実行してダウンロードしておきます。

  • examples/imagenetディレクトリでget_caffe_reference_imagenet_model.shを実行
  • data/ilsvrc12ディレクトリでget_ilsvrc_aux.shを実行

また、画像分類のサンプルとして、物体認識のデータセットの一つであるCaltech101をダウンロードしています。
後ほど学習用画像としても使います。

なお、本記事のコマンド入力例は、コンパイル済みCaffeソースコードのトップディレクトリにいる想定で書いてあります。

$ wget http://www.vision.caltech.edu/Image_Datasets/Caltech101/101_ObjectCategories.tar.gz
$ tar xf 101_ObjectCategories.tar.gz

分類

Caffe付属のPythonスクリプトを使って、Caltech101の次の画像を分類してみます。

caffe_01.jpg
$ cd python; python classify.py --raw_scale 255 ../101_ObjectCategories/airplanes/image_0001.jpg ../result.npy; cd ..

分類結果は個々のカテゴリに対する確率のみが表示されるので、名前と対応付けて結果を表示するために以下のPythonスクリプトを show_result.py という名前で保存してください。

#! /usr/bin/env python
# -*- coding: utf-8 -*-
import sys, numpy

categories = numpy.loadtxt(sys.argv[1], str, delimiter="\t")
scores = numpy.load(sys.argv[2])
top_k = 3
prediction = zip(scores[0].tolist(), categories)
prediction.sort(cmp=lambda x, y: cmp(x[0], y[0]), reverse=True)
for rank, (score, name) in enumerate(prediction[:top_k], start=1):
    print('#%d | %s | %4.1f%%' % (rank, name, score * 100))

スクリプトを実行して、分類結果を表示します。

$ python show_result.py data/ilsvrc12/synset_words.txt result.npy
#1 | n04552348 warplane, military plane | 84.8%
#2 | n04008634 projectile, missile |  5.5%
#3 | n02690373 airliner |  5.1%

84.8%の確率で飛行機(軍用機)と判定されました。

Caffeを特徴抽出器として使った分類

Caffeのリファレンスモデルはあらかじめ決められた1000のカテゴリに画像を分類しますが、実用的には使いにくいカテゴリなので、独自のモデルを用意したくなります。
しかし、同じ規模のモデルを新しく学習データをそろえて作るのは大変です。

Caffeの中間層の出力を画像特徴として使うと学習データがあまり多くなくても良い精度の画像分類ができる[2]ので、この方法でリファレンスモデルとは異なるカテゴリで分類できるようにしてみます。

さきほどダウンロードしたCaltech101データセットを使い、Caffeで画像から特徴抽出を行いliblinearで線形SVMを学習させて、Caltech101用の分類器を作ります。

特徴抽出

論文[2]にならってfc6層の値を取り出します。
活性化関数を通す前の値を取り出すため、既存のモデル定義ファイルを一部変更した新しい定義ファイルを作ります。

$ cp examples/imagenet/imagenet_deploy.prototxt examples/imagenet/imagenet_feature.prototxt
$ vi examples/imagenet/imagenet_feature.prototxt

imagenet_feature.prototxtを以下のように編集します。

  • nameが"fc6"になっているlayersの定義で、topの値を"fc6"から"fc6wi"に変更
  • nameが"relu6"になっているlayersの定義で、bottomの値を"fc6"から"fc6wi"に変更

一つの画像ファイルに対して特徴抽出を実行するPythonスクリプトの例(feature.py)を下記に示します。

#! /usr/bin/env python
# -*- coding: utf-8 -*-
import sys, os, os.path, numpy, caffe

MEAN_FILE = 'python/caffe/imagenet/ilsvrc_2012_mean.npy'
MODEL_FILE = 'examples/imagenet/imagenet_feature.prototxt'
PRETRAINED = 'examples/imagenet/caffe_reference_imagenet_model'
LAYER = 'fc6wi'
INDEX = 4

net = caffe.Classifier(MODEL_FILE, PRETRAINED)
net.set_phase_test()
net.set_mode_cpu()
net.set_mean('data', numpy.load(MEAN_FILE))
net.set_raw_scale('data', 255)
net.set_channel_swap('data', (2,1,0))

image = caffe.io.load_image(sys.argv[1])
net.predict([ image ])
feat = net.blobs[LAYER].data[INDEX].flatten().tolist()
print(' '.join(map(str, feat)))

実行すると、Caffeがモデルを読み込む様子が表示された後、特徴量である4096個の数値が表示されます。

$ python feature.py 101_ObjectCategories/airplanes/image_0001.jpg
-15.0908336639 -2.32919740677 -7.1326918602 -21.7519626617 ...

このようにして抽出した特徴量をlibsvmフォーマットで並べた以下のようなファイルを作ります。
さきほど紹介した特徴抽出スクリプトの出力はlibsvmフォーマットではないので、出力もしくはスクリプトを加工して使用してください。
今回、学習用には1カテゴリあたり30枚の画像を使っています。

$ cat train.txt
0 1:-44.596577 2:-30.565985 3:-26.004364 4:-1.526159 ...
 :
$ cat test.txt
0 1:-45.810318 2:-39.670868 3:-33.117592 4:-21.308578 ...
 :

線形SVMの学習

学習を実行します。定数Cの探索手順は省略しています。

$ svm-scale -s scale.txt train.txt > train_scaled.txt
$ svm-scale -r scale.txt test.txt > test_scaled.txt
$ train -c 0.03 train_scaled.txt caltech101.model

分類結果

テスト用データで正解率89.1%となるモデルが得られました。

$ predict test_scaled.txt caltech101.model result.txt
Accuracy = 89.1486% (2670/2995)

Caltech101の標準的な手順には沿っていない部分があるので評価の数値は参考ですが、簡単な手順でかなり良いCaltech101の分類モデルを作ることができたと言えると思います。

次の画像は「カンガルー」に正しく分類されています。

分類結果: カンガルー(正解: カンガルー)
分類結果: カンガルー(正解: カンガルー)

次の画像は正解は「蝶」ですが、「顔」に分類されています。
そういう風に見えなくもありません。

分類結果: 顔(正解: 蝶)
分類結果: 顔(正解: 蝶)

ファイン・チューニング

学習データがあまり多くない時にも使える別の方法として、大規模なデータセットで学習済みの状態から目的とする別のデータセットへ学習し直す方法(ファイン・チューニング)があります[3]。

具体的に、ILSVRC2012で学習済みのCaffeリファレンスモデルをベースとしてCaltech101データセットでファイン・チューニングする手順を説明します。

データベースとmeanファイルの作成

Caffeで学習を行う際は、通常は事前に画像データをデータベースに格納しておき、そのデータベースから画像データを読み込ませて行います。

データベースの内容を指定するため、以下のような学習用(train.txt)とテスト用(val.txt)の画像のリストを作成します。
1行ごとに画像ファイル名とカテゴリに割り当てた番号をスペース区切りで記述します。

$ cat train.txt
accordion/image_0001.jpg 0
   :
$ cat val.txt
accordion/image_0031.jpg 0
   :

画像データを格納した学習用とテスト用のデータベースを作成します。
学習用データベース内の全画像の平均を格納したmeanファイルも作成します。

$ build/tools/convert_imageset.bin 101_ObjectCategories/ train.txt caltech101_train_leveldb 1 leveldb 256 256
$ build/tools/convert_imageset.bin 101_ObjectCategories/ val.txt caltech101_val_leveldb 1 leveldb 256 256
$ build/tools/compute_image_mean.bin caltech101_train_leveldb caltech101_mean.binaryproto leveldb

モデル定義ファイルの作成

Caffeのリファレンスモデル(imagenet_*.prototxt)の定義ファイルを元にして、ファイン・チューニング用のモデル定義作成を作成します。

$ cp examples/imagenet/imagenet_train_val.prototxt caltech101_train_val.prototxt
$ vi caltech101_train_val.prototxt

caltech101_train_val.prototxtを以下のように編集します。

  • nameが"data"になっている2つのlayersの定義について以下のように編集
    • include: { phase: TRAIN }が含まれているlayers定義について、data_paramのsourceを"caltech101_train_leveldb"に変更
    • include: { phase: TEST }が含まれているlayers定義について、data_paramのsourceを"caltech101_val_leveldb"に変更
    • 2つのlayersの定義に対して、data_paramのmean_fileを"caltech101_mean.binaryproto"に変更
  • nameが"fc8"のlayersのinner_product_paramのnum_outputを1000から102に変更
  • 全てのfc8を別の名前(例えばfc8ft)に置換

ソルバー設定ファイルの作成

ソルバーの設定ファイルもリファレンスモデルのソルバー設定ファイル(imagenet_solver.prototxt)を元に作成します。

$ cp examples/imagenet/imagenet_solver.prototxt caltech101_solver.prototxt
$ vi caltech101_solver.prototxt

caltech101_solver.prototxtを以下のように編集します。

  • net: モデル定義のファイル名。今回は caltech101_train_val.prototxt を指定
  • base_lr: 開始時の学習率。今回はベースとなるモデルの最後の学習率である0.001を指定

また、学習データの量に合わせて、test_iter, test_interval, snapshotといったパラメータも調整します。

ファイン・チューニングの実行と結果

ファイン・チューニングを実行します。

$ build/tools/caffe train -solver caltech101_solver.prototxt -weights examples/imagenet/caffe_reference_imagenet_model

200回の繰り返しで正解率87.2%, 1600回で正解率89.6%のモデルが得られました。

正しく分類できた例と間違って分類された例をひとつずつ示します。

分類結果: サクソフォン(正解: サクソフォン)
分類結果: サクソフォン(正解: サクソフォン)
分類結果: ビーバー(正解: イルカ)
分類結果: ビーバー(正解: イルカ)

より詳しい情報

Caffeの使い方については、Caffe開発チームによるECCV2014のチュートリアルDIY Deep Learning for Vision: a Hands-On Tutorial with Caffeの資料が分かりやすくまとまっています。
ファイン・チューニングについてはFlickr Styleデータセットを使った例にも解説があります。

本記事ではCaffeの基本的な使い方を紹介しましたが、畳込みニューラルネットワークの手法については中部大学の山下先生のMIRU2014チュートリアル解説資料中で詳しく説明されています。
また、画像認識分野におけるディープラーニングの研究動向については東北大学の岡谷先生によるIBIS2013での講演資料(PDF)が大変参考になります。

おわりに

本記事ではCaffeを使ったデフォルトモデルでの画像分類、特徴抽出、ファイン・チューニングの3つの方法を紹介しました。
大量のデータを準備したり時間をかけて学習させなくてもうまくいことが多いので、思ったよりも手軽に使えるのではないでしょうか。

Caffeは開発による変化が速いため今回紹介した手順もすぐに古くなってしまうかもしれませんが、紹介した機能は基本的なものなので、機能自体がなくなってしまうことはないと思われます。
Caffeの使い方を紹介した情報はまだあまり無いので、本記事が参考になれば幸いです。

参考文献

[1] Alex Krizhevsky, Ilya Sutskever, Geoffrey E. Hinton, "ImageNet Classification with Deep Convolutional Neural Networks", NIPS2012

[2] Jeff Donahue, Yangqing Jia, Oriol Vinyals, Judy Hoffman, Ning Zhang, Eric Tzeng, Trevor Darrell, "DeCAF: A Deep Convolutional Activation Feature for Generic Visual Recognition", arXiv:1310.1531

[3] Ross Girshick, Jeff Donahue, Trevor Darrell, Jitendra Malik, "Rich feature hierarchies for accurate object detection and semantic segmentation", CVPR2014, PDF

Yahoo! JAPANでは情報技術を駆使して人々や社会の課題を一緒に解決していける方を募集しています。詳しくは採用情報をご覧ください。

  • このエントリーをはてなブックマークに追加