MATLAB深度学习 : 一般深度学习MATLAB代码解析

深度学习系列(五):一个简单深度学习工具箱原创 



首先介绍下一般网络的建立模型,找到在工具箱 
DeepLearnToolbox\tests\test_example_NN.m文件,这个测试函数是测试一般的网络模型,取前一段代码:

load mnist_uint8;
train_x = double(train_x) / 255;
test_x  = double(test_x)  / 255;
train_y = double(train_y);
test_y  = double(test_y);

% normalize
[train_x, mu, sigma] = zscore(train_x);
test_x = normalize(test_x, mu, sigma);

%% ex1 vanilla neural net
rand('state',0)
nn = nnsetup([784 100 10]);
opts.numepochs =  1;   %  Number of full sweeps through data
opts.batchsize = 100;  %  Take a mean gradient step over this many samples
[nn, L] = nntrain(nn, train_x, train_y, opts);
[er, bad] = nntest(nn, test_x, test_y);
assert(er < 0.08, 'Too big error');
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

使用到的是手写体数据库,该数据库已经集成到工具箱了,直接用就好,显示看一看该数据库的一部分,其目标就是通过训练该数据库达到识别的目的: 


紧接着是对该数据库数据进行归一化等等预处理。nnsetup建立一个网络,里面会有许多参数初始化,同时在设置下opts.numepochs = 1; 该参数个人感觉就是将所有数据重复试验次数,设置1就是实验一次。opts.batchsize = 100;该参数是将大量样本每随机100个作为一波送进去实验。再就是训练测试了。Ok来看看nnsetup:

function nn = nnsetup(architecture)
%NNSETUP创建前向神经网络
% nn = nnsetup(architecture) 返回一个神经网络结构,architecture为结构参数
% architecture 是一个n x 1 向量,表示每一层神经元的个数
%比如architecture=[784 100 10],表示输入层为784维输入,100个隐含层,10个输出层
%为什么是输入为784:因为每一个手写体大小为28*28的,也就是784维度
%隐含层为什么是100:随便设置的,可以随意修改,需要设计
%输出为什么是10:手写体有0-9这10种结果,所以为10

    nn.size   = architecture;
    nn.n      = numel(nn.size);

    nn.activation_function              = 'tanh_opt';   % 隐含层激活函数: 'sigm' (sigmoid) or 'tanh_opt' (默认 tanh).
    nn.learningRate                     = 2;            %  学习率: typically needs to be lower when using 'sigm' activation function and non-normalized inputs.
    nn.momentum                         = 0.5;          %  Momentum 权值动量因子
    nn.scaling_learningRate             = 1;            %  学习率变化因子 (each epoch)
    nn.weightPenaltyL2                  = 0;            %  L2 regularization正则化
    nn.nonSparsityPenalty               = 0;            %  非稀疏惩罚
    nn.sparsityTarget                   = 0.05;         %  稀疏目标值
    nn.inputZeroMaskedFraction          = 0;            %  自动编码的去噪作用
    nn.dropoutFraction                  = 0;            %  Dropout level (http://www.cs.toronto.edu/~hinton/absps/dropout.pdf)
    nn.testing                          = 0;            %  Internal variable. nntest sets this to one.
    nn.output                           = 'sigm';       %  输出激活output unit 'sigm' (=logistic), 'softmax' and 'linear'

    for i = 2 : nn.n   
        % weights and weight momentum
        nn.W{i - 1} = (rand(nn.size(i), nn.size(i - 1)+1) - 0.5) * 2 * 4 * sqrt(6 / (nn.size(i) + nn.size(i - 1)));
        nn.vW{i - 1} = zeros(size(nn.W{i - 1}));

        % average activations (for use with sparsity)
        nn.p{i}     = zeros(1, nn.size(i));   
    end
end
      
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

这个函数理解起来很简单,初始化网络,网络需要什么初始化什么,一大堆初始化是适应所有的网络的(cnn,dbn等等),有些用到了再说吧,现在你只需要知道网络的结构,以及与稀疏编码表示有关的参数: nn.nonSparsityPenalty ,nn.sparsityTarget,这也就是上节说到的,为什么稀疏表示,具体怎么样不用管,实际使用的时候只是这么几个参数设置,其他的交给程序吧。再有就是注意下激活函数 nn.activation_function。,然后网络权值随机初始化。

紧接着是nntrain了,关于这部分,前面说的那个博主介绍的很好,里面注释也多,可以去看看(看完回来哦): 
http://blog.csdn.net/dark_scope/article/details/9421061

这里再说下这个函数整体:[nn, L] = nntrain(nn, train_x, train_y, opts);

可以看到nntrain需要的是设计的网络nn,训练数据train_x,训练对应的目标值train_y,以及附加参数opts。附加参数包括:重复训练次数opts.numepochs,训练数据每一块大小opts.batchsize等等。函数出来的就是训练好的网络nn,这个很重要,训练好的nn为结构体,里面包括你所需要的所有信息,比如说每一层网络的权值系数,训练误差,等等都可以找到,并且在nntest也是用这个训练好的nn。nntrain的具体实现细节上面那个博客的介绍吧。

nntrain

setup大概就这样一个过程,下面就到了train了,打开\NN\nntrain.m

我们跳过那些检验传入数据是否正确的代码,直接到关键的部分

denoising 的部分请参考论文:Extracting and Composing Robust Features with Denoising Autoencoders

[cpp]  view plain  copy
  1. m = size(train_x, 1);  
  2. //m是训练样本的数量  
  3. //注意在调用的时候我们设置了opt,batchsize是做batch gradient时候的大小  
  4. batchsize = opts.batchsize; numepochs = opts.numepochs;  
  5. numbatches = m / batchsize;  //计算batch的数量,训练样本的总个数除以一个batch的                                size,得到batch的总数目  
  6. assert(rem(numbatches, 1) == 0, 'numbatches must be a integer');//即上一步                                            对总数据进行分组,组数应是一个整数
  7. L = zeros(numepochs*numbatches,1);  
  8. n = 1;  
  9. //numepochs是循环的次数  
  10. for i = 1 : numepochs  
  11.     tic;  // tic用来保存当前时间,而后使用toc来记录程序完成时间
  12.     kk = randperm(m);  
  13.     //把batches打乱顺序进行训练,randperm(m)功能是随机打乱一个数字序列,生成一个       乱序的1到m的数组。例如 randperm(5),ans = 2 3 4 1 5
      
  14.     for l = 1 : numbatches  
  15.         batch_x = train_x(kk((l - 1) * batchsize + 1 : l * batchsize), :);  
  16.         //Add noise to input (for use in denoising autoencoder)  
  17.         //加入noise,这是denoising autoencoder需要使用到的部分  
  18.         //这部分请参见《Extracting and Composing Robust Features with Denoising Autoencoders》这篇论文  
  19.         //具体加入的方法就是把训练样例中的一些数据调整变为0,inputZeroMaskedFraction表示了调整的比例  
  20.         if(nn.inputZeroMaskedFraction ~= 0)  
  21.             batch_x = batch_x*
  22. (rand(size(batch_x))>nn.inputZeroMaskedFraction);  
  23.         end  
  24.         batch_y = train_y(kk((l - 1) * batchsize + 1 : l * batchsize), :);  
  25.         //这三个函数  
  26.         //nnff是进行前向传播,nnbp是后向传播,nnapplygrads是进行梯度下降  
  27.         //我们在下面分析这些函数的代码  
  28.         nn = nnff(nn, batch_x, batch_y);  
  29.         nn = nnbp(nn);  
  30.         nn = nnapplygrads(nn);  
  31.         L(n) = nn.L;  
  32.         n = n + 1;  
  33.     end  
  34.       
  35.     t = toc;  
  36.     if ishandle(fhandle)  
  37.         if opts.validation == 1  
  38.             loss = nneval(nn, loss, train_x, train_y, val_x, val_y);  
  39.         else  
  40.             loss = nneval(nn, loss, train_x, train_y);  
  41.         end  
  42.         nnupdatefigures(nn, fhandle, loss, opts, i);  
  43.     end  
  44.           
  45.     disp(['epoch ' num2str(i) '/' num2str(opts.numepochs) '. Took ' num2str(t) ' seconds' '. Mean squared error on training set is ' num2str(mean(L((n-numbatches):(n-1))))]);  
  46.     nn.learningRate = nn.learningRate * nn.scaling_learningRate;  
  47. end  

下面分析三个函数nnff,nnbp和nnapplygrads

nnff

nnff就是进行feedforward pass,其实非常简单,就是整个网络正向跑一次就可以了

当然其中有dropout和sparsity的计算

具体的参见论文“Improving Neural Networks with Dropout“和Autoencoders and Sparsity

[cpp]  view plain  copy
  1. function nn = nnff(nn, x, y)  
  2. //NNFF performs a feedforward pass  
  3. // nn = nnff(nn, x, y) returns an neural network structure with updated  
  4. // layer activations, error and loss (nn.a, nn.e and nn.L)  
  5.   
  6.     n = nn.n;  
  7.     m = size(x, 1);  
  8.       
  9.     x = [ones(m,1) x];  
  10.     nn.a{1} = x;  
  11.   
  12.     //feedforward pass  
  13.     for i = 2 : n-1  
  14.         //根据选择的激活函数不同进行正向传播计算  
  15.         //你可以回过头去看nnsetup里面的第一个参数activation_function  
  16.         //sigm就是sigmoid函数,tanh_opt就是tanh的函数,这个toolbox好像有一点改变  
  17.         //tanh_opt是1.7159*tanh(2/3.*A)  
  18.         switch nn.activation_function   
  19.             case 'sigm'  
  20.                 // Calculate the unit's outputs (including the bias term)  
  21.                 nn.a{i} = sigm(nn.a{i - 1} * nn.W{i - 1}');  
  22.             case 'tanh_opt'  
  23.                 nn.a{i} = tanh_opt(nn.a{i - 1} * nn.W{i - 1}');  
  24.         end  
  25.           
  26.         //dropout的计算部分部分 dropoutFraction 是nnsetup中可以设置的一个参数  
  27.         if(nn.dropoutFraction > 0)  
  28.             if(nn.testing)  
  29.                 nn.a{i} = nn.a{i}.*(1 - nn.dropoutFraction);  
  30.             else  
  31.                 nn.dropOutMask{i} = (rand(size(nn.a{i}))>nn.dropoutFraction);  
  32.                 nn.a{i} = nn.a{i}.*nn.dropOutMask{i};  
  33.             end  
  34.         end  
  35.         //计算sparsity,nonSparsityPenalty 是对没达到sparsitytarget的参数的惩罚系数  
  36.         //calculate running exponential activations for use with sparsity  
  37.         if(nn.nonSparsityPenalty>0)  
  38.             nn.p{i} = 0.99 * nn.p{i} + 0.01 * mean(nn.a{i}, 1);  
  39.         end  
  40.           
  41.         //Add the bias term  
  42.         nn.a{i} = [ones(m,1) nn.a{i}];  
  43.     end  
  44.     switch nn.output   
  45.         case 'sigm'  
  46.             nn.a{n} = sigm(nn.a{n - 1} * nn.W{n - 1}');  
  47.         case 'linear'  
  48.             nn.a{n} = nn.a{n - 1} * nn.W{n - 1}';  
  49.         case 'softmax'  
  50.             nn.a{n} = nn.a{n - 1} * nn.W{n - 1}';//这里的n就是循环n-1里时那个具体的数,代表最后一层输出层(不参与循环)  
  51.             nn.a{n} = exp(bsxfun(@minus, nn.a{n}, max(nn.a{n},[],2))); 
  52. //max这种表达的意思比较并取每一行的最大值,结果为列向量。
  53.             nn.a{n} = bsxfun(@rdivide, nn.a{n}, sum(nn.a{n}, 2));   
  54.      // bsxfun代表高效运算,指两个数组间元素逐个计算的二值操作,@rdivide 这里指左除;@minus是减法
     
  55.     end  
  56.     //error and loss 

  57.     //计算error  
  58.     nn.e = y - nn.a{n};  
  59.     //计算计算loss 函数  
  60.     switch nn.output  
  61.         case {'sigm''linear'}  
  62.             nn.L = 1/2 * sum(sum(nn.e .^ 2)) / m;   
  63.         case 'softmax'  
  64.             nn.L = -sum(sum(y .* log(nn.a{n}))) / m;  
  65.     end  
  66. end  

nnbp

代码:\NN\nnbp.m

nnbp呢是进行back propagation的过程,过程还是比较中规中矩,和ufldl中的Neural Network讲的基本一致

值得注意的还是dropout和sparsity的部分

[cpp]  view plain  copy
  1. if(nn.nonSparsityPenalty>0)  
  2.     pi = repmat(nn.p{i}, size(nn.a{i}, 1), 1);  
  3.     sparsityError = [zeros(size(nn.a{i},1),1) nn.nonSparsityPenalty * (-nn.sparsityTarget ./ pi + (1 - nn.sparsityTarget) ./ (1 - pi))];  
  4. end  
  5.   
  6. // Backpropagate first derivatives  
  7. if i+1==n % in this case in d{n} there is not the bias term to be removed               
  8.     d{i} = (d{i + 1} * nn.W{i} + sparsityError) .* d_act; // Bishop (5.56)  
  9. else // in this case in d{i} the bias term has to be removed  
  10.     d{i} = (d{i + 1}(:,2:end) * nn.W{i} + sparsityError) .* d_act;  
  11. end  
  12.   
  13. if(nn.dropoutFraction>0)  
  14.     d{i} = d{i} .* [ones(size(d{i},1),1) nn.dropOutMask{i}];  
  15. end  
这只是实现的内容,代码中的d{i}就是这一层的delta值,在ufldl中有讲的

dW{i}基本就是计算的gradient了,只是后面还要加入一些东西,进行一些修改

具体原理参见论文“Improving Neural Networks with Dropout“ 以及 Autoencoders and Sparsity的内容

nnapplygrads

代码文件:\NN\nnapplygrads.m

[cpp]  view plain  copy
  1. for i = 1 : (nn.n - 1)  
  2.     if(nn.weightPenaltyL2>0)  
  3.         dW = nn.dW{i} + nn.weightPenaltyL2 * nn.W{i};  
  4.     else  
  5.         dW = nn.dW{i};  
  6.     end  
  7.       
  8.     dW = nn.learningRate * dW;  
  9.       
  10.     if(nn.momentum>0)  
  11.         nn.vW{i} = nn.momentum*nn.vW{i} + dW;  
  12.         dW = nn.vW{i};  
  13.     end  
  14.           
  15.     nn.W{i} = nn.W{i} - dW;  
  16. end  

这个内容就简单了,nn.weightPenaltyL2 是weight decay的部分,也是nnsetup时可以设置的一个参数

有的话就加入weight Penalty,防止过拟合,然后再根据momentum的大小调整一下,最后改变nn.W{i}即可

nntest


Ok再来看看nntest,如下:

function [ri, right] = nntest(nn, x, y)
    labels = nnpredict(nn, x);
    [~, expected] = max(y,[],2);
    right = find(labels == expected);    
    ri = numel(right) / size(x, 1);
end
      
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

调用一下nnpredict。函数需要的就是测试数据x和标签y,如果有y的话那么可以计算准确率,如果没有y的话那么你可以自己直接调用 labels = nnpredict(nn, x)可以得到预测的标签。

nnpredict

代码文件:\NN\nnpredict.m

[cpp]  view plain  copy
  1. function labels = nnpredict(nn, x)  
  2.     nn.testing = 1;  
  3.     nn = nnff(nn, x, zeros(size(x,1), nn.size(end)));  
  4.     nn.testing = 0;  
  5.       
  6.     [~, i] = max(nn.a{end},[],2);  
  7.     labels = i;  
  8. end  

继续非常简单,predict不过是nnff一次,得到最后的output~~

max(nn.a{end},[],2); 是返回每一行的最大值以及所在的列数,所以labels返回的就是标号啦

(这个test好像是专门用来test 分类问题的,我们知道nnff得到最后的值即可)


总结


Ok这就是一个简单的一般化的神经网络了,和我们第三节的matlab自带的神经网络工具箱实现的功能差不多。然而复杂的带稀疏自编码的深度学习网络,自带的就不行了。下一节再来看看同过该工具箱建立稀疏自编码的网络。

版权声明:本文为博主原创文章,未经博主允许不得转载。


已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页