目录

1.1 数据理论

1. 张量(Tensor)

2. 数组(Array)

3. 不同环境中的定义与实现

(1) NumPy

(2) PyTorch

4. 张量与数组的区别

 1.2 数据操作

1. 创建张量

 2. 张量的形变

3. 运算符

1. 按元素计算

2. 张量连结

3. 逻辑运算

4. 广播机制

5. 索引和切片

1.3 其他相关

1. 节约内存

2. 转换为其他Python对象


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. 广播机制

        广播机制的目的是扩展张量的形状,使得形状不同的张量之间可以按元素操作。

工作机制:

  1. 通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;

  2. 对生成的数组执行按元素操作。

# 创建两个张量
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 标量:

  1. item() 函数:返回张量中的值作为 Python 标量。

  2. 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
Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐