YOLOv8损失函数代码详解(示例展示数据变换过程)

本文将展示YOLOv8中损失函数计算的完整代码解析,注释中提供了详尽的解释,并结合示例演示了数据维度的转换,以帮助更好地理解。

YOLOv8的损失函数计算代码位于'ultralytics/utils/loss.py'文件中(如下所示),我在代码中的注释提供了详细解析。

首先看一下整体包含哪些方法

__init__和preprocess的代码如下

class v8DetectionLoss:"""Criterion class for computing training losses for YOLOv8 object detection."""def __init__(self, model, tal_topk=10):  # model must be de-paralleled"""Initialize v8DetectionLoss with model parameters and task-aligned assignment settings."""# 获取模型的设备信息(模型参数所在的设备 CPU/GPU)device = next(model.parameters()).device  # get model device# 获取模型的超参数配置(如超参数字典,包含损失权重等)h = model.args  # hyperparameters# 获取模型最后一层,一般是 Detect() 模块,其中包含输出相关的属性m = model.model[-1]  # Detect() module# 定义二元交叉熵损失(带Logits版),用于分类损失计算(reduction="none"表示不在内部求和,保留逐元素损失)self.bce = nn.BCEWithLogitsLoss(reduction="none")# 保存超参数配置到属性 hyp,以便后续使用(例如获取损失权重)self.hyp = h# 模型的步长列表,每个输出层相对于原图的下采样倍数self.stride = m.stride  # model strides# 模型的类别数(number of classes)self.nc = m.nc  # number of classes# 输出通道数 no = 类别数 + 每个边界框预测的分布参数数目 (reg_max * 4)# 例如,如果类别数 nc=80,reg_max=16,则 no = 80 + 16*4 = 144self.no = m.nc + m.reg_max * 4# 每个边界框回归的最大分布区间(reg_max,一般用于分布Focal Loss)self.reg_max = m.reg_max# 保存设备信息到属性 deviceself.device = device# 标志是否使用 DFL(Distribution Focal Loss),当 reg_max > 1 时使用self.use_dfl = m.reg_max > 1# 初始化任务对齐分配器(Task-Aligned Assigner),用于在训练时匹配预测和真实目标self.assigner = TaskAlignedAssigner(topk=tal_topk, num_classes=self.nc, alpha=0.5, beta=6.0)# 初始化边界框损失函数(BboxLoss),传入 reg_max,用于计算边界框的回归损失(如 IoU 和 DFL)self.bbox_loss = BboxLoss(m.reg_max).to(device)# 创建投影张量 self.proj,用于将分布转换为具体距离(值为0,1,...,reg_max-1),放在与模型相同的设备上self.proj = torch.arange(m.reg_max, dtype=torch.float, device=device)def preprocess(self, targets, batch_size, scale_tensor):"""预处理目标数据:转换为张量格式并缩放坐标模型需要固定形状的输入(如 (batch_size, N, 5)),而原始标注的目标数量是动态的。比如targets中的nl(总目标数),不同batch中的总目标数不一样,预测的结果难以统一。targets 是在 __call__ 里用concat拼出来的:参数:targets: 原始标注数据,形状为(nl, ne),其中nl = 当前batch的总目标数ne = [batch_index, class_id, x_center, y_center, width, height]batch_size: 当前batch的样本数scale_tensor: 缩放因子(用于将归一化坐标还原为输入图像尺度)示例输入:batch_size=2targets = [[0, 3, 0.5, 0.5, 0.2, 0.3],  # 图像0中的目标[0, 1, 0.3, 0.4, 0.1, 0.2],[1, 5, 0.7, 0.8, 0.2, 0.1]  # 图像1中的目标]scale_tensor = tensor([640, 640, 640, 640])(假设输入图像尺寸为640x640)"""nl, ne = targets.shapeif nl == 0:  # 如果当前batch没有目标'''out是预处理后的目标张量,其形状为: (batch_size, max_targets_per_image, 5)其中:batch_size:当前批次的图像数量(例如2)。max_targets_per_image:当前批次中单张图像的最大目标数量(例如10)。5: 每个目标的属性,格式为 [class_id, x_min, y_min, x_max, y_max]。'''out = torch.zeros(batch_size, 0, ne - 1, device=self.device)else:i = targets[:, 0]  # 取出了所有真实框的图像索引列,方便按图像分组统计。# 计算每个图像的目标数# 对一维张量i做去重,并计算每个唯一值出现的次数。counts里的每个元素就是对应那张图像有多少个真实框。_, counts = i.unique(return_counts=True)  counts = counts.to(dtype=torch.int32)# 创建输出张量:形状为(batch_size, max_counts, ne-1)# 示例:当max_counts=2时,形状为(2, 2, 5)out = torch.zeros(batch_size, counts.max(), ne - 1, device=self.device)# 按图像填充数据for j in range(batch_size):'''例:假设 batch_size=3 且i = tensor([0, 0, 1, 2, 2])  # 5 个框• 当 j = 0 :matches = tensor([ True,  True, False, False, False])• 当 j = 1 :matches = tensor([False, False,  True, False, False])• 当 j = 2 :matches = tensor([False, False, False,  True,  True ])matches 是一个布尔张量,标记哪些目标属于第 j 张图像'''matches = i == j  if n := matches.sum():  # 该图像有n个目标# 将匹配的目标数据存入out[j],去除第一列(图像索引)# 示例:当j=0时,存入前两个目标的[3,0.5,0.5,0.2,0.3]和[1,0.3,0.4,0.1,0.2]out[j, :n] = targets[matches, 1:]  # [max_counts, 5]# 将坐标从归一化的xywh转换为图像尺度的xyxy# out[..., 1:5]形状:(batch_size, max_counts, 4)# 示例转换前:[[0.5,0.5,0.2,0.3], ...] 归一化的xywh# 转换后得到绝对坐标:[[320, 320, 192, 384], ...]out[..., 1:5] = xywh2xyxy(out[..., 1:5].mul_(scale_tensor))return out  # 输出形状:(batch_size, max_counts, 5)

上面代码中的targets来源如下

bbox_decode代码如下:

def bbox_decode(self, anchor_points, pred_dist):"""将网络输出的 **距离分布(pred_dist)** 解码成最终的边界框坐标 (x1,y1,x2,y2)。─────────────────────────────────────────────────────────────✦ 参数说明----------------------------------------------------------------anchor_points : Tensor(shape=(A, 2))→ 每个输出位置(=anchor) 在特征图上的中心点坐标 (cx, cy),已按 stride归一化到与 pred_dist 同尺度;A=全部输出点总数 (H₁W₁+H₂W₂+…)pred_dist     : Tensor(shape=(B, A, 4*reg_max))→ 模型回归头直接输出的 **离散距离分布**:对于每个 anchor,一共 4 个方向(左、上、右、下),每个方向用 reg_max 个 bin 表示离地的概率分布。例:batch_size(B)=2,A=8400,reg_max=16⇒ pred_dist.shape = (2, 8400, 4*16=64)✦ 返回值Tensor(shape=(B, A, 4))   # 对应每个 anchor 的 [l, t, r, b] 像素距离─────────────────────────────────────────────────────────────"""# 1) 如果使用 Distribution Focal Loss(DFL) —— reg_max > 1if self.use_dfl:                            # True 当 reg_max > 1b, a, c = pred_dist.shape               # b=B, a=A, c=4*reg_max# ---------- ① view ----------# 把 “channel = 4*reg_max” 拆成 “4 个方向 × reg_max bins”# (B, A, 64)  →  (B, A, 4, 16)pred_dist = pred_dist.view(b, a, 4, c // 4)# ---------- ② softmax ----------# 对最后一维(16)做 softmax → 获得离散概率分布pred_dist = pred_dist.softmax(3)# ---------- ③ 期望值 (matmul) ----------# self.proj = tensor([0,1,2,…,15])  ← shape (16,)# 期望 = Σ p_k * k# 形状推导:#   (B, A, 4, 16)  @  (16,)  →  (B, A, 4)pred_dist = pred_dist.matmul(self.proj.type(pred_dist.dtype))# ⚠️ 此时 pred_dist 的含义已经从“概率分布”→“具体距离值(左,上,右,下)”# ▼ 小示例(单 anchor)------------------------------------#    假设 reg_max=4, softmax 结果 = [0.1, 0.2, 0.6, 0.1]#    self.proj = [0, 1, 2, 3]#    距离 = 0*0.1 + 1*0.2 + 2*0.6 + 3*0.1 = 1.7# --------------------------------------------------------# 2) 将 “距离格式(l,t,r,b)” 转成 “bbox 坐标(x1,y1,x2,y2)”#    dist2bbox 内部做:x1 = cx - l   ,  y1 = cy - t#                     x2 = cx + r   ,  y2 = cy + b#    如果 xywh=False ⇒ 返回 xyxybboxes = dist2bbox(pred_dist,                # (B, A, 4)  距离anchor_points,            # (A, 2)     锚点中心xywh=False)               # ← 输出 xyxyreturn bboxes

形状与数据流全览(以 B=2, A=8400, reg_max=16 为例)

步骤张量名形状备注
输入pred_dist(2, 8400, 64)每点 64 通道 = 4×16
view(2, 8400, 4, 16)拆成 4 方向 × 16 bin
softmax(2, 8400, 4, 16)每方向得到概率分布
matmul(2, 8400, 4)期望 → 距离值 (l,t,r,b)
dist2bbox(2, 8400, 4)计算 (x1,y1,x2,y2)

__call__代码如下:

def __call__(self, preds, batch):"""计算一个 batch 的 3 项损失 (box / cls / DFL) 并返回总损失。下面的中文注释**带着具体数字示例**来演示每一步张量维度的变化,方便对 YOLOv8 的内部维度有直观认识。─────────────────────────────── 示例假设 ───────────────────────────────• batch_size = 2                             # 这一 batch 有 2 张图片• 模型输出 3 个特征层 feats:┌──feature0: (B, no, 80, 80)  → 80×80 = 6400  点├──feature1: (B, no, 40, 40)  → 40×40 = 1600  点└──feature2: (B, no, 20, 20)  → 20×20 =  400  点⇒ total_points = 6400 + 1600 + 400 = 8400• nc = 80                                    # COCO 数据集 80 类• reg_max = 16                               # DFL 每方向 16 bins⇒ no = nc + 4*reg_max = 80 + 64 = 144所以:feature0.shape = (2, 144, 80, 80)feature1.shape = (2, 144, 40, 40)feature2.shape = (2, 144, 20, 20)───────────────────────────────────────────────────────────────────────"""# 1⃣ 初始化损失向量 [box, cls, dfl]loss = torch.zeros(3, device=self.device)# 2⃣ 拿到特征层列表 feats#    如果模型还输出了 mask/proto 等信息时,preds = (proto, feats)feats = preds[1] if isinstance(preds, tuple) else preds# 3⃣ 把 3 个特征层拼接成一个大张量,#    同时把通道 no 拆分为回归分布(pred_distri)和分类分数(pred_scores)# --- 3.1 先  view  ---#     把 (B, no, H, W) → (B, no, H*W)#     以 feature0 为例: (2, 144, 80, 80) → (2, 144, 6400)#     三层分别得到 (2,144,6400), (2,144,1600), (2,144,400)# --- 3.2  再  cat  ---#     按 dim=2 把三个张量拼成 (2, 144, 8400)cat_out = torch.cat([xi.view(feats[0].shape[0], self.no, -1)   # -1 相当于 H*Wfor xi in feats],dim=2)  # ⇒ shape = (2, 144, 8400)# --- 3.3  split  ---#     在 dim=1(通道维) 按 (64, 80) 切两块#     · pred_distri : (2,  64, 8400)   ← 4*reg_max#     · pred_scores : (2,  80, 8400)   ← ncpred_distri, pred_scores = cat_out.split((self.reg_max * 4, self.nc), dim=1)# 4⃣ 为后续计算把维度换成 (B, 8400, C)#    permute(0,2,1) 即把通道维和点数维互换pred_scores = pred_scores.permute(0, 2, 1).contiguous()   # (2, 8400, 80)pred_distri = pred_distri.permute(0, 2, 1).contiguous()   # (2, 8400, 64)# 5⃣ 记录一些常用量batch_size = pred_scores.shape[0]     # =2dtype       = pred_scores.dtype# 6⃣ 计算此层对应的 **原图尺寸** (img_h, img_w)#    假设最小特征层 stride=8, feats[0].shape[2:]=(80,80)imgsz = torch.tensor(feats[0].shape[2:], device=self.device, dtype=dtype) \* self.stride[0]              # (80,80)*8 → (640,640)# 7⃣ 生成 anchor_points 和 stride_tensor#    anchor_points.shape   = (8400, 2)   ← (cx, cy) in feature space#    stride_tensor.shape   = (8400, 1)   ← 每点对应的 strideanchor_points, stride_tensor = make_anchors(feats, self.stride, 0.5)# 8⃣ 组装并预处理 targets#    原始 batch 字典里是分散的,拼成 (nt,6) → preprocess → (B, M, 5)targets = torch.cat((batch["batch_idx"].view(-1, 1),   # (nt,1)batch["cls"].view(-1, 1),         # (nt,1)batch["bboxes"]), 1).to(self.device)targets = self.preprocess(targets,batch_size,scale_tensor=imgsz[[1, 0, 1, 0]])  # (2, M, 5)gt_labels, gt_bboxes = targets.split((1, 4), 2)  # (2,M,1)  (2,M,4)mask_gt = gt_bboxes.sum(2, keepdim=True).gt_(0)  # (2,M,1)  True/False# 9⃣ 把离散分布解码成 bbox 距离 → bbox 坐标pred_bboxes = self.bbox_decode(anchor_points, pred_distri)#    shape = (2, 8400, 4)  [x1,y1,x2,y2]  特征图尺度# 🔟 assigner 做正负样本匹配,得到:#    target_bboxes   (2, 8400, 4)    给回归分支一个要对齐到的坐标真值。#    target_scores   (2, 8400, 80)   给分类分支一个带权重的一热标签矩阵,权重代表匹配质量,质量越高分类损失权重越大。#    fg_mask         (2, 8400)       告诉回归分支哪些 anchor 参与回归损失。_, target_bboxes, target_scores, fg_mask, _ = self.assigner(pred_scores.detach().sigmoid(),                         # (2,8400,80)(pred_bboxes.detach() * stride_tensor).type(gt_bboxes.dtype),anchor_points * stride_tensor,gt_labels, gt_bboxes, mask_gt)# 11⃣ 计算分类损失 (BCE)target_scores_sum = max(target_scores.sum(), 1)loss[1] = self.bce(pred_scores, target_scores.to(dtype)).sum() / target_scores_sum# 12⃣ 计算回归 / DFL 损失(仅正样本)if fg_mask.sum():target_bboxes /= stride_tensor                         # 按 stride 还原到特征尺度loss[0], loss[2] = self.bbox_loss(pred_distri, pred_bboxes, anchor_points,target_bboxes, target_scores, target_scores_sum, fg_mask)# 13⃣ 乘以超参权重loss[0] *= self.hyp.box   # IoU / bboxloss[1] *= self.hyp.cls   # 分类loss[2] *= self.hyp.dfl   # DFL# 14⃣ 返回total_loss = loss.sum() * batch_size   # 注意 *B 做到“每张图总损失求和”return total_loss, loss.detach()       # loss = [box, cls, dfl]

上述代码中的BboxLoss代码如下所示:

class BboxLoss(nn.Module):"""负责计算 YOLOv8 “边界框回归” 两项损失:① IoU 损失(这里用 CIoU)② DFL(Distribution Focal Loss)—— 只有 reg_max > 1 时才启用下文所有注释都混入了 **示例数字**,沿用前面提到的场景:batch_size = 2total_points = 8400reg_max = 16"""def __init__(self, reg_max: int = 16):super().__init__()# 如果 reg_max>1 才需要 DFLoss,否则就说明只做普通 L1/IoU,不用分布回归self.dfl_loss = DFLoss(reg_max) if reg_max > 1 else Nonedef forward(self,pred_dist,          # (B, N, 4*reg_max)   ← 例: (2, 8400, 64)pred_bboxes,        # (B, N, 4)           ← 例: (2, 8400, 4)  [x1,y1,x2,y2]  (特征尺度)anchor_points,      # (N, 2)              ← (8400, 2)  每个 anchor 的中心点 (cx,cy)target_bboxes,      # (B, N, 4)           ← 跟 pred_bboxes 同形target_scores,      # (B, N, nc)          ← 用于加权的匹配质量分数target_scores_sum,  # 标量               ← 正样本权重之和, 用作归一化fg_mask             # (B, N) (Bool)       ← True 表示该 anchor 是正样本):"""返回:loss_iou : IoU 回归损失loss_dfl : DFL 分布回归损失 (若 reg_max==1 则为 0)"""# ─────────────────────────────────────────────────────────# 1⃣  IoU 损失# ─────────────────────────────────────────────────────────#   • target_scores.sum(-1)  → (B, N)     取每个 anchor 的匹配质量总分#   • [fg_mask]             → (num_fg)   只保留正样本#   • .unsqueeze(-1)        → (num_fg,1) 方便后面乘法广播#       例: 假设 num_fg = 900weight = target_scores.sum(-1)[fg_mask].unsqueeze(-1)  # (900, 1)# 计算预测框 和 真值框 的 CIoU#   bbox_iou 返回形状 (num_fg, 1)iou = bbox_iou(pred_bboxes[fg_mask],       # (900,4)target_bboxes[fg_mask],     # (900,4)xywh=False,CIoU=True)# IoU 损失 = (1 - IoU) * 权重#   形状仍 (900,1),再 sum → 标量,再除 target_scores_sum 归一化loss_iou = ((1.0 - iou) * weight).sum() / target_scores_sum# ─────────────────────────────────────────────────────────# 2⃣  DFL 损失  (仅当 reg_max>1 时启用)# ─────────────────────────────────────────────────────────if self.dfl_loss:# 2.1  GT bbox → “距离格式” 并离散到 0~(reg_max-1) 之间#      target_ltrb.shape = (B, N, 4) ,数值为整数 bin#      例如一个方向的真值距离 = 5.8 像素,当 reg_max-1 = 15,#      则 target_ltrb ≈ round(5.8) = 6target_ltrb = bbox2dist(anchor_points,                      # (N,2)target_bboxes,                     # (B,N,4)self.dfl_loss.reg_max - 1          # max_dis=15)# 2.2  把 pred_dist 和 target_ltrb 先按 fg_mask 过滤,再 reshape#      pred_dist[fg_mask] : (900, 64)#      .view(-1, reg_max) : (900*4, 16)#        ↳ 900 个正样本 × 4方向 = 3600 行# pred_flat是模型预测值:每个正样本 4 个方向 (左、上、右、下) 的离散距离概率分布,已铺平成shape = (正样本数 × 4, reg_max)。# target_flat是监督标签:对应方向的 真实距离落在哪个 bin 的整数编号shape = (正样本数 × 4,),取值范围 0 ~ reg_max-1。pred_flat = pred_dist[fg_mask].view(-1, self.dfl_loss.reg_max)   # (3600,16)target_flat = target_ltrb[fg_mask].view(-1)                      # (3600,)# 2.3  计算 DFLoss (正样本 & 逐方向)#      返回形状 (3600,)loss_dfl_each = self.dfl_loss(pred_flat, target_flat)# 2.4  给每个正样本方向乘它所属 anchor 的 weight#      weight.shape = (900,1) → view(-1,1) 再 repeat → (3600,1)loss_dfl = loss_dfl_each.unsqueeze(-1) * weight.repeat_interleave(4, dim=0)# 2.5  汇总并归一化loss_dfl = loss_dfl.sum() / target_scores_sumelse:# 不使用 DFL 时,返回 0(放到正确设备上确保 dtype 一致)loss_dfl = torch.tensor(0.0, device=pred_dist.device)return loss_iou, loss_dfl

在BboxLoss中,还包含了边界框交并比(bbox IoU)的计算,这是我们修改损失函数代码时需要注意的部分。

def bbox_iou(box1, box2,xywh: bool = True,GIoU: bool = False,DIoU: bool = False,CIoU: bool = False,eps: float = 1e-7):"""计算两组边界框的 IoU / GIoU / DIoU / CIoU。───────────────────────────── 示例假设 ─────────────────────────────• 在 bbox 回归损失里调用本函数:pred_bboxes[fg_mask]  →  (900, 4)     # 正样本预测框target_bboxes[fg_mask]→  (900, 4)     # 对应 GT 框• 也支持更高维情况:box1.shape = (B, N, 1, 4)   &box2.shape = (B, 1, M, 4)内部依靠 **逐元素广播** 同样能算出 (B,N,M) 的 IoU 矩阵。─────────────────────────────────────────────────────────────────────"""# ① 先把输入拆成 4 个分量;如果 xywh=True,需要把中心点+长宽 转成 左上+右下# -----------------------------------------------------------------if xywh:                      # (x, y, w, h) → (x1,y1,x2,y2)# .chunk(4, -1) 以最后一个维度(-1)平均切 4 份#    得到 (X, Y, W, H),形状与 box1/box2 除最后维外完全一致(x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1)# ↙ 半宽、高  (保持与原维度一致,便于广播)w1h1_2 = w1 / 2, h1 / 2w2h2_2 = w2 / 2, h2 / 2# ↙ 得到四角坐标b1_x1, b1_x2, b1_y1, b1_y2 = x1 - w1h1_2[0], x1 + w1h1_2[0], y1 - w1h1_2[1], y1 + w1h1_2[1]b2_x1, b2_x2, b2_y1, b2_y2 = x2 - w2h2_2[0], x2 + w2h2_2[0], y2 - w2h2_2[1], y2 + w2h2_2[1]# 形状依旧与原输入保持一致:例如 (900,4) 拆完后各分量 (900,1)else:                         # 已经是 (x1,y1,x2,y2) 直接拆b1_x1, b1_y1, b1_x2, b1_y2 = box1.chunk(4, -1)b2_x1, b2_y1, b2_x2, b2_y2 = box2.chunk(4, -1)# 额外算宽高,用于 union (w*h)w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + epsw2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + epsif xywh:                      # xywh 模式下 w1 h1 还没算w1, h1 = w1, h1           # 已在上方得到w2, h2 = w2, h2# ② 交集面积# -----------------------------------------------------------------# min/max 会自动广播到同形状,再 * 相乘得到交集面积 tensorinter = (b1_x2.minimum(b2_x2) - b1_x1.maximum(b2_x1)).clamp_(0) * \(b1_y2.minimum(b2_y2) - b1_y1.maximum(b2_y1)).clamp_(0)#   例:正样本情景 → inter.shape = (900,1)# ③ 并集面积  (w1*h1 + w2*h2 - inter)union = w1 * h1 + w2 * h2 - inter + eps# ④ IoUiou = inter / union           # shape 与 inter 相同# ⑤ 根据标志计算 G/DI/CIoUif CIoU or DIoU or GIoU:# ▸ 最小包围框 (convex) 宽高cw = b1_x2.maximum(b2_x2) - b1_x1.minimum(b2_x1)ch = b1_y2.maximum(b2_y2) - b1_y1.minimum(b2_y1)if CIoU or DIoU:          # 距离 IoU / 完整 IoUc2   = cw.pow(2) + ch.pow(2) + eps          # 对角线平方rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2).pow(2) +(b2_y1 + b2_y2 - b1_y1 - b1_y2).pow(2)) / 4  # 中心距平方if CIoU:# 额外的长宽比惩罚项v = (4 / math.pi**2) * ((w2 / h2).atan() - (w1 / h1).atan()).pow(2)with torch.no_grad():alpha = v / (v - iou + (1 + eps))return iou - (rho2 / c2 + v * alpha)    # CIoUelse:return iou - rho2 / c2                  # DIoUelse:  # GIoUc_area = cw * ch + epsreturn iou - (c_area - union) / c_area      # GIoUreturn iou  # 纯 IoU 模式

900 个正样本 (900,4) 情景如下面示意图所示:

box1 (900,4) ─┬─chunk─┬─> b1_x1 (900,1)│       ├─> b1_y1 (900,1)│       ├─> b1_x2 (900,1)│       └─> b1_y2 (900,1)
box2 (900,4) ─┘               ↑↑自动广播 + clamp → inter (900,1)
inter / union --------------→ iou   (900,1)  ←【返回】

如果传入形如 (B, N, 1, 4) × (B, 1, M, 4) 的高维张量,上面所有逐元素运算依靠 PyTorch 广播机制 依旧能得到 (B, N, M) 的 IoU 矩阵。这样就既支持“一对一”回归损失,也支持“一对多” NMS / 匹配场景。

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

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

相关文章

微信小程序调用蓝牙API “wx.writeBLECharacteristicValue()“ 报 errCode: 10008 的解决方案

1、问题现象 问题:在开发微信小程序蓝牙通信功能时,常常会遇到莫名其妙的错误,查阅官方文档可能也无法找到答案。如在写入蓝牙数据时,报了这样的错误: {errno: 1500104, errCode: 10008, errMsg: "writeBLECharacteristicValue:fail:system error, status: UNKNOW…

软考 UML中的 用例图 的泛化 包含 扩展 关系

用例图的泛化、扩展和包含 - ^_^肥仔John - 博客园

MyBatis-Plus的自带分页方法生成的SQL失败:The error occurred while setting parameters

1、error描述 数据库是postgres,Java使用mybatis-plus的分页功能,生成的分页SQL不能正常运行。 "msg": "nested exception is org.apache.ibatis.exceptions.PersistenceException: Error querying database. Cause: com.baomidou.my…

Redis从入门到实战 - 原理篇

一、数据结构 1. 动态字符串SDS 我们都知道Redis中保存的key是字符串,value往往是字符串或者字符串的集合。可见字符串是Redis中最常用的一种数据结构。 不过Redis没有直接使用C语言中的字符串,因为C语言字符串存在很多问题: 获取字符串长…

人形机器人通过观看视频学习人类动作的技术可行性与前景展望

摘要 本文深入探讨人形机器人通过观看视频学习人类动作这一技术路线的正确性与深远潜力。首先阐述该技术路线在模仿人类学习过程方面的优势,包括对人类动作、表情、发音及情感模仿的可行性与实现路径。接着从技术原理、大数据训练基础、与人类学习速度对比等角度论证…

高分辨率北半球多年冻土数据集(2000-2016)

关键数据集分类&#xff1a;冰冻圈数据集时间分辨率&#xff1a;10 year < x < 100 year空间分辨率&#xff1a;1km - 10km共享方式&#xff1a;开放获取数据大小&#xff1a;339.79 MB数据时间范围&#xff1a;2000-01-01 — 2016-12-31元数据更新时间&#xff1a;2022-…

零售智能执行大模型架构设计:从空间建模到上下文推理,再到智能Agent

零售智能执行大模型架构设计&#xff1a;从空间建模到上下文推理&#xff0c;再到智能Agent &#x1f9e0; 引言&#xff1a;零售智能执行的再定义 在传统零售执行中&#xff0c;面对SKU数量庞杂、货架布置多变、陈列标准难以落地等问题&#xff0c;靠人力巡检或轻量识别模型已…

RIP 协议实验全记录:从配置到问题解决

在网络世界中&#xff0c;路由协议就像是交通指挥员&#xff0c;引导数据在不同网络之间顺畅传输。今天&#xff0c;我们就来深入探索 RIP&#xff08;Routing Information Protocol&#xff09;协议&#xff0c;通过一系列实验&#xff0c;揭开它的神秘面纱&#xff01; 一、搭…

基于SpringBoot的网上租赁系统设计与实现

项目简介 本项目是基于 Spring Boot Vue 技术栈开发的 网上租赁系统。该系统通过前后端分离的架构&#xff0c;提供用户和管理员两种角色的操作权限&#xff0c;方便用户进行商品租赁、订单管理、信息查询等操作&#xff0c;同时也为管理员提供了商品管理、用户管理、订单管理…

uni-app学习笔记六-vue3响应式基础

一.使用ref定义响应式变量 在组合式 API 中&#xff0c;推荐使用 ref() 函数来声明响应式状态&#xff0c;ref() 接收参数&#xff0c;并将其包裹在一个带有 .value 属性的 ref 对象中返回 示例代码&#xff1a; <template> <view>{{ num1 }}</view><vi…

CUDA 性能优化 | 共享内存机制 / 向量化访存策略

注&#xff1a;本文为“CUDA 性能优化”相关文章合辑。 图片清晰度受引文原图所限。 重传部分 CSDN 转储失败图片。 略作重排&#xff0c;未整理去重。 如有内容异常&#xff0c;请看原文。 Shared Memory 上的广播机制和 Bank Conflict 到底是怎么回事&#xff1f; 发表于 2…

NVMe高速传输之摆脱XDMA设计1

NVMe IP放弃XDMA原因 选用XDMA做NVMe IP的关键传输模块&#xff0c;可以加速IP的设计&#xff0c;但是XDMA对于开发者来说&#xff0c;还是不方便&#xff0c;原因是它就象一个黑匣子&#xff0c;调试也非一番周折&#xff0c;尤其是后面PCIe4.0升级。 因此决定直接采用PCIe设…

企业级单元测试流程

企业级的单元测试流程不仅是简单编写测试用例&#xff0c;而是一整套系统化、自动化、可维护、可度量的工程实践&#xff0c;贯穿从代码编写到上线部署的全生命周期。下面是一个尽可能完善的 企业级单元测试流程设计方案&#xff0c;适用于 Java 生态&#xff08;JUnit Mockit…

关于vector、queue、list哪边是front、哪边是back,增加、删除元素操作

容器的 front、back 及操作方向 1.1vector&#xff08;动态数组&#xff09; 结构&#xff1a;连续内存块&#xff0c;支持快速随机访问。 操作方向&#xff1a; front&#xff1a;第一个元素&#xff08;索引 0&#xff09;。 back&#xff1a;最后一个元素&#xff08;索引…

嵌入式之汇编程序示例

目录 经典例子:求阶乘 一:数组求和 二:数据压栈退栈 三:函数嵌套调用 经典例子:求阶乘 知识点: BGT 用于判断 r2 > r0&#xff0c;确保循环执行 恰好 r0 次。BNE 用于判断 r2 ≠ r0&#xff0c;会导致循环多执行一次&#xff0c;得到错误结果。 这就是阶乘代码中必须…

【MySQL】第九弹——索引(下)

文章目录 &#x1f30f;索引(上)回顾&#x1f30f;使用索引&#x1fa90;自动创建索引&#x1fa90;手动创建索引&#x1f680;主键索引&#x1f680;普通索引&#x1f680;唯一索引&#x1f680;复合索引 &#x1fa90;查看索引&#x1fa90;删除索引&#x1f680;删除主键索引…

毕业论文格式(Word)

目录 Word目录怎么自动生成&#xff1f;快速生成试试这3个方法&#xff01; - 知乎https://zhuanlan.zhihu.com/p/692056836目录生成需要先设置标题样式&#xff0c;这个不仅是目录生成需要&#xff0c;和后续的图表也有关系。 最好不要自己创建新的样式&#xff0c;而是在现有…

PostGIS实现栅格数据转二进制应用实践【ST_AsBinary】

ST_AsBinary解析与应用实践&#xff08;同ST_AsWKB&#xff09; 一、函数概述二、核心参数解析三、典型用法示例四、Out-DB 波段处理机制五、二进制格式与其他格式的转换六、性能与存储优化七、应用场景八、注意事项九、扩展应用&#xff1a;基于Python Web的栅格二进制数据的…

线性回归原理推导与应用(七):逻辑回归原理与公式推导

逻辑回归是一种分类算法&#xff0c;常用于二分类&#xff0c;也就是得出的结果为是和不是&#xff0c;例如通过各种因素判断一个人是否生病&#xff0c;信用卡是否违约等。逻辑回归在社会和自然科学中应用非常广泛&#xff0c; 前置知识 线性回归 逻辑回归的底层方法就是线…

Fastrace:Rust 中分布式追踪的现代化方案

原文链接&#xff1a;Fastrace: A Modern Approach to Distributed Tracing in Rust | FastLabs / Blog 摘要 在微服务架构中&#xff0c;分布式追踪对于理解应用程序的行为至关重要。虽然 tokio-rs/tracing 在 Rust 中被广泛使用&#xff0c;但它存在一些显著的挑战&#xf…