CNN 用于手写体识别 matlab 代码理解

1. 加载数据集并划分训练数据与测试数据

% function test_example_CNN
load mnist_uint8;
%该模型采用的数据为mnist_uint8.mat
%含有70000个手写数字样本其中60000作为训练样本,10000作为测试样本,把数据转成相应的格式,并归一化。
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');

2. 设置网络结构及训练参数

%设置网络结构及训练参数
rand('state',0)

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
};
%训练选项,alpha学习率(不用),batchsize批训练中样本的数量,numepochs迭代次数。
opts.alpha = 1;
opts.batchsize = 50;
opts.numepochs = 5;

3. 初始化网络

cnn = cnnsetup(cnn, train_x, train_y);
%初始化网络
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;%输入图片数量
    mapsize = size(squeeze(x(:, :, 1)));%获取训练图像的大小

     % 下面通过传入net这个结构体来逐层构建CNN网络  
    for l = 1 : numel(net.layers)
        if strcmp(net.layers{l}.type, 's')
            % 如果这层是下采样层  
            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; %将偏置初始化为0
            end
        end
        if strcmp(net.layers{l}.type, 'c')
            % 如果这层是卷积层,且步长为 1 
            mapsize = mapsize - net.layers{l}.kernelsize + 1;
            %outputmaps表示卷积核的个数,fan_out表示卷积层需要的总参数个数
            fan_out = net.layers{l}.outputmaps * net.layers{l}.kernelsize ^ 2;
            %遍历每个卷积核
            for j = 1 : net.layers{l}.outputmaps  
                %所有输入图片每个卷积核需要的参数个数
                fan_in = inputmaps * net.layers{l}.kernelsize ^ 2;
                for i = 1 : inputmaps
                    %为每张图片的每个卷积核随机初始化权值,
                    %每个卷积核的权值是一个kernelsize*kernelsize的矩阵
                    % rand(n)是产生n×n的 0-1之间均匀取值的数值的矩阵,再减去0.5就相当于产生-0.5到0.5之间的随机数  
                    % 再 *2 就放大到 [-1, 1] 
                    % 反正就是将卷积核每个元素初始化为[-sqrt(6 / (fan_in + fan_out)), sqrt(6 / (fan_in + fan_out))]  
                    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
             % 只有在卷积层的时候才会改变特征map的个数,pooling的时候不会改变个数。这层输出的特征map个数就是  
            % 输入到下一层的特征map个数 
            inputmaps = net.layers{l}.outputmaps;
        end
    end
   % fvnum 是输出层的前面一层的神经元个数。  
    % 这一层的上一层是经过pooling后的层,包含有inputmaps个特征map。每个特征map的大小是mapsize。  
    % 所以,该层的神经元个数是 inputmaps * (每个特征map的大小)  
    % 在这里 mapsize = [特征map的行数 特征map的列数],所以prod后就是 特征map的行*列  
    fvnum = prod(mapsize) * inputmaps;
    % onum 是标签的个数,也就是输出层神经元的个数。你要分多少个类,自然就有多少个输出神经元 
    onum = size(y, 1);

    % 这里是最后一层神经网络的设定,即全连接层
    % ffb 是输出层每个神经元对应的基biases 
    net.ffb = zeros(onum, 1);
    % ffW 输出层前一层 与 输出层 连接的权值,这两层之间是全连接的
    net.ffW = (rand(onum, fvnum) - 0.5) * 2 * sqrt(6 / (onum + fvnum));
end

4. 根据初始化的参数对数据进行训练

cnn = cnntrain(cnn, train_x, train_y, opts);
  • 对输入图片分批次
  • 迭代开始,抽取每个批次的训练样本
  • 计算每个批次的网络输出
  • 根据上面的网络输出,根据真实标签用bp算法计算误差对网络权值(卷积核参数)的导数
  • 根据导数对网络参数进行更新
  • 计算误差

猜你喜欢

转载自blog.csdn.net/weixin_42970026/article/details/83279011