[深度学习]神经网络线性回归简易实例

线性回归简易实例

文章目录

导入模块

python 复制代码
import torch
import matplotlib.pyplot as plt #画图

import random #随机
  • torch库帮助进行梯度计算和参数更新,实现训练过程(随机梯度下降、损失函数的计算等),提供了高效的张量计算和自动求导功能,使得训练机器学习模型更加简便和高效。
  • matplotlib.pyplot库用于绘制图像,使运行结果可视化。
  • random的作用是引入随机性。

所模拟的模型

假设数据符合一个如下图的线性回归模型:

  • 输入数据x有四个维度。分别是外貌、性格、财富、内涵。
  • 输出数据y为恋爱次数。
  • w的四个维度为x的真实权值分为为8.1,2,2,4。分别对应外貌、性格、财富、内涵。
  • b的真实值为1.1。

神经网络的目的就是使用随机的w和b通过深度学习的方法找出真实的w和b的值。

生成数据

python 复制代码
x = torch.normal(0, 1, (data_num, len(w)))
y = torch.matmul(x, w) + b #矩阵相乘

从正态分布中随机生成平均数为0,方差为1,数量为data_num,维度为len(w)的数据集x。对生成的数据集x与真实的w进行矩阵相乘加上真实的b得到一个符合假设模型的数据集。

计算过程如下图:

python 复制代码
noise = torch.normal(0, 0.01, y.shape) #噪声加到y上
y += noise

由于实际情况下,真实的数据不会完全符合模型,因此加入一些噪声更好的模拟真实的数据情况。

完整的生成数据代码:

python 复制代码
def create_data(w, b, data_num): #生成数据
    x = torch.normal(0, 1, (data_num, len(w)))
    y = torch.matmul(x, w) + b #矩阵相乘

    noise = torch.normal(0, 0.01, y.shape) #噪声加到y上
    y += noise

    return x, y

num = 500 #生成的输入数据个数为500个

true_w = torch.tensor([8.1, 2, 2, 4]) #设定真实的参数用于生成数据
true_b = torch.tensor(1.1)

X, Y = create_data(true_w, true_b, num)

plt.scatter(X[:, 0], Y, 1) #打印输入数据第一列和输出数据的关系
plt.show()

结果如下:

获取数据

由于实际情况下,数据是收集得到的,因此实际的第一步操作是要获取数据。

python 复制代码
def data_provider(data, label, batchsize):  #每次访问这个函数,就能提供一批数据
    length = len(label)
    indices = list(range(length))
    #把数据打乱,让数据具有普适性
    random.shuffle(indices)

    for each in range(0, length, batchsize):
        get_indices = indices[each: each+batchsize] #每次取batchsieze个数据
        get_data = data[get_indices]
        get_label = label[get_indices]

        yield get_data, get_label #有存档点的return


batchsize = 16
  • data参数为输入数据集。

  • label参数为输出数据集。

  • 使用 random.shuffle 随机打乱 indices 列表中的元素顺序,这样每次训练时访问的数据顺序都不会固定,从而避免模型在训练过程中出现过拟合。

  • yield get_data, get_label 语句的作用是将当前批次的数据(get_data)和标签(get_label)返回给调用方,并且使得函数的执行暂停,待下一次调用时继续从当前位置执行。这使得 data_provider 函数成为一个生成器。具体来说过程如下:

    第一次调用 yield 后的执行流程

    1. 当你第一次迭代 data_provider(data, label, batchsize) 时:
      • 生成器函数从头开始执行。
      • 执行到 for 循环的第一步,获取当前批次的数据(get_data)和标签(get_label)。
      • 然后执行 yield get_data, get_label,这时函数暂停并返回 get_dataget_label
    2. 函数的状态
      • 此时,生成器函数的执行暂停,所有局部变量(如 indices, get_indices, get_data, get_label)都被保存下来,以便下一次继续使用。
      • 程序控制流回到调用 for 循环的地方,返回数据。

    第二次及以后调用时函数后的执行流程

    1. 恢复执行 :生成器函数不会从头开始执行,而是从上次暂停的地方继续执行。也就是说,函数会从上次执行 yield 后的地方继续(在本例中是从 for 循环的下一个迭代开始,因为每循环一次,就遇到yield进行了暂停)。
    2. 继续执行
      • 在上次暂停后,生成器已经完成了 yield get_data, get_label,此时函数的执行状态被保留。
      • 生成器函数继续执行下一个批次的数据生成,并再次遇到 yield
      • 返回下一个批次的 get_dataget_label
    3. 暂停并返回数据 :每次执行到 yield 时,函数会暂停并返回相应的批次数据,直到下一次调用。
  • batchsize设定每次所取的数据个数。

定义模型

python 复制代码
def fun(x, w, b): #模型
    pred_y = torch.matmul(x, w) + b
    return pred_y

定义线性回归模型,使用该模型拟合数据的关系,获取预测值。

公式如下图:

定义LOSS

python 复制代码
def maeLoss(pre_y, y):
    return torch.sum(abs(pre_y-y))/len(y)

定义LOSS函数,用于计算预测值和真实数据的误差。

公式如下图:

使用模型拟合出真实参数

实现梯度下降函数,用于更新参数

梯度下降,更新参数的方法如下图:

首先,设置一个初始的权重 w 0 w^0 w0​(w的第0个版本),然后计算损失函数 L(衡量模型预测结果与真实结果之间差异的函数),然后计算损失函数相对于参数的梯度。梯度表示了损失函数在某个点的斜率,指示了在当前参数下,损失函数增加或减少的方向和大小。例如,对于权重 w 0 w^0 w0,其梯度是损失函数对权重的导数。然后使用如下公式更新参数:

知道参数的损失函数值符合我们的需求。其中学习率learning rate决定了优化算法(如梯度下降)在每一步中调整模型权重的幅度。所有参数包括偏置在梯度下降过程中操作相当。

梯度下降代码的具体代码实现

python 复制代码
def sgd(paras, lr):    #随机梯度下降,更新参数
    with torch.no_grad():  #属于这句代码的部分,不计算梯度
        for para in paras:
            para -= para.grad*lr #不能写成para = para - para.grad*lr
            para.grad.zero_() #使用过的梯度,归零


lr = 0.03 #学习度
w_0 = torch.normal(0, 0.01, true_w.shape, requires_grad=True)  #这个需要计算梯度
b_0 = torch.tensor(0.01, requires_grad=True)
  • with torch.no_grad() 表示在此代码块中的操作不会计算梯度,防止在执行参数更新操作时计算不必要的梯度。因为推理参数阶段,不需要计算梯度,只进行前向传播来获取输出,只有在进行优化更新参数时需要使用梯度。
  • para.grad.zero_()用来将当前参数的梯度归零的。因为在PyTorch中,每次反向传播时,梯度会默认被累加。为了防止梯度不断累加导致的错误,我们需要在每次更新参数之后,将这些梯度清零。
  • lr = 0.03,设置学习度
  • w_0 = torch.normal(0, 0.01, true_w.shape, requires_grad=True)来从正态分布中随机初始化权重,其中:0是均值,0.01是标准差,true_w.shape表示权重 的形状和真实权重 true_w 相同,requires_grad=True表示该张量需要计算梯度。
  • b_0 = torch.tensor(0.01, requires_grad=True)定义了初始的偏置 b_0。它使用 torch.tensor() 创建了一个数值为 0.01 的标量,并且同样设置了 requires_grad=True,表示这个值需要计算梯度。

训练函数

使用定义的模型和LOSS函数,进行训练,得到参数,具体代码如下:

python 复制代码
epochs = 50 #训练的次数


for epoch in range(epochs):
    data_loss = 0
    for batch_x, batch_y in data_provider(X, Y, batchsize):
        pred_y = fun(batch_x, w_0, b_0)
        loss = maeLoss(pred_y, batch_y)
        loss.backward()
        sgd([w_0, b_0], lr)
        data_loss += loss

    print("epoch %03d: loss: %.6f"%(epoch, data_loss)) #打印训练次数,以及误差

print("真实的函数值是", true_w, true_b)
print("训练得到的参数值是", w_0, b_0)

idx = 0
plt.plot(X[:, idx].detach().numpy(), X[:, idx].detach().numpy()*w_0[idx].detach().numpy()+b_0.detach().numpy())
plt.scatter(X[:, idx], Y, 1) #用来选择第idx个特征(即 X[:, idx])来绘制模型的预测结果和真实数据。
plt.show() #打印
  • epochs = 50设定训练的次数。
  • for batch_x, batch_y in data_provider(X, Y, batchsize):使用data_provider获取batchsize个数的数据。
  • pred_y = fun(batch_x, w_0, b_0),使用当前的权重偏置计算出预测的输出值。
  • loss = maeLoss(pred_y, batch_y),计算真实值和预测值的误差。
  • loss.backward() 是用来执行反向传播过程的,它会根据损失函数 loss 计算所有可训练参数(如 w_0b_0)的梯度。。
  • sgd([w_0, b_0], lr)这是一个基于 随机梯度下降(SGD) 的优化步骤,更新模型参数。sgd 函数根据计算出的梯度更新 w_0b_0 的值,学习率由 lr 指定。具体来说,sgd 会使用每个参数的梯度来调整它们的值,从而最小化损失函数。

输出结果如下图:

完整代码

python 复制代码
import torch
import matplotlib.pyplot as plt #画图

import random #随机


def create_data(w, b, data_num): #生成数据
    x = torch.normal(0, 1, (data_num, len(w)))
    y = torch.matmul(x, w) + b #矩阵相乘

    noise = torch.normal(0, 0.01, y.shape) #噪声加到y上
    y += noise

    return x, y

num = 500

true_w = torch.tensor([8.1, 2, 2, 4])
true_b = torch.tensor(1.1)

X, Y = create_data(true_w, true_b, num)

# plt.scatter(X[:, 0], Y, 1)
# plt.show()


def data_provider(data, label, batchsize):  #每次访问这个函数,就能提供一批数据
    length = len(label)
    indices = list(range(length))
    #把数据打乱,让数据具有普适性
    random.shuffle(indices)

    for each in range(0, length, batchsize):
        get_indices = indices[each: each+batchsize] #每次取batchsieze个数据
        get_data = data[get_indices]
        get_label = label[get_indices]

        yield get_data, get_label #有存档点的return


batchsize = 16


# for batch_x, batch_y in data_provider(X, Y, batchsize):
#      print(batch_x, batch_y)


def fun(x, w, b): #模型
    pred_y = torch.matmul(x, w) + b
    return pred_y


def maeLoss(pre_y, y):
    return torch.sum(abs(pre_y-y))/len(y)


def sgd(paras, lr):    #随机梯度下降,更新参数
    with torch.no_grad():  #属于这句代码的部分,不计算梯度
        for para in paras:
            para -= para.grad*lr #不能写成para = para - para.grad*lr
            para.grad.zero_() #使用过的梯度,归零


lr = 0.03
w_0 = torch.normal(0, 0.01, true_w.shape, requires_grad=True)  #这个需要计算梯度
b_0 = torch.tensor(0.01, requires_grad=True)
# print(w_0, b_0)


epochs = 50


for epoch in range(epochs):
    data_loss = 0
    for batch_x, batch_y in data_provider(X, Y, batchsize):
        pred_y = fun(batch_x, w_0, b_0)
        loss = maeLoss(pred_y, batch_y)
        loss.backward()
        sgd([w_0, b_0], lr)
        data_loss += loss

    print("epoch %03d: loss: %.6f"%(epoch, data_loss))

print("真实的函数值是", true_w, true_b)
print("训练得到的参数值是", w_0, b_0)

idx = 0
plt.plot(X[:, idx].detach().numpy(), X[:, idx].detach().numpy()*w_0[idx].detach().numpy()+b_0.detach().numpy())
plt.scatter(X[:, idx], Y, 1)
plt.show()
相关推荐
yzx9910134 分钟前
Python根据图片生成学生excel成绩表
python·mysql·excel
深图智能6 分钟前
PyTorch使用教程(4)-torch.nn
人工智能·pytorch·深度学习
smartcat201011 分钟前
Lora理解&QLoRA
深度学习
Channing Lewis17 分钟前
Python 3.9及以上版本支持的新的字符串函数 str.removeprefix()
服务器·python
一语雨在生无可恋敲代码~27 分钟前
wandb使用遇到的一些问题
pytorch·计算机视觉
唐BiuBiu1 小时前
python如何解析word文件格式(.docx)
python·word
测试秃头怪1 小时前
银行测试:第三方支付平台业务流,功能/性能/安全测试方法
自动化测试·软件测试·python·功能测试·测试工具·测试用例·安全性测试
dwjf3212 小时前
神经网络基础-正则化方法
人工智能·深度学习·神经网络
清弦墨客2 小时前
【蓝桥杯】43689.包子凑数
python·蓝桥杯·编程算法
被放养的研究生2 小时前
神经网络常见操作(卷积)输入输出
深度学习·神经网络·机器学习