一.梯度
在机器学习的时候都了解过了,梯度是一个向量,导数+变化最快的方向
损失函数:
通过梯度使损失降到最
用y=wx+b举例也就是使用梯度来更新w的值,w=w-学习率*梯度。大于零就减小,反之增大
二.反向传播
就比如搭积木,反向传播就是将一个个积木搭建成一个形状的过程
在函数求导中我们都知道对于复合函数求导要用到的链式法则。
比如J(a,b,c)=3(a+bc),令v=bc,u=a+v,J(a,b,c)=3u,要对这个函数求导
将这个过程画成图就是计算图了:
而从b,c到v一直到结果的过程就是向前的,而反向传播就是反过来的
1.向前计算
对于tensor创建的张量,里面有一个属性requires_grad,如果设置为True那么它将记录这个张量的所有的操作保存在grad_fn当中
1.1计算过程:
import torch
# 创建一个张量
x=torch.ones(2,2,requires_grad=True)
# 对这个张量计算
y=x+5
print(y)
# 输出里面就会有一个属性grad_fn来保存这个张量x的计算过程
那么通过这个grad_fn保存的操作过程就可以用来组成上面的那种计算图
补充(有时我们不需要这个记录操作的时候我们可以将不需要的那个操作封装到with torch.no_grad()中。)
with torch.no_grad():
z=x*3+2 # 此时创建的张量z中的requires_grad=False
反向传播
比如我们要计算y=(x+1)^2
import torch
x=torch.ones(1,1,requires_grad=True)
z=x+1
y=z*z
out=y
对于正向计算后得到的out而言,就可以使用backward来进行反向传播,计算梯度
out.backward()
# 计算结果就是这个函数的导数2
注意(在输出为一个标量的情况下可以直接使用backward方法,但是在输出不为标量的情况下要向backward中传入参数)
那么对于损失来说,loss.backward()计算出导数也就是梯度,并且保存到grad中来更新梯度(比如梯度下降的时候不可能只计算一次梯度,肯定是要更新的)
做一个简单的例子:
比如我们现在的模型是y=wx+b,其中w和b均为参数。然后我们使用y=3x+1来构造x和y的数据,然后通过模型来确定w和b的值,在和y=3x+1来比较误差
import torch
import numpy as np
from matplotlib import pyplot as plt
# 1准备数据
x=torch.rand([100])
y=3*x+1
# 先随机生成一个w和b
w=torch.rand(1,requires_grad=True)
b=torch.rand(1,requires_grad=True)
# 定义一个对损失反向传播得到梯度的函数
def loss_fn(y,y_predict):
loss=(y_predict-y).pow(2).mean()
for i in [w,b]:
# 每次反向传播前把梯度设置为0
if i.grad is not None:
i.grad.data.zero_()
loss.backward()
return loss.data
# 定义一个函数计算w和b的下降后的值,learning_rate为学习率
def optimize(learning_rate):
w.data-=learning_rate*w.grad.data
b.data-=learning_rate*b.grad.data
for i in range(3000): #下降3000次,次数越多越精确
# 使用当前的w和b值来计算出预测值
y_predict=x*w+b
# 计算误差
loss=loss_fn(y,y_predict)
# 更新w和b
optimize(0.01)
# 计算最终的预测值
predict=x*w+b
# 计算最后的误差
loss=predict-y