前言
学习地址
方向导数:研究函数在某一点处沿某个方向的变化率
梯度:本质是一个向量,它的方向是函数在某一点处方向导数取得最大值的方向,也就是某一点处变化率最快的方向,它的向量模就意味着最大值。
python中的autograd:运行时动态地追踪你的计算过程,也可以轻松的计算梯度,我们通过研究损失函数的梯度来不断的修正深度学习方向
使用
简单示例
用requires_grad开启计算图跟踪,用backward()对一个函数来求梯度(也就是对各个自变量的偏导),按照数学上的定义,用grad来获得的只是某个自变量的偏导值,但是在pytorch中,很多时候直接称grad拿到的就是梯度。
# 创建一个0-2pi上均匀分布的含有25个元素的张量a=torch.linspace(0.0,2.0*math.pi,steps=25,requires_grad=True)print(a)b=torch.sin(a)# 输出中可以看到b是怎么来的print(b)# matplotlib 要求输入为 NumPy 数组,所以需要在绘图前进行分离plt.plot(a.detach(),b.detach())c=2*b+1out=c.sum()out.backward()# 只能获取叶子节点(也就是自变量)的梯度,输入端a可以,b、c都不行print(a.grad)plt.plot(a.detach(),a.grad.detach())plt.show()训练模型中的autograd
通过一个实际的训练模型来了解到autograd的意义
BATCH_SIZE=16# 批次大小:一次处理16个样本DIM_IN=1000# 输入维度:每个样本有1000个特征HIDDEN_SIZE=100# 隐藏层大小:中间层有100个神经元DIM_OUT=10# 输出维度:每个样本输出10个值classTinyModel(torch.nn.Module):def__init__(self):# 初始化层结构,三层神经网络super(TinyModel,self).__init__()self.layer1=torch.nn.Linear(DIM_IN,HIDDEN_SIZE)self.relu=torch.nn.ReLU()self.layer2=torch.nn.Linear(HIDDEN_SIZE,DIM_OUT)# 对基类方法的重写,torch.nn.Module 在底层实现了 __call__ 方法,并会在其中调用forwarddefforward(self,x):# 前向传播,描述了数据进入模型后的计算顺序x=self.layer1(x)x=self.relu(x)x=self.layer2(x)returnxdeft2():some_input=torch.randn(BATCH_SIZE,DIM_IN,requires_grad=False)ideal_output=torch.randn(BATCH_SIZE,DIM_OUT,requires_grad=False)model=TinyModel()# 查看权重print("------------weight1-----------------")print(model.layer2.weight[0][0:10])print(model.layer2.weight.grad)# 把模型中的layer1和layer2的权重传进来作为可优化的参数,训练后计算均方误差optimizer=torch.optim.SGD(model.parameters(),lr=0.001)prediction=model(some_input)loss=(ideal_output-prediction).pow(2).sum()print(loss)# 普通张量的requires_grad默认是false,但是模型参数比如权值的是true,所以最后的结果也是true# 为权值计算梯度,但是权值相较上面还没有发生变化,因为optimizer优化器还没有启动loss.backward()print("--------------weight2---------------")print(model.layer2.weight[0][0:10])print(model.layer2.weight.grad[0][0:10])optimizer.step()print("--------------weight3---------------")print(model.layer2.weight[0][0:10])print(model.layer2.weight.grad[0][0:10])# 梯度清零,否则每次调用 loss.backward() 时,算出的梯度不会覆盖参数上已有的.grad# 而是累加到 .grad 属性中。optimizer.zero_grad(set_to_none=False)print(model.layer2.weight.grad[0][0:10])在这段代码中关于梯度有几个值得注意的点:
1.普通张量的梯度开关默认是False,而训练模型中的参数,如权重张量的开关默认是True,所以最后的结果是可以求梯度的
2.模型的改进在于权重,比如一个线性模型y=Ax+b,研究的就是A、b哪一个更合适,而输入是给定的,所以求权重的梯度并改进权重才是训练模型应该做的
3.PyTorch 的 loss.backward() 默认要求调用者必须是一个标量
4.多次训练会存在梯度累加的问题,而我们的模型往往需要循环训练多次,因此每轮训练后要做梯度清零的操作
# 标准的训练循环模式forepochinrange(num_epochs):# 1. 必须先清空梯度!optimizer.zero_grad()# 2. 前向传播prediction=model(some_input)# 3. 计算损失loss=(ideal_output-prediction).pow(2).sum()# 4. 反向传播(计算当前 Batch 的梯度)loss.backward()# 5. 更新参数optimizer.step()梯度的原地操作
在使用autograd时,必须谨慎使用原地操作,因为这样做可能会破坏在调用backward()时计算导数所需的信息,进而报错,比如:
a=torch.linspace(0.,2.*math.pi,steps=25,requires_grad=True)a.sin_()