一、基本张量操作
torch.tensor()
torch.tensor() 会拷贝数据
torch.tensor(12)
torch.tensor([0,1,2,3])
torch.tensor([[1,2,3,4],
[5,6,7,8],
[9,10,11,12]])
# --------------------------
np_array = np.array([1, 2, 3])
tensor = torch.tensor(np_array)
tensor[0] = 999
print(np_array) # [1 2 3]
torch.from_numpy()
NumPy Array 转化成 Torch Tensor
内存共享,零拷贝,只能用于CPU
np_array = np.array([1, 2, 3])
tensor = torch.from_numpy(np_array)
tensor[0] = 999
print(np_array) # [999 2 3]
print(tensor) # tensor([999, 2, 3])
torch.as_tensor()
from_numpy = 强制共享(零拷贝)
as_tensor = 尽量共享,dtype 不同就拷贝,不共享内存了
as_tensor + GPU = 一定拷贝
"""
情况一,零拷贝,双向同步
"""
np_array = np.array([1, 2, 3])
tensor = torch.as_tensor(np_array) # 行为和 from_numpy() 一样
tensor[0] = 999
print(np_array) # [999 2 3]
print(tensor) # tensor([999, 2, 3], dtype=torch.int32)
"""
情况二
"""
np_array = np.array([1, 2, 3], dtype=np.int32)
tensor = torch.as_tensor(np_array, dtype=torch.float32)
tensor[0] = 999
print(np_array) # [1 2 3],与numpy一样
print(tensor) # tensor([999., 2., 3.])
"""
情况三,NumPy 只能在 CPU,GPU Tensor 一定重新分配内存
"""
np_array = np.array([1, 2, 3])
tensor = torch.as_tensor(np_array, device="cuda")
tensor[0] = 999
print(np_array) # [1 2 3]
print(tensor) # tensor([999, 2, 3], device='cuda:0')
torch.zeros()
torch.zeros((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.]]])
torch.randn()
随机初始化参数的值,其中的每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样
torch.randn(3, 4)
tensor([[-0.0135, 0.0665, 0.0912, 0.3212],
[ 1.4653, 0.1843, -1.6995, -0.3036],
[ 1.7646, 1.0450, 0.2457, -0.7732]])
torch.arange()
tensor = torch.arange(12) # tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
tensor.shape()/reshape()
reshape() 尽量返回 view,但在非连续内存时会自动拷贝
tensor.shape()
tensor.reshape(-1,1) # -1 根据剩下的维度计算出数组的另外一个shape属性值
tensor.reshape(-1,2) # -1 表示自动计算维度,而 2 表示将数组调整为两列
tensor = tensor.reshape(3,4)
tensor.shape()
torch.Size([12])
tensor([[ 0],
[ 1],
[ 2],
[ 3],
[ 4],
[ 5],
[ 6],
[ 7],
[ 8],
[ 9],
[10],
[11]])
tensor([[ 0, 1],
[ 2, 3],
[ 4, 5],
[ 6, 7],
[ 8, 9],
[10, 11]])
tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
torch.Size([3,4])
tensor.numel()
张量中元素的总数,即形状的所有元素乘积
tensor.numel() # 12
tensor.item()
item()函数用于从只包含单个元素的张量中提取Python数值,将张量转换为标量值
import torch
# 创建一个只包含一个元素的张量
tensor = torch.tensor([3.14])
value = tensor.item()
print(value) # 3.140000104904175
二、重要的张量处理方法
torch.t(A)
用于计算矩阵的转置
A = torch.arange(12).reshape(3,4)
torch.t(A)
tensor([[ 0, 4, 8],
[ 1, 5, 9],
[ 2, 6, 10],
[ 3, 7, 11]])
torch.mm(A,B)
用于计算两个矩阵的乘积
A = torch.arange(12).reshape(3,4)
B = torch.arange(12).reshape(4,3)
torch.mm(A,B)
tensor([[ 42, 48, 54],
[114, 136, 158],
[186, 224, 262]])
torch.mv(A,x)
用于计算矩阵-向量积
A = torch.arange(9).reshape(3,3) # tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
x = torch.arange(3) #tensor([0, 1, 2])
torch.mv(A,x)
tensor([ 5, 14, 23])
torch.mul()
用于执行两个张量的元素级乘法
tensor1 = torch.tensor([[1, 2], [3, 4]])
tensor2 = torch.tensor([[5, 6], [7, 8]])
torch.mul(tensor1, tensor2)
tensor([[ 5, 12],
[21, 32]])
torch.sum()
用于计算张量元素的和
tensor = torch.tensor([[1, 2], [3, 4]])
torch.sum(tensor)
tensor(10)
torch.mean()
用于计算张量元素的平均值
tensor = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
torch.mean(tensor)
tensor(2.5000)
torch.norm(x)
计算向量L2L_2L2的范数
x = torch.tensor([1.0,2.0,3.0])
torch.norm(x)
tensor(3.7417)
torch.abs(x).sum()
计算向量L1L_1L1的范数
x = torch.tensor([1.0,2.0,3.0])
torch.abs(x).sum()
tensor(6.)
三、运算操作
torch.cat()
沿现有指定维度拼接张量
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.]]))
torch.stack()
在不改变原始张量内容的情况下,在特定维度上创建更高维度的张量。所有张量必须具有相同的大小。
应用场景:
在自然语言处理(NLP)和图像卷积神经网络(CV)中非常常见,因为它可以保留序列信息和张量矩阵信息。例如,在循环神经网络(RNN)中,输出数据通常是一个包含多个形状为 [batchsize,outputsize][batch_{size}, output_{size}][batchsize,outputsize] 的张量的列表。为了便于计算,需要使用 torch.stacktorch.stacktorch.stack 进行拼接,以保留时间步长和张量属性信息。
m = torch.tensor([[1, 2, 3], [4, 5, 6]])
n = torch.tensor([[7, 8, 9], [10, 11, 12]])
v = torch.tensor([[13, 14, 15], [16, 17, 18]])
torch.stack((m, n, v), dim=0)
tensor([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]],
[[13, 14, 15],
[16, 17, 18]]])
shape: (3,2,3)
torch.transpose(tensor,dim0,dim1) / tensor.transpose(dim0,dim1)
交换张量的两个维度
x = torch.arange(12).reshape(2,6)
torch.transpose(x,0,1).shape
torch.transpose(x,1,0).shape
x.transpose(0,1).shape
y = torch.arange(24).reshape(2,4,3)
torch.transpose(y,0,2).shape
torch.transpose(y,2,1).shape
y.transpose(0,1).shape
torch.Size([6, 2])
torch.Size([6, 2])
torch.Size([6, 2])
torch.Size([3, 4, 2])
torch.Size([2, 3, 4])
torch.Size([4, 2, 3])
tensor.permute(∗*∗dims)
用于重新排列张量的所有维度,可以一次操作多维数据,且必须传入所有维度数
y.permute(2,0,1)
y.permute(2,0,1).shape
tensor([[[ 0, 3, 6, 9],
[12, 15, 18, 21]],
[[ 1, 4, 7, 10],
[13, 16, 19, 22]],
[[ 2, 5, 8, 11],
[14, 17, 20, 23]]])
shape:(3,2,4)
tensor.squeeze()
用于移除张量中维度为 1 的维度的函数。默认会移除所有维度为 1 的维度,也可指定要移除的维度,但指定的维度大小必须为 1 。它可以简化张量的形状,常用于数据预处理或模型输入调整。
z = torch.arange(24).reshape(2,4,3,1)
z.squeeze().shape
z.squeeze()
z.squeeze(3).shape
torch.Size([2, 4, 3])
tensor([[[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]],
[[12, 13, 14],
[15, 16, 17],
[18, 19, 20],
[21, 22, 23]]])
torch.Size([2, 4, 3])
torch.unsqueeze(tensor, dim)
用于在指定的维度上为张量新增一个大小为 1 的维度。这对于调整张量形状以适应特定操作或模型需求非常有帮助
应用场景
计算机视觉:将单通道图像数据从 [H,W][H, W][H,W] 转换为 [1,1,H,W][1, 1, H, W][1,1,H,W] ,以适应卷积神经网络的输入格式。
自然语言处理:将一维文本序列数据从 [T][T][T] 转换为 [1,T,1][1, T, 1][1,T,1] ,以便进行批量处理或适应特定模型需求。
a = torch.tensor([[1, 2, 3], [4, 5, 6]])
torch.unsqueeze(a, 0).shape
torch.unsqueeze(a, -1).shape
torch.unsqueeze(a, 1).shape
torch.Size([1, 2, 3])
torch.Size([2, 3, 1])
torch.Size([2, 1, 3])
tensor.expand()
将一个张量(Tensor)的单个维度扩展到更大的尺寸。扩展张量不会分配新的内存,只是在存在的张量上创建一个新的视图(view),其中尺寸为 1 的维度被扩展到更大的尺寸,通过将步长(stride)设置为0来实现。
应用场景
expand函数常用于广播操作,例如在卷积神经网络(CNN)中全局平均池化(GAP)后的操作。它允许将具有较小尺寸的张量扩展到与另一个张量相匹配的尺寸,以便进行元素间的操作,如加法或乘法。
e = torch.tensor([[1], [2], [3]])
e.expand(3, 4)
e.expand(4, 1)
f = torch.Tensor([[[[1,2], [2,3], [3,4],[4,5]]]])
f.expand(2, 1, 4, 2)
f.expand(1, 2, 4, 2)
tensor([[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3]])
RuntimeError: The expanded size of the tensor (4) must match the existing size (3) at non-singleton dimension 0. Target sizes: [4, 1]. Tensor sizes: [3, 1]
tensor([[[[1., 2.],
[2., 3.],
[3., 4.],
[4., 5.]]],
[[[1., 2.],
[2., 3.],
[3., 4.],
[4., 5.]]]])
tensor([[[[1., 2.],
[2., 3.],
[3., 4.],
[4., 5.]],
[[1., 2.],
[2., 3.],
[3., 4.],
[4., 5.]]]])
torch.floor()
用于将张量元素向下取整,得到不超过每个元素的最大整数
tensor = torch.tensor([[1.2,2.8],[3.5,4.1]])
torch.floor(tensor)
tensor([[1., 2.],
[3., 4.]])
torch.ceil()
用于将张量元素向上取整,得到不小于每个元素的最小整数
tensor = torch.tensor([[1.2,2.8],[3.5,4.1]])
torch.ceil(tensor)
tensor([[2., 3.],
[4., 5.]])
四、数学和统计操作
torch.std()
用于计算张量元素的标准差
tensor = torch.tensor([1.0,2.0,4.0])
torch.std(tensor)
tensor(1.5275)
torch.max()
用于找到张量中的最大值及其索引
tensor = torch.arange(1,13).reshape(2,6)
torch.max(tensor)
torch.max(tensor,dim=1)
tensor(12)
torch.return_types.max(
values=tensor([ 6, 12]),
indices=tensor([5, 5]))
torch.min()
用于找到张量中的最小值及其索引
tensor = torch.arange(1,13).reshape(2,6)
torch.min(tensor)
torch.min(tensor,dim=1)
torch.return_types.min(
values=tensor([1, 7]),
indices=tensor([0, 0]))
torch.abs()
用于计算张量元素的绝对值
tensor = torch.tensor([[-1, 2], [-3, 4]])
torch.abs(tensor)
tensor([[1, 2],
[3, 4]])
torch.exp()
用于计算张量元素的指数
tensor = torch.tensor([[-1, 2], [-3, 4]])
torch.exp(tensor)
tensor([[3.6788e-01, 7.3891e+00],
[4.9787e-02, 5.4598e+01]])
torch.log()
用于计算张量元素的自然对数
tensor = torch.tensor([[1, 2], [3, 4]])
torch.log(tensor)
tensor([[0.0000, 0.6931],
[1.0986, 1.3863]])
torch.argmax()
用于返回输入张量(Tensor)中最大值的索引
import torch
tensor1 = torch.tensor([[0.1,0.2],
[0.3,0.4]])
tensor.argmax(1) # dim=1,在行里面找
tensor2 = torch.tensor([[0.1,0.2],
[0.05,0.4]])
tensor.argmax(0) # dim=0,在列里面找
tensor([1, 1])
tensor([0, 1])
五、重要的深度学习操作
1.随机性控制(可复现性)
torch.manual_seed()
设置 CPU 上随机数生成器的种子,使得随机操作(如 randn、初始化权重)结果可复现
torch.manual_seed(42)
torch.cuda.manual_seed_all()
设置所有 GPU 上的随机种子,多卡训练必须设置
torch.cuda.manual_seed_all(42)
2.设备迁移(CPU / GPU)
tensor.to(device)
将张量 / 模型迁移到指定设备,返回一个新 tensor(不是原地)
Tensor 永远“属于”某一个 device(CPU / GPU),运算双方必须在同一device
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
tensor = tensor.to(device)
model = model.to(device)
3.计算图相关操作
tensor.requires_grad_(True/False)
决定这个 Tensor 是否参与反向传播
参数默认 True,数据默认 False
x = torch.randn(3, requires_grad=True)
y = x * 2
x.requires_grad_(False)
y.backward() # 会报错,因为一旦 Tensor 已经参与计算图,修改 requires_grad 会破坏 Autograd 对计算图的假设
tensor.detach()
返回一个共享数据、但不共享计算图的新 tensor,不会影响原计算图,不计算梯度
y = x.detach()
detach()和clone()对比:
clone() : 拷贝数据 + 保留梯度历史
detach() : 共享数据 + 切断梯度
detach().clone() : 拷贝数据 + 切断梯度(最安全) → 实战中最常用的组合
torch.no_grad()
关闭梯度计算,推理 / 验证阶段必须用
with torch.no_grad():
output = model(x)
4.内存与拷贝
tensor.clone()
真正的深拷贝,打断“视图”关系
tensor.numpy()
tensor.numpy() # CPU tensor:共享内存
tensor.cpu().numpy()
CPU tensor:共享内存 GPU tensor:必须先 .cpu()
六、改变元数据的操作
在pytorch中,Tensor = 数据块(storage) + 元数据(shape / stride / offset)
只有很少几个操作是不改变tensor的内容本身,而只是重新定义下标与元素的对应关系的。换句话说,这种操作不进行数据拷贝和数据的改变,变的是元数据,即不动底层数据,只改:shape、stride、offset。
下面几个操作会改变元数据:
torch.contiguous()
方法用于确保张量的内存布局是连续的,下面view的例子中有提及
view()
改变shape,前提:内存必须是连续的
x = torch.arange(12) # x在内存中是连续的
y = x.view(3, 4) # view()只需要重新计算 stride,不需要拷贝数据
print(y)
print(y.is_contiguous())
tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
True
错误用法(非连续内存)
x = torch.arange(12).reshape(3, 4)
y = x.transpose(0, 1) # 改变 stride,不连续
print(y.is_contiguous())
z = y.view(6, 2)
False
RuntimeError: view size is not compatible with input tensor's size and stride
报错原因:transpose() 只改 stride,不拷贝数据,得到的 y 在物理内存中是“跳着走的”,view() 要求内存连续,stride 对不上,PyTorch 直接拒绝
正确修复方式
z = y.contiguous().view(6, 2) # contiguous():强制拷贝一份连续内存
transpose()
transpose()看起来变了,其实没拷贝,得到的是视图(view),不是新 tensor
x = torch.arange(6).reshape(2, 3)
y = x.transpose(0, 1)
print(x)
print(y)
print(x.storage().data_ptr() == y.storage().data_ptr())
tensor([[0, 1, 2],
[3, 4, 5]])
tensor([[0, 3],
[1, 4],
[2, 5]])
True
在使用transpose()进行转置操作时,pytorch并不会创建新的、转置后的tensor,而是修改了tensor中的一些属性(也就是元数据),使得此时的offset和stride是与转置tensor相对应的。转置的tensor和原tensor的内存是共享的!
narrow(input, dim, start, length)
和transpose类似,不拷贝,得到的是view,常用于大tensor的局部窗口。
返回一个经过裁剪的 input 张量的新张量。维度 dim 从 start 到 start + length。返回的张量和 input 张量共享相同的底层存储。
x = torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# y = torch.narrow(x, 0, 0, 2)
# y = torch.narrow(x, 1, 1, 2)
y = torch.narrow(x, -1, torch.tensor(-1), 1)
y[0] = 999
print(x)
print(y)
tensor([[1, 2, 3],
[4, 5, 6]])
tensor([[2, 3],
[5, 6],
[8, 9]])
tensor([[3],
[6],
[9]])
tensor([[ 1, 2, 999],
[ 4, 5, 6],
[ 7, 8, 9]])
tensor([[999],
[ 6],
[ 9]])
写操作会影响原tensor
expand()
没有拷贝,扩展维度的stride=0,适合做广播计算
x = torch.tensor([[1], [2], [3]])
x.expand(3, 4)
x.expand(-1, 5) # -1 means not changing the size of that dimension
tensor([[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3]])
tensor([[1, 1, 1, 1, 1],
[2, 2, 2, 2, 2],
[3, 3, 3, 3, 3]])
不能对 expand 后的 tensor 做写操作,因为expand 后的多个位置指向同一个物理内存,写一个 = 写多个,PyTorch 禁止这种歧义写操作
报错:
RuntimeError: a view of a leaf Variable that requires grad is being used in an in-place operation
在无梯度情况下:
RuntimeError: unsupported operation: more than one element of the written-to tensor refers to a single memory location
正确做法应该是先clone,再去对clone后的tensor做写操作
总结
🧠 PyTorch Tensor 操作的三条铁律
①是否拷贝数据,不看“函数名”,看 storage 是否共享
② view / transpose / narrow / expand → 改的是 元数据(shape / stride),不是数据
③ 只要出现以下任意一个,几乎一定会拷贝:
- dtype 改变
- device 改变
- contiguous()
- 明确的 clone()
2061

被折叠的 条评论
为什么被折叠?



