开源项目地址:d2l-ai/d2l-zh

教材官网:https://zh.d2l.ai/

书介绍:https://zh-v2.d2l.ai/

笔记基于2021年7月26日发布的版本,书及代码下载地址在github网页的最下面

交流者论坛

额外:
https://distill.pub/

pytorch中文文档(哪个函数不明白用法查询非常好用)


(d2l-ai/d2l-zh)《动手学深度学习》pytorch 笔记(1)(序言、pytorch的安装、神经网络涉及符号)

(d2l-ai/d2l-zh)《动手学深度学习》pytorch 笔记(2)前言(介绍各种机器学习问题)

文章目录

  • 3.1 线性回归
    • 3.1.1 线性回归的基本元素(102)(linear regression)
      • 线性模型(102)(仿射变换)
      • 损失函数(103)
      • 解析解(104)
      • 小批量随机梯度下降(104)(批量大小、学习率、超参数(hyperparameter)、调参、验证数据集、泛化的概念)
      • 用学习到的模型进行预测【预测(prediction)与推断(inference)】(105)
    • 3.1.2 矢量化加速(就是不用for循环一个一个计算)(105)
    • 3.1.3 正态分布与平方损失(107)([最大似然估计](https://baike.baidu.com/item/%E6%9C%80%E5%A4%A7%E4%BC%BC%E7%84%B6%E4%BC%B0%E8%AE%A1/4967925?fr=aladdin):最大可能性估计)
    • 3.1.4 从线性回归到深度⽹络(108)
      • 神经网络图(108)(线性回归模型可以看作单层神经网络)(特征维度、全连接层、稠密层的概念)
      • 生物学(109)(了解了解就好)
    • 3.1.5 ⼩结(110)
    • 3.1.6 练习(110)
  • 3.2 线性回归的从零开始实现(110)
    • 3.2.1 ⽣成数据集(111)
    • 3.2.2 读取数据集(112)
    • 3.2.3 初始化模型参数(113)
    • 3.2.4 定义模型(114)
    • 3.2.5 定义损失函数(114)
    • 3.2.6 定义优化算法(114)
    • 3.2.7 训练(115)(迭代周期(epoch))
      • 代码:总(代码里也有结论,别懒不看代码!@)
    • 3.2.8 ⼩结(116)
    • 3.2.9 练习(116)
  • 3.3 线性回归的简洁实现(116)(框架功能:张量存储、线代运算、自动微分求梯度、【数据迭代器、损失函数、优化器】)
    • 3.3.1 生成数据集(117)
    • 3.3.2 读取数据集(117)
    • 3.3.3 定义模型(118)(线性回归)
    • 3.3.4 初始化模型参数(119)
    • 3.3.5 定义损失函数(119)
    • 3.3.6 定义优化算法(119)(SGD:随机梯度下降(stochastic gradient descent))
    • 3.3.7 训练(119)
    • 代码:线性回归的简洁实现
    • 3.3.8 ⼩结(120)
    • 3.3.9 练习
  • 3.4 softmax回归(分类问题)(121)
    • 3.4.1 分类问题(121)(独热编码(one-hot encoding))
    • 3.4.2 网络结构(122)
    • 3.4.3 全连接层的参数开销(122)
    • 3.4.4 softmax运算(122)(校准(calibration)属性)
    • 3.4.5 小批量样本的⽮量化(123)
    • 3.4.6 损失函数(123)
      • 对数似然(124)
      • softmax及其导数(124)
      • 交叉熵损失(125)
    • 3.4.7 信息论基础(125)略
    • 3.4.8 模型预测和评估(126)
    • 3.4.9 小结(136)
    • 3.4.10 练习(127)
  • 3.5 图像分类数据集(127)
    • 3.5.1 读取数据集(127)
      • 测试代码:

在介绍深度神经⽹络之前,我们需要了解神经⽹络训练的基础知识。在本章中,我们将介绍神经⽹络的整个训练过程,包括:定义简单的神经⽹络架构、数据处理、指定损失函数和如何训练模型。经典统计学习技术中的线性回归和softmax回归可以视为 线性神经⽹络。为了更容易学习,我们将从这些经典算法开始,向你介绍神经⽹络的基础知识。这些知识将为本书其他部分中更复杂的技术奠定基础。

3.1 线性回归

回归(regression)是指⼀类为⼀个或多个⾃变量与因变量之间关系建模的⽅法。

3.1.1 线性回归的基本元素(102)(linear regression)

线性回归(linear regression)在回归的各种标准⼯具中最简单而且最流⾏。它可以追溯到19世纪初。线性回归基于⼏个简单的假设:⾸先,假设⾃变量 x 和因变量 y 之间的关系是线性的,即y可以表⽰为 x 中元素的加权和,这⾥通常允许包含观测值的⼀些噪声;其次,我们假设任何噪声都⽐较正常,如噪声遵循正态分布。为了解释线性回归,我们举⼀个实际的例⼦:我们希望根据房屋的⾯积(平⽅英尺)和房龄(年)来估算房屋价格(美元)。为了开发⼀个能预测房价的模型,我们需要收集⼀个真实的数据集。这个数据集包括了房屋的销售价格、⾯积和房龄。在机器学习的术语中,该数据集称为 训练数据集(training data set)或训练集(training set),每⾏数据(在这个例⼦中是与⼀次房屋交易相对应的数据)称为样本(sample),也可以称为数据点(data point)或数据样本(data instance)。我们要试图预测的⽬标(在这个例⼦中是房屋价格)称为 标签(label)或 ⽬标(target)。预测所依据的⾃变量(⾯积和房龄)称为 特征(features)或 协变量(covariates)。

线性模型(102)(仿射变换)

线性假设是指⽬标(房屋价格)可以表⽰为特征(⾯积和房龄)的加权和,如下⾯的式⼦:







加入噪声项,啥意思?

损失函数(103)





解析解(104)


像线性回归这样的简单问题存在解析解,但并不是所有的问题都存在解析解。解析解可以进⾏很好的数学分析,但解析解的限制很严格,导致它⽆法应⽤在深度学习⾥。

小批量随机梯度下降(104)(批量大小、学习率、超参数(hyperparameter)、调参、验证数据集、泛化的概念)

即使在我们⽆法得到解析解的情况下,我们仍然可以有效地训练模型。

梯度下降(gradient descent)法,这种⽅法⼏乎可以优化所有深度学习模型。它通过不断地在损失函数递减的⽅向上更新参数来降低误差。

梯度下降最简单的⽤法是计算损失函数(数据集中所有样本的损失均值)关于模型参数的导数(在这⾥也可以称为梯度)。但实际中的执⾏可能会⾮常慢:因为在每⼀次更新参数之前,我们必须遍历整个数据集。因此,我们通常会在每次需要计算更新的时候随机抽取⼀小批样本,这种变体叫做小批量随机梯度下降(minibatchstochastic gradient descent)

在每次迭代中,我们⾸先随机抽样⼀个小批量B,它是由固定数量的训练样本组成的。然后,我们计算小批量的平均损失关于模型参数的导数(也可以称为梯度)。最后,我们将梯度乘以⼀个预先确定的正数η(步长),并从当前参数的值中减掉。




在训练了预先确定的若⼲迭代次数后(或者直到满⾜某些其他停⽌条件后),我们记录下模型参数的估计值,表⽰为wˆ ,ˆb。但是,即使我们的函数确实是线性的且⽆噪声,这些估计值也不会使损失函数真正地达到最小值。因为算法会使得损失向最小值缓慢收敛,但却不能在有限的步数内⾮常精确地达到最小值。

线性回归恰好是⼀个在整个域中只有⼀个最小值的学习问题。但是对于像深度神经⽹络这样复杂的模型来说,损失平⾯上通常包含多个最小值。幸运的是,出于某种原因,深度学习实践者很少会去花费⼤⼒⽓寻找这样⼀组参数,使得在 训练集上的损失达到最小。事实上,更难做到的是找到⼀组参数,这组参数能够在我们从未⻅过的数据上实现较低的损失,这⼀挑战被称为 泛化(generalization)。

用学习到的模型进行预测【预测(prediction)与推断(inference)】(105)


我们将尝试坚持使⽤预测这个词。虽然推断这个词已经成为深度学习的标准术语,但其实推断这个词有些⽤词不当。在统计学中,推断更多地表⽰基于数据集估计参数。当深度学习从业者与统计学家交谈时,术语的误⽤经常导致⼀些误解。

3.1.2 矢量化加速(就是不用for循环一个一个计算)(105)

在训练我们的模型时,我们经常希望能够同时处理整个小批量的样本。为了实现这⼀点,需要我们对计算进⾏⽮量化,从而利⽤线性代数库,而不是在Python中编写开销⾼昂的for循环。

矢量化???这个名词的来源是什么?

为了说明⽮量化为什么如此重要,我们考虑对向量相加的两种⽅法。我们实例化两个全1的10000维向量。在⼀种⽅法中,我们将使⽤Python的for循环遍历向量。在另⼀种⽅法中,我们将依赖对 + 的调⽤。

import math
import time
import numpy as np
import torch
from d2l import torch as d2l# 由于在本书中我们将频繁地进⾏运⾏时间的基准测试,所以让我们定义⼀个计时器。
class Timer:  # @save"""记录多次运⾏时间。"""def __init__(self):self.times = []self.start()def start(self):"""启动计时器。"""self.tik = time.time()def stop(self):"""停⽌计时器并将时间记录在列表中。"""self.times.append(time.time() - self.tik)return self.times[-1]def avg(self):"""返回平均时间。"""return sum(self.times) / len(self.times)def sum(self):"""返回时间总和。"""return sum(self.times)def cumsum(self):"""返回累计时间。"""return np.array(self.times).cumsum().tolist()def main():n = 10000a = torch.ones(n)b = torch.ones(n)# 现在我们可以对⼯作负载进⾏基准测试。# ⾸先,我们使⽤for循环,每次执⾏⼀位的加法。c = torch.zeros(n)timer = Timer()for i in range(n):c[i] = a[i] + b[i]print(f'{timer.stop():.5f} sec')  # 0.07998 sec# print('{:.5f} sec'.format(timer.stop()))    # 0.07998 sec# 或者,我们使⽤重载的 + 运算符来计算按元素的和。timer.start()d = a + bprint(f'{timer.stop():.5f} sec')  # 0.00100 secif __name__ == '__main__':main()

结果很明显,第⼆种⽅法⽐第⼀种⽅法快得多。⽮量化代码通常会带来数量级的加速。另外,我们将更多的数学运算放到库中,而⽆需⾃⼰编写那么多的计算,从而减少了出错的可能性。

3.1.3 正态分布与平方损失(107)(最大似然估计:最大可能性估计)

接下来,我们通过对噪声分布的假设来解读平⽅损失⽬标函数。


下⾯我们定义⼀个Python函数来计算正态分布。

import math
import numpy as np
import torch
from d2l import torch as d2ldef normal(x, mu, sigma):p = 1 / math.sqrt(2 * math.pi * sigma ** 2)return p * np.exp(-0.5 / sigma ** 2 * (x - mu) ** 2)def main():# 我们现在可视化正态分布。# 再次使⽤numpy进⾏可视化x = np.arange(-7, 7, 0.01)# 均值和标准差对params = [(0, 1), (0, 2), (3, 1)]d2l.plot(x, [normal(x, mu, sigma) for mu, sigma in params], xlabel='x',ylabel='p(x)', figsize=(4.5, 2.5),legend=[f'mean {mu}, std {sigma}' for mu, sigma in params])d2l.plt.show()if __name__ == '__main__':main()




实在没搞清楚它后面讲的是啥???

因此,在⾼斯噪声的假设下,最小化均⽅误差等价于对线性模型的最⼤似然估计。??

3.1.4 从线性回归到深度⽹络(108)

到⽬前为⽌,我们只谈论了线性模型。尽管神经⽹络涵盖了更多更为丰富的模型,我们依然可以⽤描述神经⽹络的⽅式来描述线性模型,从而把线性模型看作⼀个神经⽹络。⾸先,让我们⽤“层”符号来重写这个模型。

神经网络图(108)(线性回归模型可以看作单层神经网络)(特征维度、全连接层、稠密层的概念)

深度学习从业者喜欢绘制图表来可视化模型中正在发⽣的事情。在 图3.1.2 中,我们将线性回归模型描述为⼀个神经⽹络。需要注意的是,该图只显⽰连接模式,即只显⽰每个输⼊如何连接到输出,隐去了权重和偏置的值。


在 图3.1.2 所⽰的神经⽹络中,输⼊为 x1, . . . , xd,因此输⼊层中的 输⼊数(或称为 特征维度 feature dimensionality)为 d。⽹络的输出为o1,因此输出层中的 输出数是 1。需要注意的是,输⼊值都是已经给定的,并且只有⼀个计算神经元。由于模型重点在发⽣计算的地⽅,所以通常我们在计算层数时不考虑输⼊层。也就是说,图3.1.2 中神经⽹络的 层数为1。我们可以将线性回归模型视为仅由单个⼈⼯神经元组成的神经⽹络,或称为单层神经⽹络。

对于线性回归,每个输⼊都与每个输出(在本例中只有⼀个输出)相连,我们将这种变换(图3.1.2 中的输出层)称为 全连接层(fully-connected layer)(或称为 稠密层 dense layer)。下⼀章将详细讨论由这些层组成的⽹络。

生物学(109)(了解了解就好)

3.1.5 ⼩结(110)

• 机器学习模型中的关键要素是训练数据,损失函数,优化算法,还有模型本⾝。
• ⽮量化使数学表达上更简洁,同时运⾏的更快。
• 最小化⽬标函数和执⾏最⼤似然估计等价。
• 线性回归模型也是神经⽹络。

3.1.6 练习(110)

  1. 假设我们有⼀些数据 x1, . . . , xn ∈ R。我们的⽬标是找到⼀个常数b,使得

    最小化 。
  • 找到最优值 b 的解析解。
  • 这个问题及其解与正态分布有什么关系?
  1. 推导出使⽤平⽅误差的线性回归优化问题的解析解。为了简化问题,可以忽略偏置b(我们可以通过向 X添加所有值为1的⼀列来做到这⼀点)。
  • ⽤矩阵和向量表⽰法写出优化问题(将所有数据视为单个矩阵,将所有⽬标值视为单个向量)。
  • 计算损失对w的梯度。
  • 通过将梯度设为0、求解矩阵⽅程来找到解析解。
  • 什么时候可能⽐使⽤随机梯度下降更好?这种⽅法何时会失效?
  1. 假定控制附加噪声 ϵ 的噪声模型是指数分布。也就是说,
  • 写出模型 − log P(y | X) 下数据的负对数似然。
  • 你能写出解析解吗?
  • 提出⼀种随机梯度下降算法来解决这个问题。哪⾥可能出错?(提⽰:当我们不断更新参数时,在驻点附近会发⽣什么情况)你能解决这个问题吗?

https://discuss.d2l.ai/t/topic/1775

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

在了解线性回归的关键思想之后,我们可以开始通过代码来动⼿实现线性回归了。在这⼀节中,我们将从零开始实现整个⽅法,包括数据流⽔线、模型、损失函数和小批量随机梯度下降优化器。虽然现代的深度学习框架⼏乎可以⾃动化地进⾏所有这些⼯作,但从零开始实现可以确保你真正知道⾃⼰在做什么。同时,了解更细致的⼯作原理将⽅便我们⾃定义模型、⾃定义层或⾃定义损失函数。在这⼀节中,我们将只使⽤张量和⾃动求导。在之后的章节中,我们会充分利⽤深度学习框架的优势,介绍更简洁的实现⽅式。

3.2.1 ⽣成数据集(111)



你可以将 ϵ 视为捕获特征和标签时的潜在观测误差。在这⾥我们认为标准假设成⽴,即ϵ服从均值为0的正态分布。为了简化问题,我们将标准差设为0.01。下⾯的代码⽣成合成数据集。

import random
import torch
from d2l import torch as d2l# synthetic 人造的、合成的
def synthetic_data(w, b, num_examples):  # @save"""⽣成 y = Xw + b + 噪声。"""X = torch.normal(0, 1, (num_examples, len(w)))  # 正态分布 行:num_examples,列:len(w)# print(X.shape)  # torch.Size([1000, 2])y = torch.matmul(X, w) + b  # matmul点积y += torch.normal(0, 0.01, y.shape)  # 加上正态分布噪声# print(X.shape)  # torch.Size([1000, 2])# print(y.shape)  # torch.Size([1000])# print(y.reshape((-1, 1)).shape)  # torch.Size([1000, 1])return X, y.reshape((-1, 1))def main():# true_w = torch.tensor([2, -3.4])true_w = torch.tensor([2, -3.4])   # true_w的第二个参数决定点形状的分布趋势,因为后面画图的时候X取得是第二列true_b = 4.2features, labels = synthetic_data(true_w, true_b, 1000)  # 生成人造数据# 注意,features 中的每⼀⾏都包含⼀个⼆维数据样本,labels 中的每⼀⾏都包含⼀维标签值(⼀个标量)。print('features:', features[0], '\nlabel:', labels[0])# features: tensor([-2.7495, -0.2577])# label: tensor([-0.4297])# 通过⽣成第⼆个特征 features[:, 1] 和 labels 的散点图,可以直观地观察到两者之间的线性关系。d2l.set_figsize()d2l.plt.scatter(features[:, 1].detach().numpy(), labels.detach().numpy(), 1)  # 1是点的大小d2l.plt.show()# 为什么X小y大,X大y小?(见上)if __name__ == '__main__':main()

3.2.2 读取数据集(112)

回想⼀下,训练模型时要对数据集进⾏遍历,每次抽取⼀小批量样本,并使⽤它们来更新我们的模型。由于这个过程是训练机器学习算法的基础,所以有必要定义⼀个函数,该函数能打乱数据集中的样本并以小批量⽅式获取数据
在下⾯的代码中,我们定义⼀个data_iter 函数,该函数接收批量⼤小、特征矩阵和标签向量作为输⼊,⽣成⼤小为batch_size的小批量。每个小批量包含⼀组特征和标签。

通常,我们使⽤合理⼤小的小批量来利⽤GPU硬件的优势,因为GPU在并⾏处理⽅⾯表现出⾊。每个样本都可以并⾏地进⾏模型计算,且每个样本损失函数的梯度也可以被并⾏地计算,GPU可以在处理⼏百个样本时,所花费的时间不⽐处理⼀个样本时多太多。

让我们直观感受⼀下。读取第⼀个小批量数据样本并打印。每个批量的特征维度说明了批量⼤小和输⼊特征数。同样的,批量的标签形状与 batch_size 相等。

import random
import torch
from d2l import torch as d2l# synthetic 人造的、合成的
def synthetic_data(w, b, num_examples):  # @save"""⽣成 y = Xw + b + 噪声。"""X = torch.normal(0, 1, (num_examples, len(w)))  # 正态分布 行:num_examples,列:len(w)# print(X.shape)  # torch.Size([1000, 2])y = torch.matmul(X, w) + b  # matmul点积y += torch.normal(0, 0.01, y.shape)  # 加上正态分布噪声# print(X.shape)  # torch.Size([1000, 2])# print(y.shape)  # torch.Size([1000])# print(y.reshape((-1, 1)).shape)  # torch.Size([1000, 1])return X, y.reshape((-1, 1))def data_iter(batch_size, features, labels):num_examples = len(features)    # 1000indices = list(range(num_examples)) # 生成列表[0, 1, 2, ..., 998, 999]# 这些样本是随机读取的,没有特定的顺序random.shuffle(indices) # 打乱indicesfor i in range(0, num_examples, batch_size):batch_indices = torch.tensor(indices[i:min(i + batch_size, num_examples)])  # 为什么要加min?担心i+batch_size# 超过num_examples? yield features[batch_indices], labels[batch_indices]def main():# true_w = torch.tensor([2, -3.4])true_w = torch.tensor([2, -3.4])  # true_w的第二个参数决定点形状的分布趋势,因为后面画图的时候X取得是第二列true_b = 4.2features, labels = synthetic_data(true_w, true_b, 1000)  # 生成人造数据# 注意,features 中的每⼀⾏都包含⼀个⼆维数据样本,labels 中的每⼀⾏都包含⼀维标签值(⼀个标量)。print('features:', features[0], '\nlabel:', labels[0])# features: tensor([-2.7495, -0.2577])# label: tensor([-0.4297])# 通过⽣成第⼆个特征 features[:, 1] 和 labels 的散点图,可以直观地观察到两者之间的线性关系。d2l.set_figsize()d2l.plt.scatter(features[:, 1].detach().numpy(), labels.detach().numpy(), 1)  # 1是点的大小d2l.plt.show()# 为什么X小y大,X大y小?(见上)# 读取数据batch_size = 10for X, y in data_iter(batch_size, features, labels):print(X, '\n', y)break
#     tensor([[-0.0187, -0.4961],
#         [ 0.2226,  0.9894],
#         [ 0.9929,  0.0527],
#         [-1.6255,  1.0358],
#         [-1.2527,  0.8796],
#         [-0.4309,  2.4232],
#         [ 0.2163,  1.3185],
#         [ 1.1462, -0.5068],
#         [-0.9749,  0.4630],
#         [ 0.4969,  1.8860]])
#  tensor([[ 5.8492],
#         [ 1.2785],
#         [ 6.0115],
#         [-2.5758],
#         [-1.2782],
#         [-4.9061],
#         [ 0.1676],
#         [ 8.2293],
#         [ 0.6850],
#         [-1.2083]])if __name__ == '__main__':main()

当我们运⾏迭代时,我们会连续地获得不同的小批量,直⾄遍历完整个数据集。上⾯实现的迭代对于教学来说很好,但它的执⾏效率很低,可能会在实际问题上陷⼊⿇烦。例如,它要求我们将所有数据加载到内存中,并执⾏⼤量的随机内存访问。在深度学习框架中实现的内置迭代器效率要⾼得多,它可以处理存储在⽂件中的数据和通过数据流提供的数据。(内置迭代器在哪??长什么样子??DataLoader,see you laterrrrr)

3.2.3 初始化模型参数(113)

在我们开始⽤小批量随机梯度下降优化我们的模型参数之前,我们需要先有⼀些参数。在下⾯的代码中,我们通过从均值为0、标准差为0.01的正态分布中采样随机数来初始化权重,并将偏置初始化为0。

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

在初始化参数之后,我们的任务是更新这些参数,直到这些参数⾜够拟合我们的数据。每次更新都需要计算损失函数关于模型参数的梯度。有了这个梯度,我们就可以向减小损失的⽅向更新每个参数。因为⼿动计算梯度很枯燥而且容易出错,所以没有⼈会⼿动计算梯度。我们使⽤ 2.5节 中引⼊的⾃动微分来计算梯度。

3.2.4 定义模型(114)

接下来,我们必须定义模型,将模型的输⼊和参数同模型的输出关联起来。回想⼀下,要计算线性模型的输出,我们只需计算输⼊特征 X 和模型权重w的矩阵-向量乘法后加上偏置b。注意,上⾯的Xw 是⼀个向量,而b是⼀个标量。回想⼀下 2.1.3节 中描述的⼴播机制。当我们⽤⼀个向量加⼀个标量时,标量会被加到向量的每个分量上。

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

3.2.5 定义损失函数(114)

因为要更新模型。需要计算损失函数的梯度,所以我们应该先定义损失函数。这⾥我们使⽤ 3.1节 中描述的平⽅损失函数。在实现中,我们需要将真实值y的形状转换为和预测值y_hat的形状相同。

def squared_loss(y_hat, y): #@save"""均⽅损失。"""return (y_hat - y.reshape(y_hat.shape))**2 / 2

3.2.6 定义优化算法(114)

正如我们在 3.1节 中讨论的,线性回归有解析解。然而,这是⼀本关于深度学习的书,而不是⼀本关于线性回归的书。由于这本书介绍的其他模型都没有解析解,下⾯我们将在这⾥介绍小批量随机梯度下降的⼯作⽰例。

在每⼀步中,使⽤从数据集中随机抽取的⼀个小批量,然后根据参数计算损失的梯度。接下来,朝着减少损失的⽅向更新我们的参数。下⾯的函数实现小批量随机梯度下降更新。该函数接受模型参数集合、学习速率和批量⼤小作为输⼊。每⼀步更新的⼤小由学习速率lr决定。因为我们计算的损失是⼀个批量样本的总和,所以我们⽤批量⼤小(batch_size)来归⼀化步⻓,这样步⻓⼤小就不会取决于我们对批量⼤小的选择。

def sgd(params, lr, batch_size): #@save"""⼩批量随机梯度下降。"""with torch.no_grad():for param in params:param -= lr * param.grad / batch_sizeparam.grad.zero_()

3.2.7 训练(115)(迭代周期(epoch))

现在我们已经准备好了模型训练所有需要的要素,可以实现主要的训练过程部分了。理解这段代码⾄关重要,因为在整个深度学习的职业⽣涯中,你会⼀遍⼜⼀遍地看到⼏乎相同的训练过程。在每次迭代中,我们读取⼀小批量训练样本,并通过我们的模型来获得⼀组预测。计算完损失后,我们开始反向传播,存储每个参数的梯度。最后,我们调⽤优化算法 sgd 来更新模型参数。

概括⼀下,我们将执⾏以下循环:

在每个迭代周期(epoch)中,我们使⽤ data_iter 函数遍历整个数据集,并将训练数据集中所有样本都使⽤⼀次(假设样本数能够被批量⼤小整除)。这⾥的迭代周期个数num_epochs和学习率lr都是超参数(不在训练过程中更新的参数),分别设为3和0.03。设置超参数很棘⼿,需要通过反复试验进⾏调整。我们现在忽略这些细节,以后会在chap_optimization 中详细介绍。

代码:总(代码里也有结论,别懒不看代码!@)
import random
import torch
from d2l import torch as d2l# synthetic 人造的、合成的
def synthetic_data(w, b, num_examples):  # @save"""⽣成 y = Xw + b + 噪声。"""X = torch.normal(0, 1, (num_examples, len(w)))  # 正态分布 行:num_examples,列:len(w)# print(X.shape)  # torch.Size([1000, 2])y = torch.matmul(X, w) + b  # matmul点积y += torch.normal(0, 0.01, y.shape)  # 加上正态分布噪声# print(X.shape)  # torch.Size([1000, 2])# print(y.shape)  # torch.Size([1000])# print(y.reshape((-1, 1)).shape)  # torch.Size([1000, 1])return X, y.reshape((-1, 1))# 读取数据
def data_iter(batch_size, features, labels):num_examples = len(features)  # 1000indices = list(range(num_examples))  # 生成列表[0, 1, 2, ..., 998, 999]# 这些样本是随机读取的,没有特定的顺序random.shuffle(indices)  # 打乱indicesfor i in range(0, num_examples, batch_size):batch_indices = torch.tensor(indices[i:min(i + batch_size, num_examples)])  # 为什么要加min?担心i+batch_size# batch_indices = torch.tensor(indices[i:i + batch_size])  # 为什么要加min?担心i+batch_size# 超过num_examples?或者是担心最后一组不够数?# print(features.shape, batch_indices.shape)  # torch.Size([1000, 2]) torch.Size([10])# print(features[batch_indices].shape, labels[batch_indices].shape)   # torch.Size([10, 2]) torch.Size([10, 1])yield features[batch_indices], labels[batch_indices]  # 每次从1000里随机抽十个,抽100次# 定义模型
def linreg(X, w, b):  # @save"""线性回归模型。"""return torch.matmul(X, w) + b# 定义损失函数
def squared_loss(y_hat, y):  # @save"""均⽅损失。"""return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2# 定义优化算法
def sgd(params, lr, batch_size):  # @save"""⼩批量随机梯度下降。"""with torch.no_grad():  # 禁用梯度计算以加快计算速度for param in params:param -= lr * param.grad / batch_size  # 为什么要除以batch_size?# 因为我们计算的损失是⼀个批量样本的总和,# 所以我们⽤批量⼤小(batch_size)来归⼀化步⻓,这样步⻓⼤小就不会取决于我们对批量⼤小的选择。param.grad.zero_()  # 梯度清零def main():# true_w = torch.tensor([2, -3.4])true_w = torch.tensor([2, -3.4])  # true_w的第二个参数决定点形状的分布趋势,因为后面画图的时候X取得是第二列true_b = 4.2features, labels = synthetic_data(true_w, true_b, 1000)  # 生成人造数据# 注意,features 中的每⼀⾏都包含⼀个⼆维数据样本,labels 中的每⼀⾏都包含⼀维标签值(⼀个标量)。# print('features:', features[0], '\nlabel:', labels[0])# features: tensor([-2.7495, -0.2577])# label: tensor([-0.4297])# 通过⽣成第⼆个特征 features[:, 1] 和 labels 的散点图,可以直观地观察到两者之间的线性关系。d2l.set_figsize()d2l.plt.scatter(features[:, 1].detach().numpy(), labels.detach().numpy(), 1)  # 1是点的大小d2l.plt.show()# 为什么X小y大,X大y小?(见上)# 读取数据batch_size = 10for X, y in data_iter(batch_size, features, labels):# print(X, '\n', y)break#     tensor([[-0.0187, -0.4961],#         [ 0.2226,  0.9894],#         [ 0.9929,  0.0527],#         [-1.6255,  1.0358],#         [-1.2527,  0.8796],#         [-0.4309,  2.4232],#         [ 0.2163,  1.3185],#         [ 1.1462, -0.5068],#         [-0.9749,  0.4630],#         [ 0.4969,  1.8860]])#  tensor([[ 5.8492],#         [ 1.2785],#         [ 6.0115],#         [-2.5758],#         [-1.2782],#         [-4.9061],#         [ 0.1676],#         [ 8.2293],#         [ 0.6850],#         [-1.2083]])w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)b = torch.zeros(1, requires_grad=True)lr = 0.03num_epochs = 3  # 迭代周期net = linregloss = squared_loss# print(type(net))    # <class 'function'>for epoch in range(num_epochs):  # 这个循环有3次for X, y in data_iter(batch_size, features, labels):  # 这个循环有100次l = loss(net(X, w, b), y)  # `X`和`y`的⼩批量损失# 因为`l`形状是(`batch_size`, 1),⽽不是⼀个标量。`l`中的所有元素被加到⼀起,# 并以此计算关于[`w`, `b`]的梯度l.sum().backward()  # backward的调用者是l.sum()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}')# epoch 1, loss 0.037703# w的估计误差: tensor([ 0.1122, -0.1454], grad_fn=<SubBackward0>)# b的估计误差: tensor([0.2054], grad_fn=<RsubBackward1>)# epoch 2, loss 0.000141# w的估计误差: tensor([ 0.0064, -0.0065], grad_fn=<SubBackward0>)# b的估计误差: tensor([0.0099], grad_fn=<RsubBackward1>)# epoch 3, loss 0.000050# w的估计误差: tensor([7.0143e-04, 3.7909e-05], grad_fn=<SubBackward0>)# b的估计误差: tensor([0.0002], grad_fn=<RsubBackward1>)# 注意,我们不应该想当然地认为我们能够完美地恢复参数。在机器学习中,我们通常不太关⼼恢复真正的参# 数,而更关⼼那些能⾼度准确预测的参数。幸运的是,即使是在复杂的优化问题上,随机梯度下降通常也能# 找到⾮常好的解。其中⼀个原因是,在深度⽹络中存在许多参数组合能够实现⾼度精确的预测。if __name__ == '__main__':main()

3.2.8 ⼩结(116)

• 我们学习了深度⽹络是如何实现和优化的。在这⼀过程中只使⽤张量和⾃动微分,不需要定义层或复杂的优化器。
• 这⼀节只触及到了表⾯知识。在下⾯的部分中,我们将基于刚刚介绍的概念描述其他模型,并学习如何更简洁地实现其他模型。

3.2.9 练习(116)

  1. 如果我们将权重初始化为零,会发⽣什么。算法仍然有效吗?(可以,完全没问题)
  2. 假设你是 乔治·西蒙·欧姆49 ,试图为电压和电流的关系建⽴⼀个模型。你能使⽤⾃动微分来学习模型的参数吗?
  3. 您能基于 普朗克定律50 使⽤光谱能量密度来确定物体的温度吗?
  4. 如果你想计算⼆阶导数可能会遇到什么问题?你会如何解决这些问题?
  5. 为什么在 squared_loss 函数中需要使⽤ reshape 函数?(对啊,为啥啊,不用reshape不也可以吗?)
  6. 尝试使⽤不同的学习率,观察损失函数值下降的快慢。
  7. 如果样本个数不能被批量⼤小整除,data_iter函数的⾏为会有什么变化?

https://discuss.d2l.ai/t/1778

3.3 线性回归的简洁实现(116)(框架功能:张量存储、线代运算、自动微分求梯度、【数据迭代器、损失函数、优化器】)

3.3.1 生成数据集(117)

3.3.2 读取数据集(117)

我们可以调⽤框架中现有的API来读取数据。我们将 features 和 labels 作为API的参数传递,并在实例化数据迭代器对象时指定 batch_size。此外,布尔值 is_train 表⽰是否希望数据迭代器对象在每个迭代周期内打乱数据。

使⽤ data_iter 的⽅式与我们在 3.2节 中使⽤ data_iter 函数的⽅式相同。为了验证是否正常⼯作,让我们读取并打印第⼀个小批量样本。与 3.2节 不同,这⾥我们使⽤ iter 构造Python迭代器,并使⽤ next 从迭代器中获取第⼀项。

3.3.3 定义模型(118)(线性回归)

简而言之,就是偷懒

我们也得把定义模型这项工作给简化

对于标准操作,我们可以使⽤框架的预定义好的层。这使我们只需关注使⽤哪些层来构造模型,而不必关注层的实现细节。我们⾸先定义⼀个模型变量net,它是⼀个 Sequential 类的实例。Sequential 类为串联在⼀起的多个层定义了⼀个容器。当给定输⼊数据,Sequential 实例将数据传⼊到第⼀层,然后将
第⼀层的输出作为第⼆层的输⼊,依此类推。在下⾯的例⼦中,我们的模型只包含⼀个层,因此实际上不需要Sequential。但是由于以后⼏乎所有的模型都是多层的,在这⾥使⽤Sequential会让你熟悉标准的流⽔线。

回顾 图3.1.2 中的单层⽹络架构,这⼀单层被称为 全连接层(fully-connected layer),因为它的每⼀个输⼊都通过矩阵-向量乘法连接到它的每个输出。

在 PyTorch 中,全连接层在 Linear 类中定义。值得注意的是,我们将两个参数传递到 nn.Linear 中。第⼀个指定输⼊特征形状,即 2,第⼆个指定输出特征形状,输出特征形状为单个标量,因此为 1。(就是俩神经元变一个神经元呗!)

3.3.4 初始化模型参数(119)

在使⽤net之前,我们需要初始化模型参数。如在线性回归模型中的权重和偏置。深度学习框架通常有预定义的⽅法来初始化参数。在这⾥,我们指定每个权重参数应该从均值为0、标准差为0.01的正态分布中随机采样,偏置参数将初始化为零。

正如我们在构造 nn.Linear 时指定输⼊和输出尺⼨⼀样。现在我们直接访问参数以设定初始值。我们通过net[0] 选择⽹络中的第⼀个图层,然后使weight.data 和 bias.data ⽅法访问参数。然后使⽤替换⽅法 normal_ 和 fill_ 来重写参数值。

3.3.5 定义损失函数(119)

计算均⽅误差使⽤的是MSELoss类,也称为平⽅ L2 范数。默认情况下,它返回所有样本损失的平均值。

3.3.6 定义优化算法(119)(SGD:随机梯度下降(stochastic gradient descent))

小批量随机梯度下降算法是⼀种优化神经⽹络的标准⼯具,PyTorch 在 optim 模块中实现了该算法的许多变种。当我们实例化 SGD 实例时,我们要指定优化的参数(可通过 net.parameters() 从我们的模型中获得)以及优化算法所需的超参数字典。小批量随机梯度下降只需要设置 lr值,这⾥设置为 0.03。(批量batch不设置了??【在前面创建数据迭代器那儿设置了】)

3.3.7 训练(119)

通过深度学习框架的⾼级API来实现我们的模型只需要相对较少的代码。我们不必单独分配参数、不必定义我们的损失函数,也不必⼿动实现小批量随机梯度下降。当我们需要更复杂的模型时,⾼级API的优势将⼤⼤增加。当我们有了所有的基本组件,训练过程代码与我们从零开始实现时所做的⾮常相似。

回顾⼀下:在每个迭代周期⾥,我们将完整遍历⼀次数据集(train_data),不停地从中获取⼀个小批量的输⼊和相应的标签。对于每⼀个小批量,我们会进⾏以下步骤:

• 通过调⽤ net(X) ⽣成预测并计算损失 l(正向传播)。
• 通过进⾏反向传播来计算梯度。
• 通过调⽤优化器来更新模型参数。

为了更好的衡量训练效果,我们计算每个迭代周期后的损失,并打印它来监控训练过程。

下⾯我们⽐较⽣成数据集的真实参数和通过有限数据训练获得的模型参数。要访问参数,我们⾸先从 net 访问所需的层,然后读取该层的权重和偏置。正如在从零开始实现中⼀样,我们估计得到的参数与⽣成数据的真实参数⾮常接近。

以下是所有代码:

代码:线性回归的简洁实现

import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l# 读取数据——数据迭代器
def load_array(data_arrays, batch_size, is_train=True):  # @save"""构造⼀个PyTorch数据迭代器。"""dataset = data.TensorDataset(*data_arrays)return data.DataLoader(dataset, batch_size, shuffle=is_train)def main():# 生成数据集true_w = torch.tensor([2, -3.4])true_b = 4.2features, labels = d2l.synthetic_data(true_w, true_b, 1000)batch_size = 10data_iter = load_array((features, labels), batch_size)  # 创建数据迭代器# print(next(iter(data_iter)))# [tensor([[-0.2552, -1.5246],#         [ 0.0945,  0.1609],#         [ 0.5682, -0.7111],#         [ 0.3185,  1.3219],#         [ 0.2326, -0.0534],#         [ 0.5968, -0.7186],#         [ 0.2951,  1.7579],#         [ 0.3770,  0.0517],#         [-1.3157, -1.0104],#         [ 0.5440, -0.4792]]), tensor([[ 8.8947],#         [ 3.8377],#         [ 7.7548],#         [ 0.3475],#         [ 4.8705],#         [ 7.8354],#         [-1.1890],#         [ 4.7883],#         [ 5.0079],#         [ 6.9127]])]# `nn` 是神经⽹络的缩写from torch import nnnet = nn.Sequential(nn.Linear(2, 1))  # 俩神经元输出到一个神经元?# 初始化神经网络(net)参数# print(net[0])   # Linear(in_features=2, out_features=1, bias=True)net[0].weight.data.normal_(0, 0.01)  # 初始化权重wnet[0].bias.data.fill_(0)  # 初始化偏置# 定义损失函数loss = nn.MSELoss()  # 均方误差损失函数# 定义优化算法trainer = torch.optim.SGD(net.parameters(), lr=0.03)# 监视训练过程num_epochs = 3for epoch in range(num_epochs):for X, y in data_iter:l = loss(net(X), y)trainer.zero_grad() # 梯度清零(一般放在backward函数前)# print(type(trainer))    # <class 'torch.optim.sgd.SGD'>l.backward()    # 计算梯度trainer.step()  # 进行单次优化 (参数更新)l = loss(net(features), labels)print(f'epoch {epoch + 1}, loss {l:f}')# epoch 1, loss 0.000218# epoch 2, loss 0.000102# epoch 3, loss 0.000101w = net[0].weight.dataprint('w的估计误差:', true_w - w.reshape(true_w.shape))b = net[0].bias.dataprint('b的估计误差:', true_b - b)# w的估计误差: tensor([0.0002, 0.0001])# b的估计误差: tensor([0.0005])if __name__ == '__main__':main()

3.3.8 ⼩结(120)

• 我们可以使⽤ PyTorch 的⾼级 API更简洁地实现模型。
• 在 PyTorch 中,data 模块提供了数据处理⼯具,nn 模块定义了⼤量的神经⽹络层和常⻅损失函数。
• 我们可以通过_ 结尾的⽅法将参数替换,从而初始化参数。

3.3.9 练习

  1. 如果我们⽤ nn.MSELoss() 替换 nn.MSELoss(reduction=‘sum’),为了使代码的⾏为相同,需要怎么更改学习速率?为什么?(添加reduction='sum’后,学习变慢了。。。)
  2. 查看 PyTorch ⽂档,了解提供了哪些损失函数和初始化⽅法。⽤Huber损失来代替。
  3. 你如何访问 net[0].weight 的梯度?
    这样?
    print(net[0].weight.grad)   # tensor([[0.0062, 0.0095]])print(net[0].bias.grad) # tensor([-0.0025])

https://discuss.d2l.ai/t/1781

3.4 softmax回归(分类问题)(121)

在 3.1节 中我们介绍了线性回归。随后,在 3.2节 中我们从头实现了线性回归。然后在 3.3节 中我们使⽤深度学习框架的⾼级API来完成繁重的⼯作。

回归可以⽤于预测 多少的问题。⽐如预测房屋被售出价格,或者棒球队可能获得的胜利数,⼜或者患者住院的天数。

通常,机器学习实践者⽤分类这个词来描述两个有微妙差别的问题:
(1)我们只对样本的硬性类别感兴趣,即属于哪个类别;
(2)我们希望得到软性类别,即得到属于每个类别的概率。这两者的界限往往很模糊。其中的⼀个原因是,即使我们只关⼼硬类别,我们仍然使⽤软类别的模型。

3.4.1 分类问题(121)(独热编码(one-hot encoding))

独热编码是⼀个向量,它的分量和类别⼀样多。类别对应的分量设置为1,其他所有分量设置为0。在我们的例⼦中,标签 y 将是⼀个三维向量,其中 (1, 0, 0) 对应于“猫”、(0, 1, 0) 对应于“鸡”、(0, 0, 1) 对应于“狗”:

y ∈ {(1, 0, 0),(0, 1, 0),(0, 0, 1)}.

3.4.2 网络结构(122)

为了估计所有可能类别的条件概率,我们需要⼀个有多个输出的模型,每个类别对应⼀个输出。为了解决线性模型的分类问题,我们需要和输出⼀样多的仿射函数(affine function)。每个输出对应于它⾃⼰的仿射函数。在我们的例⼦中,由于我们有4个特征3个可能的输出类别,我们将需要12个标量来表⽰权重(带下标的w),3个标量来表⽰偏置(带下标的b)。下⾯我们为每个输⼊计算三个未归⼀化的预测(logits):o1、o2和o3。


我们可以⽤神经⽹络图 图3.4.1 来描述这个计算过程。与线性回归⼀样,softmax回归也是⼀个单层神经⽹络。由于计算每个输出o1、o2和o3取决于所有输⼊x1、x2、x3和x4,所以softmax回归的输出层也是全连接层。


为了更简洁地表达模型,我们仍然使⽤线性代数符号。通过向量形式表达为 o = Wx + b,这是⼀种更适合数学和编写代码的形式。我们已经将所有权重放到⼀个 3 × 4 矩阵中。对于给定数据样本的特征 x,我们的输出是由权重与输⼊特征进⾏矩阵-向量乘法再加上偏置b得到的。

3.4.3 全连接层的参数开销(122)

正如我们将在后续章节中看到的,在深度学习中,全连接层⽆处不在。然而,顾名思义,全连接层是“完全”连接的,可能有很多可学习的参数。具体来说,对于任何具有d个输⼊和q个输出的全连接层,参数开销为O(dq),在实践中可能⾼得令⼈望而却步。幸运的是,将d个输⼊转换为q个输出的成本可以减少到

其中超参数n可以由我们灵活指定,以在实际应⽤中平衡参数节约和模型有效性 [Zhang et al., 2021] 。

3.4.4 softmax运算(122)(校准(calibration)属性)

在这⾥要采取的主要⽅法是将模型的输出视作为概率。我们将优化参数以最⼤化观测数据的概率。为了得到预测结果,我们将设置⼀个阈值,如选择具有最⼤概率的标签。


你可能会想能否将未归⼀化的预测 o 直接视作我们感兴趣的输出。但是,将线性层的输出直接视为概率时存在⼀些问题:⼀⽅⾯,没有限制这些数字的总和为1。另⼀⽅⾯,根据输⼊的不同,它们可以为负值。这些违反了 2.6节 中所说的概率基本公理。

要将输出视为概率,我们必须保证在任何数据上的输出都是⾮负的且总和为1。此外,我们需要⼀个训练⽬标,来⿎励模型精准地估计概率。在分类器输出0.5的所有样本中,我们希望这些样本有⼀半实际上属于预测的类。这个属性叫做校准(calibration)

社会科学家邓肯·卢斯于1959年在选择模型(choice models)的背景下发明的softmax函数正是这样做的。为了将未归⼀化的预测变换为⾮负并且总和为1,同时要求模型保持可导。**我们⾸先对每个未归⼀化的预测求幂,这样可以确保输出⾮负。为了确保最终输出的总和为1,我们再对每个求幂后的结果除以它们的总和。**如下式:

(注:exp(n)函数是返回自然之数e的n次幂,这真是太牛掰了@!)



(上面这个公式啥意思,看不太懂??)

尽管softmax是⼀个⾮线性函数,但softmax回归的输出仍然由输⼊特征的仿射变换决定。因此,softmax回归是⼀个线性模型。

3.4.5 小批量样本的⽮量化(123)


3.4.6 损失函数(123)

接下来,我们需要⼀个损失函数来度量预测概率的效果。我们将依赖最大似然估计,这与我们在为线性回归(3.1.3节 )中的均⽅误差⽬标提供概率证明时遇到的概念完全相同。

对数似然(124)



根据最⼤似然估计,我们最⼤化 P(Y | X),相当于最小化负对数似然:


softmax及其导数(124)

由于softmax和相关的损失函数很常⻅,因此值得我们更好地理解它的计算⽅式。将 (3.4.3) 代⼊损失 (3.4.8)中。利⽤softmax的定义,我们得到:



注:o为未归一化的预测值

换句话说,导数是我们模型分配的概率(由softmax得到)与实际发⽣的情况(由独热标签向量表⽰)之间的差异。从这个意义上讲,与我们在回归中看到的⾮常相似,其中梯度是观测值y和估计值yˆ之间的差异。这不是巧合,在任何指数族分布(参⻅ 关于分布的在线附录53)模型中,对数似然的梯度正是由这给出的。这使梯度计算在实践中变得容易。
53 https://d2l.ai/chapter_appendix-mathematics-for-deep-learning/distributions.html

交叉熵损失(125)

现在考虑这样⼀个例⼦:我们观察到的不仅仅是⼀个结果,而是整个结果分布。对于标签 y,我们可以使⽤与以前相同的表⽰形式。唯⼀的区别是,我们现在⽤⼀个概率向量表⽰,如(0.1, 0.2, 0.7),而不是仅包含⼆元项的向量(0, 0, 1)。我们使⽤ (3.4.8) 来定义损失 l。它是所有标签分布的预期损失值。此损失称为 交叉熵损失(cross-entropy loss),它是分类问题最常⽤的损失之⼀。我们将通过介绍信息论的基础来理解这个名字。如果你想了解更多信息论细节,你可以进⼀步参考 信息论的在线附录54。

3.4.7 信息论基础(125)略

3.4.8 模型预测和评估(126)

在训练softmax回归模型后,给出任何样本特征,我们可以预测每个输出类别的概率。通常我们使⽤预测概率最⾼的类别作为输出类别。如果预测与实际类别(标签)⼀致,则预测是正确的。在接下来的实验中,我们将使⽤ 准确率来评估模型的性能。准确率等于正确预测数与预测的总数之间的⽐率。

3.4.9 小结(136)

• softmax运算获取⼀个向量并将其映射为概率。
• softmax回归适⽤于分类问题。它使⽤了softmax运算中输出类别的概率分布。
• 交叉熵是⼀个衡量两个概率分布之间差异的很好的度量。它测量给定模型编码数据所需的⽐特数。

3.4.10 练习(127)



https://discuss.d2l.ai/t/1785

3.5 图像分类数据集(127)

⽬前⼴泛使⽤的图像分类数据集之⼀是 MNIST 数据集 [LeCun et al., 1998]。虽然它是很不错的基准数据集,但按今天的标准,即使是简单的模型也能达到95%以上的分类准确率,因此不适合区分强模型和弱模型。如今,MNIST更像是⼀个健全检查,而不是⼀个基准。为了提⾼难度,我们将在接下来的章节中讨论在2017年发布的性质相似但相对复杂的Fashion-MNIST数据集 [Xiao et al., 2017]。

3.5.1 读取数据集(127)

我们可以通过框架中的内置函数将 Fashion-MNIST 数据集下载并读取到内存中。

Fashion-MNIST 由 10 个类别的图像组成,每个类别由训练数据集中的 6000 张图像和测试数据集中的 1000 张图像组成。测试数据集(test dataset)不会⽤于训练,只⽤于评估模型性能。训练集和测试集分别包含 60000和 10000 张图像。

每个输⼊图像的⾼度和宽度均为 28 像素。数据集由灰度图像组成,其通道数为1。为了简洁起⻅,在这本书中,我们将⾼度h像素,宽度w像素图像的形状记为h × w或(h, w)。

Fashion-MNIST中包含的10个类别分别为t-shirt(T恤)、trouser(裤⼦)、pullover(套衫)、dress(连⾐裙)、coat(外套)、sandal(凉鞋)、shirt(衬衫)、sneaker(运动鞋)、bag(包)和ankle boot(短靴)。以下函数
⽤于在数字标签索引及其⽂本名称之间进⾏转换。

get_fashion_mnist_labels(labels)

我们现在可以创建⼀个函数来可视化这些样本。

show_images(imgs, num_rows, num_cols, titles=None, scale=1.5)

以下是训练数据集中前⼏个样本的图像及其相应的标签(⽂本形式)。

X, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))
show_images(X.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y));
测试代码:
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l# import matplotlib.pyplot as plt
# plt.switch_backend('agg')
# d2l.plt.switch_backend('agg')# 以下函数⽤于在数字标签索引及其⽂本名称之间进⾏转换。
def get_fashion_mnist_labels(labels):  # @save"""返回Fashion-MNIST数据集的⽂本标签。"""text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']return [text_labels[int(i)] for i in labels]# 将图像数据可视化(函数每太仔细看里面的函数啥意思!)
def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):  # @save"""Plot a list of images."""figsize = (num_cols * scale, num_rows * scale)_, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)axes = axes.flatten()for i, (ax, img) in enumerate(zip(axes, imgs)):if torch.is_tensor(img):# 图⽚张量ax.imshow(img.numpy())else:# PIL图⽚ax.imshow(img)ax.axes.get_xaxis().set_visible(False)ax.axes.get_yaxis().set_visible(False)if titles:ax.set_title(titles[i])return axesdef main():# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式# 并除以255使得所有像素的数值均在0到1之间trans = transforms.ToTensor()mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True,transform=trans,download=True)mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False,transform=trans, download=True)# print(len(mnist_train), len(mnist_test))  # 60000 10000# print(mnist_train[0][0].shape)  # torch.Size([1, 28, 28])X, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))show_images(X.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y))d2l.plt.show()  # 绘图不显示,加这个函数就行if __name__ == '__main__':main()


我发现有好多matplotlib的函数方法看不懂,有一点懵逼,极大的影响了我学习神经网络的效率,d但是现在时间紧迫。我决定直接去搞个pytorch yolovX已经搭建好的训练框架,来直接跑一下,然后导出可用c++调用的模型,调取摄像头输入图像,让它输出结果,生成可用于生产的可执行文件

(d2l-ai/d2l-zh)《动手学深度学习》pytorch 笔记(4)线性神经网络(暂停)相关推荐

  1. 【AI】《动手学-深度学习-PyTorch版》笔记(五):线性代数

    AI学习目录汇总 1.标量 1.1 介绍 标量就是我们常见的单个数字(包括整数.小数等等),可以使用只有一个元素的张量表示 1.2 表示方法 用小写字母表示,如:x.y.z 1.3 程序示例 impo ...

  2. 动手学深度学习(PyTorch实现)(六)--卷积神经网络基础

    卷积神经网络基础 1. 二维卷积层 1.1 二维互相关运算 1.2 互相关运算与卷积运算 1.3 特征图与感受野 2. 填充与步幅 2.1 填充 2.2 步幅 3. 多通道 3.1 多输入通道 3.2 ...

  3. 伯禹公益AI《动手学深度学习PyTorch版》Task 07 学习笔记

    伯禹公益AI<动手学深度学习PyTorch版>Task 07 学习笔记 Task 07:优化算法进阶:word2vec:词嵌入进阶 微信昵称:WarmIce 优化算法进阶 emmmm,讲实 ...

  4. 伯禹公益AI《动手学深度学习PyTorch版》Task 03 学习笔记

    伯禹公益AI<动手学深度学习PyTorch版>Task 03 学习笔记 Task 03:过拟合.欠拟合及其解决方案:梯度消失.梯度爆炸:循环神经网络进阶 微信昵称:WarmIce 过拟合. ...

  5. 伯禹公益AI《动手学深度学习PyTorch版》Task 05 学习笔记

    伯禹公益AI<动手学深度学习PyTorch版>Task 05 学习笔记 Task 05:卷积神经网络基础:LeNet:卷积神经网络进阶 微信昵称:WarmIce 昨天打了一天的<大革 ...

  6. 伯禹公益AI《动手学深度学习PyTorch版》Task 06 学习笔记

    伯禹公益AI<动手学深度学习PyTorch版>Task 06 学习笔记 Task 06:批量归一化和残差网络:凸优化:梯度下降 微信昵称:WarmIce 批量归一化和残差网络 BN和Res ...

  7. 伯禹公益AI《动手学深度学习PyTorch版》Task 04 学习笔记

    伯禹公益AI<动手学深度学习PyTorch版>Task 04 学习笔记 Task 04:机器翻译及相关技术:注意力机制与Seq2seq模型:Transformer 微信昵称:WarmIce ...

  8. 【动手学深度学习PyTorch版】6 权重衰退

    上一篇移步[动手学深度学习PyTorch版]5 模型选择 + 过拟合和欠拟合_水w的博客-CSDN博客 目录 一.权重衰退 1.1 权重衰退 weight decay:处理过拟合的最常见方法(L2_p ...

  9. 【动手学深度学习PyTorch版】12 卷积层

    上一篇移步[动手学深度学习PyTorch版]11 使用GPU_水w的博客-CSDN博客 目录 一.卷积层 1.1从全连接到卷积 ◼ 回顾单隐藏层MLP ◼ Waldo在哪里? ◼ 原则1-平移不变性 ...

  10. 李沐动手学深度学习(pytorch版本)d2lzh_pytorch包的缺少安装问题

    学习深度学习时候,很多人参考的是李沐的动手学深度学习Pytorch版本(附上官方地址:https://tangshusen.me/Dive-into-DL-PyTorch/#/). 在学习3.5.1节 ...

最新文章

  1. mysql实验报告四_实验报告四
  2. websocket客户端
  3. DL:深度学习算法(神经网络模型集合)概览之《THE NEURAL NETWORK ZOO》的中文解释和感悟(二)
  4. 我设计了一个牛逼的本地缓存!
  5. 【概念信息】COPC概览
  6. python操作excel表格写入多行和多列_python多处理:写入同一excel-fi
  7. Redis安装与配置( Windows10 )
  8. 深入浅出-iOS程序性能优化
  9. Linux两种方式rd.break和init重置root管理员密码
  10. 2020计算机二级office高级应用,2020计算机二级ms-office高级应用试题.docx
  11. [Lisp]slime
  12. Akka网络编程基本介绍
  13. “无法找到Internet Explorer”的解决方法
  14. 12月13日 新概念3一周一篇计划
  15. 安卓手机小说阅读器_手机阅读的好帮手,安卓小说神奇的扛把子
  16. PR转场预设 放大特效带有重影效果的PR视频转场预设
  17. 绿云签约服务的酒店数量超越2万家,同比增长超过15%
  18. 企业微信怎么通过手机号添加好友?企业微信添加好友话术!
  19. 50G-PON,继10G PON之后的新一代PON技术
  20. Hadoop生态系统——HiveQL操作实战

热门文章

  1. 纯HTML+CSS带说明的黄色导航菜单
  2. 硬件描述语言(HDL)基础——过程块
  3. mysql 根据分数分等级_数分面试-SQL篇
  4. JS字符串截取 “指定字符” 前面和后面的内容!
  5. 从零到一:如何用你的电脑成功登录QQ
  6. 夫妻驾驶途中打瞌睡 车辆定速巡航120迈飞下四米高速路
  7. maven聚合工程里子工程导入依赖出现unknown错误
  8. QMessageBox 中的 OK 按钮改为中文“确定”
  9. 小米 9 SE 获取Root 和 安装Magisk
  10. vm内核参数之内存脏页dirty_writeback_centisecs和dirty_expire_centisecs