PyTorch中一些常用的函数

1. torch.as_tensor(data, dtype=None,device=None)->Tensor : 为data生成tensor。

这里data的数据类型可以是list、tuple、ndarray、scalar等

若data已经是tensor,且dtype和device与参数相同,则生成的tensor会和data共享内存。如果data是ndarray, 且dtype对应,devices为cpu,则同样共享内存。其他情况不共享内存。

import torch
import numpy as np

arr = np.array([1,2,3,4,5,6])
tensor = torch.as_tensor(arr)

这里是pytorch的torchvision中集成的一个检测代码的一个实际例子

class AnchorGenerator(nn.Module):  
    ... 
    def generate_anchors(self, scales, aspect_ratios, dtype=torch.float32, device="cpu"):
        # type: (List[int], List[float], int, Device)  # noqa: F821
        scales = torch.as_tensor(scales, dtype=dtype, device=device)
        aspect_ratios = torch.as_tensor(aspect_ratios, dtype=dtype, device=device)
        h_ratios = torch.sqrt(aspect_ratios)
        w_ratios = 1 / h_ratios

        ws = (w_ratios[:, None] * scales[None, :]).view(-1)
        hs = (h_ratios[:, None] * scales[None, :]).view(-1)

        base_anchors = torch.stack([-ws, -hs, ws, hs], dim=1) / 2
        return base_anchors.round()

2. torch.meshgrid()函数的用法

torch.meshgrid()函数是用来生成网格的,因此也可以用于坐标生成。

函数的输入必须是数据类型相同的一维张量, 输出也是两个张量。其中输出张量的行数为第一个输入张量中元素的个数, 列数为第二个输入张量中元素的个数。

第一个输出张量填充了第一个输入张量中的元素, 每一行的元素相同;第二个输出张量填充了第二个输入张量中的元素, 每一列的元素相同

import torch
a = torch.tensor([1, 2, 3, 4])
b = torch.tensor([5, 6, 7])
x, y = torch.meshgrid(a, b)
print(x)
print(y)

#######
tensor([[1, 1, 1],
        [2, 2, 2],
        [3, 3, 3],
        [4, 4, 4]])
tensor([[5, 6, 7],
        [5, 6, 7],
        [5, 6, 7],
        [5, 6, 7]])

在YOLO系列的算法中,在将图像划分为网格的时候就用到了torch.meshgrid()函数

3. torch.flatten(input,start_dim=0,end_dim=-1)→ Tensor

  • input (Tensor) – the input tensor.
  • start_dim (int) – the first dim to flatten
  • end_dim (int) – the last dim to flatten
>>> t = torch.tensor([[[1, 2],
                       [3, 4]],
                      [[5, 6],
                       [7, 8]]])
torch.flatten(t)
# tensor([1, 2, 3, 4, 5, 6, 7, 8])
torch.flatten(t, start_dim=1)
# tensor([[1, 2, 3, 4],
#         [5, 6, 7, 8]])

4. torch.nonzero() 作用是找出所有非零元素的索引

输出张量的每一行包含输入张量中的非零元素的索引

import torch
a = torch.tensor([[1,6,0], [0,0,1]])
print(a.nonzero())

# output
# tensor([[0, 0],
#        [0, 1],
#        [1, 2]])

有时候我们只希望得到一种元素对应的索引

import torch
a = torch.tensor([[1,6,0], [0,0,1]])
print((a == 1).nonzero())
# print((a > 1).nonzero())

# output:
# tensor([[0, 0],
#        [1, 2]])

下面介绍mask操作, 这在深度学习中非常常用, 经常被用在图像兴趣提取、 数据遮挡、数据过滤、 语句padding和模型损失计算中

5. masked_fill_(mask, value)方法

其中mask是张量,元素是布尔值, value是要填充的值。该方法会在mask中为True的位置上填充value值。mask和value的形状要么是相同的, 要么是可以进行广播的, 否则会报错。

import torch

mask = torch.randint(0, 2, (3, 1)).bool()
target = torch.randn(3, 2)
print(target)
# tensor([[-0.4297,  0.6459],
#         [ 1.2334, -1.5065],
#         [ 0.1295,  0.2587]])

print(mask)
# tensor([[False],
#         [False],
#         [ True]])

# 注意mask和target是可以广播的
target.masked_fill_(mask, -1)
print(target)
# tensor([[-0.4297,  0.6459],
#         [ 1.2334, -1.5065],
#         [-1.0000, -1.0000]])
# 如果执行target.masked_fill(mask, -1), 是非in_place操作, 那么target本身的值不会改变

通常, 我们可以通过该方法来提取兴趣区域或者非兴趣区域

import torch

a = torch.randint(0, 255, (2, 3, 3))
mask = torch.tensor([[1, 0, 0], [0, 1, 0], [0, 0, 1]]).bool()
a.masked_fill_(~mask, 0)

在NLP任务中, 输入文本的长度通常不一致, 为了在batch维度进行并行化处理,一般会将较长的文本截断,较短的文本进行padding操作。注意padding操作只是为了数据维度上的对齐,他不应该对后续网络的计算做出任何贡献,因此必须要mask。

在PyTorch的词向量嵌入nn.Embedding()中, 可以通过设置padding_idx自动对指定的padding_idx进行mask操作, 使得改词网络的前向和反向传播俊失效,达到mask的目的。

import torch
import torch.nn as nn


a = torch.tensor([[1,2,3], [2,1,0]]) # 这里是两段文本数据, 注意这里的0是填充的, 会和下面padding_idx的值对应 
# 填充id,比如,输入长度为100,但是每次的句子长度并不一样,后面就需要用统一的数字填充,
# 而这里就是指定这个数字,这样,网络在遇到填充id时,就不会计算其与其它符号的相关性。
model = nn.Embedding(num_embeddings = 10, embedding_dim = 6, padding_idx = 0) # 可以尝试把padding_idx 设置为1
b = model(a)
print(b)

# output: 可以看到对应填充0的单词生成的embedding全是0
# tensor([[[-0.3996,  0.1086, -1.8453, -0.2596, -0.9255,  0.1927],
#          [-0.2327,  0.8808,  0.4689, -0.5840, -1.1757, -0.0386],
#          [-0.9343,  0.3486, -0.8714, -1.2797,  0.2659,  0.4425]],
# 
#         [[-0.2327,  0.8808,  0.4689, -0.5840, -1.1757, -0.0386],
#          [-0.3996,  0.1086, -1.8453, -0.2596, -0.9255,  0.1927],
#          [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000]]],
#        grad_fn=)

#_________________________________________________________________________________________________

# 当然, 你也可以自己手动mask
a = torch.tensor([[1,2,3], [2,1,0]])
model = nn.Embedding(num_embeddings = 10, embedding_dim = 6)
b = model(a)
mask = (a!=0).float().unsqueeze(-1)
result = b * mask

关于padding的损失计算:实际上在padding位置的loss不应该被计算, 那么也应该相应的在计算loss的时候mask掉padding部分的loss。 在PyTorch中的CrossEntropyLoss等损失函数中,可以直接设置ignore_index参数来忽略padding处的损失。

import torch
import torch.nn as nn
import torch.nn.functional as F

labels = torch.tensor([1, 2, 0])
prediction = torch.tensor([[1.6, 0.8, 1.1], [0.9, 0.6, 0.3], [2.6, 0.5, 1.7]])

#不屏蔽padding
criterion1 = nn.CrossEntropyLoss()
#屏蔽padding
criterion2 = nn.CrossEntropyLoss(ignore_index = 0)

loss1 = criterion1(prediction, labels)
loss2 = criterion2(prediction, labels)

#____________________________________________________________________________________________________
# 手动实现
labels = torch.tensor([1, 2, 0])
prediction = torch.tensor([[1.6, 0.8, 1.1], [0.9, 0.6, 0.3], [2.6, 0.5, 1.7]])

def pad_loss(prediction, target, pad_index = None):
    if pad_index == None: # 没有对pad_index进行mask操作
        mask = torch.ones_like(target, dtype = torch.float)
    else: # 考虑了mask掉pad_index
        mask = (target != pad_index).float() 
        # 实际计算的样本label数
        num_labels = mask.sum().item() 
        target = torch.zeros(prediction.shape).scatter(dim = 1, index = target.unsueeze(-1), source = torch.tensor(1))
        target = target * mask.unsqueeze(-1) # 对label进行mask
        loss = -(F.log_softmax(prediction, dim = -1) * target.float()).sum() / num_labels
    return loss

最后再说一个Transformer中使用的self-attention中对padding的处理。在self-attention计算每个attention score的权重的时候, 不能考虑padding的文本score, 因此需要mask操作

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np

a = torch.tensor([2, 1, 0])
score = torch.tensor([3.6, 2.4, 1.0])
mask = (a == 0).bool()
# 将padding处的score设置为-inf, 从而对结果不造成影响
score = F.softmax(score.masked_fill(mask, -np.inf), dim = -1)

[1]. torch.as_tensor()_Wanderer001的博客-CSDN博客_torch.as_tensor

[2]. torch.meshgrid()函数解析_小娜美要努力努力的博客-CSDN博客_torch.meshgrid

[3]. 浅析深度学习中的mask操作_guofei_fly的博客-CSDN博客_深度学习mask

你可能感兴趣的:(pytorch,python,深度学习)