[PyTorch] 基于python和pytorch的多项式回归

本文详细介绍了使用PyTorch进行三次多项式拟合的过程,包括数据生成、网络搭建、训练及结果展示,强调矢量化编程的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

讲解

须导入和函数库

mport torch
import numpy as np
from torch.autograd import Variable
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt

设定目标多项式的参数

本次拟合的是三次多项式
y = a 1 x 1 + a 2 x 2 + a 3 x 3 + b y=a_1x_1+a_2x_2+a_3x_3+b y=a1x1+a2x2+a3x3+b
所以需要 w_target = [a1, a2, a3] 和 b_target

# 设定target参数
w_target = torch.tensor([0.5, 3, 2.4]).unsqueeze(1)    # unsqueeze 升维
b_target = torch.tensor([0.9])

生成w_target用到的.unsqueeze(axis)函数为升维函数。
如果不加该函数,w_targetshapetorch.Size([3]),并不是向量(矩阵)。

.unsqueeze(1) 是在w_target的第二维的位置加上一个维度,变为torch.Size([3, 1]),变为向量(矩阵)。

关于升维降维函数的讲解见本人的另一个博客增加维度或者减少维度 ——a.squeeze(axis) 和 a.unsqueeze(axis)

生成训练集

通过batch_size=256确定训练集内元素的个数

生成 x 的序列(以向量存在)

目标: 生成随机、有序的x的序列( 以向量存在 )
实现思路:

  1. 生成batch_size数量的随机数(tensor类型)
  2. 类型转换:tensor ==> numpyndarray
  3. 对生成的随机数(ndarray类型)排序
  4. 类型转换:ndarray ==> tensor
  5. 升维,构成矩阵
random = torch.randn(batch_size)    # 生成batch_size个随机数
random = random.numpy()
random.sort()                       # 升序排列random,涉及类型转换,tensor 2 array
random = torch.from_numpy(random)   # random 类型转换 array 2 tensor
random = random.unsqueeze(1)        # 升维构成矩阵
计算 y 的实际值

这里有一个很重要的知识点:矢量化编程

矢量化编程简单来说,就是用 矩阵运算 代替 显性的for循环
矢量化编程的作用:能极大提高运行速度(划重点:极大)

在通过
y = a 1 x 1 + a 2 x 2 + a 3 x 3 + b y=a_1x_1+a_2x_2+a_3x_3+b y=a1x1+a2x2+a3x3+b计算y时,我们用矢量化编程来代替显性的for循环

# 原理讲解
# torch.Size([256, 3])
# x = [[x_1 x_1^2 x_1^3]
# 	   [x_2 x_2^2 x_2^3]
# 	   [x_3 x_3^2 x_3^3]
#	   ...
#	   [x_256 x_256^2 x_256^3]]
#
# torch.Size([3, 1])
# w_target =([a_1],
#       	 [a_2],
#   	     [a_3]])

# 根据**线性代数**知识:
# y = x * w_target + b
# torch.Size([256, 1])

代码实现如下:

 x = torch.cat([ random ** i for i in range(1, 4)], 1)            # 生成x, x^2, x^3 的列矩阵
 y = x.mm(w_target) + b_target[0]    # 生成真实y

关于 x = torch.cat([ random ** i for i in range(1, 4)], 1) 的原理,在本人的另一篇博客专门很仔细地讲过 拼接多个tensor:torch.cat((A,B),axis),故此处不再赘述。

GPU or CPU

这里首先要定义一个人为设定的超参数DEVICE

DEVICE = ("cuda" if torch.cuda.is_available() else "cpu")

之后就可以用.to(DEVICE)tensor放到相应的GPUCPU上了。

return x.to(DEVICE), y.to(DEVICE)

生成训练集的整体代码如下:

DEVICE = ("cuda" if torch.cuda.is_available() else "cpu")

# 生成训练集
def get_batch(batch_size=256):
	
	# 生成随机、有序的x的序列(组成 -> 向量)
    random = torch.randn(batch_size)    # 生成batch_size个随机数
    random = random.numpy()
    random.sort()                       # 升序排列random,涉及类型转换,tensor 2 array
    random = torch.from_numpy(random)   # random 类型转换 array 2 tensor
    random = random.unsqueeze(1)        # 升维
    
    x = torch.cat([ random ** i for i in range(1, 4)], 1)            # 生成x, x^2, x^3 的列矩阵
    y = x.mm(w_target) + b_target[0]    # 生成真实y

	return x.to(DEVICE), y.to(DEVICE)

搭建网络结构

class poly_model(nn.Module):
    def __init__(self):
        super(poly_model, self).__init__()
        self.poly = nn.Linear(3, 1)         # 输入三维,输出一维
    def forward(self, x):
        out = self.poly(x)
        return out

model = poly_model().to(DEVICE)

model = poly_model().to(DEVICE)将网络实例化,使其有了输入输出接口

另外,注意每个网络层的输入输出维度
这个网络只有一个网络层。输入维度为 3 ,输出维度为 1。体现在self.poly = nn.Linear(3, 1) 的两个参数31上。

最后的model为:

poly_model(
  (poly): Linear(in_features=3, out_features=1, bias=True)
)

定义损失函数和优化器

这部分没什么可说的。

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

训练网络

# 训练网络 直到误差小于1e-3
epoch = 0
while True:
    # get data
    batch_x, batch_y = get_batch()      # 得到训练集
    
    # forward
    predict = model(batch_x)     # 用模型计算predict
    loss = criterion(predict, batch_y)       #计算损失函数,前者为预测值,后者为真实值

    # backward pass
    optimizer.zero_grad()   # 优化器梯度归零
    loss.backward()
    optimizer.step()

    epoch+=1

    if (epoch-1)%500 == 0:
        batch_x = batch_x.cpu()
        batch_y = batch_y.cpu()
        predict = predict.cpu()

        predict = predict.detach().numpy()

        print('epoch:{}'.format(epoch))
        print('loss:{}',format(loss))

        predict = torch.from_numpy(predict)
        batch_x = batch_x.cuda()
        batch_y = batch_y.cuda()
        output = predict.cuda()


    # --------------------------------------------------------------------------
    if loss < 1e-3:
        print('Done!')
        print('epoch:{}'.format(epoch))
        break

前向传播可反向传播没什么可说的。如果想看可以去本人的另一篇博客 基于python和pytorch的线性拟合(完结),那里说得详细些。

这里要讨论的一点,是if (epoch-1)%500 == 0:这部分中,GPU --> CPU 然后 CPU -->GPU,在这儿是没什么意义的(单纯的输出数值在GPU上也行)。
但是如果想要画图的话GPU --> CPU 然后 CPU -->GPU的过程就是必须的(画图时要求的数据类型为ndarray,再跑下一个epoch的时候数据类型又要变回tensor)。
这里依旧保留GPU --> CPU 然后 CPU -->GPU的过程,就是为了说明以下常规流程(当然像现在的情况可以简化)。

显示结果

# GPU ==> CPU
batch_x = batch_x.cpu()
batch_y = batch_y.cpu()
output = predict.cpu()

output = output.detach().numpy()
plt.plot(batch_x.numpy()[:,0], batch_y.data.numpy(), color='b', label='real curve')
plt.show()

唯一要说的.detach(),在 基于python和pytorch的线性拟合(完结)中也讲到了,这里也不说了。

查看训练后的参数

for param in model.named_parameters():
    print(param[0]) 		# poly.weight
    print(param[1])			# poly.bias

要说的在注释里了。

整体源代码

代码如下

import torch
import numpy as np
from torch.autograd import Variable
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt

# 设定target参数
w_target = torch.tensor([0.5, 3, 2.4]).unsqueeze(1)  # unsqueeze 升维
b_target = torch.tensor([0.9])

DEVICE = ("cuda" if torch.cuda.is_available() else "cpu")

# 返回x y
def get_batch(batch_size=256):
    random = torch.randn(batch_size)  # 生成batch_size个随机数
    random = random.numpy()
    random.sort()  # 升序排列random,涉及类型转换,tensor 2 array
    random = torch.from_numpy(random)  # random 类型转换 array 2 tensor

    random = random.unsqueeze(1)  # 升维
    x = torch.cat([random ** i for i in range(1, 4)], 1)  # 生成x, x^2, x^3 的列矩阵
    y = x.mm(w_target) + b_target[0]  # 生成真实y

    return x.to(DEVICE), y.to(DEVICE)


# -------------------------定义模型---------------------------
class poly_model(nn.Module):
    def __init__(self):
        super(poly_model, self).__init__()
        self.poly = nn.Linear(3, 1)  # 输入三维,输出一维

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


model = poly_model().to(DEVICE)

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

# 训练网络 直到误差小于1e-3
epoch = 0
while True:
    # get data
    batch_x, batch_y = get_batch()  # 得到数据集

    # forward
    predict = model(batch_x)  # 用模型计算predict
    loss = criterion(predict, batch_y)  # 计算损失函数,前者为预测值,后者为真实值


    # backward pass
    optimizer.zero_grad()  # 优化器梯度归零
    loss.backward()
    optimizer.step()

    epoch += 1

    if (epoch - 1) % 500 == 0:
        batch_x = batch_x.cpu()
        batch_y = batch_y.cpu()
        predict = predict.cpu()

        predict = predict.detach().numpy()

        print('epoch:{}'.format(epoch))
        print('loss:{}', format(loss))

        predict = torch.from_numpy(predict)
        batch_x = batch_x.cuda()
        batch_y = batch_y.cuda()
        output = predict.cuda()

    # --------------------------------------------------------------------------
    if loss < 1e-3:
        print('Done!')
        print('epoch:{}'.format(epoch))
        break

# 去cuda化
batch_x = batch_x.cpu()
batch_y = batch_y.cpu()
output = predict.cpu()

output = output.detach().numpy()

plt.plot(batch_x.numpy()[:, 0], batch_y.data.numpy(), color='b', label='real curve')

plt.show()

for param in model.named_parameters():
    print(param[0])
    print(param[1])

结果

# epoch:1
# loss:{} 107.65440368652344
# epoch:501
# loss:{} 0.17065830528736115
# epoch:1001
# loss:{} 0.02675577811896801
# epoch:1501
# loss:{} 0.010034649632871151
# epoch:2001
# loss:{} 0.0034075819421559572
# epoch:2501
# loss:{} 0.0011402576928958297
# Done!
# epoch:2522
# poly.weight
# Parameter containing:
# tensor([[0.4681, 2.9867, 2.4066]], device='cuda:0', requires_grad=True)
# poly.bias
# Parameter containing:
# tensor([0.9329], device='cuda:0', requires_grad=True)
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值