大域的目標
本シリーズの目標はCNNのことを全く知らない人間が少し理解することにある.前回までの取り組みは以下のリンクより.
今回の目標
- 自らprototxtを作成し,MNISTの学習を行う
- Deployment(学習した結果を用いて認識)を行う
自らprototxtを作成し,MNISTの学習を行う
1. prototxtの作成
前回も紹介したこのサイトを参考に,以下の2つのprototxtを作成した.
train.prototxt
name: "LeNet" layer { name: "mnist" type: "Data" top: "data" top: "label" include { phase: TRAIN } transform_param { scale: 0.00390625 } data_param { source: "mnist_train_lmdb" batch_size: 64 backend: LMDB } } layer { name: "mnist" type: "Data" top: "data" top: "label" include { phase: TEST } transform_param { scale: 0.00390625 } data_param { source: "mnist_test_lmdb" batch_size: 100 backend: LMDB } } layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" convolution_param { num_output: 20 kernel_size: 5 weight_filler { type: "xavier" } } } 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: 20 kernel_size: 5 weight_filler { type: "xavier" } } } 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 weight_filler { type: "xavier" } } } layer { name: "relu1" type: "ReLU" bottom: "ip1" top: "ip1" } layer { name: "ip2" type: "InnerProduct" bottom: "ip1" top: "ip2" inner_product_param { num_output: 500 weight_filler { type: "xavier" } } } layer { name: "loss" type: "SoftmaxWithLoss" bottom: "ip2" bottom: "label" top: "loss" } layer { name: "accuracy" type: "Accuracy" bottom: "ip2" bottom: "label" top: "accuracy" }
solver.prototxt
net: "train.prototxt" base_lr: 0.01 lr_policy: "fixed" max_iter: 5000 display: 100 test_interval: 200 test_iter: 100 solver_mode: CPU
2. 学習の実行
手順は以下の2ステップ.
2.1 ファイルを構成
まず,MNISTのLMDB(前回作成済み)と2つのprototxtを,以下の図の様にCAFFE_ROOT/mycnn内に入れる.
2.2 trainコマンドの実行
後は,以下の通り学習を実行する.
- コマンドプロンプトを起動
- mycnnフォルダに移動
cd %CAFFE_ROOT%\mycnn
- 学習を実行
..\build\tools\Release\caffe.exe train --solver=solver.prototxt
- 学習が終了するまで数分~十数分待つ
- 以下のファイルが生成されたか確認
これで第一の目標達成だ!
Deployment(学習した結果を用いて認識)を行う
手順は以下の4ステップ.
1. deploy.prototxtの作成
本家Webサイトを参考に,train.prototxtからdeploy.prototxtを作成する.具体的には,以下の様に編集する.
- 元のdataレイヤをInputレイヤに置き換え
- lossレイヤを別のlossレイヤに置き換え
- accuracyレイヤを削除
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 weight_filler { type: "xavier" } } } 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: 20 kernel_size: 5 weight_filler { type: "xavier" } } } 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 weight_filler { type: "xavier" } } } layer { name: "relu1" type: "ReLU" bottom: "ip1" top: "ip1" } layer { name: "ip2" type: "InnerProduct" bottom: "ip1" top: "ip2" inner_product_param { num_output: 500 weight_filler { type: "xavier" } } } layer { name: "loss" type: "Softmax" bottom: "ip2" top: "loss" }
2. classify_digits.pyの作成
書籍「Caffeをはじめよう―深層学習による画像解析の実践」の3.4 画像分類を参考に,以下のPythonスクリプトを作成し,%CAFFE_ROOT%\mycnnへ入れる.
import numpy as np import sys import os import caffe model = "deploy.prototxt" weights = "_iter_5000.caffemodel" argvs = sys.argv argc = len(argvs) if argc < 2: print("usage: python classify_digits.py <imagepath>") sys.exit(1) image_file = argvs[1] if not os.path.isfile(weights): print("error: pre-trained Caffe model...") caffe.set_mode_cpu() net = caffe.Classifier(model, weights, channel_swap=[0], image_dims=(28, 28)) input_image = caffe.io.load_image(image_file, color=False) prediction = net.predict([input_image], False) print("prediction shape: {}".format(prediction[0].shape)) print("prediction shape: {}".format(prediction[0].argmax()))
3. 入力画像の用意
PhotoshopやPaint.net等のドローソフトを使って画像データを作成し,mycnnフォルダに入れておく.今回作成したデータは以下のようなものだ.尚,画像は背景が黒で文字が白,28×28画素かつ3チャンネルの画像でないといけない.
4. Pythonスクリプトの実行
最後に,python classify_digits.py 7.png
を実行.すると以下のような結果が得られる.尚,認識させたい画像によって,引数7.pngの部分を変更する.
C:\Libraries\caffe\mycnn> python classify_digits.py 7.png (中略) I1124 13:58:26.040210 5676 net.cpp:744] Ignoring source layer ip2_ip2_0_split I1124 13:58:26.040210 5676 net.cpp:744] Ignoring source layer accuracy C:\Users\Shohei\Anaconda3\lib\site-packages\skimage\transform\_warps.py:84: UserWarning: The default mode, 'constant', will be changed to 'reflect' in skimage 0.15. warn("The default mode, 'constant', will be changed to 'reflect' in " prediction shape: (500,) prediction shape: 7
最後に出てきた数字が認識結果なので「7」となっており,認識に成功していることが分かる.第2の目標達成だ!
所感
今回の場合6と9を8に誤って認識していた.別の入力データを使って試すも,同じような傾向が見られた.おそらく,学習データとして与えられているMNIST内の手書きデータが私のものとそもそも結構違うのだろう.
実際に確認してみる.ここのサイトにあるMNISTの学習用データの一部を拝借して確認すると,予想通り,結構違って見える.
※この図はここのサイトより拝借.
さて,残りの謎は,LMDBをどうやって作るか?という部分なので,次回はそれに取り組みたい.