Pytorch使用——函数拟合问题样例

本文介绍了如何使用PyTorch搭建一个多层神经网络,通过线性层和激活函数的组合来拟合复杂的函数曲线。作者首先展示了如何建立一个包含多个隐藏层的模型,并使用LeakyReLU作为激活函数。然后,创建了一个训练器,包含了模型训练的基本流程,如梯度下降、损失计算等。训练过程中,作者模拟了一个二次函数并进行拟合。训练结果显示,模型在训练区间内拟合效果良好,但在未训练区间表现较差,体现了过拟合的现象。
摘要由CSDN通过智能技术生成

前言

Pytorch使用——回归问题样例
在上一篇文章中,我们解决了一个简单的线性回归问题,即建立了一个线性模型,通过机器学习的方法把bias和weight求出来了。
那么现在我们就要真正地搭建一个神经网络,将线性层和激活函数进行排列组合,完成一些复杂的工作,比如任意函数曲线拟合,我曾经看到有人说理论上线性层加激活函数的排列组合可以模拟任意模型。

头文件

这一次我们使用了这些库

from turtle import forward
from typing import Any, Callable
import torch
import numpy as np
import time
import torch.nn.functional as F
import matplotlib.pyplot as plt
import math

建立模型

可以看到我们这一次的类依然主要包含了两个函数,分别是初始化函数__init__以及实例化前向预测函数forward,而LReLU函数是我自己写的Leaky ReLU激活函数,为了兼容所有shape的tensor,所以效率很低,最后经过测试也发现实际上效率确实很低(无奈), 追踪F.relu函数,发现它是一个导入的C函数,所以也没办法参考源码了。

我设置了3个输入参数,分别是模型的输入参数数量,输出参数数量,隐藏层参数数量,根据隐藏层参数数量的list定义了len(list)数量的线性层

class multiLayer(torch.nn.Module):
	'''
	构造函数
	输入参数:
	 		_input:		输入层数量,如1
			_output:	输出层数量,如1
			list:		中间层结构,如[3,4,5,6]
	'''
    def __init__(self, _input=1, _output=1, list = []):
        self.layout = len(list)
        self.parameters = []
        # 如果中间层list的size为0,即没有中间层,则该模型回归线性模型
        if self.layout == 0:
            self.model = [torch.nn.Linear(_input, _output)]
        # 如果存在中间层,则模型model将被组装为[_input, list[0], list[1], ..., _output]的形式
        else:
            for i in range(0, self.layout):
                if i == 0:
                    self.model = [torch.nn.Linear(_input, list[0])]
                else:
                    self.model.append(torch.nn.Linear(list[i - 1], list[i]))
            self.model.append(torch.nn.Linear(list[self.layout - 1], _output))
        # self.modelSize用于储存模型的总层数 ,(输入层-隐藏层1-输出层)算2层
        self.modelSize = len(self.model)
        # 使用self.parameters来记录每一层的偏置和权重,用于反向传播
        for i in range(0, self.modelSize):
            self.parameters.append(self.model[i].weight)
            self.parameters.append(self.model[i].bias)
            
    def forward(self, x):
        for i in range(0, self.modelSize):
            x = self.model[i](x)
            if i < self.modelSize - 1:
                x = F.relu(x)
                # x = self.LReLU(x)
        return x        
        
    def LReLU(self, x):
        size = x.shape
        k = 1
        for i in size:
            k = k * i
        x = x.view(k)
        for i in x:
            i = i if i > 0 else 0.01 * i
        return x.view(size)
 
    __call__: Callable[..., Any] = forward

建立训练器

我们在构造函数中传入上面建立好的模型,将模型中需要训练的参数传入SGD优化器,并设置学习率,设置损失函数为MSE均方误差。
将SGD的功能都封装一下,方便调用(zero_grad/step)。
最后,我们需要添加一个归一化方法(很重要),如果不加归一化,当输入数据十分大并且存在负数时,经过几层网络的非线性层之后,一个非常大的输入很有可能直接就变成0或者一个很小的数字,导致我们所需要的特征直接丢失了,最后使网络发散。(个人理解,如果不对,还请指正)
因此我们将输入的数据线性变换到了[0-1]范围内了,为了方便使用,我们还定义了一个predict函数,直接调用predict函数就可以得到反归一化之后的结果了。

class multiTrain():
    def __init__(self, multiLayer: multiLayer) -> None:
        self.model = multiLayer
        self.opt = torch.optim.SGD(self.model.parameters, lr=1e-2)
        self.loss = torch.nn.MSELoss()

    def predict(self, x):
        return self.d_normalize(self.model(x))

    def zero_grad(self):
        # for i in range(0, len(self.opt)):
        self.opt.zero_grad()
    
    def step(self):
        # for i in range(0, len(self.opt)):
        self.opt.step()     

    def normalize(self, x):
        self.dataMax = x.max()
        self.dataMin = x.min()
        return (x - self.dataMin) / (self.dataMax - self.dataMin)

    def d_normalize(self, x):
        return x * (self.dataMax - self.dataMin) + self.dataMin

训练

模型和训练器都搭好之后我们,接下来就是训练过程。
我们模拟了一个y = 3.24 * x ^ 2 - 4.21112的函数。

#初始化数据
x1 = torch.tensor(np.linspace(-10,20,1000), dtype=torch.float32).view(1000,1)
x2 = torch.tensor(np.linspace(-20,40,500), dtype=torch.float32).view(500,1)
yreal = (x1 * 3.24) * x1 - 4.21112 #+ torch.randn(x1.size())

T = multiTrain(multiLayer(1, 1, list = [10,10,10,10,10,10,10,10]))
yreal = T.normalize(yreal)
for i in range(50000):

    y = T.model(x1)
    loss = T.loss(yreal, y)
    T.zero_grad()
    loss.backward()
    T.step()
    # 打印
    if i % 100 == 0:
        pass

整体代码

from turtle import forward
from typing import Any, Callable
import torch
import numpy as np
import time
import torch.nn.functional as F
import matplotlib.pyplot as plt
import math


#这是一个能够拟合任意函数的模型框架


# class linearModel(torch.nn.Module):
#     def __init__(self, _input = 1, _output = 1):
#         super().__init__()
#         self.model = torch.nn.Linear(_input, _output)

#     def forward(self, x):
#         return self.model(x)


# class Train():
#     def __init__(self, model) -> None:
#         self.model = model
#         self.opt = torch.optim.SGD(model.parameters(), lr=1e-3)
#         self.loss = torch.nn.MSELoss()

class multiLayer(torch.nn.Module):
    def __init__(self, _input=1, _output=1, list = []):
        self.layout = len(list)
        self.parameters = []
        if self.layout == 0:
            self.model = [torch.nn.Linear(_input, _output)]
        else:
            for i in range(0, self.layout):
                if i == 0:
                    self.model = [torch.nn.Linear(_input, list[0])]
                else:
                    self.model.append(torch.nn.Linear(list[i - 1], list[i]))
            self.model.append(torch.nn.Linear(list[self.layout - 1], _output))
        self.modelSize = len(self.model)
        for i in range(0, self.modelSize):
            self.parameters.append(self.model[i].weight)
            self.parameters.append(self.model[i].bias)
        
    def LReLU(self, x):
        size = x.shape
        k = 1
        for i in size:
            k = k * i
        x = x.view(k)
        for i in x:
            i = i if i > 0 else 0.01 * i
        return x.view(size)
    
    def forward(self, x):
        for i in range(0, self.modelSize):
            x = self.model[i](x)
            if i < self.modelSize - 1:
                x = F.relu(x)
                # x = self.LReLU(x)
        return x
    
    __call__: Callable[..., Any] = forward

class multiTrain():
    def __init__(self, multiLayer: multiLayer) -> None:
        self.model = multiLayer
        self.opt = torch.optim.SGD(self.model.parameters, lr=1e-2)
        # for i in range(0, self.model.modelSize):
        #     self.opt.append(torch.optim.SGD(self.model.model[i].parameters(), lr=1e-4))
        self.loss = torch.nn.MSELoss()

    def predict(self, x):
        return self.d_normalize(self.model(x))

    def zero_grad(self):
        # for i in range(0, len(self.opt)):
        self.opt.zero_grad()
    
    def step(self):
        # for i in range(0, len(self.opt)):
        self.opt.step()     

    def normalize(self, x):
        self.dataMax = x.max()
        self.dataMin = x.min()
        return (x - self.dataMin) / (self.dataMax - self.dataMin)

    def d_normalize(self, x):
        return x * (self.dataMax - self.dataMin) + self.dataMin

#初始化数据
x1 = torch.tensor(np.linspace(-10,20,1000), dtype=torch.float32).view(1000,1)
x2 = torch.tensor(np.linspace(-20,40,500), dtype=torch.float32).view(500,1)
x3 = torch.tensor(np.linspace(-10, 20, 30), dtype=torch.float32).view(30,1)
yreal = (x1 * 3.24) * x1 - 4.21112 #+ torch.randn(x1.size())
y3 = (x3 * 3.24) * x3 - 4.21112 #+ torch.randn(x1.size())
T = multiTrain(multiLayer(1, 1, list = [10,10,10,10,10,10,10,10]))
yreal = T.normalize(yreal)
for i in range(50000):

    y = T.model(x1)
    loss = T.loss(yreal, y)
    T.zero_grad()
    loss.backward()
    T.step()
    # 打印
    if i % 100 == 0:
        pass
        # print('grad w %f and r is %f' % (T.model.model.weight, T.model.model.bias))
    # 跳出条件
    # if not (abs(wt.grad) > 1e-3 or abs(bt.grad) > 1e-3) :
    #     break 
y = T.predict(x1) 
y2 = T.predict(x2) 
yreal = T.d_normalize(yreal)
plt.plot(x1.data.numpy(),y.data.numpy(), x2.data.numpy(), y2.data.numpy())
plt.scatter(x3.data.numpy(), y3.data.numpy())
plt.show()

结果

可以看到-10到20的训练数据区,曲线拟合的还不错,而-20到-10,以及20到40的未训练区则与实际情况相差较大,并且每次训练的结果都是不一样的。
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值