1.安装Caffe
按照官方https://github.com/BVLC/caffe说明进行编译,若使用了较老的cudnn版本,则要在老版本的caffe与新版本的caffe进行合并,具体是将cudnn开头的相关文件替换掉旧版本caffe里的。
2.整理自己的数据
图像文件可以放置在硬盘的任何位置,将训练的图像及label生成一个train.txt文件,然后验证图像和label生成一个val.txt,一起放到caffe/data/mydata文件夹下。
path/to/image/001.jpg 0
path/to/image/002.jpg 1
path/to/image/003.jpg 2
在caffe/examples/imagenet中,将创建lmdb文件和mean文件的的几个sh文件拷贝到自己的examples/mytask中,然后修改这两个文件中的几个地址和名称:
修改create_imagenet.sh
//当前路径
EXAMPLE=/home/xxx/caffe/examples/mytask
//存放train.txt和val.txt的路径,就是data/mydata
DATA=/home/xxx/caffe/data/mydata
//不变
TOOLS=/home/xxx/caffe/build/tools
//存放train和val图片文件夹的主目录(这里与txt文件中的目录合起来才是图片目录)
TRAIN_DATA_ROOT=/home/xxx/project-data/
VAL_DATA_ROOT=/home/xxx/project-data/
GLOG_logtostderr=1 $TOOLS/convert_imageset \
--resize_height=$RESIZE_HEIGHT \
--resize_width=$RESIZE_WIDTH \
--shuffle \
$TRAIN_DATA_ROOT \
$DATA/train.txt \
$EXAMPLE/train_lmdb ##修改名称,与prototxt中的对应
echo "Creating val lmdb..."
GLOG_logtostderr=1 $TOOLS/convert_imageset \
--resize_height=$RESIZE_HEIGHT \
--resize_width=$RESIZE_WIDTH \
--shuffle \
$VAL_DATA_ROOT \
$DATA/val.txt \
$EXAMPLE/val_lmdb ##修改名称
同理,修改make_imagenet_mean.sh。
分别执行这两个命令,创建文件lmdb数据库和均值mean文件。
3.设置模型
在caffe/models下有很多预制的模型可供使用,模型文件一共可包括至少三个文件:deploy.prototxt;solver.prototxt;train_val.prototxt,有时需要finetune已训练好的模型,还需要一些比如在imagenet上预训练的caffemodel文件。
sovler里面包含超参数和一些信息:
net: "models/bvlc_googlenet/train_val.prototxt"
test_iter: 1000
test_interval: 4000
test_initialization: false
display: 40
average_loss: 40
base_lr: 0.01
lr_policy: "step"
stepsize: 320000
gamma: 0.96
max_iter: 10000000
momentum: 0.9
weight_decay: 0.0002
snapshot: 40000
snapshot_prefix: "models/bvlc_googlenet/bvlc_googlenet"
solver_mode: GPU
train_val里面的主要修改的地方是batch_size,它与sovler里面的test_iter相乘的结果对应训练图像的总数;修改train和val的sorce文件地址:train_lmdb,val_lmdb.
data_param {
source: "examples/imagenet/ilsvrc12_train_lmdb"
batch_size: 32
backend: LMDB
}
PS: 若使用lmdb的话,type为Data,使用图像的话就是ImageData
layer {
name: "data"
type: "Data"
top: "data"
top: "label"
include {
phase: TRAIN
}
PS: 在finetune残差网络resnet时,网络结构文件中BatchNorm层的参数要注意:
1.在训练时所有BN层要设置use_global_stats: false(也可以不写,caffe默认是false)
2.在测试时所有BN层要设置use_global_stats: true
#1.训练如果不设为false,会导致模型不收敛
#2.测试如果不设置为true,会导致准确率极低
4.开始训练
#!/bin/bash
#!/usr/bin/env sh
PRETRAINED_MODEL=./models/resnet50/ResNet-50-model.caffemodel
t=$(date +%Y-%m-%d_%H:%M:%S)
LOG=./log/train_res50_$t.log
GLOG_logtostderr=1 ./build/tools/caffe train \
--solver=./models/resnet50/solver.prototxt \
--weights=$PRETRAINED_MODEL \
--gpu=0,1 2>&1 | tee $LOG
训练时若出现内存不足停止,要减小train_val.prototxt中batchsize的大小。
若出现waiting for data,可能是硬盘读写速度慢,那就waiting吧。。。
5.测试
#!/bin/bash
#!/usr/bin/env sh
GLOG_logtostderr=1 ./build/tools/caffe test \
--model=./models/resnet50/ResNet-50-train-val.prototxt \
--weights=./models/resnet50/output_iter_100000.caffemodel \
--gpu=1 \
--iterations=2000
6.分类demo
在caffe/python下的classify.py文件可以用来对图像进行分类,但是没有输出可见的分类结果,需要改写些内容。开始之前需要把mean均值文件转换为.npy格式,代码如下:
import caffe
import numpy as np
import sys
if len(sys.argv) != 3:
print "Usage: python convert_protomean.py proto.mean out.npy"
sys.exit()
blob = caffe.proto.caffe_pb2.BlobProto()
data = open( sys.argv[1] , 'rb' ).read()
blob.ParseFromString(data)
arr = np.array( caffe.io.blobproto_to_array(blob) )
out = arr[0]
np.save( sys.argv[2] , out )
自己改写的classify-demo.py
import numpy as np
import sys
import os
import caffe
import io
#from sys import argv
#input filefolder you want to test
#tools,filefolder=argv
caffe.set_device(0)
caffe.set_mode_gpu()
MODEL_FILE ="../models/googlenet/deploy.prototxt"
PRETRAINED = "../models/googlenet/output/bvlc_googlenet_iter_200000.caffemodel"
MEAN = "mean.npy"
net = caffe.Classifier(MODEL_FILE, PRETRAINED, mean = np.load(MEAN).mean(1).mean(1),
channel_swap=(2,1,0),
raw_scale=255,
image_dims=(256, 256))
filewriter = open("../data/mydata/result.txt","w+")
labels_file = '../data/mydata/synset_words.txt'
labels = np.loadtxt(labels_file, str, delimiter='\t')
#load groundtruth txt to dictionary
groundtruth_file = '../data/mydata/groundtruth/abc.txt'
dict_data = {}
with open(groundtruth_file, 'r') as df:
for kv in [d.strip().split(' ') for d in df]:
dict_data[kv[0]] = kv[1]
for root,dirs,files in os.walk("/home/xxx/project_data/mydata/abc/"): # all the images are in test folder
for file in files:
IMAGE_FILE = os.path.join(root,file).decode('gbk').encode('utf8')
#print IMAGE_FILE
input_image = caffe.io.load_image(IMAGE_FILE)
prediction = net.predict([input_image])
string = os.path.basename(IMAGE_FILE)+" "+str(prediction[0].argmax())+"\n"
filewriter.write(string)
#print os.path.basename(IMAGE_FILE), prediction[0].argmax()
# load ImageNet labels
label_true=dict_data[os.path.basename(IMAGE_FILE)]
if label_true==str(prediction[0].argmax()):
print os.path.basename(IMAGE_FILE)+" "+labels[prediction[0].argmax()]+' '+'v'
else:
print os.path.basename(IMAGE_FILE)+" "+labels[prediction[0].argmax()]+' '+'x'
filewriter.close()
7.plot训练测试曲线
caffe/tools/extra下包含几个有用的工具,其中包括将日志文件.log绘制出训练阶段的loss变化和测试阶段accuracy的变化情况,具体:
./parse_log.sh xxxx.log #解析日志,划分为train test两个文件
./plot_training_log.py.example 0~7 save.png xxxx.log #绘制图
Notes:
1. Supporting multiple logs.
2. Log file name must end with the lower-cased ".log".
Supported chart types:
0: Test accuracy vs. Iters
1: Test accuracy vs. Seconds
2: Test loss vs. Iters
3: Test loss vs. Seconds
4: Train learning rate vs. Iters
5: Train learning rate vs. Seconds
6: Train loss vs. Iters
7: Train loss vs. Seconds