深度学习实验二:线性回归及Softmax回归

实验代码案例来自于此书:https://zh-v2.d2l.ai/

一、实验环境

Windows 11、VS Code、Python 3.8.8

二、实验内容

1.线性回归“从零开始实现”及“简洁实现”

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

①引入模块
在这里插入图片描述
②生成数据集
生成一个包含1000个样本的数据集,每个样本包含从标准正态分布中采样的2个特征。合成数据集是一个矩阵,使用线性模型参数和噪声项生成数据集及其标签:
在这里我们认为标准假设成立,即服从均值为0的正态分布。为了简化问题,标准差设为0.01。
在这里插入图片描述

features中的每一行都包含一个二维数据样本,labels中的每一行都包含一维标签值(一个标量)。
在这里插入图片描述
通过生成第二个特征features[:, 1]和labels的散点图,可以直观观察到两者之间的线性关系。
在这里插入图片描述
③读取数据集
定义一个data_iter函数, 该函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size的小批量。每个小批量包含一组特征和标签。
在这里插入图片描述
小批量运算:读取第一个小批量数据样本并打印。每个批量的特征维度显示批量大小和输入特征数。同样的,批量的标签形状与batch_size相等。

④初始
④初始化模型参数
在开始用小批量随机梯度下降优化我们的模型参数之前,我们需要先有一些参数。在下面的代码中,我们通过从均值为0、标准差为0.01的正态分布中采样随机数来初始化权重,并将偏置初始化为0。
在这里插入图片描述
在初始化参数之后,我们的任务是更新这些参数,直到这些参数足够拟合我们的数据。每次更新都需要计算损失函数关于模型参数的梯度。
⑤定义模型
定义模型,将模型的输入和参数同模型的输出关联起来。
要计算线性模型的输出,我们只需计算输入特征X和模型w权重的矩阵-向量乘法后加上偏置。注意,上面的Xw是一个向量,而b是一个标量。
在这里插入图片描述

⑥定义损失函数
此处使用平方损失函数。
将真实值y的形状转换为和预测值y_hat的形状相同。
在这里插入图片描述
⑦定义优化算法
在这里插入图片描述
⑧训练
在每次迭代中,我们读取一小批量训练样本,并通过我们的模型来获得一组预测。 计算完损失后,开始反向传播,存储每个参数的梯度。最后调用优化算法sgd来更新模型参数。
执行以下循环:
在这里插入图片描述
在每个迭代周期中,我们使用data_iter函数遍历整个数据集,并将训练数据集中所有样本都使用一次(假设样本数能够被批量大小整除)。这里的迭代周期个数num_epochs和学习率lr都是超参数,分别设为3和0.03。
在这里插入图片描述
可以通过比较真实参数和通过训练学到的参数来评估训练的成功程度。
在这里插入图片描述

(2)线性回归的简洁实现

①生成数据集

在这里插入图片描述
②读取数据集
调用框架中现有的API来读取数据。我们将features和labels作为API的参数传递,并通过数据迭代器指定batch_size。此外,布尔值is_train表示是否希望数据迭代器对象在每个迭代周期内打乱数据。
在这里插入图片描述
为了验证是否正常工作,读取并打印第一个小批量样本。这里我们使用iter构造Python迭代器,并使用next从迭代器中获取第一项。
在这里插入图片描述
③定义模型
先定义一个模型变量net,它是一个Sequential类的实例。Sequential类将多个层串联在一起。当给定输入数据时,Sequential实例将数据传入到第一层,然后将第一层的输出作为第二层的输入,以此类推。
在PyTorch中,全连接层在Linear类中定义。值得注意的是,我们将两个参数传递到nn.Linear中。第一个指定输入特征形状,即2,第二个指定输出特征形状,输出特征形状为单个标量,因此为1。
在这里插入图片描述
④初始化模型参数
在使用net之前,我们需要初始化模型参数。在这里,我们指定每个权重参数应该从均值为0、标准差为0.01的正态分布中随机采样,偏置参数将初始化为零。
我们通过net[0]选择网络中的第一个图层,然后使用weight.data和bias.data方法访问参数。我们还可以使用替换方法normal_和fill_来重写参数值。
在这里插入图片描述
⑤定义损失函数
计算均方误差使用的是MSELoss类,也称为平方范数。默认情况下,它返回所有样本损失的平均值。

在这里插入图片描述
⑥定义优化算法
小批量随机梯度下降算法是一种优化神经网络的标准工具,PyTorch在optim模块中实现了该算法的许多变种。当我们实例化一个SGD实例时,我们要指定优化的参数(可通过net.parameters()从我们的模型中获得)以及优化算法所需的超参数字典。小批量随机梯度下降只需要设置lr值,这里设置为0.03。
在这里插入图片描述
⑦训练
为了更好的衡量训练效果,我们计算每个迭代周期后的损失,并打印它来监控训练过程。
在这里插入图片描述
比较生成数据集的真实参数和通过有限数据训练获得的模型参数。 要访问参数,我们首先从net访问所需的层,然后读取该层的权重和偏置。
在这里插入图片描述

2. Softmax回归“从零开始实现”及“简洁实现”

引入Fashion-MNIST数据集,并设置数据迭代器的批量大小为256。
在这里插入图片描述

(1)Softmax回归的从零开始实现

①初始化模型参数
和之前线性回归的例子一样,这里的每个样本都将用固定长度的向量表示。 原始数据集中的每个样本都是28×28的图像。 在本节中,我们将展平每个图像,把它们看作长度为784的向量。
在softmax回归中输出与类别一样多。因为数据集有10个类别,所以网络输出维度为10。 因此,权重将构成一个784×10的矩阵,偏置将构成一个1×10的行向量。与线性回归一样,使用正态分布初始化权重W,偏置初始化为0
在这里插入图片描述
②定义softmax操作
sum运算符沿着张量中的特定维度工作:给定一个矩阵X,我们可以对所有元素求和(默认情况下)。也可以只求同一个轴上的元素,即同一列(轴0)或同一行(轴1)。
在这里插入图片描述
实现softmax由三个步骤组成:

  • 对每个项求幂(使用exp);
  • 对每一行求和(小批量中每个样本是一行),得到每个样本的规范化常数;
  • 将每一行除以其规范化常数,确保结果的和为1。

表达式:
在这里插入图片描述
分母或规范化常数,有时也称为配分函数(其对数称为对数-配分函数)。该名称来自统计物理学中一个模拟粒子群分布的方程。
在这里插入图片描述
对于任何随机输入,我们将每个元素变成一个非负数。此外,依据概率原理,每行总和为1。
在这里插入图片描述
③定义模型
定义softmax操作后,我们可以实现softmax回归模型。下面的代码定义了输入如何通过网络映射到输出。注意,将数据传递到模型之前,我们使用reshape函数将每张原始图像展平为向量。
在这里插入图片描述
④定义损失函数
实现交叉熵损失函数:
交叉熵采用真实标签的预测概率的负对数似然。创建一个数据样本y_hat,其中包含2个样本在3个类别的预测概率,以及它们对应的标签y。有了y,我们知道在第一个样本中,第一类是正确的预测;而在第二个样本中,第三类是正确的预测。然后使用y作为y_hat中概率的索引,选择第一个样本中第一个类的概率和第二个样本中第三个类的概率。
在这里插入图片描述
现在只需一行代码就可以实现交叉熵损失函数:
在这里插入图片描述
⑤分类精度
给定预测概率分布y_hat,当我们必须输出硬预测时,我们通常选择预测概率最高的类。
当预测与标签分类y一致时,即是正确的。分类精度即正确预测数量与总预测数量之比。
为了计算精度,我们执行以下操作。首先,如果y_hat是矩阵,那么假定第二个维度存储每个类的预测分数。使用argmax获得每行中最大元素的索引来获得预测类别。然后将预测类别与真实y元素进行比较。由于等式运算符“==”对数据类型很敏感,因此将y_hat的数据类型转换为与y的数据类型一致。结果是一个包含0(错)和1(对)的张量。最后求和会得到正确预测的数量。
在这里插入图片描述
继续使用之前定义的变量y_hat和y分别作为预测的概率分布和标签。可以看到,第一个样本的预测类别是2(该行的最大元素为0.6,索引为2),这与实际标签0不一致。第二个样本的预测类别是2(该行的最大元素为0.5,索引为2),这与实际标签2一致。因此,这两个样本的分类精度率为0.5。
在这里插入图片描述
同样,对于任意数据迭代器data_iter可访问的数据集,可以评估在任意模型net的精度。
在这里插入图片描述
这里定义实用程序类Accumulator,用于对多个变量进行累加。在上面的evaluate_accuracy函数中,我们在Accumulator实例中创建了2个变量,分别用于存储正确预测的数量和预测的总数量。当我们遍历数据集时,两者都将随着时间的推移而累加。
在这里插入图片描述
由于我们使用随机权重初始化net模型,因此该模型的精度应接近于随机猜测。例如在有10个类别情况下的精度为0.1。
在这里插入图片描述
⑥训练
首先,我们定义一个函数来训练一个迭代周期。updater是更新模型参数的常用函数,它接受批量大小作为参数。它可以是d2l.sgd函数,也可以是框架的内置优化函数。
在这里插入图片描述
在展示训练函数的实现之前,我们定义一个在动画中绘制数据的实用程序类Animator。
在这里插入图片描述
下来我们实现一个训练函数,它会在train_iter访问到的训练数据集上训练一个模型net。 该训练函数将会运行多个迭代周期(由num_epochs指定)。在每个迭代周期结束时,利用test_iter访问到的测试数据集对模型进行评估。我们将利用Animator类来可视化训练进度。
在这里插入图片描述
作为一个从零开始的实现,我们使用之前定义的小批量随机梯度下降来优化模型的损失函数,设置学习率为0.1。
在这里插入图片描述
现在,我们训练模型10个迭代周期。迭代周期(num_epochs)和学习率(lr)都是可调节的超参数。通过更改它们的值,我们可以提高模型的分类精度。
在这里插入图片描述
⑦预测
现在训练已经完成,我们的模型已经准备好对图像进行分类预测。给定一系列图像,我们将比较它们的实际标签(文本输出的第一行)和模型预测(文本输出的第二行)。
在这里插入图片描述

(2)Softmax回归的简洁实现

同之前一样,继续使用Fashion-MNIST数据集,并保持批量大小为256。
在这里插入图片描述

①初始化模型参数
softmax回归的输出层是一个全连接层。因此,为了实现我们的模型,我们只需在Sequential中添加一个带有10个输出的全连接层。同样,在这里Sequential并不是必要的,但它是实现深度模型的基础。我们仍然以均值0和标准差0.01随机初始化权重。
在这里插入图片描述
②重新审视Softmax的实现
通过将softmax和交叉熵结合在一起,可以避免反向传播过程中可能会困扰我们的数值稳定性问题。
我们希望保留传统的softmax函数,以备我们需要评估通过模型输出的概率。但我们没有将softmax概率传递到损失函数中,而是在交叉熵损失函数中传递未规范化的预测,并同时计算softmax及其对数,这是一种类似”LogSumExp技巧”的聪明方式。
在这里插入图片描述
③优化算法
使用学习率为0.1的小批量随机梯度下降作为优化算法。
在这里插入图片描述
④训练
调用之前定义的训练函数来训练模型。

在这里插入图片描述
和以前一样,这个算法使结果收敛到一个相当高的精度,而且这次的代码比之前更精简了。

三、实验小结

①遇到问题:d2l包里的代码没有和文档代码一起更新。
导致的结果是代码跑不动,并出现了很多bug,最后重写了一遍,就解决了。
②关于softmax归一的实现:
softmax归一并没有自己手写,Module中也没有,因为也不需要,argmax 对未归一化前的 O向量和归一化后的 Y_hat向量结果一致。其实Softmax操作应该是在CrossEntropyLoss中实现了,其满足了反向传播的需要,也通过特殊的技巧避免了溢出的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Moonee_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值