【目标检测】YOLOv7算法实现(二):正样本匹配(SimOTA)与损失计算

  本系列文章记录本人硕士阶段YOLO系列目标检测算法自学及其代码实现的过程。其中算法具体实现借鉴于ultralytics YOLO源码Github,删减了源码中部分内容,满足个人科研需求。
  本篇文章在YOLOv5算法实现的基础上,进一步完成YOLOv7算法的实现。YOLOv7相比于YOLOv5,最主要的不同之处如下:

  • 模型结构:引进了更为高效的特征提取模块(ELAN)、下采样模块(MP),不同的空间池化层(SPPCSPC),重参数卷积(RepConv)
  • 正样本匹配:结合YOLOv5中和正样本匹配方法和YOLOX中的正样本筛选方法(SimOTA)

文章地址:
YOLOv7算法实现(一):模型搭建
YOLOv7算法实现(二):正样本匹配(SimOTA)与损失计算

本文目录

  • 0 引言
  • 1 正样本匹配
  • 2 损失计算
  • 3 代码实现
    • 3.1 正样本匹配
    • 3.2 损失计算

0 引言

  YOLOv7中的正样本匹配在YOLOv5的正样本匹配基础上进一步通过SimOTA对正样本进行筛选,损失计算流程如图1所示。
在这里插入图片描述

图1 YOLOv7损失计算流程

1 正样本匹配

  YOLOv5的正样本匹配方法可见文章YOLOv5算法实现(四):损失计算。在YOLOv5正样本匹配方法中,在每一个feature_map上,根据目标中心点所在位置至多使用三个预测单元对目标进行匹配,在每一个预测单元中,根据宽高比至多使用三个Anchor对目标进行匹配,因此经过YOLOv5正样本匹配后,一个目标至多得到27个匹配样本。
  SimOTA正样本筛选流程如下:

  1. 计算实际目标nt与匹配样本nt_n的IoU损失:
    p a i r _ w i s e _ i o u _ l o s s = − log ⁡ ( i o u ) pair\_wise\_iou\_loss = - \log (iou) pair_wise_iou_loss=log(iou)
  2. 计算实际目标nt与匹配样本nt_n的类别交叉熵损失:
    p a i r _ w i s e _ c l s _ l o s s = − y log ⁡ ( σ ( y p r e d ) ) − ( 1 − y ) log ⁡ ( σ ( 1 − y p r e d ) ) pair\_wise\_cls\_loss = - y\log (\sigma ({y_{pred}})) - (1 - y)\log (\sigma (1 - {y_{pred}})) pair_wise_cls_loss=ylog(σ(ypred))(1y)log(σ(1ypred))
  3. 根据IoU损失总和确定每一个实际目标ntdynamic_k(每一个nt匹配的样本数量)
  4. 计算匹配样本总损失:
    p a i r _ w i s e _ l o s s = p a i r _ w i s e _ c l s _ l o s s + 3 p a i r _ w i s e _ i o u _ l o s s pair\_wise\_loss = pair\_wise\_cls\_loss + 3pair\_wise\_iou\_loss pair_wise_loss=pair_wise_cls_loss+3pair_wise_iou_loss
  5. 根据总损失和dynamic_k对匹配的正样本进行筛选
      假设某目标(类别为3)在某训练批次中得到了7个匹配结果,其SimOTA正样本筛选示例如图2所示。

在这里插入图片描述

图2 SimOTA计算示例

2 损失计算

  YOLOv7中损失计算方式与YOLOv5一致,包含以下三个部分:

  • 位置损失(仅计算正样本)
    I o u L o s s = 1 − C I o U IouLoss = 1 - CIoU IouLoss=1CIoU

在这里插入图片描述

图3 常见IoU计算方法
  • 类别损失(仅计算正样本):
    C l s L o s s = ∑ i = 0 n f { 1 n ∑ j = 0 n [ 1 n c ∑ k = 0 k = n c ( y log ⁡ ( σ ( p ) ) + ( 1 − y ) log ⁡ ( 1 − σ ( p ) ) ) ] } ClsLoss = \sum\limits_{i = 0}^{nf} {\{ {1 \over n}\sum\limits_{j = 0}^n {[{1 \over {nc}}\sum\limits_{k = 0}^{k = nc} {({y}} } } \log (\sigma ({p})) + (1 - {y})\log (1 - \sigma ({p})))]\} ClsLoss=i=0nf{n1j=0n[nc1k=0k=nc(ylog(σ(p))+(1y)log(1σ(p)))]}
  • 置信度损失(所有样本)
    O b j L o s s = ∑ i = 0 n f { 1 n a ∑ j = 0 n a [ 1 g r i d y × g r i d x ∑ m = 0 g r i d y ∑ n = 0 g r i d x ( y log ⁡ ( σ ( p ) ) + ( 1 − y ) log ⁡ ( 1 − σ ( p ) ) ) ] } ObjLoss = \sum\limits_{i = 0}^{nf} {\{ {1 \over {na}}\sum\limits_{j = 0}^{na} {[{1 \over {gridy \times gridx}}\sum\limits_{m = 0}^{gridy} {\sum\limits_{n = 0}^{gridx} {(y\log (\sigma (p)) + (1 - y)\log (1 - \sigma (p)))]} } } } \} ObjLoss=i=0nf{na1j=0na[gridy×gridx1m=0gridyn=0gridx(ylog(σ(p))+(1y)log(1σ(p)))]}

3 代码实现

3.1 正样本匹配

YOLOv5匹配方法

    def find_3_positive(self, p, targets):# Build targets for compute_loss(), input targets(num_gt,(image_index,class,x,y,w,h))# input p (num_feature_map, bs, ba, y, x, (x, y, w, h, obj, classes)) 相对坐标# na: 每个特征图上的anchors数量; nt: 当前训练图像的正样本个数na, nt = self.na, targets.shape[0]  # number of anchors, targetsindices, anch = [], []# gain是为了后面将targets=[na, nt, t]中归一化了的xywh映射到相对feature map尺度上# image_index + class + xywh + anchor_indexgain = torch.ones(7, device=targets.device).long()ai = torch.arange(na, device=targets.device).float().view(na, 1).repeat(1, nt)  # same as .repeat_interleave(nt)# tagets [na, num_gt, (image_index,class,x,y,w,h, anchors_index)]# 对一张特征图上的三个anchors均进行正样本匹配targets = torch.cat((targets.repeat(na, 1, 1), ai[:, :, None]), 2)  # append anchor indices# 匹配的gridg = 0.5  # biasoff = torch.tensor([[0, 0],[1, 0], [0, 1], [-1, 0], [0, -1],  # j,k,l,m# [1, 1], [1, -1], [-1, 1], [-1, -1],  # jk,jm,lk,lm], device=targets.device).float() * g  # offsets# 对每一个尺度的features上的正样本进行匹配for i in range(self.nl):anchors = self.anchors[i]  # 当前feature_map上的anchors绝对尺寸# xyxy增益, 用于将targets中的(images_index, class, x, y, w, h, anchor_index)相对坐标转换为feature_map上的绝对坐标gain[2:6] = torch.tensor(p[i].shape)[[3, 2, 3, 2]]  # xyxy gaint = targets * gainif nt:# 根据目标的wh和anchors的wh比例筛选匹配的anchorsr = t[:, :, 4:6] / anchors[:, None]  # wh ratio# torch.max(r, 1. / r).max(2) -> return: values, indexj = torch.max(r, 1. / r).max(2)[0] < self.hyp['anchor_t']  # compare# j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t']  # iou(3,n)=wh_iou(anchors(3,2), gwh(n,2))# 根据长宽比对正样本进行筛选t = t[j]# Offsetsgxy = t[:, 2:4]  # gxy: 目标center相对左上角的偏置(用于选择左、上、左上grid)gxi = gain[[2, 3]] - gxy  # gxi: 目标center相对右下角的偏置(用于选择右、下、右下grid)j, k = ((gxy % 1. < g) & (gxy > 1.)).Tl, m = ((gxi % 1. < g) & (gxi > 1.)).Tj = torch.stack((torch.ones_like(j), j, k, l, m))# 将t复制5份, 用j筛选出需要保留的正样本t = t.repeat((5, 1, 1))[j]# [0, 0], [1, 0], [0, 1], [-1, 0], [0, -1]# 构造所有正样本的偏置offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j]else:t = targets[0]offsets = 0# Defineb, c = t[:, :2].long().T  # image indices, classgxy = t[:, 2:4]  # grid xy features_map上绝对坐标gwh = t[:, 4:6]  # grid whgij = (gxy - offsets).long()  # 减去偏置获得匹配的grid坐标点gi, gj = gij.T  # grid xy indices# Appenda = t[:, 6].long()  # anchor indices# image indices, anchor indices, gj, giindices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1)))# 正样本对应的anchors大小, 当前features map上的绝对尺寸anch.append(anchors[a])  # anchorsreturn indices, anch

SimOTA正样本筛选

    def build_targets(self, p, targets, imgs):''':param p: [feature_map indices, bs, ba, y, x, (x, y, w, h, obj, num_class)]回归参数:param targets: [num_gt, (image_index, classes, x, y, w, h)]相对坐标:param imgs: [num_img, 3, y, x]'''# indices: [feature_map indices, image indices list, anchor indices list, gj, gi]# anch: 每个正样本对应的anchors大小(在对应feature map上的绝对坐标)'''1. 根据target中心点x, y确定作为正样本的cell(gj, gi);根据target的w, h和anchors的长宽比确定每一个cell中进行匹配的anchorindices: feature_map_list{image_indices_list, anchor indices_list, gj, gi}anch: feature_map_list{anchor_size}'''indices, anch = self.find_3_positive(p, targets)device = torch.device(targets.device)'''2. 根据OTA算法对上一步匹配的正样本进行进一步筛选每一张图片实际目标数nt, 匹配到的对应正样本数n_gta.对每一个nt和每一个n_gt的预测结果计算其iou矩阵[nt, n_gt], 求出iou损失[nt, n_gt]b.对每一个nt和每一个n_gt的预测结果计算其类别损失矩阵[nt, n_gt]c.根据iou损失总和确定一个dynamic_k(每一个nt需要几个n_gt进行匹配)d.计算cost矩阵(loss_iou + a * cls_iou)e.根据cost矩阵和dynamic_k确定nt匹配的正样本所在feature_map, gj,gi,anchor'''matching_bs = [[] for pp in p]  # imagesmatching_as = [[] for pp in p]  # anchormatching_gjs = [[] for pp in p]  # gjmatching_gis = [[] for pp in p]  # gimatching_targets = [[] for pp in p]  # 匹配的正样本matching_anchs = [[] for pp in p]  # 对应的anchors大小nl = len(p)  # 输出不同尺寸特征图数量# 对每一张图片进行正样本匹配for batch_idx in range(p[0].shape[0]):b_idx = targets[:, 0] == batch_idxthis_target = targets[b_idx]  # 获得当前图片的实际目标if this_target.shape[0] == 0:continue# 得到在原图尺度的(x, y, w, h)绝对坐标 -> (xmin, ymin, xmax, ymax)txywh = this_target[:, 2:6] * imgs[batch_idx].shape[1]txyxy = xywh2xyxy(txywh)pxyxys = []  # 预测的位置回归参数p_cls = []  # 预测的类别置信度p_obj = []  # 预测的目标置信度from_which_layer = []  # 当前预测特征来自哪个feature_mapall_b = []  # image indices(所有特征图)all_a = []  # anchor indices(所有特征图)all_gj = []  # gj(所有特征图)all_gi = []  # gi(所有特征图)all_anch = []  # anchor size(所有特征图)# 针对每个特征图匹配到的正样本进行OTA算法cost计算进一步对正样本进行筛选for i, pi in enumerate(p):b, a, gj, gi = indices[i]  # image indices, anchor indices, gj giidx = (b == batch_idx)  # 得到第一次匹配得到的属于当前图片的正样本b, a, gj, gi = b[idx], a[idx], gj[idx], gi[idx]  # image indices, anchor indices, gj giall_b.append(b)  # 当前图片第i个输出特征图的匹配imagesall_a.append(a)  # 当前图片第i个输出特征图的匹配anchors indicesall_gj.append(gj)  # 当前图片第i个输出特征图的匹配gjall_gi.append(gi)  # 当前图片第i个输出特征图的匹配giall_anch.append(anch[i][idx])  # 当前图片第i个输出特征图的匹配anchors大小(当前特征图上的绝对尺寸)from_which_layer.append((torch.ones(size=(len(b),)) * i).to(device))  # 当前匹配的正样本来自哪个输出特征图fg_pred = pi[b, a, gj, gi]  # 当前匹配的正样本预测结果(x, y, w, h, obj, cls)p_obj.append(fg_pred[:, 4:5])  # 预测目标置信度p_cls.append(fg_pred[:, 5:])  # 预测类别类别grid = torch.stack([gi, gj], dim=1)# 预测结果(x, y)回归参数转换为原图的(x, y)绝对坐标pxy = (fg_pred[:, :2].sigmoid() * 2. - 0.5 + grid) * self.stride[i]  # / 8.# pxy = (fg_pred[:, :2].sigmoid() * 3. - 1. + grid) * self.stride[i]# 预测结果(w, h)回归参数转换为原图的(w, h)绝对坐标pwh = (fg_pred[:, 2:4].sigmoid() * 2) ** 2 * anch[i][idx] * self.stride[i]  # / 8.# 预测结果(x, y, w, h)原图上的绝对坐标 -> (xmin, ymin, xmax, ymax)pxywh = torch.cat([pxy, pwh], dim=-1)pxyxy = xywh2xyxy(pxywh)pxyxys.append(pxyxy)pxyxys = torch.cat(pxyxys, dim=0)  # 预测结果xyxy:原图上的绝对大小if pxyxys.shape[0] == 0:continuep_obj = torch.cat(p_obj, dim=0)  # 预测结果目标置信度p_cls = torch.cat(p_cls, dim=0)  # 预测结果类别置信度from_which_layer = torch.cat(from_which_layer, dim=0)  # 预测结果属于哪个feature_mapall_b = torch.cat(all_b, dim=0)  # 预测结果属于batch中哪张图片all_a = torch.cat(all_a, dim=0)  # 预测结果属于哪个anchorall_gj = torch.cat(all_gj, dim=0)  # 预测结果属于哪个gjall_gi = torch.cat(all_gi, dim=0)  # 预测结果属于哪个giall_anch = torch.cat(all_anch, dim=0)  # 预测结果的anchor大小(对应feature_map上的绝对大小)# 计算pxyxy和txyxy的iou(均为原图上的实际大小)# txytxt:[nt, 4], pxypxy:[np, 4] -> pair_wise_iou: [nt, np]pair_wise_iou = box_iou(txyxy, pxyxys)# iou损失pair_wise_iou_loss = -torch.log(pair_wise_iou + 1e-8)# 根据iou从大到小选取至多10个ioutop_k, _ = torch.topk(pair_wise_iou, min(10, pair_wise_iou.shape[1]), dim=1)# 根据iou的总和确定dynamic_ks(每一个目标选择的匹配正样本数量), 至少会选择一个正样本对目标进行匹配dynamic_ks = torch.clamp(top_k.sum(1).int(), min=1)# 对当前图片的实际标签cls进行独热编码(对每一个nt进行扩充成和p一样的数量)gt_cls_per_image = (F.one_hot(this_target[:, 1].to(torch.int64), self.nc)  # 对类别标签进行独热编码: [nt, nc].float().unsqueeze(1)  # [nt, 1, nc].repeat(1, pxyxys.shape[0], 1)  # [nt, n_gt, nc])# 当前图片的实际目标个数, 对预测的置信度(类别置信度x目标置信度)进行扩充, 给每一个nt分配num_gt = this_target.shape[0]cls_preds_ = (p_cls.float().unsqueeze(0).repeat(num_gt, 1, 1).sigmoid_()* p_obj.unsqueeze(0).repeat(num_gt, 1, 1).sigmoid_())y = cls_preds_.sqrt_()pair_wise_cls_loss = F.binary_cross_entropy_with_logits(torch.log(y / (1 - y)), gt_cls_per_image, reduction="none").sum(-1)  # 计算类别损失del cls_preds_cost = (pair_wise_cls_loss+ 3.0 * pair_wise_iou_loss)matching_matrix = torch.zeros_like(cost, device=device)# 确定每一个gt匹配的g_nt(根据cost和dynamic_k)for gt_idx in range(num_gt):_, pos_idx = torch.topk(cost[gt_idx], k=dynamic_ks[gt_idx].item(), largest=False)matching_matrix[gt_idx][pos_idx] = 1.0del top_k, dynamic_ks# 当同一个gt匹配了多个t时, 根据cost选择gt对哪一个t进行匹配anchor_matching_gt = matching_matrix.sum(0)if (anchor_matching_gt > 1).sum() > 0:_, cost_argmin = torch.min(cost[:, anchor_matching_gt > 1], dim=0)matching_matrix[:, anchor_matching_gt > 1] *= 0.0matching_matrix[cost_argmin, anchor_matching_gt > 1] = 1.0fg_mask_inboxes = (matching_matrix.sum(0) > 0.0).to(device)  # 保留匹配到的正样本matched_gt_inds = matching_matrix[:, fg_mask_inboxes].argmax(0)  # 每一个gt匹配的实际目标索引# 保留OTA算法进一步匹配到的结果from_which_layer = from_which_layer[fg_mask_inboxes]all_b = all_b[fg_mask_inboxes]all_a = all_a[fg_mask_inboxes]all_gj = all_gj[fg_mask_inboxes]all_gi = all_gi[fg_mask_inboxes]all_anch = all_anch[fg_mask_inboxes]this_target = this_target[matched_gt_inds]# 将每一个feature_map的预测结果分开for i in range(nl):layer_idx = from_which_layer == imatching_bs[i].append(all_b[layer_idx])matching_as[i].append(all_a[layer_idx])matching_gjs[i].append(all_gj[layer_idx])matching_gis[i].append(all_gi[layer_idx])matching_targets[i].append(this_target[layer_idx])matching_anchs[i].append(all_anch[layer_idx])# 将所有图片匹配到的正样本进行合并for i in range(nl):if matching_targets[i] != []:matching_bs[i] = torch.cat(matching_bs[i], dim=0)matching_as[i] = torch.cat(matching_as[i], dim=0)matching_gjs[i] = torch.cat(matching_gjs[i], dim=0)matching_gis[i] = torch.cat(matching_gis[i], dim=0)matching_targets[i] = torch.cat(matching_targets[i], dim=0)matching_anchs[i] = torch.cat(matching_anchs[i], dim=0)else:matching_bs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64)matching_as[i] = torch.tensor([], device='cuda:0', dtype=torch.int64)matching_gjs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64)matching_gis[i] = torch.tensor([], device='cuda:0', dtype=torch.int64)matching_targets[i] = torch.tensor([], device='cuda:0', dtype=torch.int64)matching_anchs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64)return matching_bs, matching_as, matching_gjs, matching_gis, matching_targets, matching_anchs

3.2 损失计算

class ComputeLossOTA:# Compute lossesdef __init__(self, model, autobalance=False):super(ComputeLossOTA, self).__init__()device = next(model.parameters()).device  # get model deviceh = model.hyp  # hyperparameters# Define criteriaBCEcls = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['cls_pw']], device=device))BCEobj = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['obj_pw']], device=device))# Class label smoothing https://arxiv.org/pdf/1902.04103.pdf eqn 3self.cp, self.cn = smooth_BCE(eps=h.get('label_smoothing', 0.0))  # positive, negative BCE targets# Focal lossg = h['fl_gamma']  # focal loss gammaif g > 0:BCEcls, BCEobj = FocalLoss(BCEcls, g), FocalLoss(BCEobj, g)m = model.model[-1]  # Detect() moduleself.balance = {3: [4.0, 1.0, 0.4]}.get(m.nl, [4.0, 1.0, 0.25, 0.06, .02])  # P3-P7self.ssi = list(m.stride).index(16) if autobalance else 0  # stride 16 indexself.BCEcls, self.BCEobj, self.gr, self.hyp, self.autobalance = BCEcls, BCEobj, 1.0, h, autobalanceself.na = m.na  # anchors数量self.nc = m.nc  # 类别数量self.nl = m.nl  # 输出特征层数量self.anchors = m.anchors  # anchors [3, 3, 2], 缩放到feature map上的anchors尺寸self.stride = m.stride  # 输出特征图在输入特征图上的跨度self.device = device  # 数据存储设备def __call__(self, p, targets, imgs):  # predictions, targets, model'''正样本匹配, 计算损失:param p: [num_feature_map, batch_size, num_anchors, y, x, (x + y + w + h + obj + num_class)]:param targets: [num_gt, (image indices, classes, x, y, w, h)]:param imgs: [num_img, 3, y, x]'''device = targets.device# 分类损失, 位置损失, 置信度损失lcls, lbox, lobj = torch.zeros(1, device=device), torch.zeros(1, device=device), torch.zeros(1, device=device)'''正样本匹配:1. 根据target中心点x, y确定作为正样本的cell(gj, gi);根据target的w, h和anchors的长宽比确定每一个cell中进行匹配的anchorinput:[nt, 6] output:[nt*cell_num*anchor_num, 6];2. 根据Optimal Transport Assignment(OTA)算法对上一步筛选出来的正样本计算cost进一步对正样本进行筛选;bs: 正样本匹配的images indices; as_: 正样本匹配的anchor索引; gjs, gis: 预测该正样本的gj, gitargets: 该正样本匹配的实际target(image indices, class, x, y, w, h)相对坐标anchors: 正样本的anchors大小(对应特征图上的绝对大小)'''bs, as_, gjs, gis, targets, anchors = self.build_targets(p, targets, imgs)# 预测结果的x, y, w, h增益(feature_map)pre_gen_gains = [torch.tensor(pp.shape, device=device)[[3, 2, 3, 2]] for pp in p]# 根据匹配的正样本计算Lossesfor i, pi in enumerate(p):  # layer index, layer predictionsb, a, gj, gi = bs[i], as_[i], gjs[i], gis[i]  # image, anchor, gridy, gridxtobj = torch.zeros_like(pi[..., 0], device=device)  # target objn = b.shape[0]  # 匹配到的正样本数量if n:ps = pi[b, a, gj, gi]  # 预测结果(x, y, w, h, obj, classes)# 预测结果进行回归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)  # 预测box(回归到对应feature_map尺度)selected_tbox = targets[i][:, 2:6] * pre_gen_gains[i]  # 转换到feature_map尺度selected_tbox[:, :2] -= gridiou = bbox_iou(pbox, selected_tbox, CIoU=True).squeeze()  # iou(prediction, target)lbox += (1.0 - iou).mean()  # iou损失# 目标置信度(根据iou给正样本标签分配, 负样本标签为0)tobj[b, a, gj, gi] = (1.0 - self.gr) + self.gr * iou.detach().clamp(0).type(tobj.dtype)  # iou ratio# 类别标签selected_tcls = targets[i][:, 1].long()if self.nc > 1:  # 分类损失(含有多个类别时), 仅计算正样本的t = torch.full_like(ps[:, 5:], self.cn, device=device)  # 负样本标签cnt[range(n), selected_tcls] = self.cp  # 正样本标签cplcls += 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 lossif 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 sizeloss = lbox + lobj + lcls# return loss * bs, torch.cat((lbox, lobj, lcls, loss)).detach()return {"box_loss": lbox,"obj_loss": lobj,"class_loss": lcls}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/641366.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

40尺货柜可以装载多少张建筑模板?

在建筑行业&#xff0c;40尺货柜一直以其标准化、便捷的特点成为建材运输的首选。然而&#xff0c;对于建筑模板这样的大型且薄型货物&#xff0c;如何在40尺货柜中实现最高效的装载一直是一项具有挑战性的任务。让我们一起揭秘&#xff0c;40尺货柜究竟能够装载多少张建筑模板…

上门回收小程序,打造回收新模式

近年来&#xff0c;我国一直秉持着环保绿色的发展理念&#xff0c;为了减少资源浪费&#xff0c;旧物回收成为了人们处理废弃物品的方式。目前&#xff0c;我国回收市场规模大约能达到3.58亿元&#xff0c;在我国经济的稳定增长和环保意识的提高下&#xff0c;回收市场规模还将…

科技、文化与旅游的融合创新:智慧文旅的未来之路

在当今社会&#xff0c;科技、文化与旅游的融合已经成为文旅产业转型升级的重要趋势。这种融合不仅有助于提升文旅产业的核心竞争力&#xff0c;更有助于推动产业的数字化转型和可持续发展。 本文将深入探讨科技、文化与旅游的融合创新&#xff0c;以及智慧文旅场景的解决方案…

【C++语言1】基本语法

前言 &#x1f493;作者简介&#xff1a; 加油&#xff0c;旭杏&#xff0c;目前大二&#xff0c;正在学习C&#xff0c;数据结构等&#x1f440; &#x1f493;作者主页&#xff1a;加油&#xff0c;旭杏的主页&#x1f440; ⏩本文收录在&#xff1a;再识C进阶的专栏&#x1…

代码+视频R语言绘制逆概率加权后的基线表

基于 PS &#xff08;倾向评分&#xff09;的逆概率加权(IPTW )法首先由&#xff32;osenbaum作为一种以模型为基础的直接标准化法提出&#xff0c;属于边际结构模型。简单来说&#xff0c;就是把许多协变量和混杂因素打包成一个概率并进行加权&#xff0c;这样的话&#xff0c…

【GoLang入门教程】Go语言工程结构详述

程序员裁员潮&#xff1a;技术变革下的职业危机 文章目录 程序员裁员潮&#xff1a;技术变革下的职业危机前言总结:专栏集锦强烈推荐写在最后 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网…

3d音响按键怎么建立模型---模大狮模型网

要建立3D音响按键的模型&#xff0c;您可以按照以下步骤进行&#xff1a; 选择建模软件&#xff1a;首先&#xff0c;选择一个三维建模软件&#xff0c;如Blender、3ds Max或Maya。这些软件都提供了丰富的建模工具和功能&#xff0c;适合用于创建复杂的三维模型。 参考图像&am…

java SSM自助快递服务平台myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM自助快递服务平台是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代 码和数据库&#xff0c;系统主要采…

DophineScheduler通俗版

1.DophineScheduler的架构 ZooKeeper&#xff1a; AlertServer&#xff1a; UI&#xff1a; ApiServer&#xff1a; 一个租户下可以有多个用户&#xff1b;一个用户可以有多个项目一个项目可以有多个工作流定义&#xff0c;每个工作流定义只属于一个项目&#xff1b;一个租户可…

人工智能原理实验4(2)——贝叶斯、决策求解汽车评估数据集

&#x1f9e1;&#x1f9e1;实验内容&#x1f9e1;&#x1f9e1; 汽车数据集 车子具有 buying,maint,doors,persons,lug_boot and safety六种属性&#xff0c;而车子的好坏分为uncc,ucc,good and vgood四种。 &#x1f9e1;&#x1f9e1;贝叶斯求解&#x1f9e1;&#x1f9e1;…

Redis——RDB持久化

前言 Redis是一个键值对数据库服务器&#xff0c;服务器中通常包含任意个非空数据库&#xff0c;而每个非空数据库中又可以包含任意个键值对&#xff0c;为了方便起见&#xff0c;我们将服务器中的非空数据库以及它们的键值对统称为数据库状态。 因为Redis数据库是内存数据库&a…

Java/Python/Go不同开发语言在进程、线程和协程的设计差异

Java/Python/Go不同开发语言在进程、线程和协程的设计差异 1. 进程、线程和协程上的差异1.1 进程、线程、协程的定义1.2 进程、线程、协程的差异1.3 进程、线程、协程的内存成本1.4 进程、线程、协程的切换成本 2. 线程、协程之间的通信和协作方式2.1 python如何实现线程通信&a…

大模型实战营Day6 笔记

本期主题为&#xff1a; 为何测评&#xff0c;因场景众多&#xff0c;需要统一的标准&#xff1a; 评测的意义&#xff1a; 传统NLP的一些评测需要&#xff1a; 到了大模型时代&#xff0c;需要评测的就更多了&#xff1a; 客观评测&#xff1a; 有些主观题可以用模型评价…

cdh6.3.2的hive配udf

背景 大数据平台的租户要使用udf&#xff0c;他们用beeline连接&#xff0c; 意味着要通过hs2&#xff0c;但如果有多个hs2&#xff0c;各个hs2之间不能共享&#xff0c;需要先把文件传到hdfs&#xff0c;然后手动在各hs2上create function。之后就可以永久使用了&#xff0c;…

docker配置node项目

首先在项目根目录创建Dockerfile FROM node:18.19RUN mkdir /appCOPY . /appWORKDIR /appRUN npm installEXPOSE 8081CMD ["npm","run","start"]添加.dockerignore文件 /dist /node_moduleslogs *.log npm-debug.log* yarn-debug.log* yarn-er…

从零开始的OpenGL光栅化渲染器构建3-法线贴图和视差贴图

前言 我们可以用一张纹理贴图来表现物体表面的基础反射颜色&#xff0c;也可以用一张镜面反射贴图&#xff0c;来指派表面是否产生高光。除此之外&#xff0c;我们可以用贴图来存储表面的法线信息&#xff0c;以及高度信息&#xff0c;从而让渲染效果更加精细。 法线贴图 我…

通过浏览器URL地址,5分钟内渗透你的网站!很刑很可拷!

今天我来带大家简单渗透一个小破站&#xff0c;通过这个案例&#xff0c;让你深入了解为什么很多公司都需要紧急修复各个中间件的漏洞以及进行URL解析拦截等重要操作。这些措施的目的是为了保护网站和系统的安全性。如果不及时升级和修复漏洞&#xff0c;你就等着被黑客攻击吧&…

浮点数详解

目录 1.概述 2.浮点数的编码方式 2.1.float类型的IEEE编码 2.2.double类型的IEEE编码 2.3.现场问题 2.4.总结 1.概述 计算机也需要运算和存储数学中的实数。在计算机的发展过程中&#xff0c;曾产生过多种存储实数的方式&#xff0c;有的现在已经很少使用了。不管如何存储…

LeetCode 48 旋转图像

题目描述 旋转图像 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4…

Vue.js 3 项目开发:迈向现代化前端开发的必经之路

文章目录 一、Vue.js 3简介二、Vue.js 3新特性1. Composition API2. 更好的性能3. 更好的TypeScript支持4. 更多的生命周期钩子5. 更好的自定义指令API 三、Vue.js 3项目开发实践1. 搭建开发环境2. 项目结构规划3. 组件开发4. 路由管理5. 状态管理6. 测试与部署 《Vue.js 3企业…