深度学习预备知识
1.Tensor 张量
定义:
张量(tensor)表示一个由数值组成的数组,这个数组可能有多个维度(轴)。具有一个轴的张量对应数学上的向量,具有两个轴的张量对应数学上的矩阵,具有两个以上轴的张量目前没有特定的数学名称。
import torch# arange创建一个行向量x,这个行向量包含以0开始的前12个整数。
x = torch.arange(12)print("x = ",x)
# x = tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
常见的属性及函数:
shape:访问张量的形状(shape,沿每个轴的长度)
print("x.shape = ",x.shape)
# x.shape = torch.Size([12])
numel:访问张量中所有元素的数量(总个数)
print("x.numel = ",x.numel())
# x.numel = 12
reshape:改变一个张量的形状而不改变元素数量和元素值
X = x.reshape(3, 4)print("X = ",X)
# X = tensor([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11]])print("X.shape = ",X.shape)
# X.shape = torch.Size([3, 4])print("X.numel = ",X.numel())
# X.numel = 12
zeros:使用全0来初始化张量
y = torch.zeros((2,3,4))print("y = ",y)
# y = tensor([[[0., 0., 0., 0.],
# [0., 0., 0., 0.],
# [0., 0., 0., 0.]],
#
# [[0., 0., 0., 0.],
# [0., 0., 0., 0.],
# [0., 0., 0., 0.]]])# print("y.shape = ",y.shape)
# y.shape = torch.Size([2, 3, 4])
ones:使用全1来初始化张量
Y = torch.ones(2,3,4)print("Y = ",Y)
# Y = tensor([[[1., 1., 1., 1.],
# [1., 1., 1., 1.],
# [1., 1., 1., 1.]],
#
# [[1., 1., 1., 1.],
# [1., 1., 1., 1.],
# [1., 1., 1., 1.]]])
randn:每个元素都从均值为0、 标准差为1的标准高斯分布(正态分布)中随机取样
z = torch.randn(3,4)print("z = ",z)
# z = tensor([[ 0.3372, 0.3380, 0.5931, -1.1368],
# [-0.4481, -0.0213, -1.6283, -1.0876],
# [-1.1692, 0.8010, 1.9560, 0.3721]])print("z.shape = ",z.shape)
# z.shape = torch.Size([3, 4])
2.运算符
按元素运算
它们将标准标量运算符应用于数组的每个元素。对于将两个数组作为输入的函数,按元素运算将二元运算符应用于两个数组的每对位置对应的元素。
对于任意具有相同形状的张量,常见的标准运算符(+、-、*、/、**)都可以被升级为按元素运算。
x_1 = torch.tensor([1.0,2,4,8])
y_1 = torch.tensor([2,2,2,2])
print("x_1 + y_1 = ", x_1 + y_1)
print("x_1 - y_1 = ", x_1 - y_1)
print("x_1 * y_1 = ", x_1 * y_1)
print("x_1 / y_1 = ", x_1 / y_1)
print("x_1 ** y_1 = ", x_1 ** y_1)
# x_1 + y_1 = tensor([ 3., 4., 6., 10.])
# x_1 - y_1 = tensor([-1., 0., 2., 6.])
# x_1 * y_1 = tensor([ 2., 4., 8., 16.])
# x_1 / y_1 = tensor([0.5000, 1.0000, 2.0000, 4.0000])
# x_1 ** y_1 = tensor([ 1., 4., 16., 64.])print("x_1 first ele is ",x_1[0])
print("x_1 last ele is ",x_1[-1])
# x_1 first ele is tensor(1.)
# x_1 last ele is tensor(8.)print("x_1 second and third ele is ",x_1[1:3])
# x_1 second and third ele is tensor([2., 4.])
连接张量
把多个张量连接在一起,把它们端对端地叠起来形成一个更大的张量。
需要提供张量列表,并给出沿哪个轴连接。
M = torch.arange(12, dtype=torch.float32).reshape(3,4)
print("M = ",M)
N = torch.tensor([[2.0, 1, 4, 3],[1, 2, 3, 4],[4, 3, 2, 1]])
print("N = ",N)
P = torch.cat((M, N), dim=0)
print("P = ",P)
Q = torch.cat((M, N), dim=1)
print("Q = ",Q)# M = tensor([[ 0., 1., 2., 3.],
# [ 4., 5., 6., 7.],
# [ 8., 9., 10., 11.]])
# N = tensor([[2., 1., 4., 3.],
# [1., 2., 3., 4.],
# [4., 3., 2., 1.]])
# P = 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.]])
# Q = 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.]])
3.广播机制
张量形状不同,可以通过广播机制来执行按元素操作。
广播机制工作方式:
1. 通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
2. 对生成的数组执行按元素操作
e = torch.arange(3).reshape(3,1)
f = torch.arange(2).reshape(1,2)
print("e = ",e)
print("f = ",f)
print("e + f = ",e+f)
# e = tensor([[0],
# [1],
# [2]])
# f = tensor([[0, 1]])
# e + f = tensor([[0, 1],
# [1, 2],
# [2, 3]])先扩展成:[[0, 0],[1, 1],[2, 2]][[0, 1],[0, 1],[0, 1]]
再按元素操作。
4.标量
仅包含一个数值被称为标量。
标量由只有一个元素的张量表示。
c = torch.tensor(3.0)
d = torch.tensor(2.0)
print("c + d = ", c+d, "\n", "c * d = ", c*d, "\n", "c ** d = ", c**d, "\n",)# c + d = tensor(5.)
# c * d = tensor(6.)
# c ** d = tensor(9.)
5.向量
向量可以被视为标量值组成的列表。这些标量值被称为向量的元素或分量。
在数学上,具有一个轴的张量表示向量。
c = torch.arange(4, dtype=torch.float32)
d = torch.ones(4, dtype=torch.float32)
print("c = ",c)
print("d = ",d)
# c = tensor([0., 1., 2., 3.])
# d = tensor([1., 1., 1., 1.])
PS.列向量是向量的默认方向。
向量的长度通常称为向量的维度。
python中可以使用len函数来访问张量的长度:
c = torch.arange(4, dtype=torch.float32)
print("c = ",c,"; len = ",len(c))# c = tensor([0., 1., 2., 3.]) ; len = 4
当用张量(只有一个轴)来表示一个向量时,可以通过.shape属性来访问向量的长度。形状是一个元素组,列出了张量沿每个轴的长度(维数)。对于只有一个轴的张量,形状只有一个元素。
c = torch.arange(4, dtype=torch.float32)
print("c = ",c,"; len = ",len(c),"; c.shape = ",c.shape)# c = tensor([0., 1., 2., 3.]) ; len = 4 ; c.shape = torch.Size([4])
PS.这里的维度:向量或轴的维度被用来表示向量或轴的长度,即向量或轴的元素数量。而张量的维度用来表示张量具有的轴数。在这个意义上,张量的某个轴的维度就是这个轴的长度。
6.矩阵
具有两个轴的张量。
可以通过指定两个分量m和n来创建一个形状为m x n的矩阵(m行n列)。
创建一个5 x 4的矩阵:
A = torch.arange(20, dtype=torch.float32).reshape(5,4)
print("A = ",A)
print("A.sum() = ",A.sum())
print("A.shape = ",A.shape)
# A = tensor([[ 0., 1., 2., 3.],
# [ 4., 5., 6., 7.],
# [ 8., 9., 10., 11.],
# [12., 13., 14., 15.],
# [16., 17., 18., 19.]])
# A.sum() = tensor(190.)
# A.shape = torch.Size([5, 4])
7.点积
给定两个向量x, y,它们的点积是相同位置的按元素乘积的和。
c = torch.arange(4, dtype=torch.float32)
d = torch.ones(4, dtype=torch.float32)
print("c = ",c)
# c = tensor([0., 1., 2., 3.]) ; len = 4
print("d = ",d)
print("dot(c,d) = ",torch.dot(c,d))
# c = tensor([0., 1., 2., 3.])
# d = tensor([1., 1., 1., 1.])
# dot(c,d) = tensor(6.)
8.矩阵--向量积
torch.mv()
a = torch.arange(4, dtype=torch.float32)
print("a = ",a)
print("a.sum() = ",a.sum())
# a = tensor([0., 1., 2., 3.])
# a.sum() = tensor(6.)
print("a.shape = ",a.shape)
# a.shape = torch.Size([4])A = torch.arange(20, dtype=torch.float32).reshape(5,4)
print("A = ",A)
print("A.sum() = ",A.sum())
print("A.shape = ",A.shape)
# A = tensor([[ 0., 1., 2., 3.],
# [ 4., 5., 6., 7.],
# [ 8., 9., 10., 11.],
# [12., 13., 14., 15.],
# [16., 17., 18., 19.]])
# A.sum() = tensor(190.)
# A.shape = torch.Size([5, 4])print("torch.mv(A, a) = ",torch.mv(A, a))
# torch.mv(A, a) = tensor([ 14., 38., 62., 86., 110.])(5 x 4)*(4 x 1) => (5 x 1)
9.矩阵--矩阵乘法
torch.mm()
A: n x k
B: k x m
A * B => n x m
A = torch.arange(20, dtype=torch.float32).reshape(5,4)
print("A = ",A)
A = tensor([[ 0., 1., 2., 3.],[ 4., 5., 6., 7.],[ 8., 9., 10., 11.],[12., 13., 14., 15.],[16., 17., 18., 19.]])B = torch.ones(4, 3)
print("B = ",B)
print("torch.mm(A, B) = ",torch.mm(A, B))B = tensor([[1., 1., 1.],[1., 1., 1.],[1., 1., 1.],[1., 1., 1.]])
torch.mm(A, B) = tensor([[ 6., 6., 6.],[22., 22., 22.],[38., 38., 38.],[54., 54., 54.],[70., 70., 70.]])
10.算子
在 PyTorch 中,“算子”通常是指一种操作或函数,它对张量(tensor)进行计算或变换。算子可以是基本的数学运算(如加法、减法、乘法等),也可以是复杂的深度学习操作(如卷积、池化、激活函数等)。这些算子在 PyTorch 中被实现为各种函数和方法,可以作用于张量,以执行各种计算任务。
如,以下是一些常见的 PyTorch 算子:
1. 基本数学运算算子:
- torch.add:张量加法
- torch.sub:张量减法
- torch.mul:张量乘法
- torch.div:张量除法
2. 线性代数算子:
- torch.mm:矩阵乘法
- torch.matmul:广义矩阵乘法
- torch.inverse:矩阵求逆
3. 深度学习相关算子:
- torch.conv2d:二维卷积
- torch.max_pool2d:二维最大池化
- torch.relu:ReLU激活函数
4. 统计运算算子:
- torch.mean:计算均值
- torch.std:计算标准差
- torch.sum:求和
11.模型量化(Model Quantization)
模型量化是机器学习和深度学习领域的一种技术,用于减少模型的存储大小、加速推理和降低功耗,特别是在资源受限的设备(如移动设备、嵌入式系统、IoT 设备)上运行时非常有用。
量化的主要思想是将模型中使用的高精度(通常是 32 位浮点数,即 float32)参数和运算,转换为低精度(如 16 位浮点数、8 位整数或 4 位整数)的参数和运算,从而减少计算量和存储需求。
为什么需要模型量化?
1. 模型存储大小变小:
高精度模型(如 float32)需要更多的存储空间,而低精度模型(如 int8)的存储需求更小。例如:float32 参数占 4 字节;int8 参数占 1 字节。因此量化可以显著减少模型的存储大小。
2. 推理速度提升:
低精度的计算需要更少的 CPU/GPU 资源,且硬件对低精度操作(如 int8)的执行速度通常更快。
3. 功耗降低:
在移动设备或边缘设备上,低精度计算能减少能源消耗。
4. 部署到资源受限的设备:
量化后的模型可以更高效地运行在嵌入式系统或 IoT 设备中。
模型量化的优缺点
优点:
1. 减少模型大小:存储需求显著降低(如从 float32 到 int8 可以减少 75% 的存储)。
2. 提高推理速度:低精度计算速度更快,特别是在硬件支持下。
3. 降低功耗:适合移动设备和嵌入式设备。
4. 支持边缘部署:适合资源受限的设备。
缺点:
1. 精度下降:量化可能导致模型精度下降,尤其是静态量化。
2. 硬件依赖:性能提升依赖于硬件是否支持低精度计算(如 int8 或 fp16)。
3. 实现复杂性:部分量化方法(如 QAT)需要重新训练模型,增加了开发复杂性。