【问题标题】:Parameters of my network on PyTorch are not updated我在 PyTorch 上的网络参数未更新
【发布时间】:2020-09-08 14:31:35
【问题描述】:

我想使用 PyTorch 制作一个自动校准系统。

我尝试将齐次变换矩阵作为神经网络的权重来处理。

我参考PyTorch教程写了一段代码,但是我的自定义参数在调用backward方法后没有更新。

当我打印每个参数的 'grad' 属性时,它是 None

我的代码如下。有什么问题吗?

请给我任何建议。谢谢。

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

import numpy as np

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.params = nn.Parameter(torch.rand(6))
        self.rx, self.ry, self.rz = self.params[0], self.params[1], self.params[2]
        self.tx, self.ty, self.tz = self.params[3], self.params[4], self.params[5]
        

    def forward(self, x):
        tr_mat = torch.tensor([[1, 0, 0, self.params[3]],
                                [0, 1, 0, self.params[4]],
                                [0, 0, 1, self.params[5]],
                                [0, 0, 0, 1]], requires_grad=True)

        rz_mat = torch.tensor([[torch.cos(self.params[2]), -torch.sin(self.params[2]), 0, 0],
                                [torch.sin(self.params[2]), torch.cos(self.params[2]), 0, 0],
                                [0, 0, 1, 0],
                                [0, 0, 0, 1]], requires_grad=True)

        ry_mat = torch.tensor([[torch.cos(self.params[1]), 0, torch.sin(self.params[1]), 0],
                                [0, 1, 0, 0],
                                [-torch.sin(self.params[1]), 0, torch.cos(self.params[1]), 0],
                                [0, 0, 0, 1]], requires_grad=True)

        rx_mat = torch.tensor([[1, 0, 0, 0],
                                [0, torch.cos(self.params[0]), -torch.sin(self.params[0]), 0],
                                [0, torch.sin(self.params[0]), torch.cos(self.params[0]), 0],
                                [0, 0, 0, 1]], requires_grad=True)

        tf1 = torch.matmul(tr_mat, rz_mat)
        tf2 = torch.matmul(tf1, ry_mat)
        tf3 = torch.matmul(tf2, rx_mat)

        tr_local = torch.tensor([[1, 0, 0, x[0]],
                                [0, 1, 0, x[1]],
                                [0, 0, 1, x[2]],
                                [0, 0, 0, 1]])
        tf_output = torch.matmul(tf3, tr_local)
        output = tf_output[:3, 3]
        return output

    def get_loss(self, output):
        pass



model = Net()

input_ex = np.array([[-0.01, 0.05, 0.92],
                    [-0.06, 0.03, 0.94]])

output_ex = np.array([[-0.3, 0.4, 0.09],
                        [-0.5, 0.2, 0.07]])
print(list(model.parameters()))

optimizer = optim.Adam(model.parameters(), 0.001)
criterion = nn.MSELoss()

for input_np, label_np in zip(input_ex, output_ex):
    input_tensor = torch.from_numpy(input_np).float()
    label_tensor = torch.from_numpy(label_np).float()
    output = model(input_tensor)

    optimizer.zero_grad()
    loss = criterion(output, label_tensor)
    loss.backward()
    optimizer.step()

print(list(model.parameters()))

【问题讨论】:

    标签: pytorch


    【解决方案1】:

    会发生什么

    您的问题与 PyTorch 将 torch.tensor 隐式转换为 float 有关。假设你有这个:

    tr_mat = torch.tensor(
        [
            [1, 0, 0, self.params[3]],
            [0, 1, 0, self.params[4]],
            [0, 0, 1, self.params[5]],
            [0, 0, 0, 1],
        ],
        requires_grad=True,
    )
    

    torch.tensor 只能从具有类似 Python 值的列表构造,其中不能有 torch.tensor。幕后发生的事情(假设)是self.params 的每个元素,可以转换为float 是(在这种情况下,它们都可以,例如self.params[3]self.params[4]self.params[5])。

    tensor 的值被转换为float 时,它的值被复制到 Python 对应项中,因此 它不再是计算图的一部分,它是一个新的纯 Python 变量(显然不能反向传播)。

    解决方案

    您可以做的是选择self.params 的元素并将它们插入到眼睛矩阵中,以便渐变流动。考虑到这一点,您可以看到对 forward 方法的重写:

    class Net(nn.Module):
        def __init__(self):
            super(Net, self).__init__()
            self.params = nn.Parameter(torch.randn(6))
    
        def forward(self, x):
            sinus = torch.cos(self.params)
            cosinus = torch.cos(self.params)
    
            tr_mat = torch.eye(4)
            tr_mat[:-1, -1] = self.params[3:]
    
            rz_mat = torch.eye(4)
            rz_mat[0, 0] = cosinus[2]
            rz_mat[0, 1] = -sinus[2]
            rz_mat[1, 0] = sinus[2]
            rz_mat[1, 1] = cosinus[2]
    
            ry_mat = torch.eye(4)
            ry_mat[0, 0] = cosinus[1]
            ry_mat[0, 2] = sinus[1]
            ry_mat[2, 0] = -sinus[1]
            ry_mat[2, 2] = cosinus[1]
    
            rx_mat = torch.eye(4)
            rx_mat[1, 1] = cosinus[0]
            rx_mat[1, 2] = -sinus[0]
            rx_mat[2, 1] = sinus[0]
            rx_mat[2, 2] = cosinus[0]
    
            tf1 = torch.matmul(tr_mat, rz_mat)
            tf2 = torch.matmul(tf1, ry_mat)
            tf3 = torch.matmul(tf2, rx_mat)
    
            tr_local = torch.tensor(
                [[1, 0, 0, x[0]], [0, 1, 0, x[1]], [0, 0, 1, x[2]], [0, 0, 0, 1]],
            )
            tf_output = torch.matmul(tf3, tr_local)
            output = tf_output[:3, 3]
            return output
    

    (您可能想仔细检查此重写,但想法成立)。 另请注意 tr_local 可以“按您的方式”完成,因为我们不需要任何值来保持渐变。

    requires_grad

    您可以看到requires_grad 没有在代码中的任何地方使用。这是因为需要梯度的不是整个眼睛矩阵(我们不会优化01),而是插入其中的参数。通常你的神经网络代码中不需要requires_grad根本,因为:

    • 输入张量未优化(通常,可能是在您进行对抗性攻击等时)
    • nn.Parameter 默认需要渐变(除非冻结)
    • 层和其他神经网络特定的东西默认需要梯度(除非冻结)
    • 不需要梯度的值(输入张量)可以通过需要梯度的层(或参数或 w/e)进行反向传播

    【讨论】:

    • 非常感谢您的善意和出色的解释。我按照您的说明编辑了我的代码,所以现在我的代码可以正常运行了。多亏了你,我才能快速解决这个问题!
    • @gus8cjf 如果问题解决,请采纳答案,谢谢。
    猜你喜欢
    • 2018-09-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-01
    • 2019-12-02
    • 2020-12-17
    • 2022-01-04
    • 2022-01-08
    • 1970-01-01
    相关资源
    最近更新 更多