sigmoid
sigmoid函数和tanh函数是研究早期被广泛使用的2种激活函数。两者都为S型饱和函数。
-
当sigmoid 函数输入的值趋于正无穷或负无穷时,梯度会趋近零,从而发生梯度弥散现象
。 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初始化。