pytorch入门案例(来自B站up主刘二大人视频)

1 简介

选择IDE为pycharm,文中y_hat表示预测值
案例如下:

已知x=1时,y为2;x=2时,y为4;x=3时,y为6,现利用线性模型(不含偏置量,仅包含斜率参数,即y_hat=w*x)预测x=4时y的值

2 代码讲解

2.1代码总览

import torch

x_data = [1.0,2.0,3.0]
y_data = [2.0,4.0,6.0]

w = torch.tensor([1.0])
w.requires_grad = True
def forward(x):
    return x * w

def loss(x,y):
    y_pred = forward(x)
    return (y_pred - y) ** 2

print("Predict(before training)",4,forward(4).item())

for epoch in range(100):
    for x,y in zip(x_data,y_data):
        l = loss(x,y)
        l.backward()
        print("\tgrad:",x,y,w.grad.item())
        w.data = w.data - 0.01* w.grad.data

        w.grad.data.zero_()

    print("progress:",epoch,l.item())
print("predict(after training)",4,forward(4).item())

此为全部代码,现分块进行讲解

2.2 代码第一部分

import torch

x_data = [1.0,2.0,3.0]
y_data = [2.0,4.0,6.0]

w = torch.tensor([1.0])
w.requires_grad = True

首先导入torch包,然后初始化已知数据x_datay_data

然后,暂时停止讲解,我们稍微介绍一下tensor,一种十分重要的数据类型,类似于numpy中的array数据类型

tensor类型中会存放两个东西,一个是data,存放标量、矩阵或高阶矩阵;另一个是grad,存放最终损失函数对该data的梯度(后续都会使用到)。如图所示
pytorch入门案例(来自B站up主刘二大人视频)_第1张图片
接着讲代码。最后两行中首先初始化了一个名为wtensor,之后将该tensorrequires_grad属性修改为True(默认为False)。该值为True,意味着之后会计算损失函数对于该参数的导数(也就是需要计算grad

后续更改参数时需要使用随机梯度下降,当然需要计算这里的梯度啦,所以为True

2.3 代码第二部分

def forward(x):
    return x * w

这里定义了前馈函数forward(),作用是计算出预测值y_hat

注意,wtensor类型,在计算时,*号会重载为适合tensor类型的乘法,且会将x类型也暂时转换为tensor类型进行计算,最终返回值为tensor类型

2.4 代码第三部分

def loss(x,y):
    y_pred = forward(x)
    return (y_pred - y) ** 2

该部分计算损失函数。

注意,forward()函数的返回值也是tensor类型,于是按照之前的说明,loss()函数的返回值也会是一个tensor类型的数据

2.5 代码第四部分

print("Predict(before training)",4,forward(4).item())

for epoch in range(100):
    for x,y in zip(x_data,y_data):
        l = loss(x,y)
        l.backward()
        print("\tgrad:",x,y,w.grad.item())
        w.data = w.data - 0.01* w.grad.data

        w.grad.data.zero_()

    print("progress:",epoch,l.item())
print("predict(after training)",4,forward(4).item())

我们训练了100轮,在每一轮中,使用随机梯度下降进行计算(梯度下降即计算损失函数对于参数w的偏导数,而这里的随机梯度下降中,每一次计算用于更新的偏导数值时,仅取3项求和项中的一项进行计算,这样有很多优势,读者可自己查询)。

具体到某一次时,我们进入这一部分代码

        l = loss(x,y)
        l.backward()
        print("\tgrad:",x,y,w.grad.item())
        w.data = w.data - 0.01* w.grad.data

		w.grad.data.zero_()

首先通过l = loss(x,y)计算出损失函数。正如之前所讲,每一次含有tensor类型的变量进行运算时,其他类型的变量会被重载。在这个计算过程中,就会构造出一张计算图
pytorch入门案例(来自B站up主刘二大人视频)_第2张图片
解释一下:对于稍微复杂一些的神经网络,梯度的手工求导计算是不可能的。我们需要pytorch进行自动计算,而pytorch计算导数的机制是基于计算图的(类似于我们的链式求导,它会将最终l部分与参数w部分之间的若干个偏导计算出来进行相乘),所以,对于requires_gradTruew来说,每一次含有它的运算都会构建出一点计算图,最终就构建出了如上图所示的计算图(该参数为真的tensor变量的每一次计算就会多构建出一点计算图,读者可以自己根据代码手绘出上述计算图)

然后是代码l.backward()。这里调用了张量lbackward()方法,它的作用是把计算图上需要计算偏导的地方都求出来,然后自动相乘,最终得到l关于w的偏导数(也就是梯度),同时释放计算图(计算图不再存在)。

之后,w.data = w.data - 0.01* w.grad.data对梯度进行更新。这里注意,更新时没有直接使用w进行更新,而使用了w.data。原因很简单,因为直接对w的使用意味着构建计算图,我们这里的本意只是想利用数值更新梯度,而不是想要构建新的计算图,所以使用w.data

梯度更新部分 w.data = w.data - 0.01* w.grad.data不再啰嗦。参数0.01是超参数。

稍微多提一句,代码中可以看见w.grad.dataw.grad.item(),它们的区别是什么?data表示这个tensor的数据信息,即维度和具体的值,而item()是一个将0阶张量 标量化的函数。

最后,w.grad.data.zero_()表示将w的梯度的值清零。原因:如果没有这一步梯度清零操作,下一次计算出的梯度值会累加上一次的值,这并不是我们想要的结果。至于程序为什么没有自动清零?因为有时候我们并不需要清零操作。这一步记住该机制即可

这里讲的应该比较清楚了,读者可以试着将模型加上偏置量,亦或使用二元函数模型进行预测,来检验是否学懂。

如有错误,欢迎指出,谢谢

你可能感兴趣的:(python,pytorch,pytorch,人工智能,python)