深度学习Caffe实战笔记(6)Windows caffe平台用Siamese网络跑自己的数据
2017-01-06 09:16
946 查看
终于到了介绍如何使用Siamese网络跑自己的数据了,在网上、论坛上、群里关于用Siamese网络的资料很多,但是实战的资料很少,难道是因为太容易了吗?反正博主查阅了各种地方,几乎没有找到Siamese网络实战的东东,即使有零星关于实战的东西,那也是基于Ubuntu系统,殊不知Ubuntu系统跑caffe可要比Windows简单的多了,所以,就本博主的调研情况来看,这篇博客绝对称的上是Windows平台使用Siamese网络跑自己的数据的第一篇详细资料!这一篇介绍如何利用Windows caffe Siamese网络跑自己的数据,下一篇打算介绍如何调整和搭建网络结构。。。。
开始train。(补充一句,这个博客是博主刚开始学习写的,有很多不足的地方,评论区有一高人指出了几个不足之处,望大家在训练的时候注意那几个地方)
1、准备数据
我们知道Siamese网络是要输入一个图像对,这个图像对是一对image pair,所以首先要把图像数据放到文件夹中,然后建立一个索引文件,索引文件的每一行是两个图像名,代表一个图像对。样式如下:
2、转换数据
转换数据需要转换数据的可执行文件,这个可执行文件哪里得到呢?哎,说到这里都是眼泪了,搞了好长时间,终于找到了相应的代码,不过这个代码有一个大坑,不知道当时写这个代码的人是没有注意,还是故意挖坑呢:
这个cpp文件里有一个大坑,下面再做介绍。在caffe-windows-master\build_cpu_only\同样的方法,把convert_imageset文件复制一下,然后把复制文件夹里的release删除,把剩下的三个文件重命名,然后把复制的这个工程用VS打开,把原来的cpp移除,把上面的代码作为cpp文件导进去,这个步骤的实现方法我就不截图了,前面的博客里都有详细的截图说明,不懂的去看前面的博客吧。
生成之后,在bin目录下会有convert_imagese_siamese.exe可执行文件,这个文件就是用来数据转换的文件,在data文件夹下新建一个文件夹用来存放数据和可执行文件,写一个数据转换脚本文件:
分别转换训练集和验证集。
3、开始训练
有了数据集,就要训练了不是,写一个训练的脚本文件:
Siamese网络还是用的caffe自带的那个Siamese网络协议和超参文件,这两个文件中需要修改的地方在这里我不做介绍了,不懂的同学去看之前的博客吧。
这个时候就出现大坑了。
cv::resize错误,咋回事?起初我还以为是我的opencv有问题,所以在工程里我配置了一下opencv结果,还是不行,老方法,我就知道这个问题去问别人,别人也解答不了,也没人去解答,靠人不如靠自己啊!去一行行分析源码吧。。。。。。不看不知道,一看吓一跳啊,数据转换代码里有一个大坑。
修改方法:把定义中的0改成你要设置的图像大小即可。
再编译一次,把数据重新转换,可以训练了。
4、一个疑问*(已经解决)*
开始训练以后,我的会caffe会报一个错误,第一个卷积层的卷积核参数共享提示维度不匹配,一个是20*1*5*5,共享层是20*5*5*5,没办法只能把这个参数共享关了,不知道具体原因是什么,参数共享的这两个卷积层明明是一样的 ,如果有知道原因的同学还想请教一下,在此先谢过了。
把第一个参数共享关闭了之后,成功。
后来查看了Glog日志,发现问题在于Slice层数据分解的问题,至于Slice层的讲解在这个博客里介绍的比较详细
http://blog.csdn.net/u012235274/article/details/52438479
解决方法,就是在slice_point: 1去除,使得数据平均分配,就没有维度不匹配的问题了。
写在后面的话:
Siamese网络是两个lenet网络的合并,用ContrastiveLoss损失函数做图像对的训练,但是我觉得Siamese更多的是一种思想,所有挖掘双分支或者更多分支关系的网络都可以称为Siamese网络,而不仅仅局限于lenet。
开始train。(补充一句,这个博客是博主刚开始学习写的,有很多不足的地方,评论区有一高人指出了几个不足之处,望大家在训练的时候注意那几个地方)
1、准备数据
我们知道Siamese网络是要输入一个图像对,这个图像对是一对image pair,所以首先要把图像数据放到文件夹中,然后建立一个索引文件,索引文件的每一行是两个图像名,代表一个图像对。样式如下:
2、转换数据
转换数据需要转换数据的可执行文件,这个可执行文件哪里得到呢?哎,说到这里都是眼泪了,搞了好长时间,终于找到了相应的代码,不过这个代码有一个大坑,不知道当时写这个代码的人是没有注意,还是故意挖坑呢:
/* * convertImgToSiamese.cpp */ #include <algorithm> #include <fstream> #include <string> #include <cstdio> #include <utility> #include <vector> //#include <cstdlib> #include "boost/scoped_ptr.hpp" #include "gflags/gflags.h" #include "glog/logging.h" #include "leveldb/db.h" #include "caffe/proto/caffe.pb.h" #include "caffe/util/io.hpp" #include "caffe/util/rng.hpp" //#include "caffe/util/format.hpp" #include "caffe/util/math_functions.hpp" #include "opencv2/opencv.hpp" #include "google/protobuf/text_format.h" #include "stdint.h" #include <cstdio> #include <iostream> #include <cmath> using namespace caffe; using std::pair; using boost::scoped_ptr; using namespace cv; using namespace std; DEFINE_bool(gray, false, "when this option is on, treat images as grayscale ones"); DEFINE_bool(shuffle, false, "randomly shuffle the order of images and their labels"); DEFINE_string(backend, "leveldb", "the backend {lmdb, leveldb} for storing the result"); DEFINE_int32(resize_width, 0, "Width images are resized to"); DEFINE_int32(resize_height, 0, "Height images are resized to"); DEFINE_bool(check_size, false, "When this option is on, check that all the datum have the same size"); DEFINE_bool(encoded, false, "When this option is on, the encoded image will be save in datum"); DEFINE_string(encode_type, "", "Optional: What type should we encode the image as ('png','jpg',...)."); DEFINE_int32(channel, 3, "channel numbers of the image"); //1 //static bool ReadImageToMemory(const string &FileName, const int Height, const int Width, char *Pixels) //2 static bool ReadImageToMemory(const string &FileName, const int Height, const int Width, char *Pixels) { //read image //cv::Mat OriginImage = cv::imread(FileName, cv::IMREAD_GRAYSCALE); cv::Mat OriginImage = cv::imread(FileName); //3. read color image CHECK(OriginImage.data) << "Failed to read the image.\n"; //resize the image cv::Mat ResizeImage; cv::resize(OriginImage, ResizeImage, cv::Size(Width, Height)); CHECK(ResizeImage.rows == Height) << "The heighs of Image is no equal to the input height.\n"; CHECK(ResizeImage.cols == Width) << "The width of Image is no equal to the input width.\n"; CHECK(ResizeImage.channels() == 3) << "The channel of Image is no equal to three.\n"; //4. should output the warning here // LOG(INFO) << "height " << ResizeImage.rows << " "; //LOG(INFO) << "weidth " << ResizeImage.cols << " "; //LOG(INFO) << "channels " << ResizeImage.channels() << "\n"; // copy the image data to Pixels for (int HeightIndex = 0; HeightIndex < Height; ++HeightIndex) { const uchar* ptr = ResizeImage.ptr<uchar>(HeightIndex); int img_index = 0; for (int WidthIndex = 0; WidthIndex < Width; ++WidthIndex) { for (int ChannelIndex = 0; ChannelIndex < ResizeImage.channels(); ++ChannelIndex) { int datum_index = (ChannelIndex * Height + HeightIndex) * Width + WidthIndex; *(Pixels + datum_index) = static_cast<char>(ptr[img_index++]); } } } return true; } int main(int argc, char** argv) { //::google::InitGoogleLogging(argv[0]); #ifndef GFLAGS_GFLAGS_H_ namespace gflags = google; #endif gflags::SetUsageMessage("Convert a set of color images to the leveldb\n" "format used as input for Caffe.\n" "Usage:\n" " convert_imageset [FLAGS] ROOTFOLDER/ LISTFILE DB_NAME\n"); caffe::GlobalInit(&argc, &argv); // 输入参数不足时报错 if (argc < 4) { gflags::ShowUsageWithFlagsRestrict(argv[0], "tools/convert_imageset"); return 1; } // 读取图像名字和标签 std::ifstream infile(argv[2]); std::vector<std::pair<std::string, std::string> > lines; std::string filename; std::string pairname; int label; while (infile >> filename >> pairname) { lines.push_back(std::make_pair(filename, pairname)); } // 打乱图片顺序 if (FLAGS_shuffle) { // randomly shuffle data LOG(INFO) << "Shuffling data"; shuffle(lines.begin(), lines.end()); } LOG(INFO) << "A total of " << lines.size() << " images."; // 设置图像的高度和宽度 int resize_height = std::max<int>(0, FLAGS_resize_height); int resize_width = std::max<int>(0, FLAGS_resize_width); int channel = std::max<int>(1, FLAGS_channel); //5. add channel info // 打开数据库 // Open leveldb leveldb::DB* db; leveldb::Options options; options.create_if_missing = true; options.error_if_exists = true; leveldb::Status status = leveldb::DB::Open( options, argv[3], &db); CHECK(status.ok()) << "Failed to open leveldb " << argv[3] << ". Is it already existing?"; // 保存到leveldb // Storing to leveldb std::string root_folder(argv[1]); //char* Pixels = new char[2 * resize_height * resize_width]; char* Pixels = new char[2 * resize_height * resize_width * channel]; //6. add channel const int kMaxKeyLength = 10; //10 char key[kMaxKeyLength]; std::string value; caffe::Datum datum; //datum.set_channels(2); // one channel for each image in the pair datum.set_channels(2 * channel); //7. 3 channels for each image in the pair datum.set_height(resize_height); datum.set_width(resize_width); // // int line_size = (int)(lines.size()/2); // std::cout<<"number of lines: "<<line_size<<endl; for (int LineIndex = 0; LineIndex < lines.size(); LineIndex++) { //int PairIndex = LineIndex + line_size; // cout<<PairIndex<<endl; // int PairIndex = caffe::caffe_rng_rand() % lines.size(); char* FirstImagePixel = Pixels; // cout<<root_folder + lines[LineIndex].first<<endl; ReadImageToMemory(root_folder + lines[LineIndex].first, resize_height, resize_width, FirstImagePixel); //8. add channel here //char *SecondImagePixel = Pixels + resize_width * resize_height; char *SecondImagePixel = Pixels + resize_width * resize_height * channel; //10. add channel ReadImageToMemory(root_folder + lines[LineIndex].second, resize_height, resize_width, SecondImagePixel); //9. add channel here // set image pair data // datum.set_data(Pixels, 2 * resize_height * resize_width); datum.set_data(Pixels, 2 * resize_height * resize_width * channel); //11. correct // set label // for training, first 1000 pairs are true; for testing,first 1000 pairs are true // if (LineIndex<4000) //train: 912,3000 true pairs, 81,1080 false pairs; //test: 35600 true pairs, 33500 false pairs if (LineIndex<9123000) { datum.set_label(1); } else { datum.set_label(0); } // printf("first index: %d, second index: %d, labels: %d \n", lines[LineIndex].second, lines[PairIndex].second, datum.label()); // serialize datum to string datum.SerializeToString(&value); int key_value = (int)(LineIndex); _snprintf(key, kMaxKeyLength, "%08d", key_value); string keystr(key); cout << "label: " << datum.label() << ' ' << "key index: " << keystr << endl; //sprintf_s(key, kMaxKeyLength, "%08d", LineIndex); db->Put(leveldb::WriteOptions(), std::string(key), value); } delete db; delete[] Pixels; return 0; }
这个cpp文件里有一个大坑,下面再做介绍。在caffe-windows-master\build_cpu_only\同样的方法,把convert_imageset文件复制一下,然后把复制文件夹里的release删除,把剩下的三个文件重命名,然后把复制的这个工程用VS打开,把原来的cpp移除,把上面的代码作为cpp文件导进去,这个步骤的实现方法我就不截图了,前面的博客里都有详细的截图说明,不懂的去看前面的博客吧。
生成之后,在bin目录下会有convert_imagese_siamese.exe可执行文件,这个文件就是用来数据转换的文件,在data文件夹下新建一个文件夹用来存放数据和可执行文件,写一个数据转换脚本文件:
分别转换训练集和验证集。
3、开始训练
有了数据集,就要训练了不是,写一个训练的脚本文件:
Siamese网络还是用的caffe自带的那个Siamese网络协议和超参文件,这两个文件中需要修改的地方在这里我不做介绍了,不懂的同学去看之前的博客吧。
这个时候就出现大坑了。
cv::resize错误,咋回事?起初我还以为是我的opencv有问题,所以在工程里我配置了一下opencv结果,还是不行,老方法,我就知道这个问题去问别人,别人也解答不了,也没人去解答,靠人不如靠自己啊!去一行行分析源码吧。。。。。。不看不知道,一看吓一跳啊,数据转换代码里有一个大坑。
// 设置图像的高度和宽度 int resize_height = std::max<int>(0, FLAGS_resize_height); int resize_width = std::max<int>(0, FLAGS_resize_width);//要把数据转换成resize大小,转到FLAGS_resize_width和FLAGS_resize_height定义,发现是两个宏定义,这就是坑! ...... DEFINE_int32(resize_width, 0, "Width images are resized to"); DEFINE_int32(resize_height, 0, "Height images are resized to");//把要转化的大小设置成了0,要命了啊,怪不得会出现resize错误,都是0,当然会报错!
修改方法:把定义中的0改成你要设置的图像大小即可。
再编译一次,把数据重新转换,可以训练了。
4、一个疑问*(已经解决)*
开始训练以后,我的会caffe会报一个错误,第一个卷积层的卷积核参数共享提示维度不匹配,一个是20*1*5*5,共享层是20*5*5*5,没办法只能把这个参数共享关了,不知道具体原因是什么,参数共享的这两个卷积层明明是一样的 ,如果有知道原因的同学还想请教一下,在此先谢过了。
把第一个参数共享关闭了之后,成功。
后来查看了Glog日志,发现问题在于Slice层数据分解的问题,至于Slice层的讲解在这个博客里介绍的比较详细
http://blog.csdn.net/u012235274/article/details/52438479
解决方法,就是在slice_point: 1去除,使得数据平均分配,就没有维度不匹配的问题了。
写在后面的话:
Siamese网络是两个lenet网络的合并,用ContrastiveLoss损失函数做图像对的训练,但是我觉得Siamese更多的是一种思想,所有挖掘双分支或者更多分支关系的网络都可以称为Siamese网络,而不仅仅局限于lenet。
相关文章推荐
- 深度学习Caffe实战笔记(19)Windows平台 Faster-RCNN 制作自己的数据集
- 深度学习Caffe实战笔记(19)Windows平台 Faster-RCNN 制作自己的数据集
- 深度学习Caffe实战笔记(21)Windows平台 Faster-RCNN 训练好的模型测试数据
- 深度学习Caffe实战笔记(20)Windows平台 Faster-RCNN 训练自己的数据集
- 深度学习Caffe实战笔记(19)Windows平台 Faster-RCNN 制作自己的数据集
- 【深度学习】笔记6:使用caffe中的CIFAR10网络模型和自己的图片数据训练自己的模型(步骤详解)
- 深度学习caffe实战笔记(4)Windows caffe平台下跑cifar10
- 深度学习caffe实战笔记(4)Windows caffe平台下跑cifar10
- 深度学习Caffe实战(9)Windows 平台caffe用MATLAB接口实现训练网络和测试
- 深度学习Caffe实战笔记(5)Windows caffe平台跑Siamese mnist数据集
- caffe用自己的数据集训练深度网络模型——windows平台适用
- 深度学习Caffe实战笔记(3)用AlexNet跑自己的数据
- 深度学习Caffe实战笔记(3)用AlexNet跑自己的数据
- 深度学习-CAFFE利用CIFAR10网络模型训练自己的图像数据获得模型-2生成图像库的均值文件
- 【深度学习】笔记17 windows下SSD网络在caffe中的配置(GPU版本)【笔记3】
- 【深度学习】笔记16 windows下SSD网络在caffe中的配置(CPU版本)【笔记2】
- Windows Caffe 学习笔记(四)搭建自己的网络,训练和测试MNIST手写字体库
- 深度学习-CAFFE利用CIFAR10网络模型训练自己的图像数据获得模型-3结合caffe中的CIFAR10修改相关配置文件并训练
- 【深度学习】【caffe实用工具5】笔记27 windows下SSD网络中的get_image_size工具的使用
- Caffe学习笔记(六)—— Windows下训练自己的数据