...
 
Commits (3)
    https://gitcode.net/Oneflow-Inc/oneflow-documentation/-/commit/c68443d27e792f0b7c27344ee4f2fe1770e1a9a2 autograd 100%(excpet imgs), optimizer 0% 2021-08-15T12:40:52+08:00 YaoChi later@usopp.net https://gitcode.net/Oneflow-Inc/oneflow-documentation/-/commit/0af00f67703c07abe67f4153065a35c03df83b8e refine optimization 100% 2021-08-15T17:23:32+08:00 YaoChi later@usopp.net https://gitcode.net/Oneflow-Inc/oneflow-documentation/-/commit/2618ad66e0fcf121319b54e709c5c32e2fa868d6 rm useless py script 2021-08-15T17:28:46+08:00 YaoChi later@usopp.net
# 自动求 # 自动求梯度
神经网络的训练过程离不开 **反向传播算法**,在反向传播过程中,需要获取 loss 函数对模型参数的梯度,用于更新参数。 神经网络的训练过程离不开 **反向传播算法**,在反向传播过程中,需要获取 loss 函数对模型参数的梯度,用于更新参数。
...@@ -34,7 +34,9 @@ l = loss(z,y) ...@@ -34,7 +34,9 @@ l = loss(z,y)
反向传播过程中,需要求得 `l``w``b` 的梯度,以更新这两个模型参数。因此,我们在创建它们时,设置 `requires_grad``True` 反向传播过程中,需要求得 `l``w``b` 的梯度,以更新这两个模型参数。因此,我们在创建它们时,设置 `requires_grad``True`
## 自动求导 ## 自动求梯度
### backward 与梯度
在反向传播的过程中,需要得到 `l` 分别对 `w``b` 的梯度 $\frac{\partial l}{\partial w}$ 和 $\frac{\partial l}{\partial b}$。我们只需要对 `l` 调用 `backward()` 方法,然后 OneFlow 就会自动计算梯度,并且存放到 `w``b``grad` 成员中。 在反向传播的过程中,需要得到 `l` 分别对 `w``b` 的梯度 $\frac{\partial l}{\partial w}$ 和 $\frac{\partial l}{\partial b}$。我们只需要对 `l` 调用 `backward()` 方法,然后 OneFlow 就会自动计算梯度,并且存放到 `w``b``grad` 成员中。
...@@ -53,88 +55,235 @@ tensor([[0.9397, 2.5428, 2.5377], ...@@ -53,88 +55,235 @@ tensor([[0.9397, 2.5428, 2.5377],
tensor([[0.9397, 2.5428, 2.5377]], dtype=oneflow.float32) tensor([[0.9397, 2.5428, 2.5377]], dtype=oneflow.float32)
``` ```
### 停止对某个 Tensor 求梯度 ### 对非叶子节点求梯度
默认情况下,只有 `requires_grad=True` 的叶子节点的梯度会被保留。非叶子节点的 `grad` 属性默认在 `backward` 执行过程中,会自动释放,不能查看。
### 对一个计算图多次 `backward()` 如果想保留并查看非叶子节点的梯度,可以调用 `Tensor.retain_grad` 方法:
### 对非叶子节点求梯度 ```python
from math import pi
n1 = flow.tensor(pi/2, requires_grad=True)
n2 = flow.sin(n1)
n2.retain_grad()
n3 = flow.pow(n2, 2)
n3.backward()
print(n1.grad)
print(n2.grad)
```
以上代码,既求 $\frac{\partial n_3}{\partial n_1}$,也求 $\frac{\partial n_3}{\partial n_2}$
输出:
## 输出为 Tensor 时如何求导 ```
- scalar implicity only tensor(-8.7423e-08, dtype=oneflow.float32)
tensor(2., dtype=oneflow.float32)
```
### 对一个计算图多次 `backward()`
默认情况下,对于给定的计算图,只能调用 `backward()` 一次。比如,以下代码会报错:
```python
n1 = flow.tensor(10., requires_grad=True)
n2 = flow.pow(n1, 2)
n2.backward()
n2.backward()
```
报错信息:
### 为什么默认保留叶子节点的导数 > Maybe you try to backward through the node a second time. Specify retain_graph=True when calling .backward() or autograd.grad() the first time.
**2.1 快速上手** 中,我们的训练循环结尾会有一步 `.zero_grad()` 的过程。这是因为,所有叶子节点的导数会被保留。每次在我们进行 `.backward()` 操作后,梯度需要被保留以用于更新参数 (也就是后文中的`optimizer.step()`) 如果想要在同一个计算图上调用多次 `backward()`,需要在调用时设置 `retain_graph=True`
## `grad` 成员及相关操作 ```python
n1 = flow.tensor(10., requires_grad=True)
n2 = flow.pow(n1, 2)
### detach n2.backward(retain_graph=True)
print(n1.grad)
n2.backward()
print(n1.grad)
```
detach 可以让 oneflow 停止对 `requires_grad=True` 的元素进行求导跟踪。例如: 输出:
```text
tensor(20., dtype=oneflow.float32)
tensor(40., dtype=oneflow.float32)
```
以上输出可知,OneFlow 会 **累加** 多次 `backward()` 计算得到的梯度。
如果想清空梯度,可以调用 `zeros_` 方法:
```python ```python
# 建立一个简单的张量,并做简单的变换 n1 = flow.tensor(10., requires_grad=True)
x = flow.tensor([1.0,2.0,3.0], requires_grad = True) n2 = flow.pow(n1, 2)
print(x)
y = x*x n2.backward(retain_graph=True)
z = x**3 print(n1.grad)
a = y+z n1.grad.zeros_()
# 用 MSE 来计算 x 与 a 的差距 n2.backward()
loss = flow.nn.MSELoss() print(n1.grad)
out = loss(x, a)
print(out)
# 反向传播,计算导数
out.backward()
print(x.grad)
``` ```
其输出为 输出:
```shell ```text
tensor([1., 2., 3.], dtype=oneflow.float32, requires_grad=True) tensor(20., dtype=oneflow.float32)
tensor(396.6667, dtype=oneflow.float32, grad_fn=<scalar_mul_backward>) tensor(20., dtype=oneflow.float32)
tensor([ 2.6667, 100. , 704. ], dtype=oneflow.float32)
``` ```
但若我们将 `z = x**3` 替换成 `z = x.detach()**3`, 输出会变为: ### 停止对某个 Tensor 求梯度
默认情况下,OneFlow 会 tracing `requires_grad``True` 的 Tensor,自动求梯度。
不过有些情况可能并不需要 OneFlow 这样做,比如只是想试一试前向推理。那么可以使用 [oneflow.no_grad](https://oneflow.readthedocs.io/en/master/oneflow.html#oneflow.no_grad)[oneflow.Tensor.detach](https://oneflow.readthedocs.io/en/master/tensor.html#oneflow.Tensor.detach) 方法设置。
```shell ```python
tensor([1., 2., 3.], dtype=oneflow.float32, requires_grad=True) z = flow.matmul(x, w)+b
tensor(396.6667, dtype=oneflow.float32, grad_fn=<scalar_mul_backward>) print(z.requires_grad)
tensor([ 0.6667, 20. , 110. ], dtype=oneflow.float32)
with flow.no_grad():
z = flow.matmul(x, w)+b
print(z.requires_grad)
```
输出:
```text
True
False
``` ```
原因很简单,加入 detach 操作后,oneflow 就不会在反向过程中对 z 所参与的节点求导,从而导致 `x.grad` 值的变化。 ```python
z_det = z.detach()
print(z_det.requires_grad)
```
在实际运用中,假设有两个模型,第一个模型的输出为第二个模型的输入。若你只想训练第二个模型,那么只需在第一个模型后面加入 detach 操作就可以达到目的。 输出:
### retain_graph ```text
False
```
上面讲到,非叶子节点的梯度会在更新完被释放。但如果我们想查看被释放的梯度呢?只需要在 autograd 函数中加上 `requires_grad=True` 即可 ## 输出不是标量时如何求导
通常,调用 `backward()` 方法的 Tensor 是神经网络的 loss,是一个标量。
比如: 但是,如果调用是张量,直接 `backward()` 时会报错。
```python ```python
import oneflow as flow x = flow.randn(1, 2, requires_grad=True)
y = 3*x + 1
y.backward()
```
x = flow.tensor(2., requires_grad=True) 报错信息:
y = flow.tensor(3., requires_grad=True)
z = x*y
x_grad = flow.autograd.grad(z,x,retain_graph=True) > Check failed: IsScalarTensor(*outputs.at(i)) Grad can be implicitly created only for scalar outputs
y_grad = flow.autograd.grad(z,y)
print(x_grad[0],y_grad[0]) 而对 `y``sum` 后可以求梯度:
```python
x = flow.randn(1, 2, requires_grad=True)
y = 3*x + 1
y = y.sum()
y.backward()
print(x.grad)
``` ```
输出: 输出:
```shell ```text
tensor(3., dtype=oneflow.float32) tensor(2., dtype=oneflow.float32) tensor([[3., 3.]], dtype=oneflow.float32)
``` ```
若没有加入 `requires_grad=True` , oneflow 会默认报错,因为梯度已被释放。 错误原因及解决方法的分析如下: `x` 张量中有两个元素,记作 $x_1$ 与 $x_2$,`y` 张量中的两个元素记作 $y_1$ 与 $y_2$,那么两者的关系是:
$$
\mathbf{x} = [x_1, x_2]
$$
$$
\mathbf{y} = [y_1, y_2] = [3x_1+1, 3x_2+1]
$$
此时,想直接求 $\frac{\partial \mathbf{y}}{\partial \mathbf{x}}$
$$
\frac{\partial \mathbf{y}}{\partial \mathbf{x}} =
\frac{[3x_1+1, 3x_2+1]}{[x_1, x_2]}
$$
在数学上是没有意义的,因此当然就报错了。
实际上,当用户调用 `y.backward()` 时,其实想要的结果通常是:
$$
[\frac{\partial y_1}{\partial x_1}, \frac{\partial y_2}{\partial x_2}]
$$
当对 `y` 进行 `sum` 运算后:
$$
y = y_1 + y_2 = 3x_1 + 3x_2 + 2
$$
此时,调用 `backward()` 时,对 $x_1$ 和 $x_2$ 可求梯度:
$$
\frac{\partial y}{\partial x_1} = \frac{\partial 3x_1 + 3x_2 + 2}{\partial x_1} = 3
$$
$$
\frac{\partial y}{\partial x_2} = \frac{\partial 3x_1 + 3x_2 + 2}{\partial x_2} = 3
$$
### 扩展阅读:VJP
还可以使用更通用方法,即 **Vector Jacobian Product(VJP)** 完成非标量的根的梯度计算。依然用上文的例子,在反向传播过程中,OneFlow 会根据计算图生成雅可比矩阵:
$$
J = \begin{pmatrix}
\frac{\partial y_1}{\partial x_1} & \frac{\partial y_1}{\partial x_2}\\
\frac{\partial y_2}{\partial x_1} & \frac{\partial y_2}{\partial x_2}
\end{pmatrix}\\
= \begin{pmatrix}
\frac{\partial y_1}{\partial x_1} & 0 \\
0 & \frac{\partial y_2}{\partial x_2}
\end{pmatrix}
$$
只需提供一个与 $\mathbf{y}$ 大小一致的向量 $\mathbf{v}$,即可计算 VJP:
$$
\begin{bmatrix}
v_1\\
v_2
\end{bmatrix}
\times
\begin{pmatrix}
\frac{\partial y_1}{\partial x_1} & 0 \\
0 & \frac{\partial y_2}{\partial x_2}
\end{pmatrix}=
\begin{bmatrix}
v_1 \frac{\partial y_1}{\partial x_1}\\
v_2 \frac{\partial y_2}{\partial x_2}
\end{bmatrix}
$$
若向量 $\mathbf{v}$ 是反向传播中上一层的梯度,VJP 的结果刚好是当前层要求的梯度。
`backward` 方法是可以接受一个张量做参数的,该参数就是 VJP 中的 $\mathbf{v}$,理解以上道理后,还可以使用以下的方式对张量求梯度:
```python
x = flow.randn(1, 2, requires_grad=True)
y = 3*x + 1
y.backward(flow.ones_like(y))
print(x.grad)
```
输出:
```text
tensor([[3., 3.]], dtype=oneflow.float32)
```
\ No newline at end of file
# 反向传播 # 反向传播与 optimizer
再上一章节中,我们讲到了求导对于模型训练的重要性。然而,在求导后,我们还需要让机器根据求出的导数来更新梯度。而这一过程,就被称之为反向传播 到目前为止,我们已经掌握如何使用 OneFlow [搭建模型](todo_build_model.md)[加载数据](todo_dataset_dataloader.md)[自动计算模型参数的梯度](./05_autograd.md),将它们组合在一起,我们就可以利用反向传播算法训练模型
## 利用自动求导手工实现反向传播 [oneflow.optim](https://oneflow.readthedocs.io/en/master/optim.html) 中,有各类 `optimizer`,它们可以简化实现反向传播的代码。
为了更方便理解自动求导的作用,我们在这里提供了一份用 numpy 纯手写的简单模型: 本文将先介绍反向传播的基本概念,再介绍如何使用 `oneflow.optimz` 类。
## numpy 手工实现反向传播
为了读者更方便理解反向传播与自动求导的关系,在这里提供了一份仅用 numpy 实现的简单模型的训练过程:
```python ```python
import numpy as np import numpy as np
...@@ -16,16 +20,16 @@ LR = 0.01 ...@@ -16,16 +20,16 @@ LR = 0.01
def forward(x, w): def forward(x, w):
return np.matmul(x, w) return np.matmul(x, w)
# 损失函数 (return MSE 的导数) # 损失函数
def loss(y_pred, y): def loss(y_pred, y):
return (0.5*(y_pred-y)**2).sum() return (0.5*(y_pred-y)**2).sum()
# 计算导数 # 计算梯度
def gradient(x, y, y_pred): def gradient(x, y, y_pred):
return np.matmul(x.T, (y_pred-y)) return np.matmul(x.T, (y_pred-y))
if __name__ == "__main__": if __name__ == "__main__":
# 训练目: Y = 2*X1 + 3*X2 # 训练目: Y = 2*X1 + 3*X2
x = np.array([[1, 2], [2, 3], [4, 6], [3, 1]], dtype=np.float32) x = np.array([[1, 2], [2, 3], [4, 6], [3, 1]], dtype=np.float32)
y = np.array([[8], [13], [26], [9]], dtype=np.float32) y = np.array([[8], [13], [26], [9]], dtype=np.float32)
...@@ -44,7 +48,7 @@ if __name__ == "__main__": ...@@ -44,7 +48,7 @@ if __name__ == "__main__":
输出: 输出:
```shell ```text
50/500 loss:0.0012162785263114685 50/500 loss:0.0012162785263114685
100/500 loss:3.11160142374838e-05 100/500 loss:3.11160142374838e-05
150/500 loss:7.960399867959713e-07 150/500 loss:7.960399867959713e-07
...@@ -55,96 +59,111 @@ if __name__ == "__main__": ...@@ -55,96 +59,111 @@ if __name__ == "__main__":
400/500 loss:8.723474589862032e-15 400/500 loss:8.723474589862032e-15
450/500 loss:2.231723694177745e-16 450/500 loss:2.231723694177745e-16
500/500 loss:5.7094113647001346e-18 500/500 loss:5.7094113647001346e-18
w:[[2.] w:[[2.00000001]
[3.]] [2.99999999]]
``` ```
可以看到,以上代码的主要目的为训练模型去寻找公式中2,3两个参数 (`Y = 2*X1 + 3*X2`)。具体训练过程及步骤在 **2.1 快速上手** 中已有详细介绍。 注意我们选择的 loss 函数表达式为 $\sum \frac{1}{2}(y_{p} - y)^2$,因此 `loss` 对参数 `w`求梯度的代码为:
**2.1 快速上手** 的反向传播时,我们与用了一个简单的 `.backward` 就解决了更新导数的问题。但为了更好的诠释 `.backward` 的过程,我们这里手写了反向传播。`loss() `的返回结果`(0.5*(y_pred-y)**2).sum()` 是我们为了方便自己定义的损失函数,且其导数显而易见: 就是 `y_pred-y`。也就是为什么我们在计算导数 (gradient) 时:
```python ```python
def gradient(x, y, y_pred): def gradient(x, y, y_pred):
return np.matmul(x.T, (y_pred-y)) return np.matmul(x.T, (y_pred-y))
``` ```
是将 x 的 transpose 与 `(y_pred-y)` 做矩阵乘法。 更新参数采用的是 [SGD](https://en.wikipedia.org/wiki/Stochastic_gradient_descent)
而更新权重就比较简单了:
```python ```python
grad = gradient(x, y, y_pred) grad = gradient(x, y, y_pred)
w -= LR*grad w -= LR*grad
``` ```
可以看到,所谓更新权重,就是简单的导数乘以学习率。 总结而言,训练中的一次完整迭代包括以下步骤:
1. 模型根据输入、参数,计算得出预测值 (`y_pred`)
2. 计算 loss,即预测值与标签之间的误差
3. 求 loss 对参数的梯度
4. 更新参数
简单来说,一次完整的反向传播就是: 计算损失函数与叶子结点 (这里 x 为叶子结点) 的导数 -> 通过导数乘以学习率来更新参数 其中 1~2 为正向传播过程;3~4为反向传播过程。
当然,更复杂的模型并不只有这简单的两步,但万变不离其宗。 ## 超参 Hyperparameters
## 利用 `flow.optim` 中已有的类进行反向传播 超参数是有关模型训练设置的参数,可以影响到模型训练的效率和结果。如以上代码中的 `ITER_COUNT``LR` 就是超参数。
上面手写的模型似乎很麻烦。我们不但要对其导数公式,还需要手写更新过程。在训练稍稍复杂一点的模型的话,工作量会大大提高 (激活函数等等都需要手写)。下面是我们用 oneflow 写出的训练 `Y = 2*X1 + 3*X2` 的模型。 ## 使用 `oneflow.optim` 中的优化器类
使用 `oneflow.optim` 中的优化器类进行反向传播会更简洁方便,接下来,我们展示如何使用。
首先,先准备好数据和模型,使用 Module 的一个方便之处就是,可以把超参放置在 Module 中便于管理。
```python ```python
import oneflow as flow import oneflow as flow
x = flow.tensor([[1, 2], [2, 3], [4, 6], [3, 1]], dtype=flow.float32)
y = flow.tensor([[8], [13], [26], [9]], dtype=flow.float32)
class MyLrModule(flow.nn.Module): class MyLrModule(flow.nn.Module):
def __init__(self, lr, iter_count): def __init__(self, lr, iter_count):
super().__init__() super().__init__()
self.w = flow.nn.Parameter(flow.tensor([[1], [1]],dtype=flow.float32)) self.w = flow.nn.Parameter(flow.randn(2, 1, dtype=flow.float32))
self.lr = lr self.lr = lr
self.iter_count = iter_count self.iter_count = iter_count
def forward(self, x): def forward(self, x):
return flow.matmul(x, self.w) return flow.matmul(x, self.w)
if __name__ == "__main__":
# train data: Y = 2*X1 + 3*X2
x = flow.tensor([[1, 2], [2, 3], [4, 6], [3, 1]], dtype=flow.float32)
y = flow.tensor([[8], [13], [26], [9]], dtype=flow.float32)
model = MyLrModule(0.01, 500) model = MyLrModule(0.01, 500)
loss = flow.nn.MSELoss(reduction='sum') ```
optimizer = flow.optim.SGD(model.parameters(), model.lr)
for i in range(0, model.iter_count): ### loss 函数
y_pred = model(x)
l = loss(y_pred, y)
if (i+1) % 50 == 0: print(f"{i+1}/{model.iter_count} loss:{l}")
l.backward() 然后,选择好 loss 函数,OneFlow 自带了多种 loss 函数,我们在这里选择 [MSELoss](https://oneflow.readthedocs.io/en/master/nn.html?highlight=mseloss#oneflow.nn.MSELoss)
optimizer.step()
optimizer.zero_grad()
print(f"w: {model.w}") ```python
loss = flow.nn.MSELoss(reduction='sum')
``` ```
### 构造 optimizer
上文总结的训练中一次迭代里,反向传播的逻辑,都被封装在 optimizer 中。我们在此选择 [SGD](https://oneflow.readthedocs.io/en/master/optim.html?highlight=sgd#oneflow.optim.SGD) 优化器,你可以根据需要选择其它的优化器,如 [Adam](https://oneflow.readthedocs.io/en/master/optim.html?highlight=adam#oneflow.optim.Adam)[AdamW](https://oneflow.readthedocs.io/en/master/optim.html?highlight=adamw#oneflow.optim.AdamW) 等。
```shell ```python
50/500 loss:tensor(0.0004, dtype=oneflow.float32, grad_fn=<reduce_sum_backward>) optimizer = flow.optim.SGD(model.parameters(), model.lr)
100/500 loss:tensor(2.2268e-07, dtype=oneflow.float32, grad_fn=<reduce_sum_backward>)
150/500 loss:tensor(1.3461e-10, dtype=oneflow.float32, grad_fn=<reduce_sum_backward>)
200/500 loss:tensor(3.8654e-12, dtype=oneflow.float32, grad_fn=<reduce_sum_backward>)
250/500 loss:tensor(3.8654e-12, dtype=oneflow.float32, grad_fn=<reduce_sum_backward>)
300/500 loss:tensor(3.8654e-12, dtype=oneflow.float32, grad_fn=<reduce_sum_backward>)
350/500 loss:tensor(3.8654e-12, dtype=oneflow.float32, grad_fn=<reduce_sum_backward>)
400/500 loss:tensor(3.8654e-12, dtype=oneflow.float32, grad_fn=<reduce_sum_backward>)
450/500 loss:tensor(3.8654e-12, dtype=oneflow.float32, grad_fn=<reduce_sum_backward>)
500/500 loss:tensor(3.8654e-12, dtype=oneflow.float32, grad_fn=<reduce_sum_backward>)
w: tensor([[2.],
[3.]], dtype=oneflow.float32, grad_fn=<accumulate_grad>)
``` ```
可以看到,我们不需要再手写损失函数以及其导数。oneflow.nn 中含有大量的损失函数供用户使用。其次,在实现反向传播时,用户只需在训练循环最后加入: 构造时 `optimizer`,将模型参数及 learning rate 传递给 `SGD`,在之后若调用 `optimizer.step()`,在其内部就会自动完成对模型参数求梯度、并按照 SGD 算法更新模型参数。
### 训练
以上准备完成后,可以开始训练:
```python ```python
l.backward() for i in range(0, model.iter_count):
optimizer.step() # 更新权重 y_pred = model(x)
optimizer.zero_grad() # 清除导数 l = loss(y_pred, y)
``` if (i + 1) % 50 == 0:
print(f"{i+1}/{model.iter_count} loss:{l}")
即可。 optimizer.zero_grad()
l.backward()
optimizer.step()
最后清除导数的原因也很简单(详见 **自动求导** - **为什么保留叶子结点导数** )。`backward()` 过后会保留导数以进行权重更新。但这个导数在每次迭代中会重新计算 (因为权重的更新会导致损失函数值的变化,每变化一次就要对新的损失函数值进行求导,与上一次迭代中的导数没有叠加关系),顾需要清除导数以方便下一次迭代的运算。 print(f"\nw: {model.w}")
```
输出:
```text
50/500 loss:0.0015626397216692567
100/500 loss:8.896231520338915e-07
150/500 loss:5.038600647822022e-10
200/500 loss:9.094947017729282e-13
250/500 loss:9.094947017729282e-13
300/500 loss:9.094947017729282e-13
350/500 loss:9.094947017729282e-13
400/500 loss:9.094947017729282e-13
450/500 loss:9.094947017729282e-13
500/500 loss:9.094947017729282e-13
w: tensor([[2.0000],
[3.0000]], dtype=oneflow.float32, grad_fn=<accumulate_grad>)
```
import oneflow as flow
import numpy as np
# 建立一个简单的张量,并做简单的变换
x = flow.tensor([1.0,2.0,3.0], requires_grad = True)
print(x)
y = x*x
z = x**3
a = y+z
# 用 MSE 来计算 x 与 y 的差距
loss = flow.nn.MSELoss()
out = loss(x, a)
print(out)
# 反向传播,计算导数
out.backward()
print(x.grad)
import numpy as np
ITER_COUNT = 500
LR = 0.01
def forward(x, w):
return np.matmul(x, w)
def loss(y_pred, y):
return (0.5*(y_pred-y)**2).sum()
def gradient(x, y, y_pred):
return np.matmul(x.T, (y_pred-y))
if __name__ == "__main__":
# train data: Y = 2*X1 + 3*X2
x = np.array([[1, 2], [2, 3], [4, 6], [3, 1]], dtype=np.float32)
y = np.array([[8], [13], [26], [9]], dtype=np.float32)
w = np.random.rand(2, 1)
for i in range(0, ITER_COUNT):
y_pred = forward(x, w)
l = loss(y_pred, y)
if (i+1) % 50 == 0: print(f"{i+1}/{500} loss:{l}")
grad = gradient(x, y, y_pred)
w -= LR*grad
print(f"w:{w}")
import oneflow as flow
class MyLrModule(flow.nn.Module):
def __init__(self, lr, iter_count):
super().__init__()
self.w = flow.nn.Parameter(flow.tensor([[1], [1]],dtype=flow.float32))
self.lr = lr
self.iter_count = iter_count
def forward(self, x):
return flow.matmul(x, self.w)
if __name__ == "__main__":
# train data: Y = 2*X1 + 3*X2
x = flow.tensor([[1, 2], [2, 3], [4, 6], [3, 1]], dtype=flow.float32)
y = flow.tensor([[8], [13], [26], [9]], dtype=flow.float32)
model = MyLrModule(0.01, 500)
loss = flow.nn.MSELoss(reduction='sum')
optimizer = flow.optim.SGD(model.parameters(), model.lr)
for i in range(0, model.iter_count):
y_pred = model(x)
l = loss(y_pred, y)
if (i+1) % 50 == 0: print(f"{i+1}/{model.iter_count} loss:{l}")
l.backward()
optimizer.step()
optimizer.zero_grad()
print(f"w: {model.w}")
import oneflow as flow
x = flow.tensor(2., requires_grad=True)
y = flow.tensor(3., requires_grad=True)
z = x*y
x_grad = flow.autograd.grad(z,x,retain_graph=True)
y_grad = flow.autograd.grad(z,y)
print(x_grad[0],y_grad[0])