关于pytorch损失函数的梯度计算

今天想实现一个idea:对同一批数据,采用两次计算。第一次就用正常的交叉熵,第二次针对第一次的情况,对gt作出改变,再做一遍交叉熵。

要想写同一批数据训练两次,还要引入控制变量,来控制哪一次是正常计算,哪一次是改变计算,太麻烦了。
于是想到,我直接在算loss的时候,就算两遍,把两遍加起来,是不是就是等效的?

最终去复习了pytorch的梯度机制,确定是差不多的。当然训练两次的话,第一次正常的结束后,模型就已经优化了,第二次改变的训练,用的是优化后的模型;而在loss里计算两次,用的都是优化前的模型。这个是唯一的不同,但是影响不大。

以上是流水账,仅自用,不用看。


比如我们有一个函数 y = x^2,要求y对于x的梯度。

首先,x初始化时,要指定requires_grad = True。
如:

x = torch.tensor([1., 2., 3., 4.], requires_grad=True)

指定和不指定,打印出来的信息有区别:
关于pytorch损失函数的梯度计算_第1张图片
输出:
在这里插入图片描述

指明requires_grad为True的意义是,现在tensor x就维护一个名为grad的变量,存放日后要用的梯度。


接下来定义y,然后使用y.backward()。调用这个函数后,就把y对于x的梯度,存到x.grad里。
关于pytorch损失函数的梯度计算_第2张图片
输出:
在这里插入图片描述

为什么这里y要调用.sum()把所有元素加起来?
本来y = x * x的话,y就是一个1x4的tensor。但是这个时候直接调用 y.backward(),会报错。因为只有纯量(scalar)能直接调用这个函数,如果是1x4的tensor要调用,需要指定一个1x4的tensor作为输入,才能调用。
至于详细的原理,掺杂到数学推导,这里就不赘述,因为反正loss都是纯量,我们早就不手写梯度回传了。

也就是y = x1^2 + x2^2 + x3^2 + x4^2,对x1、x2、x3、x4的梯度分别都是2 * x1、2 * x2、2 * x3、2 * x4,也就是对应的这些。


如果计算两次呢,梯度会自动累加在x.grad里。
关于pytorch损失函数的梯度计算_第3张图片
输出:
在这里插入图片描述

所以,我们需要在每次循环结束后,调用x.grad.zero_()来清空梯度(小知识:所有带_的函数,意思是直接在原数据上进行改动)。
关于pytorch损失函数的梯度计算_第4张图片
输出:
在这里插入图片描述


以上就是最原始的梯度计算方式。现代的pytorch早已把一切封装好,我们常见的下列代码,做的就是以上的事情:
关于pytorch损失函数的梯度计算_第5张图片

weights要指定requires_grad为True,因为它就是要求梯度的对象——我们需要求loss对于weights的梯度,以更好地更新weights。所以这里weights相当于上述的x。

求出loss后,在计算梯度前,先将之前的梯度清零:optimizer.zero_grad()。这就相当于上面的x.grad.zero_()

然后loss.backward(),weights里现在有了grad。

optimizer.step()就是根据weights的grad,更新了weights。

你可能感兴趣的:(机器学习,pytorch,深度学习,人工智能)