気が向いたら書くやつ

気が向いたら何か書きます

Windows10でGPUを利用するCaffe環境の構築

WindowsでのCaffeの環境構築と、MNISTデータセットを使った学習、画像の分類までの流れ(2019/4/14時点)。

www.oreilly.co.jp

環境

導入:ダウンロード

github.com

README記載のリンクからビルド済みバイナリをダウンロードでき、環境構築はそれで完了となる。

導入:ビルド

せっかくなので、READMEに沿ってソースコードからビルドしてみる。

必要物

cl.exeは、C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\***(アーキテクチャ名)に配置されている。 それぞれパスを通しておく。

オプション

pycaffe、GPUを利用するため次を導入。

  • Anaconda Python 3.5
  • CUDA Toolkit 8.5
  • cuDNN v5

cuDNNは、解凍し中のcudaフォルダをC:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0以下に配置する。

ビルド手順

1. リポジトリのclone

git cloneでCaffeのリポジトリをcloneする(ここではC:\projects\に配置)。

cloneしたフォルダに移動し、windowsブランチに切り替える。

c:\projects>git clone https://github.com/BVLC/caffe.git
Cloning into 'caffe'...
remote: Enumerating objects: 65066, done.
remote: Total 65066 (delta 0), reused 0 (delta 0), pack-reused 65066
Receiving objects: 100% (65066/65066), 72.54 MiB | 246.00 KiB/s, done.
Resolving deltas: 100% (41108/41108), done.
c:\projects>cd caffe
c:\projects\caffe>git checkout windows
Branch windows set up to track remote branch windows from origin.
Switched to a new branch 'windows'
2. ビルド設定の変更

各自の環境に合わせて、ビルド時の設定を変更する。

script\build_win.cmdの編集

if DEFINED APPVEYOR ( ... ) else (以下の変数を次のように変更する。

  • CPU_ONLY=0GPUを使用。
  • PYTHON_VERSION=3:Python3を使用。
  • RUN_INSTALL=1:インストールを実行。
  • WITH_NINJA=0:Ninjaビルドシステム(高速なビルドシステムらしい)を利用しない。
…
) else (
    :: Change the settings here to match your setup
    :: Change MSVC_VERSION to 12 to use VS 2013
    if NOT DEFINED MSVC_VERSION set MSVC_VERSION=14
    :: Change to 1 to use Ninja generator (builds much faster)
    if NOT DEFINED WITH_NINJA set WITH_NINJA=0
    :: Change to 1 to build caffe without CUDA support
    if NOT DEFINED CPU_ONLY set CPU_ONLY=0
    ...
    :: Change to 3 if using python 3.5 (only 2.7 and 3.5 are supported)
    if NOT DEFINED PYTHON_VERSION set PYTHON_VERSION=3
    ...
    :: Build the install target
    if NOT DEFINED RUN_INSTALL set RUN_INSTALL=1
)
…

また、CMakeのオプションを次のように変更する。

  • - DCOPY_PREREQUISITES:BOOL=0:バイナリ生成時にDLLをコピーしない。
  • -DCUDNN_ROOT=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0 ^ :cuDNNのパスを追加。
...
cmake -G"!CMAKE_GENERATOR!" ^
      -DBLAS=Open ^
      -DCMAKE_BUILD_TYPE:STRING=%CMAKE_CONFIG% ^
      -DBUILD_SHARED_LIBS:BOOL=%CMAKE_BUILD_SHARED_LIBS% ^
      -DBUILD_python:BOOL=%BUILD_PYTHON% ^
      -DBUILD_python_layer:BOOL=%BUILD_PYTHON_LAYER% ^
      -DBUILD_matlab:BOOL=%BUILD_MATLAB% ^
      -DCPU_ONLY:BOOL=%CPU_ONLY% ^
      -DCOPY_PREREQUISITES:BOOL=0 ^
      -DINSTALL_PREREQUISITES:BOOL=1 ^
      -DUSE_NCCL:BOOL=!USE_NCCL! ^
      -DCUDA_ARCH_NAME:STRING=%CUDA_ARCH_NAME% ^
      -DCUDNN_ROOT=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0 ^
      "%~dp0\.."
...
CMakeList.txtの編集

使用するコンパイラとしてcl.exeを設定する。

project(Caffe C CXX)の前に記載すること。 また、パスの分離記号に\(バックスラッシュ)を使うと失敗するので注意。

...
# C compiler
set(CMAKE_C_COMPILER "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe")
set(CMAKE_CXX_COMPILER "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe")
...
3. ビルド

scripts\build_win.cmdを実行し、Configurationの生成からCaffeのビルドまでを実施する。 インストールを設定すると、build\tools\install以下に実行バイナリが配置される(通常はbuild\tools\Release\以下)。

コマンドラインツールcaffe.exeを実行して次のような表示がされれば、ビルドは成功。

c:\projects\caffe>.\build\install\bin\caffe.exe
caffe.exe: command line brew
usage: caffe <command> <args>

commands:
  train           train or finetune a model
  test            score a model
  device_query    show GPU diagnostic information
  time            benchmark model execution time

  No modules matched: use -help

Pythonモジュールはbuild\install\python以下に生成される。 適宜パスを通したり、site-packageにコピーするなどすれば利用可能となる。

MNISTデータセットでのテスト

おなじみMNISTデータセットを使って、学習・認識のテストをしてみる。

データセットのダウンロード

yann.lecun.com

上記webサイトから次の4種類のデータをダウンロードし、解凍したファイルをexamples\mnist以下に配置する(7-zipではうまく解凍できなかったため、MSYS2を経由してgzip -dで解凍した)。

  • train-images-idx3-ubyte.gz:学習用画像データ
  • train-labels-idx1-ubyte.gz:学習用ラベルデータ
  • t10k-images-idx3-ubyte.gz:検証用画像データ
  • t10k-labels-idx1-ubyte.gz:検証用ラベルデータ

LMDBの生成

build\install\bin以下のツールconvert_mnist_data.exeを実行し、上記データファイルを学習・テスト用のLMDBデータに変換する。

パスの設定が大変なので、次のbatスクリプトを実行すると楽。

SET EXE=%~dp0%\..\..\build\install\bin\convert_mnist_data.exe
SET ROOT=%~dp0%

%EXE% %ROOT%\train-images-idx3-ubyte %ROOT%\train-labels-idx1-ubyte %ROOT%\mnist_train_lmdb --backend=lmdb
%EXE% %ROOT%\t10k-images-idx3-ubyte %ROOT%\t10k-labels-idx1-ubyte %ROOT%\mnist_test_lmdb --backend=lmdb

学習用データmnist_train_lmdbおよび、検証用データmnist_test_lmdbが生成される。

学習

ネットワークとしてLeNetを使用し、MNISTデータセットを学習する。 学習には次のファイルを使用する。

  • examples\mnist\Release\lenet_train_test.prototxt:ネットワーク定義。学習時、検証時に利用するパラメータを含む。
  • examples\mnist\Release\lenet_solver.prototxt:solverの定義。

このsolverはおおまかに次のような設定となっている。

  • Momentum SGDによる最適化
  • バッチサイズ64のミニバッチ学習
  • 10000回学習、500回ごとに検証
  • 5000回ごとに結果を保存

Caffeのルートフォルダに移動し、caffe.exe train --solver=...で学習を実施する。 prototxtが読み込まれた後学習が開始され、設定した周期でのパラメータを次のファイルとして出力する。

  • .caffemodel:ネットワークの重みパラメータ
  • .solverstate:solverのパラメータ
c:\projects\caffe>.\build\install\bin\caffe.exe train --solver=.\examples\mnist\lenet_solver.prototxt
I0414 00:14:25.140101  7368 caffe.cpp:219] Using GPUs 0
I0414 00:14:25.328984  7368 caffe.cpp:224] GPU 0: GeForce GTX 760
I0414 00:14:25.594820  7368 common.cpp:36] System entropy source not available, using fallback algorithm to generate seed instead.
I0414 00:14:25.629798  7368 solver.cpp:44] Initializing solver from parameters:
...
I0414 00:14:25.713747  7368 solver.cpp:330] Iteration 0, Testing net (#0)
I0414 00:14:27.022940  5740 data_layer.cpp:73] Restarting data prefetching from start. I0414 00:14:27.076906  7368 solver.cpp:397]     Test net output #0: accuracy = 0.1214
I0414 00:14:27.077906  7368 solver.cpp:397]     Test net output #1: loss = 2.38325 (* 1 = 2.38325 loss)
...
I0414 00:18:33.994693  7368 solver.cpp:447] Snapshotting to binary proto file examples/mnist/lenet_iter_10000.caffemodel
I0414 00:18:34.027674  7368 sgd_solver.cpp:273] Snapshotting solver state to binary proto file examples/mnist/lenet_iter_10000.solverstate
I0414 00:18:34.049660  7368 solver.cpp:310] Iteration 10000, loss = 0.00193049
I0414 00:18:34.049660  7368 solver.cpp:330] Iteration 10000, Testing net (#0)
I0414 00:18:35.302888  5740 data_layer.cpp:73] Restarting data prefetching from start. I0414 00:18:35.353857  7368 solver.cpp:397]     Test net output #0: accuracy = 0.9915
I0414 00:18:35.353857  7368 solver.cpp:397]     Test net output #1: loss = 0.0263693 (* 1 = 0.0263693 loss)
I0414 00:18:35.354856  7368 solver.cpp:315] Optimization Done.
I0414 00:18:35.354856  7368 caffe.cpp:260] Optimization Done.

GPUを使用すると5分程度で学習が終わり、10000回学習した結果のパラメータとしてlenet_iter_10000.caffemodelが得られた。

分類

学習結果のパラメータを利用し、画像の分類を実施してみた。

lenet_deploy.prototxtの作成

分類用のネットワークに学習時の設定は必要ないため、次のように簡略化したlenet_deploy.prototxtを作成する。

name: "LeNet"
layer {
  name: "data"
  type: "Input"
  top: "data"
  input_param { shape: { dim: 1 dim: 1 dim: 28 dim: 28 } }
}
layer {
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  convolution_param {
    num_output: 20
    kernel_size: 5
    stride: 1
  }
}
layer {
  name: "pool1"
  type: "Pooling"
  bottom: "conv1"
  top: "pool1"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
layer {
  name: "conv2"
  type: "Convolution"
  bottom: "pool1"
  top: "conv2"
  convolution_param {
    num_output: 50
    kernel_size: 5
    stride: 1
  }
}
layer {
  name: "pool2"
  type: "Pooling"
  bottom: "conv2"
  top: "pool2"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
layer {
  name: "ip1"
  type: "InnerProduct"
  bottom: "pool2"
  top: "ip1"
  inner_product_param {
    num_output: 500
  }
}
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "ip1"
  top: "ip1"
}
layer {
  name: "ip2"
  type: "InnerProduct"
  bottom: "ip1"
  top: "ip2"
  inner_product_param {
    num_output: 10
  }
}
layer {
  name: "prob"
  type: "Softmax"
  bottom: "ip2"
  top: "prob"
}
Pythonスクリプトの作成

コマンドライン引数として与えた画像を分類する、次のスクリプトを作成する。

"""
classification_test.py
"""
import sys
import os
import numpy as np
import caffe

# model, weightの読み込み
model   = "lenet_deploy.prototxt"
weights = "lenet_iter_10000.caffemodel"

# LeNetの構築
net = caffe.Classifier(model, weights, channel_swap=[0], image_dims=(28, 28))

# 画像データの変換
image = sys.argv[1]
input_image = caffe.io.load_image(image, color=False)

# 分類
predict = net.predict([input_image], False)

# 分類結果の出力
print("prediction class: {}".format(predict[0].argmax()))
画像の用意と分類

MNISTのデータは黒背景でサイズ28x28[pixel]のグレースケール画像となっているため、形式に合わせて0~9の数字が描かれたPNG画像を作成した。

f:id:soratobi96:20190414011904p:plain

上記スクリプトを実行すると、コマンドライン引数で指定した画像が0~9の10クラスに分類され、結果がコンソールに出力される。

c:\projects\caffe\examples\mnist>python classification_test.py 0.png
WARNING: Logging before InitGoogleLogging() is written to STDERR
W0414 01:12:09.519904  1504 _caffe.cpp:175] DEPRECATION WARNING - deprecated use of Python interface
W0414 01:12:09.520903  1504 _caffe.cpp:176] Use this instead (with the named "weights" parameter):
...
prediction class: 0

10枚の画像それぞれの分類結果は次のようになり、1.pngのみ分類を誤る結果となった。

正解 0 1 2 3 4 5 6 7 8 9
分類 0 2 2 3 4 5 6 7 8 9

すんなりと分類成功とはいかないようである。

参考

qiita.com

dynamicsoar.hatenablog.com

mugichoko.hatenablog.com