2.1.1. 入门

# 导入pytorch包,这里虽然名称叫pytorch,但导入的时候写为torch
import torch
# arange创建一个行向量,这个这个行向量包含以0开始的前12个整数,它们默认创建为整数。也可指定创建类型为浮点数。
x = torch.arange(12)
x
tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
# 可以通过张量(tensor类)的shape属性来访问张量(沿每个轴的长度)的形状
x.shape
# 这里的输出表示,张量有一个轴,这个轴的长度是12
torch.Size([12])
# 使用numel方法来获取一个张量中元素的个数
x.numel()
12
# 通过reshape方法来改变一个张量的形状而不改变元素数量和元素的值(所以reshape前后的张量元素个数必须相同)
X = x.reshape(3, 4) # 等效于x.reshape(3, -1)或x.reshape(-1, 4),通过将高度或宽度设为-1让pytorch自动运算出对应的高度和宽度
X
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]])
# 此处的shape属性值表示这个张量有三个轴,每个轴的元素个数为4
X.shape
torch.Size([3, 4])
# 使用torch.zeros方法来创建全零张量
# 使用torch.ones方法来创建全1张量
torch.zeros(2, 3, 4)
torch.ones(2, 3, 4)
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来创建随机张量,每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样。
torch.randn(3, 4)
tensor([[-0.2934,  1.8076,  0.5712,  0.4720],
        [-1.8764,  1.9466,  0.8245, -0.2314],
        [ 0.3206, -2.5215,  0.0761,  1.2041]])
torch.randn(2, 3, 4)
tensor([[[-0.6647, -0.6470, -2.1646, -1.4095],
         [ 0.2471,  0.7215,  0.6549,  0.1254],
         [ 1.0554,  1.9935, -0.1253, -1.0067]],

        [[-1.8781,  1.5071,  0.7466, -1.0695],
         [-0.9227,  0.3873, -0.0489, -0.7812],
         [-0.7515, -0.4628, -0.3134,  1.4016]]])
# 将python中的嵌套列表转化为张量,使用torch.tensor方法
torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
tensor([[2, 1, 4, 3],
        [1, 2, 3, 4],
        [4, 3, 2, 1]])

2.1.2. 运算符

# 任意具有相同形状的张量,常见的标准算数运算符都可以被升级为按元素运算
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.]))
# 除了二元运算,对张量的一元运算也可以升级为按元素运算,比如对每个元素求e的幂
# 除了按元素运算,还可以对张量执行线性代数运算,见2.3节
torch.exp(x)
tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03])
# 张量之间的连接,使用torch.cat,其中,参数dim=0表示行拼接(shape的第一个元素,append到最后一行之后),
# dim=1表示列拼接(shape的第二个元素,append到每一行之后),依次类推
X = torch.arange(12, dtype=torch.float32).reshape(3, 4)
Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)
(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.]]),
 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.]]))
# 按元素相等判断
X == Y
tensor([[False,  True, False,  True],
        [False, False, False, False],
        [False, False, False, False]])
# 按元素大小判断
X < Y, X > Y
(tensor([[ True, False,  True, False],
         [False, False, False, False],
         [False, False, False, False]]),
 tensor([[False, False, False, False],
         [ True,  True,  True,  True],
         [ True,  True,  True,  True]]))
# 更多操作,实质上是两个张量的按元素布尔运算,输出仍然是一个张量,张量的元素都是布尔值
X == Y + torch.ones(3, 4)
tensor([[False, False, False, False],
        [False, False, False, False],
        [False, False, False, False]])
# 对张量中所有元素求和,使用张量类的sum方法,返回一个单元素张量
X.sum()
tensor(66.)

2.1.3. 广播机制

# 在某些情况下,即使形状不同,我们仍然可以通过调用 广播机制(broadcasting mechanism)来执行按元素操作。这种机制的工作方式如下:
# 1. 通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
# 2. 对生成的数组执行按元素操作。
a = torch.arange(3).reshape(3, 1)
b = torch.arange(2).reshape(1, 2)
a, b
(tensor([[0],
         [1],
         [2]]),
 tensor([[0, 1]]))
""" 矩阵a将复制列, 矩阵b将复制行,然后再按元素相加。
a -> tensor([[0, 0],
[1, 1],
[2, 2]])
b -> tensor([[0, 1],
[0, 1],
[0, 1]]))
"""
a + b
tensor([[0, 1],
        [1, 2],
        [2, 3]])

2.1.4. 索引和切片

# 就像在任何其他Python数组中一样,张量中的元素可以通过索引访问
# 与任何Python数组一样:第一个元素的索引是0,最后一个元素索引是-1; 可以指定范围以包含第一个元素和最后一个之前的元素(前闭后开)。
""" X如下
(tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]]),
"""
X[-1], X[1:3]
(tensor([ 8.,  9., 10., 11.]),
 tensor([[ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.]]))
# 赋值
X[1, 2] = 9
X
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  9.,  7.],
        [ 8.,  9., 10., 11.]])
# 多个元素赋值,“:”代表沿轴1(列)的所有元素
X[0:2, :] = 12
X
tensor([[12., 12., 12., 12.],
        [12., 12., 12., 12.],
        [ 8.,  9., 10., 11.]])

2.1.5. 节省内存

# 语句Y = Y + X会为新结果分配内存,而不是原地更新变量Y,我们需要一种方法可以原地更新变量Y以节省内存
# 在机器学习中,我们可能有数百兆的参数,并且在一秒内多次更新所有参数。通常情况下,我们希望原地执行这些更新;
# 如果我们不原地更新,其他引用仍然会指向旧的内存位置,这样我们的某些代码可能会无意中引用旧的参数。
# 执行原地操作非常简单,可以使用切片表示法将操作的结果分配给先前分配的数组
Z = torch.zeros_like(Y) #使用zeros_like来分配一个全0的块,形状与张量Y相同
print('id(Z):', id(Z))
Z[:] = X + Y
print('id(Z):', id(Z))
id(Z): 2403882846624
id(Z): 2403882846624
# 如果在后续计算中没有重复使用X, 我们也可以使用X[:] = X + Y或X += Y来减少操作的内存开销。
# 观察:在torch中,X = X + Y 与 X += Y 可能是不一样的,一个开辟新的内存空间,一个原地更新
before = id(X)
X += Y
id(X) == before
True

2.1.6. 转换为其他Python对象

# 使用numpy方法或torch.tensor方法来在numpy张量和torch张量间转换
A = X.numpy()
B = torch.tensor(A)
type(A), type(B)
(numpy.ndarray, torch.Tensor)
# 要将大小为1的张量转换为Python标量,我们可以调用item函数或Python的内置函数。
a = torch.tensor([3.5])
a, a.item(), float(a), int(a)
(tensor([3.5000]), 3.5, 3.5, 3)

2.1.7. 小结

  • 深度学习存储和操作数据的主要接口是张量(维数组)。它提供了各种功能,包括基本数学运算、广播、索引、切片、内存节省和转换其他Python对象。