当前位置: 首页 > news >正文

目标检测中的标签分配算法总结

目标检测中的标签分配算法是训练过程中的一个核心环节,它决定了如何将标注好的真实目标框分配给模型预测出来的候选框(Anchor Boxes或Points),从而为这些候选框提供监督信号(正样本、负样本、忽略样本)。它的质量直接影响模型的学习效率和最终性能。

简单来说,标签分配要解决的关键问题是:“哪些预测框应该负责学习哪些真实目标?”

一、为什么标签分配如此重要?

1.定义学习目标: 它直接告诉模型哪些预测应该被优化为正样本(靠近真实目标),哪些应该被优化为负样本(背景),哪些可以忽略。

2.影响训练稳定性与效率: 好的分配策略能提供高质量、平衡的正负样本,加速模型收敛并提升最终精度。差的策略可能导致训练不稳定、收敛慢或性能低下。

3.解决模糊性: 一个真实目标可能被多个候选框覆盖(重叠度高),一个候选框也可能覆盖多个真实目标(重叠区域)。标签分配需要解决这种模糊性,明确责任归属。

4.处理正负样本不平衡: 背景(负样本)通常远多于前景(正样本)。标签分配策略需要有效地筛选出有代表性的正负样本,避免模型被海量简单负样本淹没。

二、标签分配算法的演进(从简单到复杂)

2.1  基于固定规则的分配(早期方法,静态分配)

这类方法主要依赖预先定义的几何规则(如 IoU 阈值)进行分配,规则在训练前固定不变。
基于 IoU 阈值的分配:
正样本: 与任何一个真实目标框的 IoU 大于某个高阈值(如 0.7)的候选框。
负样本: 与所有真实目标框的 IoU 都小于某个低阈值(如 0.3)的候选框。
忽略样本: IoU 介于高低阈值之间(如 0.3-0.7)的候选框,不参与损失计算。
Max-IoU 分配: 确保每个真实目标框至少有一个候选框负责(即使 IoU 低于阈值)。将每个真实目标框分配给与其 IoU 最大的那个候选框(作为正样本),同时也会应用 IoU 阈值来分配其他正/负样本。
中心采样: 在无锚框方法中常见(如 FCOS)。对于每个真实目标框,只有落在该框中心附近一定区域内的点(或特征图上的位置)才被认为是正样本候选点。
优点: 简单、直观、易于实现。
缺点:
超参数敏感: IoU 阈值的选择对性能影响很大,需要精心调参。
上下文信息缺失: 仅依赖几何信息,没有考虑分类置信度、定位精度等模型预测本身的信息。
模糊性处理粗糙: 对于重叠目标或边界框模糊的情况,分配可能不合理。
灵活性差: 固定的规则无法适应不同目标大小、形状、场景复杂度的变化。

2.2  基于统计的分配(改进静态分配)

尝试利用训练数据的统计特性来自适应设置分配参数,减少人工调参。
ATSS:
核心思想:为每个真实目标框自适应地计算一个 IoU 阈值。
步骤:
1. 对每个真实目标框 gt,计算它与所有候选框的 IoU。
2. 计算这些 IoU 的均值 m_gt 和标准差 v_gt
3. 该 gt 的自适应 IoU 阈值 t_gt = m_gt + v_gt
4. 选择与 gt 的 IoU >= t_gt 的候选框作为正样本候选。
5. 限制每个候选框最多分配给一个 gt(通常选择 IoU 最大的那个 gt)。
优点:减少了人工设定 IoU 阈值的影响,对不同数据集和检测器鲁棒性更好。
缺点:仍然是静态分配(在训练前确定),没有利用模型预测信息。

2.3  动态分配(当前主流)

这类方法在训练过程中,利用模型当前的预测结果(如分类得分、预测框位置)来指导标签分配,使分配策略能够随着模型的优化而动态调整。这是目前研究最活跃、效果最好的方向。
核心思想: 不再仅依赖固定的几何规则,而是结合模型预测的质量(分类置信度、定位精度)来评估候选框的“匹配程度”,选择最合适的正样本。
代表方法:
(1)PAA:假设正样本的联合损失(分类损失 + 定位损失)服从高斯分布,负样本的损失服从另一个高斯分布。在训练过程中,基于当前模型预测计算所有样本的联合损失。使用EM算法拟合这两个高斯分布。根据拟合出的概率密度函数,计算每个样本属于正样本分布的概率。选择属于正样本分布概率最高的 K 个样本作为正样本(K 通常基于真实目标框的数量自适应确定)。
优点:概率建模更合理,自动区分正负样本,减少超参数。
(2)OTA:将标签分配视为一个最优传输问题
源: 所有真实目标框(包含背景类别)。
目标: 所有模型预测的候选框。
代价矩阵: 每个 gt 分配给每个候选框的代价(Cost),通常是分类损失(如 Focal Loss)和定位损失(如 GIoU Loss)的加权和。
求解这个最优传输问题,得到一个全局最优的分配方案(每个 gt 分配多少正样本,分配给哪些候选框;背景也视为一个特殊的 gt 分配给负样本)。
优点:从全局视角考虑分配问题,同时优化正负样本分配,更全面地考虑样本间的关联。
(3)TOOD:
核心观察:分类预测得分最高的点不一定定位最准,定位最准的点分类得分不一定最高。分类和定位任务存在不对齐
提出任务对齐学习
任务对齐度量: 设计一个同时反映分类置信度和定位精度的分数(如 s * u,其中 s 是分类得分,u 是预测框与 gt 的 IoU)。
任务对齐分配器: 基于任务对齐度量为每个 gt 选择 Top-K 个对齐分数最高的样本作为正样本。
任务对齐损失: 在损失函数中,增加对齐分数高的正样本的权重,引导模型学习对齐的预测。
优点:显式地促进分类和定位任务的一致性,提升检测头性能。
(4)SimOTA:
是 OTA 的一个高效简化版本。
核心改进:为每个真实目标框 gt 动态选择候选样本范围(通常是 IoU 前 Top-K 的候选框),只在这个小范围内计算传输代价并求解,极大降低计算复杂度。
优点:保持了 OTA 的全局视角和优越性能,同时计算效率大幅提升,成为许多高效检测器(如 YOLOX)的首选分配策略。

动态分配的优势:
更高质量的正样本: 选择分类和定位都表现较好的候选框作为正样本,提供更可靠的学习信号。
自适应性强: 分配策略能随着模型能力的提升而动态调整,适应训练的不同阶段。
缓解任务不对齐: 如 TOOD 所示,可以显式促进分类和定位的一致性。
性能提升: 通常能带来显著的检测精度提升,尤其是在复杂场景和小目标检测上。
挑战:
计算开销: 动态计算分配方案会增加训练时间(虽然 SimOTA 等做了优化)。
实现复杂性: 算法实现比静态规则复杂。

2.4  YOLOv8 标签匹配算法 TaskAlignedAssigner 原理详解

TaskAlignedAssigner 是 YOLOv8 的核心标签匹配算法,它通过任务对齐(Task Alignment) 机制动态分配正负样本,同时优化分类和回归任务。

(1)核心思想:任务对齐度量(Task-Aligned Metric)
  • 度量公式
    \text{align\_metric} = (\text{bbox\_scores}^{\alpha}) \times (\text{IoU}^{\beta})
    其中:

    • bbox_scores:预测框对真实类别的分类置信度

    • overlaps:预测框与真实框的 CIoU 值

    • α, β:超参数(默认 α=1.0β=6.0),控制分类与回归的权重

  • 物理意义
    同时衡量 分类质量(预测类别是否正确)和 回归质量(边界框定位是否精准),实现分类与回归的任务对齐。

(2)正样本分配流程
步骤 1:初步筛选候选框
  • 锚点必须在真实框内:通过 select_candidates_in_gts 筛选中心点落在真实框内的锚点(几何约束)。

  • Top-K 任务对齐选择:对每个真实框,选择任务对齐度量最高的前 topk(默认 13)个预测框作为候选正样本。

步骤 2:解决多对一冲突
  • 冲突场景:一个预测框被分配给多个真实框

  • 解决方案select_highest_overlaps 选择与预测框 IoU 最高的真实框,确保一一匹配。

步骤 3:生成训练目标
  • 目标标签target_labels取匹配真实框的类别标签。

  • 目标框target_bboxes取匹配真实框的坐标。

  • 目标分数target_scores
    动态加权target_scores = one_hot_label * norm_align_metric
    其中 norm_align_metric 是归一化的对齐度量,强化高质量样本的损失权重。

(3)关键技术细节
  1. 动态样本权重:正样本的目标分数通过 align_metric 加权:target_scores = target_scores * norm_align_metric高质量样本(分类准 + 定位准)在训练中获得更大权重。

  2. 多真实框处理:使用 mask_gt 区分有效/无效(填充的)真实框。仅对有效真实框分配正样本。

  3. 边界框重合度计算:采用 CIoU(Complete-IoU)作为重合度度量,考虑中心点距离与宽高比:overlaps = bbox_iou(..., CIoU=True)

(4)与传统方法的区别
传统方法TaskAlignedAssigner
仅依赖 IoU 分配样本联合优化分类+回归质量
静态分配规则动态加权高质量样本
分类与回归解耦任务对齐提升一致性
(5)优势总结
  1. 任务协同:避免分类与回归优化目标冲突。

  2. 样本质量感知:自动聚焦高质量正样本。

  3. 减少超参数依赖:动态调整取代手工阈值。

  4. 兼容密集预测:适用于 Anchor-Based/Free 架构。

通过任务对齐机制,YOLOv8 显著提升了小目标检测和复杂场景的鲁棒性,成为其高性能的关键设计之一。

代码实现:

import torch # pytorch的版本最低为 1.10
import torch.nn as nn
import torch.nn.functional as F
import mathdef select_candidates_in_gts(xy_centers, gt_bboxes, eps=1e-9):"""select the positive anchor center in gtArgs:xy_centers (Tensor): shape(h*w, 2)gt_bboxes (Tensor): shape(b, n_boxes, 4)Return:(Tensor): shape(b, n_boxes, h*w)"""n_anchors = xy_centers.shape[0]bs, n_boxes, _ = gt_bboxes.shapelt, rb = gt_bboxes.view(-1, 1, 4).chunk(2, 2)  # left-top, right-bottombbox_deltas = torch.cat((xy_centers[None] - lt, rb - xy_centers[None]), dim=2).view(bs, n_boxes, n_anchors, -1)# return (bbox_deltas.min(3)[0] > eps).to(gt_bboxes.dtype)# torch.amin(input, dim, keepdim=False, *, out=None) → Tensor 返回给定维度 dim 中 input 张量的每个切片的最小值。return bbox_deltas.amin(3).gt_(eps)def select_highest_overlaps(mask_pos, overlaps, n_max_boxes):"""if an anchor box is assigned to multiple gts,the one with the highest iou will be selected.Args:mask_pos (Tensor): shape(b, n_max_boxes, h*w)overlaps (Tensor): shape(b, n_max_boxes, h*w)Return:target_gt_idx (Tensor): shape(b, h*w)fg_mask (Tensor): shape(b, h*w)mask_pos (Tensor): shape(b, n_max_boxes, h*w)"""# 一个预测框匹配真实框的个数# (b, n_max_boxes, h*w) -> (b, h*w)fg_mask = mask_pos.sum(-2)# 如果一个预测框匹配真实框的个数 > 1if fg_mask.max() > 1:  # one anchor is assigned to multiple gt_bboxes# 一个预测框匹配多个真实框的位置mask_multi_gts = (fg_mask.unsqueeze(1) > 1).repeat([1, n_max_boxes, 1])  # (b, n_max_boxes, h*w)# 与预测框IoU值最高的真实框的索引max_overlaps_idx = overlaps.argmax(1)  # (b, h*w)# 进行one-hot编码,与预测框IoU值最高的真实框的位置为 1 is_max_overlaps = F.one_hot(max_overlaps_idx, n_max_boxes)  # (b, h*w, n_max_boxes)is_max_overlaps = is_max_overlaps.permute(0, 2, 1).to(overlaps.dtype)  # (b, n_max_boxes, h*w)mask_pos = torch.where(mask_multi_gts, is_max_overlaps, mask_pos)  # (b, n_max_boxes, h*w)# 正样本的maskfg_mask = mask_pos.sum(-2)# 每个正样本与之匹配真实框的索引# find each grid serve which gt(index)target_gt_idx = mask_pos.argmax(-2)  # (b, h*w)return target_gt_idx, fg_mask, mask_posclass TaskAlignedAssigner(nn.Module):def __init__(self, topk=13, num_classes=80, alpha=1.0, beta=6.0, eps=1e-9):super().__init__()self.topk = topk # 每个gt box最多选择topk个候选框作为正样本self.num_classes = num_classesself.bg_idx = num_classesself.alpha = alphaself.beta = betaself.eps = eps@torch.no_grad()def forward(self, pd_scores, pd_bboxes, anc_points, gt_labels, gt_bboxes, mask_gt):"""This code referenced tohttps://github.com/Nioolek/PPYOLOE_pytorch/blob/master/ppyoloe/assigner/tal_assigner.pyArgs:pd_scores (Tensor): shape(bs, num_total_anchors, num_classes)pd_bboxes (Tensor): shape(bs, num_total_anchors, 4)anc_points (Tensor): shape(num_total_anchors, 2)gt_labels (Tensor): shape(bs, n_max_boxes, 1)gt_bboxes (Tensor): shape(bs, n_max_boxes, 4)mask_gt (Tensor): shape(bs, n_max_boxes, 1)Returns:target_labels (Tensor): shape(bs, num_total_anchors)target_bboxes (Tensor): shape(bs, num_total_anchors, 4)target_scores (Tensor): shape(bs, num_total_anchors, num_classes)fg_mask (Tensor): shape(bs, num_total_anchors)"""# batch size 的大小self.bs = pd_scores.size(0)# 每个图片真实框个数不同,按图片中真实框最大的个数进行补零对齐。# n_max_boxes:最大真实框的个数self.n_max_boxes = gt_bboxes.size(1)# 如果不存在真实框,直接返回结果if self.n_max_boxes == 0:device = gt_bboxes.devicereturn (torch.full_like(pd_scores[..., 0], self.bg_idx).to(device), torch.zeros_like(pd_bboxes).to(device),torch.zeros_like(pd_scores).to(device), torch.zeros_like(pd_scores[..., 0]).to(device),torch.zeros_like(pd_scores[..., 0]).to(device))# 真实框的mask,正负样本的匹配程度,正负样本的IoU值mask_pos, align_metric, overlaps = self.get_pos_mask(pd_scores, pd_bboxes, gt_labels, gt_bboxes, anc_points,mask_gt)# 对一个正样本匹配多个真实框的情况进行调整target_gt_idx, fg_mask, mask_pos = select_highest_overlaps(mask_pos, overlaps, self.n_max_boxes)# assigned target target_labels, target_bboxes, target_scores = self.get_targets(gt_labels, gt_bboxes, target_gt_idx, fg_mask)# normalizealign_metric *= mask_pospos_align_metrics = align_metric.amax(axis=-1, keepdim=True)  # b, max_num_objpos_overlaps = (overlaps * mask_pos).amax(axis=-1, keepdim=True)  # b, max_num_objnorm_align_metric = (align_metric * pos_overlaps / (pos_align_metrics + self.eps)).amax(-2).unsqueeze(-1)target_scores = target_scores * norm_align_metricreturn target_labels, target_bboxes, target_scores, fg_mask.bool(), target_gt_idxdef get_pos_mask(self, pd_scores, pd_bboxes, gt_labels, gt_bboxes, anc_points, mask_gt):# 预测框和真实框的匹配程度、预测框和真实框的IoU值# get anchor_align metric, (b, max_num_obj, h*w)align_metric, overlaps = self.get_box_metrics(pd_scores, pd_bboxes, gt_labels, gt_bboxes)# 筛选锚点在真实框内的预测框# get in_gts mask, (b, max_num_obj, h*w)mask_in_gts = select_candidates_in_gts(anc_points, gt_bboxes)# get topk_metric mask, (b, max_num_obj, h*w)# 由于为了使每张图片真实框的数量进行对齐,进行了补 0 操作,mask_gt 用于确定有效真实框mask_topk = self.select_topk_candidates(align_metric * mask_in_gts,topk_mask=mask_gt.repeat([1, 1, self.topk]).bool())# merge all mask to a final mask, (b, max_num_obj, h*w)mask_pos = mask_topk * mask_in_gts * mask_gtreturn mask_pos, align_metric, overlapsdef get_box_metrics(self, pd_scores, pd_bboxes, gt_labels, gt_bboxes):ind = torch.zeros([2, self.bs, self.n_max_boxes], dtype=torch.long)  # 2, b, max_num_objind[0] = torch.arange(end=self.bs).view(-1, 1).repeat(1, self.n_max_boxes)  # b, max_num_objind[1] = gt_labels.long().squeeze(-1)  # b, max_num_obj# get the scores of each grid for each gt cls# pd_scores[ind[0]] 将每个batch的生成的预测框的重复 max_num_obj 次 size 大小变为 b*max_num_obj*num_total_anchors*num_classes# bbox_scores 的 size 为 b*max_num_obj*num_total_anchors,ind[1] 对类别进行得分进行选取bbox_scores = pd_scores[ind[0], :, ind[1]]  # b, max_num_obj, num_total_anchors# overlaps 的 size 为 b*max_num_obj*num_total_anchors# gt_bboxes.unsqueeze(2) 的 size 为 b*max_num_obj*1*4# pd_bboxes.unsqueeze(1) 的 size 为 b*1*num_total_anchors*4# bbox_iou 的计算结果 的 size 为 b*max_num_obj*num_total_anchors*1,所以进行维度的压缩overlaps = bbox_iou(gt_bboxes.unsqueeze(2), pd_bboxes.unsqueeze(1), xywh=False,CIoU=True).squeeze(3).clamp(0)# 预测框和真实框的匹配程度 = 预测类别分值**alpha × 预测框和真实框的ciou值**betaalign_metric = bbox_scores.pow(self.alpha) * overlaps.pow(self.beta)return align_metric, overlapsdef select_topk_candidates(self, metrics, largest=True, topk_mask=None):"""Args:metrics: (b, max_num_obj, h*w).topk_mask: (b, max_num_obj, topk) or None"""num_anchors = metrics.shape[-1]  # h*w# 第一个值为排序的数组,第二个值为该数组中获取到的元素在原数组中的位置标号。topk_metrics, topk_idxs = torch.topk(metrics, self.topk, dim=-1, largest=largest)# 如果没有给出有效真实框的mask,通过真实框和预测框的匹配程度确定真实框的有效性if topk_mask is None:topk_mask = (topk_metrics.max(-1, keepdim=True) > self.eps).tile([1, 1, self.topk])# 如果真实框是无效的,将与之匹配的正样本索引值置为 0# (b, max_num_obj, topk)topk_idxs[~topk_mask] = 0# 将索引值进行 one-hot 编码is_in_topk = F.one_hot(topk_idxs, num_anchors).sum(-2)# 过滤无效值# filter invalid bboxesis_in_topk = torch.where(is_in_topk > 1, 0, is_in_topk)return is_in_topk.to(metrics.dtype)def get_targets(self, gt_labels, gt_bboxes, target_gt_idx, fg_mask):"""Args:gt_labels: (b, max_num_obj, 1)gt_bboxes: (b, max_num_obj, 4)target_gt_idx: (b, h*w)fg_mask: (b, h*w)"""# assigned target labels, (b, 1)batch_ind = torch.arange(end=self.bs, dtype=torch.int64, device=gt_labels.device)[..., None]target_gt_idx = target_gt_idx + batch_ind * self.n_max_boxes  # (b, h*w)target_labels = gt_labels.long().flatten()[target_gt_idx]  # (b, h*w)# assigned target boxes, (b, max_num_obj, 4) -> (b, h*w)target_bboxes = gt_bboxes.view(-1, 4)[target_gt_idx]# assigned target scorestarget_labels.clamp(0)target_scores = F.one_hot(target_labels, self.num_classes)  # (b, h*w, 80)fg_scores_mask = fg_mask[:, :, None].repeat(1, 1, self.num_classes)  # (b, h*w, 80)target_scores = torch.where(fg_scores_mask > 0, target_scores, 0)return target_labels, target_bboxes, target_scores# IoU,GIoU,DIoU,CIoU的计算这里不作详细解释
def bbox_iou(box1, box2, xywh=True, GIoU=False, DIoU=False, CIoU=False, eps=1e-7):# Returns Intersection over Union (IoU) of box1(1,4) to box2(n,4)# Get the coordinates of bounding boxesif xywh:  # transform from xywh to xyxy(x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1)w1_, h1_, w2_, h2_ = w1 / 2, h1 / 2, w2 / 2, h2 / 2b1_x1, b1_x2, b1_y1, b1_y2 = x1 - w1_, x1 + w1_, y1 - h1_, y1 + h1_b2_x1, b2_x2, b2_y1, b2_y2 = x2 - w2_, x2 + w2_, y2 - h2_, y2 + h2_else:  # x1, y1, x2, y2 = box1b1_x1, b1_y1, b1_x2, b1_y2 = box1.chunk(4, -1)b2_x1, b2_y1, b2_x2, b2_y2 = box2.chunk(4, -1)w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + epsw2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps# Intersection areainter = (b1_x2.minimum(b2_x2) - b1_x1.maximum(b2_x1)).clamp(0) * \(b1_y2.minimum(b2_y2) - b1_y1.maximum(b2_y1)).clamp(0)# Union Areaunion = w1 * h1 + w2 * h2 - inter + eps# IoUiou = inter / unionif CIoU or DIoU or GIoU:cw = b1_x2.maximum(b2_x2) - b1_x1.minimum(b2_x1)  # convex (smallest enclosing box) widthch = b1_y2.maximum(b2_y2) - b1_y1.minimum(b2_y1)  # convex heightif CIoU or DIoU:  # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1c2 = cw ** 2 + ch ** 2 + eps  # convex diagonal squaredrho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4  # center dist ** 2if CIoU:  # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47v = (4 / math.pi ** 2) * (torch.atan(w2 / h2) - torch.atan(w1 / h1)).pow(2)with torch.no_grad():alpha = v / (v - iou + (1 + eps))return iou - (rho2 / c2 + v * alpha)  # CIoUreturn iou - rho2 / c2  # DIoUc_area = cw * ch + eps  # convex areareturn iou - (c_area - union) / c_area  # GIoU https://arxiv.org/pdf/1902.09630.pdfreturn iou  # IoU

三、标签分配算法的关键要素

无论哪种分配策略,通常都会考虑以下一个或多个要素:

1.几何相似度: IoU(交并比)、GIoU、DIoU、CIoU 等度量预测框与真实框的重叠程度和位置关系。

2.分类置信度: 模型预测该候选框包含某类物体的概率分数。

3.定位精度: 预测框本身的质量(如预测框与真实框的 IoU,或定位损失值)。

4.上下文信息: 周围候选框的预测情况(在全局分配如 OTA 中体现)。

5.样本平衡: 控制正负样本的比例,确保模型能学到有效的判别特征。

四、总结

  • 标签分配是目标检测训练的关键步骤,决定了模型学习什么。

  • 基于固定规则(IoU阈值) -> 基于统计(ATSS) -> 动态分配(PAA, OTA/SimOTA, TOOD) 是标签分配算法的主要演进方向。

  • 动态分配算法是当前主流和前沿,它利用模型预测信息动态地为每个真实目标选择最合适的正样本(通常要求分类置信度高且定位准确),显著提升了检测性能。

  • 选择哪种标签分配算法取决于具体的检测框架(Anchor-based/Anchor-free)、性能要求、计算资源限制等因素。SimOTA 因其高效和强性能被广泛采用,TOOD 在解决任务对齐问题上表现出色。

http://www.xdnf.cn/news/1148617.html

相关文章:

  • 2021 RoboCom 世界机器人开发者大赛-本科组(初赛)解题报告 | 珂学家
  • RS485转Profibus网关助力涡街液体流量计与300PLC高效通讯
  • Python高级数据类型:字典(Dictionary)
  • 模型的评估与选择
  • 基于springboot的考研互助小程序
  • 408数据结构强化(自用)
  • Java中缓存的使用浅讲
  • 【Linux驱动-快速回顾】简单了解一下PinCtrl子系统:设备树如何被接解析与匹配
  • 标准文件和系统文件I/O
  • CSS篇——第一章 六十五项关键技能(上篇)
  • 配置华为交换机接口链路聚合-支持服务器多网卡Bind
  • 解决Maven版本不兼容问题的终极方案
  • 定时器中BDTR死区时间和刹车功能配置
  • 低代码平台ToolJet实战总结
  • Flutter基础(前端教程①③-单例)
  • java内存图
  • 【Linux服务器】-MySQL数据库参数调优
  • Ubuntu 22.04.3 LTS 安装 MySQL
  • Kubernetes常用命令总结
  • 【逻辑回归】MAP - Charting Student Math Misunderstandings
  • 自由学习记录(70)
  • 《汇编语言:基于X86处理器》第8章 高级过程(3)
  • Python 代码生成 LaTeX 数学公式:latexify 参数 parameters
  • 【C语言进阶】结构体
  • Linux常用指令大全
  • 力扣经典算法篇-26-长度最小的子数组(暴力求解法,左右指针法)
  • Java 大视界 -- Java 大数据机器学习模型在自然语言处理中的对话系统多轮交互优化与用户体验提升(351)
  • ROS2 通过相机确定物品坐标位置
  • 在非Spring Boot的Spring项目中使用Lock4j
  • 开疆智能Profinet转ModbusTCP网关连接康耐视InSight相机案例