本文记录学习下面三个任务过程中思考过的问题及大家讨论过程中学习到的知识点,欢迎指正。
基础知识部分直接参考Dive-into-DL-PyTorch
net.eval()-> train(self, mode=False)-> self.training = False
net.train()-> train(self, mode=True)->-> self.training = True
目的:避免梯度消失和爆炸,就是要控制网络网络中间输出值的范围,不能太大也不能太小。
参考文献:《Understanding the difficulty of training deep feedforward neural networks》
方差一致性:保持数据尺度维持在恰当范围,通常方差为1
适用激活函数:饱和函数,如Sigmoid,Tanh
参考文献:《Delving deep into rectifiers:Surpassing human-level performance on ImageNet classification》
方差一致性:保持数据尺度维持在恰当范围,通常方差为1
适用激活函数:ReLU及其变种。
一般是先创建网络/module,再初始化,但实际上创建module时便会自动参数初始化,后续可以根据我们需要改变初始化方式
linear = nn.Linear(2,2)
for param in linear.parameters():
print(param)
for param in linear.parameters():
nn.init.normal_(param, mean=0, std=0.01)
print(param)
# Parameter containing:
# tensor([[-0.0887, 0.5130],
# [-0.0778, 0.0761]], requires_grad=True)
# Parameter containing:
# tensor([-0.1284, -0.4132], requires_grad=True)
# Parameter containing:
# tensor([[ 0.0139, -0.0109],
# [-0.0196, 0.0003]], requires_grad=True)
# Parameter containing:
# tensor([-0.0103, 0.0051], requires_grad=True)
con2d = nn.Conv2d(in_channels=1, out_channels=2, kernel_size=2, padding=2)
for param in con2d.parameters():
print(param)
torch.nn.init.xavier_uniform_(con2d.weight)
for param in con2d.parameters():
print(param)
# Parameter containing:
# tensor([[[[-0.0118, 0.0318],
# [ 0.4365, 0.1906]]],
# [[[-0.3413, 0.0226],
# [ 0.4624, 0.0659]]]], requires_grad=True)
# Parameter containing:
# tensor([ 0.2645, -0.4907], requires_grad=True)
# Parameter containing:
# tensor([[[[ 0.5391, -0.4437],
# [-0.6743, -0.5801]]],
# [[[-0.2168, -0.0300],
# [-0.2788, -0.6323]]]], requires_grad=True)
# Parameter containing:
# tensor([ 0.2645, -0.4907], requires_grad=True)
net = torch.nn.Sequential( #Lelet
nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, padding=2), #b*1*28*28 =>b*6*28*28
nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2), #b*6*28*28 =>b*6*14*14
nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5), #b*6*14*14 =>b*16*10*10
nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2), #b*16*10*10 => b*16*5*5
Flatten(), #b*16*5*5 => b*400
nn.Linear(in_features=16*5*5, out_features=120),
nn.Sigmoid(),
nn.Linear(120, 84),
nn.Sigmoid(),
nn.Linear(84, 10)
)
for param in net.parameters():
torch.nn.init.normal_(param)
# 但这种方式会报错
for param in net.parameters():
torch.nn.init.xavier_uniform_(param)
#从下面代码便可找到原因
# 所以只能对二维以上张量才能初始化,需要改为只对weight初始化,而非bias
def init_weights(m):
if type(m) == nn.Linear or type(m) == nn.Conv2d:
torch.nn.init.xavier_uniform_(m.weight)
net.apply(init_weights)
# 等价写法
for m in net:
if type(m) == nn.Linear or type(m) == nn.Conv2d:
torch.nn.init.xavier_uniform_(m.weight)
《矩阵求导-上》https://zhuanlan.zhihu.com/p/24709748
《矩阵求导-下》https://zhuanlan.zhihu.com/p/24863977
LSTM 能解决梯度消失/梯度爆炸”是对 LSTM 的经典误解。这里我先给出几个粗线条的结论,详细的回答以后有时间了再扩展:1、首先需要明确的是,RNN 中的梯度消失/梯度爆炸和普通的 MLP 或者深层 CNN 中梯度消失/梯度爆炸的含义不一样。MLP/CNN 中不同的层有不同的参数,各是各的梯度;而 RNN 中同样的权重在各个时间步共享,最终的梯度 g = 各个时间步的梯度 g_t 的和。2、由 1 中所述的原因,RNN 中总的梯度是不会消失的。即便梯度越传越弱,那也只是远距离的梯度消失,由于近距离的梯度不会消失,所有梯度之和便不会消失。RNN 所谓梯度消失的真正含义是,梯度被近距离梯度主导,导致模型难以学到远距离的依赖关系。3、LSTM 中梯度的传播有很多条路径, 这条路径上只有逐元素相乘和相加的操作,梯度流最稳定;但是其他路径(例如 )上梯度流与普通 RNN 类似,照样会发生相同的权重矩阵反复连乘。4、LSTM 刚提出时没有遗忘门,或者说相当于 ,这时候在 直接相连的短路路径上, 可以无损地传递给 ,从而这条路径上的梯度畅通无阻,不会消失。类似于 ResNet 中的残差连接。5、但是在其他路径上,LSTM 的梯度流和普通 RNN 没有太大区别,依然会爆炸或者消失。由于总的远距离梯度 = 各条路径的远距离梯度之和,即便其他远距离路径梯度消失了,只要保证有一条远距离路径(就是上面说的那条高速公路)梯度不消失,总的远距离梯度就不会消失(正常梯度 + 消失梯度 = 正常梯度)。因此 LSTM 通过改善一条路径上的梯度问题拯救了总体的远距离梯度。6、同样,因为总的远距离梯度 = 各条路径的远距离梯度之和,高速公路上梯度流比较稳定,但其他路径上梯度有可能爆炸,此时总的远距离梯度 = 正常梯度 + 爆炸梯度 = 爆炸梯度,因此 LSTM 仍然有可能发生梯度爆炸。不过,由于 LSTM 的其他路径非常崎岖,和普通 RNN 相比多经过了很多次激活函数(导数都小于 1),因此 LSTM 发生梯度爆炸的频率要低得多。实践中梯度爆炸一般通过梯度裁剪来解决。7、对于现在常用的带遗忘门的 LSTM 来说,6 中的分析依然成立,而 5 分为两种情况:其一是遗忘门接近 1(例如模型初始化时会把 forget bias 设置成较大的正数,让遗忘门饱和),这时候远距离梯度不消失;其二是遗忘门接近 0,但这时模型是故意阻断梯度流的,这不是 bug 而是 feature(例如情感分析任务中有一条样本 “A,但是 B”,模型读到“但是”后选择把遗忘门设置成 0,遗忘掉内容 A,这是合理的)。当然,常常也存在 f 介于 [0, 1] 之间的情况,在这种情况下只能说 LSTM 改善(而非解决)了梯度消失的状况。8、最后,别总是抓着梯度不放。梯度只是从反向的、优化的角度来看的,多从正面的、建模的角度想想 LSTM 有效性的原因。选择性、信息不变性都是很好的视角,
原文