欠拟合 过拟合
训练误差 泛化误差
训练误差: 在训练数据集上表现出来的误差
泛化误差:在测试数据样本上表现出来的误差的期望
使用损失函数计算误差 , 关注降低泛化误差
模型选择
- 验证数据集
- K折交叉验证
欠拟合: 模型无法达到一个较低的训练误差
增加模型复杂度
过拟合:训练误差低,泛化误差高
增加数据集
权重衰减
L2范数正则化:模型权重参数平方和与一个正参数的乘积,通过惩罚绝对值较大的模型参数来增加限制,可能对过拟合有效.
def l2_penalty(w):
return (w**2).sum() / 2
l = loss(net(X, w, b), y) + lambd * l2_penalty(w)
l = l.sum()
#使用 pytorch
optimizer_w = torch.optim.SGD(params=[net.weight], lr=lr, weight_decay=wd) # 对权重参数衰减
optimizer_b = torch.optim.SGD(params=[net.bias], lr=lr) # 不对偏差参数衰减
dropout
以p概率随机丢弃某些元素 降低过拟合.训练时使用, 测试时不使用
梯度消失 梯度爆炸
激活函数的选择
在深层网络中尽量避免选择sigmoid和tanh激活函数,原因是这两个激活函数会把元素转换到[0, 1]和[-1, 1]之间,会加剧梯度消失的现象!
协变量偏移 标签偏移 概念偏移
协变量偏移: 一个在冬季部署的物品推荐系统在夏季的物品推荐列表中出现了圣诞礼物,可以理解为在夏季的物品推荐系统与冬季相比,时间或者说季节发生了变化,导致了夏季推荐圣诞礼物的不合理的现象,这个现象是由于协变量时间发生了变化造成的。
标签偏移: 标签偏移可以简单理解为测试时出现了训练时没有的标签
概念偏移: 可以根据其缓慢变化的特点缓解
如果数据量足够的情况下,确保训练数据集和测试集中的数据取自同一个数据集,可以防止协变量偏移和标签偏移是正确的。如果数据量很少,少到测试集中存在训练集中未包含的标签,就会发生标签偏移。
神经网络
使用torch.nn 包来构建神经网络.
一个神经网络的典型训练过程如下:
- 定义包含一些可学习参数(或者叫权重)的神经网络
- 在输入数据集上迭代
- 通过网络处理输入
- 计算损失(输出和正确答案的距离)
- 将梯度反向传播给网络的参数
- 更新网络的权重,一般使用一个简单的规则:weight = weight - learning_rate * gradient
定义网络
import torch
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
self.conv1 = nn.Conv2d(1, 6, 5) # 输入通道 1 输出通道 6 5*5 卷积核
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 5 *5 ,120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = F.max_pool2d(F.relu(self.conv1(x)),(2,2)) # 2 * 2 最大池化
x = F.max_pool2d(F.relu(self.conv2(x)),2) # 方正,可以只使用一个数字
x = x.view(-1,self.num_flat_features(x)) #flatten
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self, x):
size = x.size()[1:] # 除去批处理的其他所有维度
num_features = 1
for s in size:
num_features *= s
return num_features
net = Net()
print(net)
我们只需要定义 forward 函数,backward函数会在使用autograd时自动定义,backward函数用来计算导数。
# 可学习参数通过net.parameters()返回
params = list(net.parameters())
print(len(params))
print(params[0].size())
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
#清零所有参数的梯度缓存,然后进行随机梯度的反向传播:
net. zero_grad()
out.backward(torch.randn(1, 10))
损失函数
一个损失函数接受一对(output, target)作为输入,计算一个值来估计网络的输出和目标值相差多少。
output = net(input)
target = torch.randn(10)
print(target.shape)
target = target.view(1, -1)
print(target.shape)
criterion = nn.MSELoss() # 均方差
loss = criterion(output, target)
print(loss)
反向传播
我们只需要调用loss.backward()来反向传播权重。我们需要清零现有的梯度,否则梯度将会与已有的梯度累加。
net.zero_grad()
print('before backward')
print(net.conv1.bias.grad)
loss.backward()
print('after backward')
print(net.conv1.bias.grad)
更新权重
SGD 随机梯度下降法
weight = weight - learning_rate * gradient
learning_rate = 0.01
for i in net.parameters():
f.data.sub_(f.grad.data * learning_rate)
torch.optim 封装了不同的更新规则
import torch.optim as optim
optimizer = optim.SGD(net.parameters(),lr = 0.01)
optimizer.zero_grad()
output = net(input)
loss = criterion(out, target)
loss.backward()
optimizer.step() #更新