一.张量
在 PyTorch 中,张量(Tensor)是一种多维数组,可以用来表示各种类型的数据,例如图像、音频、文本等。张量具有以下特点:
-
多维数组:张量可以表示任意维度的多维数组,例如标量(0 维)、向量(1 维)、矩阵(2 维)等。
-
张量运算:张量支持各种数学运算,例如加法、减法、乘法、除法等,还支持逐元素运算、矩阵运算、广播运算等。
-
自动求导:张量支持自动求导,可以方便地进行反向传播算法,用于训练深度学习模型。
-
支持 GPU 计算:张量支持在 GPU 上进行计算,可以大幅提高计算速度。
在 PyTorch 中,可以使用 torch.Tensor() 函数创建一个空的张量,也可以使用 torch.tensor() 函数从 Python 列表、NumPy 数组或其他的张量中创建张量,还可以使用其他的函数创建特定类型的张量,例如 torch.zeros()、torch.ones()、torch.rand() 等。
例如,创建一个形状为 (3, 4) 的随机初始化的张量:
import torch
x = torch.rand(3, 4)
print(x)
#输出结果
tensor([[1.0236, 1.2137, 1.1852, 1.3366],
[1.5170, 1.9903, 1.9027, 1.8600],
[1.5812, 1.9834, 1.0563, 1.9288]])
二.Tensor
在 PyTorch 中,Tensor 是一个用于表示多维数组的类,类似于 NumPy 中的 ndarray。Tensor 是 PyTorch 中最基本的数据结构,可以用来表示各种类型的数据,例如图像、音频、文本等。
Tensor 有以下特点:
-
多维数组:Tensor 可以表示任意维度的多维数组,例如标量(0 维)、向量(1 维)、矩阵(2 维)等。
-
张量运算:Tensor 支持各种数学运算,例如加法、减法、乘法、除法等,还支持逐元素运算、矩阵运算、广播运算等。
-
自动求导:Tensor 支持自动求导,可以方便地进行反向传播算法,用于训练深度学习模型。
-
支持 GPU 计算:Tensor 支持在 GPU 上进行计算,可以大幅提高计算速度。
三.广播机制
pytorch中的广播机制是指在运算时,自动扩展张量的形状,使得他们能够逐元素操作,不需要额外对其进行形状转换或者复制数据。
在pytorch中广播机制如下:
1.如果两个维度的张量不同,则将维度较小的张量通过在前面加1来扩展其形状,直到两个张量维度数相同。(见例子1)
2.如果两个张量在某个维度数的形状不同,但其中一个张量的形状在这个维度上是1,则将这个张量通过复制数据来扩展其形状,使得他们在这个维度上的形状相同。(详情见例子2)
3.如果两个张量在某个维度上的形状不同,且两个张量在这个维度的形状都不是1,则无法进行广播,会抛出形状不兼容的异常。(见例子3)
import torch
# 创建形状为 (3, 1) 的张量 a,值为 0、1、2
a = torch.arange(3).reshape((3, 1))
# 创建形状为 (1, 2) 的张量 b,值为 0、1
b = torch.arange(2).reshape((1, 2))
# 输出张量 a 和 b 的值
print(a, b)
# 对张量 a 和 b 进行逐元素相加
# 由于张量 a 和 b 在第一维和第二维上的形状分别为 (3, 1) 和 (1, 2),是兼容的
# 因此 PyTorch 会自动对 a 和 b 进行扩展,使得它们的形状相同,然后进行逐元素相加
# 扩展后的 a 的形状为 (3, 2),值为 [[0, 0], [1, 1], [2, 2]]
# 扩展后的 b 的形状为 (3, 2),值为 [[0, 1], [0, 1], [0, 1]]
# a 和 b 逐元素相加得到形状为 (3, 2) 的张量,值为 [[0, 1], [1, 2], [2, 3]]
print(a + b)
#结果
tensor([[0],
[1],
[2]]) tensor([[0, 1]])
tensor([[0, 1],
[1, 2],
[2, 3]])
#例子二
import torch
# 创建一个形状为 (2, 3) 的张量 x
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
# 创建一个形状为 (2,) 的张量 y
y = torch.tensor([10, 20])
# 在第二个维度上插入一个新的维度,形状变为 (2, 1) 的张量
y = y.unsqueeze(1)
# 输出 y 的形状和值
print(y)
# 对 x 和 y 逐元素相加
# 由于 y 在第二个维度上的形状为 1,会被自动广播为形状为 (2, 3) 的张量(将张量为1的元素复制)
# 然后再和 x 逐元素相加得到形状为 (2, 3) 的张量
print(x + y)
#输出结果
tensor([[10],
[20]])
tensor([[11, 12, 13],
[24, 25, 26]])
#例子三
demo1=torch.zeros(2,3,4)
demo2=torch.ones(2,2)
demo1,demo2
demo1+demo2
#输出结果
RuntimeError Traceback (most recent call last)
in ()
2 demo2=torch.ones(2,2)
3 demo1,demo2
----> 4 demo1+demo2
RuntimeError: The size of tensor a (4) must match the size of tensor b (2) at non-singleton dimension 2
三.索引和切片
3.1索引
张量中的元素也能通过索引访问:与python中的列表一样,第⼀个元素 的索引是0,最后⼀个元素索引是-1;可以指定范围以包含第⼀个元素和最后⼀个之前的元素。
import torch
# 创建一个形状为 (3, 4) 的张量 x
x = torch.tensor([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
# 通过整数索引获取张量中的单个元素
print(x[1, 2]) # 输出 7
# 通过切片索引获取张量中的多个元素
print(x[:, 1:3]) # 输出 [[2, 3], [6, 7], [10, 11]]
3.2切片
注意:张量的切片是基于原张量的视图进行的(与python列表的不同之处),即切片操作不会创建新的张量,而是返回原张量的一个子集。因此,在对切片结果进行修改时,原张量的相应位置也会被修改。如果需要创建一个新的张量,可以使用 clone() 函数创建一个复制。
import torch
# 创建一个形状为 (3, 4) 的张量 x
x = torch.tensor([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
# 使用一个切片符号 : 表示所有元素
print(x[:]) # 输出 [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
# 使用一个整数和一个切片符号 : 表示从指定位置开始的所有元素
print(x[1:]) # 输出 [[5, 6, 7, 8], [9, 10, 11, 12]]
# 使用两个整数和一个切片符号 : 表示从指定位置开始到结束位置的所有元素
print(x[1:2, 0:2]) # 输出 [[5, 6]]
四.节省内存
深度学习模型通常需要处理大量的数据,因此内存占用是一个重要的问题。在实际应用中,通常需要训练大型的深度学习模型,这些模型需要占用大量的内存才能运行。因此,节省内存是深度学习中一个非常重要的问题。
运⾏⼀些操作可能会导致为新结果分配内存。例如,如果我们⽤Y = X + Y,我们将取消引⽤Y指向的张量, ⽽是指向新分配的内存处的张量。
x = x + y
不是原地操作,因为它会创建一个新的张量,而不是直接在原有的张量上进行修改。在执行 x = x + y
操作时,会首先计算 x + y
得到一个新的张量,然后将这个新的张量赋值给变量 x
。由于赋值操作会创建一个新的对象,并将变量指向这个新的对象,因此 x
现在指向了一个新的张量,而不是原有的张量。
相反,原地操作是指在原有的张量上进行修改,而不是创建一个新的张量。例如,x += y
和x[:]=x+y 就是一个原地操作,它会将 y
的值直接加到 x
上,而不创建一个新的张量。
使用原地操作可以减少内存开销,并且能够避免创建新的对象可能带来的一些问题,例如可能会导致 Python 垃圾回收机制的频繁调用,从而影响程序的性能。因此,在实际编程中,应该尽可能地使用原地操作。
before = id(Y)
Y = Y + X
id(Y) == before
#结果
False
before = id(X)
X += Y
id(X) == before
#结果
True
before = id(X)
X [:]=X+Y
id(X) == before
#结果
True