CNN代码学习

本程序是来自deepLearnToolbox-master里面的经典cnn程序

看了前面2篇关于卷积神经网络的博客,应该对卷积神经网络有了大概的了解吧!如下,我将介绍我个人所学的东西。

首先CNN的整体网络结构为:

 

 

在了解卷积神经网络前,首先要理解卷积是如何进行计算的:

 

一.比如次博客的:http://blog.csdn.net/what_lei/article/details/49019049关于对convn讲解的就很好,

其中conv2就是convn2维时的卷积。

语法格式:

 w=convnu,v;

计算矩阵uv的卷积,w的尺寸为sizeu+sizev-1

w=convnu,v,'shape';

返回卷积的一部分,这部分有参数shape决定:

full  返回完整的卷积(默认);

same  返回卷积的中心部分,与u有相同的大小;

valid 仅返回卷积中的那些被计算而没有填充零的部分,w的尺寸大小为max(size(u)-size(v)+1,0).

示例见:

http://blog.csdn.net/what_lei/article/details/49019049关于对convn讲解


 

 

CNN学习的代码注释:

1.主程序:

test_example_CNN.m

 

%function test_example_CNN
clc;
clear all;
load mnist_uint8;
 
train_x = double(reshape(train_x',28,28,60000))/255;%训练样本60000张,每张图片大小为28*28,并把数据归一化
test_x = double(reshape(test_x',28,28,10000))/255;%测试样本10000张,每张图片大小为28*28,并把数据归一化
train_y = double(train_y');%训练样本的标签
test_y = double(test_y');%测试样本的标签
% imshow(train_x(:,:,1)');
% [~,index]=max(train_y(:,1));
%  num=index-1
 
%% ex1 Train a 6c-2s-12c-2s Convolutional neural network
%will run 1 epoch in about 200 second and get around 11% error.
%With 100 epochs you'll get around 1.2% error
 
cnn.layers = {
    struct('type','i')%input layer
    struct('type','c','outputmaps', 6,'kernelsize', 5)%convolution layer
    struct('type','s','scale', 2)%sub sampling layer
    struct('type','c','outputmaps', 12,'kernelsize', 5)%convolution layer
    struct('type','s','scale', 2)%subsampling layer
};
cnn = cnnsetup(cnn, train_x, train_y); %初始化cnn网络
 
opts.alpha = 1;%学习率为1,及相当于不用
opts.batchsize = 50;%对数据批处理的样本数量
opts.numepochs = 10;%迭代次数1
 
cnn = cnntrain(cnn, train_x, train_y, opts);
 
[er, bad] = cnntest(cnn, test_x, test_y);
 
%plot mean squared error
figure;
plot(cnn.rL);
 
assert(er<0.12, 'Too big error');%0.111   0.1179    0.0436(迭代5次)
er


 

 

cnnsetup.m

 

function net = cnnsetup(net, x, y)%x为输入的数据,y为对应的标签,net为网络
inputmaps = 1;
mapsize = size(squeeze(x(:, :, 1)));%每张图片的尺寸为28*28,,squeeze(x(:, :, 1))移除单一维,具体看help squeeze
% mapsize 显示图片的尺寸
for l = 1 : numel(net.layers)   %  layer  numel该语句返回数组中元素的总数numel(cnn.layers)=5
    if strcmp(net.layers{l}.type,'s')%两个字符串相等的时候为1,否则为0  为s的往下执行,s表示的是降采样subsampling
        mapsize = mapsize / net.layers{l}.scale;%scale=2,由28*28变为14*14
        %如果不满足all(floor(mapsize)==mapsize),则会提示错误。
        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;%偏置初始化为0
        end
    end
    if strcmp(net.layers{l}.type,'c')%两个字符串相等的时候为1,否则为0  为c的往下执行,c表示的是卷积层
        mapsize = mapsize - net.layers{l}.kernelsize + 1;%卷积层的特征映射尺寸为28-5+1即24*24
        fan_out = net.layers{l}.outputmaps * net.layers{l}.kernelsize ^ 2;%1*6*5*5
        %fan_out该层的所有连接的数量=卷积核数*卷积核的尺寸(m*n)
        
        %对于卷积核权重,输入输出为fan_in, fan_out
        for j = 1 : net.layers{l}.outputmaps  %  output map
            fan_in = inputmaps * net.layers{l}.kernelsize ^ 2;%1*5*5
            for i = 1 : inputmaps  %  input map
                %产生0到1的随机数,减去0.5,即变成-0.5到0.5的随机数,再乘以2,再乘以sqrt(6/6*5*5+1*5*5),第二层卷积则为sqrt(6/(12*5*5+6*5*5))
                net.layers{l}.k{i}{j} = (rand(net.layers{l}.kernelsize) - 0.5) * 2 * sqrt(6 / (fan_in + fan_out));%权重  来自bengio的Understanding the difficulty of training deep feedforward neuralnetworks
            end
            net.layers{l}.b{j} = 0;%偏置依旧初始化为0
        end
        inputmaps = net.layers{l}.outputmaps;%运行一次后inputmaps变成6了,第二次变为12
    end
end
% '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.
% 'fvnum' is the number of output neurons at the last layer, the layer just before the output layer.
% 'ffb' is the biases of the output neurons.
% '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)
fvnum = prod(mapsize) * inputmaps;%mapsize变成4*4了的inputmaps为12,fvnum是最后一层的输出神经元
% inputmaps
% mapsize
onum = size(y, 1);%标签数目
 
net.ffb = zeros(onum, 1);%偏置的输出神经元
net.ffW = (rand(onum, fvnum) - 0.5) * 2 * sqrt(6 / (onum + fvnum));%最后一层与输出层是全连接,ffW代表的就是权重  
end
 


 


 

cnntrain.m

 

function net = cnntrain(net, x, y, opts)
%     net=cnn;
%     x=train_x;
%     y=train_y;
 
m = size(x, 3);%m表示训练样本的个数,为60000
numbatches = m / opts.batchsize;%批量处理的次数为1200,每次批量处理50张图片
if rem(numbatches, 1) ~= 0%如果不是整数,则会报错
    error('numbatches not integer');
end
net.rL = [];
for i = 1 : opts.numepochs  %迭代次数
    disp(['epoch ' num2str(i)'/' num2str(opts.numepochs)]);
    tic;
    kk = randperm(m);
    for l = 1 : numbatches%批量处理的次数为1200
        %每次批量处理50张图片
        batch_x = x(:, :, kk((l - 1) * opts.batchsize + 1 : l * opts.batchsize));
        batch_y = y(:,    kk((l - 1) * opts.batchsize + 1 : l * opts.batchsize));
        
        %信号前向传导过程feedforward
        net = cnnff(net, batch_x);
        %计算误差反向传导,backpropagation
        net = cnnbp(net, batch_y);
        %更新模型
        net = cnnapplygrads(net, opts);
        if isempty(net.rL)
            net.rL(1) = net.L;%% net.L为50个样本的误差函数
        end
        net.rL(end + 1) = 0.99 * net.rL(end) + 0.01 * net.L;
    end
    toc;
end
 
end
 


 

 

 

cnnff.m

 

function net = cnnff(net, x)
n = numel(net.layers);%cnn网络的层数
%  net.layers{1}.a{1}=batch_x;
net.layers{1}.a{1} = x;%第一层的输入,x包含有批量处理的50张图片
inputmaps = 1;%输入层只有一个特征map
 
for l = 2 : n   %  for each layer
    if strcmp(net.layers{l}.type,'c')  %如果是卷积层,则执行如下程序
        %  !!below can probably be handled by insane matrix operations
        for j = 1 : net.layers{l}.outputmaps   %  for each output map
            %  create temp output map
            z = zeros(size(net.layers{l - 1}.a{1}) - [net.layers{l}.kernelsize - 1 net.layers{l}.kernelsize - 1 0]);%[28 28 50]-[4 4 0]
            for i = 1 : inputmaps   %  for each input map
                %  convolve with corresponding kernel and add to temp output map
                z = z + convn(net.layers{l - 1}.a{i}, net.layers{l}.k{i}{j},'valid');%k{i}{j}表示的是k里的第i个,然后再里面找出第j个矩阵
            end
            %  add bias, pass through nonlinearity
            net.layers{l}.a{j} = sigm(z + net.layers{l}.b{j});%sigmoid(Wx+b)  6个a  24*24*50的卷积特征
        end
        %  set number of input maps to this layers number of outputmaps
        inputmaps = net.layers{l}.outputmaps;%第一次输出为6个卷积特征z
    elseif strcmp(net.layers{l}.type,'s')
        %  downsample
        for j = 1 : inputmaps
            z = convn(net.layers{l - 1}.a{j}, ones(net.layers{l}.scale) / (net.layers{l}.scale ^ 2),'valid');   %  !! replace with variable 23*23*50
            net.layers{l}.a{j} = z(1 : net.layers{l}.scale : end, 1 : net.layers{l}.scale : end, :);%第一次降采样成12*12*50
        end
    end
end
 
 
 
 
%  concatenate all end layer feature maps into vector
net.fv = [];%最后一层进行全连接
for j = 1 : numel(net.layers{n}.a)   %numel(net.layers{n}.a)=12
    sa = size(net.layers{n}.a{j});%[4 4 50]
    net.fv = [net.fv; reshape(net.layers{n}.a{j}, sa(1) * sa(2), sa(3))];    % net.fv相当于教程中的a,其中a=f(z)的
end
%size(net.fv)=[192 50]  12个  4*4  *50         12*16=192
%  feedforward into output perceptrons
net.o = sigm(net.ffW * net.fv + repmat(net.ffb, 1, size(net.fv, 2)));%ffW为10*192  在cnnsetup里面进行的随机初始化
%net.ffW [10 192]   net.fv为:[192 50]的矩阵
end
 


 

 

 

 

cnnbp.m

 此程序参考的资料见:Notes on Convolutional Neural Networks, Jake Bouvrie

function net = cnnbp(net, y)
%具体推导过程参见:http://deeplearning.stanford.edu/wiki/index.php/反向传导算法
% y=batch_y;
n = numel(net.layers);%n=5
 
%  error
net.e = net.o - y;%误差
%  loss function
net.L = 1/2* sum(net.e(:) .^ 2) / size(net.e, 2);%50个样本的误差函数
 
%%  backprop deltas
net.od = net.e .* (net.o .* (1 - net.o));   %  output delta     (net.o .* (1 - net.o))表示的是f(zi)对zi求导的结果  net.e=-(yi-ai)  输出的delta,输出层室友sigmoid函数的,所以要求导
net.fvd = (net.ffW' * net.od);              %  feature vector delta       隐藏层的delta,不过还要乘以f'(z),但是cnn中网络的采样层没有sigmoid函数,所以不需要乘以f'(z)=(1-f(z))*f(z)
if strcmp(net.layers{n}.type,'c')         %  only conv layers has sigm function
    net.fvd = net.fvd .* (net.fv .* (1 - net.fv));% net.fv相当于教程中的a,其中a=f(z),但是cnn中网络的卷积层每次输出都有sigmoid函数,所以需要乘以f'(z)=(1-f(z))*f(z)
    haha=1%整个程序并没有执行就是因为最后一层是采样层,不是卷积层
end
 
%  reshape feature vector deltas into output map style
sa = size(net.layers{n}.a{1});%sa[4 4 50],即最后的采样层的大小
fvnum = sa(1) * sa(2);
for j = 1 : numel(net.layers{n}.a)  %numel(net.layers{n}.a)=12  共有12个不同权重
    net.layers{n}.d{j} = reshape(net.fvd(((j - 1) * fvnum + 1) : j * fvnum, :), sa(1), sa(2), sa(3));%每一个都是16*50,即4*4*50  d保存的就是对应层(n层)的delta
end
 
for l = (n - 1) : -1 : 1
    if strcmp(net.layers{l}.type,'c')%公式(9)
        for j = 1 : numel(net.layers{l}.a)  % numel(net.layers{4}.a)=12张8*8的图片   [net.layers{l + 1}.scale net.layers{l + 1}.scale 1]=[2 2 1]   d{j}=12个4*4*50的矩阵    net.layers{l + 1}.scale ^ 2=4
            % net.layers{l}.d{j}与采样层net.layers{l + 1}.d{j}有关
            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);%
        end
    elseif strcmp(net.layers{l}.type,'s')
        for i = 1 : numel(net.layers{l}.a)  %numel(net.layers{l}.a)=6
            z = zeros(size(net.layers{l}.a{1})); %a{1}为6个12*12*50的矩阵
            for j = 1 : numel(net.layers{l + 1}.a)%numel(net.layers{l + 1}.a)=12
                z = z + convn(net.layers{l + 1}.d{j}, rot180(net.layers{l + 1}.k{i}{j}),'full');
            end
            net.layers{l}.d{i} = z;
        end
    end
end
 
%%  calc gradients
for l = 2 : n  %hl为卷积层
    if strcmp(net.layers{l}.type,'c')%l=2时
        for j = 1 : numel(net.layers{l}.a)% numel(net.layers{l}.a)=6
            for i = 1 : numel(net.layers{l - 1}.a)%numel(net.layers{l - 1}.a)=1
                %flipall相当于rot180,将矩阵旋转180度
                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);%公式12
            end
            net.layers{l}.db{j} = sum(net.layers{l}.d{j}(:)) / size(net.layers{l}.d{j}, 3);%size(net.layers{l}.d{j}, 3)=50,每张24*24*50小图片的平均误差
        end
    end
end
%参见机器学习20讲里面的第180面的最小面公式(hl为全连接)
net.dffW = net.od * (net.fv)' / size(net.od, 2);%size(net.od, 2)=50  net.fv:前向网络求出的参数
net.dffb = mean(net.od, 2);%对行取平均   net.od为输出的delta
 
end
 
 


 

cnnapplygrads.m

 

function net = cnnapplygrads(net, opts)
    for l = 2 : numel(net.layers)
        if strcmp(net.layers{l}.type,'c')
            for j = 1 : numel(net.layers{l}.a)
                for ii = 1 : numel(net.layers{l - 1}.a)
                    net.layers{l}.k{ii}{j} = net.layers{l}.k{ii}{j} - opts.alpha * net.layers{l}.dk{ii}{j};
                end
                net.layers{l}.b{j} = net.layers{l}.b{j} - opts.alpha * net.layers{l}.db{j};
            end
        end
    end
 
    net.ffW = net.ffW - opts.alpha * net.dffW;
    net.ffb = net.ffb - opts.alpha * net.dffb;
end
 


 

 

一些小的子函数:

 

sigm.m

 

function X = sigm(P)
    X = 1./(1+exp(-P));
end


 

 

 

 

rot180.m

 

function X = rot180(X)
     X = flipdim(flipdim(X, 1), 2);
end
 


 

flipall.m

function X=flipall(X)
    for i=1:ndims(X)
        X = flipdim(X,i);
    end
end

 

 

 

expand.m

 

 

function B = expand(A, S)
%EXPAND Replicate and tile each element of an array, similar to repmat.
% EXPAND(A,SZ), for array A and vector SZ replicates each element of A by
% SZ.  The results are tiled into an array in the same order as the
% elements of A, so that the result is size:  size(A).*SZ. Therefore the
% number of elements of SZ must equal the number of dimensions of A, or in
% MATLAB syntax: length(size(A))==length(SZ) must be true.  
% The result will have the same number of dimensions as does A.  
% There is no restriction on the number of dimensions for input A.
%
% Examples:
%
%   A = [1 2; 3 4]; % 2x2
%   SZ = [6 5];
%   B = expand(A,[6 5]) % Creates a 12x10 array.
%
%   The following demonstrates equivalence of EXPAND and expansion acheived
%   through indexing the individual elements of the array:
%
%   A = 1; B = 2; C = 3; D = 4;  % Elements of the array to be expanded.
%   Mat = [A B;C D];  % The array to expand.
%   SZ = [2 3];  % The expansion vector.
%   ONES = ones(SZ);  % The index array.
%   ExpMat1 = [A(ONES),B(ONES);C(ONES),D(ONES)]; % Element expansion.
%   ExpMat2 = expand(Mat,SZ); % Calling EXPAND.
%   isequal(ExpMat1,ExpMat2)  % Yes
%
%
% See also, repmat, meshgrid, ones, zeros, kron
%
% Author: Matt Fig
% Date: 6/20/2009
% Contact:  popkenai@yahoo.com
 
if nargin < 2
    error('Size vector must be provided.  See help.');
end
 
SA = size(A);  % Get the size (and number of dimensions) of input.
 
if length(SA) ~= length(S)
   error('Length of size vector must equal ndims(A).  See help.')
elseif any(S ~= floor(S))
   error('The size vector must contain integers only.  See help.')
end
 
T = cell(length(SA), 1);
for ii = length(SA) : -1 : 1
    H = zeros(SA(ii) * S(ii), 1);   %  One index vector into A for each dim.
    H(1 : S(ii) : SA(ii) * S(ii)) = 1;   %  Put ones in correct places.
    T{ii} = cumsum(H);   %  Cumsumming creates the correct order.
end
 
B = A(T{:});   %  Feed the indices into A.
 


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值