tensor.nozero(), mask, [mask]

1. nozero()

https://pytorch.org/docs/stable/generated/torch.nonzero.html?highlight=nonzero#torch.nonzero

torch.nonzero(input, *, out=None, as_tuple=False) → LongTensor or tuple of LongTensors

torch.nonzero(..., as_tuple=False) (default) returns a 2-D tensor where each row is the index for a nonzero value.
torch.nonzero(..., as_tuple=False) (默认)返回一个2-D张量,其中每一行是一个非零值的索引。

tensor.nozero()默认返回一个2维的tensor, 里面是符合条件的索引.

举个例子:

import torch


x = torch.randint(low=2, high=3, size=[2, 3])
idx = x.nonzero()

print(f"x:\n{x}\n")
print(f"idx:\n{idx}\n")

print(f"x.size: {x.size()}")
print(f"idx.size: {idx.size()}")

结果:

x:
tensor([[2, 2, 2],
        [2, 2, 2]])

idx:
tensor([[0, 0],
        [0, 1],
        [0, 2],
        [1, 0],
        [1, 1],
        [1, 2]])

x.size: torch.Size([2, 3])
idx.size: torch.Size([6, 2])

可以看到, 这里的idx是用2-D tensor来表示x中符合条件元素的索引.

因为x有两个维度, 所以idx中的size()[1]也是2


如果输入是多维的tensor, 那么表示也只会用2-D的tensor. 例子如下:

import torch


x = torch.randint(low=2, high=3, size=[2, 3, 4])
idx = x.nonzero()

print(f"x:\n{x}\n")
print(f"idx:\n{idx}\n")

print(f"x.size: {x.size()}")
print(f"idx.size: {idx.size()}")

结果:

x:
tensor([[[2, 2, 2, 2],
         [2, 2, 2, 2],
         [2, 2, 2, 2]],

        [[2, 2, 2, 2],
         [2, 2, 2, 2],
         [2, 2, 2, 2]]])

idx:
tensor([[0, 0, 0],
        [0, 0, 1],
        [0, 0, 2],
        [0, 0, 3],
        [0, 1, 0],
        [0, 1, 1],
        [0, 1, 2],
        [0, 1, 3],
        [0, 2, 0],
        [0, 2, 1],
        [0, 2, 2],
        [0, 2, 3],
        [1, 0, 0],
        [1, 0, 1],
        [1, 0, 2],
        [1, 0, 3],
        [1, 1, 0],
        [1, 1, 1],
        [1, 1, 2],
        [1, 1, 3],
        [1, 2, 0],
        [1, 2, 1],
        [1, 2, 2],
        [1, 2, 3]])

x.size: torch.Size([2, 3, 4])
idx.size: torch.Size([24, 3])

因为x有3个维度, 所以idx中的size()[1]也是3

再举个例子:

import torch


x = torch.randint(low=2, high=3, size=[2, 3, 4, 2])
idx = x.nonzero()

print(f"x:\n{x}\n")
print(f"idx:\n{idx}\n")

print(f"x.size: {x.size()}")
print(f"idx.size: {idx.size()}")

结果:

x:
tensor([[[[2, 2],
          [2, 2],
          [2, 2],
          [2, 2]],

         [[2, 2],
          [2, 2],
          [2, 2],
          [2, 2]],

         [[2, 2],
          [2, 2],
          [2, 2],
          [2, 2]]],


        [[[2, 2],
          [2, 2],
          [2, 2],
          [2, 2]],

         [[2, 2],
          [2, 2],
          [2, 2],
          [2, 2]],

         [[2, 2],
          [2, 2],
          [2, 2],
          [2, 2]]]])

idx:
tensor([[0, 0, 0, 0],
        [0, 0, 0, 1],
        [0, 0, 1, 0],
        [0, 0, 1, 1],
        [0, 0, 2, 0],
        [0, 0, 2, 1],
        [0, 0, 3, 0],
        [0, 0, 3, 1],
        [0, 1, 0, 0],
        [0, 1, 0, 1],
        [0, 1, 1, 0],
        [0, 1, 1, 1],
        [0, 1, 2, 0],
        [0, 1, 2, 1],
        [0, 1, 3, 0],
        [0, 1, 3, 1],
        [0, 2, 0, 0],
        [0, 2, 0, 1],
        [0, 2, 1, 0],
        [0, 2, 1, 1],
        [0, 2, 2, 0],
        [0, 2, 2, 1],
        [0, 2, 3, 0],
        [0, 2, 3, 1],
        [1, 0, 0, 0],
        [1, 0, 0, 1],
        [1, 0, 1, 0],
        [1, 0, 1, 1],
        [1, 0, 2, 0],
        [1, 0, 2, 1],
        [1, 0, 3, 0],
        [1, 0, 3, 1],
        [1, 1, 0, 0],
        [1, 1, 0, 1],
        [1, 1, 1, 0],
        [1, 1, 1, 1],
        [1, 1, 2, 0],
        [1, 1, 2, 1],
        [1, 1, 3, 0],
        [1, 1, 3, 1],
        [1, 2, 0, 0],
        [1, 2, 0, 1],
        [1, 2, 1, 0],
        [1, 2, 1, 1],
        [1, 2, 2, 0],
        [1, 2, 2, 1],
        [1, 2, 3, 0],
        [1, 2, 3, 1]])

x.size: torch.Size([2, 3, 4, 2])
idx.size: torch.Size([48, 4])

因为x有4个维度, 所以idx中的size()[1]也是4

终于, 我们可以发现结论了:

  • tensor.nozero()默认返回的是一个2-Dtensor, 行表示非零元素的索引, 列的大小 = 输入tensor维度的数量

2. mask

在手撸YOLOv3时, 需要写mask, 一般是根据置信度是否大于阈值来判断是否为正样本:

  • ≥ thresh: 张样本
  • < thresh: 负样本

在YOLOv3中, 网络的前向推理结果一般为 [N, H, W, 3, 8], 其中:

  • N: batch size
  • H: height
  • W: weight
  • 3: 3种预测尺度
  • 8: conf + loc + cls = 1 + 4 + 3

为了能够取到置信度conf, 因此output[..., 0], 由此就可以得到mask:

output = net(input)  # [N, H, W, 3, 8]

mask = output[..., 0] > thresh  # [N, H, W, 3]

那么问题来了: 为什么得到的mask的size为[N, H, W, 3]呢?

我们讲一下mask这样的取法究竟是在做什么.


import torch


x = torch.randint(low=2, high=3, size=[2, 3])

mask = x[..., 0] > 1  # 最后一个维度中的第一个元素如果大于1, 则前面的维度返回True, 否则返回False

print(f"x:\n{x}\n")
print(f"x[..., 0]:\n{x[..., 0]}\n")
print(f"x[..., 0].size:\n{x[..., 0].size()}\n")
print(f"mask:\n{mask}\n")
print(f"mask.size:\n{mask.size()}\n")
x:
tensor([[2, 2, 2],
        [2, 2, 2]])

x[..., 0]:
tensor([2, 2])

x[..., 0].size:
torch.Size([2])

mask:
tensor([True, True])

mask.size:
torch.Size([2])

或许这样看还不够明显, 那么我们将x的值修改一下:

import torch


x = torch.tensor(data=[[0, 2, 2],
                       [0, 2, 2]], dtype=torch.int8)

mask = x[..., 0] > 1  # 最后一个维度中的第一个元素如果大于1, 则前面的维度返回True, 否则返回False

print(f"x:\n{x}\n")
print(f"x[..., 0]:\n{x[..., 0]}\n")
print(f"x[..., 0].size:\n{x[..., 0].size()}\n")
print(f"mask:\n{mask}\n")
print(f"mask.size:\n{mask.size()}\n")

结果:

x:
tensor([[0, 2, 2],
        [0, 2, 2]], dtype=torch.int8)

x[..., 0]:
tensor([0, 0], dtype=torch.int8)

x[..., 0].size:
torch.Size([2])

mask:
tensor([False, False])

mask.size:
torch.Size([2])

看起来, 这个函数取的是第一列(这里的描述并不准确). 那么我们在将x的维度扩充一下看看:

import torch


x = torch.randint(low=0, high=3, size=[2, 3, 2])

mask = x[..., 0] > 1  # 最后一个维度中的第一个元素如果大于1, 则前面的维度返回True, 否则返回False

print(f"x:\n{x}\n")
print(f"x[..., 0]:\n{x[..., 0]}\n")
print(f"x[..., 0].size:\n{x[..., 0].size()}\n")
print(f"mask:\n{mask}\n")
print(f"mask.size:\n{mask.size()}\n")

结果:

x:
tensor([[[2, 1],
         [2, 1],
         [1, 0]],

        [[2, 0],
         [1, 2],
         [0, 0]]])

x[..., 0]:
tensor([[2, 2, 1],
        [2, 1, 0]])

x[..., 0].size:
torch.Size([2, 3])

mask:
tensor([[ True,  True, False],
        [ True, False, False]])

mask.size:
torch.Size([2, 3])

结论仍然是对的, 我们再将x扩充至 [N, H, W, 3]:

import torch


x = torch.randint(low=0, high=3, size=[2, 3, 2, 1])

mask = x[..., 0] > 1  # 最后一个维度中的第一个元素如果大于1, 则前面的维度返回True, 否则返回False

print(f"x:\n{x}\n")
print(f"x[..., 0]:\n{x[..., 0]}\n")
print(f"x[..., 0].size:\n{x[..., 0].size()}\n")
print(f"mask:\n{mask}\n")
print(f"mask.size:\n{mask.size()}\n")
x:
tensor([[[[0],
          [0]],

         [[0],
          [1]],

         [[2],
          [2]]],


        [[[1],
          [2]],

         [[2],
          [2]],

         [[2],
          [2]]]])

x[..., 0]:
tensor([[[0, 0],
         [0, 1],
         [2, 2]],

        [[1, 2],
         [2, 2],
         [2, 2]]])

x[..., 0].size:
torch.Size([2, 3, 2])

mask:
tensor([[[False, False],
         [False, False],
         [ True,  True]],

        [[False,  True],
         [ True,  True],
         [ True,  True]]])

mask.size:
torch.Size([2, 3, 2])

结论是对的.

3. [mask]

上面我们分析了mask究竟是在取什么, 那么将mask应用到x中(x[mask])会是怎么样的呢?

import torch


x = torch.randint(low=0, high=3, size=[2, 3])

mask = x[..., 0] > 1  # 最后一个维度中的第一个元素如果大于1, 则前面的维度返回True, 否则返回False

filtered_x = x[mask]

print(f"x:\n{x}\n")
print(f"x.size:\n{x.size()}\n")

print("-" * 50)

print(f"x[..., 0]:\n{x[..., 0]}\n")
print(f"x[..., 0].size:\n{x[..., 0].size()}\n")

print("-" * 50)

print(f"mask:\n{mask}\n")
print(f"mask.size:\n{mask.size()}\n")

print("-" * 50)

print(f"filtered_x:\n{filtered_x}\n")
print(f"filtered_x.size:\n{filtered_x.size()}\n")

结果2:

x:
tensor([[2, 2, 1],
        [0, 0, 2]])

x.size:
torch.Size([2, 3])

--------------------------------------------------
x[..., 0]:
tensor([2, 0])

x[..., 0].size:
torch.Size([2])

--------------------------------------------------
mask:
tensor([ True, False])

mask.size:
torch.Size([2])

--------------------------------------------------
filtered_x:
tensor([[2, 2, 1]])

filtered_x.size:
torch.Size([1, 3])

mask负责筛选"列", x[mask]负责取出符合条件的行, 且这个行是一个2D tensor.

结果2:

x:
tensor([[0, 1, 0],
        [1, 0, 0]])

x.size:
torch.Size([2, 3])

--------------------------------------------------
x[..., 0]:
tensor([0, 1])

x[..., 0].size:
torch.Size([2])

--------------------------------------------------
mask:
tensor([False, False])

mask.size:
torch.Size([2])

--------------------------------------------------
filtered_x:
tensor([], size=(0, 3), dtype=torch.int64)

filtered_x.size:
torch.Size([0, 3])

mask负责筛选"列", x[mask]负责取出符合条件的行, 且这个行是一个2D tensor.


我们将x的维度提升:

import torch


x = torch.randint(low=0, high=3, size=[2, 3, 2])

mask = x[..., 0] > 1  # 最后一个维度中的第一个元素如果大于1, 则前面的维度返回True, 否则返回False

filtered_x = x[mask]

print(f"x:\n{x}\n")
print(f"x.size:\n{x.size()}\n")

print("-" * 50)

print(f"x[..., 0]:\n{x[..., 0]}\n")
print(f"x[..., 0].size:\n{x[..., 0].size()}\n")

print("-" * 50)

print(f"mask:\n{mask}\n")
print(f"mask.size:\n{mask.size()}\n")

print("-" * 50)

print(f"filtered_x:\n{filtered_x}\n")
print(f"filtered_x.size:\n{filtered_x.size()}\n")

结果1:

x:
tensor([[[0, 2],
         [0, 2],
         [0, 2]],

        [[2, 2],
         [1, 0],
         [1, 2]]])

x.size:
torch.Size([2, 3, 2])

--------------------------------------------------
x[..., 0]:
tensor([[0, 0, 0],
        [2, 1, 1]])

x[..., 0].size:
torch.Size([2, 3])

--------------------------------------------------
mask:
tensor([[False, False, False],
        [ True, False, False]])

mask.size:
torch.Size([2, 3])

--------------------------------------------------
filtered_x:
tensor([[2, 2]])

filtered_x.size:
torch.Size([1, 2])

结果2:

x:
tensor([[[1, 0],
         [2, 0],
         [2, 1]],

        [[2, 0],
         [2, 0],
         [2, 1]]])

x.size:
torch.Size([2, 3, 2])

--------------------------------------------------
x[..., 0]:
tensor([[1, 2, 2],
        [2, 2, 2]])

x[..., 0].size:
torch.Size([2, 3])

--------------------------------------------------
mask:
tensor([[False,  True,  True],
        [ True,  True,  True]])

mask.size:
torch.Size([2, 3])

--------------------------------------------------
filtered_x:
tensor([[2, 0],
        [2, 1],
        [2, 0],
        [2, 0],
        [2, 1]])

filtered_x.size:
torch.Size([5, 2])

我们可以发现, 结论 mask负责筛选"列", x[mask]负责取出符合条件的行, 且这个行是一个2D tensor 依然是符合的, 我们再扩充一下维度:

import torch


x = torch.randint(low=0, high=3, size=[2, 3, 2, 2])

mask = x[..., 0] > 1  # 最后一个维度中的第一个元素如果大于1, 则前面的维度返回True, 否则返回False

filtered_x = x[mask]

print(f"x:\n{x}\n")
print(f"x.size:\n{x.size()}\n")

print("-" * 50)

print(f"x[..., 0]:\n{x[..., 0]}\n")
print(f"x[..., 0].size:\n{x[..., 0].size()}\n")

print("-" * 50)

print(f"mask:\n{mask}\n")
print(f"mask.size:\n{mask.size()}\n")

print("-" * 50)

print(f"filtered_x:\n{filtered_x}\n")
print(f"filtered_x.size:\n{filtered_x.size()}\n")

结果1:

x:
tensor([[[[0, 0],
          [0, 1]],

         [[1, 1],
          [0, 0]],

         [[0, 1],
          [2, 1]]],


        [[[2, 2],
          [0, 1]],

         [[0, 2],
          [0, 2]],

         [[0, 0],
          [2, 2]]]])

x.size:
torch.Size([2, 3, 2, 2])

--------------------------------------------------
x[..., 0]:
tensor([[[0, 0],
         [1, 0],
         [0, 2]],

        [[2, 0],
         [0, 0],
         [0, 2]]])

x[..., 0].size:
torch.Size([2, 3, 2])

--------------------------------------------------
mask:
tensor([[[False, False],
         [False, False],
         [False,  True]],

        [[ True, False],
         [False, False],
         [False,  True]]])

mask.size:
torch.Size([2, 3, 2])

--------------------------------------------------
filtered_x:
tensor([[2, 1],
        [2, 2],
        [2, 2]])

filtered_x.size:
torch.Size([3, 2])

结果2:

x:
tensor([[[[2, 2],
          [2, 2]],

         [[1, 0],
          [0, 2]],

         [[0, 2],
          [2, 2]]],


        [[[0, 0],
          [1, 0]],

         [[1, 1],
          [0, 1]],

         [[2, 1],
          [1, 0]]]])

x.size:
torch.Size([2, 3, 2, 2])

--------------------------------------------------
x[..., 0]:
tensor([[[2, 2],
         [1, 0],
         [0, 2]],

        [[0, 1],
         [1, 0],
         [2, 1]]])

x[..., 0].size:
torch.Size([2, 3, 2])

--------------------------------------------------
mask:
tensor([[[ True,  True],
         [False, False],
         [False,  True]],

        [[False, False],
         [False, False],
         [ True, False]]])

mask.size:
torch.Size([2, 3, 2])

--------------------------------------------------
filtered_x:
tensor([[2, 2],
        [2, 2],
        [2, 2],
        [2, 1]])

filtered_x.size:
torch.Size([4, 2])

结论依然存在!


4. 结论

到现在我们终于明白了,

output = net(input)  # [N, H, W, 3, 8]

mask = output[..., 0] > thresh  # [N, H, W, 3]
  • mask: 取出的是符合条件的列 (判断用的维度就消失了, 所以[N, H, W, 3, 8] -> [N, H, W, 3])
  • output[mask]: 取出符合条件的行 (是一个2-D tensor)

举个例子:

def get_idx_and_info(self, output, thresh):
    output = output.permute(0, 2, 3, 1)  # [N, 24, H, W] -> [N, H, W, 3*8]
    N, H, W, _ = output.size()
    output = output.reshape(N, H, W, 3, -1)  # N, H, W, 24] -> N, H, W, 3, 8]
    
    # 定义掩码(符合条件的行)
    mask = output[..., 0] > thresh  # 置信度 > thresh  # [N, H, W, 3] (这里应该是自动squeeze了)
    
    idx = mask.nonzero()  # size -> [符合条件数的个数, 4]  # 因为mask的维度个数有4个, 所以这里是4, 
    info = output[mask]  # size -> [符合条件数的个数, 8]  # 8 = c + loc + cls
    
    return idx, info
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值