神经网络学习求和两个数字
Neural network learning to sum two numbers
我正在学习 Pytorch,我正在尝试实现一个非常简单的网络,它采用长度为 2 的输入,即平面中的一个点,旨在学习其组件的总和。
原则上,网络应该只学习权重矩阵 W = [1.,1.] 和零偏差的线性层,因此我希望训练误差非常低。但是,我不明白为什么我没有按预期得到它。
我写的代码是这样的:
import torch
from torch import nn, optim
import numpy as np
device = torch.device("cuda:0" if
torch.cuda.is_available() else "cpu")
N = 1000 # number of samples
D = 2 # input dimension
C = 1 # output dimension
def model(z):
q = z[:,0]
p = z[:,1]
return q+p
X = torch.rand(N, D,requires_grad=True).to(device)
y = model(X)
lr = 1e-2 #Learning rate
Rete = nn.Sequential(nn.Linear(D, C))
Rete.to(device) #Convert to CUDA
criterion = torch.nn.MSELoss()
optimizer = torch.optim.Adam(Rete.parameters(), lr=lr)
for t in range(5000):
y_pred = Rete(X)
loss = criterion(y_pred, y)
print("[EPOCH]: %i, [LOSS]: %.6f" % (t, loss.item()))
optimizer.zero_grad()
optimizer.step()
有2个问题。
第一个问题是你忘了反向传播损失:
optimizer.zero_grad()
loss.backward() # you forgot this step
optimizer.step()
重要的是 optimizer.zero_grad()
不放在 loss.backwards()
和 optimizer.step()
之间,否则您将在执行梯度下降步骤之前重置梯度。通常,建议将 optimizer.zero_grad()
放在循环的最开始,或者在调用 optimizer.step()
:
之后
loss.backward()
optimizer.step()
optimizer.zero_grad() # this should go right after .step(), or at the very beginning of your training loop
即使在此更改之后,您会注意到您的模型仍然没有收敛:
[EPOCH]: 0, [LOSS]: 0.232405
[EPOCH]: 1, [LOSS]: 0.225010
[EPOCH]: 2, [LOSS]: 0.218473
...
[EPOCH]: 4997, [LOSS]: 0.178762
[EPOCH]: 4998, [LOSS]: 0.178762
[EPOCH]: 4999, [LOSS]: 0.178762
这导致我们遇到第二个问题,输出的形状 (y_pred
) 和标签 (y
) 不匹配。 y_pred
的形状为 (N, C)
,但 y
的形状为 (N,)
。要解决此问题,只需重塑 y
以匹配 y_pred
:
y = y.reshape(-1, C)
那么我们的模型会收敛:
[EPOCH]: 0, [LOSS]: 1.732189
[EPOCH]: 1, [LOSS]: 1.680017
[EPOCH]: 2, [LOSS]: 1.628712
...
[EPOCH]: 4997, [LOSS]: 0.000000
[EPOCH]: 4998, [LOSS]: 0.000000
[EPOCH]: 4999, [LOSS]: 0.000000
这两个错误都会悄无声息地失败,这使得调试它们变得困难。不幸的是,在进行机器学习时很容易遇到这类错误。我强烈建议阅读此 blog post 训练神经网络时的最佳实践,以最大限度地减少无声错误的风险。
完整代码:
import torch
import numpy as np
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
N = 1000 # number of samples
D = 2 # input dimension
C = 1 # output dimension
X = torch.rand(N, D).to(device) # (N, D)
y = torch.sum(X, axis=-1).reshape(-1, C) # (N, C)
lr = 1e-2 # Learning rate
model = torch.nn.Sequential(torch.nn.Linear(D, C)) # model
model.to(device)
criterion = torch.nn.MSELoss() # loss function
optimizer = torch.optim.Adam(model.parameters(), lr=lr) # optimizer
for epoch in range(1000):
y_pred = model(X) # forward step
loss = criterion(y_pred, y) # compute loss
loss.backward() # backprop (compute gradients)
optimizer.step() # update weights (gradient descent step)
optimizer.zero_grad() # reset gradients
if epoch % 50 == 0:
print(f"[EPOCH]: {epoch}, [LOSS]: {loss.item():.6f}")
我正在学习 Pytorch,我正在尝试实现一个非常简单的网络,它采用长度为 2 的输入,即平面中的一个点,旨在学习其组件的总和。
原则上,网络应该只学习权重矩阵 W = [1.,1.] 和零偏差的线性层,因此我希望训练误差非常低。但是,我不明白为什么我没有按预期得到它。
我写的代码是这样的:
import torch
from torch import nn, optim
import numpy as np
device = torch.device("cuda:0" if
torch.cuda.is_available() else "cpu")
N = 1000 # number of samples
D = 2 # input dimension
C = 1 # output dimension
def model(z):
q = z[:,0]
p = z[:,1]
return q+p
X = torch.rand(N, D,requires_grad=True).to(device)
y = model(X)
lr = 1e-2 #Learning rate
Rete = nn.Sequential(nn.Linear(D, C))
Rete.to(device) #Convert to CUDA
criterion = torch.nn.MSELoss()
optimizer = torch.optim.Adam(Rete.parameters(), lr=lr)
for t in range(5000):
y_pred = Rete(X)
loss = criterion(y_pred, y)
print("[EPOCH]: %i, [LOSS]: %.6f" % (t, loss.item()))
optimizer.zero_grad()
optimizer.step()
有2个问题。
第一个问题是你忘了反向传播损失:
optimizer.zero_grad()
loss.backward() # you forgot this step
optimizer.step()
重要的是 optimizer.zero_grad()
不放在 loss.backwards()
和 optimizer.step()
之间,否则您将在执行梯度下降步骤之前重置梯度。通常,建议将 optimizer.zero_grad()
放在循环的最开始,或者在调用 optimizer.step()
:
loss.backward()
optimizer.step()
optimizer.zero_grad() # this should go right after .step(), or at the very beginning of your training loop
即使在此更改之后,您会注意到您的模型仍然没有收敛:
[EPOCH]: 0, [LOSS]: 0.232405
[EPOCH]: 1, [LOSS]: 0.225010
[EPOCH]: 2, [LOSS]: 0.218473
...
[EPOCH]: 4997, [LOSS]: 0.178762
[EPOCH]: 4998, [LOSS]: 0.178762
[EPOCH]: 4999, [LOSS]: 0.178762
这导致我们遇到第二个问题,输出的形状 (y_pred
) 和标签 (y
) 不匹配。 y_pred
的形状为 (N, C)
,但 y
的形状为 (N,)
。要解决此问题,只需重塑 y
以匹配 y_pred
:
y = y.reshape(-1, C)
那么我们的模型会收敛:
[EPOCH]: 0, [LOSS]: 1.732189
[EPOCH]: 1, [LOSS]: 1.680017
[EPOCH]: 2, [LOSS]: 1.628712
...
[EPOCH]: 4997, [LOSS]: 0.000000
[EPOCH]: 4998, [LOSS]: 0.000000
[EPOCH]: 4999, [LOSS]: 0.000000
这两个错误都会悄无声息地失败,这使得调试它们变得困难。不幸的是,在进行机器学习时很容易遇到这类错误。我强烈建议阅读此 blog post 训练神经网络时的最佳实践,以最大限度地减少无声错误的风险。
完整代码:
import torch
import numpy as np
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
N = 1000 # number of samples
D = 2 # input dimension
C = 1 # output dimension
X = torch.rand(N, D).to(device) # (N, D)
y = torch.sum(X, axis=-1).reshape(-1, C) # (N, C)
lr = 1e-2 # Learning rate
model = torch.nn.Sequential(torch.nn.Linear(D, C)) # model
model.to(device)
criterion = torch.nn.MSELoss() # loss function
optimizer = torch.optim.Adam(model.parameters(), lr=lr) # optimizer
for epoch in range(1000):
y_pred = model(X) # forward step
loss = criterion(y_pred, y) # compute loss
loss.backward() # backprop (compute gradients)
optimizer.step() # update weights (gradient descent step)
optimizer.zero_grad() # reset gradients
if epoch % 50 == 0:
print(f"[EPOCH]: {epoch}, [LOSS]: {loss.item():.6f}")