当前位置: 首页 > article >正文

跟着李沐老师学习深度学习(二)

线性回归 + 基础优化算法

课程视频:https://space.bilibili.com/1567748478/channel/seriesdetail?sid=358497
课程教材:https://zh.d2l.ai/

线性回归

了解线性回归前的例子

  • 在美国买房
    看中一个房子,进行参观了解 --> 估计一个价格(出价) —— 预测问题

引出线性回归—简化模型

  • 假设 1:影响房价的关键因素是卧室个数,卫生间个数和居住面积,记为 x1,x2,x3
  • 假设2:成交价是关键因素的加权和:y=WX+Wx+Wx+b
    权重和偏差的实际值在后面决定

线性模型

  • 给定n维输入:在这里插入图片描述
  • 线性模型有一个n维权重和一个标量偏差在这里插入图片描述
  • 输出是输入的加权和:

在这里插入图片描述

在这里插入图片描述

  • 向量版本:
    在这里插入图片描述
  • 线性模型可以看作是单层神经网络:
    在这里插入图片描述
  • 神经网络来源于神经科学
    主要思想:使用人工的神经网络来模拟人的大脑;

衡量预估质量

  • 比较真实值和预估值,例如房屋售价和估价;以下式子叫做平方损失。
    在这里插入图片描述

  • 训练数据

    • 收集一些数据点来决定参数值(权重和偏差),例如过去6个月卖的房子(称为训练数据
    • 假设有n个样本,记:
      在这里插入图片描述
  • 参数学习

    • 训练损失
      在这里插入图片描述
    • 最小化损失来学习参数
      在这里插入图片描述
  • 显示解

    • 将偏差加入权重 (输入加入一列全1;权重加入偏差)在这里插入图片描述
      在这里插入图片描述

    • 损失是凸函数,所以最优解满足:
      在这里插入图片描述
      (唯一具有最优解的模型)

总结

  • 线性回归是对n维输入的加权,外加偏差
  • 使用平方损失来衡量预测值和真实值的差异
  • 线性回归有显示解(最简单的模型)
  • 线性回归可以看做是单层神经网络

基础优化方法

梯度下降

(当模型中没有显示解时,使用优化方法)

  • 挑选一个初始值w0;重复迭代参数t = 1, 2, 3
    在这里插入图片描述

    • 沿梯度方向将增加损失函数值
    • 学习率:步长的超参数(需要认为指定)

选择学习率

不能太小、也不能太大;

分类:小批量随机梯度下降(常用)

类型定义优点缺点
批量梯度下降 (Batch Gradient Descent)每次迭代时,计算整个训练数据集的梯度。收敛性较好,最终能找到全局最小值(对于凸优化问题)。计算成本高,尤其是在数据量很大的时候,因为每次都需要用整个数据集来计算梯度。
随机梯度下降 (Stochastic Gradient Descent, SGD)每次迭代时,只用一个样本来计算梯度并更新参数。计算速度更快,因为每次只使用一个样本;适合大规模数据集。每次更新的方向波动较大,可能导致收敛速度慢或者最终收敛到局部最小值。
小批量梯度下降 (Mini - batch Gradient Descent)每次迭代时,用一个小批量的数据(例如32或64个样本)来计算梯度并更新参数。比批量梯度下降速度快,但又能比随机梯度下降更稳定,适用于深度学习中。需要根据批量大小进行调整,但比批量和随机梯度下降的结合要灵活。
  • 在整个训练集上算梯度太贵

    • 一个深度神经网络模型可能需要数分钟至数小时
  • 可以随机采样b个样本i1,i2,…,ib 来近似损失
    在这里插入图片描述

    • b是批量大小,另一个重要的超参数。
  • 对于批量大小的选择:

    • 不能太小:每次计算量太小,不适合并行来最大利用计算资源。
    • 不能太大:内存消耗增加浪费计算,例如如果所有样本都是相同的

总结

  • 梯度下降通过不断沿着反梯度方向更新参数求解
  • 小批量随机梯度下降是深度学习默认的求解算法(稳定、简单)
  • 两个重要的超参数是批量大小和学习率

代码实现 – 从零实现线性回归

# 1 线性回归的从零开始实现

# 从零开始实现整个方法:包括数据流水线、模型、损失函数和小批量随机梯度下降优化器

%matplotlib inline
import random
import torch
from d2l import torch as d2l

# 根据带有的噪声的线性模型构造一个人造的数据集。我们使用线性模型参数w = [2, -3.4],b = 4.2 还有噪声项及其标签

def synthetic_data(w, b, num_examples):
    """生成 y = Xw + b + 噪声。"""
    # 均值为0,方差为1的随机数,大小:有n个样本,共len(w)列
    X = torch.normal(0, 1, (num_examples, len(w)))
    # y = Xw + b
    y = torch.matmul(X, w) + b
    # 加入随机噪音:均值为0,方差为0.01的随机数
    y += torch.normal(0, 0.01, y.shape)
    
    return X, y.reshape((-1, 1))

true_w = torch.tensor([2, -3.4])
true_b = 4.2

# 调用synthetic_data函数,生成包含 1000 个样本的特征矩阵features和目标值向量labels。
features, labels = synthetic_data(true_w, true_b, 1000)

# 通过以上代码可以得到我们的训练样本

# features 中的每一行都包含一个二维数据样本,labels中的每一行都包含一维标签值(一个标量)

print('features', features[0], '\nlabel:', labels[0])

d2l.set_figsize()
# scatter函数绘制散点图,将特征矩阵features的第二列(索引为 1)作为横坐标,目标值向量labels作为纵坐标进行可视化展示。detach().numpy()用于将 PyTorch 张量转换为 NumPy 数组以便绘图。
d2l.plt.scatter(features[:, (1)].detach().numpy(),labels.detach().numpy(), 1);

# 定义一个data_iter函数,该函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size的小批量

def data_iter(batch_size, features, labels):
    # 获取样本的数量,即特征矩阵的行数
    num_examples = len(features)
    # 创建一个包含从0到num_examples - 1的整数列表,用于表示样本的索引
    indices = list(range(num_examples))
    # 这些样本是随机读取的,没有特定的顺序;(随机打乱样本的索引顺序)
    random.shuffle(indices)
    # 使用for循环遍历样本索引,步长为batch_size
    for i in range(0, num_examples, batch_size):
        # 从打乱后的索引列表中取出当前小批量的索引
        # min(i + batch_size, num_examples) 确保最后一个小批量的样本数量不会超过剩余样本数量
        batch_indices = torch.tensor(indices[i:min(i + batch_size, num_examples)])
        # 使用生成器的yield 关键之返回当前小批量的特征和标签
        # 生成器可以逐个返回值,而不是一次性返回所有值,节省内存
        yield features[batch_indices], labels[batch_indices]
        
# 设置批量大小为10
batch_size = 10

# 使用for循环从data_iter生成器中获取小批量数据
for X, y in data_iter(batch_size, features, labels):
    # 打印当前小批量的特征和标签
    print(X, '\n', y)
    break

# 定义初始化模型参数

w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)

# 定义模型
def linreg(X, w, b):
    """线性回归模型"""
    return torch.matmul(X, w) + b

# 定义损失函数

def squared_loss(y_hat, y):
    """均方损失"""
    return (y_hat - y.reshape(y_hat.shape))**2 / 2

# 定义优化算法
def sgd(params, lr, batch_size):
    """小批量随机梯度下降"""
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size
            # 将梯度设为0;不影响下一次的计算
            param.grad.zero_()

# 训练过程
# 学习率
lr = 0.001
# 迭代次数
num_epochs = 10
# 模型
net = linreg
# 损失函数
loss = squared_loss

for epoch in range(num_epochs):
    # 每次拿出一个批量大小为batch_size的 X和 y
    for X, y in data_iter(batch_size, features, labels):
        # 拿预测值net(X, w, b) 和真实值y作损失计算
        l = loss(net(X, w, b), y) # x 和 y 的小批量损失
        # 因为l 的形状是(batch_size, 1),而不是一个标量。
        # 并以此计算关于w b 的梯度
        l.sum().backward()
        sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数
        # 这里的批量可能会有问题,最后一个批量可能数量不足以一个批量大小;(这里给出的例子是正好)
    with torch.no_grad():
        train_l = loss(net(features, w, b), labels)
        print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')

# 比较真实参数和通过训练学到的参数来评估训练的成功程度

print(f'w的估计误差:{true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差:{true_b - b}')

代码实现 – 简洁实现

# 线性回归的简洁实现
# 使用深度学习框架进行简洁实现线性回归模型 生成训练数据

import numpy as np
import torch 
from torch.utils import data
from d2l import torch as d2l

true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)

# 当 is_train 为 True 时,表示处于训练阶段,数据会被随机打乱;当为 False 时,表示处于验证或测试阶段,数据不会被打乱。
def load_array(data_arrays, batch_size, is_train=True):
    """构造一个pytorch数据迭代器"""
    # TensorDataset用于将多个张量组合成一个数据集
    dataset = data.TensorDataset(*data_arrays)
    # DataLoader 会根据这些参数将数据集划分为多个批次,并在需要时提供迭代访问这些批次的功能。
    # 当 shuffle 为 True 时,每次迭代时数据会被随机打乱,有助于提高模型训练的泛化能力。
    return data.DataLoader(dataset, batch_size, shuffle=is_train)

batch_size = 10
data_iter = load_array((features, labels), batch_size)

next(iter(data_iter))

# 接下来是模型的定义
# 使用框架的预定义好的层
# nn 是神经网络的缩写
from torch import nn

net = nn.Sequential(nn.Linear(2, 1))


# 初始化模型参数
# net[0]代表:nn.Linear(2, 1);
# normal_(0, 0.01):将张量中的每个元素用从指定正态分布中采样得到的值进行替换; 0 是正态分布的均值,0.01 是正态分布的标准差
net[0].weight.data.normal_(0, 0.01)
# fill_(): 将张量中的每个元素都填充为指定的值,这里指定的值是 0;
net[0].bias.data.fill_(0)


# 计算均方误差使用的是MSELoss类,也称为平方范数(L2)

loss = nn.MSELoss()

# 实例化SGD 实例
trainer = torch.optim.SGD(net.parameters(), lr=0.03)

# 训练过程:
num_epochs = 3
for epoch in range(num_epochs):
    for X, y in data_iter:
        l = loss(net(X) ,y)
        # 梯度清零
        trainer.zero_grad()
        l.backward()
        trainer.step()
    l = loss(net(features), labels)
    print(f'epoch {epoch + 1}, loss {l:f}')

w = net[0].weight.data
print('w的估计误差:', true_w - w.reshape(true_w.shape))
b = net[0].bias.data
print('b的估计误差:', true_b - b)


http://www.kler.cn/a/539066.html

相关文章:

  • Level DB --- Iterator
  • 【C++篇】 异常处理
  • 苍穹外卖-新增菜品(阿里云OSS文件上传mybatis主键返回批量保存口味表数据)
  • 电脑黑屏按什么键恢复?电脑黑屏的解决办法
  • 解锁C#数据校验:从基础到实战的进阶之路
  • 网络爬虫技术如何影响网络安全的
  • Jetbrains IDE http客户端使用教程
  • Unet 改进:引入残差模块ResidualBlock
  • 《qt easy3d中添加Delaunay-2d重建》
  • 使用Python进行数据采集与解析!
  • 小结:NAT
  • YouBIP 项目
  • C# 比较两个List集合内容是否相同
  • 国产编辑器EverEdit - 查找下一个单词与查找上一个单词
  • 软件工程-分析建模
  • DeepSeek 提示工程:解锁 AI 对话的六维超能力
  • 34.日常算法
  • Windows系统中常用的命令
  • 构建高效Facebook广告矩阵:精准营销与广告投放的全新策略
  • OHOS(HarmonyOS) 编译 C/C++ 库
  • 使用Termux和Ollama在Android设备上构建与运行AI模型
  • 本地部署DeepSeek方法记录
  • DeepSeek R1蒸馏版模型部署的实战教程
  • 【C语言标准库函数】浮点数分解与构造: frexp() 和 ldexp()
  • 35~37.ppt
  • linux openssl 版本管理:不要手动更新系统的openssl版本