読者です 読者をやめる 読者になる 読者になる

kivantium活動日記

プログラムを使っていろいろやります

Caffeによる特徴抽出+AROWによる分類を試したかった

今のところうまくいってないですが、忘れないためのメモ書き

Deep Learningのすごいところとしてよく上げられるのは「画像から自動で特徴抽出をしてくれる」ことです。従来の手法であればタスクに合わせた画像の特徴をうまく抜き出すような特徴量を作る必要がありましたが、Deep Learningではその必要がなくネットワークが勝手に特徴を抽出してくれるとされています。ネットワークが抽出してくれた特徴量をニューラルネットワークではないSVMなどの別の分類器で分類することもできます。Deep Learningがうまく分類するために自動で作った特徴量を利用することで精度を向上させることができると言われています。

そこで今回はDeep LearningライブラリのCaffeを使って特徴抽出を行った後、AROWというアルゴリズムで分類を行ってみたいと思います。

Caffeによる特徴抽出

Caffe | Feature extraction with Caffe C++ code.filter visualization exampleを参考にしました。

準備

Caffeのインストールは済んでいるとします。最初に手元にあったバージョンでは存在しない関数を使っていたので環境によっては更新の必要があるかもしれません。

まずcaffeディレクトリ内で

scripts/download_model_binary.py models/bvlc_reference_caffenet

を実行して事前学習済みのモデルをダウンロードします。

次のコードを実行します。

import numpy as np
import matplotlib.pyplot as plt

# Make sure that caffe is on the python path:
caffe_root = '../'  # this file is expected to be in {caffe_root}/examples
import sys
sys.path.insert(0, caffe_root + 'python')

import caffe

plt.rcParams['figure.figsize'] = (10, 10)
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

import os
if not os.path.isfile(caffe_root + 'models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel'):
    print("pre-trained CaffeNet model not found")
    sys.exit()
net = caffe.Net(caffe_root + 'models/bvlc_reference_caffenet/deploy.prototxt',
                caffe_root + 'models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel')
net.set_mode_cpu()

# input preprocessing: 'data' is the name of the input blob == net.inputs[0]
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
transformer.set_transpose('data', (2,0,1))
transformer.set_mean('data', np.load(caffe_root + 'python/caffe/imagenet/ilsvrc_2012_mean.npy').mean(1).mean(1)) # mean pixel
transformer.set_raw_scale('data', 255)  # the reference model operates on images in [0,255] range instead of [0,1]
transformer.set_channel_swap('data', (2,1,0))  # the reference model has channels in BGR order instead of RGB
net.blobs['data'].reshape(1,3,227,227)
if (len(sys.argv) == 1):
    sys.exit()
for index in range(1, len(sys.argv)):
    net.blobs['data'].data[...] = transformer.preprocess('data', caffe.io.load_image(sys.argv[index]))
    net.forward()
    print 1, #ここは対象の画像に応じて適宜書き換える
    for i in range(len(net.blobs['fc7'].data[0])):
        if net.blobs['fc7'].data[0][i][0][0]!=0:
            print str(i)+':'+str(net.blobs['fc7'].data[0][i][0][0]), 
    print

このコードは引数に与えた画像ファイルに対してLIVSVMフォーマットで4096次元の特徴ベクトルを出力します。feature_extraction.pyで保存するとして、

python feature_extraction.py image*.jpg > input.txt

のように使います。
コメントにもありますが、caffe/examples内に置くこと前提のコードです。

AROWによる分類

kbkz.connpass.com
で発表されていた
@olanleedさんによる機械学習ライブラリMochiMochiを使います。
言語はみんな大好きC++です!!(この記事はこのライブラリが使いたくて書いたようなもの)

olanleed.hatenablog.com

Eigenのインストール

MochiMochiは線形代数ライブラリのEigenに依存しているのでインストールします。
Eigen公式サイトからダウンロードして、Eigenフォルダを/usr/includeにコピーすればインストール完了です。

AROWによる分類

AROWというアルゴリズムが良さそうなので使ってみました。

作者の@olanleedさんのアイコンに敬意を評して、データセットには「一週間フレンズ。」の顔画像を使いました。
f:id:kivantium:20150628225251p:plain:w400
AROWは2値分類器なので画像が藤宮さんかどうかを判定してみようと思います。
arowのexampleをに少し書き足して標準入力の値から判定するコードを書きました。

#include "../../../src/classifier/arow.hpp"
#include "../../../src/utility/load_svmlight_file.hpp"
#include <boost/program_options.hpp>
#include <iostream>

int main(const int ac, const char* const * const av) {
  using namespace boost::program_options;

  options_description description("options");
  description.add_options()
    ("help", "")
    ("dim", value<int>()->default_value(0), "データの次元数")
    ("train", value<std::string>()->default_value(""), "学習データのファイルパス")
    ("test", value<std::string>()->default_value(""), "評価データのファイルパス")
    ("r", value<double>()->default_value(0.5), "ハイパパラメータ(r)");

  variables_map vm;
  store(parse_command_line(ac, av, description), vm);
  notify(vm);

  if(vm.count("help")) { std::cout << description << std::endl; }

  const auto dim = vm["dim"].as<int>();
  const auto train_path = vm["train"].as<std::string>();
  const auto test_path = vm["test"].as<std::string>();
  const auto r = vm["r"].as<double>();

  std::string line;
  std::ifstream train_data(train_path);

  AROW arow(dim, r);
  std::cout << "training..." << std::endl;
  while(std::getline(train_data, line)) {
    auto data = utility::read_ones(line, dim);
    arow.update(data.second, data.first);
  }

  int collect = 0;
  int all = 0;
  std::ifstream test_data(test_path);
  std::cout << "predicting..." << std::endl;
  while(std::getline(test_data, line)) {
    auto data = utility::read_ones(line, dim);
    int pred = arow.predict(data.second);
    if(pred == data.first) {
      ++collect;
    }
    ++all;
  }

  std::cout << "Accuracy = " << (100.0 * collect / all) << "% (" << collect << "/" << all << ")" << std::endl;
  std::getline(std::cin, line);
  auto data = utility::read_ones(line, dim);
  int pred = arow.predict(data.second);
  if(pred==1) std::cout << "Fujimiya-san" << std::endl;
  else std::cout << "Not Fujimiya-san" << std::endl;

  return 0;
}

makeして

./arow --dim 4096 --train train.txt --test test.txt --r 0.8 < data.txt

のように使います。(train.txt: 訓練データの入ったファイル、test.txt: テストデータの入ったファイル、data.txt: 調べたいデータのファイル)

用意したデータセットに対してAccuracyは75%だったのですが、単体のデータを調べてみるといつも-1が返ってきました。データに含まれる藤宮さんの割合は25%であることを考えると75%というのも常に-1を返した結果のように思われます。

ライブラリの使い方がおかしいのか特徴抽出の仕方がおかしいのかデータ形式がおかしいのかまだ切り分けられていません。分かったら更新します。