Caffe学习系列(八):Caffe-SSD及其轻量化模型
Mobilenet、Shufflenet重点参考:轻量化神经网络综述
Squeezenet重点参考:纵览轻量化卷积神经网络:SqueezeNet、MobileNet、ShuffleNet、Xception
模型实现集锦1
模型实现集锦2
1. Caffe-SSD
1.1 SSD 原理
1.2 SSD 训练自己的数据集
1.数据集准备
2.官方网络模型下载
下载的官方模型文件夹名称为VOC0712,放入ssd-caffe/models/VGGNet/目录下,如下图所示。
3.修改 examples/ssd/ssd_pascal.py 文件
# 第81--84行
# The database file for training data. Created by data/VOC0712/create_data.sh
train_data = "/home/hitwh/workspace/data/VOCdevkit/cucumber/lmdb/cucumber_trainval_lmdb"
# The database file for testing data. Created by data/VOC0712/create_data.sh
test_data = "/home/hitwh/workspace/data/VOCdevkit/cucumber/lmdb/cucumber_test_lmdb"
# 第234--266行
# Modify the job name if you want.
job_name = "SSD_{}".format(resize)
# The name of the model. Modify it if you want.
model_name = "VGG_VOC0712_{}".format(job_name)
# Directory which stores the model .prototxt file.
save_dir = "models/VGGNet/VOC0712/{}".format(job_name)
# Directory which stores the snapshot of models.
snapshot_dir = "models/VGGNet/VOC0712/{}".format(job_name)
# Directory which stores the job script and log file.
job_dir = "jobs/VGGNet/VOC0712/{}".format(job_name)
# Directory which stores the detection results.
output_result_dir = "{}/data/VOCdevkit/results/VOC2007/{}/Main".format(os.environ['HOME'], job_name)
# model definition files.
train_net_file = "/home/hitwh/workspace/Mobilenet-ssd/caffe/models/VGGNet/VOC0712/SSD_300x300/train.prototxt".format(save_dir)
test_net_file = "/home/hitwh/workspace/Mobilenet-ssd/caffe/models/VGGNet/VOC0712/SSD_300x300//test.prototxt".format(save_dir)
deploy_net_file = "/home/hitwh/workspace/Mobilenet-ssd/caffe/models/VGGNet/VOC0712/SSD_300x300//deploy.prototxt".format(save_dir)
solver_file = "/home/hitwh/workspace/Mobilenet-ssd/caffe/models/VGGNet/VOC0712/SSD_300x300//solver.prototxt".format(save_dir)
# snapshot prefix.
snapshot_prefix = "{}/{}".format(snapshot_dir, model_name)
# job script path.
job_file = "{}/{}.sh".format(job_dir, model_name)
# Stores the test image names and sizes. Created by data/VOC0712/create_list.sh
name_size_file = "data/cucumber/test_name_size.txt"
# The pretrained model. We use the Fully convolutional reduced (atrous) VGGNet.
# pretrain_model = "models/VGGNet/VGG_ILSVRC_16_layers_fc_reduced.caffemodel"
# Stores LabelMapItem.
label_map_file = "data/cucumber/labelmap_voc_cucumber.prototxt"
# MultiBoxLoss parameters.
num_classes = 2
# 第330--338行
# Solver parameters.
# Defining which GPUs to use.
gpus = "0" # 只有一个GPU
gpulist = gpus.split(",")
num_gpus = len(gpulist)
# Divide the mini-batch to different GPUs.
batch_size = 8 # 减小内存占用
accum_batch_size = 8
# 第358--360行
# Evaluate on whole test set.
num_test_image = 656 # 测试集图片数
test_batch_size = 2
训练若对网络模型进行了修改,则不能使用预训练模型,需注释掉以下内容:
# 第 261 行
# The pretrained model. We use the Fully convolutional reduced (atrous) VGGNet.
# pretrain_model = "models/VGGNet/VGG_ILSVRC_16_layers_fc_reduced.caffemodel"
# 第 424 行
# check_if_exist(pretrain_model)
# 第 536 行
# train_src_param = '--weights="{}" \\\n'.format(pretrain_model)
# 第 560 行
# f.write(train_src_param)
1.3 网络模型修改
VGG的网络模型由 ssd_pascal.py 文件生成,可生成 VGG,ZF,Resnet101 和 Resnet152 四种网络。
# Create train net. # 生成训练网络 往下还有 test net 和 deploy net
net = caffe.NetSpec()
net.data, net.label = CreateAnnotatedDataLayer(train_data, batch_size=batch_size_per_device,
train=True, output_label=True, label_map_file=label_map_file,
transform_param=train_transform_param, batch_sampler=batch_sampler)
VGGNetBody(net, from_layer='data', fully_conv=True, reduced=True, dilated=True,
dropout=False) # 生成 VGGNet
AddExtraLayers(net, use_batchnorm, lr_mult=lr_mult)
mbox_layers = CreateMultiBoxHead(net, data_layer='data', from_layers=mbox_source_layers,
use_batchnorm=use_batchnorm, min_sizes=min_sizes, max_sizes=max_sizes,
aspect_ratios=aspect_ratios, steps=steps, normalizations=normalizations,
num_classes=num_classes, share_location=share_location, flip=flip, clip=clip,
prior_variance=prior_variance, kernel_size=3, pad=1, lr_mult=lr_mult)
VGGNetBody 函数在 caffe/python/caffe/model_libs.py 文件中定义,此外,还定义了其他网络,因此若要修改VGGNet网络模型,需修改model_libs.py 文件。
def VGGNetBody(net, from_layer, need_fc=True, fully_conv=False, reduced=False,
dilated=False, nopool=False, dropout=True, freeze_layers=[], dilate_pool4=False):
kwargs = {
'param': [dict(lr_mult=1, decay_mult=1), dict(lr_mult=2, decay_mult=0)],
'weight_filler': dict(type='xavier'),
'bias_filler': dict(type='constant', value=0)}
assert from_layer in net.keys()
net.conv1_1 = L.Convolution(net[from_layer], num_output=64, pad=1, kernel_size=3, **kwargs)
net.relu1_1 = L.ReLU(net.conv1_1, in_place=True)
net.conv1_2 = L.Convolution(net.relu1_1, num_output=64, pad=1, kernel_size=3, **kwargs)
net.relu1_2 = L.ReLU(net.conv1_2, in_place=True)
1.3 结果检测
运行以下命令,其中将末尾的路径设置好即可
python examples/ssd/ssd_detect.py
但不知为何,检测字体如下图一样low
为了解决该问题,博主参考了这篇博客,将检测结果整理成 results.txt 文件如下:
examples/images/cat.jpg 8 0.999429 169 26 347 356
examples/images/cropped_panda.jpg 12 0.975958 0 1 95 97
examples/images/fish-bike.jpg 2 0.717551 52 81 448 307
examples/images/fish-bike.jpg 15 0.99994 204 3 344 170
然后在 caffe 目录下运行以下命令调用 plot_detections.py 文件画图,即可在 caffe/examples 目录下看到完美版效果了
python examples/ssd/plot_detections.py examples/images/result.txt /home/mx/caffe --labelmap-file data/VOC0712/labelmap_voc.prototxt --save-dir examples/
针对本文自己的数据集,使用以下命令
python examples/ssd/plot_detections.py examples/images/results.txt /home/hitwh/workspace/Mobilenet-ssd/caffe --labelmap-file data/cucumberA/labelmap_voc_cucumber.prototxt --save-dir examples/images/
1.2 SSD 结果可视化
Caffe-SSD:可视化(绘制loss,accuracy曲线)
切换至 Caffe/jobsVGGNet/VOC0712/SSD_300x300/ 目录下,可看到生成的 VGG_VOC0712_SSD_300x300.log 日志文件,运行一下命令可绘制训练曲线
python plot_training_log.py 6 trainloss.png VGG_VOC0712_SSD_300x300.log
运行以下命令可绘制训练精度曲线
python plot_training_log.py 0 testloss.png VGG_VOC0712_SSD_300x300.log
2. Mobilenet-SSD
目标检测:Mobilenet-SSD实现步骤
意味着TX1上Mobilenet-SSD能达到17帧左右,这离真正的real-time又近了一步
2.1 MobileNet-SSD 网路修改
这一步是由以下命令自动完成,不需要人为改动,但可通过上述原理了解改动原理
# usage: ./gen_model.sh CLASSNUM
./gen_model.sh 2
2.2 合并bn层
运行以下命令,得到 no_bn.prototxt 和 no_bn.caffemodel
python2 merge_bn.py --model ./example/defults/MobileNetSSD_deploy.prototxt --weights ./snapshot/defults-A/mobilenet_iter_14000.caffemodel
2.3 Mobilenet-ssdlite训练
caffe-SSD配置及用caffe-MobileNet-SSD训练自己的数据集
MobileNetv2-SSDLite训练自己的数据集
3. SqueezeNet-SSD
3.1 模型下载
git clone https://github.com/chuanqi305/SqueezeNet-SSD
将模型放在 Mobilenet-ssd 目录下,使用mobilenet-ssd 的 train.sh test.sh 和 demo.py 进行训练和测试。下述 shufflenet 同样进行此操作。
3.2 网络修改
除了修改类别/路径,还需修改最后几层的参数
fire5 | fire9 | fire10 | fire11 | fire12_2 | fire13_2 |
---|---|---|---|---|---|
16 | 24 | 24 | 24 | 24 | 16 |
8 | 12 | 12 | 12 | 12 | 8 |
代码如下:
layer {
name: "fire11_mbox_conf"
type: "Convolution"
bottom: "fire11/concat"
top: "fire11_mbox_conf"
param {
lr_mult: 1.0
decay_mult: 1.0
}
param {
lr_mult: 2.0
decay_mult: 0.0
}
convolution_param {
num_output: 12 # 修改此处
pad: 1
kernel_size: 3
stride: 1
weight_filler {
type: "msra"
}
bias_filler {
type: "constant"
value: 0.0
}
}
}
laye
此外, test.prototxt 和 deploy.prototxt 的 reshape 也要根据类别修改
layer {
name: "mbox_conf_reshape"
type: "Reshape"
bottom: "mbox_conf"
top: "mbox_conf_reshape"
reshape_param {
shape {
dim: 0
dim: -1
dim: 2 # 修改此处
}
}
}
3.3 训练报错
网络修改报错:
Check failed: 0 == bottom[0]->count() % explicit_count (0 vs. 128) bottom count (128480) must be divisible by the product of the specified dimensions (168)
解决办法:Check failed: 0 == bottom[0]->count() % explicit_count (0 vs. 76) bottom count (160600) must be divi
4. ShuffleNet-SSD
4.1 caffe 源码修改
1.ShuffleNet-SSD需要修改 caffe 源码,添加网络层,首先下载 shuffle 层文件
git clone https://github.com/farmingyard/ShuffleNet
将下载后的 shuffle_channel_layer.hpp .cpp .cu 文件放在 caffe/src/caffe/layers/ 目录下
2.修改 caffe.proto 文件
在caffe 目录下搜索 caffe.proto 文件并打开,定位到 441 行 找到 message LayerParameter 函数,添加网络层,修改如下:
message LayerParameter {
... ...
// ShuffleNet
optional ShuffleChannelParameter shuffle_channel_param = 164;
}
message ShuffleChannelParameter {
optional uint32 group = 1[default = 1]; // The number of group
}
3.重新编译
make clean
make all -j16
make pycaffe
make test
make runtest
4.2 网络修改
1.路径与尾层参数修改和 squeezenet 类似,注意 test.prototxt 和 deploy.prototxt文件的类别数修改
conv13 | conv17 | conv18_2 | conv19_2 | conv20_2 | conv21_2 |
---|---|---|---|---|---|
16 | 24 | 24 | 24 | 24 | 24 |
8 | 12 | 12 | 12 | 12 | 12 |
2.ConvolutionDepthwise层的修改
shufflenet 中还引入了“ConvolutionDepthwise”层,该层可修改为“Convolution”层,并在参数内添加 group 机制即可,数目与 num_output 一致。
ConvolutionDepthwise 主要位于在“convX/b”层中,其中 X 表示层2,3,4…。
layer {
name: "conv17/b"
type: "Convolution" # ConvolutionDepthwise 修改为 Convolution
bottom: "conv17/ChnlShf"
top: "conv17/b"
param {
lr_mult: 1.0
decay_mult: 1.0
}
convolution_param {
num_output: 240
bias_term: false
pad: 1
kernel_size: 3
stride: 1
group: 240 # 添加 group 机制
weight_filler {
type: "msra"
}
}
}
5. 模型P-R曲线
主要参考SSD算法评估
5.1 源码修改
修改四个文件: solver.cpp,caffe.prototxt,bbox_util.hpp和bbox_util.cpp
修改后的文件在这里
5.2 运行测试
在 solver_test.prototxt 文件中添加
show_pr_value: true
show_per_class_result: true
在 Mobilinet-ssd 目录下运行以下命令
sh test.sh
结果如下:
6. Tiny-SSD
深度学习【25】物体检测:Tiny SSD
Tiny-SSD : 使用Squeeze-Net 替换 VGG-16