在深度学习模型训练中 add 和 concat操作有什么区别?
add:逐元素按值相加
concat:按照指定的维度进行张量堆叠
- torch.cat在行方向上堆叠:
- torch.cat在列方向上堆叠:
ai = torch.arange(na, device=targets.device).float().view(na, 1).repeat(1, nt) 执行逻辑
- torch.arange: 分配一个张量为左闭右开区间[0, na),默认是int类型, device参数可以将张量加载到指定的设备上
- float: 将数据类型转换成float型
- view(na, 1):view方法用于改变张量形状,而不改变数据。view(na, 1)表示由一维张量变为二维张量。第二维的大小为1
- repeat(1, nt):表示在第二维的方向上重复nt次
targets = torch.cat((targets.repeat(na, 1, 1), ai[:, :, None]), 2) 的执行逻辑
- targets.repeat(na, 1, 1) :targets 在行的方向上堆叠na次
- ai[:, :, None]:用来扩展张量ai的维度,张量ai增加第三个维度,并且大小为1
- torch.cat(…, 2):将两个张量沿着第三个维度拼接
torch.tensor(p[i].shape)[[3, 2, 3, 2]] 执行逻辑
- p[i].shape:从p中找到索引为i的张量,并返回该张量的shape,用元组表示
- torch.tensor(p[i].shape):将元组类型的元素转换成tensor类型
- [[3,2,3,2]]:这是索引列表,返回索引指向的元素值
torch.max(r, 1. / r).max(2)[0] 执行逻辑
- torch.max(r, 1. / r):
t = t[j] t的维度是[3, 42, 7] 的float张量 j是[3 , 42]的bool型张量,这是怎么过滤的?
j = torch.stack((torch.ones_like(j), j, k, l, m) 执行逻辑
- torch.ones_like(j):创建一个与张量j形状相同的新张量,其中所有元素都是1。
- (torch.ones_like(j), j, k, l, m):这是一个包含五个张量的元组。这些张量将被堆叠在一起。为了成功堆叠,所有张量的形状必须相同,除了堆叠的维度。
torch.cat([pxy, pwh], dim=-1)
-1表示最后一个维度
pair_wise_iou.shape[1]
获取张量pair_wise_iou 第二个维度的大小
F.one_hot(this_target[:, 1].to(torch.int64), self.nc)
将this_target[:, 1]上所有的元素值,用one-hot向量的形式表示,向量的维度为self.nc
unsqueeze(1)
第二个维度(索引为1)处添加一个大小为1的新维度。这通常用于增加张量的维度数。
先贴一段yolov7中回归预测值的代码
def __call__(self, p, targets, imgs): # predictions, targets, model
device = targets.device
lcls, lbox, lobj = torch.zeros(1, device=device), torch.zeros(1, device=device), torch.zeros(1, device=device)
bs, as_, gjs, gis, targets, anchors = self.build_targets(p, targets, imgs)
pre_gen_gains = [torch.tensor(pp.shape, device=device)[[3, 2, 3, 2]] for pp in p]
# Losses
for i, pi in enumerate(p): # layer index, layer predictions
b, a, gj, gi = bs[i], as_[i], gjs[i], gis[i] # image, anchor, gridy, gridx
# 置信度 gt和预测值的IOU
tobj = torch.zeros_like(pi[..., 0], device=device) # target obj
n = b.shape[0] # number of targets
if n:
ps = pi[b, a, gj, gi] # prediction subset corresponding to targets
# Regression
grid = torch.stack([gi, gj], dim=1)
pxy = ps[:, :2].sigmoid() * 2. - 0.5
#pxy = ps[:, :2].sigmoid() * 3. - 1.
pwh = (ps[:, 2:4].sigmoid() * 2) ** 2 * anchors[i]
pbox = torch.cat((pxy, pwh), 1) # predicted box
selected_tbox = targets[i][:, 2:6] * pre_gen_gains[i]
selected_tbox[:, :2] -= grid
iou = bbox_iou(pbox.T, selected_tbox, x1y1x2y2=False, CIoU=True) # CIOU重叠面积、中心点距离、宽高比同时加入了计算iou(prediction, target)
lbox += (1.0 - iou).mean() # iou loss
# Objectness
tobj[b, a, gj, gi] = (1.0 - self.gr) + self.gr * iou.detach().clamp(0).type(tobj.dtype) # iou ratio当作置信度
# Classification
selected_tcls = targets[i][:, 1].long()
if self.nc > 1: # cls loss (only if multiple classes)
t = torch.full_like(ps[:, 5:], self.cn, device=device) # targets
t[range(n), selected_tcls] = self.cp#onehot一下
lcls += self.BCEcls(ps[:, 5:], t) # BCE
# Append targets to text file
# with open('targets.txt', 'a') as file:
# [file.write('%11.5g ' * 4 % tuple(x) + '\n') for x in torch.cat((txy[i], twh[i]), 1)]
obji = self.BCEobj(pi[..., 4], tobj)#大部分都是背景
lobj += obji * self.balance[i] # obj loss
if self.autobalance:
self.balance[i] = self.balance[i] * 0.9999 + 0.0001 / obji.detach().item()
if self.autobalance:
self.balance = [x / self.balance[self.ssi] for x in self.balance]
lbox *= self.hyp['box']
lobj *= self.hyp['obj']
lcls *= self.hyp['cls']
bs = tobj.shape[0] # batch size
loss = lbox + lobj + lcls
return loss * bs, torch.cat((lbox, lobj, lcls, loss)).detach()
__call__方法的使用,允许一个对象的实例像函数一样被调用,传入参数可以直接被调用,示例如下:
class YOLOv7Predictor:
def __init__(self, model):
self.model = model
def __call__(self, x):
# 在这里实现模型的预测逻辑
return self.model.predict(x)
# 假设我们有一个已经加载好的模型
model = ... # 加载YOLOv7模型
# 创建一个YOLOv7Predictor的实例
predictor = YOLOv7Predictor(model)
# 现在我们可以像调用函数一样调用predictor实例
# 假设x是我们要进行预测的数据
x = ... # 输入数据
predictions = predictor(x) # 调用__call__方法进行预测