神经网络激活函数中ReLU激活函数如何用python写出来?

研究笔记(5)
本文介绍下如何实现神经元激活函数为ReLU的深度神经网络。ReLU函数的数学公式很简单ReLU(x)=max(x,0)。若DNN用于数据分类,则可以简单的认为其主要由两个部分组成:多隐层网络+分类器。分类器采用softmax。
第一步:准备数据
1)将你需要分类的样本数据以每列的形式保存于矩阵中;-&TrainData&
2)将每个样本的类别标记按数据顺序存为一行向量,类别为1,2,3,…,n;-&TrainLabel
并将数据保存入MyData.mat数据文件中。
采用以下程序实现数据的生成。&
rt=x.*x-50*x+40*y-y.^2;
TrainData=[x;y];
for&k=1:10
&&&&v_rt_k=rt(k)
&&&&if&rt(k)&=0
&&&&&&&&TrainLabel(k)=1;
&&&&&&&&TrainLabel(k)=2;
save('MyData.mat','TrainData','TrainLabel')
第二步:网络配置、参数初始化和转换
将第一步中准备好的数据载入内存中,并采用以下程序运行数据。
1.&Main_function without minFunc
load MyData.mat
inputsize=size(TrainData ,1);%获取数据的维度
datanum=size(TrainData ,2);%获取数据的数量
% netsize=[inputsize,50,50,50];
%可以简单地用一个向量来定义网络的深度,以及每层神经元数目。这表示一个三隐藏层的神经元数都为。
netsize=[inputsize,4,3];
classnum=2;%类别数目
lastsize=netsize(end)+1;%网络最后一层神经元数数目,再考虑一个偏置。
stack = initializeNet(netsize);%初始化网络参数,以结构体的形式保存。
v_stack=stack
v_stack_1=stack{1}
v_stack_1_w=stack{1}.w
v_stack_1_b=stack{1}.b
v_stack_2=stack{2}
v_stack_2_w=stack{2}.w
v_stack_2_b=stack{2}.b
%在训练时,往往需要将参数转成一列向量,提供给损失函数。,保存一些结构参数
[stackTheta, netconfig] = stack2params(stack);
v_stackTheta=stackTheta
v_netconfig=netconfig
v_netconfig_layersizes=netconfig.layersizes
v_lastsize=lastsize
SoftmaxTheta = 0.0005 * randn(lastsize * classnum, 1);
v_SoftmaxTheta=SoftmaxTheta
Theta=[ SoftmaxT stackTheta ];%最终网络需要的参数
% the following part is for the traing epoch.
batchsize=10;
% batchsize=5;
%%每次训练的小批量样本数
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
&&&&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,grad]=ReLUDNNCost(Theta,classnum,lastsize,netconfig,lambda,subdata,sublabel);
&&&&&&&&Theta=Theta-alpha*
&&&&&&&&v_grad=grad
&&&&end &&&&&
% Note: 当Theta传递进入损失函数内部时,还需要从抽出,再转成代码如下,都是的教程里面的提供的。
2.&Main_function with minFunc of UFLDL
load MyData.mat
inputsize=size(TrainData ,1);%获取数据的维度
datanum=size(TrainData ,2);%获取数据的数量
% netsize=[inputsize,50,50,50];
%可以简单地用一个向量来定义网络的深度,以及每层神经元数目。这表示一个三隐藏层的神经元数都为。
netsize=[inputsize,4,3];
classnum=2;%类别数目
lastsize=netsize(end)+1;%网络最后一层神经元数数目,再考虑一个偏置。
% v_lastsize=lastsize
stack = initializeNet(netsize);%初始化网络参数,以结构体的形式保存。
v_stack=stack
v_stack_1=stack{1}
v_stack_1_w=stack{1}.w
v_stack_1_b=stack{1}.b
v_stack_2=stack{2}
v_stack_2_w=stack{2}.w
v_stack_2_b=stack{2}.b
%在训练时,往往需要将参数转成一列向量,提供给损失函数。,保存一些结构参数
[stackTheta, netconfig] = stack2params(stack);
v_stackTheta=stackTheta
v_netconfig=netconfig
v_netconfig_layersizes=netconfig.layersizes
v_lastsize=lastsize
SoftmaxTheta = 0.0005 * randn(lastsize * classnum, 1);
v_SoftmaxTheta=SoftmaxTheta
Theta=[ SoftmaxT stackTheta ];%最终网络需要的参数
% the following part is for the minFunc in the UFLDL tutorial
options.Method = 'lbfgs';
options.maxIter = 20000;
options.MaxFunEvals=1000000;
options.display = 'on';
lambda = 1e-4;
[OptTheta, cost] = minFunc( @(p)ReLUDNNCost(p,classnum,lastsize,netconfig,lambda, TrainData,TrainLabel),Theta, options);
v_TrainLabel=TrainLabel
save('weights_matrix_minFunc.mat','OptTheta')
% % the following part is for the SGD traing epoch.
% batchsize=10;
% % batchsize=5;
% %%每次训练的小批量样本数
% 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
% &&&&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,grad]=ReLUDNNCost(Theta,classnum,lastsize,netconfig,lambda,subdata,sublabel);
% &&&&&&&&Theta=Theta-alpha*
% &&&&&&&&v_grad=grad
% &&&&end &&&&&
% save('weights_matrix_minFunc.mat','Theta')
3.&Main_function2 with minFunc of UFLDL
load MyData.mat
inputsize=size(TrainData ,1);%获取数据的维度
datanum=size(TrainData ,2);%获取数据的数量
% netsize=[inputsize,50,50,50];
%可以简单地用一个向量来定义网络的深度,以及每层神经元数目。这表示一个三隐藏层的神经元数都为。
netsize=[inputsize,4,3];
classnum=2;%类别数目
lastsize=netsize(end)+1;%网络最后一层神经元数数目,再考虑一个偏置。
% v_lastsize=lastsize
stack = initializeNet(netsize);%初始化网络参数,以结构体的形式保存。
v_stack=stack
v_stack_1=stack{1}
v_stack_1_w=stack{1}.w
v_stack_1_b=stack{1}.b
v_stack_2=stack{2}
v_stack_2_w=stack{2}.w
v_stack_2_b=stack{2}.b
%在训练时,往往需要将参数转成一列向量,提供给损失函数。,保存一些结构参数
[stackTheta, netconfig] = stack2params(stack);
v_stackTheta=stackTheta
v_netconfig=netconfig
v_netconfig_layersizes=netconfig.layersizes
v_lastsize=lastsize
SoftmaxTheta = 0.0005 * randn(lastsize * classnum, 1);
v_SoftmaxTheta=SoftmaxTheta
Theta=[ SoftmaxT stackTheta ];%最终网络需要的参数
% the following part is for the traing epoch.
batchsize=10;
% batchsize=5;
%%每次训练的小批量样本数
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
&&&&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,grad]=ReLUDNNCost(Theta,classnum,lastsize,netconfig,lambda,subdata,sublabel);
&&&&&&&&Theta=Theta-alpha*
&&&&&&&&v_grad=grad
&&&&end &&&&&
&&&&v_trainLabels=sublabel
save('weights_matrix2.mat','Theta')
4.&ReLUDNNCost
function [cost,grad] = ReLUDNNCost(theta,numClasses,lasthiddenSize, netconfig,lambda, trainData,trainLabels)
%参数获取的一些操作
softmaxTheta = reshape(theta(1:lasthiddenSize*numClasses), numClasses, lasthiddenSize);
stack = params2stack(theta(lasthiddenSize*numClasses+1:end), netconfig);
%从向量中抽取网络权值参数并转化
stackgrad = cell(size(stack));
PARA=cell(numel(stack),1);%这里保存在应用算法求梯度时需要的数据
datanum=size(trainData,2);%传进来的样本数
%开始前馈,网络虽然多层,但只是重复而已
data=trainD
for d = 1:numel(stack)
PARA{d}.a=
z2=(stack{d}.w*data)+stack{d}.b*ones(1,datanum);
a2=relu(z2);%RelU函数
PARA{d}.daz=drelu(z2);%RelU函数的导函数
a2=[a2;ones(1,datanum)];
%开始求解损失
% v_trainLabels=trainLabels
% v_datanum=datanum
groundTruth = full(sparse(trainLabels, 1:datanum, 1));
% %这是教程原版的语句,但其在应用小批量样本训练时会出错,下一行是另一种实现方式
% v_trainLabels=trainLabels
% v_numClasses=numClasses
% v_element1=repmat(trainLabels,numClasses,1)
% v_element2=(1:1:numClasses)'
% groundTruth=bsxfun(@eq,repmat(trainLabels,numClasses,1),(1:1:numClasses)');
% v_groundTruth=groundTruth
M = softmaxTheta*a2;
h = exp(M);
h = bsxfun(@rdivide, h, sum(h));
% v_size_groundTruth=size(groundTruth)
% v_log_h=size(log(h))
cost = -1/datanum*sum(sum(groundTruth.*log(h)))+lambda/2*sum(sum(softmaxTheta.^2));
%softmax 损失函数,没啥好说的
softmaxThetaGrad = -1/datanum*((groundTruth-h)*a2')+lambda*softmaxT
%softmax 目标函数对的导数,
predelta=-softmaxTheta'*(groundTruth-h);
%想理解这里,还有后面的梯度是如何计算出的,建议看那本关于矩阵的工具书《》
predelta=predelta(1:end-1,:);
for d = numel(stack):-1:1
delta=predelta.*PARA{d}.
stackgrad{d}.w=delta*PARA{d}.a'/%.*PARA{d}.idx
stackgrad{d}.b=sum(delta,2)/
predelta=stack{d}.w'*
grad = [softmaxThetaGrad(:) ; stack2params(stackgrad)];
function&re = relu(x)
re = max(x,0)-1;
function dre= drelu(x)
dre=zeros(size(x));
dre(x&0)=1;
dre(x==0)=0.5;%这句可以不要
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);
&&&&stack{l}.w= rand(hiddenSize, visibleSize) * 2 * r - stack{l}.b= zeros(hiddenSize, 1);
8.&params2stack
function&stack = params2stack(params, netconfig)
depth = numel(netconfig.layersizes);
stack = cell(depth,1);
prevLayerSize = netconfig. % the size of the previous layer
curPos = double(1); % mark current position in parameter vector
for&d = 1:depth
% Create layer d
stack{d} =
% Extract weights
wlen = double(netconfig.layersizes{d} * prevLayerSize);
stack{d}.w = reshape(params(curPos:curPos+wlen-1), netconfig.layersizes{d}, prevLayerSize);
curPos = curPos+
% Extract bias
blen = double(netconfig.layersizes{d});
stack{d}.b = reshape(params(curPos:curPos+blen-1), netconfig.layersizes{d}, 1);
curPos = curPos+
% Set previous layer size
prevLayerSize = netconfig.layersizes{d};
9.&stack2params
function&[params, netconfig] = stack2params(stack)
params = [];
for&d = 1:numel(stack)
&&&&params = [ stack{d}.w(:) ;
&&&&&&&&stack{d}.b(:) ];
if&nargout & 1
&&&&if&numel(stack) == 0
&&&&&&&&netconfig.inputsize = 0;
&&&&&&&&netconfig.layersizes = {};
&&&&&&&&netconfig.inputsize = size(stack{1}.w, 2);
&&&&&&&&netconfig.layersizes = {};
&&&&&&&&for&d = 1:numel(stack)
&&&&&&&&&&&&netconfig.layersizes = [netconfig. size(stack{d}.w,1)];
&&&&&&&&end
第三步,采用已训练的深度网络对输入数据进行测试
1.&Main_test_function
load MyData.mat
load weights_matrix.mat
inputsize=size(TrainData ,1);%获取数据的维度
datanum=size(TrainData ,2);%获取数据的数量
% netsize=[inputsize,50,50,50];
%可以简单地用一个向量来定义网络的深度,以及每层神经元数目。这表示一个三隐藏层的神经元数都为。
netsize=[inputsize,4,3];
classnum=2;%类别数目
netconfig2.inputsize=netsize(1)
netconfig2.layersizes{1}=netsize(2)
netconfig2.layersizes{2}=netsize(3)
netconfig2.layersizes=netconfig2.layersizes'
lastsize=netsize(end)+1;%网络最后一层神经元数数目,再考虑一个偏置。
v_result = forward_computation(Theta,classnum,lastsize,netconfig2,TrainData)
v_TrainLabel=TrainLabel
2.&forward_computation
function v_result = forward_computation(theta,numClasses,lasthiddenSize, netconfig,trainData,trainLabels)
%参数获取的一些操作
softmaxTheta = reshape(theta(1:lasthiddenSize*numClasses), numClasses, lasthiddenSize);
stack = params2stack(theta(lasthiddenSize*numClasses+1:end), netconfig);
%从向量中抽取网络权值参数并转化
stackgrad = cell(size(stack));
PARA=cell(numel(stack),1);%这里保存在应用算法求梯度时需要的数据
datanum=size(trainData,2);%传进来的样本数
%开始前馈,网络虽然多层,但只是重复而已
data=trainD
for d = 1:numel(stack)
PARA{d}.a=
z2=(stack{d}.w*data)+stack{d}.b*ones(1,datanum);
a2=relu(z2);%RelU函数
PARA{d}.daz=drelu(z2);%RelU函数的导函数
a2=[a2;ones(1,datanum)];
M = softmaxTheta*a2;
h = exp(M);
h = bsxfun(@rdivide, h, sum(h));
v_result=h
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:6554次
排名:千里之外
原创:12篇
(2)(1)(5)(7)(1)
(window.slotbydup = window.slotbydup || []).push({
id: '4740887',
container: s,
size: '250,250',
display: 'inlay-fix'神经网络学习笔记 - 激活函数的作用、定义和微分证明
看到上对激活函数(Activation Function)的解释。
我一下子迷失了。
因此,匆匆写下我对激活函数的理解。
激活函数被用到了什么地方
目前为止,我见到使用激活函数的地方有两个。
逻辑回归(Logistic Regression)
神经网络(Neural Network)
这两处,激活函数都用于计算一个线性函数的结果。
了解激活函数
激活函数的作用:就是将权值结果转化成分类结果。
2类的线性分类器
先说一个简单的情况 - 一个2类的线性分类器。
了解激活函数,先要明确我们的问题是:&计算一个(矢量)数据的标签(分类)&。
以下图为例:
训练的结果,是一组\((w,b)\),和一个线性函数\(f(x) = wx + b\)。
我们现在仔细考虑一下,如何在预测函数中使用这个线性函数\(f(x)\)。
先从几何方面理解一下,如果预测的点在分割线\(wx + b = 0\)上,那么\(f(x) = wx + b = 0\)。
如果,在分割线的上方某处,\(f(x) = wx + b = 8\)(假设是8)。
8可以认为是偏移量。
注:取决于(w, b),在分割线上方的点可以是正的,也可能是负的。
例如: y - x =0,和 x - y = 0,这两条线实际上是一样的。
但是,应用点(1, 9)的结果, 第一个是8, 第二个是 -8。
然后,你该怎么办???
如何用这个偏移量来得到数据的标签?
激活函数的作用是:将8变成红色。
怎么变的呢?比如:我们使用sigmoid函数,sigmoid(8) = 0.。
sigmoid函数的结果在区间(0, 1)上。如果大于0.5,就可以认为满足条件,即是红色。
3类分类器的情况
我们再看看在一个多类分类器中,激活函数的作用。
以下图为例:
3类\({a, b, c}\)分类器的训练结果是3个\((w, b)\),三个\(f(x)\),三条分割线。
每个\(f(x)\),可以认为是针对一个分类的model。因此:
f_a(x) = w_ax + b_a \\
f_b(x) = w_bx + b_b \\
f_c(x) = w_cx + b_c
对于预测的点\(x\),会得到三个偏移量\([f_a(x), f_b(x), f_c(x)]\)。
使用激活函数sigmoid:
\(sigmoid([f_a(x), f_b(x), f_c(x)])\)
会得到一个向量, 记为:\([S_a, S_b, S_c]\)。
这时的处理方法是:再次使用激活函数(没想到吧)
一般会使用激活函数softmax。
激活函数,在这里的作用是:计算每个类别的可能性。
最后使用argmax函数得到:最大可能性的类。
注:上面差不多是Logistic Regression算法的一部分。
注:softmax也经常被使用于神经网络的输出层。
激活函数的来源
在学习神经网络的过程中,激活函数的灵感来自于生物神经网络,被认为是神经元对输入的激活程度。
最简单的输出形式是:一个开关,\({0, 1}\)。 要么\(0\),要么\(1\)。
也就是一个单位阶跃函数(Heaviside step function)。
这种思想主要是一种灵感来源,并不是严格的推理。
常用的激活函数有哪些
sigmoid - S型
\begin{align}
\sigma(x) & = \frac{e^x}{1 + e^x} \\
& = \frac{1}{1 + e^{-x}}
\end{align}
\sigma'(x) = (1 - \sigma(x))\sigma(x)
tanh(hyperbolic tangent) - 双曲正切
\begin{align}
& = sinh(x)/cosh(x) \\
& = \frac{e^x - e^{-x}}{e^x + e^{-x}} \\
& = \frac{e^{2x} - 1}{e^{2x} + 1} \\
& = \frac{1 - e^{-2x}}{1 + e^{-2x}}
\end{align}
tanh'(x) = 1 - tanh(x)^2
Rectified linear unit - ReLU - 修正线性单元
\begin{cases}
0 & \text{for}\ x < 0
x & \text{for}\ x \geqslant 0
\end{cases}
$[0, \infty)$
relu'(x) =
\begin{cases}
0 & \text{for}\ x < 0
1 & \text{for}\ x \geqslant 0
\end{cases}
f(\vec{x}) = \begin{bmatrix}
\frac{e^{x_i}}{\sum_{k=1}^{k=K}e^{x_k}} &
\end{bmatrix}
softmax'(z_t) = \frac{\partial{y_t}}{\partial{z_t}} =
\begin{cases}
\hat{y_{t_i}}(1 - \hat{y_{t_i}}), & \text{if } i = j \\
-\hat{y_{t_i}} \hat{y_{t_j}},
& \text{if } i \ne j
\end{cases}
激活函数的意义
sigmoid - S型
sigmoid的区间是[0, 1]。因此,可以用于表示Yes/No这样的信息。比如:不要(0)/要(1)。多用于过滤数据。比如:门。
tanh(hyperbolic tangent) - 双曲正切
tanh的区间是[-1, 1]。同样可以表示Yes/No的信息,而且加上了程度。比如:非常不可能(-1)/一般般(0)/非常可能(1)。非常不喜欢(-1)/一般般(0)/非常喜欢(1)。因此,tanh多用于输出数据。输出数据最终会使用softmax来计算可能性。
softmax用于输出层,计算每个分类的可能性。
Rectified linear unit - ReLU - 修正线性单元
ReLU的好处:ReLU对正值较少的数据,处理能力更强。由于,其导数为{0, 1},可以避免梯度消失问题。
激活函数的微分的证明
sigmoid函数
\sigma(x) = \frac{1}{1 + e^{-x}} \\
\sigma&#39;(x) = (1 - \sigma(x))\sigma(x)
\begin{align}
\frac{\partial \sigma(x)}{\partial x}
& = \frac{e^{-x}}{(1 + e^{-x})^2} \\
& = {\left ( \frac{1 + e^{-x} - 1}{1 + e^{-x}} \right ) }{\left ( \frac{1}{1 + e^{-x}} \right )} \\
& = (1 - \sigma(x))\sigma(x)
\end{align}
\tanh(x) = \frac{e^{2x} - 1}{e^{2x} + 1} \\
tanh&#39;(x) = 1 - tanh(x)^2
\begin{align}
\frac{\partial tanh(x)}{\partial x}
& = \left (1 - \frac{2}{e^{2x} + 1} \right )&#39; \\
& = 2 \cdot \frac{2e^{2x}}{(e^{2x} + 1)^2} \\
& = \frac{4e^{2x}}{(e^{2x} + 1)^2} \\
& = \frac{(e^{2x} + 1)^2 - (e^{2x} - 1)^2}{(e^{2x} + 1)^2} \\
& = 1 - \left (\frac{e^{2x} - 1}{e^{2x} + 1} \right )^2 \\
& = 1 - tanh(x)^2
\end{align}
激活函数softmax和损失函数会一起使用。
激活函数会根据输入的参数(一个矢量,表示每个分类的可能性),计算每个分类的概率(0, 1)。
损失函数根据softmax的计算结果\(\hat{y}\)和期望结果\(y\),根据交叉熵方法(cross entropy loss) 可得到损失\(L\)。
softmax函数
\text{softmax:} \\
\hat{y_{t_i}} = softmax(o_{t_i}) = \frac{e^{o_{t_i}}}{\sum_{k}e^{o_{t_k}}} \\
\hat{y_t} = softmax(z_t) = \begin{bmatrix}
\frac{e^{o_{t_i}}}{\sum_{k}e^{o_{t_k}}} &
\end{bmatrix} \\
softmax&#39;(z_t) = \frac{\partial{y_t}}{\partial{z_t}} =
\begin{cases}
\hat{y_{t_i}}(1 - \hat{y_{t_i}}), & \text{if } i = j \\
-\hat{y_{t_i}} \hat{y_{t_j}},
& \text{if } i \ne j
\end{cases}
softmax&#39;(z_t) = \frac{\partial \hat{y_t} }{\partial z_t } \\
\text{if } i = j \\
\begin{align}
\frac{\partial \hat{y_{t_i}} } {\partial o_{t_i} }
& = \left ( \frac{e^{o_{t_i}}}{\sum_{k}e^{o_{t_k}}} \right )&#39; \\
& = \left ( 1 - \frac{S}{\sum_{k}e^{o_{t_k}}} \right )&#39;
\text{ // set } S = \sum_{k \ne i}e^{o_{t_k}} \\
& = \left ( 1 - \frac{S}{S + e^{o_{t_i}}} \right )&#39; \\
& = \frac{S \cdot e^{o_{t_i}}}{(S + e^{o_{t_i}})^2} \\
& = \frac{S}{S + e^{o_{t_i}}} \cdot \frac{e^{o_{t_i}}}{S + e^{o_{t_i}}} \\
& = \frac{S}{S + e^{o_{t_i}}} \cdot \frac{e^{o_{t_i}}}{S + e^{o_{t_i}}} \\
& = \left ( 1 - \frac{e^{o_{t_i}}}{S + e^{o_{t_i}}} \right ) \cdot \frac{e^{o_{t_i}}}{S + e^{o_{t_i}}} \\
& = \left ( 1 - \frac{e^{o_{t_i}}}{\sum_{k}e^{o_{t_k}}} \right ) \cdot \frac{e^{o_{t_i}}}{\sum_{k}e^{o_{t_k}}} \\
& = \left ( 1 - \hat{y_{t_i}} \right ) \cdot \hat{y_{t_i}}
\text{if } i \ne j \\
\frac{\partial \hat{y_{t_j}} }{\partial o_{t_i} }
& = \left ( \frac{ e^{o_{t_j}} } { \sum_{k}e^{o_{t_k}} } \right )&#39; \\
& = \left ( \frac{e^{o_{t_j}}}{S + e^{o_{t_i}}} \right )&#39;
\text{ // set } S = \sum_{k \ne i}e^{o_{t_k}} \\
& = - \frac{ e^{o_{t_j}} \cdot e^{o_{t_i}} }{ (S + e^{o_{t_i}})^2 } \\
& = - \frac{ e^{o_{t_j}} }{ S + e^{o_{t_i}} } \cdot \frac{ e^{o_{t_i}} }{ S + e^{o_{t_i}} } \\
& = - \frac{ e^{o_{t_j}} }{ \sum_{k}e^{o_{t_k}} } \cdot \frac{ e^{o_{t_i}} }{ \sum_{k}e^{o_{t_k}} } \\
& = - \hat{y_{t_j}} \cdot \hat{y_{t_i}}
\end{align}
阅读(...) 评论()转:ReLu(Rectified&Linear&Units)激活函数
论文参考:&(很有趣的一篇paper)
起源:传统激活函数、脑神经元激活频率研究、稀疏激活性
传统Sigmoid系激活函数
<img ALT="技术分享" src="/blog7style/images/common/sg_trans.gif" real_src ="/blog/.png" WIDTH="438" HEIGHT="329" STYLE="font-family: Arial, Helvetica, sans- padding-top: 0 padding-right: 0 padding-bottom: 0 padding-left: 0 max-width: 680 overflow-x: overflow-y:"
TITLE="转:ReLu(Rectified&Linear&Units)激活函数" />
传统神经网络中最常用的两个激活函数,Sigmoid系(Logistic-Sigmoid、Tanh-Sigmoid)被视为神经网络的核心所在。
从数学上来看,非线性的Sigmoid函数对中央区的信号增益较大,对两侧区的信号增益小,在信号的特征空间映射上,有很好的效果。
从神经科学上来看,中央区酷似神经元的兴奋态,两侧区酷似神经元的抑制态,因而在神经网络学习方面,可以将重点特征推向中央区,将非重点特征推向两侧区。
无论是哪种解释,看起来都比早期的线性激活函数(y=x),阶跃激活函数(-1/1,0/1)高明了不少。
近似生物神经激活函数:Softplus&ReLu&
2001年,神经科学家Dayan、Abott从生物学角度,模拟出了脑神经元接受信号更精确的激活模型,该模型如左图所示:
<img ALT="技术分享" STYLE="font-family: Arial, Helvetica, sans- padding-top: 0 padding-right: 0 padding-bottom: 0 padding-left: 0 max-width: 680 overflow-x: overflow-y:"
TITLE="转:ReLu(Rectified&Linear&Units)激活函数" />
<img ALT="技术分享" STYLE="font-family: Arial, Helvetica, sans- padding-top: 0 padding-right: 0 padding-bottom: 0 padding-left: 0 max-width: 680 overflow-x: overflow-y:"
TITLE="转:ReLu(Rectified&Linear&Units)激活函数" />
<img ALT="技术分享" src="/blog7style/images/common/sg_trans.gif" real_src ="/blog/.png" WIDTH="414" HEIGHT="308" STYLE="font-family: Arial, Helvetica, sans- padding-top: 0 padding-right: 0 padding-bottom: 0 padding-left: 0 max-width: 680 overflow-x: overflow-y:"
TITLE="转:ReLu(Rectified&Linear&Units)激活函数" /><img ALT="技术分享" src="/blog7style/images/common/sg_trans.gif" real_src ="/blog/.png" WIDTH="391" HEIGHT="299" STYLE="font-family: Arial, Helvetica, sans- padding-top: 0 padding-right: 0 padding-bottom: 0 padding-left: 0 max-width: 680 overflow-x: overflow-y:"
TITLE="转:ReLu(Rectified&Linear&Units)激活函数" />
这个模型对比Sigmoid系主要变化有三点:&#9312;单侧抑制
&#9313;相对宽阔的兴奋边界 &#9314;稀疏激活性(重点,可以看到红框里前端状态完全没有激活)
同年,Charles Dugas等人在做正数回归预测中偶然使用了Softplus函数,Softplus函数是Logistic-Sigmoid函数原函数。
&Softplus(x)=log(1+ex)
按照论文的说法,一开始想要使用一个指数函数(天然正数)作为激活函数来回归,但是到后期梯度实在太大,难以训练,于是加了一个log来减缓上升趋势。
加了1是为了保证非负性。同年,Charles Dugas等人在NIPS会议中又调侃了一句,Softplus可以看作是强制非负校正函数max(0,x)平滑版本。
偶然的是,同是2001年,ML领域的Softplus/Rectifier激活函数与神经科学领域的提出脑神经元激活频率函数有神似的地方,这促成了新的激活函数的研究。
生物神经的稀疏激活性
在神经科学方面,除了新的激活频率函数之外,神经科学家还发现了神经元的稀疏激活性。
还是2001年,Attwell等人基于大脑能量消耗的观察学习上,推测神经元编码工作方式具有稀疏性和分布性。
2003年Lennie等人估测大脑同时被激活的神经元只有1~4%,进一步表明神经元工作的稀疏性。
从信号方面来看,即神经元同时只对输入信号的少部分选择性响应,大量信号被刻意的屏蔽了,这样可以提高学习的精度,更好更快地提取稀疏特征。
从这个角度来看,在经验规则的初始化W之后,传统的Sigmoid系函数同时近乎有一半的神经元被激活,这不符合神经科学的研究,而且会给深度网络训练带来巨大问题。
Softplus照顾到了新模型的前两点,却没有稀疏激活性。因而,校正函数max(0,x)成了近似符合该模型的最大赢家。
Part I:关于稀疏性的观点
Machine Learning中的颠覆性研究是稀疏特征,基于数据的稀疏特征研究上,派生了Deep Learning这一分支。
稀疏性概念最早由Olshausen、Field在1997年对信号数据稀疏编码的研究中引入,并最早在卷积神经网络中得以大施拳脚。
近年来,稀疏性研究不仅在计算神经科学、机器学习领域活跃,甚至信号处理、统计学也在借鉴。
总结起来稀疏性大概有以下三方面的贡献:
1.1 信息解离
当前,深度学习一个明确的目标是从数据变量中解离出关键因子。原始数据(以自然数据为主)中通常缠绕着高度密集的特征。原因
是这些特征向量是相互关联的,一个小小的关键因子可能牵扰着一堆特征,有点像蝴蝶效应,牵一发而动全身。
基于数学原理的传统机器学习手段在解离这些关联特征方面具有致命弱点。
然而,如果能够解开特征间缠绕的复杂关系,转换为稀疏特征,那么特征就有了鲁棒性(去掉了无关的噪声)。
1.2 线性可分性
稀疏特征有更大可能线性可分,或者对非线性映射机制有更小的依赖。因为稀疏特征处于高维的特征空间上(被自动映射了)
从流形学习观点来看(参见降噪自动编码器),稀疏特征被移到了一个较为纯净的低维流形面上。
线性可分性亦可参照天然稀疏的文本型数据,即便没有隐层结构,仍然可以被分离的很好。
1.3 稠密分布但是稀疏
稠密缠绕分布着的特征是信息最富集的特征,从潜在性角度,往往比局部少数点携带的特征成倍的有效。
而稀疏特征,正是从稠密缠绕区解离出来的,潜在价值巨大。
1.4 稀疏性激活函数的贡献的作用:
不同的输入可能包含着大小不同关键特征,使用大小可变的数据结构去做容器,则更加灵活。
假如神经元激活具有稀疏性,那么不同激活路径上:不同数量(选择性不激活)、不同功能(分布式激活),
两种可优化的结构生成的激活路径,可以更好地从有效的数据的维度上,学习到相对稀疏的特征,起到自动化解离效果。
Part II:基于稀疏性的校正激活函数
2.1 非饱和线性端
撇开稀疏激活不谈,校正激活函数max(0,x),与Softplus函数在兴奋端的差异较大(线性和非线性)。
几十年的机器学习发展中,我们形成了这样一个概念:非线性激活函数要比线性激活函数更加先进。
尤其是在布满Sigmoid函数的BP神经网络,布满径向基函数的SVM神经网络中,往往有这样的幻觉,非线性函数对非线性网络贡献巨大。
该幻觉在SVM中更加严重。核函数的形式并非完全是SVM能够处理非线性数据的主力功臣(支持向量充当着隐层角色)。
那么在深度网络中,对非线性的依赖程度就可以缩一缩。另外,在上一部分提到,稀疏特征并不需要网络具有很强的处理线性不可分机制。
综合以上两点,在深度学习模型中,使用简单、速度快的线性激活函数可能更为合适。
<img ALT="技术分享" src="/blog7style/images/common/sg_trans.gif" real_src ="/blog/.png" WIDTH="356" HEIGHT="271" STYLE="font-family: Arial, Helvetica, sans- padding-top: 0 padding-right: 0 padding-bottom: 0 padding-left: 0 max-width: 680 overflow-x: overflow-y:"
TITLE="转:ReLu(Rectified&Linear&Units)激活函数" />
如图,一旦神经元与神经元之间改为线性激活,网络的非线性部分仅仅来自于神经元部分选择性激活。
2.2 Vanishing Gradient Problem
更倾向于使用线性神经激活函数的另外一个原因是,减轻梯度法训练深度网络时的Vanishing Gradient Problem。
看过BP推导的人都知道,误差从输出层反向传播算梯度时,在各层都要乘当前层的输入神经元值,激活函数的一阶导数。
即Grad=Error&Sigmoid&(x)&x。使用双端饱和(即值域被限制)Sigmoid系函数会有两个问题:
&#9312;Sigmoid‘(x)∈(0,1) &导数缩放
&#9313;x∈(0,1)或x∈(-1,1) &饱和值缩放
这样,经过每一层时,Error都是成倍的衰减,一旦进行递推式的多层的反向传播,梯度就会不停的衰减,消失,使得网络学习变慢。
而校正激活函数的梯度是1,且只有一端饱和,梯度很好的在反向传播中流动,训练速度得到了很大的提高。
Softplus函数则稍微慢点,Softplus‘(x)=Sigmoid(x)∈(0,1)
,但是也是单端饱和,因而速度仍然会比Sigmoid系函数快。
Part III 潜在问题
强制引入稀疏零的合理性?
诚然,稀疏性有很多优势。但是,过分的强制稀疏处理,会减少模型的有效容量。即特征屏蔽太多,导致模型无法学习到有效特征。
论文中对稀疏性的引入度做了实验,理想稀疏性(强制置0)比率是70%~85%。超过85%,网络就容量就成了问题,导致错误率极高。
<img ALT="技术分享" src="/blog7style/images/common/sg_trans.gif" real_src ="/blog/.png" WIDTH="472" HEIGHT="359" STYLE="font-family: Arial, Helvetica, sans- padding-top: 0 padding-right: 0 padding-bottom: 0 padding-left: 0 max-width: 680 overflow-x: overflow-y:"
TITLE="转:ReLu(Rectified&Linear&Units)激活函数" />
对比大脑工作的95%稀疏性来看,现有的计算神经网络和生物神经网络还是有很大差距的。
庆幸的是,ReLu只有负值才会被稀疏掉,即引入的稀疏性是可以训练调节的,是动态变化的。
只要进行梯度训练,网络可以向误差减少的方向,自动调控稀疏比率,保证激活链上存在着合理数量的非零值。
Part IV ReLu的贡献
4.1 缩小做和不做非监督预训练的代沟
ReLu的使用,使得网络可以自行引入稀疏性。这一做法,等效于无监督学习的预训练。
<img ALT="技术分享" src="/blog7style/images/common/sg_trans.gif" real_src ="/blog/.png" WIDTH="523" HEIGHT="242" STYLE="font-family: Arial, Helvetica, sans- padding-top: 0 padding-right: 0 padding-bottom: 0 padding-left: 0 max-width: 680 overflow-x: overflow-y:"
TITLE="转:ReLu(Rectified&Linear&Units)激活函数" />
当然,效果肯定没预训练好。论文中给出的数据显示,没做预训练情况下,ReLu激活网络遥遥领先其它激活函数。
甚至出现了比普通激活函数预训练后更好的奇葩情况。当然,在预训练后,ReLu仍然有提升空间。
从这一层面来说,ReLu缩小了非监督学习和监督学习之间的代沟。当然,还有更快的训练速度。
4.2 更快的特征学习
在MNIST+LeNet4中,ReLu+Tanh的组合在epoch 50左右就能把验证集错误率降到1.05%
但是,全Tanh在epoch 150时,还是1.37%,这个结果ReLu+Tanh在epoch 17时就能达到了。
<img ALT="技术分享" src="/blog7style/images/common/sg_trans.gif" real_src ="/blog/.png" WIDTH="415" HEIGHT="336" STYLE="font-family: Arial, Helvetica, sans- padding-top: 0 padding-right: 0 padding-bottom: 0 padding-left: 0 max-width: 680 overflow-x: overflow-y:"
TITLE="转:ReLu(Rectified&Linear&Units)激活函数" />
该图来自AlexNet的对ReLu和普通Sigmoid系函数做的对比测试,可以看到,ReLu的使用,使得学习周期
大大缩短。综合速率和效率,DL中大部分激活函数应该选择ReLu。
Part V &Theano中ReLu的实现
ReLu可以直接用T.maximum(0,x)实现,用T.max(0,x)不能求导.
Part VI &ReLu训练技巧
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

我要回帖

更多关于 bp神经网络的激活函数 的文章

 

随机推荐