动手学深度学习pytorch(第一版)学习笔记 —— 第三章

目   录

第三章 ——— 线性神经网络

1 线性回归

1.1 线性回归基本知识

1.1.1 线性回归基本元素

1.1.2 线性模型

1.1.3 损失函数

1.1.4 解析解

1.1.5 梯度下降

1.1.6 正态分布

1.2 向量化加速

1.3 从线性回归到深度网络

1.4 线性回归从0开始实现

1.5 线性回归的简洁实现

1.5.1 知识总结

1.5.2 函数使用

1.5.3 简单练习

1.5.4 本节链接

1.6 小结

2 softmax回归

2.1 分类问题

2.2 网络架构

2.3 softmax运算

2.3.1 小批量样本的矢量化

2.3.2 对数似然

2.3.3  softmax及其导数

2.4 softmax回归的简洁实现

2.4.1 图像分类数据集

2.4.2 softmax回归的简洁实现

3 结  语


第三章 ——— 线性神经网络

章节导航:在介绍深度神经网络之前,我们需要在本章了解神经网络的基础知识。经典统计学习技术中的线性回归和softmax回归可以视为线性神经网络,本章将会以此为基础进行学习。

1 线性回归

        回归是是能为一个或多个自变量与因变量之间关系建模的一类方法。 在自然科学和社会科学领域,回归经常用来表示输入和输出之间的关系

在机器学习领域中的大多数任务通常都与预测有关。

1.1 线性回归基本知识

1.1.1 线性回归基本元素

        为了解释线性回归,书中举了一个实际的例子:根据房屋的面积和房龄来估算房屋价格。 为了开发一个能预测房价的模型,我们需要收集一个真实的数据集。 这个数据集包括了房屋的销售价格、面积和房龄。

相对应的,在机器学习的术语中

  1. 数据集:训练数据集或训练集

  2. 每行数据:称为样本,也可以称为数据点或数据样本

  3. 试图预测的目标(如房屋价格):称为标签(label)或目标(target)

  4. 预测所依据的自变量(面积和房龄):称为特征(feature)或协变量(covariate)

1.1.2 线性模型

线性假设:目标(房屋价格)可以表示为特征(面积和房龄)的加权和

其中

  1. warea和wage 称为权重:决定了每个特征对我们预测值的影响
  2. b:称为偏置、偏移量或截距

给定一个数据集,我们的目标是寻找模型的权重w和偏置b, 使得根据模型做出的预测大体符合数据里的真实价格。

假设现有很多个房子(样本),将每一个房子的area和age作为样本乘以对应的权重加上偏置量就是该房子的价格。那我们就考虑运用线性代数的知识:

  1. 各个属性的权重:单独提取成一个列向量w
  2. 单个房子的多个特征作为一个行向量
  3. 多个房子就组成了一个矩阵X

所以就可以得到下列的式子,其中y是一个列向量,代表每一个房子的价格

1.1.3 损失函数

        损失函数能够量化目标的实际值与预测值之间的差距。 我们一般采用平方误差函数作为损失函数,其中y代表真实值,y^代表模型的预测值:

1.1.4 解析解

        首先,我们将偏置b合并到参数w中,合并方法是在包含所有参数的矩阵中附加一列。 我们的预测问题是最小化‖y−Xw‖^2。 这在损失平面上只有一个临界点,这个临界点对应于整个区域的损失极小点。 将损失关于w的导数设为0,得到解析解(可以通过自己动手算出来,比较简单):

1.1.5 梯度下降

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

  • |B|:表示每个小批量中的样本数,这也称为批量大小(batch size)
  •  η:表示学习率

        批量大小和学习率的值通常是手动预先指定,而不是通过模型训练得到的。 这些可以调整但不在训练过程中更新的参数称为超参数调参是选择超参数的过程。 超参数通常是我们根据训练迭代结果来调整的, 而训练迭代结果是在独立的验证数据集上评估得到的。在上述过程中我们就是不断调整w和b。

1.1.6 正态分布

正态分布想必大家并不陌生,如果有遗忘或者想重新了解可以参考:3.1. 线性回归 — 动手学深度学习 2.0.0 documentation

1.2 向量化加速

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

        书中这样进行证明的,制作一个定时器。在数据开始之前记下一个时间,然后采取俩种方式进行读取数据:使用For循环;使用重载的+运算符。在运算结束后计算所消耗的时间并打印出来,结果显示for循环的开销很大。因此:矢量化代码通常会带来数量级的加速。 另外,我们将更多的数学运算放到库中,而无须自己编写那么多的计算,从而减少了出错的可能性。

1.3 从线性回归到深度网络

        我们将线性回归模型描述为一个神经网络。 需要注意的是,该图只显示连接模式,即只显示每个输入如何连接到输出,隐去了权重和偏置的值。

  • 输入:x1,…,xd, 因此输入层中的输入数或称特征维度为d
  • 输出:网络的输出为o1,因此输出层中的输出数是1
  • 每个输入都与每个输出相连, 我们将这种变换 称为全连接层或称为稠密层

        因为输入值都是已经给定的,并且只有一个计算神经元。 由于模型重点在发生计算的地方,所以通常我们在计算层数时不考虑输入层。 也就是说,上图中神经网络层数为1。 我们可以将线性回归模型视为仅由单个人工神经元组成的神经网络,或称为单层神经网络

Question:什么时候可能比使用随机梯度下降更好?这种方法何时会失效?

Anwser:当解析解不存在或者太麻烦的时候就采用反向传播梯度下降,有时对于w的最大似然估计就是平方误差的解析解。

1.4 线性回归从0开始实现

代码内容较多,可以去笔记实现,也可参考:3.2. 线性回归的从零开始实现 — 动手学深度学习 2.0.0 documentation

Question:尝试使用不同的学习率,观察损失函数值下降的快慢。

Anwser:可以得到结论如下:

  • 正常情况下,学习率大一点,损失函数的值就小一点
  • 学习率太大,导致在最优解附近震荡,甚至会发散
  • 学习率太小,下降的太慢
  • 极端情况下,学习率极大,无法训练,梯度爆炸
  • 一般建议在e^-5  ~   e^-1

1.5 线性回归的简洁实现

1.5.1 知识总结

        包含步骤:生成数据集、读取数据集、定义模型、初始化模型参数、定义损失函数、定义优化算法、训练

        对于标准深度学习模型,我们可以使用框架的预定义好的层。这使我们只需关注使用哪些层来构造模型,而不必关注层的实现细节。也许在后面的使用中,我们就可以只需要在相应的层中使用对应框架模型就可以。

1.5.2 函数使用

使用iter构造Python迭代器,并使用next从迭代器中获取第一项

next(iter(data_iter))

net[0]选择网络中的第一个图层, 然后使用weight.databias.data方法访问参数,使用替换方法normal_fill_来重写参数值

net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)

计算均方误差使用的是MSELoss类,也称为平方L2范数。 默认情况下,它返回所有样本损失的平均值,这里的nn代表神经网络(是从torch中导入的,from  torch  import  nn)

loss = nn.MSELoss()

 PyTorch在optim模块实现了该算法的许多变种。 实例化一个SGD实例时,我们要指定优化的参数 (可通过net.parameters()从我们的模型中获得)以及优化算法所需的超参数字典。lr值设置为0.03。

1.5.3 简单练习

Question:如果将小批量的总损失替换为小批量损失的平均值,需要如何更改学习率?

Anwser:对比总损失来说,平均值意味着缩小了损失函数总损失的系数,这样就会要求学习率的调整变得更加细微才可以。

1.5.4 本节链接

3.3. 线性回归的简洁实现 — 动手学深度学习 2.0.0 documentation

1.6 小结

  • 机器学习模型中的关键要素是训练数据、损失函数、优化算法,还有模型本身。

  • 矢量化使数学表达上更简洁,同时运行的更快。

  • 最小化目标函数和执行极大似然估计等价。

  • 线性回归模型也是一个简单的神经网络。

  • 深度网络实现和优化的一过程中只使用张量和自动微分,不需要定义层或复杂的优化器。

  • 可以使用PyTorch的高级API更简洁地实现模型。

  • 在PyTorch中,data模块提供了数据处理工具,nn模块定义了大量的神经网络层和常见损失函数。

  • 我们可以通过_结尾的方法将参数替换,从而初始化参数。

2 softmax回归

2.1 分类问题

        从一个图像分类问题开始。 假设每次输入是一个2×2的灰度图像。 我们可以用一个标量表示每个像素值,每个图像对应四个特征x1,x2,x3,x4。 此外,假设每个图像属于类别“猫”“鸡”和“狗”中的一个。

书中介绍了一种表示分类数据的简单方法:独热编码。 独热编码是一个向量,它的分量和类别一样多。 类别对应的分量设置为1,其他所有分量设置为0。 在我们的例子中,标签y将是一个三维向量, 其中(1,0,0)对应于“猫”、(0,1,0)对应于“鸡”、(0,0,1)对应于“狗”。

可以这样进行理解:

  • 标签向量的维数取决于有多少个目标类别,猫、鸡、狗三个类别就代表是三维向量
  • 每个向量的值代表取该类的概率

2.2 网络架构

        书中为解决线性模型的分类问题,需要和输出一样多的仿射函数,每个输出都有对应于它自己的仿射函数。

  • 由于有4个特征和3个输出类别,因此需要12个标量来表示权重(带下标的w)
  • 3个标量来表示偏置(带下标的b)
  • 为每个输入计算三个未规范化的预测:o1、o2和o3

与线性回归一样,softmax回归也是一个单层神经网络。 由于计算每个输出o1、o2和o3取决于 所有输入x1、x2、x3和x4, 所以softmax回归的输出层也是全连接层

全连接层的参数开销:对于任何具有d个输入和q个输出的全连接层, 参数开销为O(dq),这个数字在实践中可能高得令人望而却步。 幸运的是,将d个输入转换为q个输出的成本可以减少到O(dq/n), 其中超参数n可以由我们灵活指定。

2.3 softmax运算

现在我们需要将预测的o直视为概率输出,所以需要解决2个问题:概率值输出总和为1,概率非负值。

  • 概率值输出总和为1:可以理解为向量的归一化
  • 概率值非负:采用取指数的方法

softmax函数能够将未规范化的预测变换为非负数并且总和为1,同时让模型保持可导的性质。

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

2.3.1 小批量样本的矢量化

假设读取了一个批量的样本X, 其中特征维度为d,批量大小为n,输出中有q个类别。

则小批量样本的特征为X∈R(n×d), 权重为W∈R(d×q), 偏置为b∈R(1×q)。

softmax回归的矢量计算表达式为:

由于X中的每一行代表一个数据样本, 那么softmax运算可以按行执行: 对于O的每一行,我们先对所有项进行幂运算,然后通过求和对它们进行标准化。

2.3.2 对数似然

softmax函数给出了一个向量y^, 我们可以将其视为对给定任意输入x的每个类的条件概率

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

对于任何标签y和模型预测y^,损失函数为:

上述的损失函数通常被称为交叉熵损失。 由于y是一个长度为q的独热编码向量, 所以除了一个项以外的所有项j都消失了。 由于所有y^j都是预测的概率,所以它们的对数永远不会大于0。

理解:对于一个样本,肯定有具体的分类,所以实际的y只有一个值为1,其余的概率均为0而y^一定是小于等于1的,因此他们的对数永远不会>0。

2.3.3  softmax及其导数

 将图3.4.3代入损失3.4.8中。 利用softmax的定义,可以得到:

其中 3.4.9 的理解:在真实的y中,只有一个是1,其他为0时,所以在下图第二个式子中将y^的下标换成了概率为1的那个y值

2.4 softmax回归的简洁实现

继续通过深度学习框架的高级API能够使实现

2.4.1 图像分类数据集

MNIST数据集是图像分类中广泛使用的数据集之一,但作为基准数据集过于简单。 我们将使用类似但更复杂的Fashion-MNIST数据集。

%matplotlib inline
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l

d2l.use_svg_display()

1)读取数据集

# 通过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)

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

len(mnist_train), len(mnist_test)

每个输入图像的高度和宽度均为28像素。 数据集由灰度图像组成,其通道数为1。 为了简洁起见,本书将高度h像素、宽度w像素图像的形状记为h×w或(h,w)。

mnist_train[0][0].shape

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

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]

2)读取小批量

为了使我们在读取训练集和测试集时更容易,我们使用内置的数据迭代器,而不是从零开始创建。 回顾一下,在每次迭代中,数据加载器每次都会读取一小批量数据,大小为batch_size。 通过内置数据迭代器,我们可以随机打乱了所有样本,从而无偏见地读取小批量。

batch_size = 256

def get_dataloader_workers():  #@save
    """使用4个进程来读取数据"""
    return 4

train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True,
                             num_workers=get_dataloader_workers())
# 我们看一下读取训练数据所需的时间。
timer = d2l.Timer()
for X, y in train_iter:
    continue
f'{timer.stop():.2f} sec'

3) 整合所有组件

现在我们定义load_data_fashion_mnist函数,用于获取和读取Fashion-MNIST数据集。 这个函数返回训练集和验证集的数据迭代器。 此外,这个函数还接受一个可选参数resize,用来将图像大小调整为另一种形状。

def load_data_fashion_mnist(batch_size, resize=None):  #@save
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0, transforms.Resize(resize))
    trans = transforms.Compose(trans)
    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)
    return (data.DataLoader(mnist_train, batch_size, shuffle=True,
                            num_workers=get_dataloader_workers()),
            data.DataLoader(mnist_test, batch_size, shuffle=False,
                            num_workers=get_dataloader_workers()))
# 下面,我们通过指定resize参数来测试load_data_fashion_mnist函数的图像大小调整功能。 
train_iter, test_iter = load_data_fashion_mnist(32, resize=64)
for X, y in train_iter:
    print(X.shape, X.dtype, y.shape, y.dtype)
    breaktrain_iter, test_iter = load_data_fashion_mnist(32, resize=64)
for X, y in train_iter:
    print(X.shape, X.dtype, y.shape, y.dtype)
    break

2.4.2 softmax回归的简洁实现

import torch
from torch import nn
from d2l import torch as d2l

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

初始化模型参数

如我们在 3.4节所述, softmax回归的输出层是一个全连接层。 因此,为了实现我们的模型, 我们只需在Sequential中添加一个带有10个输出的全连接层。 同样,在这里Sequential并不是必要的, 但它是实现深度模型的基础。 我们仍然以均值0和标准差0.01随机初始化权重。

# PyTorch不会隐式地调整输入的形状。因此,
# 我们在线性层前定义了展平层(flatten),来调整网络输入的形状
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))

def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)

net.apply(init_weights);

重新审视Softmax的实现

loss = nn.CrossEntropyLoss(reduction='none')

 优化算法

trainer = torch.optim.SGD(net.parameters(), lr=0.1)
# 训练
num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

3 结  语

本章链接:

3. 线性神经网络 — 动手学深度学习 2.0.0 documentation

学习链接:【视频+教材】原著大佬李沐带你读《动手学习深度学习》真的通俗易懂!深度学习入门必看!(人工智能、机器学习、神经网络、计算机视觉、图像处理、AI)_哔哩哔哩_bilibili

如果你有任何建议或疑问,欢迎留言讨论,最近每天基本上都会看留言,看到会及时回复。

### 关于《动手学深度学习PyTorch章节的解析与GitHub资源 对于《动手学深度学习》一书中的PyTorch相关内容,可以参考一些开源项目来加深理解。例如,在GitHub上存在多个针对PyTorch实现的代码库,这些代码库不仅提供了书中理论的实际应用案例,还通过详细的注释帮助读者更好地掌握知识点。 #### GitHub上的相关资源 1. **Glow-Pytorch仓库** 存在一个专注于PyTorch实现的Glow模型的仓库[^1],虽然该仓库主要关注的是特定模型的实现,但它展示了如何利用PyTorch构建复杂的深度学习架构。这种实践方式可以帮助理解《动手学深度学习》中涉及的高级主题。 2. **Awesome Semantic Segmentation PyTorch** 另外一个值得推荐的资源是`awesome-semantic-segmentation-pytorch`[^3],它收集了许多有关语义分割的经典算法及其PyTorch实现。尽管其侧重点在于计算机视觉领域中的具体任务,但对于深入理解PyTorch框架本身非常有帮助。 #### 如何查找具体的章节答案? 如果需要获取更贴近教材内容的具体解答或者示例程序,则建议直接查阅官方配套资料或者其他社区贡献者整理的内容。比如,《动手学深度学习》这本书本身就强调了从基础到实际操作的学习路径[^2],因此可以通过以下方法找到更多辅助材料: - 访问作者团队维护的相关网站或论坛; - 利用搜索引擎输入关键词如“动手学深度学习 pytorch github”,通常能找到第三方上传的教学文件夹; - 加入国内外技术交流群组询问经验丰富的开发者们是否有分享过类似的总结文档。 以下是基于上述提到的技术要点给出的一个简单例子——使用PyTorch定义一个多层感知机(MLP): ```python import torch from torch import nn class MLP(nn.Module): def __init__(self, input_size=784, hidden_size=256, num_classes=10): super(MLP, self).__init__() self.fc1 = nn.Linear(input_size, hidden_size) self.relu = nn.ReLU() self.fc2 = nn.Linear(hidden_size, num_classes) def forward(self, x): out = self.fc1(x) out = self.relu(out) out = self.fc2(out) return out model = MLP() # 创建实例对象 print(model) # 打印网络结构信息 ``` 此段脚本体现了创建自定义模块类的过程以及前向传播函数的设计思路。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值