[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
    评论
### 回答1: 好的,这是一个 PyTorch 程序例子,它拟合一段非线性函数: ```python import torch import matplotlib.pyplot as plt # 定义非线性函数 def func(x): return x**2 + 10*torch.sin(x) # 生成训练数据 X = torch.unsqueeze(torch.linspace(-5, 5, 100), dim=1) y = func(X) # 定义模型 class Net(torch.nn.Module): def __init__(self): super(Net, self).__init__() self.hidden = torch.nn.Linear(1, 20) self.predict = torch.nn.Linear(20, 1) def forward(self, x): x = torch.relu(self.hidden(x)) x = self.predict(x) return x net = Net() # 定义损失函数和优化器 criterion = torch.nn.MSELoss() optimizer = torch.optim.SGD(net.parameters(), lr=0.2) # 训练模型 for epoch in range(100): prediction = net(X) loss = criterion(prediction, y) optimizer.zero_grad() loss.backward() optimizer.step() # 可视化结果 plt.figure() plt.scatter(X.data.numpy(), y.data.numpy(), label='Original data') plt.plot(X.data.numpy(), prediction.data.numpy(), label='Fitting Line') plt.legend() plt.show() ``` 祝你好运! ### 回答2: import torch import torch.nn as nn import torch.optim as optim from torch.autograd import Variable import numpy as np import matplotlib.pyplot as plt # 创建一个非线性函数 def nonlinear_function(x): y = 0.5 * (x ** 2) + 2 * x + 1 return y # 生成训练数据 x_train = torch.unsqueeze(torch.linspace(-5, 5, 100), dim=1) y_train = nonlinear_function(x_train) + torch.randn(x_train.size()) * 0.1 # 定义神经网络模型 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.hidden = nn.Linear(1, 10) self.relu = nn.ReLU() self.output = nn.Linear(10, 1) def forward(self, x): x = self.hidden(x) x = self.relu(x) x = self.output(x) return x # 实例化模型 model = Net() # 定义损失函数和优化器 criterion = nn.MSELoss() optimizer = optim.Adam(model.parameters(), lr=0.01) # 开始训练 num_epochs = 1000 for epoch in range(num_epochs): inputs = Variable(x_train) targets = Variable(y_train) # 向前传播 output = model(inputs) loss = criterion(output, targets) # 反向传播和优化 optimizer.zero_grad() loss.backward() optimizer.step() if (epoch+1) % 100 == 0: print('Epoch [{}/{}], Loss: {:.4f}'.format(epoch+1, num_epochs, loss.item())) # 显示拟合结果 model.eval() x_test = torch.unsqueeze(torch.linspace(-6, 6, 200), dim=1) y_pred = model(Variable(x_test)) plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'ro', label='Original data') plt.plot(x_test.data.numpy(), y_pred.data.numpy(), 'b-', label='Fitted line') plt.legend() plt.show() ### 回答3: import torch import torch.nn as nn import torch.optim as optim import numpy as np # 定义非线性函数 def non_linear_func(x): return torch.sin(x) # 创建训练数据 x_train = np.random.uniform(-10, 10, size=(1000, 1)) y_train = non_linear_func(torch.Tensor(x_train)).numpy() # 转换为Tensor x_train = torch.Tensor(x_train) y_train = torch.Tensor(y_train) # 定义神经网络模型(单层全连接网络) class NonLinearRegression(nn.Module): def __init__(self): super(NonLinearRegression, self).__init__() self.fc = nn.Linear(1, 1) # 输入维度为1,输出维度为1 def forward(self, x): x = self.fc(x) return x # 初始化模型和优化器 model = NonLinearRegression() optimizer = optim.SGD(model.parameters(), lr=0.01) loss_func = nn.MSELoss() # 训练模型 for epoch in range(100): y_pred = model(x_train) loss = loss_func(y_pred, y_train) optimizer.zero_grad() loss.backward() optimizer.step() if (epoch+1) % 10 == 0: print('Epoch [{}/{}], Loss: {:.4f}'.format(epoch+1, 100, loss.item())) # 预测并可视化结果 import matplotlib.pyplot as plt x_test = np.arange(-10, 10, 0.1) x_test = torch.Tensor(x_test.reshape(-1, 1)) y_pred = model(x_test) plt.scatter(x_train.numpy(), y_train.numpy(), color='b', label='Actual') plt.plot(x_test.numpy(), y_pred.detach().numpy(), color='r', label='Predicted') plt.legend() plt.xlabel('x') plt.ylabel('y') plt.title('Nonlinear Regression') plt.show()

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值