2022.05.26更新
激活函数是一种添加到人工神经网络中的函数,类似于人类大脑中基于神经元的模型,激活函数最终决定了要发射给下一个神经元的内容。
此图来自百度百科,其中step function就是激活函数,它是对之前一层进行汇总后信号进行激活,传给下一层神经元。
常用的激活函数有以下10个:
- Sigmoid
- Tanh
- ReLU
- Softmax
- Leaky ReLU
- ELU
- PReLU
- Swish
- Squareplus
- SMU
如上图是Sigmoid函数的函数图像。
Sigmoid 函数的图像看起来像一个 S 形曲线。
公式:
f ( x ) = 1 1 + e − x f(x)=\frac 1{1+e^{-x}} f(x)=1+e−x1
特点:
代码演示:
import matplotlib.pyplot as plt
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
fig, ax = plt.subplots()
x = np.linspace(-10, 10, 100)
y = sigmoid(x)
ax.plot(x, y)
# 画轴
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position(('data', 0))
ax.spines['left'].set_position(('axes', 0.5))
plt.grid() # 设置方格
plt.title("Sigmoid")
plt.show()
如上图是Tanh函数的函数图像。
Tanh 函数的图像看起来像一个有点扁的 S 形曲线。Tanh 是一个双曲正切函数。Tanh 函数和 Sigmoid 函数的曲线相对相似。但是它比 Sigmoid 函数更有一些优势。
公式:
f ( x ) = 2 1 + e − 2 x − 1 f(x)=\frac 2{1+e^{-2x}}-1 f(x)=1+e−2x2−1
特点:
import matplotlib.pyplot as plt
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def tanh(x):
return 2 / (1 + np.exp(-2*x)) - 1
fig, ax = plt.subplots()
x = np.linspace(-10, 10, 100)
y1 = tanh(x)
y2 = sigmoid(x)
ax.plot(x, y1, '-b', label='Tanh')
ax.plot(x, y2, '-r', label='Sigmoid')
ax.legend() # 设置图例
# 画轴
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position(('data', 0))
ax.spines['left'].set_position(('axes', 0.5))
plt.grid() # 设置方格
plt.title("Tanh and Sigmoid")
plt.show()
如上图是ReLU函数的函数图像。
ReLU 函数是深度学习中较为流行的一种激活函数。
公式:
f ( x ) = { m a x ( 0 , x ) x ≥ 0 0 x < 0 f(x)= \begin{cases} {max(0, x)}&\text{x ≥ 0}\\ 0& \text{x < 0} \end{cases} f(x)={max(0,x)0x ≥ 0x < 0
特点:
import matplotlib.pyplot as plt
import numpy as np
def relu(x):
return np.maximum(0, x)
fig, ax = plt.subplots()
x = np.linspace(-10, 10, 100)
y = relu(x)
ax.plot(x, y, '-r', linewidth=4)
ax.legend() # 设置图例
# 画轴
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position(('data', 0))
ax.spines['left'].set_position(('axes', 0.5))
plt.grid() # 设置方格
plt.title("ReLU")
plt.show()
如上图是Softmax函数的函数图像。
公式:
e x i ∑ j = 1 n e x j \frac {e^{x_i}}{\sum_{j=1}^ne^{x_j}} ∑j=1nexjexi
特点:
代码演示
import matplotlib.pyplot as plt
import numpy as np
def softmax(x):
x = np.exp(x) / np.sum(np.exp(x))
return x
fig, ax = plt.subplots()
x = np.linspace(-10, 10, 100)
y = softmax(x)
ax.plot(x, y)
ax.legend() # 设置图例
# 画轴
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position(('data', 0))
ax.spines['left'].set_position(('axes', 0.5))
plt.grid() # 设置方格
plt.title("Softmax")
plt.show()
如上图是Leaky ReLU函数的函数图像。
它是一种专门设计用于解决 ReLU 梯度消失问题的激活函数。
公式:
f ( x ) = { x x ≥ 0 a x x < 0 f(x)= \begin{cases} {x}&\text{x ≥ 0}\\ {ax}& \text{x < 0} \end{cases} f(x)={xaxx ≥ 0x < 0
特点:
注意: 从理论上讲,Leaky ReLU 具有 ReLU 的所有优点,而且 Dead ReLU 不会有任何问题,但在实际操作中,尚未完全证明 Leaky ReLU 总是比 ReLU 更好。
代码演示:
import matplotlib.pyplot as plt
import numpy as np
def leaky_relu(x,a=0.01):
return np.maximum(a*x, x)
fig, ax = plt.subplots()
x = np.linspace(-10, 10, 100)
y = leaky_relu(x)
ax.plot(x, y)
ax.legend() # 设置图例
# 画轴
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position(('data', 0))
ax.spines['left'].set_position(('axes', 0.5))
plt.grid() # 设置方格
plt.title("Leaky ReLu")
plt.show()
如上图是ELU函数的函数图像。
ELU 的提出也解决了 ReLU 的问题。与 ReLU 相比,ELU 有负值,这会使激活的平均值接近零。均值激活接近于零可以使学习更快,因为它们使梯度更接近自然梯度。
公式:
f ( x ) = { x x ≥ 0 α ( e x − 1 ) x < 0 f(x)= \begin{cases} {x}&\text{x ≥ 0}\\ {\alpha(e^x - 1)}& \text{x < 0} \end{cases} f(x)={xα(ex−1)x ≥ 0x < 0
特点:
注意: 它的计算强度更高。与 Leaky ReLU 类似,尽管理论上比 ReLU 要好,但目前在实践中没有充分的证据表明 ELU 总是比 ReLU 好。
代码演示:
import matplotlib.pyplot as plt
import numpy as np
def elu(x,alpha=1):
a = x[x>0]
b = alpha*(np.exp(x[x<0])-1)
result=np.concatenate((b,a),axis=0)
return result
fig, ax = plt.subplots()
x = np.linspace(-10, 10, 100)
y = elu(x)
ax.plot(x, y)
ax.legend() # 设置图例
# 画轴
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position(('data', 0))
ax.spines['left'].set_position(('axes', 0.5))
plt.grid() # 设置方格
plt.title("ELU")
plt.show()
PReLU 也是 ReLU 的改进版本。
公式:
f ( x ) = { x x ≥ 0 α x x < 0 f(x)= \begin{cases} {x}&\text{x ≥ 0}\\ {\alpha x}& \text{x < 0} \end{cases} f(x)={xαxx ≥ 0x < 0
若 α \alpha α是可学习的参数,则 f ( x ) f(x) f(x)变为 PReLU。
特点:
代码就不演示了,和上面得Leaky ReLU一样。
如上图是Swish函数的函数图像。
Swish 的设计受到了 LSTM 和高速网络中 gating 的 sigmoid 函数使用的启发。我们使用相同的 gating 值来简化 gating 机制,这称为 self-gating。
self-gating 的优点在于它只需要简单的标量输入,而普通的 gating 则需要多个标量输入。这使得诸如 Swish 之类的 self-gated 激活函数能够轻松替换以单个标量为输入的激活函数(例如 ReLU),而无需更改隐藏容量或参数数量。
公式:
y = x ∗ s i g m o i d ( x ) y = x * sigmoid (x) y=x∗sigmoid(x)
特点:
代码演示:
import matplotlib.pyplot as plt
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def swish(x):
return sigmoid(x) * x
fig, ax = plt.subplots()
x = np.linspace(-10, 10, 100)
y = swish(x)
ax.plot(x, y)
ax.legend() # 设置图例
# 画轴
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position(('data', 0))
ax.spines['left'].set_position(('axes', 0.5))
plt.grid() # 设置方格
plt.title("Swish")
plt.show()
如上图是Squareplus函数的函数图像。
Squareplus是Softplus优化版本,Squareplus由超参数b>0定义,它决定了x=0附近弯曲区域的大小。
公式:
y = 1 2 ( x + x 2 + b ) y=\frac 1{2}(x+\sqrt{x^2+b}) y=21(x+x2+b)
特点:
代码演示:
import numpy as np
import matplotlib.pyplot as plt
def Squareplus(x, b=0.2):
x = 0.5 * (x + np.sqrt(x**2+b))
return x
fig, ax = plt.subplots()
x = np.linspace(-10, 10, 100)
y = Squareplus(x)
ax.plot(x, y)
ax.legend() # 设置图例
# 画轴
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position(('data', 0))
ax.spines['left'].set_position(('axes', 0.5))
plt.grid() # 设置方格
plt.title("Squareplus")
plt.show()
该函数是在已知激活函数Leaky ReLU近似的基础上,提出了一种新的激活函数,称之为Smooth Maximum Unit(SMU)。用SMU替换ReLU,ShuffleNet V2模型在CIFAR100数据集上得到了6.22%的提升。
参考:https://github.com/iFe1er/SMU_pytorch
tensorflow2.x代码如下:
import tensorflow as tf
def SMU(x,alpha=0.25):
mu = tf.compat.v1.get_variable('SMU_mu', shape=(),
initializer=tf.constant_initializer(1000000),
dtype=tf.float32)
return ((1+alpha)*x + (1-alpha)*x*tf.math.erf(mu*(1-alpha)*x))/2
def SMU1(x,alpha=0.25):
mu = tf.compat.v1.get_variable('SMU1_mu', shape=(),
initializer=tf.constant_initializer(4.352665993287951e-9),
dtype=tf.float32)
return ((1+alpha)*x+tf.math.sqrt(tf.math.square(x-alpha*x)+tf.math.square(mu)))/2
pytorch代码如下:
import torch
from torch import nn
class SMU(nn.Module):
'''
Implementation of SMU activation.
Shape:
- Input: (N, *) where * means, any number of additional
dimensions
- Output: (N, *), same shape as the input
Parameters:
- alpha: hyper parameter
References:
- See related paper:
https://arxiv.org/abs/2111.04682
Examples:
>>> smu = SMU()
>>> x = torch.Tensor([0.6,-0.3])
>>> x = smu(x)
'''
def __init__(self, alpha = 0.25):
'''
Initialization.
INPUT:
- alpha: hyper parameter
aplha is initialized with zero value by default
'''
super(SMU,self).__init__()
self.alpha = alpha
# initialize mu
self.mu = torch.nn.Parameter(torch.tensor(1000000.0))
def forward(self, x):
return ((1+self.alpha)*x + (1-self.alpha)*x*torch.erf(self.mu*(1-self.alpha)*x))/2
class SMU1(nn.Module):
'''
Implementation of SMU-1 activation.
Shape:
- Input: (N, *) where * means, any number of additional
dimensions
- Output: (N, *), same shape as the input
Parameters:
- alpha: hyper parameter
References:
- See related paper:
https://arxiv.org/abs/2111.04682
Examples:
>>> smu1 = SMU1()
>>> x = torch.Tensor([0.6,-0.3])
>>> x = smu1(x)
'''
def __init__(self, alpha = 0.25):
'''
Initialization.
INPUT:
- alpha: hyper parameter
aplha is initialized with zero value by default
'''
super(SMU1,self).__init__()
self.alpha = alpha
# initialize mu
self.mu = torch.nn.Parameter(torch.tensor(4.352665993287951e-9))
def forward(self, x):
return ((1+self.alpha)*x+torch.sqrt(torch.square(x-self.alpha*x)+torch.square(self.mu)))/2