李哥深度学习第二节 简单线性的实现

import torch  # PyTorch深度学习框架
import matplotlib.pyplot as plt   #  画图的
import random  # 生成随机数



# 生成模拟数据的函数。也就是实现f(xw+b);之后再在外面套上逻辑回归的外函数
def create_data(w, b, data_num):  # 生成数据
    """
    生成带噪声的线性回归数据
    参数:
        w: 权重向量,形状为(n_features,)
        b: 偏置项,标量
        data_num: 生成样本数量
    返回:
        x: 特征矩阵,形状为(data_num, n_features)
        y: 目标值,形状为(data_num,)
    """
    # 生成服从标准正态分布(均值为0,标准差1)的样本的特征矩阵,形状(横纵维度)为(样本数data_num,特征数len(w))
    x = torch.normal(0, 1, (data_num, len(w)))
    # 计算线性回归目标值(矩阵乘法实现批量计算);先生成f(xw+b)
    y = torch.matmul(x, w) + b    # matmul表示矩阵相乘,这个就是神经网络矩阵相乘的公式
    # 添加高斯噪声(均值为0,标准差0.01),形状与y相同。模拟真实世界场景(真实一般满足正态分布),防止过拟合
    noise = torch.normal(0, 0.01, y.shape)  # 噪声要加到y上,添加到目标值。这是因为标准线性回归是y = Xβ + ε
    # 当噪声在y上时,模型学习目标是找到最能抵抗这些观测噪声的权重参数。如果噪声在X上,问题会转变为误差在变量模型(Errors-in-variables),需要不同的处理方法。
    y += noise

    return x, y



# 设置生成参数。并实例化上面定义的线性回归运算
num = 500   # 样本数量
# 因为torch是对张量操作,而不是序列,所以下面要用tensor进行操作
true_w = torch.tensor([8.1, 2, 2, 4])  # 真实的权重参数(四维特征)
true_b = torch.tensor(1.1)         # 真实的偏置参数



# 生成数据实例化.也就是进行线性回归
X, Y = create_data(true_w, true_b, num)




# 可视化第4个特征(索引3)与目标值的关系
'''
plt.scatter(
    X[:, 3],  # x轴数据:所有样本的第4个特征(索引3)
    Y,        # y轴数据:所有样本的目标值
    1         # s=1:散点的大小(单位:像素)
)
'''
plt.scatter(X[:, 3], Y, 1)  # 参数1表示点的大小
plt.xlabel('Feature 3')      # x轴标签
plt.ylabel('Target')        # y轴标签
plt.show()                  # 显示图形


# 关于data_provider
'''
它的主要功能是将数据集分成小批次,便于训练时逐步处理,避免一次性加载所有数据导致内存不足。
打乱索引是为了让每个epoch的数据顺序不同,增加模型的泛化能力。生成器的使用可以节省内存,特别是在处理大数据集时。
用户可能不太明白为什么需要这样的函数,或者想确认它的正确性。需要指出,虽然这个自定义函数可行,
但通常推荐使用PyTorch内置的DataLoader,因为它更高效且功能更全面,比如支持多线程数据加载等。

此外,用户可能在训练循环中调用了这个函数,所以需要说明在训练过程中如何逐批次获取数据,并更新模型参数。
还要提到,使用生成器的好处是可以惰性加载数据,不会一次性占用太多内存。

最后,要确保解释清晰,涵盖函数的关键步骤:打乱数据、分批处理、生成器返回。同时,对比标准做法,
指出用户自定义实现的优缺点,帮助用户理解为什么有时候需要自己写这样的函数,或者建议使用库函数提升效率。
'''

# 将数据集分成小批次,便于训练时逐步处理
def data_provider(data, label, batchsize):       # 每次访问这个函数, 就能提供一批数据
    """自定义数据加载器
    参数格式:
        data: 特征数据张量
        label: 标签数据张量
        batchsize: 批次大小
    返回:
        生成器,每次产生一个批次的(特征,标签)
    """
    length = len(label)  # 获取总样本数
    indices = list(range(length))  # 创建顺序索引列表 [0,1,2,...n-1]
    # 不能按顺序取,这样才符合现实世界,防止模型学习到顺序偏差。  把数据打乱。打乱索引顺序(随机化数据)
    random.shuffle(indices)

    # 按批次大小遍历数据。批次大小为batchsize,舍去最后不符合批次大小的。
    for each in range(0, length, batchsize):
        # 获取当前批次的索引(防止越界)
        get_indices = indices[each: each + batchsize]
        # 通过索引获取对应数据及标签
        get_data = data[get_indices]   # 形状:[batchsize, n_features]
        get_label = label[get_indices] # 形状:[batchsize]
        # 使用yield生成器返回批次数据(保持内存高效),其实就是便于更新参数使用。使用生成器( yield )避免一次性加载全部数据
        yield get_data, get_label



# 设置训练参数
batchsize = 16  # 每个批次的样本数
# for batch_x, batch_y in data_provider(X, Y, batchsize):
#     print(batch_x, batch_y)
#     break



# 神经网络前向传播计算函数,其实也就是矩阵计算。
def fun(x, w, b):
    """线性回归前向计算函数
        参数:
            x: 输入特征
            w: 权重参数
            b: 偏置参数
        返回:
            预测值 = x*w + b
        """
    pred_y = torch.matmul(x, w) + b
    return pred_y


# 计算损失函数,我们的目标是让损失最小,所以后面要对其求梯度
def maeLoss(pre_y, y):
    """平均绝对误差损失函数
     参数:
         pre_y: 预测值
         y: 真实值
     返回:
         平均绝对误差 = Σ|预测-真实| / 样本数
     """
    return torch.sum(abs(pre_y-y))/len(y)

# 反向传播实现
# 使用loss.backward()方法计算损失的结果loss是一个张量

# 随机梯度下降优化器,作用是更新参数。在反向传播的时候使用。
def sgd(paras, lr):
    """随机梯度下降优化器
    参数:
        paras: 需要更新的参数列表
        lr: 学习率
    """
    # 禁用梯度计算(节省内存)
    with torch.no_grad():  # 属于这句代码的部分,不计算梯度
        for para in paras:
            # 参数更新:新参数 = 旧参数 - 学习率 * 梯度
            para -= para.grad * lr      #不能写成   para = para - para.grad*lr
            # 清空梯度(防止累积)
            para.grad.zero_()      #使用过的梯度,归0
            #没有返回值

# 初始化模型参数
lr = 0.03  # 学习率
# 随机初始化权重(启用梯度计算);
w_0 = torch.normal(0, 0.01, true_w.shape, requires_grad=True)  # 这个w要计算梯度
# 初始化偏置(启用梯度计算)
b_0 = torch.tensor(0.01, requires_grad=True)
print(w_0, b_0)


# 训练循环,并取最小损失
epochs = 50  # 训练轮数50次
for epoch in range(epochs):
    data_loss = 0  # 累计损失

    # 每一轮的训练方式是按批次训练,每次训练调用自定义的data_provider小批量训练函数。积累每一轮的损失并更新至取最小值
    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

    # 每一轮训练结束,打印训练进度(格式化输出3位epoch编号(第几次训练),6位小数损失)
    print("epoch %03d: loss: %.6f" % (epoch, data_loss))


# 输出最终训练结果
print("真实的函数值是", true_w, true_b)
print("训练得到的参数值是", w_0, b_0)

# 可视化第4个特征的拟合结果
idx = 3  # 选择第4个特征(索引3)
# 绘制拟合直线:y = w*x + b
# 将PyTorch张量转为numpy数组.Matplotlib 的绘图函数是基于 NumPy 数组设计的,PyTorch 张量需要先转换为 NumPy 数组才能被正确解析。
plt.plot(X[:, idx].detach().numpy(),  # x轴值(需要从张量转换为numpy)
         X[:, idx].detach().numpy()*w_0[idx].detach().numpy()+b_0.detach().numpy(),
         color='red')  # 拟合直线
# 绘制原始数据散点图
plt.scatter(X[:, idx], Y, 1)  # 参数1表示点的大小
plt.xlabel('Feature 3')      # x轴标签
plt.ylabel('Target')        # y轴标签
plt.show()                  # 显示图形

### 关于深度学习入门教程和资源 #### 深度学习基础概念的学习 对于希望进入深度学习领域的新手来说,理解其背后的核心理论至关重要。这不仅涉及神经网络的工作原理,还包括如何训练这些模型以及它们的应用场景[^1]。 #### 使用PyTorch进行深度学习实践 作为一种流行的深度学习框架,PyTorch因其灵活性和易用性而受到广泛欢迎。通过一系列精心设计的教学材料和支持文档,即使是初学者也能快速上手并构建自己的项目。具体而言,官方提供了详细的指南来帮助用户掌握从数据预处理到模型评估的各项技能[^2]。 ```python import torch from torchvision import datasets, transforms transform = transforms.Compose([transforms.ToTensor()]) trainset = datasets.MNIST('~/.pytorch/MNIST_data/', download=True, train=True, transform=transform) trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True) dataiter = iter(trainloader) images, labels = dataiter.next() print(images.shape) ``` 这段简单Python代码展示了如何利用`torchvision.datasets`模块加载MNIST手写数字识别数据集,并对其进行初步探索。 #### 音频合成方向的特殊工具和技术 针对音乐创作方面的需求,存在专门面向音频信号处理与分析的技术栈。例如,在生成旋律线条时可以借助像Musicore这样的开放源码平台;而在后期制作过程中,则可采用SoX完成文件格式转换及参数调整等工作流操作[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Verse.Qing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值