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