动手学习深度学习(2)简单的数学知识

茴香豆 Lv5

本节主要讲解线性代数,按特定轴求和,矩阵计算和自动求导相关内容

1. 线性代数

矩阵的特征值和特征向量。

矩阵的转置。

2. 矩阵计算

常用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 按某个轴进行累加求和
A.cumsum(axis=0)
# output
tensor([[ 0, 1, 2, 3],
[ 4, 6, 8, 10],
[12, 15, 18, 21]])
# 点积运算
torch.dot(x, y)
# 叉乘运算
torch.mv(x, y) # matrix-vector 矩阵-向量乘法
torch.mm(A, B) # matrix-matrix 矩阵-矩阵乘法

# L2范数是向量元素平方和的平方根
torch.norm(u)
# L1范数是向量元素的绝对值之和
torch.abs(u).sum()
# 佛罗贝尼乌斯范数(Frobenius norm)是矩阵元素的平方和的平方根
torch.norm(A)

按特定轴求和

举例比较好理解,例如,对于2x3x4的矩阵:

  • 若axis=0,求sum后矩阵变为二维矩阵,大小为3x4
  • 若axis=1,求sum后矩阵变为二维矩阵,大小为2x4
  • 若axis=2,求sum后矩阵变为二维矩阵,大小为2x3
  • 若axis=[0, 1],求sum后矩阵变为一维矩阵,长度为4
  • 若axis=[0, 2],求sum后矩阵变为一维矩阵,长度为3
  • 若axis=[1, 2],求sum后矩阵变为一维矩阵,长度为2
  • 若axis=[0, 1, 2],求sum后矩阵变为一个数
1
2
3
4
A = torch.arange(24).reshape((2, 3, 4))
A.sum(axis=1).shape, A.sum(axis=[0, 2]).shape
# output
torch.Size([2, 4]), torch.Size([3])

计算总和或均值时保持轴数不变

保持求和后的矩阵与求和前的矩阵形状相同。

1
2
3
4
5
6
7
sum_A = A.sum(axis=1, keepdims=True)
# output
tensor([[ 6.],
[22.],
[38.],
[54.],
[70.]])

应用:使得我们可以通过广播将A除以sum_A

1
A / sum_A # 广播机制要求维度相同

3. 自动求导

矩阵求导的本质与分子布局、分母布局的本质(矩阵求导——本质篇)

基本向量求导运算:

矩阵求导运算后的形状:

计算图:

  • 将代码分解为操作子
  • 将计算表示成一个无环图

自动求导实现

假设我们想对函数 y = 2 \mathbf{x} ^{T} \mathbf{x} 关于列向量 \mathbf{x} 求导。在我们计算 y 关于 \mathbf{x} 的梯度之前,我们需要一个地方来存储梯度。

1
2
3
4
5
import torch
x = torch.arange(4.0)
x.requires_grad_(True) #告知需要存储梯度
#等价于 x = torch.arange(4.0, requires_grad=True)
y = 2 * torch.dot(x, x)

调用反向传播函数来自动计算 y 关于 \mathbf{x} 每个分量的梯度。

1
2
3
4
5
y.backward()
x.grad
# output: tensor([ 0., 4., 8., 12.])
x.grad == 4 * x
# output: tensor([True, True, True, True])

现在让我们计算 \mathbf{x} 的另一个函数。

1
2
3
4
5
6
# 在默认情况下,PyTorch会积累梯度,我们需要qin'chu之前的值
x.grad.zero_()
y = x.sum()
y.backward()
x.grad
# output: tensor([1., 1., 1., 1.])

深度学习中,我们的目的不是计算微分矩阵,而是批量中每个样本单独计算的偏导数之和。

1
2
3
4
5
6
7
# 对非标量调用‘backward’需要传入一个‘gradient’参数,该参数指定微分函数
x.grad.zero_()
y = x * x
y.sum().backward()
# 等价于y.backward(torch.ones(len(x)))
x.grad
# output: tensor([0., 2., 4., 6.])

将某些计算移动到记录的计算图之外。

1
2
3
4
5
6
7
8
x.grad.zero_()
y = x * x
u = y.detach() # 将y视作常数存储在u之中, y仍旧是x的函数,但u不是。
z = u * x

z.sum().backward()
x.grad == u
# output: tensor([True, True, True, True])
  • Title: 动手学习深度学习(2)简单的数学知识
  • Author: 茴香豆
  • Created at : 2022-09-24 19:03:10
  • Updated at : 2023-02-23 11:44:24
  • Link: https://hxiangdou.github.io/2022/09/24/DL_2/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments