论文名称:Densely Connected Convolutional Networks(CVPR 2017, Best Paper Award)
论文链接:https://arxiv.org/pdf/1608.06993.pdf
源码链接:https://github.com/liuzhuang13/DenseNet
caffe版源码: https://github.com/liuzhuang13/DenseNetCaffe
cifar10链接 : http://www.cs.toronto.edu/~kriz/cifar.html
到GitHub下载Densenet网络文件
make_densenet.py:python文件用于生产train_densenet.prototxt文件、test_densenet.prototxt文件、solver.prototxt文件。
solver.prototxt: caffe: 超参数文件
train_densenet.prototxt:训练用到的网络文件
test_densenet.prototxt:验证用到的网络文件
train.sh: Linux下运行文件(windows下用不到)
一共有三个版本,其他版本网络上介绍都有很多,这里主要是举例用bin版本,也就是提供给c语言使用的版本。
caffe有带有对cifar数据处理的程序,这里介绍的用OpenCV+vs2013保存图片。
#include
#include
#include "opencv2/highgui/highgui.hpp"
#include "opencv/cv.h"
using namespace std;
int main()
{
unsigned char FileData[3073];
FILE* fp;
unsigned char* p;
unsigned char* p2;
int i;
int j;
fopen_s(&fp, "../cifar-10-batches-bin/data_batch_1.bin", "rb");
if (NULL == fp){
printf("Read File Err!");}
for (int k = 0; k < 10000;k++){
cv::Mat img(32, 32, CV_8UC3, cv::Scalar(0, 255, 255));
fread(&FileData, 1, 3073, fp);
p = img.ptr<uchar>(0);
p2 = &FileData[1 + 0 * 3073];
for (i = 0; i < 32; i++)
{
for (j = 0; j < 32; j++)
{
*p++ = p2[i * 32 + j + 2 * 32 * 32];
*p++ = p2[i * 32 + j + 1 * 32 * 32];
*p++ = p2[i * 32 + j + 0 * 32 * 32];
}
}
//printf(" %d ", FileData[0 * 3073]);
char buff[256];
sprintf_s(buff, "../ciffarimg/%d/%d.jpg", FileData[0], k);
cv::imwrite(buff, img);
img.release();
}
fclose(fp);
fopen_s(&fp, "../cifar-10-batches-bin/data_batch_2.bin", "rb");
if (NULL == fp)
{
printf("Read File Err!");
}
for (int k = 0; k < 10000; k++)
{
cv::Mat img(32, 32, CV_8UC3, cv::Scalar(0, 255, 255));
fread(&FileData, 1, 3073, fp);
p = img.ptr<uchar>(0);
p2 = &FileData[1 + 0 * 3073];
for (i = 0; i < 32; i++)
{
for (j = 0; j < 32; j++)
{
*p++ = p2[i * 32 + j + 2 * 32 * 32];
*p++ = p2[i * 32 + j + 1 * 32 * 32];
*p++ = p2[i * 32 + j + 0 * 32 * 32];
}
}
//printf(" %d ", FileData[0 * 3073]);
sprintf_s(buff, "../ciffarimg/%d/%d.jpg", FileData[0], 10000 + k);
cv::imwrite(buff, img);
img.release();
}
fclose(fp);
fopen_s(&fp, "../cifar-10-batches-bin/data_batch_3.bin", "rb");
if (NULL == fp){printf("Read File Err!");}
for (int k = 0; k < 10000; k++){
cv::Mat img(32, 32, CV_8UC3, cv::Scalar(0, 255, 255));
fread(&FileData, 1, 3073, fp);
p = img.ptr<uchar>(0);
p2 = &FileData[1 + 0 * 3073];
for (i = 0; i < 32; i++){
for (j = 0; j < 32; j++){
*p++ = p2[i * 32 + j + 2 * 32 * 32];
*p++ = p2[i * 32 + j + 1 * 32 * 32];
*p++ = p2[i * 32 + j + 0 * 32 * 32];
}
}
char buff[256];
sprintf_s(buff, "../ciffarimg/%d/%d.jpg", FileData[0], 20000 + k);
cv::imwrite(buff, img);
img.release();
}
fclose(fp);
fopen_s(&fp, "../cifar-10-batches-bin/data_batch_4.bin", "rb");
if (NULL == fp){printf("Read File Err!");}
for (int k = 0; k < 10000; k++){
cv::Mat img(32, 32, CV_8UC3, cv::Scalar(0, 255, 255));
fread(&FileData, 1, 3073, fp);
p = img.ptr<uchar>(0);
p2 = &FileData[1 + 0 * 3073];
for (i = 0; i < 32; i++){
for (j = 0; j < 32; j++){
*p++ = p2[i * 32 + j + 2 * 32 * 32];
*p++ = p2[i * 32 + j + 1 * 32 * 32];
*p++ = p2[i * 32 + j + 0 * 32 * 32];
}
}
char buff[256];
sprintf_s(buff, "../ciffarimg/%d/%d.jpg", FileData[0], 30000 + k);
cv::imwrite(buff, img);
img.release();
}
fclose(fp);
fopen_s(&fp, "../cifar-10-batches-bin/data_batch_5.bin", "rb");
if (NULL == fp){printf("Read File Err!");}
for (int k = 0; k < 10000; k++){
cv::Mat img(32, 32, CV_8UC3, cv::Scalar(0, 255, 255));
fread(&FileData, 1, 3073, fp);
p = img.ptr<uchar>(0);
p2 = &FileData[1 + 0 * 3073];
for (i = 0; i < 32; i++){
for (j = 0; j < 32; j++){
*p++ = p2[i * 32 + j + 2 * 32 * 32];
*p++ = p2[i * 32 + j + 1 * 32 * 32];
*p++ = p2[i * 32 + j + 0 * 32 * 32];
}
}
char buff[256];
sprintf_s(buff, "../ciffarimg/%d/%d.jpg", FileData[0], 40000 + k);
cv::imwrite(buff, img);
img.release();
}
fclose(fp);
fopen_s(&fp, "../cifar-10-batches-bin/test_batch.bin", "rb");
if (NULL == fp){printf("Read File Err!");}
for (int k = 0; k < 10000; k++){
cv::Mat img(32, 32, CV_8UC3, cv::Scalar(0, 255, 255));
fread(&FileData, 1, 3073, fp);
p = img.ptr<uchar>(0);
p2 = &FileData[1 + 0 * 3073];
for (i = 0; i < 32; i++){
for (j = 0; j < 32; j++){
*p++ = p2[i * 32 + j + 2 * 32 * 32];
*p++ = p2[i * 32 + j + 1 * 32 * 32];
*p++ = p2[i * 32 + j + 0 * 32 * 32];
}
}
char buff[256];
sprintf_s(buff, "../test/%d/%d.jpg", FileData[0], 20000 + k);
cv::imwrite(buff, img);
img.release();
}
fclose(fp);
return 0;
}
需要注意的是文件的位置和保存的文件夹是写死的,可以自己修改地址,如果用源码的话要在相应位置创建文档
解压完了之后会有两个文件夹
对应有10个文件夹(对应官网上的10个类)
首先是制作对应的train.txt文件、val.txt文件,这里可以写一个c语言程序自动读取。
然后新建一个txt文件把txt文件改为create_lmdb.bat并添加以下代码
G:\Project\caffe-master\Build\x64\Release\convert_imageset.exe --shuffle --resize_width=32 --resize_height=32 G:/Project/xxx/xxx/caffar-10-atches/train/ train.txt train_lmdb -backend=lmdb
G:\Project\caffe-master\Build\x64\Release\convert_imageset.exe --shuffle --resize_width=32 --resize_height=32 G:/Project/xxx/xxx/caffar-10-atches/val/ val.txt val_lmdb -backend=lmdb
pause
双机运行即可
这里需要注意shuffle是打乱的操作,如果图片是灰度图的话要加上–gray,在运行bat是就能看见是否添加。如果存在lmdb文件夹需要删除,在运行程序。resize_width = 32 resize_height = 32: 是图片缩放到32*32
运行成功后,对应的文件夹里会生成data和lock两个文件,注意如果大小是1024kb的话,就说明生成失败
然后就是制作mean文件,同样在当前文件下创建一个bat文件,加入以下代码。
G:\Project\caffe-master\Build\x64\Release\compute_image_mean.exe G:\Project\xxx\xxx\caffar-10-atches\train_lmdb G:\Project\xxx\xxx\caffar-10-atches\mean.binaryproto
pause
生成mean文件后可以将两个lmdb文件夹和均值文件夹复制到DenseNet文件所在文件夹下,笔者是放在G:\Project\caffe-master\examples\DenseNetCaffe-master下
这里需要注意的是有需要修改的是mean文件地址,
source也就是lmdb地址,
batch_size:这个要根据你GPU的显存修改,最佳是50。
这里需要修改主要是应为,上述修改了验证的batch_size
需要保证test_iter*batch_size = 验证图片张数,这里的batch是指test里的batch
跟上述流程一样生成bat文件并加入代码
SET GLOG_logtostderr=1
G:\Project\caffe-master\Build\x64\Release\caffe.exe train --solver G:\Project\caffe-master\examples\DenseNetCaffe-master\solver.prototxt
pause
然后双机运行就开始漫长的训练时间了。
首先可以使用caffe.exe test 测试100个批次的测试集情况,用bat文件测试
SET GLOG_logtostderr=1
G:\Project\caffe-master\Build\x64\Release\caffe.exe test -model=G:\Project\caffe-master\examples\DenseNetCaffe-master\test_densenet.prototxt -weights=G:\Project\caffe-master\examples\DenseNetCaffe-master\_iter_230000.caffemodel -iterations=100
pause
测试完了就可以开始VS2013工程测试了,首先要做的就是制作Deploy.prototxt文件,简单说来就是把test_densenet.prototxt文件来做修改。
1、复制test_densenet.prototxt文件,并改名为densenet.prototxt
2、删除头和尾,即data层和accuracy层
3、在文件最开始增加Input层、这里注意
第一个dim:对待识别样本进行数据增广的数量,这里设置为1是不进行数据增广的数量。
第二个dim:图片通道数
第三、四个dim:图片的长和宽和训练时一致
4、修改Input层后面接着的Convolution层(上到下第二个层)。
这里由于Input层name为data所以需要改名为data。
5、修改SoftmaxWithLoss层(文件最后一个layer)
由于最后不是计算SoftmaxWithLoss所以需要改为SoftMax,同时要除去
bottom:”data2“
到这里就制作完成了densenet.prototxt文件了就可以开始C++测试程序制作了。
1、建立程序,由于前面有文章介绍过lenet测试程序的制作,这里不再介绍(这里是直接复制lenet测试程序来修改的)
2、将mean.binaryproto、_iter_230000.caffemodel、_iter_230000.solverstate、densenet.prototxt 四个文件复制到程序bin目录下,并修改mian函数里面导入的数据
然后根据官网提供的cifar数据排列修改lable_fikle.txt
即可开始测试效果。
这里需要注意的是由于是复制lenet程序的网络所以在头文件了没有加相应的layer如果没有添加运行会报错误
然后就能得到预期的测试效果
1、训练的num_output和验证的num_output不一致导致的
2、显存不足,这个需要减小batch_size来解决,最低可以验证和训练都设置为1
3、训练样本lmdb文件未正确生成导致的问题,重新生成lmdb文件
4、acc一直为1 ,loss 会持续降低,原因是由于 lmdb顺序未打散,生成时加入shuffle就可以,或者是学习率base_lr太高问题,降低即可