tiny_cnn源码阅读(1)-编译运行源码

机器学习是理论性很强的一门课程,在工程实践时,常常难以把理论和代码结合起来。想通过一个工程来学习一下机器学习中的卷积神经网络。
tiny_cnn是c++写的实现cnn的代码,想通过代码来了解理论,学习和锻炼一下编程能力。tiny_cnn是c++11编写的,只有头文件,不依赖其他库就能运行MINST。计算卷积以及求导,这样大规模复杂的运算一般都是用GPU,tiny_cnn并没有是用GPU,它只是是用C++实现了是用CPU计算卷积神经网络的工程,因此并需要CUDA编程等知识。

首先下载代码,点这里,Windows上面运行有直接的工程。Linux上面,直接到tiny-cnn-master/examples下,运行build.bat中的脚本即可;build.bat中的脚本为:

g++ -std=c++11 -Wall -Werror -Wextra -pedantic -I ../ -O3 main.cpp -o main

可以看出,tiny_cnn使用了c++11。只是要安装g++4.8(或以上)。生成使用了的可执行文件即可运行;运行的MINST使用的是LeNet。这里对MINST训练数据做了封装,所以不需要opencv来直接读取图像数据。
这里简单看一下main函数执行流程;在main()函数中,执行了sample1_convnet(const string& data_dir_path = "../../data"),如下:

void sample1_convnet(const string& data_dir_path) {//默认数据目录在../../data
    // construct LeNet-5 architecture
    network<mse, gradient_descent_levenberg_marquardt> nn;//定义网络.mse为LossFunction,后者为Optimizer

    // connection table [Y.Lecun, 1998 Table.1]
#define O true
#define X false
    static const bool connection [] = {//6x16.LeNet中,C2生成C3使用
        O, X, X, X, O, O, O, X, X, O, O, O, O, X, O, O,
        O, O, X, X, X, O, O, O, X, X, O, O, O, O, X, O,
        O, O, O, X, X, X, O, O, O, X, X, O, X, O, O, O,
        X, O, O, O, X, X, O, O, O, O, X, X, O, X, O, O,
        X, X, O, O, O, X, X, O, O, O, O, X, O, O, X, O,
        X, X, X, O, O, O, X, X, O, O, O, O, X, O, O, O
    };
#undef O
#undef X
    //network重载了<<操作符,把后面的层添加到了net中,保存在容器里面
    /*
    下面是构建简化的LeNet,它是一个6层网络。原LeNet有7层
    第一层是卷积层,输入大小为32x32,卷积核为5x5,共有6个Feature Map
    第二层是池化层,经过第一层卷积后,输入为28x28,池化是2x2的矩阵,共有6个Feature Map
    第三层是卷积层,经过第二层池化后,输入为14x14,卷积核5x5,有16个Feature Map,通过connection_table
          来计算,由第二层中那几个特征图生成第三层的特征图
    第四层是池化层,经过第三层卷积后,输入为10x10,有16个Feature Map,池化是2x2的矩阵
    第五层是卷积层,输入大小为5x5,有120个Feature Map
    第6层是全连接层,接收上一层120个Feature Map的输入,输出为10,即0-9数字的判断。
    */
    nn << convolutional_layer<tan_h>(32, 32, 5, 1, 6) // 32x32 in, 5x5 kernel, 1-6 fmaps conv
       << average_pooling_layer<tan_h>(28, 28, 6, 2) // 28x28 in, 6 fmaps, 2x2 subsampling
       << convolutional_layer<tan_h>(14, 14, 5, 6, 16,
                                     connection_table(connection, 6, 16)) // with connection-table
       << average_pooling_layer<tan_h>(10, 10, 16, 2)
       << convolutional_layer<tan_h>(5, 5, 5, 16, 120)
       << fully_connected_layer<tan_h>(120, 10);

    std::cout << "load models..." << std::endl;

    // load MNIST dataset
    std::vector<label_t> train_labels, test_labels;
    std::vector<vec_t> train_images, test_images;

    parse_mnist_labels(data_dir_path + "/train-labels.idx1-ubyte", &train_labels);
    parse_mnist_images(data_dir_path + "/train-images.idx3-ubyte", &train_images, -1.0, 1.0, 2, 2);
    parse_mnist_labels(data_dir_path + "/t10k-labels.idx1-ubyte", &test_labels);
    parse_mnist_images(data_dir_path + "/t10k-images.idx3-ubyte", &test_images, -1.0, 1.0, 2, 2);

    std::cout << "start learning" << std::endl;

    progress_display disp(train_images.size());
    timer t;
    int minibatch_size = 10;

    nn.optimizer().alpha *= std::sqrt(minibatch_size);

    // create callback,回调函数为labmbda表达式
    auto on_enumerate_epoch = [&](){
        std::cout << t.elapsed() << "s elapsed." << std::endl;

        tiny_cnn::result res = nn.test(test_images, test_labels);

        std::cout << nn.optimizer().alpha << "," << res.num_success << "/" << res.num_total << std::endl;

        nn.optimizer().alpha *= 0.85; // decay learning rate。学习率衰减
        nn.optimizer().alpha = std::max((tiny_cnn::float_t)0.00001, nn.optimizer().alpha);//学习率有个最小值

        disp.restart(train_images.size());
        t.restart();
    };

    auto on_enumerate_minibatch = [&](){ 
        disp += minibatch_size; 
    };

    // training,开始训练
    nn.train(train_images, train_labels, minibatch_size, 20, on_enumerate_minibatch, on_enumerate_epoch);

    std::cout << "end training." << std::endl;

    // test and show results
    nn.test(test_images, test_labels).print_detail(std::cout);

    // save networks
    std::ofstream ofs("LeNet-weights");
    ofs << nn;
}

可以看出,在此函数中构建了LeNet-5,这是一个识别手写数字的网络,关于这个网络,可以参考这里这里。首先定义了网络nn,重载了操作符<<来构建网络。
把训练/测试数据和label分别导入,label存放到vector中,而数据存放在vector,相当于二维数组。auto定义的变量为lambda函数,可以作为参数传递给其他函数。之后先训练,后测试。这就是执行main函数的流程。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值