动手学深度学习 第一第二章
废话笔记版
这b笔记感觉我自己不想再看第二遍,之后会再记得好一点的...
2. 预备知识
chapter_preliminaries
2.1 数据操作
chapter_ndarray
2.1.4 索引和切片
2.1.5 节省内存
这里 Y 的内存位置是被改变了的,我们不希望这样的事情发生,原因有两个:
- 首先,我们不想总是不必要地分配内存。在机器学习中,我们可能有数百兆的参数,并且在一秒内多次更新所有参数。通常情况下,我们希望原地执行这些更新;
- 如果我们不原地更新,其他引用仍然会指向旧的内存位置,这样我们的某些代码可能会无意中引用旧的参数。
那么执行原地操作的方法是:使用切片的形式:
2.2 数据预处理
2.2.1 读取
每次用都要重新查的一句话
2.2.2 处理缺失值
- 插值法
- 删除法
2.2.3 转换为张量格式
转换成张量格式后可以使用2.1节讲的张量函数
import torch
X = torch.tensor(inputs.to_numpy(dtype=float))
y = torch.tensor(outputs.to_numpy(dtype=float))
X, y
2.3 线性代数
2.3.2 向量
tensor([0, 1, 2, 3])默认为列向量
其中 x[3]为 tensor(3)
- 长度
len(x) >>> 4 返回第一个维度的长度
x.shape >>> torch.Size([4]),列表中是每个维度的长度
2.3.3 矩阵
- 矩阵的转置:
A.T
2.3.4 张量
一般用\(X, Y, Z\)表示
2.3.6 降维
求和函数可以沿轴求和减低维度
A.sum()
A_sum_axis0 = A.sum(axis=0) # 求和所有行的和
A_sum_axis1 = A.sum(axis=1) # 沿着列求和
A_sum = A.sum(axis=[0, 1]) # 沿着行和列求和等于全部求和
2.4 微积分
梯度是标量的梯度✍️✍️✍️
2.4.3 梯度
一些梯度计算公式
\[
\nabla_{\mathbf{x}} f(\mathbf{x}) = \bigg[\frac{\partial f(\mathbf{x})}{\partial x_1}, \frac{\partial f(\mathbf{x})}{\partial x_2}, \ldots, \frac{\partial f(\mathbf{x})}{\partial x_n}\bigg]^\top
\]
在微分多元函数时经常使用以下规则:
- 对于所有\(\mathbf{A} \in \mathbb{R}^{m \times n}\),都有\(\nabla_{\mathbf{x}} \mathbf{A} \mathbf{x} = \mathbf{A}^\top\)
- 对于所有\(\mathbf{A} \in \mathbb{R}^{n \times m}\),都有\(\nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{A} = \mathbf{A}\)
- 对于所有\(\mathbf{A} \in \mathbb{R}^{n \times n}\),都有\(\nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{A} \mathbf{x} = (\mathbf{A} + \mathbf{A}^\top)\mathbf{x}\)
- \(\nabla_{\mathbf{x}} \|\mathbf{x} \|^2 = \nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{x} = 2\mathbf{x}\)
2.5 自动微分
2.5.1 一个简单的例子
x = torch.arange(4.0)
x.require_grad_(True) # 等价于x=torch.arange(4.0,require_grad=True)
# 然后计算 y
y = 2 * torch.dot(x, x)
# 然后调用反向传播的函数,自动计算y关于x每个分量的梯度
y.backward()
x.grad # >>> tensor([0., 4., 8., 12.])
# y = 2 x^T x
# 关于x的梯度为
x.grad == 4 * x # >>> True
\(y=2 x^T x\) 求导推导
\[
x^T x = x_1^2 + x_2^2 + ... + x_n^2
\]
\[
\frac{\partial y}{\partial x_i} = \frac{\partial}{\partial x_i} (2(x_1^2 + x_2^2 + ... + x_n^2)) = 2 \cdot (2x_i) = 4x_i
\]
所以梯度$\nabla_x y(也可写做\frac{\partial y}{\partial x})$为:
\[
\frac{\partial y}{\partial \mathbf{x}} = \begin{bmatrix}
\frac{\partial f}{\partial x_1} \\
... \\
\frac{\partial f}{\partial x_n}
\end{bmatrix} = \begin{bmatrix}
4x_1 \\
...\\
4x_n
\end{bmatrix} = 4\mathbf{x}
\]
然后我们计算x的另一个函数
# 在默认情况下,PyTorch会累积梯度,我们需要清除之前的值
x.grad.zero_()
y = x.sum()
y.backward()
x.grad
>>> tensor([1., 1., 1., 1.])
关于梯度清零 x.grad.zero_()
调用backward()时,梯度不会覆盖在变量上而是 +=,这种做法可以节省显存
2.5.2 非标量变量的反向传播
y不是标量时,向量y关于向量x求导会是一个高阶的张量,我们在机器学习中不太期望这个,所以我们把反向传播的对象转移到对于y的每个分量的和关于x求导,这样就是一个标量关于x的梯度了
# 对非标量调用backward需要传入一个gradient参数,该参数指定微分函数关于self的梯度。
# 本例只想求偏导数的和,所以传递一个1的梯度是合适的
x.grad.zero_()
y = x * x
# 等价于y.backward(torch.ones(len(x)))
y.sum().backward()
x.grad
2.5.3 分离计算
可以把一个中间变量当成算出来的常量