为什么写本博客
前人种树,后人乘凉。希望自己的学习笔记可以帮助到需要的人。
本博客定位
帮助那些了解原理(指深度学习原理知道),想要实现自己想法的朋友。
目录结构
自动求导是pytorch的核心功能,也是我们实现我们的神经网络算法的核心。其主要应用是用来处理反向传播算法。
下面,我们通过一个例子来演示:
构建 y = x + b,然后对y求导
那么,实现方法如下:
import torch
b = torch.randn((3,4),requires_grad=True) # requires_grad = True 必须声明
x = torch.randn((3,4),requires_grad=True)
# 定义一个函数
t = x + b
y = t.sum() # 这一步是因为只有标量才可以求导(pytorch所规定)
# 求导
y.backward()
# b的梯度
print(b.grad)
# x的梯度
print(x.grad)
运行结果如下:
tensor([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
tensor([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
# 正确结果,因为为线性函数嘛
不难从上面看出,想要自动求导需要的步骤为:
# 1. 定义数据和函数
xxxx = torch.tensor(xxx,requires_grad=True)
fx = xxxx
# 2. 对目标求导:fx要为标量
fx.backward()
# 3. 计算想要的参数的梯度
print(xx.grad)
想要探究一下:是否所有变量都要声明requires_grad=True?
所有变量都声明
这个就是上面的例子,显然是可以正常运行的。
只有一个变量声明
代码一:
import torch
b = torch.randn((3,4),requires_grad=True)
x = torch.randn((3,4))
# 定义一个函数
t = x + b
y = t.sum()
# 求导
y.backward()
# b的梯度
print(b.grad)
# x的梯度
print(x.grad)
结果为:
tensor([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
None
代码二:
import torch
b = torch.randn((3,4))
x = torch.randn((3,4),requires_grad=True)
# 定义一个函数
t = x + b
y = t.sum()
# 求导
y.backward()
# b的梯度
print(b.grad)
# x的梯度
print(x.grad)
结果为:
None
tensor([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
一个也不声明
显然,这样结果肯定报错。
总结
通过上面的测试,可以看出:如果你想求解梯度的变量是最基层(底层,不由其它变量构成)的变量,那么你必须为它声明requires_grad=True,但是如果你对它不感兴趣,可以不用设置。
另外一个问题:是否所有变量都声明了requires_grad=True,函数才可以调用backward方法?
这个答案,其实上面的案例已经告诉我们了——不是。那么,是不是声明一个变量,那么由这个变量构成的函数就可以使用backward方法呢?
看代码:
import torch
b = torch.randn((3,4))
x = torch.randn((3,4),requires_grad=True)
# 定义一个函数
t1 = x + b # 由x构成的函数
y1 = t1.sum()
t2 = b*3 # 不由x构成的函数
y2 = t2.sum()
# 查看是否可以
print(y1.requires_grad) # 返回变量requires_grad是否为True
print(y2.requires_grad)
结果为:
True
False
综上,得出结论:只要有一个变量声明了requires_grad=True,那么由它构成的函数requires_grad也为True,可以正常使用backward方法。
有时候,我们的一些量不需要参与梯度计算。此时,就需要这个方法:
import torch
x = torch.randn((3,4),requires_grad=True)
with torch.no_grad(): # 这样y不受x的requires_grad=True影响
y = x ** 2
print(y.requires_grad) #false
梯度计算默认会累加,但是一般情况下我们并不需要累加:
import torch
x = torch.tensor([10,20,30,40],requires_grad=True,dtype=torch.float32)
for i in range(5):
y = x**2 +10
# 转为标量
y1 = y.mean()
# 自动求导
y1.backward()
print(x.grad)
运行结果:
tensor([ 5., 10., 15., 20.])
tensor([10., 20., 30., 40.])
tensor([15., 30., 45., 60.])
tensor([20., 40., 60., 80.])
tensor([ 25., 50., 75., 100.])
因此,需要进行清除累加值:
import torch
x = torch.tensor([10,20,30,40],requires_grad=True,dtype=torch.float32)
for i in range(5):
y = x**2 +10
# 转为标量
y1 = y.mean()
# 清楚累加
if x.grad is not None:
x.grad.data.zero_()
# 自动求导
y1.backward()
print(x.grad)
1. 只有浮点型和复数型可以进行自动梯度求导
2. 自动求导默认累加,需要自己清空
3. 只有标量可以自动求导,因此需要手动转换,比如上面中t.sum()
4. 当一个张量设置了requires_grad=True后,不可以直接转为numpy对象,需要先使用detach方法生成一个新张量,然后进行转换。
关于第四点这里举一个例子:
import torch
x = torch.randn((3,4),requires_grad=True)
c = x.numpy()
上面代码直接运行会报错,需要修改为:
import torch
x = torch.randn((3,4),requires_grad=True)
new_x = x.detach()
c = new_x.numpy()
print(type(c)) #