1、关于激活函数
激活函数,在神经网络中扮演着十分关键的角色。它将上一层神经元输出的加权求和结果输入到一个非线性的函数中,使之进行非线性映射,进而大大提升了神经网络的容限能力,使之可以在一定条件下逼近任意函数。
如果没有激活函数或者激活函数设定为一个线性函数,则神经网络的隐藏层将退化为一个线性映射的感知机,这个时候加深网络层次将变得没有任何效果了。
2、图解激活函数
常见的激活函数有sigmoid、tanh、relu、leaky-relu、elu,下面分别对这几个函数进行图解。
sigmoid:
其函数及其导函数如下:
作图表示如下(通过控制变量分别设定:w=1时, b= [1,4];b=0时,w = [0.3,0.4]。下同):
从上图可以看出:
- sigmoid函数的函数值是从0到1的,其均值非零,不适合作为中间层的激活函数,通常在二分类问题时用于最后的输出层;
- sigmoid函数随着自变量的绝对值变大,其导数逐渐趋近于零,也即左右软饱和,而这会导致在神经网络的参数在经过多个层的传递之后发生梯度消失(梯度弥散),最终导致模型不能收敛;
- 参数w,b对激活函数的影响是:使其斜率发生变化、中心点发生偏移。
tanh:
其函数及其导函数如下:
作图表示如下:
可以看出:
- tanh函数的输出均值为0,解决了sigmoid函数的其中一个弊端,这使得其比sigmoid更为常见的出现在神经网络模型中;
- 但是,仍然存在梯度消失问题,左右软饱和;
- 函数及导函数的图形与sigmoid非常类似;
ReLU:
其函数及其导函数如下:
作图表示如下:
可以看出:
- ReLU函数相比前两种激活函数,解决了单侧梯度消失的问题,仅左侧硬饱和;
- 这种单侧抑制的方式使得ReLU具有了稀疏激活的功能,也即只有输入进入大于0的区间时才被激活;
- 同时,ReLU的实现非常简单(if-else)、计算非常快(区别于sigmoid和tanh的浮点运算)。
关于稀疏激活,有解释如下:
稀疏激活性:从信号方面来看,即神经元同时只对输入信号的少部分选择性响应,大量信号被刻意的屏蔽了,这样可以提高学习的精度,更好更快地提取稀疏特征。当 x<0 时,ReLU 硬饱和,而当 x>0 时,则不存在饱和问题。ReLU 能够在 x>0 时保持梯度不衰减,从而缓解梯度消失问题。
Leaky-ReLU:
其函数及其导函数如下:
作图表示如下:
可以看出:
- Leaky-ReLU相比ReLU使其左侧不在产生抑制作用,而是给了一个较小的斜率,使梯度在这个范围内仍可传播;
- 但实际应用并无明显效果,反而失去了稀疏激活的特性。
ELU:
其函数及其导函数如下:
作图表示如下:
可以看出:
ELU函数既让函数的左侧软饱和,使函数在0点变得连续,又解决了Dead ReLU的问题;
关于Dead ReLU:
由于ReLU左侧导数为0的原因,在梯度较小时会出现参数无法更新,从而导致神经元死亡。
3、作图代码
现附上以上作图所用代码,以供参考:
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 10 17:52:52 2019
@author: oYeZhou
"""
import numpy as np
import matplotlib.pyplot as plt
# =============================================================================
# 定义常见激活函数及其导函数
# =============================================================================
def sigmoid(x,w,b):
return 1/(1+np.exp(-(w*x+b)))
def tanh(x,w,b):
return np.tanh(w*x+b)
def relu(x,w,b):
return [w*i+b if w*i+b>0 else 0 for i in x ]
def lrelu(x,w,b,alpha=0.5):
return [w*i+b if w*i+b>0 else alpha*(w*i+b) for i in x ]
def elu(x,w,b,alpha=0.5):
return [w*i+b if w*i+b>0 else alpha*(np.exp(w*i+b)-1) for i in x]
def sigmoid_d(x,w,b):
return sigmoid(x,w,b)*(1 - sigmoid(x,w,b))
def tanh_d(x,w,b):
return 1 - tanh(x,w,b)*tanh(x,w,b)
def relu_d(x,w,b):
return [0 if w*i+b<=0 else 1 for i in x]
def lrelu_d(x,w,b,alpha=0.5):
return [1 if w*i+b>0 else alpha for i in x]
def elu_d(x,w,b,alpha=0.5):
return [1 if w*i+b>0 else alpha*np.exp(w*i+b) for i in x]
# =============================================================================
# 定义作图函数:根据指定的数据及相应激活函数作图
# =============================================================================
def act_plot(x,act_fun,w,b):
fig = plt.figure(figsize=(12,4))
plt.subplot(121)
for i in range(len(w)):
plt.plot(x,act_fun(x,w[i],0),label='w='+str(w[i])+' b=0', alpha=0.8)
plt.legend()
plt.subplot(122)
for i in range(len(b)):
plt.plot(x,act_fun(x,1,b[i]),label='w=1 b='+str(b[i]), alpha=0.8)
plt.legend()
fig.suptitle(str(act_fun.__name__))
plt.show()
# 初始化数据
x = np.linspace(-20,20,2000)
#w = [1,2,3,4]
#b = [1,2,3,4]
w = [0.3,0.4]
b = [1,4]
# 对sigmoid函数及其导函数作图
act_plot(x,sigmoid,w,b)
act_plot(x,sigmoid_d,w,b)
# 对tanh函数及其导函数作图
act_plot(x,tanh,w,b)
act_plot(x,tanh_d,w,b)
# 对relu函数及其导函数作图
act_plot(x,relu,w,b)
act_plot(x,relu_d,w,b)
# 对leaky-relu函数及其导函数作图
act_plot(x,lrelu,w,b)
act_plot(x,lrelu_d,w,b)
# 对elu函数及其导函数作图
act_plot(x,elu,w,b)
act_plot(x,elu_d,w,b)