Pytorch完成线性回归

1.向前计算

对于pytorch中的一个tensor,如果设置它的属性.requires_gradTrue,那么它将会追踪对于该张量的所有操作,或者可以理解为,这个tensor是一个参数,后续会被计算梯度,更新该参数。

1.1计算过程

假设有一下条件(1/4表示平均值,Xi中有4个数),使用torch完成其向前计算的过程
在这里插入图片描述
如果x为参数,需要对其进行梯度的计算和更新。那么,在最开始随机设置x的值的过程中,需要设置它的requires_grad属性值为True,其默认值为False
在这里插入图片描述
从上述代码可以看出:

  1. x的requires_grad属性为True
  2. 之后的每次计算都会修改其grad_fn属性,用来记录做过的操作
    1. 通过这个函数和grad_fn能够组合才能一个和前一小节类似的计算图

1.2requires_grad和grad_fn

在这里插入图片描述
注意:
为了防止跟踪历史记录(和使用内存),可以将代码块包装在with torch.no_grad():中(其中的操作不会被追踪,看上图,并且其requires_grad也会被修改)。在评估模型时特别有用,因为模型可能具有requires_grad = True的可训练参数,但是我们不需要在此过程中对他们进行梯度计算。

1.3梯度计算

反向传播:output.backward()
获取梯度:x.gard
对于1.1中的out而言,我们可以使用backward方法来进行反向传播,计算梯度 o u t . b a c k w a r d ( ) out.backward() out.backward(),此时便能够求出倒数 d o u t / d x dout/dx dout/dx,调用 x . g r a d x.grad x.grad能够获取导数值。
在这里插入图片描述
注意:在输出是一个标量的情况下,我们可以调用输出 t e n s o r tensor tensor b a c k w a r d ( ) backward() backward()方法,但是在数据是一个向量的时候,调用 b a c k w a r d ( ) backward() backward()的时候还需要传入其他参数。
loss.backward()就是根据损失函数,对参数requires_grad=True去计算它的梯度,并且把它累加保存到x.gard此时还并未更新其梯度。所以,每次反向传播之前需要先把梯度置为0之后再计算。
在这里插入图片描述

2.线性回归实现

下面会用torch实现一个简单的线性回归
假设基础模型是 y = w x + b y=wx+b y=wx+b,其中w和b均为参数,我们使用 y = 3 x + 0.8 y=3x+0.8 y=3x+0.8来构造数据x和y。主要步骤如下:

  1. 准备数据
  2. 计算预测值
  3. 计算损失,把参数的梯度置为0,进行反向传播
  4. 更新参数
import torch
import matplotlib.pyplot as plt

learning_rate = 0.01
# 1. 准备数据
# y = 3x+0.8
x = torch.rand([500,1])
y_true = 3*x + 0.8

# 2. 通过模型计算y_predict
w = torch.rand([1,1],requires_grad=True)
# 只有浮点型的tensor才可以使用requires_grad=True属性,所以需要指定dtype=torch.float32
b = torch.tensor(0,requires_grad=True,dtype=torch.float32)

# 4. 通过循环,反向传播,更新参数
for i in range(3000):
    # 3. 计算loss
    # matmul计算矩阵乘积
    y_predict = torch.matmul(x,w) + b
    # 取均值方差当做损失函数
    loss = (y_true-y_predict).pow(2).mean()

    if w.grad is not None:
        w.grad.data.zero_()
    if b.grad is not None:
        b.grad.data.zero_()

    loss.backward() # 反向传播
    w.data = w.data - learning_rate*w.grad
    b.data = b.data - learning_rate*b.grad

    if i %50 == 0:
        print("w:{}, b:{}, loss:{}".format(w.item(),b.item(),loss.item()))

plt.figure(figsize=[20,8])
plt.scatter(x.numpy().reshape(-1),y_true.numpy().reshape(-1))
y_predict = torch.matmul(x,w) + b
plt.plot(x.numpy().reshape(-1),y_predict.detach().numpy().reshape(-1))
plt.show()

模型训练上3000次后得到w和b接近相等
在这里插入图片描述

3.Pytorch完成模型常用API

在上面部分,我们实现了通过torch完成反向传播和参数更新,当参数较多时,使用上面方法有点复杂。下面了解以下常用的API

3.1 nn.Module

nn.Moduletorch.nn提供的一个类,是pytorch中我们自定义网络的一个基类。当我们自定义网络的时候,有两个方法需要注意:

  1. __init__需要调用super方法,继承父类的属性和方法。
  2. forward方法必须实现,用来定义我们的网络的向前计算的过程。
    用前面的 y = w x + b y=wx+b y=wx+b的模型举例如下:
class lr(nn.Module):
    def __init__(self):
        super(lr,self).__init__()       # 继承父类init的参数
        self.linear = nn.Linear(1,1)

    def forward(self, x):
        out = self.linear(x)
        return out

注意:

  1. nn.Linear为torch预定义好的线性模型,也称为全连接层,传入的参数为输入的数量,输出的数量(in_features,out_features),其中不算batch_sizer的列数
    2.nn.Module定义了__call__方法,实现的就是调用forward方法 ,能够直接被传入单数调用,实际上调用的就是forward方法并传入参数。
	# 实例化模型
    model = lr()
    # 传入数据,计算结果
    predict = model(x)

3.2优化器类

优化器(optimizer),可以理解为torch为我们封装的用来进行更新参数的方法,比如常见的随机梯度下降。
优化器都是由torch.optim提供的,例如:
1.torch.optim.SGD(参数,学习率)
2. torch.optim.Adam(参数,学习率)
注意:

  • 参数可以使用model.parameters()来获取,获取模型中所有requires_grad=True的参数
  • 优化类的使用方法
    * 实例化
    * 将所有参数的梯度值设为0
    * 反向传播计算梯度
    * 更新参数值
optimizer = optim.SGD(model.parameters(),lr = 1e-3)# 1.实例化
# 因为PyTorch默认会对梯度进行累加,不想先前的梯度影响到当前梯度的计算时需要手动清零
optimizer.zero_grad()# 2.梯度置为0
loss.backward()# 3.计算梯度
optimizer.step()# 4. 更新参数的值

3.3损失函数

torch中的损失函数:

  1. 均方误差:nn.MSELoss(),常用于分类问题
  2. 交叉熵损失:nn.CrossEntropyLoss(),常用于逻辑回归
    使用方法:
model = lt()# 1.实例化模型
criterion = nn.MSELoss()# 2.实例化损失函数
optimizer = optim.SGD(model.parameters(),lr = 1e-3)# 3.实例化优化器类
for i in range(100):
	y_predict = model(x_true)# 4.向前计算预测值
	loss = criterion(y_true,y_predict)# 5. 调用损失函数传入真实值和预测值,得到损失结果
	optimizer.zero_grad()# 5.当前循环参数梯度置为0
	loss.backward()# 6.计算梯度
	optimizer.step()# 7.更新参数的值

pytorch实现线性回归完整代码:

import torch
from torch import optim
from torch import nn
import matplotlib.pyplot as plt

# 1. 定义数据
x = torch.rand([50,1])
y = x*3 + 0.8

# 2. 定义lr模型
class lr(nn.Module):
    def __init__(self):
        super(lr,self).__init__()       # 继承父类init的参数
        self.linear = nn.Linear(1,1)

    def forward(self, x):
        out = self.linear(x)
        return out

def main():
    # 2. 实例化模型,loss和优化器
    model = lr()
    criterion = nn.MSELoss()
    optimizer = optim.SGD(model.parameters(),1e-3)

    # 3. 训练模型
    for i in range(3000):
        out = model(x)  # 获取预测值
        loss = criterion(y,out)     # 计算损失
        optimizer.zero_grad() # 梯度置0
        loss.backward()     # 计算梯度
        optimizer.step()    # 更新梯度
        if i%20 == 0:
        	# 可以通过model.parameters()来说输出参数变化	
	   		params = list(model.parameters())
	   		print(params[0].item(),params[1].item())
            print("loss:%6f"%loss.data)
            print('Epoch[{}/{}],loss:{:.6f}'.format(i,3000,loss.data))

    # 4. 模型评估
    model.eval()
    predict = model(x)
    predict = predict.data.numpy()
    plt.scatter(x.data.numpy(),y.data.numpy(),c='r')
    plt.plot(x.data.numpy(),predict)
    plt.show()
if __name__ == '__main__':
    main()

model.eval()表示设置模型为评估模式,即预测模式
model.train(mode=True)表示设置模型为训练模式
在当前的线性回归中,上述并无区别。
但是在其他的一些模型中,训练的参数和预测的参数会不相同,到时候就需要具体分清楚是在进行训练还是预测,比如模型中存在Dropout,BatchNorm的时候。

3.4 使用GPU训练

当模型太大或者参数太多的时候,可以使用GPU来加快训练速度。

  1. 判断GPU是否可用torch.cuda.is_avaiable(),如果可用创建GPU的device;
device = torch.device("cuda" if torch.cuda.is_avaiable() else "cpu")
  1. 把模型参数和input数据转化为cuda 的支持类型;
model.to(device)
x_true.to(device)
  1. 在GPU上计算结果也为cuda数据类型,需要转化为numpy或者torch的cpu的tensor类型;
predict = predict.cpu().detach().numpy()

detach()的效果和data的相似,但是detach()是深拷贝,data是取值,是浅拷贝。
修改之后的代码为:

import torch
from torch import optim
from torch import nn
import matplotlib.pyplot as plt

device = torch.device("cuda" if torch.cuda.is_avaiable() else "cpu")

# 1. 定义数据
x = torch.rand([50,1]).to(device) # 修改为GPU的tensor
y = x*3 + 0.8

# 2. 定义lr模型
class lr(nn.Module):
    def __init__(self):
        super(lr,self).__init__()       # 继承父类init的参数
        self.linear = nn.Linear(1,1)

    def forward(self, x):
        out = self.linear(x)
        return out

def main():
    # 2. 实例化模型,loss和优化器
    model = lr().to(device)	# 修改为GPU的模型
    criterion = nn.MSELoss()
    optimizer = optim.SGD(model.parameters(),1e-3)

    # 3. 训练模型
    for i in range(3000):
        out = model(x)  # 获取预测值
        loss = criterion(y,out)     # 计算损失
        optimizer.zero_grad() # 梯度置0
        loss.backward()     # 计算梯度
        optimizer.step()    # 更新梯度
        if i%20 == 0:
            print("loss:%6f"%loss.data)
            print('Epoch[{}/{}],loss:{:.6f}'.format(i,3000,loss.data))

    # 4. 模型评估
    model.eval()
    predict = model(x)
    predict = predict.data.numpy()
    plt.scatter(x.data.numpy(),y.data.numpy(),c='r')
    plt.plot(x.data.numpy(),predict)
    plt.show()

if __name__ == '__main__':
    main()

在GPU上执行程序需要注意:

  • 自定义的参数和数据需要转化为cuda支持的tensor
  • model需要转化为cuda支持的model
  • 执行的结果需要和cpu的tensor计算的时候,tensor.cpu()可以把cuda的tensor转化为cpu的tensor。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值