深度学习Matlab工具箱代码注释之cnnsetup.m

时间:2022-04-24
本文章向大家介绍深度学习Matlab工具箱代码注释之cnnsetup.m,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
%%=========================================================================  
% 函数名称:cnnsetup  
% 输入参数:net,待设置的卷积神经网络;x,训练样本;y,训练样本对应标签;  
% 输出参数:net,初始化完成的卷积神经网络  
% 主要功能:对CNN的结构进行初始化  
% 算法流程:1)  
% 注意事项:1)isOctave这个语句是为了抛出一个程序在Octave平台上运行时的一个BUG,在matlab平台上可以直接注释掉  
%          2)net.layers中有五个struct类型的元素,实际上就表示CNN共有五层,这里范围的是5 
%%=========================================================================  
function net = cnnsetup(net, x, y)  
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]);  
inputmaps = 1;    %初始化网络输入层数为1层  
 
%%=========================================================================  
% 主要功能:得到输入图像的行数和列数  
% 注意事项: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网络%%%%%%%%%%%%%%%%%%%%  
for l = 1 : numel(net.layers)                    %对于每一层  
 if strcmp(net.layers{l}.type, 's')           %如果当前层是下采样层  
 
        %%=========================================================================  
        % 主要功能:获取下采样之后特征map的尺寸  
        % 注意事项:1)subsampling层的mapsize,最开始mapsize是每张图的大小28*28 
        %             这里除以scale=2,就是pooling之后图的大小,pooling域之间没有重叠,所以pooling后的图像为14*14 
        %             注意这里的右边的mapsize保存的都是上一层每张特征map的大小,它会随着循环进行不断更新  
        %%=========================================================================  
        mapsize = mapsize / net.layers{l}.scale;  
 assert(all(floor(mapsize)==mapsize), ['Layer ' num2str(l) ' size must be integer. Actual: ' num2str(mapsize)]);  
 
 for j = 1 : inputmaps          %对于上一层的每个特征图  
            net.layers{l}.b{j} = 0;    %将偏置初始化为零  
        end  
    end  
 
 if strcmp(net.layers{l}.type, 'c') %如果当前层是卷基层  
        %%=========================================================================  
        % 主要功能:获取卷积后的特征map尺寸以及当前层待学习的卷积核的参数数量  
        % 注意事项:1)旧的mapsize保存的是上一层的特征map的大小,那么如果卷积核的移动步长是1,那用  
        %             kernelsize*kernelsize大小的卷积核卷积上一层的特征map后,得到的新的map的大小就是下面这样  
        %          2)fan_out代表该层需要学习的参数个数。每张特征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在下面独立保存  
        %%=========================================================================  
        mapsize = mapsize - net.layers{l}.kernelsize + 1;  
        fan_out = net.layers{l}.outputmaps * net.layers{l}.kernelsize ^ 2;  
 
 for j = 1 : net.layers{l}.outputmaps   %对于卷积层的每一个输出map  
            %%=========================================================================  
            % 主要功能:获取卷积层与前一层输出map之间需要链接的参数链个数  
            % 注意事项:1)fan_out保存的是对于上一层的一张特征map,我在这一层需要对这一张特征map提取outputmaps种特征,  
            %             提取每种特征用到的卷积核不同,所以fan_out保存的是这一层输出新的特征需要学习的参数个数  
            %             而,fan_in保存的是,我在这一层,要连接到上一层中所有的特征map,然后用fan_out保存的提取特征  
            %             的权值来提取他们的特征。也即是对于每一个当前层特征图,有多少个参数链到前层  
            %%=========================================================================  
            fan_in = inputmaps * net.layers{l}.kernelsize ^ 2;  
 
 for i = 1 : inputmaps                 %对于上一层的每一个输出特征map(本层的输入map)  
                %%=========================================================================  
                % 主要功能:随机初始化卷积核的权值,再将偏置均初始化为零  
                % 注意事项:1)随机初始化权值,也就是共有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 个卷积核。  
                %          2)为什么这里是inputmaps * outputmaps个卷积核?  
                %%=========================================================================  
                net.layers{l}.k{i}{j} = (rand(net.layers{l}.kernelsize) - 0.5) * 2 * sqrt(6 / (fan_in + fan_out));  
            end  
            net.layers{l}.b{j} = 0;  
        end  
        inputmaps = net.layers{l}.outputmaps;    %在卷积层会更新每层网络的输出map数量  
    end  
end  
 
%%=========================================================================  
% 主要功能:初始化最后一层,也就是输出层的参数值  
% 算法流程:1)fvnum 是输出层的前面一层的神经元个数。这一层的上一层是经过pooling后的层,包含有inputmaps个  
%             特征map。每个特征map的大小是mapsize,所以,该层的神经元个数是 inputmaps * (每个特征map的大小)    
%          2)onum 是标签的个数,也就是输出层神经元的个数。你要分多少个类,自然就有多少个输出神经元  
%          3)net.ffb和net.ffW为最后一层(全连接层)的偏置和权重  
%%=========================================================================  
fvnum = prod(mapsize) * inputmaps;  
onum = size(y, 1);  
 
net.ffb = zeros(onum, 1);  
net.ffW = (rand(onum, fvnum) - 0.5) * 2 * sqrt(6 / (onum + fvnum));  
end  

量化投资与机器学习

知识、能力、深度、专业

勤奋、天赋、耐得住寂寞