[PyTorch] 基于Python和PyTorch的线性拟合

用神经网络实现线性拟合,代码源自《深度学习入门之pytorch》,本人根据新版本的PyTorch做了挺大的修改。

《深度学习入门之pytorch》的使用体验

关于这个书好不好,我也不好评价,但总体体验不是太好。
书中的代码有些不全,新手自己运行不出结果,就不利于新手理解代码。
另外GitHub上的代码版本比较旧。我用的pytorch版本为1.0.1,在运行示例代码的时候会报错,需要改动细节才能正常运行。
尤其尤其是源代码没有根据“tensorVariable合并”的大改动而更新(此次改动出现在0.4.0的版本)!!!

代码解析

导入需要的库

import torch

import numpy as np

# 可调用网络结构的库
import torch.nn as nn

# 优化器的库
import torch.optim as optim

# 画图函数的库
import matplotlib.pyplot as plt

给出待拟合的原始数据

原始数据类型为 numpyndarray,变量类型定为 float

x_train = np.array([[3.3], [4.4], [5.5], [6.71], [6.93], [4.168],
                    [9.779], [6.182], [7.59], [2.167], [7.042],
                    [10.791], [5.313], [7.997], [3.1]], dtype=np.float32)

y_train = np.array([[1.7], [2.76], [2.09], [3.19], [1.694], [1.573],
                    [3.366], [2.596], [2.53], [1.221], [2.827],
                    [3.465], [1.65], [2.904], [1.3]], dtype=np.float32)

如果不增加 dtype=np.float32 将元素定为float,则元素的数据类型就为默认的 double

out = self.linear(x)RuntimeError: Expected object of scalar type Float but got scalar type Double for argument #4 'mat1' 的错,该错的原因是类型不符(要得到 float 但得到 double )。

定义模型并确定CPU或GPU

class LinearRegression(nn.Module):
    def __init__(self):
        super(LinearRegression, self).__init__()
        self.linear = nn.Linear(1, 1)   # 输入输出都是一维
    def forward(self, x):
        out = self.linear(x)
        return out

# 判断cuda加速是否可用
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = LinearRegression().to(DEVICE)

当网络搭建完成后,通过 model = LinearRegression().to(DEVICE) 将网络实例化,即带有了输入输出接口
其中DEVICE是一个人为定义的超参数,通过if来决定tensor是在GPU上运算还是CPU上运算。

定义优化器和损失函数

在优化器中设定学习率(此处没有用到学习率的衰减)。

# 损失函数
criterion = nn.MSELoss()    

# 优化器
optimizer = optim.SGD(model.parameters(), lr=1e-3)

数据预处理

numpyndarray 转为 tensor

# array 变 Tensor
x_train = torch.from_numpy(x_train)
y_train = torch.from_numpy(y_train)

确定使用CPUGPU

# 判断GPU是否可用
if torch.cuda.is_available():
    inputs = x_train.cuda()
    target = y_train.cuda()
else:
    inputs = x_train
    target = y_train

训练网络

前向传播 + 反向传播 + 输出结果

1. 设定循环次数

# 循环100次
num_epochs = 100

2. 前向传播

  • 由网络计算输出out = model(inputs)
  • 计算实际值和预测值的损失函数loss = criterion(out, target)

3. 反向传播

  • 梯度清零:optimizer.zero_grad()
  • 损失函数反向传播:loss.backward()
  • 优化器优化:optimizer.step()

4. 每20个epoch输出一次

该部分整体代码如下:

# 循环100次
num_epochs = 100

for epoch in range(num_epochs):

    # forward
    out = model(inputs)
    loss = criterion(out, target)
    
    # backward
    optimizer.zero_grad()       # 梯度清零
    loss.backward()
    optimizer.step()
    
    # 输出循环结果(每20个epoch输出一次)
    if(epoch+1) % 20 == 0:
        print('Epoch[{}/{}], loss:{:.6f}'
              .format(epoch+1, num_epochs, loss.data))

绘图

1. 将模型改回CPU

model.cpu()

2. 计算最终的预测值(并转换为ndarray类型)

predict = model(x_train)
predict = predict.detach().numpy()

对于predict = predict.detach().numpy()中为什么要加入.detach()。因为predict在Variable的计算图中,需要从中取出tensor,再通过.numpy()转为ndarray

不过也不用太担心。因为如果没加的话,会对predict = predict.numpy()报出RuntimeError: Can't call numpy() on Variable that requires grad. Use var.detach().numpy() instead的错,告诉你用.detach().numpy()。(记住错误原因是Can't call numpy() on Variable that requires grad,在下下一个部分讨论tensorVariable合并的影响的时候会再提到)

关于tensorVariable的关系。详见本人的另一篇博客深度学习框架PyTorch中的概念和函数(待填坑),并不难理解。

关于.data().detach()的区别,详见关于 pytorch inplace operation, 需要知道的几件事。如果懒得看,也可以直接记下结论:都用.detach()就好了。

这部分的整体代码如下:

model.cpu()
predict = model(x_train)
predict = predict.numpy()
plt.plot(x_train.numpy(), y_train.numpy(), 'ro', label='Original data')
plt.plot(x_train.numpy(), predict, label='Fitting line')
plt.show()

源代码

源代码如下

import torch
import numpy as np
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt

# 原始数据为 ndarray,变量类型为 float
x_train = np.array([[3.3], [4.4], [5.5], [6.71], [6.93], [4.168],
                    [9.779], [6.182], [7.59], [2.167], [7.042],
                    [10.791], [5.313], [7.997], [3.1]], dtype=np.float32)

y_train = np.array([[1.7], [2.76], [2.09], [3.19], [1.694], [1.573],
                    [3.366], [2.596], [2.53], [1.221], [2.827],
                    [3.465], [1.65], [2.904], [1.3]], dtype=np.float32)



# ----------------定义模型------------------
class LinearRegression(nn.Module):
    def __init__(self):
        super(LinearRegression, self).__init__()
        self.linear = nn.Linear(1, 1)   # 输入输出都是一维
    def forward(self, x):
        out = self.linear(x)
        return out

# 判断cuda加速是否可用
if torch.cuda.is_available():
    model = LinearRegression().cuda()
else:
    model = LinearRegression()
# ----------------定义模型完毕--------------

# ------------定义损失函数和优化函数--------------
criterion = nn.MSELoss()    # 损失函数
optimizer = optim.SGD(model.parameters(), lr=1e-3)


# array 变 Tensor
x_train = torch.from_numpy(x_train)
y_train = torch.from_numpy(y_train)
# 判断GPU是否可用
if torch.cuda.is_available():
    inputs = x_train.cuda()
    target = y_train.cuda()
else:
    inputs = x_train
    target = y_train

# 循环100次
num_epochs = 100
for epoch in range(num_epochs):

    # forward
    out = model(inputs)
    loss = criterion(out, target)

    # backward
    optimizer.zero_grad()       # 梯度归零
    loss.backward()
    optimizer.step()

    # 输出循环结果(每20个epoch输出一次)
    if(epoch+1) % 20 == 0:
        print('Epoch[{}/{}], loss:{:.6f}'
              .format(epoch+1, num_epochs, loss.data))


model.cpu()
predict = model(x_train)
predict = predict.detach().numpy()
plt.plot(x_train.numpy(), y_train.numpy(), 'ro', label='Original data')
plt.plot(x_train.numpy(), predict, label='Fitting line')
plt.show()

梳理

1. 如何简单地将tensor从CPU,简单地转移到GPU上?

.cuda()即可。

需要用.cuda()的有:

  • 网络实例化的modelmodel = LinearRegression().cuda()
  • 所有用到的数据inputstargetinputs = x_train.cuda()target = y_train.cuda()

2. 关于tensorVariable,新版的PyTorch已经将两者合并。而本次参考的代码比较老旧,故看看有没有可以改进的地方?

一个最明显的改变就是,不用再把tensor转成Variable,即:tensor已经承载了Variable的功能。

另外需要注意的是,numpy并不能处理tensor。所以如果要对最终的输出结果,就需要将输出结果从tensor转为numpyndarray,需要用到的函数是.detach().numpy()

回想上上一部分提到的,不加.detach()的报错Can't call numpy() on Variable that requires grad,再联系深度学习框架PyTorch中的概念和函数(待填坑)中对Variable的三个组件.data.grad、.grad_fn

报错说明了tensor在与Variable合并后也有了这三个组件,而能转回ndarray必须是不带gradtensorrequire_grad=False),而这可以通过.detach()函数提取出tensor.data组件来实现。

3. 承接第2点,tensor、Variable、CUDA、CPU、ndarray,数据类型太过于复杂,有没有减少数据类型的方法?

首先tensorVariable合并,就只剩下了ndarraytensor

默认tensor是在CPU上,需要转到GPU需要调用.cuda()

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值