sigmoid

sigmoid函数和tanh函数是研究早期被广泛使用的2种激活函数。两者都为S型饱和函数。

  1. 当sigmoid 函数输入的值趋于正无穷或负无穷时,梯度会趋近零,从而发生梯度弥散现象
  2. sigmoid函数的输出恒为正值,不是以零为中心的,这会导致权值更新时只能朝一个方向更新,从而影响收敛速度。

tanh激活函数是sigmoid 函数的改进版,是以零为中心的对称函数,收敛速度快,不容易出现 loss 值晃动,但是无法解决梯度弥散的问题

2个函数的计算量都是指数级的,计算相对复杂。softsign 函数是 tanh 函数的改进版,为 S 型饱和函数,以零为中心,值域为(−1,1)。

公式

更多可以直接看

# -*- coding: utf8 -*-
#
import math

import torch
from torch.functional import F

print('sigmoid'.center(60, '-'))


def sigmoid(x):
    return 1 / (1 + math.exp(-x))


print(torch.sigmoid(torch.tensor([1., 2.])))
print([sigmoid(1.), sigmoid(2.)])

print('softmax'.center(60, '-'))


def softmax(xs):
    z_exp = [math.exp(x) for x in xs]
    sum_z_exp = sum(z_exp)
    return [_z_exp / sum_z_exp for _z_exp in z_exp]


print(torch.softmax(torch.tensor([1., 2., 7.]), dim=-1))
print(softmax([1., 2., 7.]))

print('tanh'.center(60, '-'))


def tanh(x):
    return (math.exp(x) - math.exp(-x)) / (math.exp(x) + math.exp(-x))


print(torch.tanh(torch.tensor([1., 2.])))
print([tanh(1.), tanh(2.)])

print('cross_entropy'.center(60, '-'))
import torch


def my_cross_entropy(input, target, reduction="mean"):
    # input.shape: torch.size([-1, class])
    # target.shape: torch.size([-1])
    # reduction = "mean" or "sum"
    # input是模型输出的结果,与target求loss
    # target的长度和input第一维的长度一致
    # target的元素值为目标class
    # reduction默认为mean,即对loss求均值
    # 还有另一种为sum,对loss求和

    # 这里对input所有元素求exp
    exp = torch.exp(input)
    # 根据target的索引,在exp第一维取出元素值,这是softmax的分子
    tmp1 = exp.gather(1, target.unsqueeze(-1)).squeeze()
    # 在exp第一维求和,这是softmax的分母
    tmp2 = exp.sum(1)
    # softmax公式:ei / sum(ej)
    softmax = tmp1 / tmp2
    # cross-entropy公式: -yi * log(pi)
    # 因为target的yi为1,其余为0,所以在tmp1直接把目标拿出来,
    # 公式中的pi就是softmax的结果
    log = -torch.log(softmax)
    # 官方实现中,reduction有mean/sum及none
    # 只是对交叉熵后处理的差别
    if reduction == "mean":
        return log.mean()
    else:
        return log.sum()

# example1
# input = torch.randn(3, 5,)
# target = torch.randint(5, (3,), dtype=torch.int64)
# example2
input = torch.tensor([[0.1, 0.9], [0.9, 0.1]])
target = torch.tensor([1, 0])
# example3
# input = torch.tensor([[0., 1.], [1., 0.]])
# target = torch.tensor([1, 0])
loss1_mean = F.cross_entropy(input, target)
loss2_mean = my_cross_entropy(input, target)
print(loss1_mean)
print(loss2_mean)

loss1_sum = F.cross_entropy(input, target, reduction="sum")
loss2_sum = my_cross_entropy(input, target, reduction="sum")
print(loss1_sum)
print(loss2_sum)


def bce_loss_with_logit(y_pred, y_true, reduction='mean'):
    y_pred = sigmoid(y_pred)

    loss = -y_true * torch.log(y_pred) - (1 - y_true) * torch.log(1 - y_pred)
    if reduction == 'mean':
        return torch.mean(loss)
    raise NotImplementedError

交叉熵

有木有发现,伯努利分布加上log就是交叉熵。

另外其实可以理解交叉熵就是极大似然估计加上log。

如何解释梯度消失呢?

描述

梯度消失是指模型在反向传播的过程中,更上层模型获取到的梯度值越来越小,导致更上层的权重基本保持不变,导致整个模型无法正常收敛以及训练。

原因?
其出现原因在Xavier Glorot和Yoshua Bengio在2010年的一篇论文中阐述了一些观点,包含像当时流行的sigmoid激活函数以及均值为0标准差为1的权重初始化方案,每层输出的方差远大于输入的方差,随着网络的延伸,方差在每一层之后都会增加,直到激活函数在顶层达到饱和为止。而实际像simoid这种激活函数随x的增大其导数值趋向于0,导致在反向传播的过程中,基本没有什么可以传播回去。

解决方式?

他提出应该在每一层网络的输出的方差等于其输入的方差。但是除非该层具有相等数量的输入和神经元,否则这条条件无法满足,故提出一些这种方案,像Xavier初始化或者Glorot初始化。