作业题
1.调整线性回归模型停止条件以及y = 2x + (5 + torch.randn(20, 1))中的斜率,训练一个线性回归模型。
我先记录了自己的踩坑记录,最后附上了完整的代码,个人觉得踩坑的过程收获更大,共勉吧。
step5 反向更新梯度,训练模型
w = w - learning_rate * w.grad
b = b - learning_rate * b.grad
这里报错:
原因是我又重新创建了一个变量。
## step 2 构建模型
w = Variable(torch.Tensor([1]),requires_grad = True)
print('original address')
print(id(w))
b = Variable(torch.Tensor([1]),requires_grad = True)
print(id(b))
## step5 反向更新梯度,训练模型
w = w - learning_rate * w.grad
b = b - learning_rate * b.grad
print('New address')
print(id(w))
print(id(b))
我通过打印地址来查看两次操作以后张量的地址:
original address
2613165770072
2613165770648
tensor(4.4057, grad_fn=<MeanBackward0>)
New address
2613165727104
2613165848760
结果看到更新梯度以后的w和b的地址与创建的时候的是不一致的,在执行w = w - learning_rate * w.grad;b = b - learning_rate * b.grad
计算时,w,b都变成了中间节点,此时梯度会释放,因此在反向传播的时候没有梯度可以参与计算了,于是就报错NoneType。
于是继续查找资料.既然地址不能改变,那我就直接in place原位操作好了,遂暴力执行:
## step5 反向更新梯度,训练模型
w -= learning_rate * w.grad
b -= learning_rate * b.grad
哭泣,报错:a leaf Variable that requires grad has been used in an in-place operation.
原因是在定义w,b的时候,w,b是一个叶子节点,什么是叶子节点呢?要先回顾一下pytorch的动态图的计算原理了
在这个计算图中,w,b就是叶子节点,不允许in Place
操作,就是不允许更新以后的值(执行加减法计算后的值)保存在原来的内存地址上;于是,继续修改,想着,再复制一份一样的数据用来计算梯度,并且新的结果不保存再w,b原来的地址不就好了!
于是,查资料,tensor.data能处,有问题它真上!
Tensor.data的解释是:
out = x .data 返回和 x 的相同数据 tensor,而且这个新的tensor out和原来的tensor x是共用数据的,一者改变,另一者也会跟着改变,而且新分离得到的tensor的require s_grad = False, 即不可求导的。(这一点其实detach是一样的)
于是报错的代码改成:
w.data -= learning_rate * w.grad; b.data -= learning_rate * b.grad
**Run了! 奈斯!!**查看一开始创建的w,b以及更新梯度后w,b的存储地址,并检查是否是叶子节点:
original address
The original address of w is: 2613166122544
The original address of b is: 2613166121320
cheak leaf node:
Is w a leaf node? True
Is b a leaf node? True
tensor(4.6758, grad_fn=<MeanBackward0>)
New address
The current address of w is: 2613166122544
The current address of b is: 2613166121320
Check grad function of w : None
Check grad function of b : None
Now, is w a leaf node? True
Now, is b a leaf node? True
现在,附上完整的代码:
## step1 需要生成数据
import torch
from torch.autograd import Variable
x = torch.rand(20,1)*5
print(x.size())
y = 2*x + torch.randn((20,1))
## step 2 构建模型
w = Variable(torch.Tensor([1]),requires_grad = True)
print('original address')
print(f'The original address of w is: {id(w)}')
b = Variable(torch.Tensor([1]),requires_grad = True)
print(f'The original address of b is: {id(b)}')
#print(w.grad_fn)
#print(b.grad_fn)
print('cheak leaf node:')
print(f'Is w a leaf node? {w.is_leaf}')
print(f'Is b a leaf node? {b.is_leaf}')
epochs = 100
for i in range(epochs):
yy = torch.mul(w,x)
y_predict = yy + b
## step3 计算损失函数
loss = (0.5*(y-y_predict)**2).mean()
print(loss)
loss.backward()
## step4 设计优化器
learning_rate = 0.01
## step5 反向更新梯度,训练模型
w.data -= learning_rate * w.grad
b.data -= learning_rate * b.grad
print('New address')
print(f'The current address of w is: {id(w)}')
print(f'The current address of b is: {id(b)}')
print(f'Check grad function of w : {w.grad_fn}')
print(f'Check grad function of b : {b.grad_fn}')
print(f'Now, is w a leaf node? {w.is_leaf}')
print(f'Now, is b a leaf node? {b.is_leaf}')
w.grad.zero_()
b.grad.zero_()
if loss <=0.1:
break
为了查看最后学习的模型长什么样子,我可视化了一下。
import matplotlib.pyplot as plt
final_x = x.numpy()
final_y = y_predict.data.numpy()
plt.scatter(final_x,y.data.numpy())
plt.plot(final_x,final_y,'y-')
plt.show()
总结:
- 𝑤,𝑏均是叶子节点,在创建的时候requires_grad = True 不支持in place操作
- w = w-w.gradlr or b = b- b.gradlr 会破坏上述计算图,使得w,b变成中间节点,w,b的存储地址会较创建它们时候的地址发生改变;中间节点不会保存其梯度,所以无法进行更新;
- 正确更新方式为:
w.data = w.data – w.grad*lr; b.data = b.data – b.grad*lr
或者w.data.sub_(w.grad*lr); b.data.sub_(b.data*lr)
或者w.data -= lr* w.grad; b.data -= lr* b.grad Sub_(下划线表示的是原位操作)
2. 计算图的两个主要概念是什么?
计算图是一个有向的无环图。
两个主要概念是节点和边:节点表示的是变量;边表示的是计算。
3. 动态图与静态图的区别是什么?
动态图是变量与运算操作边执行边创建;
静态图则是先创建好变量,再执行操作。
举个例子:动态图是自驾游,静态图是跟团游。