Fork me on GitHub

DeepLearnToolBox之BP算法

这篇文章只要是基于BP网络代码的简单分析,基于公式推导的文章请参阅:BP神经网络部分具体的推导

工具箱下载

下载地址:
https://github.com/rasmusbergpalm/DeepLearnToolbox

总体测试代码

\tests\test_example_NN.m

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); 

很简单的几步就训练了一个NN,我们发现其中最重要的几个函数就是nnsetup,nntrainnntest了。

nnsetup

NNSETUP创建前向反馈神经网络。

代码解释

详细代码如下:

function nn = nnsetup(architecture)
%NNSETUP creates a Feedforward Backpropagate Neural Network
% nn = nnsetup(architecture) returns an neural network structure with n=numel(architecture)
% layers, architecture being a n x 1 vector of layer sizes e.g. [784 100 10]

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

    nn.activation_function              = 'tanh_opt';   %  Activation functions of hidden layers: 'sigm' (sigmoid) or 'tanh_opt' (optimal tanh).
    nn.learningRate                     = 1;            %  learning rate Note: typically needs to be lower when using 'sigm' activation function and non-normalized inputs.
    nn.momentum                         = 0.5;          %  Momentum 权值动量因子
    nn.scaling_learningRate             = 1;            %  Scaling factor for the learning rate (each epoch) 学习率变化因子 (each epoch)
    nn.weightPenaltyL2                  = 0;            %  L2 regularization
    nn.nonSparsityPenalty               = 0;            %  Non sparsity penalty 非稀疏惩罚
    nn.sparsityTarget                   = 0.05;         %  Sparsity target 稀疏目标值
    nn.inputZeroMaskedFraction          = 0;            %  Used for Denoising AutoEncoders 自动编码的去噪作用
    nn.dropoutFraction                  = 0;            %  Dropout level (http://www.cs.toronto.edu/~hinton/absps/dropout.pdf)
    nn.testing                          = 0;            %  Internal variable. nntest sets this to one. 一个标志参数--在nntest.m这个函数中会用到
    nn.output                           = 'softmax';       %  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

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

//对每一层的网络结构进行初始化,一共三个参数W,vW,p,其中W是主要的参数
//vW是更新参数时的临时参数,p是所谓的sparsity,(等看到代码了再细讲)

##使用实例

nn=nnsetup([size(feature,2) 200 200 length(azimuth)]);

nntrain

截取出的主要框架为:

for i = 1 : numepochs 
    for l = 1 : numbatches  

        nn = nnff(nn, batch_x, batch_y);  
        nn = nnbp(nn);  
        nn = nnapplygrads(nn);  
        L(n) = nn.L;  
        n = n + 1;  
    end  
end  

第一层for循环为迭代次数。第二次为遍历所有的batch。其中,先计算前向传播(ff),在计算反向传播(bp),接着更新参数(nnapplygrads),最后计算损失函数(代价函数)L。

下面分析三个函数nnff,nnbpnnapplygrads

nnff

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

当然其中有dropout和sparsity的计算,具体的参见论文“Improving Neural Networks with Dropout“和Autoencoders and Sparsity

提取出主要框架为:

for i = 2 : n-1  (遍历隐含层)
    //根据激活函数计算隐层输出

    //隐层的dropout计算,舍弃部分输出

    //计算sparsity,nonSparsityPenalty 是对没达到sparsitytarget的参数的惩罚系数

    //Add the bias term 
end

//根据输出层的结构计算输出层的输出。

//计算误差以及损失函数  

nnbp

代码:\NN\nnbp.m
nnbp呢是进行back propagation的过程。值得注意的还是dropout和sparsity的部分。

提取出主要框架为:

①计算输出层的输出
②依次反向计算隐层输出
for i = (n - 1) : -1 : 2 (反向遍历隐层)
    d_act为隐层 激活函数的导数

    d{i}为隐层输出函数的导数。 计算中用到非稀疏惩罚项,以及dropout
end

③计算权值的导数nn.dW{i}

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

nnapplygrads

代码文件:\NN\nnapplygrads.m

for i = 1 : (nn.n - 1)

    //应用weightPenaltyL2,learningRate ,momentum修正dW

    nn.W{i} = nn.W{i} - dW;  //权值W更新

end

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

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

nntest

function [er, bad] = nntest(nn, x, y)  
    labels = nnpredict(nn, x);  
    [~, expected] = max(y,[],2);  
    bad = find(labels ~= expected);      
    er = numel(bad) / size(x, 1);  
end  

nntest再简单不过了,就是调用一下nnpredict,在和test的集合进行比较

nnpredict

代码文件:\NN\nnpredict.m

function labels = nnpredict(nn, x)  
    nn.testing = 1;  
    nn = nnff(nn, x, zeros(size(x,1), nn.size(end)));  
    nn.testing = 0;  

    [~, i] = max(nn.a{end},[],2);  
    labels = i;  
end  

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

max(nn.a{end},[],2); 是返回每一行的最大值以及所在的列数,所以labels返回的就是标号啦
(这个test好像是专门用来test分类问题的,我们知道nnff得到最后的值即可)

总结

这篇文章只要是基于代码的简单分析,基于公式推导的文章请参阅:BP神经网络部分具体的推导

参考资料

------ 本文结束感谢您的阅读 ------
坚持原创技术分享,您的支持将鼓励我继续创作!