PyTorch实现L1,L2正则化以及Dropout
程序员文章站
2022-07-13 10:38:46
...
正则化(Regularization)
机器学习中,许多策略被显式的设计来减少测试误差(可能会以增大训练误差为代价),这些策略统称为正则化。正则化的目的是限制参数过多或者过大,避免模型更加复杂。
L1正则化和L2正则化是在损失函数后面会添加一个额外项,可以看做是损失函数的惩罚项。所谓“惩罚”是指对损失函数中的某些参数做一些限制。
L1正则化和L2正则化的作用:
- L1正则化可以产生稀疏权值矩阵,即产生一个稀疏模型,可以用于特征选择
- L2正则化可以防止模型过拟合(overfitting);一定程度上,L1也可以防止过拟合
Dropout
Dropout可以作为训练深度神经网络的一种trick供选择。在每个训练批次中,通过忽略一半的特征检测器(让一半的隐层节点值为0),这样可以使模型泛化性更强,因为它不会太依赖某些局部的特征,从而明显地减少过拟合现象。
Dropout流程:
- 首先随机(临时)删掉网络中一半的隐藏神经元,输入输出神经元保持不变。
- 把输入x通过修改后的网络前向传播,然后把得到的损失结果通过修改的网络反向传播。一小批训练样本执行完这个过程后,在没有被删除的神经元上按照随机梯度下降法更新对应的参数(w,b)。
- 然后继续重复这一过程:
恢复被删掉的神经元(此时被删除的神经元保持原样,而没有被删除的神经元已经有所更新);
从隐藏层神经元中随机选择一个一半大小的子集临时删除掉(备份被删除神经元的参数);
对一小批训练样本,先前向传播然后反向传播损失并根据随机梯度下降法更新参数(没有被删除的那一部分参数得到更新,删除的神经元参数保持被删除前的结果)。
pytorch实现代码
#L2正则化:torch.optim集成了很多优化器,如SGD,Adadelta,Adam,Adagrad,RMSprop等
#这些优化器自带的一个参数weight_decay,用于指定权值衰减率,相当于L2正则化中的λ参数
import torch
import torch.nn as nn
import numpy as np
class net(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(100,50)
self.fc2 = nn.Linear(50,1)
self.relu = nn.ReLU(inplace=True)
def forward(self, inputs):
layer = self.fc1(inputs)
layer = self.relu(layer)
layer = self.fc2(layer)
return layer
inputs = np.random.normal(size=(8,100))
inputs = torch.tensor(inputs).float()
labels = np.ones((8,1))
labels = torch.tensor(labels).float()
n = net()
weight_p, bias_p = [],[]
for name, p in n.named_parameters():
if 'bias' in name:
bias_p += [p]
else:
weight_p += [p]
criterion = nn.MSELoss()
logit = n(inputs)
loss = criterion(input=logit, target=labels)
opt = torch.optim.SGD([{'params': weight_p, 'weight_decay':1e-5},
{'params': bias_p, 'weight_decay':0}],
lr=1e-2,
momentum=0.9)
opt.zero_grad()
loss.backward()
opt.step()
#Dropout
import torch
import numpy as np
class Dropout:
def __init__(self, dropout_ratio=0.5):
self.dropout_ratio = dropout_ratio
self.train_flg = True
self.mask = None
def __call__(self, x, manual_mask=None, train_flg=True):
if train_flg:
if manual_mask is None:
self.mask = np.random.rand(*x.shape) > self.dropout_ratio
else:
self.mask = manual_mask
out = x * self.mask / (1.0 - self.dropout_ratio)
return out
else:
return x
def backward(self, d_loss):
dx = d_loss * self.mask / (1.0 - self.dropout_ratio)
return dx
np.set_printoptions(precision=6, suppress=True, linewidth=120)
np.random.seed(12)
torch.random.manual_seed(3)
x_numpy = np.random.random((3, 7))
x_tensor = torch.tensor(x_numpy, requires_grad=True)
drop_out_numpy = Dropout(dropout_ratio=0.45)
drop_out_tensor = torch.nn.Dropout(p=0.45)
print("\n----- 训练阶段 -----")
train_flag = True
drop_out_tensor.train()
out_tensor = drop_out_tensor(x_tensor)
mask = out_tensor > 0
mask = mask.data.numpy()
out_numpy = drop_out_numpy(x_numpy, mask, train_flg=train_flag)
print("train mask : \n", mask)
print("train x : \n", x_numpy)
print("numpy out : \n", out_numpy)
print("tensor out : \n", out_tensor.data.numpy())
print("\n----- 反向传播 -----")
d_loss_numpy = np.random.random((3, 7))
d_loss_tensor = torch.tensor(d_loss_numpy, requires_grad=True)
dx_numpy = drop_out_numpy.backward(d_loss_numpy)
out_tensor.backward(d_loss_tensor)
dx_tensor = x_tensor.grad
print("dx_numpy : \n", dx_numpy)
print("dx_tensor : \n", dx_tensor.data.numpy())
print("\n----- 测试阶段 -----")
train_flag = False
drop_out_tensor.eval()
out_tensor = drop_out_tensor(x_tensor)
mask = out_tensor > 0
mask = mask.data.numpy()
out_numpy = drop_out_numpy(x_numpy, mask, train_flg=train_flag)
print("test mask : \n", mask)
print("test x : \n", x_numpy)
print("numpy out : \n", out_numpy)
print("tensor out : \n", out_tensor.data.numpy())
参考:
机器学习中正则化项L1和L2的直观理解
机器学习中的范数规则化之(一)L0、L1与L2范数
【通俗易懂】机器学习中 L1 和 L2 正则化的直观解释
正则化及正则化项的理解
理解dropout
深度学习中Dropout原理解析
神经网络九:Regularization(正则化)与Dropout
Python和PyTorch对比实现dropout函数及反向传播