深度学习实战(一)——简单线性回归任务

项目背景

每一个数据点(一组数据)都有四种特征x1,x2,x3,x4,分别表示外貌,性格,财富,内涵,每一个特征都有一个对应的权重w1,w2,w3,w4,经过模型计算得到一个输出y^,即恋爱次数

具体代码实现

导包

import torch  # 导入PyTorch库,用于构建和训练神经网络
import matplotlib.pyplot as plt  # 导入matplotlib的pyplot模块,用于绘图
import random  # 导入random模块,用于生成随机数

创建随机的模拟实际的数据x,y

# 创建模拟数据x,y的函数
def create_data(w, b, data_num):
    """
    生成带有噪声的模拟数据。

    参数:
    - w: 权重向量
    - b: 偏置
    - data_num: 随机数的组数,即数据点的数量

    返回:
    - x: 输入数据张量,服从均值为0,标准差为1的正态分布
    - y: 输出数据张量,通过线性变换计算得到,并加上噪声
    """
    # 使用 torch.normal 生成服从正态分布的随机数据,均值为0,标准差为1,形状为 (data_num, len(w))
    # 这里是二维的,所以是二维矩阵(张量)500*4,len(w)为w的维度=x列数
    x = torch.normal(0, 1, (data_num, len(w)))
    # 计算输出数据 y,通过矩阵乘法和偏置项,matmul是做矩阵乘法的函数
    y = torch.matmul(x, w) + b
    # 生成噪声,形状与 y 相同
    noise = torch.normal(0, 0.01, y.shape)
    # 将噪声添加到 y 中
    y += noise
    return x, y

# 设500组数据,每一组都有x1,x2,x3,x4
num = 500

# true_w 和 true_b 真实权重和偏置,用于生成模拟数据,其中w为四维向量
# 将数据转换为张量(Tensor)的主要原因是为了与 PyTorch 中的操作和函数兼容
true_w = torch.tensor([8.1,2,2,4])
true_b = torch.tensor(1.1)

# 将数据代入函数,生成模拟数据
X,Y = create_data(true_w,true_b,num)    # (500,4) (500,1)

# 用 Matplotlib 库中的 scatter 函数将生成的数据点以散点图的形式可视化出来。这里 X 是输入数据,Y 是对应的输出数据,1 是指定散点的大小。
plt.scatter(X[:, 0],Y,1)    #这里x只取第一列,如果直接(X,Y,1) 则会因为X Y形状不相同报错
plt.show()

创建数据提供器,小批量提供训练数据

data_provider是一个生成器(generator),它的作用是批量地提供训练数据,在训练过程中,通常不会一次性使用所有的数据,而是将数据分成多个小批量(batch),这样可以更有效地利用内存,同时也可以提供梯度下降算法的稳定性

# 数据提供器函数,用于小批量提供训练数据,每次访问,就提供一笔数据
# data:输入数据x,label:输出数据y(实际的标签),batchsize:每批提供的数据大小
def data_provider(data, label, batchsize):
    # 标签长度 (500)
    length = len(label)
    # 创建一个索引列表,包含从0到label长度减1的所有整数。这个列表用于在数据集中进行索引。
    indieces = list(range(length))
    # 使用random.shuffle函数随机打乱索引列表。这样做可以确保数据在每个epoch中都是随机的,有助于模型的泛化能力。
    random.shuffle(indieces)
    # 开始一个循环,从0开始,以batchsize为步长,直到遍历完整个label列表。这个循环用于分批次地处理数据
    for each in range(0, length, batchsize):
        # 创建了一个名为get_indices的新列表,它通过切片操作从indices列表中获取当前批次的索引。
        # each是当前批次的起始索引,each + batchsize是当前批次的结束索引。这样,get_indices就包含了从indices列表中为当前批次选择的索引。
        get_indices = indieces[each : each + batchsize]
        # 从输入数据data中提取对应的数据子集。因为data是一个二维张量,所以会选择相应的行,从而得到一个新的张量get_data,它包含了当前批次的所有输入数据
        get_data = data[get_indices]
        # 同上,从标签label中提取当前批次的标签
        get_label = label[get_indices]
        # yield是有存档点的return,取下一批数据时是接着上一批数据取的
        yield get_data, get_label

# 共500组数据,但是每批只取16组数据训练(16个数据点),16组数据得到一个loss
batchsize = 16

# 每次在数据生成器中取当前批次的输入数据 batch_x(16*4)及标签 batch_y(16*1)并打印
for batch_x, batch_y in data_provider(X, Y, batchsize):
    print(batch_x, batch_y)
    # 循环在打印出第一个批次的数据后就会停止,实际的训练过程中,我们通常希望移除 break 语句,以便让循环继续执行,直到遍历完所有的数据批次。
    break

定义线性回归模型模型(预测函数)

# 定义一个函数(线性回归模型),用于生成预测值,拟合真实数据
def fun(x, w, b):
    # 矩阵乘法函数 torch.matmul 来计算线性变换 x * w, 然后加上偏置 b 得到预测值 pred_y
    pred_y = torch.matmul(x, w) + b
    return pred_y

定义损失函数loss

这里我们使用平均绝对误差(MAE)作为损失函数

# loss函数,这里使用平均绝对误差(MAE)作为损失函数
def maeLoss(pred_y, y):
    # torch.abs 函数计算pred_y 和真实值 y 之间的逐元素差的绝对值,使用 torch.sum 函数对所有数据点的绝对误差进行求和
    # 最后,将总误差除以数据点的数量 len(y),这里是16。这样做的目的是将误差总和平均化,得到单个数据点的平均误差
    loss = torch.sum(abs(pred_y - y)) / len(y)  
    return loss

定义梯度下降函数,更新参数

# 定义梯度下降函数,用于更新模型参数
def sgd(paras, lr):
    # 禁用梯度计算,因为在参数更新时不需要计算梯度。
    # 梯度计算是在梯度下降的第二步梯度回传时计算的,这里只是更新参数,不用再次计算浪费资源
    with torch.no_grad():
        # 遍历所有参数
        for para in paras:
            # 使用梯度下降更新参数:当前参数减去梯度乘以学习率
            para -= para.grad*lr
            # 清零梯度,为下一次梯度计算做准备
            para.grad.zero_()

初始化模型参数w0,b0

# 学习率(超参数)
lr = 0.03

# 初始化模型参数(随机的),后续需要再次基础上更新
# true_w.shape指定了这个张量的形状,它应该与真实权重true_w的形状相同
w_0 = torch.normal(0, 0.001, true_w.shape, requires_grad=True)
# requires_grad=True:这个参数告诉PyTorch,我们需要计算这个张量在后续计算中的梯度
# 在初始化偏置b时,通常不需要从某个分布中随机采样,而是直接赋予一个固定的较小初始值,故不用normal
b_0 = torch.tensor(0.001, requires_grad=True)
print(w_0, b_0)

训练模型

# 设置训练轮数
epochs = 50

# 训练50轮,每一轮都会遍历所有批次(两个for循环)
for epoch in range(epochs):
    # 初始化每个epoch的累积损失,方便我们观察每一轮loss的变化
    data_loss = 0
    # 遍历数据提供器生成的数据批次
    for batch_x, batch_y in data_provider(X, Y, batchsize):
        # 使用模型进行预测
        pred = fun(batch_x, w_0, b_0)
        # 计算损失
        loss = maeLoss(pred, batch_y)
        # 反向传播,计算梯度(具体求偏导的代码不用管,交给torch)
        loss.backward()
        # 更新参数
        sgd([w_0,b_0], lr)
        # 累加损失
        data_loss += loss
        # 打印每个epoch的损失
        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(),label="pred")
# 绘制真实数据点
plt.scatter(X[:, idx], Y, 1, label="real")
# 显示图表
plt.show()

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值