具有交叉熵误差的整流线性单元激活的反向传播

Posted

技术标签:

【中文标题】具有交叉熵误差的整流线性单元激活的反向传播【英文标题】:Backpropagation for rectified linear unit activation with cross entropy error 【发布时间】:2014-08-12 14:59:18 【问题描述】:

我正在尝试使用反向传播实现神经网络的梯度计算。 我无法使用交叉熵误差和校正线性单元 (ReLU) 作为激活。

我设法让我的实现使用 sigmoid、tanh 和 ReLU 激活函数处理平方误差。正确计算了具有 sigmoid 激活梯度的交叉熵 (CE) 误差。但是,当我将激活更改为 ReLU 时 - 它失败了。 (我跳过了 CE 的 tanh,因为它返回 (-1,1) 范围内的值。)

是因为 log 函数在值接近 0 时的行为(ReLU 大约有 50% 的时间返回归一化输入)? 我试图通过以下方式缓解这个问题:

log(max(y,eps))

但它只有助于将误差和梯度带回实数 - 它们仍然不同于数值梯度。

我使用数值梯度验证结果:

num_grad = (f(W+epsilon) - f(W-epsilon)) / (2*epsilon)

以下 matlab 代码展示了我的实验中使用的简化且精简的反向传播实现:

function [f, df] = backprop(W, X, Y)
% W - weights
% X - input values
% Y - target values

act_type='relu';    % possible values: sigmoid / tanh / relu
error_type = 'CE';  % possible values: SE / CE

N=size(X,1); n_inp=size(X,2); n_hid=100; n_out=size(Y,2);
w1=reshape(W(1:n_hid*(n_inp+1)),n_hid,n_inp+1);
w2=reshape(W(n_hid*(n_inp+1)+1:end),n_out, n_hid+1);

% feedforward
X=[X ones(N,1)];
z2=X*w1'; a2=act(z2,act_type); a2=[a2 ones(N,1)];
z3=a2*w2'; y=act(z3,act_type);

if strcmp(error_type, 'CE')   % cross entropy error - logistic cost function
    f=-sum(sum( Y.*log(max(y,eps))+(1-Y).*log(max(1-y,eps)) ));
else % squared error
    f=0.5*sum(sum((y-Y).^2));
end

% backprop
if strcmp(error_type, 'CE')   % cross entropy error
    d3=y-Y;
else % squared error
    d3=(y-Y).*dact(z3,act_type);
end

df2=d3'*a2;
d2=d3*w2(:,1:end-1).*dact(z2,act_type);
df1=d2'*X;

df=[df1(:);df2(:)];

end

function f=act(z,type) % activation function
switch type
    case 'sigmoid'
        f=1./(1+exp(-z));
    case 'tanh'
        f=tanh(z);
    case 'relu'
        f=max(0,z);
end
end

function df=dact(z,type) % derivative of activation function
switch type
    case 'sigmoid'
        df=act(z,type).*(1-act(z,type));
    case 'tanh'
        df=1-act(z,type).^2;
    case 'relu'
        df=double(z>0);
end
end

编辑

经过另一轮实验,我发现最后一层使用softmax:

y=bsxfun(@rdivide, exp(z3), sum(exp(z3),2));

和softmax成本函数:

f=-sum(sum(Y.*log(y)));

使实现适用于包括 ReLU 在内的所有激活函数。

这让我得出结论,它是逻辑成本函数(二元分类器)不适用于 ReLU:

f=-sum(sum( Y.*log(max(y,eps))+(1-Y).*log(max(1-y,eps)) ));

但是,我仍然无法弄清楚问题出在哪里。

【问题讨论】:

【参考方案1】:

每个压缩函数 sigmoid、tanh 和 softmax(在输出层) 表示不同的成本函数。 那么 RLU(在输出层)与交叉熵成本函数不匹配是有道理的。 我将尝试一个简单的平方误差成本函数来测试 RLU 输出层。

RLU 的真正威力在于深度网络的隐藏层,因为它不受梯度消失误差的影响。

【讨论】:

在阅读了几篇关于神经网络的论文后,我得出了类似的结论。当我需要分类时,softmax 的输出层由 sigmoid 单元组成。其他层(隐藏)仍然由 ReLU 组成。【参考方案2】:

如果您使用梯度下降,您需要导出激活函数,以便稍后在反向传播方法中使用。你确定'df = double(z> 0)'吗?因为逻辑和 tanh 似乎是正确的。

此外,您确定这个 'd3=y-Y' 吗?我会说当你使用逻辑函数而不是 ReLu 时这是真的(导数不一样,因此不会导致那个简单的方程)。

您可以使用 softplus 函数,它是 ReLU 的平滑版本,其导数是众所周知的(逻辑函数)。

【讨论】:

ReLU函数的导数是:df=0 for input0 在matlab中相当于double(z>0)。 d3 是最后一层的增量,它是正确的形式。 ReLU 比 softplus 函数具有优势 - 例如检查 here。【参考方案3】:

我认为缺陷在于与数值计算的导数比较。在您的导数激活函数中,您将 ReLu 在 0 处的导数定义为 0。在 x=0 处的导数的数值计算表明它是 (ReLU(x+epsilon)-ReLU(x-epsilon)/(2*epsilon)) 在 x =0 时为 0.5。因此,将 ReLU 在 x=0 处的导数定义为 0.5 即可解决问题

【讨论】:

【参考方案4】:

我想我会分享我遇到类似问题的经验。我也设计了我的多分类器ANN,所有隐藏层都使用RELU作为非线性激活函数,输出层使用softmax函数。

我的问题在某种程度上与我使用的编程语言/平台的数值精度有关。就我而言,我注意到如果我使用“普通”RELU 不仅会杀死渐变,而且我使用的编程语言会产生以下softmax 输出向量(这只是一个示例):

⎡1.5068230536681645e-35⎤
⎢ 2.520367499064734e-18⎥
⎢3.2572859518007807e-22⎥
⎢                     1⎥
⎢ 5.020155103452967e-32⎥
⎢1.7620297760773188e-18⎥
⎢ 5.216008990667109e-18⎥
⎢ 1.320937038894421e-20⎥
⎢2.7854159049317976e-17⎥
⎣1.8091246170996508e-35⎦

请注意大多数元素的值都接近0,但最重要的是请注意输出中的1 值。

我使用了与您使用的不同的cross-entropy 错误函数。我没有计算log(max(1-y, eps)),而是坚持基本的log(1-y)。所以给定上面的输出向量,当我计算log(1-y) 时,我得到-Inf 作为cross-entropy 的结果,这显然杀死了算法。

我想如果你的eps 不够高,以至于log(max(1-y, eps)) -> log(max(0, eps)) 不会产生太小的log 输出,你可能会像我一样处于类似的泡菜中。

我对这个问题的解决方案是使用Leaky RELU。一旦我开始使用它,我可以继续使用多分类器cross-entropy,而不是您决定尝试的softmax-cost 功能。

【讨论】:

以上是关于具有交叉熵误差的整流线性单元激活的反向传播的主要内容,如果未能解决你的问题,请参考以下文章

正向传播和反向传播

正向传播和反向传播

神经网络的前向与后向传播简单理解

日常手写三层反向传播神经网络(损失函数交叉熵+正则项+反向求导)

前向传播和反向传播

深度学习知识点查漏补缺(反向传播)