前面做了模型训练与验证,我们会发现,模型在训练集、测试集的准确性并不一致,主要是因为存在泛化误差。
深度学习中常用于应对过拟合问题的方法:权重衰减和丢弃法(dropout)
Ten Techniques Learned From fast.ai中提到一些深度学习模型训练过程常用于应对过拟合问题的方法:合适的学习率、测试时增强(TTA)
1 权重衰减
1.1 基本内容
权重衰减即L2范数正则化,是在损失函数基础上加入权重参数平方和,以惩罚绝对值较大的权重参数项。那么在小批量随机梯度下降中,线性回归损失函数的权重更新变成了权重参数乘惩罚因子
λ
\lambda
λ的式子:L2范数正则化令权重w1和w2先自乘小于1的数,再减去不含惩罚项的梯度。随着训练进行,权重衰减,从而可能抑制过拟合。
下图是原始权重更新步骤
添加L2正则化项后,如图所示
1.2 Pytorch实现
直接在构造优化器实例时通过weight_decay 指定权重衰减超参数wd,默认情况下Pytorch对权重w和偏差b同时衰减,因此可以分别对权重和偏差构造优化器实例,从而只对权重衰减。
def fit_and_plot_pytorch(wd):
# 对权重参数衰减。
net = nn.Linear(num_inputs, 1)
nn.init.normal_(net.weight, mean=0, std=1)
nn.init.normal_(net.bias, mean=0, std=1)
optimizer_w = torch.optim.SGD(params=[net.weight], lr=lr,
weight_decay=wd) # 对权重参数衰减
optimizer_b = torch.optim.SGD(params=[net.bias], lr=lr) # 对偏差参数衰减
train_ls, test_ls = [], []
for _ in range(num_epochs):
for X, y in train_iter:
l = loss(net(X), y).mean()
optimizer_w.zero_grad()
optimizer_b.zero_grad()
l.backward()
# 对两个optimizer实例分别调用step函数,从而分别更新权重和偏差
optimizer_w.step()
optimizer_b.step()
train_ls.append(loss(net(train_features),
train_labels).mean().item())
test_ls.append(loss(net(test_features),
test_labels).mean().item())
semilogy(range(1, num_epochs + 1), train_ls, 'epochs','loss',range(1, num_epochs + 1), test_ls, ['train','test'])
print('L2 norm of w:', net.weight.data.norm().item())
2 倒置丢弃法(Dropout)
2.1 基本内容
随机丢弃隐藏层中的某些神经元,避免了输出层过度依赖某个神经元,从而在训练过程中起到正则化作用,从而减少过拟合的发生。涉及到的超参数是丢弃概率。
- 在测试模型中,一般不使用丢弃法。
- 一般情况,在全连接层部分,采用较大概率的dropout而在卷积层采用低概率或者不采用dropout。
![]() |
![]() |
2.2 Pytorch实现
def train_ch3(net, train_iter, test_iter, loss, num_epochs,batch_size,params=None, lr=None, optimizer=None):
for epoch in range(num_epochs):
train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
for X, y in train_iter:
y_hat = net(X)
l = loss(y_hat, y).sum()
# 梯度清零
if optimizer is not None:
optimizer.zero_grad()
elif params is not None and params[0].grad is not None:
for param in params:
param.grad.data.zero_()
l.backward()
if optimizer is None:
d2l.sgd(params, lr, batch_size)
else:
optimizer.step() #
train_l_sum += l.item()
train_acc_sum += (y_hat.argmax(dim=1) ==
y).sum().item()
n += y.shape[0]
test_acc = evaluate_accuracy(test_iter, net)
print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'
% (epoch + 1, train_l_sum / n, train_acc_sum / n,
test_acc))
# 模型
net = nn.Sequential(
d2l.FlattenLayer(),
nn.Linear(num_inputs, num_hiddens1),
nn.ReLU(),
nn.Dropout(drop_prob1),#以指定的丢弃概率随机丢弃上一层输出元素
nn.Linear(num_hiddens1, num_hiddens2),
nn.ReLU(),
nn.Dropout(drop_prob2),#以指定的丢弃概率随机丢弃上一层输出元素
nn.Linear(num_hiddens2, 10)
)
#参数初始化
for param in net.parameters():
nn.init.normal_(param, mean=0, std=0.01)
#定义优化器
optimizer = torch.optim.SGD(net.parameters(), lr=0.5)
#训练
train_ch3(net, train_iter, test_iter, loss, num_epochs,batch_size, None, None, optimizer)
3 合适的学习率
在Cyclical Learning Rates方法中,首先使用较低学习率来训练神经网络,并在每个批次中以指数形式增加学习率,然后找出学习率最高且Loss值仍在下降的拐点确定最佳学习率。在下面的情况中,最佳学习率将为0.01。
![]() |
![]() |
4 测试集数据扩增
4.1 基本内容
测试时增强(test time augmentation, TTA):测试样本原始图像造出多个不同版本(包括不同区域裁剪和更改缩放程度等,比如tta=3),并将它们输入到模型中;然后对多个版本进行多次预测,并平均作为图像的最终输出分数。
这种技术很有效,因为测试样本原始图像显示的区域可能会缺少一些重要特征,在模型中输入图像的多个版本并取平均值,能解决上述问题。
4.2 Pytorch实现
def predict(test_loader, model, tta=3):
model.eval()
test_pred_tta = 0
# TTA 次数
for _ in range(tta):
test_pred = []
with torch.no_grad():
for i, (input, target) in enumerate(test_loader):
c0, c1, c2, c3, c4, c5 = model(data[0])
output = np.concatenate([c0.data.numpy(), c1.data.numpy(),
c2.data.numpy(), c3.data.numpy(),
c4.data.numpy(), c5.data.numpy()], axis=1)
test_pred.append(output)
test_pred = np.vstack(test_pred)
test_pred_tta += test_pred
test_pred_tta /= tta
return test_pred_tta
# 测试集
test_path = glob.glob('../input/test_a/*.png')
test_path.sort()
test_label = [[1]] * len(test_path)
print(len(val_path), len(val_label))
# 测试集Loader TTA
test_loader = torch.utils.data.DataLoader(
SVHNDataset(test_path, test_label,
transforms.Compose([
transforms.Resize((64, 128)),
transforms.RandomCrop((60, 120)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])),
batch_size=40,
shuffle=False,
num_workers=0,
)
test_predict_label = predict(test_loader, model, tta=3)
参考
《动手学深度学习》
称霸Kaggle的十大深度学习技巧
Ten Techniques Learned From fast.ai
Datawhale 零基础入门CV - Basline