torch 的 tensor类
torch的tensor类
导包
import torch
或者
from torch import tensor
构造函数(创建tensor)
(function) def tensor(
data: Any,
dtype: _dtype | None = None,
device: _device | str | None = None,
requires_grad: _bool = False
) -> Tensor
# 例如
# tensor([1,2])
# tensor([[1,2],[1,2]])
# x = np.array([1,2])
# tensor(x)
创建tensor
- arange
def arange(
start: Number,
end: Number,
step: Number,
*,
out: Tensor | None = None,
dtype: _dtype | None = None,
device: _device | str | None = None,
requires_grad: _bool = False
) -> Tensor: ...
# 返回一个1维张量,长度为 floor((end−start)/step)
# 。包含从start到end,以step为步长的一组序列值(默认步长为1)。
# 参数:
# 位置参数
# start (Number):
# 起始值(包含在结果中)。可以是整数或浮点数。
# end (Number):
# 终止值(不包含在结果中)。生成的张量中的值会小于 end(如果 step > 0)或大于 end(如果 step < 0)。
# step (Number):
# 步长,默认为 1。表示相邻两个元素之间的差值。
# 关键字参数
# out (Tensor | None, 可选):
# 输出张量。如果提供,则将结果写入此张量中。
# dtype (_dtype | None, 可选):
# 指定返回张量的数据类型(如 torch.float32, torch.int64 等)。如果不指定,PyTorch 会根据 start, end, 和 step 的类型推断数据类型。
# device (_device | str | None, 可选):
# 指定返回张量所在的设备(如 'cpu' 或 'cuda:0')。如果不指定,默认使用当前设备。
# requires_grad (_bool, 可选):
# 如果设置为 True,则返回的张量将被标记为需要梯度计算。默认为 False。
- zero / ones
def ones(
size: _size,
*,
out: Tensor | None = None,
dtype: _dtype = None,
layout: _layout | None = strided,
device: _device | str | None = None,
pin_memory: _bool = False,
requires_grad: _bool = False
) -> Tensor: ...
3.其他的可见runoob链接
tensor属性
import torch
# 创建一个 2D 张量
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.float32)
# 张量的属性
print("Tensor:\n", tensor)
print("Shape:", tensor.shape) # 获取形状
print("Size:", tensor.size()) # 获取形状(另一种方法)
print("Data Type:", tensor.dtype) # 数据类型
print("Device:", tensor.device) # 设备
print("Dimensions:", tensor.dim()) # 维度数
print("Total Elements:", tensor.numel()) # 元素总数
print("Requires Grad:", tensor.requires_grad) # 是否启用梯度
print("Is CUDA:", tensor.is_cuda) # 是否在 GPU 上
print("Is Contiguous:", tensor.is_contiguous()) # 是否连续存储
# 获取单元素值
single_value = torch.tensor(42)
print("Single Element Value:", single_value.item())
# 转置张量
tensor_T = tensor.T
print("Transposed Tensor:\n", tensor_T)
广播机制
广播(Broadcasting)是 NumPy 和 PyTorch 等库中用于执行不同形状的数组或张量之间的算术运算的一种机制。当两个数组或张量的形状不完全相同时,广播机制允许它们在特定条件下进行操作,而不需要显式地复制数据以使它们的形状一致。
广播规则
广播机制遵循一套严格的规则来决定两个张量是否可以一起操作,并如何扩展较小的张量以匹配较大的张量。以下是广播的基本规则:
- 从后向前比较维度大小:从最后一个维度开始逐个比较两个张量的维度大小。
- 相同尺寸或者其中一个为 1:如果两个张量在某个维度上的大小相同,或者其中一个张量在该维度上的大小为 1,则这两个张量在这个维度上是兼容的。否则,无法广播。
- 自动扩展:对于尺寸为 1 的维度,该维度会被隐式地重复使用以匹配另一个张量的对应维度。
- 形状对齐:最终结果的形状是由每个维度中的最大值决定的。
具体步骤
- 检查维度数量:如果两个张量的维度数量不同,则在形状较短的张量前面添加 1,直到两者的维度数量相同。
- 逐维度比较:从最后一位开始逐位比较维度大小。如果两者之一为 1 或者两者相等,则继续检查下一个维度;否则,广播失败。
- 确定输出形状:输出张量的形状是输入张量中每个维度的最大值。
import torch
# 形状 (3,)
tensor_a = torch.tensor([1, 2, 3])
# 形状 (3, 1)
tensor_b = torch.tensor([[1], [2], [3]])
result = tensor_a + tensor_b
print(result)
# 输出:
# tensor([[2, 3, 4],
# [3, 4, 5],
# [4, 5, 6]])
tensor_a 被视为 (1, 3),tensor_b 是 (3, 1)。
在第一个维度上,tensor_a 的大小为 1,tensor_b 的大小为 3,因此 tensor_a 沿第一个维度被广播。
在第二个维度上,tensor_b 的大小为 1,tensor_a 的大小为 3,因此 tensor_b 沿第二个维度被广播。
结果的形状是 (3, 3)。
张量操作
- mv, mm, bmm, matmul
## mv matrix * vector
A = torch.ones(3,4)
b = torch.ones(4)
A, b, torch.mv(A,b)
## mm matrix * matrix
A = torch.ones(3,4)
B = torch.ones(4,3)
A, B, torch.mm(A,B)
## bmm Batch mm
A = torch.ones(2,3,4)
B = torch.ones(2,4,3)
A, B, torch.bmm(A,B)
## matmul
A = torch.ones(2,3,4)
B = torch.ones(1,4,3)
A, B, torch.matmul(A,B), torch.matmul(A,B) == A@B
- 切片
### 普通切片
A = torch.ones(3,4)
A[0,0], A[:,0], A[0,:], A[:], A[0:2, 1:3]
### 传入bool数组切片
A = torch.ones(3,4)
B = torch.zeros_like(A)
B[:,0] = 1
print(A)
print(B)
B = B>0
print(A)
print(B)
A[B] = 2
print(A)
print(B)
节省内存/原地操作/深浅拷贝
运行一些操作可能会导致为新结果分配内存。
例如,如果我们用Y = X + Y,我们将取消引用Y指向的张量,而是指向新分配的内存处的张量。
这可能是不可取的,原因有两个:
- 首先,我们不想总是不必要地分配内存。在机器学习中,我们可能有数百兆的参数,并且在一秒内多次更新所有参数。通常情况下,我们希望原地执行这些更新;
- 如果我们不原地更新,其他引用仍然会指向旧的内存位置,这样我们的某些代码可能会无意中引用旧的参数。
幸运的是,(执行原地操作)非常简单。
我们可以使用切片表示法将操作的结果分配给先前分配的数组,例如Y[:] = <expression>。
如果在后续计算中没有重复使用X,
我们也可以使用X[:] = X + Y或X += Y来减少操作的内存开销。
## 浅拷贝 改变B也改变A
A = torch.ones(3,4)
B = A
B+=1
B, A
## 深拷贝 复制一次内存,改变B不改变A
A = torch.ones(3,4)
B = A.clone()
B+=1
B, A
## 指向新地址,非原地操作
B = torch.ones(3,4)
ori_id = id(B)
A = torch.zeros_like(B)
B = A+B
ori_id == id(B)
## 原地操作
B = torch.ones(3,4)
ori_id = id(B)
A = torch.zeros_like(B)
B[:] = A+B
ori_id == id(B)
L2和 L1范数
## l2
torch.norm(A)
## L1
torch.abs(A).sum()
求和/平均/降维
### 求和
(method)
def sum(
*,
dtype: _dtype | None = None
) -> Tensor: ...
def sum(
dim: _size | _int,
keepdim: _bool = False,
*,
dtype: _dtype | None = None
) -> Tensor: ...
def sum(
dim: Sequence[str | ellipsis | None],
keepdim: _bool = False,
*,
dtype: _dtype | None = None
) -> Tensor: ...
A = torch.ones(2,3,4)
A.sum(), A.sum(0,True), A.sum([0,1])
###平均
A = torch.ones(2,3,4)
A.mean(), A.mean(0) , A.mean(0, True), A.mean([0,1])
###平均的另一种实现
A = torch.ones(3,4)
A.mean(), A.sum() / A.numel()
A.mean(0), A.sum(0)/ A.shape[0]
转换成numpy
import torch
import numpy as np
# 1. NumPy 数组转换为 PyTorch 张量
print("1. NumPy 转为 PyTorch 张量")
numpy_array = np.array([[1, 2, 3], [4, 5, 6]])
print("NumPy 数组:\n", numpy_array)
# 使用 torch.from_numpy() 将 NumPy 数组转换为张量
tensor_from_numpy = torch.from_numpy(numpy_array)
print("转换后的 PyTorch 张量:\n", tensor_from_numpy)
# 修改 NumPy 数组,观察张量的变化(共享内存)
numpy_array[0, 0] = 100
print("修改后的 NumPy 数组:\n", numpy_array)
print("PyTorch 张量也会同步变化:\n", tensor_from_numpy)
# 2. PyTorch 张量转换为 NumPy 数组
print("\n2. PyTorch 张量转为 NumPy 数组")
tensor = torch.tensor([[7, 8, 9], [10, 11, 12]], dtype=torch.float32)
print("PyTorch 张量:\n", tensor)
# 使用 tensor.numpy() 将张量转换为 NumPy 数组
numpy_from_tensor = tensor.numpy()
print("转换后的 NumPy 数组:\n", numpy_from_tensor)
# 修改张量,观察 NumPy 数组的变化(共享内存)
tensor[0, 0] = 77
print("修改后的 PyTorch 张量:\n", tensor)
print("NumPy 数组也会同步变化:\n", numpy_from_tensor)
# 3. 注意:不共享内存的情况(需要复制数据)
print("\n3. 使用 clone() 保证独立数据")
tensor_independent = torch.tensor([[13, 14, 15], [16, 17, 18]], dtype=torch.float32)
numpy_independent = tensor_independent.clone().numpy() # 使用 clone 复制数据
print("原始张量:\n", tensor_independent)
tensor_independent[0, 0] = 0 # 修改张量数据
print("修改后的张量:\n", tensor_independent)
print("NumPy 数组(不会同步变化):\n", numpy_independent)

浙公网安备 33010602011771号