一文理解常用激活函数

1. Sigmoid

公式:

sigmoid 1 sigmoid1等于多少_python

Sigmoid的函数形状如下图所示:

sigmoid 1 sigmoid1等于多少_python_02


对应的导数形式如下图所示:

sigmoid 1 sigmoid1等于多少_神经网络_03


Sigmoid函数的优点:

  • 便于求导,可以看到Sigmoid的梯度是平滑的函数
  • 能够压缩数据,数据会压缩在0-1之间
  • 适合用于前向传播

Sigmoid函数的缺点:

  • 容易出现梯度消失
  • Sigmoid的输出不是0均值
  • 幂函数计算相对比较耗时

2. Tanh

公式:
sigmoid 1 sigmoid1等于多少_机器学习_04

Tanh的函数形状如下图所示:

sigmoid 1 sigmoid1等于多少_机器学习_05

对应的导数形式如下图所示:

sigmoid 1 sigmoid1等于多少_机器学习_06

tanh函数的优点:

  • 将数据收敛到-1~+1之间,并且输出是0均值
  • 收敛速度比Sigmoid函数更快

tanh函数的缺点:

  • 仍然存在梯度消失的问题
  • 仍然存在幂指數运算,计算成本很高

3. ReLU

公式:
sigmoid 1 sigmoid1等于多少_深度学习_07

ReLU的函数形状如下图所示:

sigmoid 1 sigmoid1等于多少_机器学习_08

对应的导数形式如下图所示:

sigmoid 1 sigmoid1等于多少_sigmoid 1_09

ReLU函数的优点:

  • 收敛速度更快
  • 解决了一部分梯度消失的问题

ReLU函数的缺点:

  • 没有完全解决梯度消失的问题,在x轴为负的部分神经元的梯度始终为0,相当于神经元一旦失活后,就不会被激活

4. LeakyReLU

公式:

sigmoid 1 sigmoid1等于多少_sigmoid 1_10

LeakyReLU的函数形状如下图所示:

sigmoid 1 sigmoid1等于多少_python_11

对应的导数形式如下图所示:

sigmoid 1 sigmoid1等于多少_机器学习_12

LeakyReLU函数的优点:

  • 解决了ReLU中神经一旦失活,就无法再次激活的问题

LeakyReLU函数的缺点:

  • 无法为正负输入值提供一致的关系预测,可以看到不同区间的函数是不一致的

5. ELU

公式:
sigmoid 1 sigmoid1等于多少_深度学习_13

ELU的函数形状如下图所示:

sigmoid 1 sigmoid1等于多少_机器学习_14

对应的导数形式如下图所示:

sigmoid 1 sigmoid1等于多少_机器学习_15

ELU函数的优点:

  • 有ReLU所有的优点

ELU函数的缺点:

  • 计算相较ReLU来说会比较复杂,效果也不一定比ELU好

6. GELU

公式:

sigmoid 1 sigmoid1等于多少_sigmoid 1_16

sigmoid 1 sigmoid1等于多少_深度学习_17

GELU的函数形状如下图所示:

sigmoid 1 sigmoid1等于多少_深度学习_18

对应的导数形式如下图所示:

sigmoid 1 sigmoid1等于多少_深度学习_19


GELU函数的优点:

  • 之前描述的所有的激活函数正则化和激活函数是分开进行的,GELU是同时进行正则化和激活函数

GELU函数的缺点:

  • 暂无,等待补充

PS: 上述图例代码

import torch
import matplotlib.pylab as plt
import torch.nn.functional as F
import os

func_pic_res = "./func_pic_res/"
if not os.path.exists(func_pic_res):
    os.makedirs(func_pic_res)


def xyplot(x_vals, y_vals, name):
    plt.figure()
    plt.rcParams['figure.figsize'] = (5, 3.5)
    plt.plot(x_vals.detach().numpy(), y_vals.detach().numpy(), label=name, linewidth=1.5, color='#FF0000')
    plt.grid(True, linestyle=':')
    plt.legend(loc='upper left')
    # dark_background, seaborn, ggplot
    plt.style.use("seaborn")
    ax = plt.gca()
    ax.spines['right'].set_color("none")
    ax.spines['top'].set_color("none")
    ax.spines['bottom'].set_position(("data", 0))
    ax.spines['left'].set_position(("data", 0))
    ax.spines['bottom'].set_linewidth(0.5)
    ax.spines['left'].set_linewidth(0.5)
    ax.xaxis.set_ticks_position('bottom')
    ax.yaxis.set_ticks_position('left')
    plt.savefig(func_pic_res + "{}.jpg".format(name))


# sigmoid激活函数
def test_Sigmoid():
    x = torch.arange(-10.0, 10.0, 0.1, requires_grad=True)
    y = x.sigmoid()
    xyplot(x, y, 'Sigmoid')
    # 导数
    y.sum().backward()
    xyplot(x, x.grad, 'grad of Sigmoid')


def test_Tanh():
    x = torch.arange(-10.0, 10.0, 0.1, requires_grad=True)
    y = x.tanh()
    xyplot(x, y, 'Tanh')
    # 导数
    y.sum().backward()
    xyplot(x, x.grad, 'grad of Tanh')


def test_ReLU():
    x = torch.arange(-10.0, 10.0, 0.1, requires_grad=True)
    y = x.relu()
    xyplot(x, y, 'ReLU')
    # 导数
    y.sum().backward()
    xyplot(x, x.grad, 'grad of ReLU')


def test_LeakyReLU():
    x = torch.arange(-10.0, 10.0, 0.1, requires_grad=True)
    y = F.leaky_relu(x, negative_slope=0.1, inplace=False)
    xyplot(x, y, 'LeakyReLU(negative_slope=0.1)')
    # 导数
    y.sum().backward()
    xyplot(x, x.grad, 'grad of LeakyReLU(negative_slope=0.1)')


def test_ELU():
    x = torch.arange(-10.0, 10.0, 0.1, requires_grad=True)
    y = F.elu(x, alpha=0.3, inplace=False)
    xyplot(x, y, 'ELU(alpha=0.3)')
    # 导数
    y.sum().backward()
    xyplot(x, x.grad, 'grad of ELU(alpha=0.3)')


def test_GELU():
    x = torch.arange(-10.0, 10.0, 0.1, requires_grad=True)
    y = F.gelu(x)
    xyplot(x, y, 'GELU')
    # 导数
    y.sum().backward()
    xyplot(x, x.grad, 'grad of GELU')


if __name__ == "__main__":
    test_Sigmoid()
    test_Tanh()
    test_ReLU()
    test_LeakyReLU()
    test_ELU()
    test_GELU()