7 激活函數 -庖丁解牛之pytorch

pytorch中實現了大部分激活函數,你也可以自定義激活函數,激活函數的實現在torch.nn.functional中,每個激活函數都對應激活模塊類,但最終還是調用torch.nn.functional,看了定義,你也能自定義激活函數,我們從最早的激活函數來看

sigmoid

def sigmoid(input):
    r"""sigmoid(input) -> Tensor

    Applies the element-wise function :math:`\text{Sigmoid}(x) = \frac{1}{1 + \exp(-x)}`

    See :class:`~torch.nn.Sigmoid` for more details.
    """
    warnings.warn("nn.functional.sigmoid is deprecated. Use torch.sigmoid instead.")
    return input.sigmoid()

源碼顯示這個激活函數直接調用tensor.sigmoid函數,值域在[0,1]之間,也就是把數據的所有值都壓縮在[0,1]之間,映射概率不錯,如果作爲激活函數有如下缺點

  • 神經元容易飽和,其值不在[-5, 5]之間,梯度基本爲0,導致權重更新非常緩慢
  • 值域中心不是0,相當於捨棄負值部分
  • 計算有點小貴,畢竟每次都算兩個exp,一定要做內存和計算的葛朗臺

tanh

def tanh(input):
    r"""tanh(input) -> Tensor

    Applies element-wise,
    :math:`\text{Tanh}(x) = \tanh(x) = \frac{\exp(x) - \exp(-x)}{\exp(x) + \exp(-x)}`

    See :class:`~torch.nn.Tanh` for more details.
    """
    warnings.warn("nn.functional.tanh is deprecated. Use torch.tanh instead.")
    return input.tanh()

這個函數的值域正常了,避免了sigmoid的問題,是[-1, 1],以0爲中心,但是依然存在一些問題梯度消失的神經元飽和問題,而且計算更貴!

relu

def relu(input, inplace=False):
    if inplace:
        return torch.relu_(input)
    return torch.relu(input) 

relu的函數定義就是max(0, x),解決了梯度消失的飽和問題,計算高效,線性值,一般來說比Sigmoid/tanh快6倍左右。而且有資料顯示,和生物神經激活機制非常相近。但是引入了新的問題,就是負值容易引起神經死亡,也就是說每次這個激活函數會擼掉負值的部分。

Leaky Relu

def leaky_relu(input, negative_slope=0.01, inplace=False):
    r"""
    leaky_relu(input, negative_slope=0.01, inplace=False) -> Tensor

    Applies element-wise,
    :math:`\text{LeakyReLU}(x) = \max(0, x) + \text{negative\_slope} * \min(0, x)`

    See :class:`~torch.nn.LeakyReLU` for more details.
    """
    if inplace:
        return torch._C._nn.leaky_relu_(input, negative_slope)
    return torch._C._nn.leaky_relu(input, negative_slope)

爲了處理負值的情況,Relu有了變種,其函數是max(0.01*x, x),這個函數解決了神經飽和問題,計算高效,而且神經不死了。

PRelu

def prelu(input, weight):
    r"""prelu(input, weight) -> Tensor

    Applies element-wise the function
    :math:`\text{PReLU}(x) = \max(0,x) + \text{weight} * \min(0,x)` where weight is a
    learnable parameter.

    See :class:`~torch.nn.PReLU` for more details.
    """
    return torch.prelu(input, weight)

這個函數的定義是max(ax, x),其中參數a可以隨時調整。

Elu Exponential Line Unit

def elu(input, alpha=1., inplace=False):
    r"""Applies element-wise,
    :math:`\text{ELU}(x) = \max(0,x) + \min(0, \alpha * (\exp(x) - 1))`.

    See :class:`~torch.nn.ELU` for more details.
    """
    if inplace:
        return torch._C._nn.elu_(input, alpha)
    return torch._C._nn.elu(input, alpha)

這個函數的定義是max(x, a*(exp(x)-1)),繼承了Relu的所有優點,but貴一點,均值爲0的輸出、而且處處一階可導,眼看着就順滑啊,哈哈,負值很好的處理了,魯棒性很好, nice!學完批標準化後,我們展示一個小示例,它居然在那個例子中幹掉了批標準化。
於是其他變種應運而生

SELU

def selu(input, inplace=False):
    r"""selu(input, inplace=False) -> Tensor

    Applies element-wise,
    :math:`\text{SELU}(x) = scale * (\max(0,x) + \min(0, \alpha * (\exp(x) - 1)))`,
    with :math:`\alpha=1.6732632423543772848170429916717` and
    :math:`scale=1.0507009873554804934193349852946`.

    See :class:`~torch.nn.SELU` for more details.
    """
    if inplace:
        return torch.selu_(input)
    return torch.selu(input)

還有其他變種relu6、celu等等

這些激活函數我們來個經驗參考:

  • 首先使用Relu,然後慢慢調整學習率
  • 可以嘗試Lecky Relu/Elu
  • 試一下tanh,不要期望太多
  • 不要嘗試sigmoid
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章