全连接神经网络的表达能力与泛化能力

本文通过实验揭示全连接神经网络的强大拟合能力可能导致过拟合,即使在随机噪声下,模型有时会表现出低于实际误差的测试性能。作者通过改变数据分布解释了这一现象,并讨论了其背后的原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在深度学习中,我们经常看到两个概念:表达能力和泛化能力
表达能力指的是模型拟合训练集的能力,可以用训练损失来衡量
而泛化集指的是模型迁移到测试集中的能力,可以用测试误差来衡量

一般来说,全连接的神经网络表达能力很强,很容易过拟合,导致泛化能力较弱

于是乎我就突发奇想,做一个小实验:
首先随便造一个测试集,输入是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,一次小于实际误差的情况都没有,问题解决。

这个小实验简单地,不严谨地说明了全连接神经网络强大的表现能力和容易过拟合的特点。

但现在又有一个问题:既然测试的数据的噪声的产生方式是完全随机的,神经网络是怎么做到误差比实际误差还小的?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值