• 动手学习深度学习08----线性模型+基础优化算法


    参考视频:b站,李沐老师,动手学习深度学习pytorch
    链接:https

    原理分析

    线性模型

    线性回归是一个最基础的模型

    以预测房价为例子

    我们假设影响房价的关键因素是卧室个数、卫生间个数和居住面积,记为 x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3

    假设我们的房价y是关键因素的加权和
    y = w 1 x 1 + w 2 x 2 + w 3 x 3 + b y=w_1x_1+w_2x_2+w_3x_3+b y=w1x1+w2x2+w3x3+b
    其中的w和b分别是权重和偏差。(后面再决定)

    那么把这个拓展为一个一般化的线性模型,如图所示

    在这里插入图片描述

    那么这个线性模型就是一个简单的模型,可以看作一个单层的神经网络。

    输入层就是 x 1 , x 2 , x 3 . . . x_1,x_2,x_3... x1,x2,x3...,输出层就是房价y。

    有了模型之后,我们要做预测,那么我们还需要评估我们的预测的质量,也就是比较真实值和预估值。

    假设y是真实值, y ^ \widehat{y} y 是预估值,我们用如下函数来评价预测的质量(这是常用的一个平方差)
    φ ( y , y ^ ) = 1 2 ( y − y ^ ) 2 \varphi(y,\widehat{y})=\frac{1}{2} (y-\widehat{y})^2 φ(y,y )=21(yy )2
    至于前面提到的权重和偏差如何决定呢?我们通过一些大量的数据来决定。

    比如,我们收集过去几个月的房子成交数据,我们称为训练数据。

    在这里插入图片描述

    这个大写的 X X X就是我们有n个样本,每一行都是一个向量,如 x 1 x_1 x1就是一个向量,就是一个成交房子的数据,也就是我们前面说的诸多关键因素, y y y也是n组数据,每一个 y 1 y_1 y1都是一个实数的数值,也就是房子的成交价。

    那么对应我们之前说的评估函数,我们称为损失函数:

    前面我们说了,我们的预测房价 y = < x , w > + b y=+b y=<x,w>+b,那么用平方差损失就是真实的房价减去预测的房价,再求和,再乘以n分之一,求个平均值,最外面乘上1/2,整个式子如下所示:

    在这里插入图片描述

    那么这个损失函数就是评估了预测值与真实值的差距,那么我们当然是期望我们预测的越来越准,所以我们的目标就是求出这个损失函数的最小值。

    怎么求最小值呢,就是不断地调整w和b的值,我们的目标就是找到一个w和b,使得我们的整个损失函数取值最小

    那么这个过程也是我们确定w和b的取值的过程,这就是我们的参数学习过程,在这个训练过程中,不断学习,调整权重和偏差。

    在这里插入图片描述

    因为我们这是一个线性模型,所以我们是有显式解的,也就是能直接表示出他的解。

    他的显式解怎么求呢?

    首先,我们把偏差b,全部设为1,然后把他加入到W中去。

    这样就直接写作 y=Wx,不要b了。然后对这个损失函数求偏导,可以直接求出来W
    在这里插入图片描述

    那么这就是一个完整的线性回归的模型。

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

    优化方法

    在没有显式解的情况下,我们一般采用的办法是梯度下降法

    就是随机的选择一个初试参数: W 0 W_0 W0

    在这里插入图片描述

    这个方法的大概意思就是:比如当前时刻的参数值是 W t W_t Wt ,上一时刻的参数值是 W t − 1 W_{t-1} Wt1

    那么 W t − 1 W_{t-1} Wt1 求导,就是求出 W t − 1 W_{t-1} Wt1 的梯度(就是这个点上函数值下降最快的方向),

    这个梯度取一个负号,负的梯度,就是下降的方向。

    这个 η \eta η是学习率,就是一次下降程度大概是多少,这俩相乘,用 W t − 1 W_{t-1} Wt1 加上这个结果,就是下一时刻的 W t W_{t} Wt

    在这里插入图片描述

    如图所示,就是这样不断下降,不断靠近最优解(最小值)

    这个学习率不应过大(一次下降太多,来回震荡),也不应过小(下降太慢)

    然后这个梯度下降,我们每次下降,都要对我们的整个损失函数求导,而这个损失函数是对整个样本的损失。

    对所有的数据,都计算一遍,那耗时太久。

    所以,我们还会采用“小批量随机梯度下降”

    每次采样一部分数据进行下降

    在这里插入图片描述

    这个样本也是一样,不能取太大,也不能取太小。

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

    代码实现

    • 整个过程其实就是 我们先假设一个真实的w和b
    • 然后呢 再生成一组随机的向量 合在一起叫做X
    • 再结合 y=wX+b 这个线性函数 得到一组y(为了增加难度 我们还加了一组噪音)
    • 然后 我们把 X 和 y 看作我们的训练数据
    • 然后开始完整的机器学习求解过程
    • 我们面对 X 和 y ,使用我们定义好的线性模型
    • 目的是求解出w和b 我们随机的初始化一个w和b 然后使用梯度下降法
    • 不断的更新我们的w和b 经过多次训练 求解得到一个 w和b

    torch.normal(mean=0.,std=1.,size=(2,2))

    该函数返回从单独的正态分布中提取的随机数的张量,该正态分布的均值是mean,标准差是std。

    torch.matmul(X, w)做乘法
    detach就是说从梯度计算的计算图摘出来,不需要做梯度计算了。
    reshape(行,列)
    with torch.no_grad():下面的代码都不要梯度计算

    手动实现

    import torch
    import random
    import matplotlib.pyplot as plt
    
    # 三个参数  我们给定真实的 w 和 b 以及我们要生成的数据量num_examples
    def synthetic_data(w, b, num_examples):
        """生成 y = Xw + b + 噪声。"""
        # 仿照给定的 w  生成n行数据
        X = torch.normal(0, 1, (num_examples, len(w)))
        # torch.matmul 做乘法
        y = torch.matmul(X, w) + b
        # 再加一个噪声
        y += torch.normal(0, 0.01, y.shape)
        # reshape(行,列)  -1 代表未给定
        return X, y.reshape((-1, 1))
    
    # 这个函数就是传入一些数据 我每次给你返回随机的一批样本 大小由你指定为batch_size
    # 该函数接收特征矩阵和标签向量作为输入,给定一个batch_size,生成大小为batch_size的小批量
    # 接收一组样本 features labels  大小为batch_size
    def data_iter(batch_size, features, labels):
        # 先看下数据一共多少个
        num_examples = len(features)
        # 生成n个下标
        indices = list(range(num_examples))
        # 把这些下标打乱顺序 这样待会可以随机访问
        random.shuffle(indices)
        # 遍历 i从0到n 每次间隔batch_size个大小
        for i in range(0, num_examples, batch_size):
            # 这个就是取出这个批量的数据
            # 比如batch_size=10  循环时候i=0,10,20,30...
            # 进入循环就是取0-10的这10个数据 防止超出范围 所以用一个min
            batch_indices = torch.tensor(indices[i:min(i +batch_size, num_examples)])
            # yield就相当于一个迭代器 他使得这个函数可以被迭代
            # 就是每次调用都会返回一组大小为batch_size的样本
            yield features[batch_indices], labels[batch_indices]
    
    def linreg(X, w, b):
        """线性回归模型。"""
        return torch.matmul(X, w) + b
    
    def squared_loss(y_hat, y):
        """均方损失。y_hat是真实值 y是预测值  相减 再平方 再1/2 """
        return (y_hat - y.reshape(y_hat.shape))**2 / 2
    
    def sgd(params, lr, batch_size):
        """小批量随机梯度下降。给定参数  学习率 batch_size"""
        with torch.no_grad():
            for param in params:
                # 更新每一个参数 求损失的时候没有做均值 所以这里做均值
                param -= lr * param.grad / batch_size
                # 梯度清0
                param.grad.zero_()
    
    if __name__ == '__main__':
        # 给定真实的w 和 b
        true_w = torch.tensor([2, -3.4])
        true_b = 4.2
    
        # 生成1000组 X是特征 y是标注
        features, labels = synthetic_data(true_w, true_b, 1000)
        # plt.scatter(features[:, 1].detach().numpy(),labels.detach().numpy(), 1);
        # plt.show()
    
        lr = 0.03 # 学习率
        num_epochs = 3 # 迭代次数
        net = linreg #  网络模型
        loss = squared_loss # 损失函数
        batch_size = 10 # 批量大小
        # 初始化我们的参数模型
        # w随机初始化为均值=0 方差=0.01 大小2行1列的向量
        w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
        # 偏差直接给0
        b = torch.zeros(1, requires_grad=True)
    
        for epoch in range(num_epochs):
            # 每次拿出batch_size的数据
            for X, y in data_iter(batch_size, features, labels):
                # 计算损失
                l = loss(net(X, w, b), y)
                # 求和  sgd里面做了平均
                # backward() 求梯度
                l.sum().backward()
                # 用参数去优化
                sgd([w, b], lr, batch_size)
            with torch.no_grad():
                # net(features, w, b)是对整个数据的预测 与 labels 求个损失
                # 就是评价一下预测结果
                train_l = loss(net(features, w, b), labels)
                print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
    
        # 因为我们是自己建的数据集 所以我们是知道真实的w和b的
        # 看一下我们学习得到的w和b 与 真实值的差距
        '''
            整个过程其实就是 我们先假设一个真实的w和b
            然后呢 再生成一组随机的向量  合在一起叫做X
            再结合 y=wX+b 这个线性函数  得到一组y(为了增加难度 我们还加了一组噪音)
            然后 我们把 X 和 y  看作我们的训练数据
            然后开始完整的机器学习求解过程
            我们面对 X 和 y ,使用我们定义好的线性模型
            目的是求解出w和b  我们随机的初始化一个w和b  然后使用梯度下降法
            不断的更新我们的w和b  经过多次训练  求解得到一个 w和b  
        '''
        print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
        print(f'b的估计误差: {true_b - b}')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104

    输出结果:

    epoch 1, loss 0.034254
    epoch 2, loss 0.000125
    epoch 3, loss 0.000049
    w的估计误差: tensor([ 0.0004, -0.0002], grad_fn=<SubBackward0>)
    b的估计误差: tensor([0.0011], grad_fn=<RsubBackward1>)
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用pytorch实现

    那么我们把上面那个模型,使用pytorch提供的一些函数,来快速方便的实现一下:

    import random
    import matplotlib.pyplot as plt
    import numpy as np
    import torch
    from torch import nn
    from torch.utils import data
    
    def synthetic_data(w, b, num_examples):
        """生成 y = Xw + b + 噪声。"""
        X = torch.normal(0, 1, (num_examples, len(w)))
        y = torch.matmul(X, w) + b
        y += torch.normal(0, 0.01, y.shape)
        return X, y.reshape((-1, 1))
    
    def load_array(data_arrays, batch_size, is_train=True):
        """构造一个PyTorch数据迭代器。"""
        # 把所有数据送进来
        dataset = data.TensorDataset(*data_arrays)
        # 生成一个batch_size大小的批量数据 shuffle代表顺序随机
        return data.DataLoader(dataset, batch_size, shuffle=is_train)
    
    if __name__ == '__main__':
        true_w = torch.tensor([2, -3.4])
        true_b = 4.2
        # 生成数据集
        features, labels = synthetic_data(true_w, true_b, 1000)
        # 指定 batch_size  创建数据迭代器
        batch_size = 10
        data_iter = load_array((features, labels), batch_size)
        # 定义网络 Sequential 就是把网络的每个层依次放在这里面
        net = nn.Sequential(nn.Linear(2, 1))
        # 初始化 网络参数
        net[0].weight.data.normal_(0, 0.01)
        net[0].bias.data.fill_(0)
        # 损失函数
        loss = nn.MSELoss()
        # 优化器
        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)
                # 梯度清0
                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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    输出结果:

    epoch 1, loss 0.000214
    epoch 2, loss 0.000100
    epoch 3, loss 0.000100
    w的估计误差: tensor([0.0003, 0.0003])
    b的估计误差: tensor([-0.0002])
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    Scratch 第十六课-弹珠台游戏
    YOLOv8改进全新Inner-IoU损失函数:全网首发|2023年11月最新论文|扩展到其他SIoU、CIoU等主流损失函数,带辅助边界框的损失
    基于双向长短期神经网络bilstm的径流量预测,基于gru神经网络的径流量预测
    Java:Jar包反编译,解压和压缩
    [iOS开发]iOS持久化
    CSS13_由html{height: 100%} 引发的CSS百分比宽高度的思考
    岭回归自动调参
    【OpenCV-Python】教程:3-9 轮廓(4)更多函数
    N-羟乙基-1,8-萘二甲酰亚胺四苯乙烯衍生物聚集诱导发光微球/AIE席夫碱化合物的研究制备
    【QT开发(14)】QT P2P chat 聊天
  • 原文地址:https://blog.csdn.net/holly_Z_P_F/article/details/126719452