在深度学习中,我们经常看到两个概念:表达能力和泛化能力
表达能力指的是模型拟合训练集的能力,可以用训练损失来衡量
而泛化集指的是模型迁移到测试集中的能力,可以用测试误差来衡量
一般来说,全连接的神经网络表达能力很强,很容易过拟合,导致泛化能力较弱
于是乎我就突发奇想,做一个小实验:
首先随便造一个测试集,输入是x,输出(标签)y是x^2
然后需要引入噪声,我的方法十分简单粗暴:直接将标签y随机按比例增加或减少10%以内,变为y*(0.9~1.1)
于是乎得到了数据集构造函数:
def generate(num): # num为数据集的大小
x = np.random.randint(-10,10, num)
y = x**2 * (1 + (random.random() - 0.5) / 5)
return x,y
然后就是简单的使用全连接神经网络进行训练,为了过拟合,特意多跑了一些轮次
class DSnet(nn.Module):
def __init__(self):
super(DSnet, self).__init__()
self.layer1 = nn.Linear(1, 20)
self.layer2 = nn.Linear(20, 10)
self.layer3 = nn.Linear(10, 1)
self.relu = nn.ReLU()
def forward(self, x):
x = self.layer1(x)
x = torch.tanh(x)
x = self.layer2(x)
x = torch.tanh(x)
x = self.layer3(x)
return x
batchs = 100000
batch_size = 1000
global_step = 0
total_loss = 100000
model = DSnet()
viz = visdom.Visdom(port=8097, server="127.0.0.1", env="Test")
loss_win = viz.line(np.arange(1))
optimizer = torch.optim.SGD(model.parameters(), lr=0.0001, momentum=0.5)
model.train()
for i in range(batchs):
data, lable = generate(batch_size)
data = torch.Tensor(data).unsqueeze(1)
lable = torch.Tensor(lable)
optimizer.zero_grad()
output = model(data)
criterion = nn.MSELoss()
loss = criterion(output.squeeze(dim=1), lable)
loss.backward(torch.ones_like(loss))
optimizer.step()
truth = data**2
loss2 = criterion(truth.squeeze(dim=1), lable)
if i % 100 == 0:
global_step += 1
viz.line(Y=np.array([loss.item()]),
X=np.array([global_step]),
name='训练损失',
update='append',
win=loss_win)
viz.line(Y=np.array([loss2.item()]),
X=np.array([global_step]),
name='实际损失',
update='append',
win=loss_win)
训练的loss大致如下:
其中橙色的是训练损失,损失函数就是简单的均方差MSE
绿的是实际损失,计算的是x^2和y之间的均方差
在训练的最后,训练误差时不时会小于实际的误差了
然后,按常理来说,在训练集上跑的时候,由于过拟合,测试误差会大于实际误差才对,但实际操作中,经常发现测试误差小于实际误差的情况,于是我干脆跑了100000次,看测试误差大于实际误差的情况有多少次:
def eval():
model.eval()
data, lable = generate(100)
data = torch.Tensor(data).unsqueeze(1)
lable = torch.Tensor(lable)
out = model(data)
truth = data ** 2
criterion = nn.MSELoss()
loss_model = criterion(out.squeeze(1), lable)
loss_truth = criterion(truth.squeeze(1), lable)
return loss_model < loss_truth
cnt = 0
for i in range(100000):
cnt += 1 if eval() else 0
cnt
然后不可思议的情况出现了:
差不多有一半的时候,测试误差还小于实际误差?不是在开玩笑吧?
后来转念一想,训练集和测试集的x都是在(-10,10)之间的随机数,他们的分布是完全一致的,那么测试误差(这个时候和训练误差是等价的)更小实际上也是过拟合现象的一种体现,也就说得过去了
然后我就改变了一下测试集的分布
x = np.random.randint(-15,15, num)
然后在进行测试,结果就比较正常了:
干干净净的0,一次小于实际误差的情况都没有,问题解决。
这个小实验简单地,不严谨地说明了全连接神经网络强大的表现能力和容易过拟合的特点。
但现在又有一个问题:既然测试的数据的噪声的产生方式是完全随机的,神经网络是怎么做到误差比实际误差还小的?