【动手学深度学习】1.1 预备知识 基于pytorch的数据操作
目录
1.1 数据理论
张量(Tensor)和数组(Array)是数学和计算机科学中用于表示多维数据的基本结构。它们在深度学习、科学计算和数据分析中广泛应用。以下是它们的定义及在不同环境(如PyTorch)中的实现。
1. 张量(Tensor)
张量是一个多维数组的泛化概念,可以看作是标量、向量和矩阵的扩展。张量的维度称为阶(Rank)或轴(Axis)。
- 标量(Scalar):0 阶张量,例如 5 。
- 向量(Vector):1阶张量,例如 [1, 2, 3] 。
- 矩阵(Matrix):2阶张量,例如 [ [1, 2], [3, 4] ] 。
- 高阶张量:3阶及以上,例如 [ [ [1, 2], [3, 4] ], [ [5, 6], [7, 8] ] ] 。
张量在深度学习中可以高效的表示和处理多维数据(视频、文本、音频等)。
2. 数组(Array)
数组是一种数据结构,用于存储相同类型的元素。数组可以是多维的,例如:
-
一维数组:[1, 2, 3]
-
二维数组:[ [1, 2, 3] , [4, 5, 6] ]
在编程中,数组通常由特定的库或语言原生支持,例如 Numpy、原生数组。
3. 不同环境中的定义与实现
(1) NumPy
NumPy是Python中用于科学计算的核心库,提供了多维数组(ndarray)对象。
import numpy as np
# 创建一个2x2的数组
array = np.array([[1, 2], [3, 4]])
特点:
-
支持高效的数值计算。
-
提供了丰富的数学函数和操作(如广播、切片等)。
-
与深度学习框架(如PyTorch、TensorFlow)兼容。
(2) PyTorch
PyTorch是一个流行的深度学习框架,使用张量作为核心数据结构。
import torch
# 创建一个2x2的张量
tensor = torch.tensor([[1, 2], [3, 4]])
特点:
-
张量支持GPU加速计算。
-
提供了自动求导功能(autograd),用于深度学习中的反向传播。
- 与NumPy数组可以互相转换:
# 张量转 NumPy 数组
numpy_array = tensor.numpy()
# NumPy 数组转张量
tensor = torch.from_numpy(numpy_array)
4. 张量与数组的区别
| 特性 | 张量(Tensor) | 数组(Array) |
|---|---|---|
| 定义 | 多维数据的泛化概念 | 多维数据的存储结构 |
| 框架支持 | PyTorch、TensorFlow等 | NumPy、原生数组等 |
| 硬件加速 | 支持GPU/TPU加速 | 通常仅支持CPU |
| 自动求导 | 支持(如PyTorch、TensorFlow) | 不支持 |
| 主要用途 | 深度学习、科学计算 | 通用数值计算 |
张量表示一个由数值组成的数组,这个数组可能有多个维度。张量的维度(轴数)决定了它在数学上的具体表示形式,具体来说:
| 维度(轴数) | 名 称 | 示 例 | 数学意义 |
|---|---|---|---|
| 0阶 | 标量(Scalar) | 5 | 单个数值,没有方向。 |
| 1阶 | 向量(Vector) | [1, 2, 3] | 一维数组,表示一个有方向的量。 |
| 2阶 | 矩阵(Matrix) | [ [1, 2], [3, 4] ] | 二维数组,表示线性变换或数据表。 |
| 3阶及以上 | 高阶张量 | [ [ [1, 2], [3, 4] ], [ [5, 6], [7, 8] ] ] | 多维数组,没有特定的数学名称,但在实际应用中非常重要(如图像、视频、时间序列等)。 |
1.2 数据操作
1. 创建张量
import torch
x = torch.arange(12)
# 通过 arrange() 创建一个行向量,包含以0开始的12个整数,默认创建整数,也可指定创建浮点数。
# 张量中的每个值称之为元素,除非额外指定,新的张量将存储在内存中,并采用基于CPU的计算。
# x = tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
# 其他创建张量常用函数
torch.zeros(2, 3, 4)
# torch.zeros 创建元素全为 0 的张量
# 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.]]])
torch.ones(2, 3, 4)
# torch.ones 创建元素全为 1 的张量
# 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.]]])
torch.randn(3, 4)
# torch.randn 创建每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样的张量。
# tensor([[-0.0135, 0.0665, 0.0912, 0.3212],
# [ 1.4653, 0.1843, -1.6995, -0.3036],
# [ 1.7646, 1.0450, 0.2457, -0.7732]])
2. 张量的形变
y = x.reshape(3, 4)
# reshape() 用于改变张量的维度,如下使用 reshape 将 y 变为一个三行四列的张量
# 可以用 -1 调用 reshape() 的自动计算维度功能,例如原式可用 x.reshape(-1, 4) 代替
# y = tensor([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11]])
x.shape()
y.shape()
# shape 用于访问张量的形状和元素个数
# x.shape = torch.Size([12]), x 仅有一行,共十二个元素。
# y.shape = torch.Size([3, 4]), y 有三行四列,共十二个元素。
x.numel()
y.numel()
# 如果仅想知道元素个数,可以使用numel
# x.numel = y.numel = 12
3. 运算符
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 # **运算符是求幂运算
# 对于两个形状相同的张量,按元素运算会对它们的对应位置上的元素进行操作。
# (tensor([ 3., 4., 6., 10.]),
# tensor([-1., 0., 2., 6.]),
# tensor([ 2., 4., 8., 16.]),
# tensor([0.5000, 1.0000, 2.0000, 4.0000]),
# tensor([ 1., 4., 16., 64.]))
“按元素”方式可以应用更多的计算,包括像求幂这样的一元运算符。除了按元素计算外,我们还可以执行线性代数运算,包括向量点积和矩阵乘法。
torch.exp(x)
# 对张量 x 中的每个元素进行指数运算,即 e^Xi
# tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03])
对张量中的所有元素求和,会产生一个单元素张量。
x.sum()
# tensor(15.)
2. 张量连结
张量连结通俗来讲是将多个张量沿指定的维度连结到一起,形成一个新的张量。
关键在于:
-
选择轴:指定沿哪个轴(维度)连接在一起。
-
形状匹配:除了连接轴之外,其他轴的大小必须一致。
例:
x = arrange(12, dtype=torch.float32).reshape(3, 4)
y = torch.tensor[[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]]
# x = tensor([[ 0., 1., 2., 3.],
# [ 4., 5., 6., 7.],
# [ 8., 9., 10., 11.]])
# y = tensor([[2.0, 1, 4, 3],
# [1 , 2, 3, 4],
# [4 , 3, 2, 1]])
torch.cat((x, y), dim=0) # dim = 0 代表沿行连结, 3 + 3 = 6 行
# 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.]]),
torch.cat((x, y), dim=1) # dim = 1 代表沿列连结,4 + 4 = 8 列
# 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,否则该位置为0。
x == x
# tensor([[True, True, True, True],
# [True, True, True, True],
# [True, True, True, True]])
x == y
# tensor([[False, True, False, True],
# [False, False, False, False],
# [False, False, False, False]])
4. 广播机制
广播机制的目的是扩展张量的形状,使得形状不同的张量之间可以按元素操作。
工作机制:
-
通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
-
对生成的数组执行按元素操作。
# 创建两个张量
a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
# a = tensor([[0],
# [1],
# [2]])
# b = tensor([[0, 1]])
a + b
# tensor([[0, 1],
# [1, 2],
# [2, 3]])
# a 被扩展为:[[0, 0], b被扩展为:[[0, 1],
# [1, 1], [0, 1],
# [2, 2]] [0, 1]]
# a 的列数从1扩展为2(复制列), b 的行数从1扩展为3(复制行)。
5. 索引和切片
类似 python 的数组,张量中的元素可以通过索引来访问。第一个元素的索引是 0,最后一个元素索引是 -1。
x = arrange(12, dtype=torch.float32).reshape(3, 4)
# x = tensor([[ 0., 1., 2., 3.],
# [ 4., 5., 6., 7.],
# [ 8., 9., 10., 11.]])
x[-1]
# tensor([ 8., 9., 10., 11.])
x[1:3]
# tensor([ 4., 5., 6., 7.],
# [ 8., 9., 10., 11.])
# 注意索引 [a:b] 表示从 a 开始,到 b-1 结束,不包括 b ,数学符号即 [a,b)
除读取外,我们还可以通过指定索引来将元素写入矩阵。
x[1, 2] = 9
# tensor([[ 0., 1., 2., 3.],
# [ 4., 5., 9., 7.],
# [ 8., 9., 10., 11.]])
x[0:2, :] = 12
# tensor([[12., 12., 12., 12.],
# [12., 12., 12., 12.],
# [ 8., 9., 10., 11.]])
# ':' 表示取所有行或所有列
1.3 其他相关
1. 节约内存
一些操作可能会为新结果分配新的内存。
例:
before = id(y) # id()提供了内存中引用对象的确切地址
y = y + x
id(y) == before
# 结果为False,原因是为 y 分配了新的内存
这可能是不可取的,在机器学习中,我们可能有数百兆的参数,并且在一秒内多次更新所有参数。通常情况下,我们希望原地执行这些更新;如果我们不原地更新,其他引用仍然会指向旧的内存位置,这样我们的某些代码可能会无意中引用旧的参数。
before = id(X)
X += Y
id(X) == before
# 结果为True
# 可以使用 X[:] = X + Y 或 X += Y 来减少操作的内存开销。
2. 转换为其他Python对象
PyTorch 张量和 NumPy 数组可以互相转换,且在某些情况下会共享底层内存。这意味着,如果一个对象的值被修改,另一个对象的值也会随之改变。
import torch
import numpy as np
# 创建一个 PyTorch 张量
X = torch.tensor([[1, 2], [3, 4]])
# 将 PyTorch 张量转换为 NumPy 数组
A = X.numpy()
# 将 NumPy 数组转换回 PyTorch 张量
B = torch.tensor(A)
print("A 的类型:", type(A)) # numpy.ndarray
print("B 的类型:", type(B)) # torch.Tensor
# 输出:
# A 的类型: <class 'numpy.ndarray'>
# B 的类型: <class 'torch.Tensor'>
当使用 X.numpy() 将 PyTorch 张量转换为 NumPy 数组时, A 和 X 会共享底层内存。如果修改 A 或 X 的值,另一个对象的值也会随之改变。
如果张量的大小为 1(即只包含一个元素),可以使用以下方法将其转换为 Python 标量:
-
item() 函数:返回张量中的值作为 Python 标量。
-
Python 内置函数:如 float 或 int 。
# 创建一个大小为 1 的张量
a = torch.tensor([3.5])
# 转换为 Python 标量
print("a:", a)
# a: tensor([3.5000])
print("a.item():", a.item()) # 使用 item() 函数
# a.item(): 3.5
print("float(a):", float(a)) # 使用 float() 函数
# float(a): 3.5
print("int(a):", int(a)) # 使用 int() 函数
# int(a): 3更多推荐


所有评论(0)