我正在尝试实现一个近似逻辑 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/