其他分享
首页 > 其他分享> > Dive_into_Deep_Learning

Dive_into_Deep_Learning

作者:互联网

1.前言

1.2.关键组件

  1. 我们可以学习的数据(data)。
  2. 如何转换数据的模型(model)。
  3. 一个目标函数(objective function),用来量化模型的有效性。
  4. 调整模型参数以优化目标函数的算法

2.预备知识

2.1.数据操作

2.1.1.入门

  1. 张量:n维数组;
    1. 无论使用哪个深度学习框架,它的张量类都与Numpy的ndarray类似,但又比Numpy的ndarray多一些重要功能:
      首先,GPU很好地支持加速计算,而NumPy仅支持CPU计算;其次,张量类支持自动微分。这些功能使得张量类更适合深度学习。
    2. 表示由一个数值组成的数组,这个数组可能有多个维度。具有一个轴的张量对应数学上的向量(vector)。
      具有两个轴的张量对应数学上的矩阵(matrix)。具有两个轴以上的张量没有特殊的数学名称。
  2. 可以使用arange创建一个行向量x。它们被默认创建为浮点数。除非额外指定,否则新的张量将存储在内存中,并采用基于CPU的计算。
x = torch.arange(12)
x
tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
  1. shape属性来访问张量的形状 (沿每个轴的长度)
x.shape
torch.Size([12])
  1. 如果只想知道张量中元素的总数,即形状的所有元素乘积,可以检查它的大小(size)
x.numel()
12
  1. 要改变一个张量的形状而不改变元素数量和元素值,可以调用reshape函数。
X = x.reshape(3, 4)
X
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]])

不需要通过手动指定每个维度来改变形状。可以通过在希望张量自动推断的维度放置-1来调用此功能
6. 创建一个形状为(2,3,4)的张量,其中所有元素都设置为0

torch.zeros((2, 3, 4))

创建一个形状为(2,3,4)的张量,其中所有元素都设置为1

torch.ones((2, 3, 4))
  1. 创建一个形状为(3,4)的张量。其中的每个元素都从均值为0、标准差为1的标准高斯(正态)分布中随机采样
torch.randn(3, 4)
tensor([[-0.9464,  0.7712, -0.0070,  1.0236],
        [-2.1246, -0.7854, -1.9674, -0.1727],
        [ 0.0397, -0.0477, -0.0160, -0.0113]])

8.为所需张量中的每个元素赋予确定值

torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
tensor([[2, 1, 4, 3],
        [1, 2, 3, 4],
        [4, 3, 2, 1]])

2.1.2.运算

1.计算

x = torch.tensor([1.0, 2, 4, 8])
y = torch.tensor([2, 2, 2, 2])
x + y, x - y, x * y, x / y, x ** y  # **运算符是求幂运算
torch.exp(x)

2.张量连结(concatenate)

X = torch.arange(12, dtype=torch.float32).reshape((3,4))
Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)

(tensor([[ 0.,  1.,  2.,  3.],
         [ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.],
         [ 2.,  1.,  4.,  3.],
         [ 1.,  2.,  3.,  4.],
         [ 4.,  3.,  2.,  1.]]),
 tensor([[ 0.,  1.,  2.,  3.,  2.,  1.,  4.,  3.],
         [ 4.,  5.,  6.,  7.,  1.,  2.,  3.,  4.],
         [ 8.,  9., 10., 11.,  4.,  3.,  2.,  1.]]))

2.二元张量

X == Y

tensor([[False,  True, False,  True],
        [False, False, False, False],
        [False, False, False, False]])

3.张量元素求和,产生只有一个元素的张量

X.sum()

tensor(66.)

2.1.3.广播机制

1.广播机制(broadcasting mechanism)

  1. 首先,通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状。
  2. 其次,对生成的数组执行按元素操作。
a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b

(tensor([[0],
         [1],
         [2]]),
 tensor([[0, 1]]))

由于a和b分别是 3×1 和 1×2 矩阵,如果我们让它们相加,它们的形状不匹配。我们将两个矩阵广播为一个更大的 3×2 矩阵,如下所示:矩阵a将复制列,矩阵b将复制行,然后再按元素相加

a + b

tensor([[0, 1],
        [1, 2],
        [2, 3]])

2.1.4.索引和切片

和python一样

2.1.5.节省内存

运行一些操作可能会导致为新结果分配内存。

before = id(Y)
Y = Y + X
id(Y) == before

False

执行原地操作非常简单。我们可以使用切片表示法将操作的结果分配给先前分配的数组 zeros_like来分配一个全 0 的块

Z = torch.zeros_like(Y)
print('id(Z):', id(Z))
Z[:] = X + Y
print('id(Z):', id(Z))

id(Z): 140272150341696
id(Z): 140272150341696

也可以使用X += Y来减少操作的内存开销。

before = id(X)
X += Y
id(X) == before

True

2.1.6.转换为其他Python对象

1.转换后的结果不共享内存

A = X.numpy()
B = torch.tensor(A)
type(A), type(B)

(numpy.ndarray, torch.Tensor)

2.要将大小为1的张量转换为Python标量,我们可以调用item函数或Python的内置函数。

a = torch.tensor([3.5])
a, a.item(), float(a), int(a)

(tensor([3.5000]), 3.5, 3.5, 3)

2.2.数据预处理

2.2.1.读取数据集

2.2.2.处理缺失值

  1. iloc()
  2. fillna()
  3. get_dummies()

2.2.3.转换为张量格式

具体见 2.2数据预处理.py

2.3.线性代数

2.3.1.标量

2.3.2.向量

2.3.3.矩阵

A = torch.arange(20).reshape(5, 4)
A

tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15],
        [16, 17, 18, 19]])

矩阵转置 A.T

2.2.4.张量

见上文

2.3.5.张量算法的基本性质

  1. 给定具有相同形状的任意两个张量,任何按元素二元运算的结果都将是相同形状的张量
  2. 两个矩阵的按元素乘法称为哈达玛积(Hadamard product)(数学符号 ⊙) A * B
  3. 将张量乘以或加上一个标量不会改变张量的形状,其中张量的每个元素都将与标量相加或相乘

2.3.6.降维

1.求和降维

A_sum_axis0 = A.sum(axis=0)
A_sum_axis0, A_sum_axis0.shape

沿着行和列对矩阵求和,等价于对矩阵的所有元素进行求和

A.sum(axis=[0, 1])  # Same as `A.sum()`

2.平均值

A.mean(), A.sum() / A.numel()

(tensor(9.5000), tensor(9.5000))
A.mean(axis=0), A.sum(axis=0) / A.shape[0]

(tensor([ 8.,  9., 10., 11.]), tensor([ 8.,  9., 10., 11.])

2.3.6.1.非降维求和

  1. 例如,由于sum_A在对每行进行求和后仍保持两个轴,我们可以通过广播将A除以sum_A。
sum_A = A.sum(axis=1, keepdims=True)
sum_A

tensor([[ 6.],
        [22.],
        [38.],
        [54.],
        [70.]])
A / sum_A

tensor([[0.0000, 0.1667, 0.3333, 0.5000],
        [0.1818, 0.2273, 0.2727, 0.3182],
        [0.2105, 0.2368, 0.2632, 0.2895],
        [0.2222, 0.2407, 0.2593, 0.2778],
        [0.2286, 0.2429, 0.2571, 0.2714]])
  1. 沿某个轴计算A元素的累积总和,比如axis=0(按行计算),我们可以调用cumsum函数。此函数不会沿任何轴降低输入张量的维度
A.cumsum(axis=0)

tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  6.,  8., 10.],
        [12., 15., 18., 21.],
        [24., 28., 32., 36.],
        [40., 45., 50., 55.]])

2.3.7.点积

y = torch.ones(4, dtype = torch.float32)
x, y, torch.dot(x, y)

(tensor([0., 1., 2., 3.]), tensor([1., 1., 1., 1.]), tensor(6.))

2.3.8.矩阵-向量积

A.shape, x.shape, torch.mv(A, x)

(torch.Size([5, 4]), torch.Size([4]), tensor([ 14.,  38.,  62.,  86., 110.]))

2.3.9.矩阵-矩阵乘法

B = torch.ones(4, 3)
torch.mm(A, B)

tensor([[ 6.,  6.,  6.],
        [22., 22., 22.],
        [38., 38., 38.],
        [54., 54., 54.],
        [70., 70., 70.]])

2.3.10.范数

  1. 范数:在线性代数中,向量范数是将向量映射到标量的函数 f
    1. 如果我们按常数因子 α 缩放向量的所有元素,其范数也会按相同常数因子的绝对值缩放
    2. 三角不等式
    3. 简单地说范数必须是非负的
  2. L2 范数是向量元素平方和的平方根
u = torch.tensor([3.0, -4.0])
torch.norm(u)

tensor(5.)
  1. L1 范数,它表示为向量元素的绝对值之和
torch.abs(u).sum()

tensor(7.)
  1. 矩阵 X∈Rm×n 的弗罗贝尼乌斯范数(Frobenius norm)是矩阵元素平方和的平方根
torch.norm(torch.ones((4, 9)))
tensor(6.)

2.3.10.1.范数和目标

通常,目标,或许是深度学习算法最重要的组成部分(除了数据),被表达为范数。

2.4.微分

2.4.3梯度

2.5.自动求导

深度学习框架通过自动计算导数,即自动求导(automatic differentiation),来加快这项工作。实际中,根据我们设计的模型,
系统会构建一个计算图(computational graph),来跟踪计算是哪些数据通过哪些操作组合起来产生输出。自动求导使系统能够随后反向传播梯度。
这里,反向传播(backpropagate)只是意味着跟踪整个计算图,填充关于每个参数的偏导数。

2.5.1.一个简单的例子

import torch

x = torch.arange(4.0)
print(x)

x.requires_grad_(True)  # 等价于 `x = torch.arange(4.0, requires_grad=True)`
print(x.grad)  # 默认值是None

y = 2 * torch.dot(x, x)
print(y)

y.backward()
print(x.grad)

print(x.grad == 4 * x)

2.5.2.非标量变量的反向传播

2.5.3.分离计算

2.5.4. Python控制流的梯度计算

2.6.概率

2.7.查阅文档

3.线性神经网络

3.1.线性回归

3.1.5.小结

3.3.线性回归的简洁实现

3.3.1. 生成数据集

3.3.2.读取数据集

3.3.3.定义模型

3.3.4.初始化模型参数

3.3.5.定义损失函数

3.3.6.定义优化算法

3.3.7.训练

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

3.4.softmax回归

3.4.1.分类问题

独热编码 (one-hot encoding)。独热编码是一个向量,它的分量和类别一样多。类别对应的分量设置为1,其他所有分量设置为0。

3.4.2.网络结构

全连接层:每个输出取决于全部输入。

3.4.4.softmax运算

3.4.5. 小批量样本的矢量化

3.4.6. 损失函数

3.4.6.1. 对数似然

交叉熵损失 (cross-entropy loss)

3.4.7.2. 惊异

如果我们不能完全预测每一个事件,那么我们有时可能会感到惊异。当我们赋予一个事件较低的概率时,我们的惊异会更大。克劳德·香农决定用 log ⁡ 1 P ( j ) = − log ⁡ P ( j ) \log \frac{1}{P(j)}=-\log P(j) logP(j)1​=−logP(j)来量化一个人的 惊异 (surprisal)。在观察一个事件j,并赋予它(主观)概率P(j)。定义的熵是当分配的概率真正匹配数据生成过程时的 预期惊异 (expected surprisal)。

3.4.7.3. 重新审视交叉熵

所以,如果熵是知道真实概率的人所经历的惊异程度,那么你可能会想知道,什么是交叉熵? 交叉熵PQ,记为H(P,Q),是主观概率为Q的观察者在看到根据概率P实际生成的数据时的预期惊异。当P=Q时,交叉熵达到最低。在这种情况下,从PQ的交叉熵是 H ( P , P ) = H ( P ) H(P,P)=H(P) H(P,P)=H(P)。

简而言之,我们可以从两方面来考虑交叉熵分类目标:(i)最大化观测数据的似然;(ii)最小化传达标签所需的惊异。

3.4.9. 小结

3.7.softmax回归的简洁实现

3.7.1.初始化模型参数

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

3.7.2. 重新审视Softmax的实现

loss = nn.CrossEntropyLoss()

3.7.3. 优化算法

在这里,我们使用学习率为0.1的小批量随机梯度下降作为优化算法。这与我们在线性回归例子中的相同,这说明了优化器的普适性。

trainer = torch.optim.SGD(net.parameters(), lr=0.1)

3.7.4. 训练

num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

4.多层感知机

4.1.1.隐藏层

4.1.1.1.线性模型可能会出错

4.1.1.2.在网络中加入隐藏层

4.1.1.3.从线性到非线性

4.1.1.4.通用近似定理

4.1.2.激活函数

4.1.2.1.ReLU函数

4.1.2.2.sigmoid函数

$$
sigmoid(x)=\frac{1}{1+exp(-x)}

$$

4.1.2.3.tanh函数

$$
tanch(x)= \frac{1-exp(-2x)}{1+exp(-2x)}

$$

4.4.模型选择、欠拟合和过拟合

4.4.1.训练误差和泛化误差

4.4.1.1.统计学习理论

4.4.1.2. 模型复杂性

4.4.2. 模型选择

4.4.2.1. 验证集

4.4.2.2. K 折交叉验证

4.4.3. 欠拟合还是过拟合?

4.4.3.1. 模型复杂性

4.4.3.2. 数据集大小

另一个需要牢记的重要因素是数据集的大小。训练数据集中的样本越少,我们就越有可能(且更严重地)遇到过拟合。 随着训练数据量的增加,泛化误差通常会减小。
此外,一般来说,更多的数据不会有什么坏处。 对于固定的任务和数据分布,模型复杂性和数据集大小之间通常存在关系。
给出更多的数据,我们可能会尝试拟合一个更复杂的模型。能够拟合更复杂的模型可能是有益的。如果没有足够的数据,简单的模型可能更有用。
对于许多任务,深度学习只有在有数千个训练样本时才优于线性模型。从一定程度上来说,深度学习目前的成功要归功于互联网公司、廉价存储、互联设备以及数字化经济带来的海量数据集。

4.4.4. 多项式回归

4.4.多项式回归.py

4.5. 权重衰减

4.5.1. 范数与权重衰减

4.5.2. 高维线性回归

4.6. Dropout

4.6.1. 重新审视过拟合

4.6.2. 扰动的鲁棒性

根据设计,期望值保持不变,即$ E [ h " ] = h E [ h " ] = h E[h"]=hE[h^"]=h E[h"]=hE[h"]=h

4.6.3. 实践中的dropout

4.7. 正向传播、反向传播和计算图

4.7.1. 正向传播

4.7.3. 反向传播

4.7.4. 训练神经网络

4.8. 数值稳定性和模型初始化

4.8.1. 梯度消失和梯度爆炸

4.8.1.1. 梯度消失

4.8.1.2. 梯度爆炸

4.8.1.3. 打破对称性

4.8.2. 参数初始化

4.8.2.1. 默认初始化

4.8.2.2. Xavier初始化

4.8.3. 小结

5. 深度学习计算

5.1. 层和块

5.1.1. 自定义块

5.1.2. 顺序块

现在我们可以更仔细地看看Sequential类是如何工作的。回想一下Sequential的设计是为了把其他模块串起来。为了构建我们自己的简化的MySequential,我们只需要定义两个关键函数:

  1. 一种将块逐个追加到列表中的函数。
  2. 一种正向传播函数,用于将输入按追加块的顺序传递给块组成的“链条”。
class MySequential(nn.Module):
    def __init__(self, *args):
        super().__init__()
        for block in args:
            # 这里,`block`是`Module`子类的一个实例。我们把它保存在'Module'类的成员变量
            # `_modules` 中。`block`的类型是OrderedDict。
            self._modules[block] = block

    def forward(self, X):
        # OrderedDict保证了按照成员添加的顺序遍历它们
        for block in self._modules.values():
            X = block(X)
        return X

5.1.3. 在正向传播函数中执行代码

5.1.5. 小结

5.2. 参数管理

5.2.1. 参数访问

我们从已有模型中访问参数。当通过Sequential类定义模型时,我们可以通过索引来访问模型的任意层。这就像模型是一个列表一样。每层的参数都在其属性中。如下所示,我们可以检查第二个全连接层的参数。

print(net[2].state_dict())

5.2.1.1. 目标参数

5.2.1.2. 一次性访问所有参数

5.2.2. 参数初始化

5.2.2.1. 内置初始化

5.2.2.2. 自定义初始化

5.2.3. 参数绑定

5.5. 读写文件

5.5.1. 加载和保存张量

5.5.2. 加载和保存模型参数

torch.save(net.state_dict(), 'mlp.params')

5.6. GPU

5.6.1. 计算设备

5.6.2. 张量与gpu

5.6.2.1. 存储在GPU上

5.6.2.3. 旁注

5.6.3. 神经网络与GPU

5.6.4. 小结

6. 卷积神经网络

6.1.1. 不变性

  1. 平移不变性 (translation invariance):不管检测对象出现在图像中的哪个位置,神经网络的前面几层应该对相同的图像区域具有相似的反应,即为“平移不变性”。
  2. 局部性 (locality):神经网络的前面几层应该只探索输入图像中的局部区域,而不过度在意图像中相隔较远区域的关系,这就是“局部性”原则。最终,在后续神经网络,整个图像级别上可以集成这些局部特征用于预测。

6.1.2. 限制多层感知机

6.1.2.1. 平移不变性

6.1.2.2. 局部性

6.1.3. 卷积

6.1.4.1. 通道

6.1.5. 小结

6.2. 图像卷积

6.2.1. 互相关运算

6.2.2. 卷积层

6.2.3. 图像中目标的边缘检测

6.2.4. 学习卷积核

6.2.5. 互相关和卷积

6.2.6. 特征映射和感受野

6.2.7. 小结

6.3. 填充和步幅

6.3.1. 填充

6.3.2. 步幅

6.3.3. 小结

6.4. 多输入多输出通道

6.4.1. 多输入通道

6.4.2. 多输出通道

6.4.3. 1×1卷积层

6.4.4. 小结

6.5. 汇聚层

6.5.1. 最大汇聚层和平均汇聚层

6.5.2. 填充和步幅

6.5.3. 多个通道

6.5.4. 小结

6.6. 卷积神经网络(LeNet)

6.6.1. LeNet

6.6.3. 小结

7. 现代卷积神经网络

7.1. 深度卷积神经网络(AlexNet)

7.1.1. 学习表征

7.1.1.1. 缺少的成分:数据

7.1.1.2. 缺少的成分:硬件

深度学习对计算资源要求很高,训练可能需要数百个迭代周期,每次迭代都需要通过代价高昂的许多线性代数层传递数据。这也是为什么在20世纪90年代至21世纪初,优化凸目标的简单算法是研究人员的首选。然而,用GPU训练神经网络改变了这一格局。图形处理器 (Graphics Processing Unit,GPU)早年用来加速图形处理,使电脑游戏玩家受益。GPU可优化高吞吐量的 4×4 矩阵和向量乘法,从而服务于基本的图形任务。幸运的是,这些数学运算与卷积层的计算惊人地相似。由此,英伟达(NVIDIA)和ATI已经开始为通用计算操作优化gpu,甚至把它们作为 通用GPU(general-purpose GPUs,GPGPU)来销售。
那么GPU比CPU强在哪里呢?
首先,我们深度理解一下中央处理器(Central Processing Unit,CPU)的核心。 CPU的每个核心都拥有高时钟频率的运行能力,和高达数MB的三级缓存(L3 Cache)。 它们非常适合执行各种指令,具有分支预测器、深层流水线和其他使CPU能够运行各种程序的功能。 然而,这种明显的优势也是它的致命弱点:通用核心的制造成本非常高。 它们需要大量的芯片面积、复杂的支持结构(内存接口、内核之间的缓存逻辑、高速互连等等),而且它们在任何单个任务上的性能都相对较差。 现代笔记本电脑最多有4核,即使是高端服务器也很少超过64核,因为它们的性价比不高。
相比于CPU,GPU由 100∼1000 个小的处理单元组成(NVIDIA、ATI、ARM和其他芯片供应商之间的细节稍有不同),通常被分成更大的组(NVIDIA称之为warps)。 虽然每个GPU核心都相对较弱,有时甚至以低于1GHz的时钟频率运行,但庞大的核心数量使GPU比CPU快几个数量级。 例如,NVIDIA最近一代的Ampere GPU架构为每个芯片提供了高达312 TFlops的浮点性能,而CPU的浮点性能到目前为止还没有超过1 TFlops。 之所以有如此大的差距,原因其实很简单:首先,功耗往往会随时钟频率呈二次方增长。 对于一个CPU核心,假设它的运行速度比GPU快4倍,你可以使用16个GPU内核取代,那么GPU的综合性能就是CPU的 16×1/4=4 倍。 其次,GPU内核要简单得多,这使得它们更节能。 此外,深度学习中的许多操作需要相对较高的内存带宽,而GPU拥有10倍于CPU的带宽。

7.1.2. AlexNet

AlexNet和LeNet的设计理念非常相似,但也存在显著差异。 首先,AlexNet比相对较小的LeNet5要深得多。 AlexNet由八层组成:五个卷积层、两个全连接隐藏层和一个全连接输出层。 其次,AlexNet使用ReLU而不是sigmoid作为其激活函数。 下面,让我们深入研究AlexNet的细节。

7.1.2.1. 模型设计

在AlexNet的第一层,卷积窗口的形状是 11×11 。 由于ImageNet中大多数图像的宽和高比MNIST图像的多10倍以上,因此,需要一个更大的卷积窗口来捕获目标。 第二层中的卷积窗口形状被缩减为 5×5 ,然后是 3×3 。 此外,在第一层、第二层和第五层卷积层之后,加入窗口形状为 3×3 、步幅为2的最大汇聚层。 而且,AlexNet的卷积通道数目是LeNet的10倍。
在最后一个卷积层后有两个全连接层,分别有4096个输出。

7.1.2.2. 激活函数

此外,AlexNet将sigmoid激活函数改为更简单的ReLU激活函数。 一方面,ReLU激活函数的计算更简单,它不需要如sigmoid激活函数那般复杂的求幂运算。 另一方面,当使用不同的参数初始化方法时,ReLU激活函数使训练模型更加容易。 当sigmoid激活函数的输出非常接近于0或1时,这些区域的梯度几乎为0,因此反向传播无法继续更新一些模型参数。 相反,ReLU激活函数在正区间的梯度总是1。 因此,如果模型参数没有正确初始化,sigmoid函数可能在正区间内得到几乎为0的梯度,从而使模型无法得到有效的训练。

7.1.2.3. 容量控制和预处理

AlexNet通过dropout( 4.6节 )控制全连接层的模型复杂度,而LeNet只使用了权重衰减。 为了进一步扩充数据,AlexNet在训练时增加了大量的图像增强数据,如翻转、裁切和变色。 这使得模型更健壮,更大的样本量有效地减少了过拟合。

7.1.5. 小结

标签:tensor,模型,卷积,Dive,torch,张量,Deep,我们,into
来源: https://blog.csdn.net/qq_41763040/article/details/120880417