张量(Tensor)是深度学习中用于表示和处理多维数据的数据结构。张量与 Numpy 类似,但是不同的是深度学习框架专门为张量提供了丰富的操作API,包括创建张量、数据转换、数学运算、索引和切片等,使张量可以使用 GPU 加速,大大提高处理大量数据时的计算速度;同时提供自动微分 Autograd,自动计算和应用梯度。
简单的说,张量(Tensor)就是为深度学习框架量身定制的数据结构。使用张量,深度学习在处理数据和模型计算上更加方便和高效。
张量是深度学习框架的数据结构,读者首先需要选择一种深度学习框架(TensorFlow、PyTorch…)并下载,本博文基于 PyTorch 深度学习框架。
# 首先下载PyTorch框架
pip install torch==1.12.0
pip install torchvision==0.13.0
# 本bash执行于Anaconda Prompt
# 建议读者使用PyCharm或者VSCode IDE工具
import torch
第一个张量我们使用 arange
创建一个行向量 x:
x = torch.arange(12)
print(x)
tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
x 为创建的包含 12 个整数元素的张量。
通过张量的 shape
属性来访问张量的形状。
print(x.shape)
torch.Size([12])
如果只想知道张量中元素的数量,可以使用 numel()
函数。
print(x.numel())
12
如果只想改变张量的形状而不改变张量元素数量和值,可以通过使用 reshape()
函数。
y = x.reshape(3,4)
print(y)
tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
把张量x从形状(12,)的行向量转换为形状为(3,4)的矩阵y。新的张量形状发生改变,但是元素值并没有改变。
张量的转换也支持自动计算维度的功能,比如:
# 下列 z_1, z_2 张量同上述 reshape(3, 4) 函数功能
z_1 = x.reshape(-1, 4)
z_2 = x.reshape(3, -1)
有时,我们希望创建全0、全1张量,或者从特定随机分布中随机采样的数字来初始化矩阵,如下。
创建全 0 张量:
# 创建一个形状为 (2, 3, 4) 、值全部为 0 的三维张量
import torch
x_1 = torch.zeros(2,3,4)
print(x_1)
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.]]])
创建全 1 张量:
import torch
x_2 = torch.ones(2,3,4)
print(x_2)
print(x_2.shape)
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.Size([2, 3, 4])
创建正态分布随机采样张量:
创建一个形状为(3,4)的张量,其中每个元素从均值为 0, 标准差为 1 的标准高斯分布中随机采样。
import torch
x = torch.randn(3,4)
print(x)
tensor([[ 0.8532, -0.9053, -0.6921, 0.9674],
[-0.8108, -1.4881, -0.0434, 0.1967],
[-1.7507, -0.4498, 1.3718, -0.6994]])
数学运算中最常见的标准算数运算符(+,-,*,/和**)。将两个张量数组作为输入,按元素运算将二元运算符应用于两个数组中的每对位置对应的元素:
import torch
x = torch.tensor([1.0, 2, 4, 8])
y = torch.tensor([2, 2, 2, 2])
print("x+y=", x+y, "\nx-y=", x-y, "\nx*y=", x*y, "\nx/y=", x/y, "\nx**y=", x**y)
x+y= tensor([ 3., 4., 6., 10.])
x-y= tensor([-1., 0., 2., 6.])
x*y= tensor([ 2., 4., 8., 16.])
x/y= tensor([0.5000, 1.0000, 2.0000, 4.0000])
x**y= tensor([ 1., 4., 16., 64.])
按元素计算很重要的一点就是两个张量的形状 shape 必须相同,否则将因找不到对应位置的张量从而无法计算。
除了按元素计算外,还可以将多个张量连结在一起 cat(tensors, dim=)
,端对端叠起来形成更大的张量。
import torch
x = torch.arange(12).reshape(3, 4)
y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
print(torch.cat((x,y), dim=1))
将张量 x(3,4)与张量 y(3,4)横向连结形成更大的张量,形状(3,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.]])
dim=0 的连结结果将为形状(6,4)。
通过逻辑运算构建张量,对于两个张量的每个位置,如果对照位置相同,则对应位置值为 True,否则为 False;
import torch
x = torch.arange(12).reshape(3, 4)
y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
print("x:", x)
print("y:", y)
print("x==y:", x==y)
x: tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
y: tensor([[2., 1., 4., 3.],
[1., 2., 3., 4.],
[4., 3., 2., 1.]])
x==y: tensor([[False, True, False, True],
[False, False, False, False],
[False, False, False, False]])
如果想对张量中所有元素进行求和,使用 sum()
函数;
print("x:", x)
print(x.sum())
x: tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
tensor(66)
在上述张量的基本运算中,我们很多操作都规定需要两个张量保持相同的形状才能完成运算。但是在广播机制中,则不同。张量的广播机制用于扩展张量,虽然不要求两个向量完全相同,但是要求两个向量要分别满足形状:(x, y)与(y, z)方才可以进行运算。
e . g . e.g. e.g.
import torch
a = torch.arange(3).reshape(3, 1)
b = torch.arange(2).reshape(1, 2)
print("a=", a)
print("b=", b)
print("a+b=", a+b)
print("a*b=", a*b)
a= tensor([[0],
[1],
[2]])
b= tensor([[0, 1]])
a+b= tensor([[0, 1],
[1, 2],
[2, 3]])
a*b= tensor([[0, 0],
[0, 1],
[0, 2]])
上述张量的广播机制操作中,我们将张量 a(3,1)与张量 b(1,2)广播扩展为一个更大的张量,形状(3,2)。
如 Python 中数组一样,张量中的元素支持通过索引访问。与其他数组一样,张量中第一个元素的索引值为 0,最后一个元素的索引值为 -1。
import torch
x = torch.arange(12)
print("x=", x)
print("x[0]=", x[0], "\nx[-1]=", x[-1])
# 通过切片方式访问张量中的元素值
print("x[1:3]=", x[1:3])
x= tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
x[0]= tensor(0)
x[-1]= tensor(11)
x[1:3]= tensor([1, 2])
当然张量的操作支持通过索引改变其中元素的值;
import torch
x = torch.arange(12)
print(x)
x[-2] = 22
print(x)
tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 22, 11])
在张量中,运行一些操作可能会导致需要为新结果分配内存。在深度学习框架张量中,我们可以通过 id()
函数来访问一个张量占用内存的物理位置。
import torch
x = torch.arange(12)
print(id(x))
x = x + 1
print(id(x))
2606440820144
2606440818944
打印发展张量 x 在计算前后更换了占用的内存位置,或者说,新计算后为张量 x 新分配了内存空间存储其结果。
这种新分配内存空间不仅产生了不必要的内存占用,还会因为其他引用旧的内存位置导致出错。为了避免这种错误,通过切片方式原地更新;
import torch
x = torch.arange(12)
print(id(x))
x[:] = x + 1
print(id(x))
2296319225264
2296319225264
观察发现,运算前后占用的内存地址空间相同。
Torch 张量与 Numpy 数组共享底层内存,可以就地操作转换;
张量转换为数组:
a.item()
, float(a)
, int(a)
:
import torch
a = torch.arange(12)
b = a.numpy()
print(type(a), type(int(a[1])), type(b))
<class 'torch.Tensor'> <class 'int'> <class 'numpy.ndarray'>
数组转换为张量:
import numpy as np
import torch
x = np.arange(12)
y = torch.tensor(x)
print(type(x), type(y))
<class 'numpy.ndarray'> <class 'torch.Tensor'>
以上内存便是预备知识的第一部分张量的相关介绍;
如有任何问题,请联系或者留言;
2024.2.12