您好,欢迎访问三七文档
CNN代码理解下面是自己对代码的注释:cnnexamples.mclearall;closeall;clc;addpath('../data');addpath('../util');loadmnist_uint8;train_x=double(reshape(train_x',28,28,60000))/255;test_x=double(reshape(test_x',28,28,10000))/255;train_y=double(train_y');test_y=double(test_y');%%ex1%willrun1epochinabout200secondandgetaround11%error.%With100epochsyou'llgetaround1.2%errorcnn.layers={struct('type','i')%inputlayerstruct('type','c','outputmaps',6,'kernelsize',5)%convolutionlayerstruct('type','s','scale',2)%subsamplinglayerstruct('type','c','outputmaps',12,'kernelsize',5)%convolutionlayerstruct('type','s','scale',2)%subsamplinglayer};%这里把cnn的设置给cnnsetup,它会据此构建一个完整的CNN网络,并返回cnn=cnnsetup(cnn,train_x,train_y);%学习率opts.alpha=1;%每次挑出一个batchsize的batch来训练,也就是每用batchsize个样本就调整一次权值,而不是%把所有样本都输入了,计算所有样本的误差了才调整一次权值opts.batchsize=50;%训练次数,用同样的样本集。我训练的时候:%1的时候11.41%error%5的时候4.2%error%10的时候2.73%erroropts.numepochs=10;%然后开始把训练样本给它,开始训练这个CNN网络cnn=cnntrain(cnn,train_x,train_y,opts);%然后就用测试样本来测试[er,bad]=cnntest(cnn,test_x,test_y);%plotmeansquarederrorplot(cnn.rL);%showtesterrordisp([num2str(er*100)'%error']);cnnsetup.mfunctionnet=cnnsetup(net,x,y)inputmaps=1;%B=squeeze(A)返回和矩阵A相同元素但所有单一维都移除的矩阵B,单一维是满足size(A,dim)=1的维。%train_x中图像的存放方式是三维的reshape(train_x',28,28,60000),前面两维表示图像的行与列,%第三维就表示有多少个图像。这样squeeze(x(:,:,1))就相当于取第一个图像样本后,再把第三维%移除,就变成了28x28的矩阵,也就是得到一幅图像,再size一下就得到了训练样本图像的行数与列数了mapsize=size(squeeze(x(:,:,1)));%下面通过传入net这个结构体来逐层构建CNN网络%n=numel(A)返回数组A中元素个数%net.layers中有五个struct类型的元素,实际上就表示CNN共有五层,这里范围的是5forl=1:numel(net.layers)%layerifstrcmp(net.layers{l}.type,'s')%如果这层是子采样层%subsampling层的mapsize,最开始mapsize是每张图的大小28*28%这里除以scale=2,就是pooling之后图的大小,pooling域之间没有重叠,所以pooling后的图像为14*14%注意这里的右边的mapsize保存的都是上一层每张特征map的大小,它会随着循环进行不断更新mapsize=floor(mapsize/net.layers{l}.scale);forj=1:inputmaps%inputmap就是上一层有多少张特征图net.layers{l}.b{j}=0;%将偏置初始化为0endendifstrcmp(net.layers{l}.type,'c')%如果这层是卷积层%旧的mapsize保存的是上一层的特征map的大小,那么如果卷积核的移动步长是1,那用%kernelsize*kernelsize大小的卷积核卷积上一层的特征map后,得到的新的map的大小就是下面这样mapsize=mapsize-net.layers{l}.kernelsize+1;%该层需要学习的参数个数。每张特征map是一个(后层特征图数量)*(用来卷积的patch图的大小)%因为是通过用一个核窗口在上一个特征map层中移动(核窗口每次移动1个像素),遍历上一个特征map%层的每个神经元。核窗口由kernelsize*kernelsize个元素组成,每个元素是一个独立的权值,所以%就有kernelsize*kernelsize个需要学习的权值,再加一个偏置值。另外,由于是权值共享,也就是%说同一个特征map层是用同一个具有相同权值元素的kernelsize*kernelsize的核窗口去感受输入上一%个特征map层的每个神经元得到的,所以同一个特征map,它的权值是一样的,共享的,权值只取决于%核窗口。然后,不同的特征map提取输入上一个特征map层不同的特征,所以采用的核窗口不一样,也%就是权值不一样,所以outputmaps个特征map就有(kernelsize*kernelsize+1)*outputmaps那么多的权值了%但这里fan_out只保存卷积核的权值W,偏置b在下面独立保存fan_out=net.layers{l}.outputmaps*net.layers{l}.kernelsize^2;forj=1:net.layers{l}.outputmaps%outputmap%fan_out保存的是对于上一层的一张特征map,我在这一层需要对这一张特征map提取outputmaps种特征,%提取每种特征用到的卷积核不同,所以fan_out保存的是这一层输出新的特征需要学习的参数个数%而,fan_in保存的是,我在这一层,要连接到上一层中所有的特征map,然后用fan_out保存的提取特征%的权值来提取他们的特征。也即是对于每一个当前层特征图,有多少个参数链到前层fan_in=inputmaps*net.layers{l}.kernelsize^2;fori=1:inputmaps%inputmap%随机初始化权值,也就是共有outputmaps个卷积核,对上层的每个特征map,都需要用这么多个卷积核%去卷积提取特征。%rand(n)是产生n×n的0-1之间均匀取值的数值的矩阵,再减去0.5就相当于产生-0.5到0.5之间的随机数%再*2就放大到[-1,1]。然后再乘以后面那一数,why?%反正就是将卷积核每个元素初始化为[-sqrt(6/(fan_in+fan_out)),sqrt(6/(fan_in+fan_out))]%之间的随机数。因为这里是权值共享的,也就是对于一张特征map,所有感受野位置的卷积核都是一样的%所以只需要保存的是inputmaps*outputmaps个卷积核。net.layers{l}.k{i}{j}=(rand(net.layers{l}.kernelsize)-0.5)*2*sqrt(6/(fan_in+fan_out));endnet.layers{l}.b{j}=0;%将偏置初始化为0end%只有在卷积层的时候才会改变特征map的个数,pooling的时候不会改变个数。这层输出的特征map个数就是%输入到下一层的特征map个数inputmaps=net.layers{l}.outputmaps;endend%fvnum是输出层的前面一层的神经元个数。%这一层的上一层是经过pooling后的层,包含有inputmaps个特征map。每个特征map的大小是mapsize。%所以,该层的神经元个数是inputmaps*(每个特征map的大小)%prod:Productofelements.%Forvectors,prod(X)istheproductoftheelementsofX%在这里mapsize=[特征map的行数特征map的列数],所以prod后就是特征map的行*列fvnum=prod(mapsize)*inputmaps;%onum是标签的个数,也就是输出层神经元的个数。你要分多少个类,自然就有多少个输出神经元onum=size(y,1);%这里是最后一层神经网络的设定%ffb是输出层每个神经元对应的基biasesnet.ffb=zeros(onum,1);%ffW输出层前一层与输出层连接的权值,这两层之间是全连接的net.ffW=(rand(onum,fvnum)-0.5)*2*sqrt(6/(onum+fvnum));endcnntrain.mfunctionnet=cnntrain(net,x,y,opts)m=size(x,3);%m保存的是训练样本个数numbatches=m/opts.batchsize;%rem:Remainderafterdivision.rem(x,y)isx-n.*y相当于求余%rem(numbatches,1)就相当于取其小数部分,如果为0,就是整数ifrem(numbatches,1)~=0error('numbatchesnotinteger');endnet.rL=[];fori=1:opts.numepochs%disp(X)打印数组元素。如果X是个字符串,那就打印这个字符串disp(['epoch'num2str(i)'/'num2str(opts.numepochs)]);%tic和toc是用来计时的,计算这两条语句之间所耗的时间tic;%P=randperm(N)返回[1,N]之间所有整数的一个随机的序列,例如%randperm(6)可能会返回[245613]%这样就相当于把原来的样本排列打乱,再挑出一些样本来训练kk=randperm(m);forl=1:numbatches%取出打乱顺序后的batchsize个样本和对应的标签batch_x=x(:,:,kk((l-1)*opts.batchsize+1:l*opts.batchsize));batch_y=y(:,kk((l-1)*opts.batchsize+1:l*opts.batchsize));%在当前的网络权值和网络输入下计算网络的输出net=cnnff(net,batch_x);%Feedforward%得到上面的网络输出后,通过对应的样本标签用bp算法来得到误差对网络权值%(也就是那些卷积核的元素)的导数net=cnnbp(net,batch_y);%Backpropagation%得到误差对权值的导数后,就通过权值更新方法去更新权值net=cnnapplygrads(net,opts);ifisempty(net.rL)net.rL(1)=net.L;%代价函数值,也就是误差值endnet.rL(end+1)=0.99*net.rL(end)+0.01*net.L;%保存历史的误差值,以便画图分析endtoc;endendcnnff.mfunctionnet=cnnff(net,x)n=numel(net.layers);%层数net.layers{1}.a{1}=x;%网络的第一层就是输入,但这里的输入包含了多个训练图像inputmaps=1;%输入层只有一个特征map,也就是原始的输入图像forl=2:n%foreachlayerifstrcmp(net.laye
本文标题:CNN代码理解
链接地址:https://www.777doc.com/doc-4209452 .html