# 使用MindSpore实现pytorch中的前反向传播

**1. 系统环境**

硬件环境\(Ascend/GPU/CPU\): Ascend/GPU/CPU

软件环境: MindSpore 版本: 2.2.0

执行模式（静态图/动态图）： 不限

Python 版本: 3.7.6

操作系统平台: linux

**2. 报错信息**

2.1 问题描述

如何使用MindSpore实现pytorch中的前向传播和反向传播方式。

2.2 脚本信息

```
        output = rnn(b_x)  # rnn output
        loss = loss_func(output, b_y)  # cross entropy loss
        optimizer.zero_grad()  # clear gradients for this training step
        loss.backward()  # backpropagation, compute gradients
        optimizer.step()  # apply gradients
```

**3. 根因分析**

mindspore下：

在模型训练中，一个完整的训练过程（step）需要实现以下三步：

正向计算：模型预测结果（logits），并与正确标签（label）求预测损失（loss）。

反向传播：利用自动微分机制，自动求模型参数（parameters）对于loss的梯度（gradients）。

参数优化：将梯度更新到参数上。

MindSpore使用函数式自动微分机制，因此针对上述步骤需要实现：

定义正向计算函数。

使用value\_and\_grad通过函数变换获得梯度计算函数。

定义训练函数，使用set\_train设置为训练模式，执行正向计算、反向传播和参数优化。

**4. 解决方案**

以下用一个简单的网络来对比torch和mindspore 前向传播和反向传播的差别。

```
import torch
import torch.nn as nn
import torch.optim as optim

# 定义一个简单的模型
class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.weight = nn.Parameter(torch.randn(1))
        self.bias = nn.Parameter(torch.randn(2))

    def forward(self, x):
        y = self.weight*x +self.bias
        return y

# 实例化模型
model = Model()

# 随机的输入和目标
inputs = torch.tensor([1.,2.], dtype=torch.float32, requires_grad=True)
targets = torch.tensor([101., 202.])

# 定义损失函数
loss_fn = nn.MSELoss()

# 定义优化器，这里以SGD为例
optimizer = optim.SGD(model.parameters(), lr=0.01)


for i in range(1000):
    print(f"-----------------------loop:{i}---------------------")
    # 前向传播
    outputs = model(inputs)
    loss = loss_fn(outputs, targets)

    print("loss:", loss)
    if loss.detach().numpy() <0.001:
        break
    # 反向传播
    loss.backward()
    print("inputs.grad:", inputs.grad)

    print("inputs before optimizer.step:", model.parameters())
    for i in  model.parameters():
        print(i)
    # 梯度更新
    optimizer.step()
    print("inputs after optimizer.step:", model.parameters())
    for i in  model.parameters():
        print(i)
    optimizer.zero_grad()
```

运行结果：

```
-----------------------loop:0---------------------
loss: tensor(25024.0586, grad_fn=<MseLossBackward0>)
inputs.grad: tensor([-107.0528, -217.2061])
inputs before optimizer.step: <generator object Module.parameters at 0x0000029FE4BF6900>
Parameter containing:
tensor([1.0824], requires_grad=True)
Parameter containing:
tensor([ 1.0168, -0.8306], requires_grad=True)
inputs after optimizer.step: <generator object Module.parameters at 0x0000029FE4BF6900>
Parameter containing:
tensor([6.0848], requires_grad=True)
Parameter containing:
tensor([2.0058, 1.1760], requires_grad=True)
-----------------------loop:1---------------------
loss: tensor(22111.3359, grad_fn=<MseLossBackward0>)
inputs.grad: tensor([ -672.3835, -1365.1215])
inputs before optimizer.step: <generator object Module.parameters at 0x0000029FE4BF6900>
Parameter containing:
tensor([6.0848], requires_grad=True)
Parameter containing:
tensor([2.0058, 1.1760], requires_grad=True)
inputs after optimizer.step: <generator object Module.parameters at 0x0000029FE4BF6900>
Parameter containing:
tensor([10.7869], requires_grad=True)
Parameter containing:
tensor([2.9349, 3.0626], requires_grad=True)
。。。。。。

-----------------------loop:333---------------------
loss: tensor(0.0010, grad_fn=<MseLossBackward0>)
inputs.grad: tensor([ -61202.0000, -142309.9844])
inputs before optimizer.step: <generator object Module.parameters at 0x0000029FE4BF6900>
Parameter containing:
tensor([84.4544], requires_grad=True)
Parameter containing:
tensor([16.5859, 33.0709], requires_grad=True)
inputs after optimizer.step: <generator object Module.parameters at 0x0000029FE4BF6900>
Parameter containing:
tensor([84.4544], requires_grad=True)
Parameter containing:
tensor([16.5855, 33.0712], requires_grad=True)
-----------------------loop:334---------------------
loss: tensor(0.0010, grad_fn=<MseLossBackward0>)
```

具体差别查看可以看代码，都有相关注释。

网络定义：在网络定义中，一般会定义出需要的前向网络，损失函数和优化器。在Net\(\)中定义前向网络，PyTorch的网络继承nn.Module；类似地，MindSpore的网络继承nn.Cell。在MindSpore中，损失函数和优化器除了使用MindSpore中提供的外，用户还可以使用自定义的优化器。可参考模型模块自定义。可以使用functional/nn等接口拼接需要的前向网络、损失函数和优化器，详细接口用法可参照接口对比。

正向计算：运行实例化后的网络，可以得到logit，将logit和target作为输入计算loss。需要注意的是，如果正向计算的函数有多个输出，在反向计算时需要注意多个输出对于计算结果的影响。

反向计算：得到loss后，我们可以进行反向计算。在PyTorch中可使用loss.backward\(\)计算梯度，在MindSpore中，先用mindspore.grad\(\)定义出反向传播方程net\_backward，再将输入传入net\_backward中，即可计算梯度。如果正向计算的函数有多个输出，在反向计算时，可将has\_aux设置为True，即可保证只有第一个输出参与求导，其它输出值将直接返回。对于反向计算中接口用法区别详见自动微分对比。

梯度更新：将计算后的梯度更新到网络的Parameters中。在PyTorch中使用optim.step\(\)；在MindSpore中，将Parameter的梯度传入定义好的optim中，即可完成梯度更新。

