动手学深度学习(PyTorch版)笔记 02 预备知识

2. 预备知识

2.1 数据操作

  1. import torch :导入PyTorch
  2. x = torch.arange(12) :创建一个行向量 x ,这个行向量包含从0开始的前12个整数
  3. x.shape :查看张量的形状(维度)
  4. x.numel():查看张量的元素总数(给出各维度相乘的一个值)
  5. x = x.reshape((2,4)) :改变张量的形状而不改变元素数量和元素值
  6. torch.zeros((2,3,4)) :创建一个形状为 (2,3,4) 的全零向量
  7. torch.ones((2,3,4)) :创建一个形状为 (2,3,4) 的全一向量
  8. torch.randn(3, 4):创建一个形状为 (3, 4) 的张量,元素值服从均值为0,标准差为1的标准高斯分布的随机采样
  9. torch.tensor([1, 3, 4]) :根据 list 列表创建一个张量
  10. 运算符:加( + )减( - )乘( * )除( / )幂( ** ),指数( torch.exp(x)
  11. torch.cat((X, Y), dim=0) :在沿 轴-0 连接张量
  12. X == Y :按每个位置进行逻辑运算
  13. X.sum() :对张量中的所有元素进行求和,产生一个单元素的张量
  14. 索引和切片:X[-1], X[1:3],X[0:2, :]
  15. A = X.numpy() :将张量转换为Numpy的ndarray类型
  16. B = torch.tensor(A) :将Numpy的数组类型转换成张量
  17. Y = Y + X 会为 Y 重新分配一个地址,可以采用以下方法避免不必要地分配内存以减少内存开销:
    1. Z[:] = X + Y
    2. X += Y

2.2 数据预处理

  1. os.makedirs():创建文件夹。 exist_true : 在目标目录已存在的情况下,不会引发错误而是忽略该错误。
  2. pd.read_csv(data_file) :采用 pandas 包读取 csv 文件
  3. NaN :即 not a number 代表缺失值,为了处理缺失的数据,典型的方法包括插值法和删除法。
  4. df.iloc[ : , 0:2] :对 pandasDataFrame 数据结构进行位置索引
  5. pd.get_dummies(data, dummy_na=True) :将 DataFrame 中分类变量(字符串型)转换称虚拟变量(数值型)

2.3 线性代数

  1. len(x) :查看张量 x 的长度
  2. A.T :矩阵 A 的转置
  3. B = A.clone() :创建张量 A 的副本 B 。A与B具有相同的数据和属性,二者相互独立(有各自的内存地址),修改其中一个不会影响另一个。
  4. A.sum(axis=0) :沿 x 轴求和并降低维度,可以通过设置 keepdims=True 使得计算总和或均值时保持轴数不变
  5. A.mean() :求张量平均值
  6. A.cumsum(axis=0) :沿 轴-0 计算张量 A 的累积总和,对于一个tensor,从左到右从外到内依次是 轴-0轴-1轴-2 以此类推。
  7. torch.dot(x, y) :向量点积
  8. torch.mv(A, x) :矩阵 A 和向量 x 的矩阵-向量积
  9. torch.mm(A, B) :矩阵乘法

范数:范数是一种用来度量向量或矩阵的大小的函数。在数学中,范数是对向量空间中的元素进行度量的一种方式,它将向量的长度或大小映射到非负实数上。范数被称为"范数"是因为它们满足一些特定的性质,这些性质在拉丁语中被称为"norma"(规范、标准)。

L p L_p Lp 范数 ∥ x ∥ p = ( ∑ i = 1 n ∣ x i ∣ p ) 1 / p \|\mathbf{x}\|_{p}=\left(\sum_{i=1}^{n}\left|x_{i}\right|^{p}\right)^{1 / p} xp=(i=1nxip)1/p

len():对于 Tensor 类型, len() 函数的输出是该张量的第一个维度的大小(即张量的长度)。如果要获取张量的完整形状信息,可以使用 tensor.size()tensor.shape 属性。

2.4 微积分

假设有一个函数 f : R → R f: \mathbb{R} \rightarrow \mathbb{R} f:RR,其输入输出都是标量。如果 f f f 的导数存在,这个极限被定义为:
f ′ ( x ) = lim ⁡ h → 0 f ( x + h ) − f ( x ) h f^{\prime}(x)=\lim _{h \rightarrow 0} \frac{f(x+h)-f(x)}{h} f(x)=h0limhf(x+h)f(x)
导数可以被解释为函数相对于其变量的瞬时变化率,它也是函数曲线的切线的斜率。

f-string:python 3.6 引入的一种格式化字符串的方法, f-string 在形式上以 fF修饰符引领字符串,形如: f'xxx,{}:.3' ,其中 {} 中为被替换的字段(变量), : 后指定被替换的字段的格式,例如 :.3 代表保留3位浮点数。

微分法则(假设函数 f f f g g g 都是可微的, C C C 是一个常数):

  1. 常数相乘法则: d d x [ C f ( x ) ] = C d d x f ( x ) \dfrac{d}{d x}[C f(x)]=C \dfrac{d}{d x} f(x) dxd[Cf(x)]=Cdxdf(x)
  2. 加法法则: d d x [ f ( x ) + g ( x ) ] = d d x f ( x ) + d d x g ( x ) \dfrac{d}{d x}[f(x)+g(x)]=\dfrac{d}{d x} f(x)+\dfrac{d}{d x} g(x) dxd[f(x)+g(x)]=dxdf(x)+dxdg(x) ==> ( u + v ) ′ = u ′ + v ′ (u+v)'=u'+v' (u+v)=u+v
  3. 乘法法则: d d x [ f ( x ) g ( x ) ] = f ( x ) d d x [ g ( x ) ] + g ( x ) d d x [ f ( x ) ] \dfrac{d}{d x}[f(x) g(x)]=f(x) \dfrac{d}{d x}[g(x)]+g(x) \dfrac{d}{d x}[f(x)] dxd[f(x)g(x)]=f(x)dxd[g(x)]+g(x)dxd[f(x)] ==> ( u ⋅ v ) ′ = u ⋅ v ′ + v ⋅ u ′ (u·v)'=u·v'+v·u' (uv)=uv+vu
  4. 除法法则: d d x [ f ( x ) g ( x ) ] = g ( x ) d d x [ f ( x ) ] − f ( x ) d d x [ g ( x ) ] [ g ( x ) ] 2 \dfrac{d}{d x}\left[\dfrac{f(x)}{g(x)}\right]=\dfrac{g(x) \dfrac{d}{d x}[f(x)]-f(x) \dfrac{d}{d x}[g(x)]}{[g(x)]^{2}} dxd[g(x)f(x)]=[g(x)]2g(x)dxd[f(x)]f(x)dxd[g(x)] ==> ( u v ) ′ = v ⋅ u ′ − u ⋅ v ′ v 2 (\dfrac{u}{v})'=\dfrac{v·u'-u·v'}{v^2} (vu)=v2vuuv

赋值条件语句:语法形如: 变量 = 真值部分 if 条件 else 假值部分 ,如果条件为真,则变量将被赋值为真值部分;如果条件为假,则变量将被赋值为假值部分。

plt.gca():gca:get current axes,通过调用 plt.gca() 用于获取当前的坐标轴(Axes)对象。

# 如果X有一个轴,输出True
def has_one_axis(X):
    return (hasattr(X, "ndim") and X.ndim == 1 or isinstance(X, list)
            and not hasattr(X[0], "__len__"))
  1. hasattr(X, "ndim") :检查对象 X 是否具有 ndim 属性。ndim 属性通常用于表示对象的维度(轴的数量)。
  2. isinstance(X, list)):判断 X是否为列表。 isinstance() 是一个 Python 内置函数,用于检查一个对象是否属于指定的类型。 isinstance(object, classinfo) 接受两个参数:
    1. object:要检查的对象。
    2. classinfo:可以是一个类型对象(如 intstr)或一个类型元组(如 (int, float)
  3. 列表具有一个__len__ 属性,即代表列表第一维的长度。如果 X 是一个一维列表, X[0]将是一个标量,即不存在 __len__ 属性
  4. axes.cla() :清除坐标轴上的当前图形。

梯度(gradient):我们可以连结一个多元函数对其所有变量的偏导数,以得到该函数的梯度 (gradient) 向量。具体而言,设函数 f : R n → R f: \mathbb{R}^{n} \rightarrow \mathbb{R} f:RnR 的输入是一个 n n n 维向量 x = [ x 1 , x 2 , … , x n ] ⊤ \mathbf{x}=\left[x_{1}, x_{2}, \ldots, x_{n}\right]^{\top} x=[x1,x2,,xn],并且输出是一个标量。函数 f ( x ) f(\mathbf{x}) f(x) 相对于 x \mathbf{x} x 的梯度是一个包含 n n n 个偏导数的向量:
∇ x f ( x ) = [ ∂ f ( x ) ∂ x 1 , ∂ f ( x ) ∂ x 2 , … , ∂ f ( x ) ∂ x n ] ⊤ \nabla_{\mathbf{x}} f(\mathbf{x})=\left[\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}}\right]^{\top} xf(x)=[x1f(x),x2f(x),,xnf(x)]
其中 ∇ x f ( x ) \nabla_{\mathbf{x}} f(\mathbf{x}) xf(x) 通常在没有歧义时被 ∇ f ( x ) \nabla f(\mathbf{x}) f(x) 取代。

链式法则 d y d x = d y d u d u d x \dfrac{d y}{d x}=\dfrac{d y}{d u} \dfrac{d u}{d x} dxdy=dudydxdu

对于函数具有任意数量的情况,假设可微分函数 y y y 有变量 u 1 , u 2 , … , u m u_{1}, u_{2}, \ldots, u_{m} u1,u2,,um,其中每个 可微分函数 u i u_{i} ui 都有变量 x 1 , x 2 , … , x n x_{1}, x_{2}, \ldots, x_{n} x1,x2,,xn 。注意, y y y x 1 , x 2 , … , x n x_{1}, x_{2}, \ldots, x_{n} x1,x2,,xn 的函数。对于任意 i = 1 , 2 , … , n i=1,2, \ldots, n i=1,2,,n,链式法则给出:
∂ y ∂ x i = ∂ y ∂ u 1 ∂ u 1 ∂ x i + ∂ y ∂ u 2 ∂ u 2 ∂ x i + ⋯ + ∂ y ∂ u m ∂ u m ∂ x i \dfrac{\partial y}{\partial x_{i}}=\dfrac{\partial y}{\partial u_{1}} \dfrac{\partial u_{1}}{\partial x_{i}}+\frac{\partial y}{\partial u_{2}} \dfrac{\partial u_{2}}{\partial x_{i}}+\cdots+\frac{\partial y}{\partial u_{m}} \dfrac{\partial u_{m}}{\partial x_{i}} xiy=u1yxiu1+u2yxiu2++umyxium

常见函数的导数(梯度):(其中 x \mathbf{x} x n n n 维向量)

  1. 对于所有 A ∈ R m × n \mathbf{A} \in \mathbb{R}^{m \times n} ARm×n,都有 ∇ x A x = A ⊤ \nabla_{\mathbf{x}} \mathbf{A} \mathbf{x}=\mathbf{A}^{\top} xAx=A
  2. 对于所有 A ∈ R n × m \mathbf{A} \in \mathbb{R}^{n \times m} ARn×m,都有 ∇ x x ⊤ A = A \nabla_{\mathbf{x}} \mathbf{x}^{\top} \mathbf{A}=\mathbf{A} xxA=A
  3. 对于所有 A ∈ R n × n \mathbf{A} \in \mathbb{R}^{n \times n} ARn×n,都有 ∇ x x ⊤ A x = ( A + A ⊤ ) x \nabla_{\mathbf{x}} \mathbf{x}^{\top} \mathbf{A} \mathbf{x}=\left(\mathbf{A}+\mathbf{A}^{\top}\right) \mathbf{x} xxAx=(A+A)x
  4. ∇ x ∥ x ∥ 2 = ∇ x x ⊤ x = 2 x \nabla_{\mathbf{x}}\|\mathbf{x}\|^{2}=\nabla_{\mathbf{x}} \mathbf{x}^{\top} \mathbf{x}=2 \mathbf{x} xx2=xxx=2x

2.5 自动求导

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

自动求导的两种模式:正向累积和反向累积

  1. 链式法则: ∂ y ∂ x = ∂ y ∂ u n ∂ u n ∂ u n − 1 … ∂ u 2 ∂ u 1 ∂ u 1 ∂ x \dfrac{\partial y}{\partial x}=\dfrac{\partial y}{\partial u_{n}} \dfrac{\partial u_{n}}{\partial u_{n-1}} \ldots \dfrac{\partial u_{2}}{\partial u_{1}} \dfrac{\partial u_{1}}{\partial x} xy=unyun1unu1u2xu1
  2. 正向累积: ∂ y ∂ x = ∂ y ∂ u n ( ∂ u n ∂ u n − 1 ( … ( ∂ u 2 ∂ u 1 ∂ u 1 ∂ x ) ) ) \dfrac{\partial y}{\partial x}=\dfrac{\partial y}{\partial u_{n}}\left(\dfrac{\partial u_{n}}{\partial u_{n-1}}\left(\ldots\left(\dfrac{\partial u_{2}}{\partial u_{1}} \dfrac{\partial u_{1}}{\partial x}\right)\right)\right) xy=uny(un1un((u1u2xu1)))
  3. 反向累积(又称反向传递): ∂ y ∂ x = ( ( ( ∂ y ∂ u n ∂ u n ∂ u n − 1 ) … ) ∂ u 2 ∂ u 1 ) ∂ u 1 ∂ x \dfrac{\partial y}{\partial x}=\left(\left(\left(\dfrac{\partial y}{\partial u_{n}} \dfrac{\partial u_{n}}{\partial u_{n-1}}\right) \ldots\right) \dfrac{\partial u_{2}}{\partial u_{1}}\right) \dfrac{\partial u_{1}}{\partial x} xy=(((unyun1un))u1u2)xu1

反向累积计算梯度的过程示例(首先需要执行一次正向计算存储中间结果,接下来执行反向计算):

image-20230825112247509

前向:执行计算图,存储中间结果(例如 b b b a a a 的值 )

反向:从相反的方向执行图,利用正向计算的中间结果以及链式法则,直接得出梯度值。

反向累积复杂度

  1. 计算复杂度: O ( n ) O(n) O(n) , n n n 是操作子个数,通常正向与反向的代价类似
  2. 内存复杂度: O ( n ) O(n) O(n) ,因为需要存储正向的所有中间结果。(因此深度神经网络特别消耗GPU资源)

正向累积复杂度

  1. 计算复杂度:计算一个变量的梯度的计算复杂度为 O ( n ) O(n) O(n) ,每一层的梯度都需要单独计算
  2. 内存复杂度: O ( 1 ) O(1) O(1),即不需要存储中间结果。

控制流梯度计算

使用自动微分的一个好处是: 即使构建函数的计算图需要通过Python控制流(例如,条件、循环或任意函数调用),我们仍然可以计算得到的变量的梯度。

相关代码

  1. x.requires_grad_(True) :用于指示是否需要计算梯度,当 True 时,PyTorch 会自动跟踪该张量的所有操作。
  2. y.backward() :计算一个标量(scalar)相对于计算图中所有具有 requires_grad=True 的张量的梯度,并将结果存储在相应张量的 grad 属性中。例如可以用 x.grad 获取张量 x 的梯度值。当调用 backward() 方法之后,计算图中的梯度信息会被清除(此时若再一次执行 y.backward() 将会报错)。我们可以通过设置 retain_graph=True 参数来保留计算图的梯度信息,例如 y.backward(retain_graph=True)
  3. y.sum() :计算张量中的所有元素进行求和,产生一个单元素的张量(scalar)。因为 y.backward() 只有在 y 是标量(scalar)时才能够正常执行,因此,若 y 是一个向量,通常需要进行 y.sum() 操作。 我们通常一次会计算一批样本的loss,但是,我们并不需要计算微分矩阵,而是单独计算批量中每个样本的偏导数之和。即假设有样本 $ S_1, S_2 ,S_3 $ ,网络中间变量为 x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3 ,可得到每一个样本相对应的 loss 为 y 1 , y 2 , y 3 y_1,y_2,y_3 y1,y2,y3 ,最终我们计算的变量 x 1 x_1 x1 的梯度信息将是 ∂ ( y 1 + y 2 + y 3 ) ∂ x 1 = ∂ y 1 + ∂ y 2 + ∂ y 3 ∂ x 1 \dfrac{\partial{(y_1+y_2+y_3)}}{\partial{x_1}} = \dfrac{\partial{y_1}+\partial{y_2}+\partial{y_3}}{\partial{x_1}} x1(y1+y2+y3)=x1y1+y2+y3
  4. x.grad.zero_() :由于 PyTorch 中张量的梯度是依次累加的,故有时需要手动清除梯度信息。
  5. u = y.detach() :有时我们需要将张量 y y y 视为一个常数,此时需要从计算图中将该张量分离出来。即用u = y.detach() 实现,将 y y y 分离出来返回一个新的变量 u u u ,该变量 u u u 具有与 y y y 相同的值,但丢弃计算图中如何计算 y y y 的任何信息(不再与计算图相关联)。

2.6 概率

联合概率(joint probability): P ( A = a , B = b ) P(A=a, B=b) P(A=a,B=b) A = a A=a A=a B = b B=b B=b 同时发生的概率。

条件概率(conditional probability): P ( B = b ∣ A = a ) P(B=b \mid A=a) P(B=bA=a) A = a A=a A=a 发生的前提下, B = b B=b B=b 发生的概率。

贝叶斯定理(Bayes’ theorem): P ( A , B ) = P ( B ∣ A ) P ( A ) = P ( A ∣ B ) P ( B ) P(A, B)=P(B \mid A) P(A) = P(A \mid B) P(B) P(A,B)=P(BA)P(A)=P(AB)P(B) P ( A ∣ B ) = P ( B ∣ A ) P ( A ) P ( B ) P(A \mid B)=\dfrac{P(B \mid A) P(A)}{P(B)} P(AB)=P(B)P(BA)P(A)

边际化 P ( B ) = ∑ A P ( A , B ) P(B)=\sum_{A} P(A, B) P(B)=AP(A,B) B的概率相当于在 A A A 的所有可能下,他们的联合概率之和。边际化结果的概率或分布称为边际概率(marginal probability) 或边际分布(marginal distribution)

独立与依赖: A ⊥ B A \perp B AB 表示两个随机变量 A A A B B B 是独立的,意味着事件 A A A 的发生跟 B B B 事件的发生无关。

2.7 查阅文档

  1. dir() :获取对象的属性列表(包括函数名)
  2. help() :获取对象的帮助文档
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值