自动微分模块torch.autograd负责自动计算张量操作的梯度,具有自动求导功能。自动微分模块是构成神经网络训练的必要模块,可以实现网络权重参数的更新,使得反向传播算法的实现变得简单而高效
import torch
1. **张量**
Torch中一切皆为张量,属性requires_grad决定是否对其进行梯度计算。默认是 False,如需计算梯度则设置为True。
2. **计算图**:
torch.autograd通过创建一个动态计算图来跟踪张量的操作,每个张量是计算图中的一个节点,节点之间的操作构成图的边。
3. **反向传播**
使用tensor.backward()方法执行反向传播,从而计算张量的梯度。这个过程会自动计算每个张量对损失函数的梯度。
4. **梯度**
计算得到的梯度通过tensor.grad访问,这些梯度用于优化模型参数,以最小化损失函数。
张量的梯度计算 导数值
使用tensor.backward() 求tensor的导数值 会先求 tensor的导函数 然后带入值 这个tensor 是关于另一个tensor的表达式 原来的tensor是一组数据浮点型 这个tensor是一个函数
创建 原来的tensor时 要设置 requires_grad=True 表示这个tensor要被用于求导 设置了之后 tensor每次参加运算都会被求导一次 吃性能
调用backward 后 原来的tensor 就有个grad属性 这个属性是 导数值
一个 标量映射成另一个标量 对这个映射的结果做反向传播 结果在原来 标量里面
def demo1():
t1= torch.tensor(7.0, requires_grad=True, dtype=torch.float32)
t2= t1**2 + 2 * t1 + 7
t2.backward()
print(t1.grad)
向量的梯度计算
映射的结果必须是一个标量才行 对映射进行一下处理让他为标量 比如求和 或者平均值
def demo2():
torch.manual_seed(666)
t1=torch.rand(20,4, requires_grad=True, dtype=torch.float32)
t2= t1**2 + 2 * t1 + 7
t2=t2.sum()
t2.backward()
print(t1.grad) # 返回的是原向量每一个元素的梯度 是一个跟原来向量size一样的tensor
多标量梯度计算 就是映射式子里面有两个未知数 两个变量tensor 就是求了一个偏导
两个变量就都有了grad
def demo3():
t1= torch.tensor(7.0, requires_grad=True, dtype=torch.float32)
t2= torch.tensor(5.0, requires_grad=True, dtype=torch.float32)
t3= t1**2 + 2 * t1 + 7+t2**2
t3.backward()
print(t1.grad)
print(t2.grad)
多向量也差不多不过要先 变为标量
理解 一个tensor 代表一个W tensor里面代表这个W的取值情况 ,然后当这些W取值的时候 Y应该也有个对应的值组成了一个数据集 有x和y的 这样就可以 算梯度了
y.backward()的时候 相当于把y 表达式的 导函数求出来存在y的一个属性里面 然后再把y表达式中的变量的值传进来计算导数 再返回给 变量的grad属性里面
def demo4():
torch.manual_seed(666)
t1=torch.rand(20,4, requires_grad=True, dtype=torch.float32)
torch.manual_seed(3)
t2=torch.rand(20,4, requires_grad=True, dtype=torch.float32)
t3= t1**2 + 2 * t1 + 7+t2**2
t3=t3.sum()
t3.backward()
print(t1.grad)
print(t2.grad)
梯度的上下文控制
梯度计算的上下文控制和设置对于管理计算图、内存消耗、以及计算效率至关重要。下面我们学习下Torch中与梯度计算相关的一些主要设置方式。
映射函数y的requires_grad默认为ture 不用的话占性能 可以手动关了
def demo4():
x=torch.rand(20,4, requires_grad=True, dtype=torch.float32)
使用with语法 torch.no_grad() 关闭梯度 with torch.no_grad(): 局部作用 再with里面所有的torch都没有requires_grad
with torch.no_grad():
y = x**2 + 2 * x + 3 # 没有梯度的y
print(y.requires_grad) # False
或者装饰器也可以
@torch.no_grad()
def test():
y = x**2 + 2 * x + 3 # 没有梯度的y
return y
全局设置 设置了之后后面代码全部都没有梯度了
torch.set_grad_enabled(False)
累积梯度 多次backward 的时候每次给变量返回的导数值 会累加起来 而不是覆盖 因为不管你怎么变y的表达式 都会给y里面的变量 返回个梯度 让他存起来
def demo5():
torch.manual_seed(666)
t1=torch.rand(20,4, requires_grad=True, dtype=torch.float32)
for x in range(3):
t2= t1**2 + 2 * t1 + 7
t2=t2.sum()
t2.backward()
print(t1.grad)
梯度清空 每次backward 把梯度清空了 tensor.grad.zero_()
def demo6():
torch.manual_seed(666)
t1=torch.rand(20,4, requires_grad=True, dtype=torch.float32)
for x in range(3):
t2= t1**2 + 2 * t1 + 7
t2=t2.sum()
if t1.grad != None:
t1.grad.zero_()
t2.backward()
print(t1.grad)
梯度更新 手动
def demo7():
定义初始W 学习率和 训练轮次
torch.manual_seed(666)
w1=torch.rand(3,3,3,requires_grad=True)
torch.manual_seed(6)
w2=torch.rand(3,3,3,requires_grad=True)
print(w1,w2)
lr=0.1
turn=100
开始训练 假设 均方差公式
for x in range(turn):
y=w1**2+w2**2+2*w1+2*w2+100
清空梯度
if w1.grad!=None:
w1.grad.zero_()
if w2.grad!=None:
w2.grad.zero_()
计算梯度
y=y.mean()
y.backward()
得到当前梯度
梯度更新 使用tensor.data
w1.data=w1.data-lr*w1.grad
w2.data=w1.data-lr*w2.grad
得到训练完后的 w值 可以保存 这个时候w有requires_grad=Ture 保存的时候 去掉用detach 下次使用 load出来
print(w1,w2)
torch.save(w1.detach(),"assets/w/w1.plt")
torch.save(w2.detach(),"assets/w/w2.plt")
注意事项
当requires_grad=True时,在调用numpy转换为ndarray时报错
使用detach()方法创建张量的叶子节点即可 tensor.detach().numpy()
detach() 就是原来 requires_grad=Flase的tensor 和原来的tensor 浅拷贝 改一个两个都变
def test():
torch.manual_seed(666)
w1=torch.rand(3,3,3,requires_grad=True)
print(w1)
w2=w1.detach()
print(w2)
w2[:,:,1]=100
print(w1)
print(w2)
if name=="main":
demo1()
demo2()
demo3()
demo4()
demo5()
demo6()
demo7()
test()