【Pytorch学习:对比与总结】

文章目录

  • 前言
  • 一、Pytorch技巧总结
    • 1. torch.nonzero:标号提取
    • 2. torch.unique:标号分离
    • 3. torch.argsort:标号排序
  • 二、Pytorch方法比较
    • 1. torch.cat与torch.stack
    • 2. 乘法运算
      • torch.mm
      • torch.bmm
      • torch.matmul
      • torch.mul
      • 乘法运算符 @ 与 *
    • 3. 张量复制
      • torch.clone
      • Tensor.detach
      • clone 与detach联合使用
      • Tensor.new_tensor
  • 总结


前言

本博客主要作为个人学习使用,记录Pytorch与计算机视觉学习过程当中遇到的一些有用的代码技巧,以及一些函数的效果对比,方便查阅与交流,有关的问题欢迎大家在评论区进行讨论,也欢迎大家补充自己觉得好用的代码技巧。


一、Pytorch技巧总结

注:目前初步是对遇到的代码技巧进行归总,到一定数量以后再进行分类整理。每个小节的有超链接可直接跳转函数对应的Pytorch官方文档。

1. torch.nonzero:标号提取

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.05360.14170.56570.20590.7459])

则达到IoU阈值的锚框序号可由以下方式求得,reshape用于将结果调整为一维张量。

iou_threshold = 0.5
anc_i = torch.nonzero(max_ious > iou_threshold).reshape(-1)
print(anc_i)
tensor([2,4])

2. torch.unique:标号分离

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])

3. torch.argsort:标号排序

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方法比较

注:每个小节的有超链接可直接跳转函数对应的Pytorch官方文档,本部分与第一部分不同,后续可能不会另作整理。

1. torch.cat与torch.stack

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])

2. 乘法运算

本部分内容参考来自RYDER的知乎回答。

torch.mm

torch.mm(input, mat2, *, out=None) → Tensor

torch.mm实现矩阵乘法,一般只用来计算两个二维张量的矩阵乘法,且不支持broadcast操作。假设input的大小为 n × m n \times m n×mmat2的大小为 m × p m \times p m×p,则输出张量的大小为 n × p n \times p n×p

torch.bmm

torch.bmm(input, mat2, *, out=None) → Tensor

torch.bmm实现批量化的矩阵乘法,便于深度网络mini-batch的训练方法。该函数的两个输入必须是三维矩阵且第一维相同(表示Batch维度),且不支持broadcast操作。假设input的大小为 B × n × m B \times n \times m B×n×mmat2的大小为 B × m × p B \times m \times p B×m×p,则输出张量的大小为 B × n × p B \times n \times p B×n×p

torch.matmul

torch.matmul(input, other, *, out=None) → Tensor

torch.matmul同样实现矩阵乘法,同时支持高维输入和broadcast操作,但使用较为复杂,建议参考Pytorch官方文档。

torch.mul

torch.mul(input, other, *, out=None) → Tensor

torch.mul实现逐元素乘法,其中other可以是标量也可以是任意维度的矩阵,只要满足broadcast以后能够相乘即可,即该操作支持broadcast。

乘法运算符 @ 与 *

@ @ @操作符可以执行矩阵乘法操作,类似 torch.mmtorch.bmmtorch.matmul; 而 ∗ * 乘法操作可以执行逐元素矩阵乘法,使用方法类似torch.mul

3. 张量复制

torch.clone

torch.clone(input, *, memory_format=torch.preserve_format) → Tensor

torch.clone开辟新的空间存储复制后的张量,因此源张量和复制后的张量互不影响,属于深拷贝(Deep Copy)。此外,复制结果的shape、dtype和device属性均与原张量相同,且仍然保留在计算图中,若源张量的require_grad=True,则复制结果在运算过程产生的梯度会回溯到源张量,因此torch.clone可以被理解为一种identity mapping的操作。

其他的深拷贝方法(参考链接)还包括:

  • torch.tensor
  • Tensor.new_tensor
  • Tensor.copy_

Tensor.detach

Tensor.detach()

返回与源张量完全相同的张量,但新的张量与源张量共享内存,任何一方的改变也会相应地同步在另一方上,属于浅拷贝(Shallow Copy)。此外,与torch.copy()不同,detach()的结果将脱离计算图中,不再参与梯度的计算。

其他的浅拷贝方法(参考链接)还包括:

  • Tensor.numpy
  • torch.from_numpy
  • torch.as_tensor
  • Tensor.view
  • 索引,如y = x[:]
  • model.forward()
  • 就地操作(in-place)

clone 与detach联合使用

综合前述,tensor.copy().detach()将使得复制结果与源张量完全分离,既不共享内存,也不再存有计算图上的关联,且结果与两个方法的顺序无关。

Tensor.new_tensor

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)

总结

以上就是文章的全部内容,后续会对博客持续更新,敬请期待。

你可能感兴趣的:(pytorch,学习,深度学习,计算机视觉)