文章目录
一、过拟合与欠拟合
-
线性模型
-
非线性模型
-
拟合过程存在噪声
-
多次项的拟合能力
注:随着阶数越来越高,学习(拟合)数据的能力越来越强
- 在神经网络模型中反映为更深的神经网络,即:前向传播过程可以表示为高阶的多项式
1.1 欠拟合
① 表现为预测模型不能很好的拟合已知数据
② 数值上反映为:训练集、测试集的准确率均比较低
1.2 过拟合
① 表现为过度的拟合训练集,即:预测模型包含了训练集噪声
② 数值上反映为:训练集的准确率均比较高;测试集的准确率比较低
1.3 总结拟合的三种情况
注:现实中,由于计算机计算能力逐步提高,神经网络越来越复杂,更多时候是过拟合
二、交叉验证 — 判定模型的拟合程度
-
之前的test数据集充当了验证集的功能,用以判断拟合程度
-
early stopping:可以选取训练集与测试(验证)集均表现“好”时的参数作为checkpoint,用于制止checkpoint之后会出现过拟合的情况
-
标准的工程中,包括:训练集、验证集(用于判定拟合程度)、测试集(实际中用户的使用情况)
-
pytorch实现中,将原始的训练集拆分为:训练集 + 验证集两部分
torch.utils.data.random_split(dataset,[sub_dataset1_num,sub_dataset2_num])
# -*- coding: UTF-8 -*-
'''
@version: 1.0
@PackageName: pytorch_learning - high_level_multi_classificial_task.py
@author: yonghao
@Description: 使用pytorch集成的方法快速搭建深度学习网络
@since 2021/02/22 17:57
'''
import torch, torchvision
from torch import nn
from visdom import Visdom
# 创建一个visdom对象
vis = Visdom()
# torch.cuda.set_device(0)
# 设置visdom可视化迭代次数
global_step = 0
# 每批量个数
batch_size = 200
train_db = torchvision.datasets.MNIST('mnist_data', train=True, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(
(0.1307,), (0.3081,))
]))
train_db, val_db = torch.utils.data.random_split(train_db, [50000, 10000])
test_db = torchvision.datasets.MNIST('mnist_data/', train=False, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(
(0.1307,), (0.3081,))
]))
train_loader = torch.utils.data.DataLoader(
train_db, batch_size=batch_size, shuffle=True)
val_loader = torch.utils.data.DataLoader(
val_db, batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(
test_db, batch_size=batch_size, shuffle=False)
# 构建网络
class MLB(nn.Module):
def __init__(self):
super(MLB, self).__init__()
self.model = nn.Sequential(
nn.Linear(28 * 28, 200),
nn.ReLU(inplace=True),
nn.Linear(200, 200),
nn.ReLU(inplace=True),
nn.Linear(200, 10)
# nn.ReLU(inplace=True)
)
def forward(self, x):
x = self.model(x)
return x
# 训练
device = torch.device('cuda:0')
criteon = nn.CrossEntropyLoss().to(device)
net = MLB().to(device)
def train(learning_rate=1e-3, epochs=7):
optimizer = torch.optim.SGD(net.parameters(), lr=learning_rate)
for epoch in range(epochs):
for batch_idx, (data, label) in enumerate(train_loader):
data = data.view(-1, 28 * 28)
data, label = data.to(device), label.cuda()
out = net(data)
loss = criteon(out, label)
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 将train_loss 加载至visdom
global global_step
vis.line([loss.item()], [global_step], win='train_loss', update='append')
global_step += 1
if batch_idx % 100 == 0:
print("Train Epoch: {} [ {} / {} ({:.0f}%)] \tLoss:{:.6f}".format(epoch, batch_idx * len(data),
len(train_loader.dataset),
100. * batch_idx / len(train_loader),
loss.item()))
# test()
# 验证
def validata():
val_loss = 0
correct = 0
for data, label in val_loader:
data = data.view(-1, 28 * 28).to(device)
label = label.cuda()
out = net(data)
pred = out.argmax(dim=1)
correct += pred.eq(label).sum()
val_loss += criteon(out, label).item()
val_loss /= len(val_loader.dataset)
print("\nVal set:Average loss:{:.4f},Accuracy:{}/{} ({:.0f}%)\n".format(val_loss, correct,
len(val_loader.dataset),
100. * correct / len(val_loader.dataset)))
# 测试
def test():
test_loss = 0
correct = 0
for data, label in test_loader:
data = data.view(-1, 28 * 28).to(device)
label = label.cuda()
out = net(data)
pred = out.argmax(dim=1)
correct += pred.eq(label).sum()
test_loss += criteon(out, label).item()
test_loss /= len(test_loader.dataset)
print("\nTest set:Average loss:{:.4f},Accuracy:{}/{} ({:.0f}%)\n".format(test_loss, correct,
len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
if __name__ == "__main__":
vis.line([0.], [0.], win='train_loss', opts=dict(title='train loss'))
train()
validata()
test()
# print(torch.cuda.is_available()) # 查看cuda是否可用
#
# print(torch.cuda.device_count()) # 返回GPU数目
#
# print(torch.cuda.get_device_name(0)) # 返回GPU名称,设备索引默认从0开始
#
# print(torch.cuda.current_device()) # 返回当前设备索引
三、降低过拟合
- 方式一:更多的数据集
- 方式二:降低模型复杂度
① 减少隐藏层,使模型变浅
② 正则化 - 方式三:随机失活Droput
- 方式四:数据增强
- 方式五:提前停止(根据validation set与train set间accuration 或 loss对比,提前终止train过程)
3.1 正则化
损失函数添加参数范数项
-
对损失函数求最小值时,会使各参数(w,b等)尽可能趋向于0,此时可以削减前向传播高次项的影响,从而降低了模型的复杂程度
-
举例展示正则化
-
L1,L2正则化
-
pytorch实现正则化
只需要对梯度下降算法定制对象 optim.SGD(weight_decay) 中weight_decay设置值,其作为二范数前的超参数 λ \lambda λ 值
四、动量与学习率衰减
4.1 动量
-
动量即为惯性量,使当前参数更新值与 ① 当前参数值在loss函数的梯度;②之前一次的参数梯度下降值 均有关
-
pytorch实现
仅需要设置 optim.SGD(momentum) 中momentum参数值(动量超参数 β \beta β=0.9)
4.2 学习率衰减
-
过小、适中、过大
-
工程上设置学习率:随着迭代次数越来越小,即衰减的学习率
- pytorch实现
① 方案一:设置梯度下降中,自动检测loss值处于平缓区时,下降学习率
② 方案二:设置梯度下降中,每固定迭代设定值次数后,衰减学习率
五、Early stopping,dropout等
5.1 Early stopping
找寻学习过程中,训练集与验证集(图中为test set)均处于准确度上升时对应最大准确率时的各个参数值。(或对应loss均下降时最小值时的各个参数)
5.2 Dropout随机失活
-
pytorch实现Dropout
nn.Dropout(drop_rate):对线性层实现dropout
-
torch.nn.Dropout() 与 tf.nn.dropout()的区别
-
在做验证、测试时应使用:.eval() 手动关闭训练时的dropout
-
使用mini-batch做训练集分割,每次梯度下降计算仅针对于mini-batch大小的数据,减小显存的使用