Advertisement

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

阅读量:

线性回归 + 基础优化算法

课程视频: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)
    
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    

全部评论 (0)

还没有任何评论哟~