神经网络欧式距离损失函数和softmaxwithloss损失函数转换示例

1. 神经网络损失函数说明

神经网络欧式距离损失函数用于连续值训练样本的拟合,softmaxwithloss损失函数用于分类训练样本的拟合。另外,深度网络对于分类样本的拟合能力强于对于连续值样本的拟合能力。同样的深度网络如果能拟合10分类的样本,可能只能拟合3连续值的样本

欧式距离损失函数如下式所示:

 

                       (f1)

 

它在 Logistic Regression 里其到的作用是讲线性预测值转化为类别概率:假设 (f2) 是第i 个类别的线性预测结果,带入 Softmax 的结果其实就是先对每一个   exponential 变成非负,然后除以所有项之和进行归一化,现在每个 (f3)就可以解释成观察到的数据x  属于类别i 的概率,或者称作似然 (Likelihood)

然后 Logistic Regression 目标函数是根据最大似然原则来建立的,假设数据 x 所对应的类别为y,则根据我们刚才的计算最大似然就是要最大化Oy  的值 (通常是使用 negative log-likelihood 而不是 likelihood,也就是说最小化-log(oy)  的值,这两者结果在数学上是等价的。)。后面这个操作就是 caffe 文档里说的 Multinomial Logistic Loss,具体写出来是这个样子:

代价函数

 

Softmax-Loss 其实就是把两者结合到一起,只要把Oy  的定义展开即可

softmaxloss损失函数:

注意,这里的log代表ln的意思,即:=

                                 (f4)

其中,y为图像所属于的类别编号,是真实的标签值;z为图像所对应的数据,这个数据也是输入softmax层的数据这个数据是深度神经网络输出的数据

没有任何 fancy 的东西。比如如果我们要写一个 Logistic Regression solver,那么因为要处理的就是这个东西,比较自然地就可以将整个东西合在一起来考虑,或者甚至将(f2)  的定义直接一起带进去然后对w  b  进行求导来得到 Gradient Descent update rule,例如我们之前介绍 Gradient Descent 的时候举的两类 Logistic Regression 的例子就是这样做的。

反过来,如果是在设计 Deep Neural Networks 的库,则可能会倾向于将两者分开来看待:因为 Deep Learning 的模型都是一层一层叠起来的结构,一个计算库的主要工作是提供各种各样的 layer,然后让用户可以选择通过不同的方式来对各种 layer 组合得到一个网络层级结构就可以了。比如用户可能最终目的就是得到各个类别的概率似然值,这个时候就只需要一个 Softmax Layer,而不一定要进行 Multinomial Logistic Loss 操作;或者是用户有通过其他什么方式已经得到了某种概率似然值,然后要做最大似然估计,此时则只需要后面的 Multinomial Logistic Loss 而不需要前面的 Softmax 操作。因此提供两个不同的 Layer 结构比只提供一个合在一起的 Softmax-Loss Layer 要灵活许多。从代码的角度来说也显得更加模块化。但是这里自然地就出现了一个问题:numerical stability

让我们回到 Softmax-Loss 层,由于该层没有参数,我们只需要计算向后传递的导数就可以了,此外由于该层是最顶层,所以不用使用 chain rule 就可以直接计算对于最终输出(loss)的导数。回忆一下我们刚才的 notationSoftmax-Loss 层合在一起的时候我们用(f5)  来表示,它有两个输入,一个是 true label ,直接来自于最底部的数据层,并且我们不需要对数据层做任何的 gradient descent 参数更新,所以我们不需要像那个输入进行 back propagation,但是另外一个输入z 则来自于下面的计算层,对于 Logistic Regression 或者普通的 DNNs 下面会是一个全连通的线性内积层,不过具体是什么我们也不需要关心,只要把 (f6) 计算出来丢给下面让他们自己去算后面的就好了。根据普通的微积分知识,我们很容易算出:

 

     ; ; ;

  

   K=1,2,…..m m类别; 

其中   Softmax-Loss 的中间步骤 Softmax Forward Pass 的计算结果,k=1,…..,m,表示softmax的输入端口编号

 

求出之后,令,求出了zk输出端口应该呈现出来的值。应该进行调整的数量(f7)

 

 

 

                                (f8)

 

2. Softmax with loss 代码示例

clear all

clc

close all

x1=0;

x2=-0.4;

x3=0.2

 

for k=1:2160

v_k=k

p1=exp(x1);

p2=exp(x2);

p3=exp(x3);

 

np1=p1/(p1+p2+p3);

np2=p2/(p1+p2+p3);

np3=p3/(p1+p2+p3);

 

loss_1(k)=-log(np1);

loss_2(k)=-log(np2);

loss_3(k)=-log(np3);

 

x1=x1-(np1);

x2=x2-(np2-1);  % the probability of x2 should be 1.

x3=x3-(np3);

end

figure

subplot(1,3,1)

plot(log(loss_1))

subplot(1,3,2)

plot(log(loss_2))

subplot(1,3,3)

plot(log(loss_3))

 

上述示例程序中,随着x1,x2,x3的更新,loss_2值不断减小。x2概率值变量np2不断增加最终接近于1Loss_2loss_3值不断增加。x1x3的概率值变量np1np3不断减小最终接近于0

 

                           1 x1x2x3损失曲线图 (f9)

 

3. 欧式距离损失函数示例代码

本例包含四组样本深度网络较好地拟合。网络参数

netsize=[inputsize,5,6,8,7,5,4];

(1) Main_function

clear all

clc

close all

TrainData =[1   2  3   4   5   6  7   8  9  10

          1   9  17  25  33  41  49  57 65  73];

batchsize=4;

TrainData=TrainData(:,1:batchsize);

TrainLabel=[1    0.3   0.8  0.2;

            1.8  0.8   1.2  1.1];

classnum=2;%输出端数目

 

%获取数据的维度

inputsize=size(TrainData ,1);

%获取数据的数量

datanum=size(TrainData ,2);

% 用一个向量来定义网络的深度,以及每层神经元数目。

netsize=[inputsize,5,6,8,7,5,4];

% netsize=[inputsize,5,6,8,9,9,8,7,5,4];

 

%网络最后一层神经元数数目,再考虑一个偏置。

lastsize=netsize(end)+1;

 

%初始化网络参数,以结构体的形式保存。

stack = initializeNet(netsize);

 

% 在训练时,往往需要将参数转成一列向量,提供给损失函数。

% stack ->stackThetanetconfig保存一些结构参数。

[stackTheta, netconfig] = stack2params(stack);

 

% 指定固定的最后一层的初始化的值;

rand('state',2)

lastTheta = 0.0005 * randn(lastsize * classnum, 1);

 

%最终网络需要的参数

Theta=[ lastTheta ; stackTheta ];

%lastTheta表示深度网络最后一层的权值

 

% the following part is for the traing epoch.

% batchsize=5;

%%每次训练的小批量样本数</span>  

batchnum=floor(size(TrainData,2)/batchsize);  

DataNum=size(TrainData,2);  

alpha=1e-2;

%这是学习率,一般随着网络的悬念都需要不断的减小  

lambda = 1e-4; % Weight decay parameter

for epoch=1:16000

    v_epoch=epoch

    if epoch>=16000

        alpha=1e-3;

    end

    idx=randperm(DataNum);  

   for t=1:batchnum  

      subdata=TrainData(:,idx((t-1)*batchsize+1:(t)*batchsize));  

      sublabel=TrainLabel(:,idx((t-1)*batchsize+1:(t)*batchsize));

      [cost(epoch),grad]=ReLUDNNCost(Theta,classnum,lastsize,netconfig,lambda,subdata,sublabel);

      Theta=Theta-alpha*grad;  

   end      

end

plot(log(cost))

 

(2) drelu

function dre= drelu(x)

  dre=zeros(size(x));

  dre(x>0)=1;

  dre(x==0)=0.5; %这句可以不要

end

(3) relu

function re = relu(x)

re = max(x,0)-1;

end

(4) params2stack

function stack = params2stack(params, netconfig)

depth = numel(netconfig.layersizes);

stack = cell(depth,1);

prevLayerSize = netconfig.inputsize; % the size of the previous layer

curPos = double(1); % mark current position in parameter vector

for d = 1:depth

% Create layer d

stack{d} = struct;

% Extract weights

wlen = double(netconfig.layersizes{d} * prevLayerSize);

stack{d}.w = reshape(params(curPos:curPos+wlen-1), netconfig.layersizes{d}, prevLayerSize);

curPos = curPos+wlen;

% Extract bias

blen = double(netconfig.layersizes{d});

stack{d}.b = reshape(params(curPos:curPos+blen-1), netconfig.layersizes{d}, 1);

curPos = curPos+blen;

% Set previous layer size

prevLayerSize = netconfig.layersizes{d};

end

end

(5) ReLUDNNCost

function [cost,grad] = ReLUDNNCost(theta,numClasses,lasthiddenSize, netconfig,lambda, trainData,trainLabels)

%参数获取的一些操作

lastTheta = reshape(theta(1:lasthiddenSize*numClasses), numClasses, lasthiddenSize);

%theta向量中抽取网络权值参数并转化

stack = params2stack(theta(lasthiddenSize*numClasses+1:end), netconfig);

stackgrad = cell(size(stack));

 

%这里保存在应用BP算法求梯度时需要的数据

PARA=cell(numel(stack),1);

%传进来的样本数

datanum=size(trainData,2);

 

%开始前馈,网络虽然多层,但只是重复而已

data=trainData;

for d = 1:numel(stack)

PARA{d}.a=data;

z2=(stack{d}.w*data)+stack{d}.b*ones(1,datanum);

%ReLU函数

a2=relu(z2);

data=a2;

%ReLU函数的导函数

PARA{d}.daz=drelu(z2);

end

a2=[a2;ones(1,datanum)];

%开始求解损失

groundTruth=trainLabels;

v_groundTruth=groundTruth

M = lastTheta*a2;

v_M=M

 

% 损失函数,

cost=sum(sum((groundTruth-M).^2))./datanum;

%最后一层神经元的目标函数对lastTheta 的导数,

lastThetaGrad = -1/datanum*((groundTruth-M)*a2')+lambda*lastTheta;

% 输出层误差传导至倒数第二层神经元的值

predelta=-lastTheta'*(groundTruth-M);

 

predelta=predelta(1:end-1,:);

for d = numel(stack):-1:1

delta=predelta.*PARA{d}.daz;

stackgrad{d}.w=delta*PARA{d}.a'/datanum;%.*PARA{d}.idx

stackgrad{d}.b=sum(delta,2)/datanum;

predelta=stack{d}.w'*delta;

end

grad = [lastThetaGrad(:) ; stack2params(stackgrad)];

end

(6) stack2params

function [params, netconfig] = stack2params(stack)

params = [];

for d = 1:numel(stack)

    params = [params ; stack{d}.w(:) ;

        stack{d}.b(:) ];

end

if nargout > 1

    if numel(stack) == 0

        netconfig.inputsize = 0;

        netconfig.layersizes = {};

    else

        netconfig.inputsize = size(stack{1}.w, 2);

        netconfig.layersizes = {};

        for d = 1:numel(stack)

            netconfig.layersizes = [netconfig.layersizes ; size(stack{d}.w,1)];

        end

    end

end

end

 

(7) initializeNet

function stack = initializeNet(netsize)

layersize=length(netsize(:));

stack = cell(layersize-1,1);

for l=1:layersize-1

    hiddenSize=netsize(l+1);

    visibleSize=netsize(l);

    r =sqrt(6) / sqrt(hiddenSize+visibleSize+1);

    rand('state',2)

    stack{l}.w= rand(hiddenSize, visibleSize) * 2 * r - r; stack{l}.b= zeros(hiddenSize, 1);

end

end

 

(8) 运行结果

深度网络收敛,运行结果如下图所示:

 

  (f10)

输出标签值如下:

v_groundTruth =

    0.2000    0.3000    1.0000    0.8000

    1.1000    0.8000    1.8000    1.2000

深度网络实际拟合值如下:

v_M =

    0.2001    0.3001    0.9999    0.7999

1.0999    0.8000    1.7999    1.2000

上面数据比较可见,两者差距较,深度网络很好地拟合连续值输出的样本。

 

4. softmaxloss损失函数示例代码

3部分的代码进行修改,使得深度网络的损失函数采用softmaxwithloss损失函数。主要修改的部分为main_function.m 文件ReLUDNNCost.m文件示例代码可以识别三个不同的种类。样本5样本。

(1) Main_function

clear all; clc ; close all

TrainData =[1   2  3   4   5   6  7   8  9  10

          1   9  17  25  33  41  49  57 65  73];

batchsize=5;

TrainData=TrainData(:,1:batchsize);

TrainLabel=[0 1 0 0 1;

          1 0 0 1 0;

          0 0 1 0 0];

classnum=3;%输出端数目

 

%获取数据的维度

inputsize=size(TrainData ,1);

%获取数据的数量

datanum=size(TrainData ,2);

% 用一个向量来定义网络的深度,以及每层神经元数目。

netsize=[inputsize,5,6,8,7,5,4];

% netsize=[inputsize,5,6,8,9,9,8,7,5,4];

 

%网络最后一层神经元数数目,再考虑一个偏置。

lastsize=netsize(end)+1;

 

%初始化网络参数,以结构体的形式保存。

stack = initializeNet(netsize);

 

% 在训练时,往往需要将参数转成一列向量,提供给损失函数。

% stack ->stackThetanetconfig保存一些结构参数。

[stackTheta, netconfig] = stack2params(stack);

 

% 指定固定的最后一层的初始化的值;

rand('state',2)

lastTheta = 0.0005 * randn(lastsize * classnum, 1);

 

%最终网络需要的参数

Theta=[ lastTheta ; stackTheta ];

%lastTheta表示深度网络最后一层的权值

 

% the following part is for the traing epoch.

% batchsize=5;

%%每次训练的小批量样本数</span>  

batchnum=floor(size(TrainData,2)/batchsize);  

DataNum=size(TrainData,2);  

alpha=1e-2;

%这是学习率,一般随着网络的悬念都需要不断的减小  

lambda = 1e-4; % Weight decay parameter

for epoch=1:3600

    v_epoch=epoch

    if epoch>=16000

        alpha=1e-3;

    end

    idx=randperm(DataNum);  

   for t=1:batchnum  

      subdata=TrainData(:,idx((t-1)*batchsize+1:(t)*batchsize));  

      sublabel=TrainLabel(:,idx((t-1)*batchsize+1:(t)*batchsize));

      [cost(epoch),grad]=ReLUDNNCost(Theta,classnum,lastsize,netconfig,lambda,subdata,

sublabel);

      Theta=Theta-alpha*grad;  

   end      

end

plot(log(cost))

 

(9) ReLUDNNCost

识别三个种类的深度网络。

function [cost,grad] = ReLUDNNCost(theta,numClasses,lasthiddenSize, netconfig,lambda, trainData,trainLabels)

%参数获取的一些操作

lastTheta = reshape(theta(1:lasthiddenSize*numClasses), numClasses, lasthiddenSize);

%theta向量中抽取网络权值参数并转化

stack = params2stack(theta(lasthiddenSize*numClasses+1:end), netconfig);

stackgrad = cell(size(stack));

 

%这里保存在应用BP算法求梯度时需要的数据

PARA=cell(numel(stack),1);

%传进来的样本数

datanum=size(trainData,2);

 

%开始前馈,网络虽然多层,但只是重复而已

data=trainData;

for d = 1:numel(stack)

PARA{d}.a=data;

z2=(stack{d}.w*data)+stack{d}.b*ones(1,datanum);

%ReLU函数

a2=relu(z2);

data=a2;

%ReLU函数的导函数

PARA{d}.daz=drelu(z2);

end

a2=[a2;ones(1,datanum)];

%开始求解损失

groundTruth=trainLabels;

v_groundTruth=groundTruth

M = lastTheta*a2;

v_M=M;

 

p1=exp(M(1,:));

p2=exp(M(2,:));

p3=exp(M(3,:));

 

np1=p1./(p1+p2+p3)

np2=p2./(p1+p2+p3)

np3=p3./(p1+p2+p3)

 

dnp1=-(np1-trainLabels(1,:));

dnp2=-(np2-trainLabels(2,:));

dnp3=-(np3-trainLabels(3,:));

 

loss_1=log(np1);

loss_2=log(np2);

cost=(loss_1+loss_2)/2;

cost=0;

last_delta=[dnp1;dnp2;dnp3];

% 损失函数,

%最后一层神经元的目标函数对lastTheta 的导数,

% lastThetaGrad = -1/datanum*((groundTruth-M)*a2')+lambda*lastTheta;

lastThetaGrad = -1/datanum*(last_delta*a2')+lambda*lastTheta;

% 输出层误差传导至倒数第二层神经元的值

predelta=-lastTheta'*last_delta;

 

predelta=predelta(1:end-1,:);

for d = numel(stack):-1:1

delta=predelta.*PARA{d}.daz;

stackgrad{d}.w=delta*PARA{d}.a'/datanum;%.*PARA{d}.idx

stackgrad{d}.b=sum(delta,2)/datanum;

predelta=stack{d}.w'*delta;

end

grad = [lastThetaGrad(:) ; stack2params(stackgrad)];

end

5. 总结

深度网络的数据预处理中,去均值是非常重要的步骤。而归一化处理反而不是必须采用的步骤。

深度神经网络可以对少量样本进行很好的拟合,而较多的样本无法很好拟合的时候,很可能是深度网络的层数不够深,即深度网络的特征无法很好地表征所有样本的特征。此时,可以通过加深网络的层次来使得深度网络对较多的样本进行拟合。

需要记住的是:参数越多,模型越复杂,而越复杂的模型越容易过拟合。过拟合就是说模型在训练数据上的效果远远好于在测试集上的性能。此时可以考虑正则化,通过设置正则项前面的hyper parameter,来权衡损失函数和正则项,减小参数规模,达到模型简化的目的,从而使模型具有更好的泛化能力。

2.2 权重初始化

我们之前已经看过一个完整的神经网络,是怎么样通过神经元和连接搭建起来的,以及如何对数据做预处理。在训练神经网络之前,我们还有一个任务要做,那就是初始化参数。

很小的随机数,其实我们依旧希望初始的权重是较小的数,趋于0,但是就像我们刚刚讨论过的一样,不要真的是0。综合上述想法,在实际场景中,我们通常会把初始权重设定为非常小的数字,然后正负尽量一半一半。这样,初始的时候权重都是不一样的很小随机数,然后迭代过程中不会再出现迭代一致的情况。举个例子,我们可能可以这样初始化一个权重矩阵W=0.0001*np.random.randn(D,H)。这个初始化的过程,使得每个神经元的权重向量初始化为多维高斯中的随机采样向量,所以神经元的初始权重值指向空间中的随机方向。

特别说明:其实不一定更小的初始值会比大值有更好的效果。还要具体问题具体分析。

方差归一化,上面提到的建议有一个小问题,对于随机初始化的神经元参数下的输出,其分布的方差随着输入的数量,会增长。我们实际上可以通过除以总输入数目的平方根,归一化每个神经元的输出方差到1。也就是说,我们倾向于初始化神经元的权重向量为w = np.random.randn(n) / sqrt(n),其中n为输入数。

2.3 正则化

在前一节里我们说了我们要通过正则化来控制神经网络,使得它不那么容易过拟合。有几种正则化的类型供选择:

L2正则化,我们在损失函数里,加入对每个参数的惩罚度。也就是说,对于每个权重w,我们在损失函数里加入一项12λw2,其中λ是我们可调整的正则化强度。顺便说一句,这里在前面加上1/2的原因是,求导/梯度的时候,刚好变成λw而不是2λwL2正则化理解起来也很简单,它对于特别大的权重有很高的惩罚度,以求让权重的分配均匀一些,而不是集中在某一小部分的维度上。我们再想想,加入L2正则化项,其实意味着,在梯度下降参数更新的时候,每个权重以W += -lambda*W的程度被拉向0

L1正则化,这也是一种很常见的正则化形式。在L1正则化中,我们对于每个权重w的惩罚项为λ|w|。有时候,你甚至可以看到大神们混着L1L2正则化用,也就是说加入惩罚项λ1w+λ2w2L1正则化有其独特的特性,它会让模型训练过程中,权重特征向量逐渐地稀疏化,这意味着到最后,我们只留下了对结果影响最大的一部分权重,而其他不相关的输入(例如『噪声』)因为得不到权重被抑制。所以通常L2正则化后的特征向量是一组很分散的小值,而L1正则化只留下影响较大的权重。在实际应用中,如果你不是特别要求只保留部分特征,那么L2正则化通常能得到比L1正则化更好的效果

最大范数约束,另外一种正则化叫做最大范数约束,它直接限制了一个上行的权重边界,然后约束每个神经元上的权重都要满足这个约束。实际应用中是这样实现的,我们不添加任何的惩罚项,就按照正常的损失函数计算,只不过在得到每个神经元的权重向量w 之后约束它满足w 2<c。在神经网络训练学习率设定很高的时候,它也能很好地约束住权重更新变化,不至于直接挂掉。

Dropout: 在训练过程中,我们对每个神经元,都以概率p保持它是激活状态,1-p的概率直接关闭它。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值