Tiny_cnn用自己的数据训练和测试

来源:互联网 发布:威纶触摸屏数据导出 编辑:程序博客网 时间:2024/06/04 23:32

转自:http://blog.csdn.net/u012507022/article/details/51815701

Tiny_cnn是一个简洁的纯C++11实现的深度学习框架。在Windows系统只需高版本的VS(VS2013或者更高本)+OpenCV即可,本人用的是VS2013+opencv2.4.11。关于tiny-cnn的具体细节,很多博客写得很详细。本人只介绍一种基于tiny-cnn快速简单的使用方法。

Tiny_cnn下载地址:https://github.com/nyanp/tiny-cnn

该方法很简单,只需自己准备好训练数据与测试数据。不需要像caffe那样制作标签和转换数据格式。把训练图像放到一个文件夹下,如文件夹train,把所有的图像直接批量重命名,但要保证每一类图像是连续存放的。如图1所示,7张dogs图片(1)—(7)、7张goats图片(8)—(14)、10张cats图片(15)—(24),一共24个训练样本。 测试图像文件夹test与文件夹train一样,只不过样本数量少一点。如图2所示,每一类的顺序与train相同,dog、goat、cat,一共10个测试数据。这样,就可以直接跑程序了。


图1.训练图像集


图2.测试图像集

在程序运行的过程中,加入标签。如图3所示。只需输入每一个类别的个数,程序自动制作标签。

最终结果界面:


对于输出的4*4的表格,第一行0,1,2与第一列0,1,2就是分类的标签。

剩下的3*3,位于对角线上的就是分类正确的,不在对角线上,就是分错的。如图,对角线上只有3,accuracy=3/10。其中,每一列的和就是测试数据中该类的个数。

注:该例子只为说明tiny-cnn训练数据的方法,在真正运用中,样本数量比这多得多。

补充:

运行程序时,很多人反应,准确率accuracy保持不变的情况。针对这个问题,本人今天又测试了一下。

用到的数据:http://download.csdn.NET/detail/u012507022/9679177

需要说明几点

(1)此程序的卷积神经网络LeNet,是为手写字体识别设计的网络,所以针对不同的问题,还需选着网络结构。

(2)此程序只能处理灰度图像,虽然读取的是RGB图像,但是imread()直接转换成灰度图了;而且图像也被放缩为              32*32。(处理RGB格式图像的程序在最下面)

(3)刚开始,我使用的.bmp格式的图像;后来证明.jpg图像也可以。

直接上代码:(注:此程序网络为LeNet)

[cpp] view plain copy
  1. /*tiny-cnn训练*/  
  2. #include <iostream>  
  3. #include "tiny_cnn/tiny_cnn.h"  
  4. #include <opencv2/opencv.hpp>  
  5. #include<strstream>  
  6.   
  7. using namespace std;  
  8. using namespace cv;  
  9. using namespace tiny_cnn;  
  10. using namespace tiny_cnn::activation;  
  11. //**********************************************************************************//  
  12. //定义全局变量  
  13. const int  TRNUM = 24;   //训练样本的个数  
  14. const int  TENUM =10;    //测试样本的个数  
  15. const int  C = 3;      //分类的个数  
  16. //**********************************************************************************//  
  17. //定义网络结构  
  18. void construct_net(network<mse, adagrad>& nn) {  
  19.     // connection table [Y.Lecun, 1998 Table.1]  
  20. #define O true  
  21. #define X false  
  22.     static const bool tbl[] = {  
  23.         O, X, X, X, O, O, O, X, X, O, O, O, O, X, O, O,  
  24.         O, O, X, X, X, O, O, O, X, X, O, O, O, O, X, O,  
  25.         O, O, O, X, X, X, O, O, O, X, X, O, X, O, O, O,  
  26.         X, O, O, O, X, X, O, O, O, O, X, X, O, X, O, O,  
  27.         X, X, O, O, O, X, X, O, O, O, O, X, O, O, X, O,  
  28.         X, X, X, O, O, O, X, X, O, O, O, O, X, O, O, O  
  29.     };  
  30. #undef O  
  31. #undef X  
  32.   
  33.     // construct nets  
  34.     nn << convolutional_layer<tan_h>(32, 32, 5, 1, 6)  // C1, 1@32x32-in, 6@28x28-out  
  35.        << average_pooling_layer<tan_h>(28, 28, 6, 2)   // S2, 6@28x28-in, 6@14x14-out  
  36.        << convolutional_layer<tan_h>(14, 14, 5, 6, 16,  
  37.             connection_table(tbl, 6, 16))              // C3, 6@14x14-in, 16@10x10-in  
  38.        << average_pooling_layer<tan_h>(10, 10, 16, 2)  // S4, 16@10x10-in, 16@5x5-out  
  39.        << convolutional_layer<tan_h>(5, 5, 5, 16, 120) // C5, 16@5x5-in, 120@1x1-out  
  40.        << fully_connected_layer<tan_h>(120,C);       // F6, 120-in, 10-out  
  41. }  
  42. //**********************************************************************************//  
  43. // convert image to vec_t  
  44. void convert_image(const string& imagefilename,double scale,int w,int h,std::vector<vec_t>& data)  
  45. {  
  46.     auto img = imread(imagefilename, IMREAD_GRAYSCALE);  
  47.     if (img.data == nullptr) return// cannot open, or it's not an image  
  48.     //imshow("img", img);  
  49.     //cvWaitKey(0);  
  50.     cv::Mat_<uint8_t> resized;  
  51.     cv::resize(img, resized, cv::Size(w, h));  
  52.     vec_t d;  
  53.   
  54.     std::transform(resized.begin(), resized.end(), std::back_inserter(d),  
  55.         [=](uint8_t c) { return c * scale; });  
  56.     data.push_back(d);  
  57. }  
  58.   
  59. //int 转换string  
  60. string int2string(int&i){  
  61.     strstream ss;  string str;  
  62.     ss << i; ss >> str;  
  63.     return str;  
  64. }  
  65.   
  66. int main() {  
  67.     //【第一步】定义网络结构 specify loss-function and learning strategy  
  68.     std::cout << "load models..." << std::endl;  
  69.     network<mse, adagrad> nn;  
  70.     construct_net(nn);  
  71.  //**********************************************************************************//  
  72.     //【第二步】加载数据  
  73.       //加载训练数据  
  74.     std::vector<label_t> train_labels, test_labels;  
  75.     std::vector<vec_t> train_images, test_images;  
  76.     for (int i = 1; i <= TRNUM; i++){  
  77.         string  str0 = int2string(i);  
  78.         string trainpath = "E:/2013Mycode/Mytin_cnn/train/animal ("+str0+").jpg"//训练集路径  
  79.         //转换图像格式  
  80.         convert_image(trainpath, 1.0, 32, 32, train_images);  
  81.     }  
  82.     cout <<"cout << train_images.size():"<< train_images.size()<< endl;  
  83.     //加载训练数据  
  84.     for (int i = 1; i <= TENUM; i++){  
  85.         string  str1 = int2string(i);  
  86.           
  87.         string testpath = "E:/2013Mycode/Mytin_cnn/test/animal (" + str1 + ").jpg";  //测试集路径  
  88.         convert_image(testpath, 1.0, 32, 32, test_images);  
  89.     }  
  90.     cout << "cout << test_images.size():" << test_images.size() << endl;  
  91. //**********************************************************************************//  
  92.     //【第三步】加载标签 手工添加 标签从0开始  
  93.     printf("加载训练集标签\n");   
  94.     int Ci; //每一类训练样本的个数  
  95.     for (size_t k = 0; k < C; k++){  // C 分类的个数  
  96.         printf("第i类样本的个数:");  scanf("%d", &Ci);  
  97.         for (size_t j = 1; j <= Ci; j++){  
  98.             train_labels.push_back((label_t)k);  
  99.         }  
  100.     }  
  101.     cout<<"train_labels" << train_labels.size() << endl;  
  102.     printf("/加载测试集标签\n");   
  103.     int Ti; //每一类测试样本的个数  
  104.     for (size_t k = 0; k < C; k++){  // C 分类的个数  
  105.         printf("第i类样本的个数:");  scanf("%d", &Ti);  
  106.         for (size_t j = 1; j <= Ti; j++){  
  107.             test_labels.push_back((label_t)k);  
  108.         }  
  109.     }  
  110.     cout << "test_labels" << test_labels.size() << endl;  
  111. //**********************************************************************************//  
  112.     //【第四步】训练  
  113.     std::cout << "start training" << std::endl;  
  114.   
  115.     progress_display disp(train_images.size());//进度显示,初始化对象disp  
  116.     timer t;  
  117.     int minibatch_size = 10;  
  118.     int num_epochs = 30;      //迭代次数  
  119.   
  120.     nn.optimizer().alpha *= std::sqrt(minibatch_size);  
  121.   
  122.     // create callback  
  123.     auto on_enumerate_epoch = [&](){  
  124.         std::cout << t.elapsed() << "s elapsed." << std::endl;  
  125.         tiny_cnn::result res = nn.test(test_images, test_labels);  
  126.         std::cout << res.num_success << "/" << res.num_total << std::endl;  
  127.   
  128.         disp.restart(train_images.size());  
  129.         t.restart();  
  130.     };  
  131.   
  132.     auto on_enumerate_minibatch = [&](){  
  133.         disp += minibatch_size;  
  134.     };  
  135.   
  136.     // training  
  137.     nn.train(train_images,    //训练数据  
  138.             train_labels,     //标签  
  139.             minibatch_size,   
  140.             num_epochs,  
  141.             on_enumerate_minibatch,  
  142.             on_enumerate_epoch);  
  143.   
  144.     std::cout << "end training." << std::endl;  
  145.   
  146.     // test and show results  
  147.     nn.test(test_images, test_labels).print_detail(std::cout);  
  148.   
  149.     // 保存训练好的分类器  
  150.     std::ofstream ofs("LeNet-weights");    
  151.     ofs << nn;  
  152.   
  153.     system("pause");  
  154. }  

补充:

运行程序时,可能出现准确率accuracy保持不变的情况。针对这个问题。需要说明几点

(1)此程序的卷积神经网络LeNet,是为手写字体识别设计的网络,所以针对不同的问题,还需选着网络结构。

(2)使用.bmp格式的图像效果比较好,不会出现accuracy不变的情况;可能是因为.bmp是无压缩的。

(3)此程序只能处理灰度图像,虽然读取的是RGB图像,但是imread()直接转换成灰度图了;而且图像也被放缩为              32*32。处理RGB格式图像的程序如下:

[cpp] view plain copy
  1. void convert_image(const std::string& filename,  
  2.                     std::vector<vec_t> &data,  
  3.                     float min,  
  4.                     float max,  
  5.                     int padding){  
  6.     Mat src = imread(filename, -1);  
  7.     int W = src.cols;  
  8.     int H = src.rows;  
  9.     float denominator = 255;  
  10.     vec_t img(3 * (H + 2 * padding)*(W + 2 * padding), min);  
  11.       
  12.     for (int i = 0; i < H; i++) { // Go over all rows  
  13.         for (int j = 0; j < W; j++) { // Go over all columns  
  14.             for (int c = 0; c < 3; c++) { // Go through all channels  
  15.                 img[W*H*c + W*(i + padding) + (j + padding)] = src.at<cv::Vec3b>(i, j)[c]/denominator*(max - min) + min;  
  16.             }  
  17.         }  
  18.     }  
  19.     data.push_back(img);  
  20. }  

我用300个mnist数据的测试结果如下: