pytorch - 为什么大于 1 的小批量不起作用,但更大的累积梯度却起作用?

标签 pytorch neural-network array-broadcasting

我正在尝试实现一个近似逻辑 XOR 函数的神经网络,但是,该网络仅在使用批量大小为 1 时收敛。

我不明白为什么:当我对多个大小为 1 的小批量使用梯度累积时,收敛非常平滑,但大小为 2 或更大的小批量根本不起作用。

无论学习率如何,都会出现这个问题,而且我对另一个比 XOR 问题(更复杂)也有同样的问题。

我加入我的代码供引用:

import numpy as np
import torch.nn as nn
import torch
import torch.optim as optim
import copy

#very simple network
class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc = nn.Linear(2,3,True)
        self.fc1 = nn.Linear(3,1, True)

    def forward(self, x):
        x = torch.sigmoid(self.fc(x))
        x = self.fc1(x)
        return x

def data(n): # return n sets of random XOR inputs and output
    inputs = np.random.randint(0,2,2*n)
    inputs = np.reshape(inputs,(-1,2))
    outputs = np.logical_xor(inputs[:,0], inputs[:,1])
    return torch.tensor(inputs, dtype = torch.float32),torch.tensor(outputs, dtype = torch.float32)


N = 4
net = Net() # first network, is updated with minibatches of size N
net1 = copy.deepcopy(net) # second network, updated with N minibatches of size 1
inputs = torch.tensor([[0,0],[0,1],[1,0],[1,1]], dtype = torch.float32)
labels = torch.tensor([0,1,1,0], dtype = torch.float32)
optimizer = optim.SGD(net.parameters(), lr=0.01)
optimizer1 = optim.SGD(net1.parameters(), lr=0.01)
running_loss = 0
running_loss1 = 0
for epoch in range(25000):  # loop over the dataset multiple times
    # get the inputs; data is a list of [inputs, labels]
    input, labels = data(N)

    # zero the parameter gradients
    optimizer.zero_grad()
    optimizer1.zero_grad()
    # forward + backward + optimize
    loss1_total = 0
    for i in range(N):
        outputs1 = net1(input[i])
        loss1 = (outputs1-labels[i]).pow(2)/N # I divide by N to get the effective mean
        loss1.backward()
        loss1_total += loss1.item()


    outputs = net(input)
    loss = (outputs-labels).pow(2).mean()
    loss.backward()
    
    # optimization
    optimizer.step()
    optimizer1.step()

    # print statistics
    running_loss += loss.item()
    running_loss1 += loss1_total
    if epoch % 1000 == 999:    # print every 1000 mini-batches
        print(f'[{epoch + 1},  loss: {running_loss/1000 :.3f}, loss1: {running_loss1/1000 :.3f}')
        running_loss1 = 0.0
        running_loss = 0.0
        
print('Finished Training')
 # exemples of data and outputs for reference ; network 2 always converge to the sub-optimal point(0.5,0.5)
datatest = data(4)
outputs = net(datatest[0])
outputs1 = net1(datatest[0])
inputs = datatest[0]
labels = datatest[1]
print("input",inputs)
print("target",labels)
print("net output",outputs)
print("net output",outputs1)

[编辑]提高了可读性并更新了代码

结果:

[1000,  loss: 0.259, loss1: 0.258
[2000,  loss: 0.252, loss1: 0.251
[3000,  loss: 0.251, loss1: 0.250
[4000,  loss: 0.252, loss1: 0.250
[5000,  loss: 0.251, loss1: 0.249
[6000,  loss: 0.251, loss1: 0.247
[7000,  loss: 0.252, loss1: 0.246
[8000,  loss: 0.251, loss1: 0.244
[9000,  loss: 0.252, loss1: 0.241
[10000,  loss: 0.251, loss1: 0.236
[11000,  loss: 0.252, loss1: 0.230
[12000,  loss: 0.252, loss1: 0.221
[13000,  loss: 0.250, loss1: 0.208
[14000,  loss: 0.251, loss1: 0.193
[15000,  loss: 0.251, loss1: 0.175
[16000,  loss: 0.251, loss1: 0.152
[17000,  loss: 0.252, loss1: 0.127
[18000,  loss: 0.251, loss1: 0.099
[19000,  loss: 0.251, loss1: 0.071
[20000,  loss: 0.251, loss1: 0.048
[21000,  loss: 0.251, loss1: 0.029
[22000,  loss: 0.251, loss1: 0.016
[23000,  loss: 0.250, loss1: 0.008
[24000,  loss: 0.251, loss1: 0.004
[25000,  loss: 0.251, loss1: 0.002

Finished Training

input tensor([[1., 0.],
        [0., 0.],
        [0., 0.],
        [0., 0.]])
target tensor([1., 0., 0., 0.])
net output tensor([[0.4686],
        [0.4472],
        [0.4472],
        [0.4472]], grad_fn=<AddmmBackward0>)
net1 output tensor([[0.9665],
        [0.0193],
        [0.0193],
        [0.0193]], grad_fn=<AddmmBackward0>)

请您解释一下为什么会出现这种奇怪的现象?在网上找了很久,没有成功……

如果我的问题格式不好,请原谅,这是我第一次提出有关堆栈溢出的问题。

编辑: 我发现,比较大小为 1 的小批量的累积梯度和大小为 N 的小批量的梯度,计算出的梯度大部分是相同的,只有很小的(但明显的)差异可能是由于近似误差而出现的,所以我的实现乍一看看起来不错。我仍然不明白大小为 1 的小批量的这种强收敛特性从何而来。

最佳答案

问题在于你定义 labels 的方式/计算损失

 loss = (outputs-labels).pow(2).mean()

我们有labels.shape = [4]但是outputs.shape =[4, 1] 。这是由于广播造成的差异

(outputs - labels).shape = [4, 4]

这意味着我们计算输出和标签之间的所有成对差异(然后取它们的二次方并求平均值),这基本上意味着没有发生有意义的监督。

解决此问题的快速方法是在此处添加虚拟尺寸:

loss = (outputs-labels[:, None]).pow(2).mean()

但是干净的方法是从一开始就以正确的方式进行操作,即以 labels.shape = [_, 1] 的方式定义标签。 :

labels = torch.tensor([[0], [1], [1], [0]], dtype=torch.float32)

(与您的 data() 函数类似)。

关于pytorch - 为什么大于 1 的小批量不起作用,但更大的累积梯度却起作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72331452/

相关文章:

python - Pytorch 中的批量学习是如何进行的?

python - 将类对象添加到 Pytorch Dataloader : batch must contain tensors

Python list reversed 不能被多次访问

用列表索引的 Python 数组,但数组维度被置换

python - 操作系统错误: [WinError 127] The specified procedure could not be found

pytorch - 谷歌 Colab : "Unable to connect to the runtime" after uploading Pytorch model from local

python - tensorflow 中的恢复模型和预测

artificial-intelligence - 这个分类结果可以接受吗?

R 神经网络包对于数百万条记录来说太慢

python - 将每一行乘以不同的旋转矩阵