hans

hans

【Caffe】自分のデータを素早く上手にトレーニングする 《真剣にCaffeについて話す》


トレーニングを始めるのは比較的簡単ですが、難しいのはハイパーパラメータの調整です。

1. データセットの準備#

データセットが少なすぎると、トレーニングがすぐに過学習してしまいます。これは、テストロスが変わらず、トレインロスがまだ減少していることとして現れます。

データセットを増やすために、以下のブログ記事を参照してください:

【Python】【Shell】【Caffe】トレーニングセット前処理 —— データ拡張 《真剣に Caffe について話す》

トレーニングセット、検証セット、テストセット。シーン認識の例を挙げると、

準備する最初の文書は words.txt です:第一列は番号、第二列はカテゴリです。

0 balcony
1 bathroom shower
2 bedroom bedchamber
3 childs_room

二つ目の文書は train.txt です:トレーニングセットのファイルパスとファイル名 + スペース + カテゴリ番号。

三つ目の文書は val.txt で、形式は同じですが、検証セットのファイルになります。

生成スクリプトを共有します:

#!/bin/sh

classes=(balcony bathroom shower bedroom bedchamber childs_room)
num=0
for class in ${classes[@]}
do
    ls $class/* > $class.txt
    sed -i "s/$/ $num/g" $class.txt #末尾にカテゴリインデックスを追加
    let num+=1
    cat $class.txt >> temp.txt
    rm $class.txt
done
cat temp.txt | awk 'BEGIN{srand()}{print rand()"\t"$0}' | sort -k1,1 -n | cut -f2- > train.txt #シャッフル

2. lmdb ファイルの準備#

これは train、val、test それぞれに対して行う必要があります。

$ROOT/caffe/example/imagenet/create_imagenet.sh を見つけて、パスを変更します。以下は私のバージョンです。

#!/bin/sh
set -e

EXAMPLE=/path/to/lmdb   # 生成されたlmdbディレクトリの保存場所
DATA=/path/to/train.txt & val.txt & test.txt 
TOOLS=/path/to/caffe/build/tools

TRAIN_DATA_ROOT=/absolute/path/to/calss # 上記のbalconyなどのカテゴリディレクトリへのパス、train.txt内の画像の完全な絶対パスを形成します
VAL_DATA_ROOT=/absolute/path/to/calss


echo "トレインlmdbを作成中..."

GLOG_logtostderr=1 $TOOLS/convert_imageset \
    --resize_height=0 \ # データセットの画像サイズが不一致の場合、train_val.prototxtネットワーク構造に基づいてすべての画像をリサイズする必要があります。
    --resize_width=0 \
    --shuffle=false \
    $TRAIN_DATA_ROOT \
    $DATA/train.txt \
    $EXAMPLE/train_lmdb

echo "val lmdbを作成中..."

GLOG_logtostderr=1 $TOOLS/convert_imageset \
    --resize_height=0 \
    --resize_width=0 \
    --shuffle=false \  # trueに変更すると自動的にシャッフルされます

    $VAL_DATA_ROOT \
    $DATA/val.txt \
    $EXAMPLE/val_lmdb

echo "完了しました。"

3. 平均ファイル(mean file)の準備#

$ROOT/caffe/example/imagenet/make_imagenet_mean.sh を見つけて、パスを変更します。以下は私のバージョンです。

#!/bin/sh

EXAMPLE=/path/to/lmdb
DATA=/path/to/your/train.txt & val.txt   # パスは短くする必要があります
TOOLS=/path/to/caffe/build/tools

$TOOLS/compute_image_mean $EXAMPLE/train_lmdb $DATA/mean.binaryproto

echo "完了しました。"

4. 設定ファイルの変更#

私は alexnet を使用しています。まず train_val_alexnet.prototxt を変更します。

name: "AlexNet"
layer {
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    crop_size: 227
    mean_file: "/path/to/mean.binaryproto" #変更
    mirror: true
  }
  data_param {
    source: "/path/to/train_lmdb" #変更
    backend: LMDB
    batch_size: 32 #変更
  }
}
layer {
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
  transform_param {
    mirror: false
    crop_size: 227
    mean_file: "/path/to/mean.binaryproto" #変更
  }
  data_param {
    source: "/path/to/val_lmdb" #変更
    batch_size: 16 #変更
    backend: LMDB
  }

...

...

...

layer {
  name: "fc8" # 最後の全結合層
  type: "InnerProduct"
  bottom: "fc7"
  top: "fc8"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  inner_product_param {
    num_output: 5  # あなたのカテゴリ数に変更
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}

...

...

...

次にハイパーパラメータを変更します、solver_alexnet.prototxt

net: "/path/to/alexnet.prototxt"
test_iter: 500  # 一度のテストでのイテレーション数、この数値にテストバッチサイズを掛けると、検証セットの全サンプル数になります
test_interval: 200  # トレーニング中に何回イテレーションしたらテストを行うか
base_lr: 0.001  # 基本学習率
lr_policy: "step"  # 学習ポリシー、ステップ式
gamma: 0.1 # 学習率減衰係数
stepsize: 140000 # 学習率減衰ステップ
display: 200 # トレーニング中に何回イテレーションしたらターミナルにデータを表示するか
max_iter: 420000 # 最大トレーニングイテレーション数
momentum: 0.9 # モーメンタム、変更しないでください
weight_decay: 0.0005 # 重み減衰量、変更しないでください
snapshot: 1000 # トレーニングイテレーションの何回後にスナップショットを保存するか
snapshot_prefix: "/path/" # スナップショットパス
solver_mode: GPU # モード

5. トレーニングを開始#

直接実行します:

./build/tools/caffe train --solver=/path/to/solver.prototxt

----------[2017.07.20 更新]---------- ファインチューニング -------------------------------

これまであまり多くのカテゴリでトレーニングを行っていなかったので、直接トレーニングしてもまあまあの結果が得られました。

昨日、20+、30 + カテゴリのトレーニングを試みたところ、一晩で 10 万回以上のイテレーションを行ってもロスが高いままで、精度は非常に低い値で安定していました。

2 回学習率を変更しても問題が解決しなかった後、頭の中にファインチューニングという言葉が浮かびました。試してみた結果、成功しました。

私はここから http://dl.caffe.berkeleyvision.org/
bvlc_googlenet.caffemodel をダウンロードしました。

モデルは caffe/model/bvlc_googlenet/train_val.prototxt を参考にしています。

ハイパーパラメータは caffe/model/bvlc_googlenet/solver.prototxt を参考にしています。

変更が必要な内容は以下の通りです:

  1. mean_value の 3 行を削除し、meanfile=/path/to/your/meanfile.prototxt に変更します。

  2. トレーニングとテストのソースパスを変更します。

  3. loss1 を含むすべての層を削除し、loss2 を含むすべての層を削除します。

  4. 最後の name: "loss3/top-5" 層を削除します。

  5. すべての loss3/loss3 を loss に変更します。

  6. すべての loss3/top-1 を loss に変更します。

  7. num_output: 1000 をあなたのカテゴリ数に変更します。

  8. すべての loss3/classifier の名前を適当に変更します(例えば、loss3 に変更します)。

これらの内容を変更した後、以下のコードを実行してファインチューニングを行うことができ、精度が急速に上昇します。

./build/tools/caffe train -solver /path/to/solver.prototxt -weights /path/to/bvlc_googlenet.caffemodel

#

6. ロスの可視化#

実行します:

script loss_acc.txt

./build/tools/caffe train --solver=/path/to/solver.prototxt

トレーニングが終了したら、ctrl+d でスクリーン録画を終了してください。

以下は可視化用の matlab コードで、このコードは『深層学習:21 日間の実践 Caffe』の学習曲線可視化方法を参考にして修正したものです。

clear;
clc;
close all;

train_log_file = 'loss_acc.txt';
display=20;  %solverの値に対応
test_interval=20;  %同上

[~, string_output] = dos(['cat ', train_log_file, ' | grep "Train net output #0" | awk ''{print $11}''']);
train_loss = str2num(string_output);
n = 1:length(train_loss);
idx_train = (n-1)*display;

[~, string_output] = dos(['cat ', train_log_file, ' | grep "Test net output #1" | awk ''{print $11}''']);
test_loss = str2num(string_output);
m = 1:length(test_loss);
idx_test = (m - 1)*test_interval;
figure;plot(idx_train, train_loss);
hold on;
plot(idx_test, test_loss,'--r');

grid on;
legend('トレインロス', 'テストロス');
xlabel('イテレーション');
ylabel('ロス');
title(' トレイン & テストロス曲線');

[~, acc_output] = dos(['cat ', train_log_file, ' | grep "Test net output #0" | awk ''{print $11}''']);
test_acc = str2num( acc_output);
k = 1:length(test_acc);
idx_acc = (k - 1)*test_interval;

figure;
plot(idx_acc, test_acc);

grid on;
box = legend('テスト精度');
set(box,'Location','southeast');
xlabel('イテレーション');
ylabel('精度');
title(' テスト精度曲線');

-------【2017.09.26】------ python の可視化コードを更新しましたが、digits が最も便利です ------------

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Tue Aug 29 10:05:13 2017

@author: hans

http://blog.csdn.net/renhanchi
"""
import matplotlib.pyplot as plt
import numpy as np
import commands
import argparse

parser = argparse.ArgumentParser()
parser.add_argument(
    '-p',
    type = str,
    default = '',
    help = """\
    ログファイルへのパス\
    """
)

FLAGS = parser.parse_args()

train_log_file = FLAGS.p

display = 10 #solver
test_interval = 100 #solver

train_output = commands.getoutput("cat " + train_log_file + " | grep 'Train net output #0' | awk '{print $11}'")  #トレインロス
test_output = commands.getoutput("cat " + train_log_file + " | grep 'Test net output #1' | awk '{print $11}'")  #テストロス
accu_output = commands.getoutput("cat " + train_log_file + " | grep 'Test net output #0' | awk '{print $11}'") #テスト精度
train_loss = train_output.split("\n")
test_loss = test_output.split("\n")
test_accu = accu_output.split("\n")
l = len(test_accu)

_,ax1 = plt.subplots()
ax2 = ax1.twinx()

l1, = ax1.plot(display*np.arange(len(train_loss)), train_loss)
l2, = ax1.plot(test_interval*np.arange(l), test_loss, 'g')
l3, = ax2.plot(test_interval*np.arange(l), test_accu, 'r')

ax1.set_xlabel('イテレーション')
ax1.set_ylabel('ロス')
ax2.set_ylabel('テスト精度')

plt.legend([l1, l2, l3], ['トレインロス', 'テストロス', 'テスト精度'], loc = 'upper left')
plt.show()

これらはすべて簡単ですが、最も難しいのはハイパーパラメータの調整です!!調整!!調整!!!

7. モデルのテスト#

実行します:

./build/tools/caffe test \

--model=/path/to/train_val_alexnet.prototxt \

--weights=path/to/xxxxxxxxxx.caffemodel

ここで注意が必要です。通常、このように実行すると val データセットでテストが行われます。独自のテストセットを準備した場合は、上記の手順に従って test_lmdb フォルダを生成し、train_val_alexnet.prototxt ファイル内で val_lmdb を test_lmdb に変更します。その後、同じコマンドを実行すれば大丈夫です。

8. classify.py を使用してモデルを呼び出して分類#

参照してください 【Caffe】【シーン分類】Places365 のインストール、docker の実行、およびローカル caffe の実行(Ubuntu14.04)

132 行目の labels.txt のパスを変更するだけです。

しかし!!!注意!!!!

コマンド内の --model_def を直接 train_val_alexnet.prototxt に選択することはできません。エラーが発生します。

新しいファイルを作成し、train_val_alexnet.prototxt と同じ内容のファイルをdeploy_alexnet.prototxtという名前で作成します。

次に、data 層、accuracy 層を削除し、loss 層を変更します。具体的には以下の通りです。

name: "AlexNet"
input: "data"
input_dim: 1
input_dim: 3
input_dim: 227
input_dim: 227
layer {
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  convolution_param {
    num_output: 96
    kernel_size: 11
    stride: 4
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}

...

...

...

layer {
  name: "fc8"
  type: "InnerProduct"
  bottom: "fc7"
  top: "fc8"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  inner_product_param {
    num_output: 10
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layer {
  name: "prob"
  type: "Softmax"
  bottom: "fc8"
  top: "prob"
}

変更が完了したら、実行します:

cd $ROOT/caffe/python

python classify.py /path/to/image.jpg result.npy \

--model_def  /path/to/deploy_alexnet.prototxt

--pretrained_model /path/to/xxxxxx.caffemodel

もちろん、皆さんも classify.py の最初の model_def と pretrained_model のデフォルト値を変更することで、実行コマンドを簡略化できます。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。