pascal-5i数据集小样本分割实战:fold划分、episodic采样与避坑指南

简介:Pascal-5i 数据集面向小样本学习方向的研究者与算法工程师,基于经典 PASCAL VOC 的 20 个类别,通过采样策略划分基础类别与新类别,支持 5-way 1-shot 与 5-way 5-shot 场景,用于评估模型在极少样本下的泛化与适应能力。资源包共 6 个文件,压缩后约 151KB,包含 py 读取脚本、ipynb 示例笔记、md 说明文档、txt 依赖清单及 license 等,覆盖数据加载、实验演示与使用说明等环节,便于快速接入原型学习、元学习或知识迁移等方法的训练与评测流程。目前已有 1444 人学习下载。借助配套脚本与示例,读者可省去数据解析与划分的重复工作,直接复现小样本分类实验,并对照 F1 分数、one-vs-rest 混淆矩阵等评估方式分析模型表现,适合作为入门与对比实验的标准化起点。

1. pascal-5i数据集:为什么你的小样本分割模型总在5-way 1-shot上翻车

如果你正在做小样本语义分割,大概率绕不开 pascal-5i 数据集。它不是什么新数据集,而是把 PASCAL VOC 2012 重新切分后得到的一套标准小样本评测协议。很多人第一次跑 5-way 1-shot 时都会遇到同一个反直觉现象:训练集上 mIoU 冲到 60 多,换到测试的 novel 类别直接掉到 20 出头。问题往往不在模型结构,而在你根本没搞清楚 pascal-5i 的 fold 划分逻辑和 episodic 采样规则。这套数据集解决的核心问题,是让不同论文的小样本分割结果能在同一套类别划分和采样协议下比较。它适合两类人:一是刚入门小样本分割、需要一套可复现 baseline 的工程师;二是已经跑通但指标对不上、需要排查数据管线的人。下面从划分逻辑讲到落地采样,再到避坑和验证,全部按能直接抄作业的方式写。

2. pascal-5i 的 fold 划分与 episodic 采样:先把协议吃透再写模型

2.1 四个 fold 到底怎么切、为什么不能自己乱切

pascal-5i 的全称里那个 “5i” 指的是 5 个类别一组、共 4 个 fold 的交叉验证协议。PASCAL VOC 2012 分割任务原本有 20 个前景类加 1 个背景类,pascal-5i 把这 20 个类分成 4 组,每组 5 个类。每个 fold 里,15 个类作为 base(训练时可见),5 个类作为 novel(测试时才出现)。四个 fold 轮流让不同的 5 类当 novel,保证每个类都有机会被当作新类评测。

这里第一个坑就来了:很多人以为 base 和 novel 是随机切的,其实不是。常见的做法是固定划分,比如 fold-0 的 novel 类是 aeroplane、bicycle、bird、boat、bottle,fold-1 是 bus、car、cat、chair、cow,以此类推。你如果自己随机切,指标和论文完全没法比。我一般会直接把划分写死在配置文件里,避免每次跑实验时采样逻辑引入随机性。

# pascal_5i_split.py # 固定 pascal-5i 四个 fold 的类别划分,类别名对应 PASCAL VOC 2012 的 20 个前景类 PASCAL_5I_SPLITS = { "fold0": { "novel": ["aeroplane", "bicycle", "bird", "boat", "bottle"], "base": ["bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"] }, "fold1": { "novel": ["bus", "car", "cat", "chair", "cow"], "base": ["aeroplane", "bicycle", "bird", "boat", "bottle", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"] }, "fold2": { "novel": ["diningtable", "dog", "horse", "motorbike", "person"], "base": ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "pottedplant", "sheep", "sofa", "train", "tvmonitor"] }, "fold3": { "novel": ["pottedplant", "sheep", "sofa", "train", "tvmonitor"], "base": ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person"] } }

这段配置的关键在于:novel 和 base 的并集必须恰好是全部 20 个类,且两两不重叠。参数上你只需要改 fold 名来切换评测目标。逻辑说明:训练阶段只允许用 base 类的标注数据,novel 类的图像可以出现但不能用其分割标签;测试阶段才用 novel 类的 support 集和 query 集。如果你在训练时不小心把 novel 类的 mask 也喂进去了,指标会虚高,这就是典型的协议违规。

2.2 episodic 采样:5-way 1-shot 的一个 episode 长什么样

小样本分割的评测单位是 episode。一个 episode 包含一个 support set 和一个 query set。5-way 1-shot 的意思是:从当前 fold 的 novel 类里随机抽 5 个类,每个类给 1 张带标注的 support 图像,再给若干张 query 图像(通常每类若干张),要求模型根据 support 的分割结果去分割 query。

这里第二个坑是采样粒度。常见做法是每个 episode 的 5 个类不重复,support 图像从该类中随机选一张,query 图像从剩余图像中选。但有些实现会把 support 和 query 混在一起,导致同一张图既当 support 又当 query,指标直接失真。我一般会在 Dataset 类里显式维护一个已用图像集合,确保 support 和 query 不重叠。

import random from collections import defaultdict class EpisodicSampler: """按 pascal-5i 协议采样 episode,保证 support 与 query 不重叠""" def __init__(self, novel_classes, image_dict, n_way=5, n_shot=1, n_query=5): self.novel_classes = novel_classes # 当前 fold 的 5 个 novel 类 self.image_dict = image_dict # {class_name: [img_id, ...]} self.n_way = n_way self.n_shot = n_shot self.n_query = n_query def sample_episode(self): # 1. 从 novel 类中不放回抽 n_way 个类 selected = random.sample(self.novel_classes, self.n_way) support, query = [], [] for cls in selected: imgs = self.image_dict[cls][:] random.shuffle(imgs) # 2. 前 n_shot 张做 support,紧接着 n_query 张做 query if len(imgs) < self.n_shot + self.n_query: raise ValueError(f"{cls} 图像不足,无法构成 episode") support.append((cls, imgs[:self.n_shot])) query.append((cls, imgs[self.n_shot:self.n_shot + self.n_query])) return support, query

参数说明:n_way 固定为 5 对应 5-way;n_shot 设 1 就是 1-shot,设 5 就是 5-shot;n_query 常见取 5 或 10,取决于你的显存和评测稳定性。逻辑上,support 和 query 的切分必须在同一个类内完成,且不能跨类复用图像。如果你发现同一张图在两个 episode 里反复出现,检查一下 image_dict 是否被原地修改了。

2.3 训练时 base 类怎么用、novel 类怎么隔离

训练阶段的标准做法是:在 base 类上做 episodic 训练,或者做普通的语义分割预训练再元学习。无论哪种,novel 类的分割标签绝对不能进入梯度更新。常见做法是在 DataLoader 层面加一个过滤器,只保留 base 类的样本。

def build_base_loader(all_samples, base_classes, batch_size=4): """只保留 base 类的样本用于训练,novel 类样本直接丢弃""" base_samples = [s for s in all_samples if s["class_name"] in base_classes] # 注意:这里不能简单按类别过滤,还要保证每张图的 mask 只包含 base 类 # 如果一张图里同时有 base 和 novel 类,常见做法是把 novel 类像素置为 ignore_index return base_samples

这里有个容易忽略的点:PASCAL VOC 的一张图里可能同时出现 base 类和 novel 类。如果你只按图像级过滤,novel 类的像素仍然会参与 loss 计算。正确做法是在生成 mask 时,把 novel 类对应的像素值改成 255(ignore_index),让损失函数跳过这些位置。这个细节在多数论文里不会写,但不做的话指标会偏高。

3. 从 VOC 原始标注到 pascal-5i 可训练格式:转换脚本与四个边界坑

3.1 目录结构怎么摆、文件命名怎么对齐

PASCAL VOC 2012 的原始结构是 JPEGImages、SegmentationClass、ImageSets/Segmentation 三件套。pascal-5i 本身不改变图像和标注文件,只改变你读取时的类别映射和采样逻辑。所以转换工作的核心是:建立类别名到连续 id 的映射,并生成每个 fold 的 base/novel 列表。

常见做法是保留原始 PNG 标注,在代码里做 remap。我一般会生成一个 json 文件,记录每个 fold 的类别 id 映射,避免每次训练时重新算。

import json import numpy as np from PIL import Image # PASCAL VOC 2012 的 21 类(含背景)原始 id 映射 VOC_CLASSES = [ "background", "aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor" ] def build_remap(fold_name, split_info): """为指定 fold 生成 0..N 的连续 id 映射,背景固定为 0""" remap = {0: 0} # 背景 # base 类从 1 开始编号 for idx, cls in enumerate(split_info["base"], start=1): remap[VOC_CLASSES.index(cls)] = idx # novel 类接着编号,但训练时会被 ignore for idx, cls in enumerate(split_info["novel"], start=len(split_info["base"]) + 1): remap[VOC_CLASSES.index(cls)] = idx return remap def convert_mask(mask_path, remap, ignore_index=255): """把原始 mask 按 remap 转换,未映射的类别置为 ignore_index""" mask = np.array(Image.open(mask_path)) out = np.full_like(mask, ignore_index, dtype=np.uint8) for src, dst in remap.items(): out[mask == src] = dst return out

参数说明:remap 的 key 是原始 VOC id,value 是训练时用的连续 id。ignore_index 通常设 255,和多数分割框架的默认值一致。逻辑上,背景必须保留为 0,否则模型会把背景当普通类处理。如果你发现训练 loss 一直不降,先检查 remap 里有没有漏掉某个类。

3.2 四个边界坑:图像级过滤、像素级过滤、类别不平衡、评测脚本

第一个坑是图像级过滤不等于像素级过滤,前面已经提过。第二个坑是类别不平衡:base 类里 person 和 car 的像素量远大于其他类,直接训练会让模型偏向大头类。常见做法是在 loss 里加类别权重,或者对稀有类做过采样。第三个坑是评测脚本:很多开源实现的 mIoU 计算方式不一致,有的按类平均,有的按像素平均。pascal-5i 的标准是按类平均,且只统计 novel 类。第四个坑是 support 集的标注质量:PASCAL VOC 的标注本身有噪声,1-shot 时如果抽到一张标注很差的 support,整个 episode 的指标都会崩。我一般会在采样时加一个简单的质量过滤,比如 support 的前景像素占比低于 1% 就重新抽。

def is_valid_support(mask, min_ratio=0.01): """support 前景像素占比过低时判为无效,重新采样""" fg = (mask > 0).sum() total = mask.size return (fg / total) >= min_ratio

这个过滤不是论文里的标准操作,但在实际复现时能明显降低 episode 间的方差。参数 min_ratio 我一般设 0.01,设太高会导致采样失败率上升,设太低起不到过滤作用。

4. 避坑与排查:pascal-5i 复现时最常见的 5 个翻车现场

4.1 指标虚高:novel 类标签泄漏进训练

现象:训练时 mIoU 正常,测试 novel 类时高得离谱,甚至超过 base 类。原因:训练时没有把 novel 类像素置为 ignore_index,或者 DataLoader 里混入了 novel 类的样本。解决:在 mask 转换阶段显式检查 novel 类 id 是否被置为 255,并在训练循环里打印每个 batch 的 unique 值,确认没有 novel 类 id 出现。

4.2 指标虚低:support 和 query 图像重叠

现象:测试指标远低于论文报告值,且不同 episode 之间波动极大。原因:采样时 support 和 query 从同一个图像池里抽,导致同一张图既当 support 又当 query。解决:在 EpisodicSampler 里维护已用图像集合,或者直接在采样前把图像列表打乱后按顺序切分,确保不重叠。

4.3 训练不收敛:remap 后类别 id 不连续

现象:loss 震荡或直接 NaN。原因:remap 时跳过了某些 id,导致类别数对不上模型输出通道数。解决:打印 remap 的 value 集合,确认是 0 到 N 的连续整数,且 N 等于模型输出通道数减一。

4.4 评测结果和论文对不上:mIoU 计算方式不一致

现象:同样的模型和权重,你的 mIoU 比论文低 5 到 10 个点。原因:有的实现按像素平均,有的按类平均,还有的把背景也算进去了。解决:统一按类平均,只统计 novel 类的 5 个类,背景不计入。可以在评测脚本里加一行打印每个类的 IoU,方便定位是哪个类拖后腿。

4.5 显存爆炸:episodic 采样时 batch 内图像尺寸不一致

现象:训练到一半 OOM。原因:PASCAL VOC 图像尺寸不统一,episodic 采样时一个 batch 里混了不同尺寸的图。解决:在 Dataset 里统一 resize 到固定尺寸,比如 473x473 或 513x513,并同步 resize mask,用最近邻插值避免引入新类别。

5. 用 1-shot 结果反推数据管线是否健康:一个可复现的验证技巧

最后一章讲一个我常用的验证方法:不要一上来就训大模型,先用一个极简的 prototype 分割器跑通全流程,用 1-shot 的指标反推数据管线有没有问题。具体做法是:对每个 episode,把 support 的前景像素取平均得到一个 prototype 向量,然后对 query 的每个像素算余弦相似度,阈值化后得到分割结果。这个方法不需要训练,纯推理,几分钟就能跑完一个 fold。

import torch import torch.nn.functional as F def prototype_segment(support_feat, support_mask, query_feat, threshold=0.5): """ support_feat: [N_shot, C, H, W] support_mask: [N_shot, H, W],值为 0/1 query_feat: [C, H, W] 返回 query 的二值分割结果 """ # 1. 对 support 的前景像素做平均,得到 prototype fg_feats = [] for i in range(support_feat.shape[0]): mask = support_mask[i].unsqueeze(0) # [1, H, W] fg = support_feat[i] * mask # 只保留前景 fg_feats.append(fg.sum(dim=(1, 2)) / (mask.sum() + 1e-6)) proto = torch.stack(fg_feats).mean(dim=0) # [C] # 2. 对 query 每个像素算余弦相似度 C, H, W = query_feat.shape q = query_feat.permute(1, 2, 0).reshape(-1, C) # [H*W, C] q = F.normalize(q, dim=1) p = F.normalize(proto.unsqueeze(0), dim=1) # [1, C] sim = (q @ p.T).reshape(H, W) # [H, W] # 3. 阈值化得到二值 mask return (sim > threshold).float()

参数说明:threshold 一般取 0.5,但实际用的时候可以扫 0.3 到 0.7,看哪个值在验证集上最稳。逻辑上,这个方法的指标肯定不如训练过的模型,但如果你的数据管线有问题,它的指标会异常低,比如 novel 类 mIoU 低于 10,那就说明 support 和 query 的类别对不上,或者 mask 转换错了。我一般会先用这个方法跑一遍 fold-0,确认 mIoU 在 30 左右,再开始训正式模型。如果低于 20,先回去查第 3 章的 remap 和采样逻辑,别急着调模型。

还有一个习惯:每次换 fold 或换 backbone,我都会固定随机种子跑 3 个 episode,看指标的方差。如果方差超过 5 个点,说明采样不稳定,需要加 support 质量过滤或者增大 n_query。这个习惯帮我省了很多后悔药,因为小样本分割的指标对采样极其敏感,不控制方差的话,你根本分不清是模型变好了还是抽到了简单 episode。

希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

AI建站与传统建站深度对比:流程、架构与长期成本全解析

AI建站与传统建站深度对比:流程、架构与长期成本全解析

1. 从两个真实场景说起&#xff1a;为什么这个话题值得聊去年帮一个做手工皮具的朋友看他的独立站&#xff0c;他花了小两万找外包公司做的&#xff0c;上线三个月&#xff0c;产品页的跳出率一直在70%以上。我打开页面一看&#xff0c;首屏是一张巨大的轮播图&#xff0c;加载…

2026/10/20 6:50:43 阅读更多 →
AI超节点硬件设计:HBD电互联与高速链路工程实践

AI超节点硬件设计:HBD电互联与高速链路工程实践

1. 从一颗芯片到一台超节点&#xff1a;为什么硬件形态成了大模型时代的胜负手聊到大模型训练&#xff0c;大多数人第一反应是算法、是参数量、是数据质量。但真正在机房里摸爬滚打过的人都知道&#xff0c;当集群规模跨过千卡门槛之后&#xff0c;决定训练效率的往往不再是模型…

2026/10/20 6:50:43 阅读更多 →
WorkBuddy AI漫剧:文字一键转专业漫剧的结构化叙事引擎

WorkBuddy AI漫剧:文字一键转专业漫剧的结构化叙事引擎

1. 项目概述&#xff1a;WorkBuddy AI漫剧到底是什么&#xff0c;它凭什么让B站用户集体“真香”WorkBuddy AI漫剧——这名字刚在B站弹幕里刷出来时&#xff0c;我第一反应是又一个套壳PPT动画工具。直到连续三天蹲守某UP主的更新节奏&#xff0c;亲眼看着他用同一段30秒口播文…

2026/10/20 6:49:43 阅读更多 →

最新新闻

纯.NET自绘打印模板设计器:拖拽式可视化与动态渲染

纯.NET自绘打印模板设计器:拖拽式可视化与动态渲染

简介&#xff1a;这是一套面向.NET开发者的C#可视化打印模板设计源码&#xff0c;纯.NET实现、不依赖任何第三方控件&#xff0c;适合需要为发票、报告、证书、标签及单据类业务快速搭建打印能力的初中级工程师。资源提供模板编辑器、图形设计工具与布局管理器等组件&#xff0…

2026/10/20 7:37:06 阅读更多 →
token预算与分块预填充:大模型推理调度的核心引擎

token预算与分块预填充:大模型推理调度的核心引擎

1. 为什么“token预算”不是个玄学概念&#xff0c;而是调度器的血压计你有没有遇到过这样的情况&#xff1a;模型明明还有算力余量&#xff0c;推理请求却开始排队&#xff1b;或者刚压测完吞吐量&#xff0c;一上真实业务流量就卡在预填充阶段&#xff0c;GPU利用率掉到30%以…

2026/10/20 7:37:06 阅读更多 →
许愿机:下一代开发者协作范式的本质跃迁

许愿机:下一代开发者协作范式的本质跃迁

1. 这不是命名迭代&#xff0c;是人机关系的临界点突破“Coding Agent”这个词我第一次听到是在2022年底&#xff0c;当时某开源社区里几个开发者用PythonLangChain搭了个能读GitHub PR描述、自动生成review comment的小工具&#xff0c;大家笑着叫它“coding agent”——像给程…

2026/10/20 7:37:06 阅读更多 →
低质量图像增强实战:从传统算法到深度学习的完整链路解析

低质量图像增强实战:从传统算法到深度学习的完整链路解析

做低质量图像增强这个课题&#xff0c;是我在调研了好几个月的传统算法之后才真正意识到方向应该怎么选的。那时候我手上有一批夜间监控画面和手机随手拍的暗光照片&#xff0c;第一反应是去试各种老牌图像处理方案&#xff1a;直方图均衡化、伽马校正、维纳滤波、BM3D&#xf…

2026/10/20 7:37:06 阅读更多 →
PIDNet实时语义分割模型:原理、环境配置与训练实战

PIDNet实时语义分割模型:原理、环境配置与训练实战

1. 为什么PIDNet能在语义分割里杀出一条路&#xff1a;先搞清楚它在解决什么问题在做环境配置和代跑训练之前&#xff0c;我建议你先花十分钟想明白一件事&#xff1a;PIDNet到底比之前的实时语义分割模型强在哪。如果你只是照着README把环境装好、把权重下下来、跑两张图就收工…

2026/10/20 7:37:06 阅读更多 →
2003-2025美国每日野火烟雾数据:PM2.5暴露评估与健康影响研究指南

2003-2025美国每日野火烟雾数据:PM2.5暴露评估与健康影响研究指南

这两年做环境健康方向的数据分析&#xff0c;我有一个很深的感触&#xff1a;野火烟雾数据的价值&#xff0c;被严重低估了。以前大家做野火研究&#xff0c;最关心的是火点本身——哪儿烧了、烧了多大、过火面积多少。但真正影响几千万人口健康的&#xff0c;其实是烟雾。烟雾…

2026/10/20 7:36:06 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →