有时候神经网络的训练能在平时的测验中得到很好的成绩,但在大型考试中却发挥失常。这就是过拟合,神经网络不能表达除了训练数据以外的其他数据。
如何解决:
1.增加数据量
2.运用正则化。L1, L2…regularization
专门用于神经网络的正则化方法:dropout (随机失活)regularization
[每次训练其中的一部分数据,随机忽略训练,反复如此使得结果不依赖某一部分特定神经元。]
这种方法的关键步骤在于训练时随机丢失网络的单元包括与之连接网络权值。在训练的时候,丢失网络单元的方法也可以是的网络变得更为稀薄紧凑。在测试阶段,也可以使用这种稀薄的网络更容易的预测网络的输出。这种方式有效的减少了网络的过拟合问题,并且比其他的规则化的方法有了明显的提升。同时我们也展示了这种丢失数据的方法用于监督学习实例上,比如视觉处理、语音识别、文本分类和计算机生物学上对应用,在大多数数据集上该方法都表现出目前最优的效果。https://blog.csdn.net/on2way/article/details/50525548
https://blog.csdn.net/weixin_42398658/article/details/84578230
实现:
#包
import torch
import torch.nn as nn
import torch.nn.functional as F
#torchvision 包收录了若干重要的公开数据集、网络模型和计算机视觉中的常用图像变换
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
L1
对于每个 ω 我们都向目标函数增加一个λ|ω| 。
L1正则化有一个有趣的性质,它会让权重向量在最优化的过程中变得稀疏(即非常接近0)。也就是说,使用L1正则化的神经元最后使用的是它们最重要的输入数据的稀疏子集,同时对于噪音输入则几乎是不变的了。
相较L1正则化,L2正则化中的权重向量大多是分散的小数字。在实践中,如果不是特别关注某些明确的特征选择,一般说来L2正则化都会比L1正则化效果好。
PyTorch里的optimizer只能实现L2正则化,L1正则化只能手动实现:
regularization_loss = 0
for param in model.parameters():
regularization_loss += torch.sum(abs(param))
calssify_loss = criterion(pred,target)
loss = classify_loss + lamda * regularization_loss
optimizer.zero_grad()
loss.backward()
optimizer.step()
对于网络中的每个权重 ω ,向目标函数中增加一个1/2λω2 其中 λ 是正则化强度。这样该式子关于梯度就是 λω 了。
L2正则化可以直观理解为它对于大数值的权重向量进行严厉惩罚,倾向于更加分散的权重向量。
最后需要注意在梯度下降和参数更新的时候,使用L2正则化意味着所有的权重都以 w += -lambda * W向着0线性下降。
选择一个合适的权重衰减系数λ非常重要,这个需要根据具体的情况去尝试,初步尝试可以使用 1e-4 或者 1e-3
在PyTorch中某些optimizer优化器的参数weight_decay (float, optional)就是 L2 正则项,它的默认值为0。
optimizer = torch.optim.SGD(model.parameters(),lr=0.01,weight_decay=0.001)
dropout
torch.manual_seed(1) # Sets the seed for generating random numbers.reproducible
N_SAMPLES = 20
N_HIDDEN = 300
# training data
x = torch.unsqueeze(torch.linspace(-1, 1, N_SAMPLES), 1)
print('x.size()',x.size())
#torch.normal(mean, std, out=None) → Tensor
y = x + 0.3*torch.normal(torch.zeros(N_SAMPLES, 1), torch.ones(N_SAMPLES, 1))
#test data
test_x = torch.unsqueeze(torch.linspace(-1, 1, N_SAMPLES), 1)
test_y = test_x + 0.3*torch.normal(torch.zeros(N_SAMPLES, 1), torch.ones(N_SAMPLES, 1))
#show data
plt.scatter(x.data.numpy(), y.data.numpy(), c='magenta', s=50, alpha=0.5, label='train')
plt.scatter(test_x.data.numpy(), test_y.data.numpy(), c='cyan', s=50, alpha=0.5, label='test')
plt.legend(loc='upper left')
plt.ylim((-2.5, 2.5))
plt.show()
numpy实现dropout
""" 普通版随机失活: 不推荐实现 """
p = 0.5 # 激活神经元激活神经元激活神经元(重要的事情说三遍)的概率. p值更高 = 随机失活更弱
def train_step(X):
""" X中是输入数据 """
# 3层neural network的前向传播
H1 = np.maximum(0, np.dot(W1, X) + b1)
U1 = np.random.rand(*H1.shape) < p # 第一个dropout mask
H1 *= U1 # drop!
H2 = np.maximum(0, np.dot(W2, H1) + b2)
U2 = np.random.rand(*H2.shape) < p # 第二个dropout mask
H2 *= U2 # drop!
out = np.dot(W3, H2) + b3
# 反向传播:计算梯度... (略)
# 进行参数更新... (略)
def predict(X):
# 前向传播时模型集成
H1 = np.maximum(0, np.dot(W1, X) + b1) * p # 注意:激活数据要乘以p
H2 = np.maximum(0, np.dot(W2, H1) + b2) * p # 注意:激活数据要乘以p
out = np.dot(W3, H2) + b3
上述操作不好的性质是必须在测试时对激活数据要按照 p 进行数值范围调整,我们可以使其在训练时就进行数值范围调整,从而让前向传播在测试时保持不变。这样做还有一个好处,无论你决定是否使用随机失活,预测方法的代码可以保持不变。
这就是反向随机失活(inverted dropout):
"""
inverted dropout(反向随机失活): 推荐实现方式.
在训练的时候drop和调整数值范围,测试时不用任何改变.
"""
p = 0.5 # 激活神经元的概率. p值更高 = 随机失活更弱
def train_step(X):
# 3层neural network的前向传播
H1 = np.maximum(0, np.dot(W1, X) + b1)
U1 = (np.random.rand(*H1.shape) < p) / p # 第一个dropout mask. 注意/p!
H1 *= U1 # drop!
H2 = np.maximum(0, np.dot(W2, H1) + b2)
U2 = (np.random.rand(*H2.shape) < p) / p # 第二个dropout mask. 注意/p!
H2 *= U2 # drop!
out = np.dot(W3, H2) + b3
# 反向传播:计算梯度... (略)
# 进行参数更新... (略)
def predict(X):
# 前向传播时模型集成
H1 = np.maximum(0, np.dot(W1, X) + b1) # 不用数值范围调整了
H2 = np.maximum(0, np.dot(W2, H1) + b2)
out = np.dot(W3, H2) + b3
https://blog.csdn.net/LoseInVain/article/details/81708474