注意此处 import 的不是 pytorch 而是 torch。Torch 是采用 lua 语言实现的,从某种程度上说,PyTorch 是 Torch 的 Python 版本。(当然还是有部分差别的)
import torch
张量(tensor)可以说是机器学习中的数组,直观的看张量像是封装了数组以及一系列相关操作的数据结构。
一个轴的张量对应数学上的向量(vector),两个轴的张量对应数学上的矩阵(matrix),两个轴以上的张量还没有特定的数学名称。
可以调用 arange 函数创建指定范围、元素类型的行向量。除非额外指定,否则张量默认存储在内存中并采用基于CPU的运算。
可以通过张量的 shape 属性访问张量的形状,即张量由外向内的维度。
可以调用 numel 函数获取张量中的元素数量。
x = torch.arange(12)
x, x.shape, x.numel()
(tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]),
torch.Size([12]),
12)
可以调用 reshape 函数改变张量的形状而不改变元素数量与元素值。其中参数可用 -1 替代可自行计算补全参数
X = x.reshape(3, 4) # (3, 4)等价于(-1, 4)等价于(3, -1)
X, X.shape, X.numel()
(tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]]),
torch.Size([3, 4]),
12)
可以调用 zeros 函数和 ones 函数创建指定形状的元素全0/1的张量。
可以调用 randn 函数创建指定形状的元素从均值为0、标准差为1的正态分布中随机采样的张量。
当然也可以调用 tensor 函数直接使用列表创建张量。
torch.zeros((2,3,4)), torch.ones((2,3,4))
(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.]]]),
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((2,3,4))
(tensor([[ 1.6222, 0.7231, -1.4024, 0.7588],
[-0.1125, 0.5207, 0.6089, 1.2352],
[ 2.0539, 1.1926, 0.5572, 0.6947]]),
tensor([[[-0.7136, -1.2191, -1.2900, -1.5955],
[ 0.4796, -1.0602, -0.7268, -0.4906],
[ 0.5334, -0.3188, -1.2223, 1.4670]],
[[ 0.3615, 0.4890, -1.0624, -0.3882],
[ 0.1696, -1.2423, 0.4243, 0.7088],
[-0.2190, 1.1008, 1.2330, 0.3669]]]))
torch.tensor([[2, 1, 4, 3], [9, 1, 4, 3], [2, 1, 0, 3]])
tensor([[2, 1, 4, 3],
[9, 1, 4, 3],
[2, 1, 0, 3]])
常见的标准运算符都可以进行按元素(elementwise)运算,按元素方式可以应用于更多的计算,包括 exp 函数。
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, torch.exp(x) # **是求幂运算
(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.]),
tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03]))
可以调用 cat 函数把多个张量按某个轴连接(concatenate)在一起。
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]])
X, Y, 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.]]),
tensor([[2., 1., 4., 3.],
[1., 2., 3., 4.],
[4., 3., 2., 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.]]))
可以通过逻辑运算符构建二元张量,例如可以使用 == 符号比较每个位置元素的值。
调用 sum 函数会对张量中所有元素求和后产生一个单元素张量。
X == Y
tensor([[False, True, False, True],
[False, False, False, False],
[False, False, False, False]])
X.sum()
tensor(66.)
在某些情况下,不同形状的张量可以通过广播机制(broadcasting mechanism)自动扩展以进行按元素操作。
a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b, a + b
(tensor([[0],
[1],
[2]]),
tensor([[0, 1]]),
tensor([[0, 1],
[1, 2],
[2, 3]]))
和任何的其他的Python数组一样,张量可以通过索引访问、写入等操作。
X, X[-1], X[1:3]
(tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]]),
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.]])
X[0:2, :] = 12 # 访问第1行和第2行的所有元素
X
tensor([[12., 12., 12., 12.],
[12., 12., 12., 12.],
[ 8., 9., 10., 11.]])
直接使用 Y = Y + X 则会在运算过程中给Y重新申请内存。
执行原地操作有两种方式:
before = id(Y)
Y = Y + X # 新Y会重新分配内存
id(Y) == before
False
Z = torch.zeros_like(Y)
print("id(Z):", id(Z))
Z[:] = X + Y # 使用切片表示法将操作结果分配给原数组以实现原地操作
print("id(Z):", id(Z))
id(Z): 2695200905792
id(Z): 2695200905792
before = id(X)
X += Y # 使用 += 运算符进行原地操作
id(X) == before
True
PyTorch张量可以很容易转换为NumPy张量。要将大小为1的张量转换为Python标量可以调用 item 函数,也可以使用Python内置的强制类型转换。
A = X.numpy()
B = torch.tensor(A)
type(A), type(B)
(numpy.ndarray, torch.Tensor)
a = torch.tensor([3.5])
a, a.item(), float(a), int(a)
(tensor([3.5000]), 3.5, 3.5, 3)
(1)将 == 替换为 > 或 < 得到的仍是按元素进行逻辑运算的结果。
X < Y, X > Y
(tensor([[False, False, False, False],
[False, False, False, False],
[False, False, False, False]]),
tensor([[True, True, True, True],
[True, True, True, True],
[True, True, True, True]]))
(2)可广播的一对张量需满足以下规则:
x = torch.ones(5,3,4,1)
y = torch.ones( 3,1,1)
(x + y).shape # 可广播
torch.Size([5, 3, 4, 1])
x = torch.empty((0,))
y = torch.empty(2, 2)
# (x + y).shape # 不可广播 不满足第一条规则
x = torch.ones(5, 2, 4, 1)
y = torch.ones( 3, 1, 1)
# (x + y).shape # 不可广播 不满足第二条规则