赞
踩
本博客主要作为个人学习使用,记录Pytorch与计算机视觉学习过程当中遇到的一些有用的代码技巧,以及一些函数的效果对比,方便查阅与交流,有关的问题欢迎大家在评论区进行讨论,也欢迎大家补充自己觉得好用的代码技巧。
注:目前初步是对遇到的代码技巧进行归总,到一定数量以后再进行分类整理。每个小节的有超链接可直接跳转函数对应的Pytorch官方文档。
torch.nonzero能够提取Tensor当中非零元素的标号(indices),常用于对Tensor中满足一定逻辑关系的元素进行特征提取,即:
import torch
target_indices = torch.nonzero(<logic expression>)
例如,5个锚框(anchor box)各自与真实边界框(groundtruth bounding box)的最大交并比(IoU)为:
max_iou = torch.tensor([0.0536,0.1417,0.5657,0.2059,0.7459])
则达到IoU阈值的锚框序号可由以下方式求得,reshape
用于将结果调整为一维张量。
iou_threshold = 0.5
anc_i = torch.nonzero(max_ious > iou_threshold).reshape(-1)
print(anc_i)
tensor([2,4])
torch.unique能够删除输入的Tensor中重复的元素并将结果输出,输出的Tensor(记为output
)中每个元素都与其他元素互异。通过设定sorted=True
可以得到经过排序后的结果,通过设定return_counts=True
,可以一并返回output
中每个元素在输入Tensor中出现的次数。
torch.unique可以用于集合中子集及其补集的分离。例如,一组锚框对应的标号为:
import torch
all_idx = torch.tensor([0, 1, 2, 3])
假设经过非极大值抑制(NMS)后,保留下来的前景锚框对应标号为:
keep = torch.tensor([0, 3])
那么剩余的被排除掉的锚框对应的标号non_keep
可以通过以下方式求出:
combined = torch.cat((keep, all_idx))
uniques, counts = combined.unique(return_counts=True)
non_keep = uniques[counts == 1]
print(non_keep)
tensor([1, 2])
torch.argsort可以将输入Tensor中的元素以指定次序进行排列,并返回排序结果中每个值对应的标号。与torch.nonzero配合使用,可以在保持大小顺序的情况下进行标号的多次提取。例如,假定有四个边界框(bounding box),它们的置信度分别为:
import torch
scores = tensor([0.9000, 0.8000,0.7000, 0.9000])
则利用torch.argsort排序,就可以得到:
B = torch.argsort(scores, dim=-1, descending=True)
print(B)
tensor([0, 3, 1, 2])
在B
中,各个标号按scores
中对应值的大小降序排列,因此B[0]
就是置信度最高的边界框的标号,假设它与剩余的边界框交并比为:
ious = tensor([0.0000,0.7368,0.5454])
假定IoU阈值为:
iou_threshold = 0.7
经过非极大值抑制,剩余的边界框对应标号为:
inds = torch.nonzero(iou <= iou_threshold).reshape(-1)
print(inds)
tensor([0, 2])
B = B[inds + 1]
print(B)
tensor([3, 2])
从而下一次迭代中需要做非极大值抑制的边界框对应的标号就是:
注:每个小节的有超链接可直接跳转函数对应的Pytorch官方文档,本部分与第一部分不同,后续可能不会另作整理。
torch.cat与torch.stack都起到连接Tensor的作用,输入参数的形式也相同,那么它们有什么区别呢?两者在官方文档中的解释如下:
torch.cat: Concatenates the given sequence of seq tensors in the given dimension.
torch.stack: Concatenates a sequence of tensors along a new dimension.
可见前者是在已有的维度上级联Tensor,dim从已有的维度中指定一个;而后者在新的维度上对Tensor进行级联,dim指定的是维度与维度间的间隙,因此同样的输入实际上会产生不同的结果,例如:
import torch
x = torch.randn(2, 3)
y_cat = torch.cat((x, x, x), 0)
y_stack = torch.stack((x, x, x), 0)
print('Shape of y_cat: ', y_cat.shape)
print('Shape of y_stack: ', y_stack.shape)
Shape of y_cat: torch.Size([6, 3])
Shape of y_stack: torch.Size([3, 2, 3])
本部分内容参考来自RYDER的知乎回答。
torch.mm(input, mat2, *, out=None) → Tensor
torch.mm实现矩阵乘法,一般只用来计算两个二维张量的矩阵乘法,且不支持broadcast操作。假设input
的大小为
n
×
m
n \times m
n×m,mat2
的大小为
m
×
p
m \times p
m×p,则输出张量的大小为
n
×
p
n \times p
n×p。
torch.bmm(input, mat2, *, out=None) → Tensor
torch.bmm实现批量化的矩阵乘法,便于深度网络mini-batch的训练方法。该函数的两个输入必须是三维矩阵且第一维相同(表示Batch维度),且不支持broadcast操作。假设input
的大小为
B
×
n
×
m
B \times n \times m
B×n×m,mat2
的大小为
B
×
m
×
p
B \times m \times p
B×m×p,则输出张量的大小为
B
×
n
×
p
B \times n \times p
B×n×p。
torch.matmul(input, other, *, out=None) → Tensor
torch.matmul同样实现矩阵乘法,同时支持高维输入和broadcast操作,但使用较为复杂,建议参考Pytorch官方文档。
torch.mul(input, other, *, out=None) → Tensor
torch.mul实现逐元素乘法,其中other
可以是标量也可以是任意维度的矩阵,只要满足broadcast以后能够相乘即可,即该操作支持broadcast。
@ @ @操作符可以执行矩阵乘法操作,类似 torch.mm、torch.bmm及torch.matmul; 而 ∗ * ∗ 乘法操作可以执行逐元素矩阵乘法,使用方法类似torch.mul。
torch.clone(input, *, memory_format=torch.preserve_format) → Tensor
torch.clone开辟新的空间存储复制后的张量,因此源张量和复制后的张量互不影响,属于深拷贝(Deep Copy)。此外,复制结果的shape、dtype和device属性均与原张量相同,且仍然保留在计算图中,若源张量的require_grad=True
,则复制结果在运算过程产生的梯度会回溯到源张量,因此torch.clone
可以被理解为一种identity mapping
的操作。
其他的深拷贝方法(参考链接)还包括:
Tensor.detach()
返回与源张量完全相同的张量,但新的张量与源张量共享内存,任何一方的改变也会相应地同步在另一方上,属于浅拷贝(Shallow Copy)。此外,与torch.copy()
不同,detach()
的结果将脱离计算图中,不再参与梯度的计算。
其他的浅拷贝方法(参考链接)还包括:
综合前述,tensor.copy().detach()
将使得复制结果与源张量完全分离,既不共享内存,也不再存有计算图上的关联,且结果与两个方法的顺序无关。
Tensor.new_tensor能够在深拷贝的同时提供更细致的dtype和device属性的控制。在默认参数下,即tensor.new_tensor(x)
等同于x.copy().detach()
,tensor.new_tensor(x, requires_grad=True)
则等同于x.clone().detach().requires_grad_(True)
。
以上就是文章的全部内容,后续会对博客持续更新,敬请期待。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。