赞
踩
原文链接:https://xiets.blog.csdn.net/article/details/131710553
版权声明:原创文章禁止转载
专栏目录:PyTorch 专栏(总目录)
PyTorch 相关网站:
PyTorch 是由 Facebook 研发的开源的深度学习框架,并且支持 CPU 和 GPU 加速计算。
PyTorch 安装参考:get-started/locally
torch.Tensor
是包含单一数据类型元素的多维数组,它是 PyTorch 中数据存储和运算的基本单元,类似于 NumPy 的 numpy.ndarray
,两者的函数接口和运算方式几乎一样。实际上 PyTorch 内部也是把 ndarray
包装成 Tensor
。
Tensor
中文称为 张量(可以看做是一个多维数组),是 PyTorch 中最基本的数据类型。在数学中,标量是一个只有大小没有方向的量,如:1、2、3 等。向量是既有大小又有方向的量(一维数组),如 A = (x0, y0, z0)。矩阵是由多个向量组成的二维数组。实际上,标量、向量 和 矩阵 都可以看做是张量的特例,标量是零维张量,向量是一维张量,矩阵是二维张量。
Tensor
官方文档:torch.Tensor
Torch 定义了 10 种具有 CUP 和 GPU 变体的张量类型:
数据类型 CPU 类型 GPU 类型 类型说明
---------------------------------------------------------------------------------------------------
torch.float32 or torch.float torch.FloatTensor torch.cuda.FloatTensor 32位浮点数
torch.float64 or torch.double torch.DoubleTensor torch.cuda.DoubleTensor 64位浮点数
torch.float16 or torch.half torch.HalfTensor torch.cuda.HalfTensor 16位浮点数
torch.bfloat16 torch.BFloat16Tensor torch.cuda.BFloat16Tensor 16位浮点数
torch.complex32 or torch.chalf 32位复数
torch.complex64 or torch.cfloat 64位复数
torch.complex128 or torch.cdouble 128位复数
torch.uint8 torch.ByteTensor torch.cuda.ByteTensor 8位整数(无符号)
torch.int8 torch.CharTensor torch.cuda.CharTensor 8位整数(有符号)
torch.int16 or torch.short torch.ShortTensor torch.cuda.ShortTensor 16位整数(有符号)
torch.int32 or torch.int torch.IntTensor torch.cuda.IntTensor 32位整数(有符号)
torch.int64 or torch.long torch.LongTensor torch.cuda.LongTensor 64位整数(有符号)
torch.bool torch.BoolTensor torch.cuda.BoolTensor 布尔值
torch.quint8 torch.ByteTensor / 量化的8位整数(无符号)
torch.qint8 torch.CharTensor / 量化的8位整数(有符号)
torch.qint32 torch.IntTensor / 量化的32位整数(有符号)
torch.quint4x2 torch.ByteTensor / 量化的4位整数(无符号)
torch.float64
、torch.int64
、torch.bool
等数据类型是一个 torch.dtype
类型的实例,用于表示 torch
数据类型(dtype
)。
torch.Tensor
表示张量一个类,可以通过它的构造函数直接创建 Tensor
实例,或者通过 torch 模块中的其他 Tensor 构建函数创建。
Tensor
的创建操作参考:Creation Ops
使用 torch.Tensor()
创建 Tensor:
import torch
# 创建 形状为 2x3x4 的张量, 默认用 0 填充
t = torch.Tensor(2, 3, 4)
print(type(t)) # <class 'torch.Tensor'>
print(t.type()) # torch.FloatTensor
print(t.dtype) # torch.float32
print(t.size()) # torch.Size([2, 3, 4])
print(t.shape) # torch.Size([2, 3, 4])
print(t)
"""
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.]]])
"""
# 使用预先存在的数据 (Python序列 或 numpy.ndarray) 创建张量
t = torch.Tensor([[1, 2, 3], [4, 5, 6]])
print(t.dtype) # torch.float32
print(t)
"""
tensor([[1., 2., 3.],
[4., 5., 6.]])
"""
使用 torch.Tensor()
创建的 Tensor
的数据类型默认为 torch.float32
(CPU/GPU类型为FloatTensor
)。也可以使用 torch.DoubleTensor()
、torch.IntTensor()
等其他张量类型的构造函数创建 Tensor
实例:
import torch
t = torch.DoubleTensor(2, 3, 4) # 默认用 0 填充
print(type(t)) # <class 'torch.Tensor'>
print(t.type()) # torch.DoubleTensor
print(t.dtype) # torch.float64
print(t.shape) # torch.Size([2, 3, 4])
t = torch.IntTensor([1, 2, 3]) # 默认用 0 填充
print(type(t)) # <class 'torch.Tensor'>
print(t.type()) # torch.IntTensor
print(t.dtype) # torch.int32
print(t.shape) # torch.Size([3])
注意:使用 torch.DoubleTensor()
、torch.IntTensor()
等其他张量类型的构造函数创建出来的对象实例都是 torch.Tensor
类型,只是其数据类型 dtype
和 type()
不一样。
torch.tensor()
函数通过 复制 现有的数据(Python序列、numpy.ndarray、张量 等)构造一个没有 autograd 历史的张量,并且可以指定 数据类型、设备类型、是否requires_grad 等。
torch.tensor()
函数:
torch.tensor(data, *, dtype=None, device=None, requires_grad=False, pin_memory=False) -> Tensor
"""
参数:
data 张量的初始数据。可以是列表、元组、numpy.ndarray、标量、张量 或 其他类型。
dtype 数据类型(torch.dtype), 默认值为 None 表示从 data 数据中推断类型。
可取值 torch.float64, torch.int64, torch.bool 等。
device 构造张量的设备 ("cpu", "cuda" or "mps")。
如果 device=None, 并且 data 是张量, 则使用 data.device。
如果 device=None, 并且 data 不是张量, 则 device="cpu"。
requires_grad 是否需要 autograd。
pin_memory True 表示返回的张量将分配在固定内存中。仅适用于 CPU 张量。默认值为 False。
"""
在处理张量时,如果需要使用现有张量创建新的没有 autograd 历史的张量,可以使用
torch.Tensor.clone()
、torch.Tensor.detach()
和torch.Tensor.requires_grad_()
以提高可读性。如果
t
是一个张量,则torch.tensor(t)
等价于t.clone().detach()
,torch.tensor(t, requires_grad=True)
等价于t.clone().detach().requires_grad_(True)
。
torch.tensor()
创建 Tensor
示例:
import torch
import numpy as np
# data传递一个标量数字, 创建出来的是0维张量(也就是一个标量), 整数推断为 torch.int64 类型
t = torch.tensor(10)
print(t.dtype, t.type(), t.shape) # torch.int64 torch.LongTensor torch.Size([])
print(t) # tensor(10)
# data传递一个一维数组, torch数据类型指定为torch.int32
t = torch.tensor([1, 2, 3], dtype=torch.int32)
print(t.dtype, t.type(), t.shape) # torch.int32 torch.IntTensor torch.Size([3])
print(t) # tensor([1, 2, 3], dtype=torch.int32)
# data传递一个二维数组, 包含浮点数则默认推断为torch.float32
t = torch.tensor([[1.0, 2.0, 3.0], [4, 5, 6]])
print(t.dtype, t.type(), t.shape) # torch.float32 torch.FloatTensor torch.Size([2, 3])
print(t)
"""
tensor([[1., 2., 3.],
[4., 5., 6.]])
"""
# data传递一个 ndarray, 因为 numpy 默认的是 np.float64 类型, 所有结果张量是 torch.float64 类型
t = torch.tensor(np.zeros(shape=(2, 3, 4)))
print(t.dtype, t.type(), t.shape) # torch.float64 torch.DoubleTensor torch.Size([2, 3, 4])
print(t)
"""
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.]]], dtype=torch.float64)
"""
# requires_grad=True (如果是整数类型, 则 requires_grad 必须为 False, 因为整数无法 autograd)
t = torch.tensor([1, 2, 3], dtype=torch.float32, requires_grad=True)
print(t.dtype, t.type(), t.shape) # torch.float32 torch.FloatTensor torch.Size([3])
print(t)
"""
tensor([1., 2., 3.], requires_grad=True)
"""
torch.as_tensor()
保留 autograd 历史并尽可能避免复制。torch.from_numpy()
创建一个与 NumPy 数组共享存储空间的张量。
函数详情:
torch.as_tensor(data, dtype=None, device=None) -> Tensor
"""
转换 data 为张量, 共享数据并在可能的情况下保留 autograd 历史记录。
如果 data 是张量, 且 dtype 和 device 和请求的一样, 则返回 data 本身。
如果 data 是张量, 且 dtype 或 device 和请求的不同, 则复制 data (不共享内存)。
如果 data 是 ndarray, 且 dtype 和 device 和请求的一样, 则使用 torch.from_numpy()。
如果 data 是 Python 序列, 始终不共享内存。
"""
torch.from_numpy(ndarray) -> Tensor
"""
从 numpy.ndarray 创建一个 Tensor。
返回的张量和 ndarray 共享内存。对 张量 的修改将反应在 ndarray, 对 ndarray 的修改也会反应在 张量。
返回的张量不可调整大小。不支持写入从只读 ndarray 创建的张量, 这将导致不可知的行为。
"""
torch.as_tensor()
代码示例:
import torch
import numpy as np
a = np.array([1, 2, 3], dtype=np.int64) # 创建 ndarray, 数据类型为 int64
t = torch.as_tensor(a, dtype=torch.int64) # 创建张量, 数据类型也为 int64, 将转为调用 torch.from_numpy()
print(a, a.dtype) # [1 2 3] int64
print(t, t.dtype) # tensor([1, 2, 3]) torch.int64
a[0] = -10 # 对 ndarray 修改, 将同步反应在 张量
t[1] = -20 # 对 张量 修改, 将同步反应在 ndarray
print(a) # [-10 -20 3]
print(t) # tensor([-10, -20, 3])
# 如果数据类型不同, 则 as_tensor() 将复制 data, 即不共享内存
a = np.array([1, 2, 3], dtype=np.int64)
t = torch.as_tensor(a, dtype=torch.float64)
a[0] = 123 # 修改 data, 不会反应在 张量
print(t) # tensor([1., 2., 3.], dtype=torch.float64)
torch.from_numpy()
代码示例:
import torch
import numpy as np
a = np.array([1, 2, 3])
t = torch.from_numpy(a)
print(a) # [1 2 3]
print(t) # tensor([1, 2, 3])
a[0] = 10
t[1] = 20
print(a) # [10 20 3]
print(t) # tensor([10, 20, 3])
torch
模块中有许多用于快速创建 Tensor
的函数,参考:Creation Ops
创建具有固定值的 Tensor
:
函数 | 说明 |
---|---|
torch.zeros() | 返回一个用标量值 0 填充的张量,其形状由参数 size 决定。 |
torch.zeros_like() | 返回一个用标量值 0 填充的张量,其形状与输入的 input 张量相同。 |
torch.ones() | 返回一个用标量值 1 填充的张量,其形状由参数 size 决定。 |
torch.ones_like() | 返回一个用标量值 1 填充的张量,其形状由参数 size 决定。 |
torch.full() | 返回一个用 fill_value 参数值填充,形状大小为 size 的张量。 |
torch.full_like() | 返回一个用 fill_value 参数值填充,形状大小与 input 张量相同的张量。 |
torch.eys() | 返回一个二维张量(单位矩阵),对角线为 1,其他地方为 0。 |
根据范围策略创建 Tensor
:
函数 | 说明 |
---|---|
torch.arange() | 在 [start, end) 范围内,以 step 为步长创建一维张量,元素数量为 (end-start)/step 。 |
torch.linspace() | 在 [start, end] 范围内,构造均匀分布的,有 steps 个元素的一维张量。 |
torch.logspace() | 生成一个大小为 steps 的一维张量, 元素值为 basex,其中 x 是 torch.linspace() 产生的序列值。 |
创建随机数 Tensor
:
函数 | 说明 |
---|---|
torch.rand() | 创建在 [0.0, 1.0) 区间内均匀分布的一组随机数张量,其形状由参数 size 决定。 |
torch.rand_like() | 创建在 [0.0, 1.0) 区间内均匀分布的一组随机数张量,其形状与输入的 input 张量相同。 |
torch.randint() | 创建在 [low, high) 区间内均匀分布的一组随机数整数张量,其形状由参数 size 决定。 |
torch.randint_like() | 创建在 [low, high) 区间内均匀分布的一组随机数整数张量,其形状与输入的 input 张量相同。 |
torch.randn() | 创建符合 标准正态分布(均值为0,方差为1)的随机数张量,其形状由参数 size 决定。 |
torch.randn_like() | 创建符合 标准正态分布(均值为0,方差为1)的随机数张量,其形状与输入的 input 张量相同。 |
如果需要设置随机数生成种子的状态,可以调用:torch.set_rng_state(new_state)
快速创建函数代码示例:
import torch
t = torch.zeros((2, 3)) # size 可以以 序列的形式传入, 也可以以 *size 的形式传入
print(t, t.dtype)
"""
tensor([[0., 0., 0.],
[0., 0., 0.]]) torch.float32
"""
t0 = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.int8)
t = torch.ones_like(t0) # size 和 dtype 都保持与 t0 一致
print(t)
"""
tensor([[1, 1, 1],
[1, 1, 1]], dtype=torch.int8) torch.int8
"""
t = torch.full((3, 5), 100)
print(t)
"""
tensor([[100, 100, 100, 100, 100],
[100, 100, 100, 100, 100],
[100, 100, 100, 100, 100]])
"""
t = torch.eye(3, 5)
print(t)
"""
tensor([[1., 0., 0., 0., 0.],
[0., 1., 0., 0., 0.],
[0., 0., 1., 0., 0.]])
"""
t = torch.arange(0, 10, 1)
print(t)
"""
tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
"""
t = torch.linspace(0, 10, 5)
print(t)
"""
tensor([ 0.0000, 2.5000, 5.0000, 7.5000, 10.0000])
"""
t = torch.rand((1, 5))
print(t)
"""
tensor([[0.7706, 0.1781, 0.2407, 0.4579, 0.0864]])
"""
t = torch.randint(0, 9, (3, 5))
print(t)
"""
tensor([[3, 4, 8, 2, 7],
[5, 8, 7, 0, 7],
[0, 0, 8, 1, 8]])
"""
t = torch.randn((3, 6))
print(t)
"""
tensor([[ 0.9932, -1.1636, -0.3698, -0.6131, 0.0571, 0.6054],
[-0.5878, -0.1389, -1.6374, -0.2527, 0.3637, -0.3284],
[-0.9119, 0.3085, 0.8913, 0.9905, 0.6498, -0.7845]])
"""
Tensor
可以直接转换为 NumPy 数组或 Python 列表,相关方法:
Tensor.numpy(*, force=False) -> numpy.ndarray
"""
Tensor 转换为 NumPy 数组。
如果 force=False (默认), 则仅当张量在 CPU 上时才执行转换, 不需要梯度, 没有设置共轭位, 并且是 NumPy 支持的 dtype 和布局。
返回的 ndarray 和张量将共享它们的存储空间,因此对张量的更改将反映在 ndarray 中, 反之亦然。
如果 force=True, 这相当于调用 t.detach().cpu().resolve_conj().resolve_neg().numpy()。
如果张量不在 CPU 上或者设置了共轭位或负位, 则张量不会与返回的 ndarray 共享其存储空间。设置 force=True 可能是一个有用的速记。
"""
Tensor.tolist() -> List[number]
"""
将张量作为 Python (嵌套) 列表返回。对于标量, 返回一个标准的 Python 数据类型, 就像 Tensor.item()。
如有必要, 张量会首先自动移至 CPU。
也可以先转换为 NumPy 再转换为 Python 列表, 如: Tensor.numpy().tolist()。
"""
torch 中的标量默认也是一个 Tensor
(0维的张量),如果要转换为 Python 标准数据类型,可以调用 Tensor.item()
:
>>> import torch
>>> t = torch.tensor(1)
>>> t
tensor(1)
>>> t.item()
1
>>> type(t.item())
<class 'int'>
>>> t = torch.tensor([2])
>>> t.item()
2
>>> t = torch.tensor([[[3]]])
>>> t.item()
3
# 只有一个元素的行列才能调用 item() 方法转换为 Python 标量。
# 如果有多个元素, 需调用 tolist() 转换为列表。
Tensor
对象的常用属性:
Tensor.dtype # 数据类型(torch.dtype), torch.float32、torch.int64、torch.bool 等
Tensor.shape # 形状(torch.Size), 是一个序列类型, 也可以使用 Tensor.size() 获取
Tensor.ndim # 维度(int), 也可以通过 Tensor.dim() 获取
Tensor.requires_grad # bool, 此张量是否需要自动计算梯度 (自动微分)
Tensor.grad # 计算出来的的梯度之和, 默认为 None, 并且在第一次调用 backward() 为 self 计算梯度时成为一个 Tensor
Tensor.grad_fn # 用于计算梯度的函数
Tensor.data # 返回形状和数据相同, 但 requires_grad=False 的 Tensor (即返回不需要计算梯度的张量)
Tensor.device # 张量所在位置(torch.device), cpu 或 cuda
Tensor.is_cuda # bool, 张量是否存储在 GPU 上
Tensor.is_quantized # 张量是否是量化的
Tensor.is_meta # 张量是否是元张量
Tensor.T # 返回此张量的维度反转的视图
Tensor.H # 返回共轭和转置矩阵 (二维张量) 的视图, 只用用于二维张量
Tensor.mT # 返回此张量的视图, 最后两个维度已转置
Tensor.mH # 访问此属性等同于调用 adjoint()。即返回共轭张量的视图, 最后两个维度已转置。
Tensor.real # 如果是复数张量, 则返回实部值组成的新张量
Tensor.imag # 如果是复数张量, 则返回虚部值组成的新张量
torch
数学运算参考:Math Operations
torch
中的数学常量:
torch.e # 自然常数
torch.inf # 无穷大值
torch.nan # 无效的数值
torch.pi # 圆周率π
torch.tau # 常数τ, 2倍圆周率
Tensor
对象的常用的数学操作:
Tensor.add() # 加法, 将标量或张量添加到 self 张量, 相当于 self + other
Tensor.mul() # 乘法, self 与 标量或张量 相乘, 相当于 self * other
Tensor.div() # 除法, self / other
Tensor.fmod() # 取模, self % other
Tensor.remainder() # 求余数, 与 fmod() 一样
Tensor.abs() # 求绝对值
Tensor.ceil() # 向上取整
Tensor.floor() # 向下取整
Tensor.clamp() # 取上下限, 低于 min 的设置为 min, 高于 max 的设置为 max
Tensor.round() # 四舍五入取整
Tensor.frac() # 取小数部分, 如 3.14 -> 0.14
Tensor.neg() # 取负, -self
Tensor.reciprocal() # 取倒数, 1/self
Tensor.log() # 取自然对数, log_e(self)
Tensor.log10() # 取以10为底的对数, log_10(self)
Tensor.pow() # 取幂, self^exponent
Tensor.exp() # 自然常数的指数, e^self
Tensor.sigmoid() # 应用 sigmoid() 函数
Tensor.sign() # 求每个元素的正负值, 小于0的为 -1, 等于0的为0, 大于0为为1
Tensor.sqrt() # 求开平方根
Tensor.dist() # 求范数
Tensor.mean() # 求均值
Tensor.nanmean() # 计算指定维度上所有非 NaN 元素的平均值
Tensor.prod() # 计算所有元素的乘积
Tensor.sum() # 计算所有元素的的和
Tensor.max() # 计算所有元素中的最大值
Tensor.min() # 计算所有元素中的最小值
Tensor
的数学操作可以使用自身方法调用,也可以使用 torch
模块中的函数调用,例如 tensor.add(...)
相当于 torch.add(tensor, ...)
代码示例:
import torch
t = torch.ones((1, 3))
print(t) # tensor([[1., 1., 1.]])
print(t.add(5)) # tensor([[6., 6., 6.]])
print(torch.add(t, 5)) # tensor([[6., 6., 6.]])
两个形状相同的张量之间可以进行数学运算,张量和标量也可以进行数学运算。
张量、张量、标量 之间的数学运算规则:
import torch
t1 = torch.ones((2, 3))
print(t1)
"""
tensor([[1., 1., 1.],
[1., 1., 1.]])
"""
t2 = torch.full((2, 3), 5)
print(t2)
"""
tensor([[5, 5, 5],
[5, 5, 5]])
"""
t3 = t1 + t2 # 两个张量直接的数学运算, 形状必须相同
print(t3)
"""
tensor([[6., 6., 6.],
[6., 6., 6.]])
"""
t4 = t1.add(t2) # 相当于 torch.add(t1, t2), 也相当于 t1 + t2
print(t4)
"""
tensor([[6., 6., 6.],
[6., 6., 6.]])
"""
t5 = t1 + 1 # 张量 和 标量 之间数学运算, 会把标量应用到张量中的每一个元素, 并且返回新的张量
print(t5) # t1 + 1 相当于 相当于 torch.add(t1, 1), 也相当于 t1.add(1)
"""
tensor([[2., 2., 2.],
[2., 2., 2.]])
"""
Tensor
的数学操作方法,有两种形式。一种是运算结果产生副本返回的方法,如 add()
、mul()
;另一种是不产生副本,直接把结果存储到 self
中,这种方法以下划线_
结尾,如 add_()
、mul_()
。一般 Tensor
的数学操作方法,都会有一个以下划线 _
结尾的对应方法。代码示例:
import torch
t = torch.ones((2, 3))
print(t)
"""
tensor([[1., 1., 1.],
[1., 1., 1.]])
"""
t.add(5) # 运算结果返回副本
print(t)
"""
tensor([[1., 1., 1.],
[1., 1., 1.]])
"""
t.add_(5) # 运行结果直接保存在 self, 并返回 self
print(t)
"""
tensor([[6., 6., 6.],
[6., 6., 6.]])
"""
torch
模块中常用的线性代数操作函数:
torch.dot(input, other, *, out=None) -> Tensor
"""
计算两个一维张量的点积 (内积, 相同位置元素的乘积之和)。
与 NumPy 的 dot 不同, torch.dot() 有意只支持计算两个具有相同元素个数的一维张量的点积。
"""
torch.mv(input, vec, *, out=None) -> Tensor
"""
矩阵与向量乘法 (矩阵乘法的一个特例), input 的形状为 (n, m), vec 的形状为 (m,), 结果张量的形状为 (n,)
"""
torch.mm(input, mat2, *, out=None) -> Tensor
"""
矩阵乘法, nput 的形状为 (n, m), mat2 的形状为 (m, p), 结果张量的形状为 (n, p)
矩阵乘法: 第1个矩阵的 第n(i)行 和 第2个矩阵的 第p(i)列 对应元素的乘积之和, 作为结果张量的 第 i行i列 的值。
"""
张量相关乘法的代码示例:
import torch
t1 = torch.tensor([1, 2, 3])
t2 = torch.tensor([4, 5, 6])
print(torch.dot(t1, t2)) # tensor(32)
# t1 dot t2 = 1*4 + 2*5 + 3*6 == 32
t1 = torch.tensor([[1, 2, 3],
[4, 5, 6]])
t2 = torch.tensor([7, 8, 9])
print(torch.mv(t1, t2)) # tensor([ 50, 122])
# [1*7 + 2*8 + 3*9, 4*7 + 5*8 + 6*9] == [50, 122]
t1 = torch.randn((2, 3))
t3 = torch.randn((3, 4))
print(torch.mm(t1, t3))
"""
tensor([[ 0.4608, -0.1280, -0.2437, 0.7501],
[-2.7646, -0.8995, -0.4848, -1.5221]])
"""
Tensor
可以使用符合 Python 语法的丰富的索引和切片功能。
使用 [ ]
运算符对 Tensor
索引 和 切片,访问形式:
tensor[index]
: 降维访问,第1维被移除(返回 index 位置的其他所有维度),如果原张量形状为 (n, m, q)
则返回张量的形状为 (m, q)
。tensor[start:end]
:切片访问,维度不变,其中第1维只返回 [start, end)
索引位置表示的范围。tensor[start:]
:切片访问,维度不变,end
省略表示到最后。tensor[:end]
:切片访问,维度不变,start
省略表示从 0 开始。tensor[:]
:切片访问,维度不变,start
和 end
都省略表示从 0 开始到最后。tensor[index, start:end]
:第1维降维, 第2维切片,如果原张量形状为 (n, m, q)
则返回张量的形状为 (m, q)
。其中索引位置可以用负数来表示,
-1
表示最后索引位置,-2
表示倒数第2个索引位置。索引会降维(只在指定的一个索引位置访问其他维度),切片不降维(访问的是一个范围,即多个索引位置的组合)。张量的每一个维度都可以在
[ ]
中括号中依序表示为索引或切片,维度之间用逗号,
分隔,没有在[ ]
括号中表示出来的维度相当于:
。如果张量的所有维度都被索引(降维到了 0 维),返回的就是一个标量。
索引和切片的形状变化示例:
# 一个形状为 (a, b, c, d) 的 4 维张量
tensor = torch.rand((a, b, c, d))
# 第1维降维(变为0维), 结果形状为 (b, c, d)
tensor[index]
# 相当于 tensor[index, start:end, :, :], 结果形状为 (end-start, c, d), 也相当于 tensor[index][start:end]
tensor[index, start:end]
# 结果形状为 (e1-s1, e2, c-s3, d)
tensor[s1:e1, :e2, s3:, :]
# 前面 3 维都被降为 0, 结果形状为 (d,)
tensor[i1, i2, i3, :]
# 第 2 维被降为 0, 结果形状为 (a, c, d)
tensor[:, i2, :, :]
# 所有维度都被降为 0, 结果返回的是一个标量
tensor[i1, i2, i3, i4]
索引和切片后的形状,代码示例:
import torch
t = torch.randint(0, 100, (3, 4, 5))
print(t.shape) # 原张量形状, 输出: torch.Size([3, 4, 5])
print(t[0][1][2]) # 所有维度被索引(所有降维被降为0维), 输出索引位置的标量: tensor(81)
print(t[0, :, :].shape) # 第1维降为0维, 输出: torch.Size([4, 5])
print(t[0].shape) # 相当于 t[0, :, :].shape, 输出: torch.Size([4, 5])
print(t[0][0, :].shape) # 相当于 t[0, 0, :].shape, 输出: torch.Size([5])
print(t[:, 0, 0:2].shape) # 第1维输出前部范围, 第2位索引0位置(维度被降维0), 第3维输出[0,2)范围, 输出: torch.Size([3, 2])
索引和切片读写值,代码示例:
import torch
t = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(t)
"""
tensor([[1, 2, 3],
[4, 5, 6]])
"""
print(t[0, 0]) # 所有维度都被降到0维, 输出索引位置的标量, 相当于 t[0][0]
"""
tensor(1)
"""
t1 = t[0] # 第1位降到0维, 第2维切片取全部, 相当于 t[0, :]
print(t1)
"""
tensor([1, 2, 3])
"""
print(t1[0]) # t1 只有1维, 索引后被降为0维, 输出索引位置的标量
"""
tensor(1)
"""
t1[0] = 100 # 对索引/切片位置赋值
print(t) # 切片返回的是原张量的视图, 所以对切片赋值是作用在原张量
"""
tensor([[100, 2, 3],
[ 4, 5, 6]])
"""
t[0] = torch.tensor([10, 20, 30]) # 可以把形状相同的张量赋值给切片(张量), 会在对应位置分别复制
print(t)
"""
tensor([[10, 20, 30],
[ 4, 5, 6]])
"""
t[1] = 123 # 如果把一个标量赋值给切片(张量), 则会把张量赋值到切片(张量)的每一个元素 (广播机制)
print(t)
"""
tensor([[ 10, 20, 30],
[123, 123, 123]])
"""
在 torch 中,标量也是以
Tensor
(张量)的形式表示,只不过该张量的维度是 0。可以通过torch.tensor()
把普通的 Python 标量转换为 torch 标量,例如torch.tensor(100)
。torch 中的Tensor
标量,可以当做是普通 Python 标量直接使用(用作各种数学运算)。
通过连接函数可以把多个张量连接成一个,通过拆分函数可以把一张张量拆分为多个张量。
Tensor
连接与拆分常用函数:
torch.cat(tensors, dim=0, *, out=None) -> Tensor
"""
在给定维度中连接给定的张量序列。所有张量除了连接所在维度外, 其他维度的大小必须相等。
连接后的形状: 连接所在维度的大小为各张量对应维度大小之和, 其他维度保持不变。
例如 形状为 (a, b1, c) 和 (a, b2, c) 的张量在第2维度上连接, 结果张量形状为 (a, b1+b2, c)。
torch.cat() 可以看作是 torch.split() 和 torch.chunk() 的逆运算。
"""
torch.stack(tensors, dim=0, *, out=None) -> Tensor
"""
沿新的维度连接一序列张量 (堆叠张量), 所有张量的形状大小必须相同。
和 cat() 不同, stack() 连接后会增加一个维度(新增的连接维度)。
例如 3 个形状为 (a, b, c) 的张量,
如果在 dim=1 的维度上 stack, 结果张量形状为 (a, 3, b, c);
如果在 dim=3 的维度上 stack, 结果张量形状为 (a, b, c, 3)。
其中 3 表示参与 stack 的张量的数量。
因为 stack 会新增加维度, 所以连接维度 dim 可以比原张量维度数大1。
"""
torch.chunk(input, chunks, dim=0) -> Seq[Tensor]
"""
尝试将张量在 dim 维度上拆分为 chunks 块, 每一块都是原张量的视图。
如果所在维度的大小不能别 chunks 整除, 拆分出来的块数可能不是 chunks。
如果需要准确返回指定数量的块数, 可以使用 torch.tensor_split()。
"""
torch.split(tensor, split_size_or_sections, dim=0) -> Seq[Tensor]
"""
在给定维度上将张量拆分成块, 每一块都是原张量的视图。
split_size_or_sections 是 整数 或 整数列表, 整数表示被拆分成大小相等的块(如果可能), 列表表示拆分后每块的大小。
"""
Tensor
连接代码示例:torch.cat()
import torch
t = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(t.shape) # torch.Size([2, 3])
print(t)
"""
tensor([[1, 2, 3],
[4, 5, 6]])
"""
# 张量连接, 在第1维上连接, 连接后第1维的大小为原各张量第1维大小相加, 即 t.shape[0] + t.shape[0] + t.shape[0]
# 参与连接的张量除了连接所在维度外, 其他维度大小必须相同不变
cat_t1 = torch.cat((t, t, t), dim=0)
print(cat_t1.shape) # torch.Size([6, 3])
print(cat_t1)
"""
tensor([[1, 2, 3],
[4, 5, 6],
[1, 2, 3],
[4, 5, 6],
[1, 2, 3],
[4, 5, 6]])
"""
# 张量连接, 在第2维上连接, 连接后第2维的大小变为 t.shape[1] + t.shape[1] + t.shape[1]
cat_t2 = torch.cat((t, t, t), dim=1)
print(cat_t2.shape) # torch.Size([2, 9])
print(cat_t2)
"""
tensor([[1, 2, 3, 1, 2, 3, 1, 2, 3],
[4, 5, 6, 4, 5, 6, 4, 5, 6]])
"""
t3 = torch.tensor([[7, 8, 9]])
print(t.shape) # torch.Size([2, 3])
print(t3.shape) # torch.Size([1, 3])
cat_t3 = torch.cat((t, t3), dim=0)
print(cat_t3.shape) # torch.Size([3, 3])
print(cat_t3)
"""
tensor([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
"""
Tensor
(堆叠)连接代码示例:torch.stack()
import torch
t = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(t.shape) # torch.Size([2, 3])
print(t)
"""
tensor([[1, 2, 3],
[4, 5, 6]])
"""
stack_t = torch.stack((t, t, t, t), dim=0)
print(stack_t.shape) # torch.Size([4, 2, 3])
print(stack_t)
"""
tensor([[[1, 2, 3],
[4, 5, 6]],
[[1, 2, 3],
[4, 5, 6]],
[[1, 2, 3],
[4, 5, 6]],
[[1, 2, 3],
[4, 5, 6]]])
"""
Tensor
拆分代码示例:torch.chunk()
import torch
t = torch.tensor([[1, 2, 3, 4], [5, 6, 7, 8]])
print(t.shape) # torch.Size([2, 5])
print(t)
"""
tensor([[1, 2, 3, 4],
[5, 6, 7, 8]])
"""
for x in torch.chunk(t, chunks=2, dim=0):
print(x)
"""
tensor([[1, 2, 3, 4]])
tensor([[5, 6, 7, 8]])
"""
for x in torch.chunk(t, chunks=2, dim=1):
print(x)
"""
tensor([[1, 2],
[5, 6]])
tensor([[3, 4],
[7, 8]])
"""
Tensor
拆分代码示例:torch.split()
import torch
t = torch.tensor([[1, 2, 3, 4], [5, 6, 7, 8]])
print(t.shape) # torch.Size([2, 5])
print(t)
"""
tensor([[1, 2, 3, 4],
[5, 6, 7, 8]])
"""
for x in torch.split(t, 2, dim=1):
print(x)
"""
tensor([[1, 2],
[5, 6]])
tensor([[3, 4],
[7, 8]])
"""
for x in torch.split(t, [1, 2, 1], dim=1):
print(x)
"""
tensor([[1],
[5]])
tensor([[2, 3],
[6, 7]])
tensor([[4],
[8]])
"""
Tensor
重塑(变形)常用函数:
torch.reshape(input, shape) -> Tensor
"""
返回具有相同数据和数量的指定形状的张量。如果可能, 返回的张量将是视图, 否则是一个副本。
如果需要确定返回的是视图, 可以使用 Tensor.view() 方法。
单个维度可以是 -1, 表示它是从剩余维度中的元素数量推断出来的。
Tensor.reshape(*shape) 等效方法。
"""
Tensor.view(*shape) -> Tensor
"""
返回张量 self 的指定形状的视图。
"""
Tensor
重塑(变形)代码示例:
import torch
t = torch.tensor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
print(t)
"""
tensor([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
"""
print(t.reshape(3, 4)) # 形状可以以 *shape 的方式传入, 也可以以元祖的方式传入
"""
tensor([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
"""
print(t.view(-1, 3)) # -1 表示根据剩余维度和数量推导, 只能有一个维度用 -1 表示
"""
tensor([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]])
"""
在深度学习的训练过程中,需要经过大量的张量计算。使用 CPU 计算一般会比较耗时,可以使用 GPU 进行加速计算。
要使用 GPU (CUDA) 加速计算,需要安装支持 GPU (CUDA) 版本的 PyTorch,并且在计算之前判断当前计算机的显卡是否支持 CUDA:
>>> import torch
>>> torch.cuda.is_available()
True
如果有支持 CUDA 的 NVIDIA 显卡,但返回 False,有可能是没有正确安装显卡驱动、CUDA 等相关软件。
下面通过随机生成 2 个 10000x10000 的矩阵,分别在 CPU 和 GPU 上做矩阵乘法运算,对比两者的耗时:
import torch
import time
x = torch.rand((10000, 10000))
y = torch.rand((10000, 10000))
start = time.perf_counter()
xy = torch.mm(x, y)
cpu_use_time = time.perf_counter() - start
print(f"CPU calc use time: {cpu_use_time:.6f} sec")
if torch.cuda.is_available():
x = x.cuda()
y = y.cuda()
start = time.perf_counter()
xy = torch.mm(x, y)
gpu_use_time = time.perf_counter() - start
print(f"GPU calc use time: {gpu_use_time:.6f} sec")
print(f"CPU / GPU: {cpu_use_time / gpu_use_time:.2f}")
else:
print("not support GPU")
在 GPU 上计算,一般会比 CPU 快很多,可能会快十倍、百倍、甚至千倍以上,具体看显卡性能。
要使用 GPU (CUDA) 计算,需要先调用 Tensor.cuda()
方法把 CPU 张量转存到 GPU 上,或者在创建张量时直接指定计算设备(device="cuda"
)。如果一个 CUDA 类型的张量需要转换回 CPU 张量,可以调用 Tensor.cpu()
方法。
一般大多数情况,GPU 加速计算使用的显卡都是支持 CUDA 的 NVIDIA 显卡。除了 CUDA,还有其他 GPU 计算平台,如:ROCm
、MPS
等,具体看最新 PyTorch 的支持情况。
最新版 PyTorch 已支持 Mac M系列芯片的 GPU (MPS),下面在我的 Mac 笔记本上(MacBook Pro,Apple M1 Pro 芯片组,16GB内存)测试 CPU 与 GUP (MPS) 的计算耗时对比:
import torch
import time
# 在 CPU 设备上创建矩阵
x = torch.rand((10000, 10000), device="cpu")
y = torch.rand((10000, 10000), device="cpu")
print(x.dtype, x.device)
start = time.perf_counter()
torch.mm(x, y)
cpu_use_time = time.perf_counter() - start
print(f"cpu calc use time: {cpu_use_time:.6f} sec\n")
# 在 MPS 设备上创建矩阵
x = torch.rand((10000, 10000), device="mps")
y = torch.rand((10000, 10000), device="mps")
print(x.dtype, x.device)
start = time.perf_counter()
torch.mm(x, y)
gpu_use_time = time.perf_counter() - start
print(f"gpu calc use time: {gpu_use_time:.6f} sec\n")
print(f"cpu / gpu: {cpu_use_time / gpu_use_time:.2f}")
# PS: 没有 tensor.mps() 转换方法, 创建 MPS 设备上的 Tensor, 需要在创建时指定 device="mps"
输出:
torch.float32 cpu
cpu calc use time: 1.071757 sec
torch.float32 mps:0
gpu calc use time: 0.004266 sec
cpu / gpu: 251.25
可以看出,GPU (MPS) 的计算时间比 CPU 计算时间快了 250 多倍(当计算量增大的时候,差距会更加大)。
Autograd 也叫 自动梯度(自动微分),也就是在计算过程中通过反向传播自动计算 Tensor 的梯度。Autograd 是 PyTorch 进行神经网络优化的核心。
Autograd 相关文档:AUTOMATIC DIFFERENTIATION PACKAGE - TORCH.AUTOGRAD
import torch
# 需要自动计算梯度的 Tensor, requires_grad 属性设置为 True
x = torch.tensor([1, 2], dtype=torch.float32, requires_grad=True)
print("x:", x) # x: tensor([1., 2.], requires_grad=True)
print(x.grad) # None
# 前向传播经过一序列计算得到输出y
z = x * 4
print("z:", z) # z: tensor([4., 8.], grad_fn=<MulBackward0>)
y = z.norm()
print("y:", y) # y: tensor(8.9443, grad_fn=<LinalgVectorNormBackward0>)
# y 反向传播计算 x 的梯度
y.backward()
print(x.grad) # tensor([1.7889, 3.5777])
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。