该内容已被发布者删除 该内容被自由微信恢复
文章于 2019年12月31日 被检测为删除。
查看原文
被用户删除
其他

Deep Learning深度学习之(五-1)CNN卷积神经网络代码理解

2016-06-23 ​zouxy 海贝能源

  里面包含的是我对一个作者的CNN笔记的翻译性的理解,对CNN的推导和实现做了详细的介绍,看明白这个笔记对代码的理解非常重要,所以强烈建议先看懂上面这篇文章。

 

         下面是自己对代码的注释:

cnnexamples.m

[plain] view plain copy

  1. clear all; close all; clc;  

  2. addpath('../data');  

  3. addpath('../util');  

  4. load mnist_uint8;  

  5.   

  6. train_x = double(reshape(train_x',28,28,60000))/255;  

  7. test_x = double(reshape(test_x',28,28,10000))/255;  

  8. train_y = double(train_y');  

  9. test_y = double(test_y');  

  10.   

  11. %% ex1   

  12. %will run 1 epoch in about 200 second and get around 11% error.   

  13. %With 100 epochs you'll get around 1.2% error  

  14.   

  15. cnn.layers = {  

  16.     struct('type', 'i') %input layer  

  17.     struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer  

  18.     struct('type', 's', 'scale', 2) %sub sampling layer  

  19.     struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer  

  20.     struct('type', 's', 'scale', 2) %subsampling layer  

  21. };  

  22.   

  23. % 这里把cnn的设置给cnnsetup,它会据此构建一个完整的CNN网络,并返回  

  24. cnn = cnnsetup(cnn, train_x, train_y);  

  25.   

  26. % 学习率  

  27. opts.alpha = 1;  

  28. % 每次挑出一个batchsize的batch来训练,也就是每用batchsize个样本就调整一次权值,而不是  

  29. % 把所有样本都输入了,计算所有样本的误差了才调整一次权值  

  30. opts.batchsize = 50;   

  31. % 训练次数,用同样的样本集。我训练的时候:  

  32. % 1的时候 11.41% error  

  33. % 5的时候 4.2% error  

  34. % 10的时候 2.73% error  

  35. opts.numepochs = 10;  

  36.   

  37. % 然后开始把训练样本给它,开始训练这个CNN网络  

  38. cnn = cnntrain(cnn, train_x, train_y, opts);  

  39.   

  40. % 然后就用测试样本来测试  

  41. [er, bad] = cnntest(cnn, test_x, test_y);  

  42.   

  43. %plot mean squared error  

  44. plot(cnn.rL);  

  45. %show test error  

  46. disp([num2str(er*100) '% error']);  


cnnsetup.m

[plain] view plain copy

  1. function net = cnnsetup(net, x, y)  

  2.     inputmaps = 1;  

  3.     % B=squeeze(A) 返回和矩阵A相同元素但所有单一维都移除的矩阵B,单一维是满足size(A,dim)=1的维。  

  4.     % train_x中图像的存放方式是三维的reshape(train_x',28,28,60000),前面两维表示图像的行与列,  

  5.     % 第三维就表示有多少个图像。这样squeeze(x(:, :, 1))就相当于取第一个图像样本后,再把第三维  

  6.     % 移除,就变成了28x28的矩阵,也就是得到一幅图像,再size一下就得到了训练样本图像的行数与列数了  

  7.     mapsize = size(squeeze(x(:, :, 1)));  

  8.   

  9.     % 下面通过传入net这个结构体来逐层构建CNN网络  

  10.     % n = numel(A)返回数组A中元素个数  

  11.     % net.layers中有五个struct类型的元素,实际上就表示CNN共有五层,这里范围的是5  

  12.     for l = 1 : numel(net.layers)   %  layer  

  13.         if strcmp(net.layers{l}.type, 's') % 如果这层是 子采样层  

  14.             % subsampling层的mapsize,最开始mapsize是每张图的大小28*28  

  15.             % 这里除以scale=2,就是pooling之后图的大小,pooling域之间没有重叠,所以pooling后的图像为14*14  

  16.             % 注意这里的右边的mapsize保存的都是上一层每张特征map的大小,它会随着循环进行不断更新  

  17.             mapsize = floor(mapsize / net.layers{l}.scale);  

  18.             for j = 1 : inputmaps % inputmap就是上一层有多少张特征图  

  19.                 net.layers{l}.b{j} = 0; % 将偏置初始化为0  

  20.             end  

  21.         end  

  22.         if strcmp(net.layers{l}.type, 'c') % 如果这层是 卷积层  

  23.             % 旧的mapsize保存的是上一层的特征map的大小,那么如果卷积核的移动步长是1,那用  

  24.             % kernelsize*kernelsize大小的卷积核卷积上一层的特征map后,得到的新的map的大小就是下面这样  

  25.             mapsize = mapsize - net.layers{l}.kernelsize + 1;  

  26.             % 该层需要学习的参数个数。每张特征map是一个(后层特征图数量)*(用来卷积的patch图的大小)  

  27.             % 因为是通过用一个核窗口在上一个特征map层中移动(核窗口每次移动1个像素),遍历上一个特征map  

  28.             % 层的每个神经元。核窗口由kernelsize*kernelsize个元素组成,每个元素是一个独立的权值,所以  

  29.             % 就有kernelsize*kernelsize个需要学习的权值,再加一个偏置值。另外,由于是权值共享,也就是  

  30.             % 说同一个特征map层是用同一个具有相同权值元素的kernelsize*kernelsize的核窗口去感受输入上一  

  31.             % 个特征map层的每个神经元得到的,所以同一个特征map,它的权值是一样的,共享的,权值只取决于  

  32.             % 核窗口。然后,不同的特征map提取输入上一个特征map层不同的特征,所以采用的核窗口不一样,也  

  33.             % 就是权值不一样,所以outputmaps个特征map就有(kernelsize*kernelsize+1)* outputmaps那么多的权值了  

  34.             % 但这里fan_out只保存卷积核的权值W,偏置b在下面独立保存  

  35.             fan_out = net.layers{l}.outputmaps * net.layers{l}.kernelsize ^ 2;  

  36.             for j = 1 : net.layers{l}.outputmaps  %  output map  

  37.                 % fan_out保存的是对于上一层的一张特征map,我在这一层需要对这一张特征map提取outputmaps种特征,  

  38.                 % 提取每种特征用到的卷积核不同,所以fan_out保存的是这一层输出新的特征需要学习的参数个数  

  39.                 % 而,fan_in保存的是,我在这一层,要连接到上一层中所有的特征map,然后用fan_out保存的提取特征  

  40.                 % 的权值来提取他们的特征。也即是对于每一个当前层特征图,有多少个参数链到前层  

  41.                 fan_in = inputmaps * net.layers{l}.kernelsize ^ 2;  

  42.                 for i = 1 : inputmaps  %  input map  

  43.                     % 随机初始化权值,也就是共有outputmaps个卷积核,对上层的每个特征map,都需要用这么多个卷积核  

  44.                     % 去卷积提取特征。  

  45.                     % rand(n)是产生n×n的 0-1之间均匀取值的数值的矩阵,再减去0.5就相当于产生-0.5到0.5之间的随机数  

  46.                     % 再 *2 就放大到 [-1, 1]。然后再乘以后面那一数,why?  

  47.                     % 反正就是将卷积核每个元素初始化为[-sqrt(6 / (fan_in + fan_out)), sqrt(6 / (fan_in + fan_out))]  

  48.                     % 之间的随机数。因为这里是权值共享的,也就是对于一张特征map,所有感受野位置的卷积核都是一样的  

  49.                     % 所以只需要保存的是 inputmaps * outputmaps 个卷积核。  

  50.                     net.layers{l}.k{i}{j} = (rand(net.layers{l}.kernelsize) - 0.5) * 2 * sqrt(6 / (fan_in + fan_out));  

  51.                 end  

  52.                 net.layers{l}.b{j} = 0; % 将偏置初始化为0  

  53.             end  

  54.             % 只有在卷积层的时候才会改变特征map的个数,pooling的时候不会改变个数。这层输出的特征map个数就是  

  55.             % 输入到下一层的特征map个数  

  56.             inputmaps = net.layers{l}.outputmaps;   

  57.         end  

  58.     end  

  59.       

  60.     % fvnum 是输出层的前面一层的神经元个数。  

  61.     % 这一层的上一层是经过pooling后的层,包含有inputmaps个特征map。每个特征map的大小是mapsize。  

  62.     % 所以,该层的神经元个数是 inputmaps * (每个特征map的大小)  

  63.     % prod: Product of elements.  

  64.     % For vectors, prod(X) is the product of the elements of X  

  65.     % 在这里 mapsize = [特征map的行数 特征map的列数],所以prod后就是 特征map的行*列  

  66.     fvnum = prod(mapsize) * inputmaps;  

  67.     % onum 是标签的个数,也就是输出层神经元的个数。你要分多少个类,自然就有多少个输出神经元  

  68.     onum = size(y, 1);  

  69.   

  70.     % 这里是最后一层神经网络的设定  

  71.     % ffb 是输出层每个神经元对应的基biases  

  72.     net.ffb = zeros(onum, 1);  

  73.     % ffW 输出层前一层 与 输出层 连接的权值,这两层之间是全连接的  

  74.     net.ffW = (rand(onum, fvnum) - 0.5) * 2 * sqrt(6 / (onum + fvnum));  

  75. end  

下续

本文来自网络,只用于学习交流之用,如有侵权,请联系作者就行处理。

      通过石油纽带,小编认识了很多油气行业的朋友,加小编微信“hboilgas”,你有什么石油方面的需求,小编会免费为您发布,天下石油人是一家,结识天下石油豪杰。





您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存