深度学习 1. CNN的构建和解释--最简单的CNN构造(LeNet-5)# By deepLearnToolbox-master

来源:互联网 发布:imp命令导入远程数据库 编辑:程序博客网 时间:2024/06/07 07:42

本文为原创文章转载请注明出处,博主博客地址:http://blog.csdn.net/qq_20259459  

作者( jinweizhi93@gmai.com )信息。



今天介绍一个曾经用过的简单的CNN Toolbox--------DeepLearnToolbox-master。 


DeepLearnToolbox-master 介绍:

贡献者:Rasmus Berg Palm(丹麦)

最后更新时间:2012年

现在状态:停止维护

简介:DeepLearnToolbox-master 最为一个工具箱里面包括了CAE,CNN,DBN,NN,SAE这些模型。其本身而言是非常简单的设计,所以就目前而言早已经过时了。也不会再被用于开发中。现在主流当然是: Theanotorchtensorflow, Matconvnet, MxNet, caffe,后面我会选择介绍。

但是对于新学者来说,DeepLearnToolbox-master 中的CNN模型是以LeNet-5编写的,所以非常好,适合我们去理解学习。


DeepLearnToolbox-master下载地址:  https://github.com/rasmusbergpalm/DeepLearnToolbox


CNN 流程:

首先我们运行 test_example_CNN.m.

然后 test_example_CNN.mat 会载入mnist_uint8.mat 

接着去调用cnnsetup.m, cnntrain.m, cnntest.m.

接下来cnntrain.m 会去调用 cnnff.m, cnnbp.m, cnnapplygrads.m.

最后就是画出 MSE-- data 的图像。


所以可以看出流程还是非常简单明了的。


最终我们会形成的CNN图示--经典的LeNet-5:




如果初学者不知道权重权值怎么计算请看下图,当时自己学习时做的笔记。我一步一步都写的非常的清楚,很好理解。




下面开始介绍CODE:


1. test_example_CNN.mat :


%主要功能:在mnist数据库上做实验,验证工具箱的有效性  
% 算法流程:1)载入训练样本和测试样本  
%          2)设置CNN参数,并进行训练  
%          3)进行检测cnntest()  
% 注意事项:1)由于直接将所有测试样本输入会导致内存溢出,故采用一次只测试一个训练样本的测试方法 

 

[plain] view plain copy
 print?
  1. function test_example_CNN  
  2.   
  3. %%load data   
  4. load mnist_uint8; %载入数据  
  5.   
  6. %% input data   
  7.   
  8. train_x = double(reshape(train_x',28,28,60000))/255;   %标准化数据(这里作者给出的方法并不是很好需要改进)  
  9. test_x = double(reshape(test_x',28,28,10000))/255;       
  10. train_y = double(train_y');      
  11. test_y = double(test_y');    
  12.   
  13. %% ex1 Train a 6c-2s-12c-2s Convolutional neural network   
  14. %will run 1 epoch in about 200 second and get around 11% error.   
  15. %With 100 epochs you'll get around 1.2% error  
  16. %% 翻译内容:  
  17. %%%%%%%%%%%%%%%%%%%%设置卷积神经网络参数%%%%%%%%%%%%%%%%%%%%    
  18. % 主要功能:训练一个6c-2s-12c-2s形式的卷积神经网络,预期性能如下:    
  19. %          1)迭代一次需要200秒左右,错误率大约为11%    
  20. %          2)迭代一百次后错误率大约为1.2%    
  21. % 算法流程:1)构建神经网络并进行训练,以CNN结构体的形式保存    
  22. %          2)用已知的训练样本进行测试    
  23. % 注意事项:1)之前在测试的时候提示内存溢出,后来莫名其妙的又不溢出了,估计到了系统的内存临界值    
  24. %%=========================================================================    
  25.   
  26.   
  27. rand('state',0)  
  28.   
  29. cnn.layers = {  
  30.     struct('type', 'i')   <span style="white-space:pre">                    </span> %input layer%输入层   
  31.     struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) <span style="white-space:pre">    </span> %convolution layer%卷积层  
  32.     struct('type', 's', 'scale', 2)<span style="white-space:pre">               </span> %sub sampling layer%下采样层   
  33.     struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) <span style="white-space:pre">   </span> %convolution layer%卷积层    
  34.     struct('type', 's', 'scale', 2)<span style="white-space:pre">               </span> %subsampling layer%下采样层    
  35. };  
  36.   
  37. %% 训练选项,alpha学习效率(不用),batchsiaze批训练总样本的数量,numepoches迭代次数  
  38. opts.alpha = 0.1;  
  39. opts.batchsize = 100;  
  40. opts.numepochs = 100;  
  41.   
  42. %%  
  43. cnn = cnnsetup(cnn, train_x, train_y);  
  44. cnn = cnntrain(cnn, train_x, train_y, opts);  
  45.   
  46. [er, bad] = cnntest(cnn, test_x, test_y);  
  47.   
  48. %plot mean squared error  
  49. figure; plot(cnn.rL);  
  50. assert(er<0.12, 'Too big error');  



2. cnnsetup:


% 输入参数:net,待设置的卷积神经网络;x,训练样本;y,训练样本对应标签;  
% 输出参数:net,初始化完成的卷积神经网络  
% 主要功能:对CNN的结构进行初始化  
% 注意事项:isOctave错误发生,请把 util 文件添加至运行路径中。

[plain] view plain copy
 print?
  1. function net = cnnsetup(net, x, y)    
  2.     assert(~isOctave() || compare_versions(OCTAVE_VERSION, '3.8.0', '>='), ['Octave 3.8.0 or greater is required for CNNs as there is a bug in convolution in previous versions. See http://savannah.gnu.org/bugs/?39314. Your version is ' myOctaveVersion]);  
  3.     inputmaps = 1; <span style="white-space:pre">   </span>%初始化网络输入层数为1层    
  4. %%=========================================================================    
  5. % 主要功能:得到输入图像的行数和列数    
  6. % 注意事项:1)B=squeeze(A) 返回和矩阵A相同元素但所有单一维都移除的矩阵B,单一维是满足size(A,dim)=1的维。    
  7. %             train_x中图像的存放方式是三维的reshape(train_x',28,28,60000),前面两维表示图像的行与列,    
  8. %             第三维就表示有多少个图像。这样squeeze(x(:, :, 1))就相当于取第一个图像样本后,再把第三维    
  9. %             移除,就变成了28x28的矩阵,也就是得到一幅图像,再size一下就得到了训练样本图像的行数与列数了    
  10. %%=========================================================================    
  11.       
  12.       mapsize = size(squeeze(x(:, :, 1)));  
  13. %       mapsize = size(x);  
  14.       
  15.     %%%%%%%%%%%%%%%%%%%%下面通过传入net这个结构体来逐层构建CNN网络%%%%%%%%%%%%%%%%%%%%    
  16.     for l = 1 : numel(net.layers)   %对于每一层    
  17.         if strcmp(net.layers{l}.type, 's')  %如果当前层是下采样层  
  18.               
  19.         %%=========================================================================    
  20.         % 主要功能:获取下采样之后特征map的尺寸    
  21.         % 注意事项:1)subsampling层的mapsize,最开始mapsize是每张图的大小28*28    
  22.         %             这里除以scale=2,就是pooling之后图的大小,pooling域之间没有重叠,所以pooling后的图像为14*14    
  23.         %             注意这里的右边的mapsize保存的都是上一层每张特征map的大小,它会随着循环进行不断更新    
  24.         %%=========================================================================    
  25.               
  26.             mapsize = mapsize / net.layers{l}.scale;  
  27.             assert(all(floor(mapsize)==mapsize), ['Layer ' num2str(l) ' size must be integer. Actual: ' num2str(mapsize)]);  
  28.             
  29.               
  30.             for j = 1 : inputmaps   %对于上一层的每个特征图   
  31.                 net.layers{l}.b{j} = 0;  %将偏置初始化为零  
  32.             end  
  33.         end  
  34.           
  35.         if strcmp(net.layers{l}.type, 'c')   %如果当前层是卷基层  
  36.               
  37.         %%=========================================================================    
  38.         % 主要功能:获取卷积后的特征map尺寸以及当前层待学习的卷积核的参数数量    
  39.         % 注意事项:1)旧的mapsize保存的是上一层的特征map的大小,那么如果卷积核的移动步长是1,那用    
  40.         %             kernelsize*kernelsize大小的卷积核卷积上一层的特征map后,得到的新的map的大小就是下面这样    
  41.         %          2)fan_out代表该层需要学习的参数个数。每张特征map是一个(后层特征图数量)*(用来卷积的patch图的大小)    
  42.         %             因为是通过用一个核窗口在上一个特征map层中移动(核窗口每次移动1个像素),遍历上一个特征map    
  43.         %             层的每个神经元。核窗口由kernelsize*kernelsize个元素组成,每个元素是一个独立的权值,所以    
  44.         %             就有kernelsize*kernelsize个需要学习的权值,再加一个偏置值。另外,由于是权值共享,也就是    
  45.         %             说同一个特征map层是用同一个具有相同权值元素的kernelsize*kernelsize的核窗口去感受输入上一    
  46.         %             个特征map层的每个神经元得到的,所以同一个特征map,它的权值是一样的,共享的,权值只取决于    
  47.         %             核窗口。然后,不同的特征map提取输入上一个特征map层不同的特征,所以采用的核窗口不一样,也    
  48.         %             就是权值不一样,所以outputmaps个特征map就有(kernelsize*kernelsize+1)* outputmaps那么多的权值了    
  49.         %             但这里fan_out只保存卷积核的权值W,偏置b在下面独立保存    
  50.         %%====================================================================  
  51.               
  52.           
  53.           
  54.             mapsize = mapsize - net.layers{l}.kernelsize + 1;  
  55.             fan_out = net.layers{l}.outputmaps * net.layers{l}.kernelsize ^ 2;  
  56.              
  57.               
  58.             for j = 1 : net.layers{l}.outputmaps  %  %对于卷积层的每一个输出map  
  59.                   
  60.             %%=========================================================================    
  61.             % 主要功能:获取卷积层与前一层输出map之间需要链接的参数链个数    
  62.             % 注意事项:1)fan_out保存的是对于上一层的一张特征map,我在这一层需要对这一张特征map提取outputmaps种特征,    
  63.             %             提取每种特征用到的卷积核不同,所以fan_out保存的是这一层输出新的特征需要学习的参数个数    
  64.             %             而,fan_in保存的是,我在这一层,要连接到上一层中所有的特征map,然后用fan_out保存的提取特征    
  65.             %             的权值来提取他们的特征。也即是对于每一个当前层特征图,有多少个参数链到前层    
  66.             %%=================================================================  
  67.                   
  68.                 fan_in = inputmaps * net.layers{l}.kernelsize ^ 2;  
  69.                   
  70.                 for i = 1 : inputmaps   %对于上一层的每一个输出特征map(本层的输入map)  
  71.                       
  72.                 %%=========================================================================    
  73.                 % 主要功能:随机初始化卷积核的权值,再将偏置均初始化为零    
  74.                 % 注意事项:1)随机初始化权值,也就是共有outputmaps个卷积核,对上层的每个特征map,都需要用这么多个卷积核去卷积提取特征。    
  75.                 %             rand(n)是产生n×n的 0-1之间均匀取值的数值的矩阵,再减去0.5就相当于产生-0.5到0.5之间的随机数    
  76.                 %             再 *2 就放大到 [-1, 1]。然后再乘以后面那一数,why?    
  77.                 %             反正就是将卷积核每个元素初始化为[-sqrt(6 / (fan_in + fan_out)), sqrt(6 / (fan_in + fan_out))]    
  78.                 %             之间的随机数。因为这里是权值共享的,也就是对于一张特征map,所有感受野位置的卷积核都是一样的    
  79.                 %             所以只需要保存的是 inputmaps * outputmaps 个卷积核。    
  80.                 %          2)为什么这里是inputmaps * outputmaps个卷积核?    
  81.                 %%===============================================================  
  82.                       
  83.                     net.layers{l}.k{i}{j} = (rand(net.layers{l}.kernelsize) - 0.5) * 2 * sqrt(6 / (fan_in + fan_out));  
  84.                 end  
  85.                 net.layers{l}.b{j} = 0;  
  86.             end  
  87.             inputmaps = net.layers{l}.outputmaps;  %在卷积层会更新每层网络的输出map数量    
  88.         end  
  89.     end  
  90.     % 'onum' is the number of labels, that's why it is calculated using size(y, 1). If you have 20 labels so the output of the network will be 20 neurons.  
  91.     % 'fvnum' is the number of output neurons at the last layer, the layer just before the output layer.  
  92.     % 'ffb' is the biases of the output neurons.  
  93.     % 'ffW' is the weights between the last layer and the output neurons. Note that the last layer is fully connected to the output layer, that's why the size of the weights is (onum * fvnum)  
  94.     %%=========================================================================    
  95. % 主要功能:初始化最后一层,也就是输出层的参数值    
  96. % 算法流程:1)fvnum 是输出层的前面一层的神经元个数。这一层的上一层是经过pooling后的层,包含有inputmaps个    
  97. %             特征map。每个特征map的大小是mapsize,所以,该层的神经元个数是 inputmaps * (每个特征map的大小)      
  98. %          2)onum 是标签的个数,也就是输出层神经元的个数。你要分多少个类,自然就有多少个输出神经元    
  99. %          3)net.ffb和net.ffW为最后一层(全连接层)的偏置和权重    
  100. %%=========================================================================     
  101.       
  102.     fvnum = prod(mapsize) * inputmaps;  
  103.     onum = size(y, 1);  
  104.   
  105.     net.ffb = zeros(onum, 1);  
  106.     net.ffW = (rand(onum, fvnum) - 0.5) * 2 * sqrt(6 / (onum + fvnum));  
  107. end  


3. cnntrain.m:


%输入参数:net,神经网络;x,训练数据矩阵;y,训练数据的标签矩阵;opts,神经网络的相关训练参数  
%输出参数:net,训练完成的卷积神经网络  
%算法流程:1)将样本打乱,随机选择进行训练;  
%         2)取出样本,通过cnnff2()函数计算当前网络权值和网络输入下网络的输出  
%         3)通过BP算法计算误差对网络权值的导数  
%         4)得到误差对权值的导数后,就通过权值更新方法去更新权值  
%注意事项:1)使用BP算法计算梯度  


[plain] view plain copy
 print?
  1. function net = cnntrain(net, x, y, opts)  
  2.     m = size(x, 3);   %m保存的是训练样本个数  
  3.     numbatches = m / opts.batchsize;    %numbatches表示每次迭代中所选取的训练样本数  
  4.     if rem(numbatches, 1) ~= 0          %如果numbatches不是整数,则程序发生错误   
  5.         error('numbatches not integer');  
  6.     end  
  7.       
  8. %%=====================================================================    
  9. %主要功能:CNN网络的迭代训练    
  10. %实现步骤:1)通过randperm()函数将原来的样本顺序打乱,再挑出一些样本来进行训练    
  11. %         2)取出样本,通过cnnff2()函数计算当前网络权值和网络输入下网络的输出    
  12. %         3)通过BP算法计算误差对网络权值的导数    
  13. %         4)得到误差对权值的导数后,就通过权值更新方法去更新权值    
  14. %注意事项:1)P = randperm(N),返回[1, N]之间所有整数的一个随机的序列,相当于把原来的样本排列打乱,    
  15. %            再挑出一些样本来训练    
  16. %         2)采用累积误差的计算方式来评估当前网络性能,即当前误差 = 以前误差 * 0.99 + 本次误差 * 0.01    
  17. %            使得网络尽可能收敛到全局最优    
  18. %%=====================================================================  
  19.       
  20.     net.rL = [];       %代价函数值,也就是误差值   
  21.     for i = 1 : opts.numepochs       %对于每次迭代  
  22.         disp(['epoch ' num2str(i) '/' num2str(opts.numepochs)]);  
  23.         tic;                 %使用tic和toc来统计程序运行时间    
  24.           
  25.         %%%%%%%%%%%%%%%%%%%%取出打乱顺序后的batchsize个样本和对应的标签 %%%%%%%%%%%%%%%%%%%%   
  26.           
  27.         kk = randperm(m);  
  28.         for l = 1 : numbatches  
  29.             batch_x = x(:, :, kk((l - 1) * opts.batchsize + 1 : l * opts.batchsize));  
  30.             batch_y = y(:,    kk((l - 1) * opts.batchsize + 1 : l * opts.batchsize));  
  31.   
  32.             %%%%%%%%%%%%%%%%%%%%在当前的网络权值和网络输入下计算网络的输出(特征向量)%%%%%%%%%%%%%%%%%%%%  
  33.               
  34.             net = cnnff(net, batch_x);  
  35.               
  36.             %%%%%%%%%%%%%%%%%%%%通过对应的样本标签用bp算法来得到误差对网络权值的导数%%%%%%%%%%%%%%%%%%%%    
  37.               
  38.             net = cnnbp(net, batch_y);  
  39.               
  40.             %%%%%%%%%%%%%%%%%%%%通过权值更新方法去更新权值%%%%%%%%%%%%%%%%%%%%  
  41.             net = cnnapplygrads(net, opts);  
  42.               
  43.               
  44.             if isempty(net.rL)  
  45.                 net.rL(1) = net.L;    %代价函数值,也就是均方误差值 ,在cnnbp.m中计算初始值 net.L = 1/2* sum(net.e(:) .^ 2) / size(net.e, 2);  
  46.             end  
  47.             net.rL(end + 1) = 0.99 * net.rL(end) + 0.01 * net.L;    %采用累积的方式计算累计误差  
  48.         end  
  49.         toc;  
  50.     end  
  51.       
  52. end  


4. cnntest.m:


[plain] view plain copy
 print?
  1. function [er, bad] = cnntest(net, x, y)  
  2.     %  feedforward  
  3.     net = cnnff(net, x);  
  4.     [~, h] = max(net.o);  
  5.     [~, a] = max(y);  
  6.     bad = find(h ~= a);  
  7.   
  8.     er = numel(bad) / size(y, 2);  
  9. end  



5. cnnff.mat:


%输入参数:net,神经网络;x,训练数据矩阵;  
%输出参数:net,训练完成的卷积神经网络  
%主要功能:使用当前的神经网络对输入的向量进行预测  
%算法流程:1)将样本打乱,随机选择进行训练;  
%         2)讲样本输入网络,层层映射得到预测值  
%注意事项:1)使用BP算法计算梯度  


[plain] view plain copy
 print?
  1. function net = cnnff(net, x)  
  2.     n = numel(net.layers);    %层数   
  3.     net.layers{1}.a{1} = x;   %网络的第一层就是输入,但这里的输入包含了多个训练图像    
  4.     inputmaps = 1;            %输入层只有一个特征map,也就是原始的输入图像  
  5.   
  6.     for l = 2 : n   %  for each layer       %对于每层(第一层是输入层,循环时先忽略掉)  
  7.         if strcmp(net.layers{l}.type, 'c')  %如果当前是卷积层  
  8.             %  !!below can probably be handled by insane matrix operations   
  9.             for j = 1 : net.layers{l}.outputmaps   %  for each output map   %对每一个输入map,需要用outputmaps个不同的卷积核去卷积图像   
  10.                   
  11.             %%=========================================================================    
  12.             %主要功能:创建outmap的中间变量,即特征矩阵    
  13.             %实现步骤:用这个公式生成一个零矩阵,作为特征map    
  14.             %注意事项:1)对于上一层的每一张特征map,卷积后的特征map的大小是:(输入map宽 - 卷积核的宽 + 1)* (输入map高 - 卷积核高 + 1)    
  15.             %         2)由于每层都包含多张特征map,则对应的索引则保存在每层map的第三维,及变量Z中    
  16.             %%=========================================================================    
  17.                   
  18.                 %  create temp output map  
  19.                 z = zeros(size(net.layers{l - 1}.a{1}) - [net.layers{l}.kernelsize - 1 net.layers{l}.kernelsize - 1 0]);  
  20.                   
  21.                 for i = 1 : inputmaps   %  for each input map %对于输入的每个特征map   
  22.                       
  23.                 %%=========================================================================    
  24.                 %主要功能:将上一层的每一个特征map(也就是这层的输入map)与该层的卷积核进行卷积    
  25.                 %实现步骤:1)进行卷积    
  26.                 %         2)加上对应位置的基b,然后再用sigmoid函数算出特征map中每个位置的激活值,作为该层输出特征map    
  27.                 %注意事项:1)当前层的一张特征map,是用一种卷积核去卷积上一层中所有的特征map,然后所有特征map对应位置的卷积值的和    
  28.                 %         2)有些论文或者实际应用中,并不是与全部的特征map链接的,有可能只与其中的某几个连接    
  29.                 %%============================================================  
  30.                       
  31.                     %  convolve with corresponding kernel and add to temp output map  
  32.                     z = z + convn(net.layers{l - 1}.a{i}, net.layers{l}.k{i}{j}, 'valid');  
  33.                 end  
  34.                 %  add bias, pass through nonlinearity  
  35.                 net.layers{l}.a{j} = sigm(z + net.layers{l}.b{j});    %加基(加上加性偏置b)  
  36.             end  
  37.             %  set number of input maps to this layers number of outputmaps  
  38.             inputmaps = net.layers{l}.outputmaps;                     %更新当前层的map数量;  
  39.         elseif strcmp(net.layers{l}.type, 's')                        %如果当前层是下采样层   
  40.             %  downsample  
  41.             for j = 1 : inputmaps  
  42.                   
  43.             %%=========================================================================    
  44.             %主要功能:对特征map进行下采样    
  45.             %实现步骤:1)进行卷积    
  46.             %         2)最终pooling的结果需要从上面得到的卷积结果中以scale=2为步长,跳着把mean pooling的值读出来    
  47.             %注意事项:1)例如我们要在scale=2的域上面执行mean pooling,那么可以卷积大小为2*2,每个元素都是1/4的卷积核    
  48.             %         2)因为convn函数的默认卷积步长为1,而pooling操作的域是没有重叠的,所以对于上面的卷积结果    
  49.             %         3)是利用卷积的方法实现下采样    
  50.             %%=========================================================================   
  51.                   
  52.                 z = convn(net.layers{l - 1}.a{j}, ones(net.layers{l}.scale) / (net.layers{l}.scale ^ 2), 'valid');   %  !! replace with variable  
  53.                 net.layers{l}.a{j} = z(1 : net.layers{l}.scale : end, 1 : net.layers{l}.scale : end, :);    %跳读mean pooling的值   
  54.             end  
  55.         end  
  56.     end  
  57.   
  58.       
  59. %%=========================================================================    
  60. %主要功能:输出层,将最后一层得到的特征变成一条向量,作为最终提取得到的特征向量    
  61. %实现步骤:1)获取倒数第二层中每个特征map的尺寸    
  62. %         2)用reshape函数将map转换为向量的形式    
  63. %         3)使用sigmoid(W*X + b)函数计算样本输出值,放到net成员o中    
  64. %注意事项:1)在使用sigmoid()函数是,是同时计算了batchsize个样本的输出值    
  65. %%=========================================================================  
  66.       
  67.       
  68.     %  concatenate all end layer feature maps into vector  
  69.     net.fv = [];                                %net.fv为神经网络倒数第二层的输出map   
  70.     for j = 1 : numel(net.layers{n}.a)          %最后一层的特征map的个数  
  71.         sa = size(net.layers{n}.a{j});          %第j个特征map的大小  
  72.         net.fv = [net.fv; reshape(net.layers{n}.a{j}, sa(1) * sa(2), sa(3))];  
  73.     end  
  74.     %  feedforward into output perceptrons  
  75.     net.o = sigm(net.ffW * net.fv + repmat(net.ffb, 1, size(net.fv, 2)));     %通过全连接层的映射得到网络的最终预测结果输出    
  76.   
  77. end  


6. cnnbp.m:


%输入参数:net,呆训练的神经网络;y,训练样本的标签,即期望输出  
%输出参数:net,经过BP算法训练得到的神经网络  
%主要功能:通过BP算法训练神经网络参数  
%实现步骤:1)将输出的残差扩展成与最后一层的特征map相同的尺寸形式  
%         2)如果是卷积层,则进行上采样  
%         3)如果是下采样层,则进行下采样  
%         4)采用误差传递公式对灵敏度进行反向传递  
%注意事项:1)从最后一层的error倒推回来deltas,和神经网络的BP十分相似,可以参考“UFLDL的反向传导算法”的说明  
%         2)在fvd里面保存的是所有样本的特征向量(在cnnff.m函数中用特征map拉成的),所以这里需要重新换回来特征map的形式,  
%            d保存的是delta,也就是灵敏度或者残差  
%         3)net.o .* (1 - net.o))代表输出层附加的非线性函数的导数,即sigm函数的导数  


[plain] view plain copy
 print?
  1. function net = cnnbp(net, y)  
  2.     n = numel(net.layers);                                %网络层数  
  3.   
  4.     %   error  
  5.     net.e = net.o - y;                                    %实际输出与期望输出之间的误差    
  6.     %  loss function  
  7.     net.L = 1/2* sum(net.e(:) .^ 2) / size(net.e, 2);     %代价函数,采用均方误差函数作为代价函数    
  8.   
  9.     %%  backprop deltas  
  10.     net.od = net.e .* (net.o .* (1 - net.o));   %  output delta%输出层的灵敏度或者残差,(net.o .* (1 - net.o))代表输出层的激活函数的导数  
  11.     net.fvd = (net.ffW' * net.od);              %  feature vector delta%残差反向传播回前一层,net.fvd保存的是残差   
  12.     if strcmp(net.layers{n}.type, 'c')         %  only conv layers has sigm function%只有卷积层采用sigm函数  
  13.         net.fvd = net.fvd .* (net.fv .* (1 - net.fv));       %net.fv是前一层的输出(未经过simg函数),作为输出层的输入  
  14.     end  
  15.   
  16.       
  17.     %%%%%%%%%%%%%%%%%%%%将输出的残差扩展成与最后一层的特征map相同的尺寸形式%%%%%%%%%%%%%%%%%%%%    
  18.       
  19.     %  reshape feature vector deltas into output map style  
  20.     sa = size(net.layers{n}.a{1});           %最后一层特征map的大小。这里的最后一层都是指输出层的前一层  
  21.     fvnum = sa(1) * sa(2);                   %因为是将最后一层特征map拉成一条向量,所以对于一个样本来说,特征维数是这样  
  22.     for j = 1 : numel(net.layers{n}.a)       %最后一层的特征map的个数  
  23.         net.layers{n}.d{j} = reshape(net.fvd(((j - 1) * fvnum + 1) : j * fvnum, :), sa(1), sa(2), sa(3));  
  24.     end  
  25.   
  26.     for l = (n - 1) : -1 : 1                  %对于输出层前面的层(与输出层计算残差的方式不同)       
  27.         if strcmp(net.layers{l}.type, 'c')    %如果是卷积层,则进行上采样  
  28.             for j = 1 : numel(net.layers{l}.a)  %该层特征map的个数  
  29.                   
  30.             %%=========================================================================    
  31.             %主要功能:卷积层的灵敏度误差传递    
  32.             %注意事项:1)net.layers{l}.d{j} 保存的是 第l层 的 第j个 map 的 灵敏度map。 也就是每个神经元节点的delta的值    
  33.             %            expand的操作相当于对l+1层的灵敏度map进行上采样。然后前面的操作相当于对该层的输入a进行sigmoid求导    
  34.             %            这条公式请参考 Notes on Convolutional Neural Networks    
  35.             %%=========================================================================   
  36.                   
  37.                 net.layers{l}.d{j} = net.layers{l}.a{j} .* (1 - net.layers{l}.a{j}) .* (expand(net.layers{l + 1}.d{j}, [net.layers{l + 1}.scale net.layers{l + 1}.scale 1]) / net.layers{l + 1}.scale ^ 2);  
  38.             end  
  39.               
  40.         elseif strcmp(net.layers{l}.type, 's')    %如果是下采样层,则进行下采样  
  41.               
  42.         %%=========================================================================    
  43.         %主要功能:下采样层的灵敏度误差传递    
  44.         %注意事项:1)这条公式请参考 Notes on Convolutional Neural Networks    
  45.         %%====================================================================  
  46.               
  47.             for i = 1 : numel(net.layers{l}.a)                 %第i层特征map的个数  
  48.                 z = zeros(size(net.layers{l}.a{1}));  
  49.                 for j = 1 : numel(net.layers{l + 1}.a)         %第l+1层特征map的个数  
  50.                      z = z + convn(net.layers{l + 1}.d{j}, rot180(net.layers{l + 1}.k{i}{j}), 'full');  
  51.                 end  
  52.                 net.layers{l}.d{i} = z;  
  53.             end  
  54.         end  
  55.     end  
  56.   
  57. %%=========================================================================    
  58. %主要功能:计算梯度    
  59. %实现步骤:    
  60. %注意事项:1)这里与Notes on Convolutional Neural Networks中不同,这里的子采样层没有参数,也没有    
  61. %            激活函数,所以在子采样层是没有需要求解的参数的    
  62. %%=========================================================================   
  63.       
  64.     %%  calc gradients  
  65.     for l = 2 : n  
  66.         if strcmp(net.layers{l}.type, 'c')  
  67.             for j = 1 : numel(net.layers{l}.a)  
  68.                 for i = 1 : numel(net.layers{l - 1}.a)  
  69.                     
  70.                       %%%%%%%%%%%%%%%%%%%%dk保存的是误差对卷积核的导数%%%%%%%%%%%%%%%%%%%%   
  71.                     net.layers{l}.dk{i}{j} = convn(flipall(net.layers{l - 1}.a{i}), net.layers{l}.d{j}, 'valid') / size(net.layers{l}.d{j}, 3);  
  72.                 end  
  73.                   
  74.                 %%%%%%%%%%%%%%%%%%%%db保存的是误差对于bias基的导数%%%%%%%%%%%%%%%%%%%%   
  75.                 net.layers{l}.db{j} = sum(net.layers{l}.d{j}(:)) / size(net.layers{l}.d{j}, 3);  
  76.             end  
  77.         end  
  78.     end  
  79.       
  80.     %%%%%%%%%%%%%%%%%%%%最后一层perceptron的gradient的计算%%%%%%%%%%%%%%%%%%%%   
  81.     net.dffW = net.od * (net.fv)' / size(net.od, 2);  
  82.     net.dffb = mean(net.od, 2);  
  83.   
  84.     function X = rot180(X)  
  85.         X = flipdim(flipdim(X, 1), 2);  
  86.     end  
  87. end  


7. cnnapplygrads:


%函数名称:cnnapplygrads(),权值更新函数  
%输入参数:net,权值待更新的卷积神经网络;opts,神经网络训练的相关参数  
%输出参数:  
%算法流程:先更新卷积层的参数,再更新全连接层参数  

[plain] view plain copy
 print?
  1. <span style="font-family:SimSun;font-size:18px;">function net = cnnapplygrads(net, opts)  
  2.     for l = 2 : numel(net.layers)  
  3.         if strcmp(net.layers{l}.type, 'c')  
  4.             for j = 1 : numel(net.layers{l}.a)  
  5.                 for ii = 1 : numel(net.layers{l - 1}.a)  
  6.                       
  7.                     %这里没什么好说的,就是普通的权值更新的公式:W_new = W_old - alpha * de/dW(误差对权值导数)   
  8.                     net.layers{l}.k{ii}{j} = net.layers{l}.k{ii}{j} - opts.alpha * net.layers{l}.dk{ii}{j};  
  9.                 end  
  10.                 net.layers{l}.b{j} = net.layers{l}.b{j} - opts.alpha * net.layers{l}.db{j};  
  11.             end  
  12.         end  
  13.     end  
  14.   
  15.     net.ffW = net.ffW - opts.alpha * net.dffW;  
  16.     net.ffb = net.ffb - opts.alpha * net.dffb;  
  17. end</span>  

0 0
原创粉丝点击