DSOD:从零训练目标检测模型,无需预训练权重的结构设计
目标检测这块从早年的手工特征时代一路走到深度学习时代绝大多数能打的模型都绕不开一个前提得先在大型分类数据集上把骨干网络喂饱再迁移到检测任务上微调。这个套路几乎成了行业默认的“标准姿势”以至于很多人下意识觉得不预训练就做检测根本不可能收敛。但有一类工作偏偏要挑战这个默认设定DSOD就是其中最有代表性的一个。它做的事情说起来很朴素——从随机初始化开始端到端地把检测模型训出来不借助任何外部预训练权重。这件事在当年听起来有点反直觉但它背后有一套相当扎实的设计逻辑而且对后来很多轻量化、小样本、边缘部署的场景都有启发。我自己第一次接触DSOD是在一个数据量不大、又没法用大骨干网络的项目里。当时手头只有几千张标注图用常规的预训练加微调套路模型要么过拟合要么在骨干上根本学不动。后来把DSOD的思路拆开看了一遍才发现它真正解决的不是“省掉预训练”这么简单而是重新思考了检测网络里每一层到底该承担什么职责。这篇文章我就把这套算法的来龙去脉、核心设计、实操细节和踩过的坑尽量讲透适合想深入理解检测网络结构、或者手头数据有限、又或者要在资源受限设备上跑检测的同行参考。1. 为什么“不预训练”这件事值得认真对待1.1 预训练到底帮我们解决了什么问题要理解DSOD的价值得先搞清楚预训练在常规检测流程里扮演的角色。以经典的两阶段或单阶段检测器为例骨干网络通常先在ImageNet这类大规模分类数据集上训练学到的是通用的纹理、边缘、局部形状等底层特征。迁移到检测任务时这些底层特征基本可以直接复用网络只需要重点学习“哪里是目标”“目标是什么类别”这些检测特有的能力。这么做的好处很直接收敛快、对标注数据量要求低、最终精度也更高。但代价同样明显。第一预训练权重本身是个额外的依赖你得先有一个足够大的分类数据集还得有算力把它训出来或者去下载别人训好的权重。第二预训练和检测任务之间存在领域差异分类任务关注的是整张图的全局语义检测关注的是局部区域的位置和类别两者对特征的需求并不完全一致。第三当你把模型部署到某些特定领域比如医学影像、工业质检、遥感图像时ImageNet上学到的特征未必迁移得动反而可能带来负迁移。注意预训练不是万能的。在领域差异极大的场景下强行用通用预训练权重有时候还不如从零开始训一个结构更匹配的小网络。1.2 DSOD的核心主张与它要回答的质疑DSOD的核心主张可以用一句话概括在合理的网络结构设计下检测模型可以从随机初始化直接训练并且达到接近甚至媲美预训练模型的精度。这个主张要成立必须回答几个尖锐的质疑。第一个质疑是没有预训练深层网络的梯度能顺利传下去吗检测网络的骨干往往很深随机初始化时梯度容易消失或爆炸。第二个质疑是没有预训练提供的通用特征网络能自己学出有用的底层表示吗第三个质疑是检测任务本身的标注数据通常远少于分类数据集从零训练会不会严重过拟合DSOD的答案不是靠某个单点技巧而是一整套结构上的重新设计。它借鉴了DenseNet的密集连接思想把特征复用做到极致同时针对检测任务的特点做了多处改造。下面我会逐层拆开讲。1.3 适合关注DSOD的几类人不是所有人都需要从零训练检测模型。如果你手头有充足的标注数据、有现成的预训练权重、追求的是极致精度那常规套路依然是最省事的选择。但如果你属于下面几类情况DSOD的思路就值得认真研究。一是数据量有限、领域特殊预训练权重迁移效果差的场景。二是要在算力受限的边缘设备上部署希望模型结构紧凑、参数利用率高的场景。三是想深入理解检测网络内部特征流动机制做结构创新研究的人。四是教学和实验场景需要一套不依赖外部权重、可以完全复现的检测流程。这几类需求恰好都是DSOD设计时重点考虑的。2. 从DenseNet到DSOD结构设计的取舍逻辑2.1 为什么选DenseNet作为骨干基础DSOD的骨干网络脱胎于DenseNet这不是随便选的。DenseNet最核心的特点是密集连接每一层的输入都来自前面所有层的输出拼接。这种设计带来两个直接好处。一是特征复用率极高浅层学到的特征可以被深层反复利用不需要每一层都重新学一遍。二是梯度可以通过密集的短路连接直接回传到浅层缓解了深层网络训练困难的问题。对于从零训练这个目标来说这两点恰好对症下药。没有预训练网络必须自己从数据里学出所有层级的特征密集连接让每一层的学习负担变轻梯度传播也更顺畅。相比之下ResNet的残差连接是相加DenseNet是拼接后者在特征复用上更彻底对数据量的依赖相对更低。但直接照搬DenseNet也不行。DenseNet是为分类任务设计的它的下采样策略、特征图分辨率变化节奏都是围绕“最后输出一个全局类别”来安排的。检测任务需要多尺度特征、需要保留空间细节两者的需求并不一致。所以DSOD在DenseNet基础上做了一系列针对性改造。2.2 骨干网络的关键改造点第一个改造是“stem”结构的设计。标准DenseNet的第一层是一个较大的卷积加池化用来快速降低分辨率。但DSOD发现从零训练时这个初始下采样太激进会丢失太多信息导致后续层学不到足够的底层特征。于是它把stem改成了一个更温和的结构用几个小卷积堆叠逐步降低分辨率同时增加通道数。这样做的代价是前期计算量略增但换来的是更稳的收敛。第二个改造是密集块的堆叠方式。DSOD调整了每个密集块的层数和增长率让整个网络在深度和宽度之间取得平衡。增长率太大参数爆炸太小特征表达能力不足。经过实验它选择了一个相对克制的增长率配合适度的块数量保证网络既有足够容量又不会因为参数过多而在小数据上过拟合。第三个改造是下采样位置的处理。DenseNet在密集块之间用过渡层做下采样DSOD保留了这个设计但对过渡层的压缩率做了调整。压缩率过高会丢信息过低则参数冗余。这个参数在实际操作中需要根据数据量来调数据少的时候适当提高压缩率相当于一种正则化。2.3 检测头的设计考量骨干之外检测头是另一个关键。DSOD采用的是单阶段检测框架直接在多个尺度的特征图上预测类别和边界框。这里有几个设计细节值得说。一是多尺度特征的选择。它没有用全部的特征图而是挑选了几个分辨率合适的层分别负责不同大小的目标。浅层分辨率高负责小目标深层语义强负责大目标。这个分配逻辑和后来的FPN思路一致但DSOD的实现更轻量没有复杂的自顶向下和横向连接。二是预测层的初始化。从零训练时检测头的初始化非常关键。如果分类分支和回归分支的初始权重设置不当训练初期会产生大量无意义的预测把梯度带偏。DSOD对这部分做了针对性的初始化策略让网络在训练初期输出接近均匀分布避免过早陷入局部最优。三是损失函数的平衡。分类损失和回归损失的权重需要仔细调。回归损失过大网络会过度关注框的位置而忽略类别分类损失过大则定位精度上不去。这个平衡点在不同数据集上不一样需要实验确定。3. 核心机制拆解密集连接与特征复用如何支撑从零训练3.1 密集连接的具体实现与梯度流动密集连接听起来简单实现起来有几个细节要注意。在一个密集块内假设有L层第l层的输入是前面所有层输出的拼接。这意味着随着层数增加输入通道数会线性增长。为了控制参数量每一层内部的卷积通常先用一个1x1卷积做通道压缩再用3x3卷积做特征提取。这个瓶颈结构是DenseNet的标准做法DSOD沿用了它。从梯度角度看密集连接相当于给每一层都提供了通往损失函数的短路径。反向传播时浅层不仅能从相邻层拿到梯度还能从所有后续层直接拿到梯度。这种多路径的梯度流动让从零训练时的梯度信号更稳定不容易出现某些层完全学不动的情况。提示密集连接的一个副作用是显存占用高因为每层的输出都要保留下来供后续层拼接。实际训练时可以通过梯度检查点技术来缓解代价是训练速度略降。3.2 特征复用对数据效率的影响特征复用是DSOD能在小数据上从零训练的重要原因。常规网络里每一层都要重新学习特征浅层学到的边缘、纹理到了深层往往被丢弃或覆盖。密集连接让这些底层特征一直保留在后续层的输入里网络不需要重复学习相当于把有限的数据信息利用率提高了。打个比方常规网络像是一条流水线每个工位只把自己加工的半成品传给下一个工位前面的半成品就丢了。密集连接则像是每个工位都能看到之前所有工位的产出需要什么直接取用。在数据充足时这种差异不明显数据紧张时复用带来的效率提升就很关键了。3.3 从零训练时的初始化与正则化策略从零训练初始化和正则化是两道必须迈过的坎。初始化方面DSOD采用了适合密集连接的初始化方式保证每一层的输出方差在传播过程中保持稳定。如果初始化方差过大深层输出会爆炸过小则信号逐层衰减。这个平衡需要通过理论推导结合实验来确定。正则化方面除了常规的权重衰减和批归一化DSOD还依赖密集连接本身带来的隐式正则效果。因为每一层都能直接访问浅层特征网络对单个层权重的依赖降低过拟合的风险也随之下降。此外数据增强在从零训练时尤为重要随机裁剪、翻转、颜色抖动这些手段相当于人为扩大了数据分布帮助网络学到更鲁棒的特征。4. 实操复现从零训练一个DSOD风格检测器4.1 环境与数据准备要复现DSOD的训练流程先得把环境和数据理清楚。框架层面主流深度学习框架都能实现选自己熟悉的即可。硬件上由于密集连接显存占用较高建议至少有一块显存够用的显卡训练时适当调小批大小。数据方面准备标准的检测数据集格式每张图对应标注文件包含类别和边界框坐标。如果数据量本身不大数据增强的强度要相应加大。我个人的经验是从零训练时增强策略要比微调时更激进一些因为网络没有预训练提供的先验需要更多样的样本才能学好。4.2 网络结构搭建的关键参数搭建网络时有几个参数需要重点确定。密集块的层数和增长率决定了骨干的容量数据少时层数不宜过多增长率也要控制。过渡层的压缩率影响参数效率和信息保留的平衡。检测头选择的特征层要根据数据集中目标的大小分布来定小目标多就多用浅层大目标多就多用深层。下面是一个简化的结构配置示例仅示意参数组织方式# 骨干配置示意 dense_blocks [4, 6, 8, 6] # 每个密集块的层数 growth_rate 24 # 每层新增通道数 compression 0.5 # 过渡层压缩率 stem_channels 48 # stem输出通道数 # 检测头使用的特征层索引 detection_layers [block2_out, block3_out, block4_out]这些数值不是固定的需要根据实际数据反复调整。增长率从24起步是个稳妥的选择数据量大的话可以往上加。4.3 训练流程与超参数设置训练流程上从零训练和微调最大的区别在于学习率策略。微调时通常用较小的学习率避免破坏预训练权重从零训练则需要一个相对完整的学习率周期先预热再衰减。预热阶段让网络在初期用很小的学习率稳定下来避免一开始就产生剧烈震荡。批大小受显存限制能大则大因为批归一化对批大小敏感。优化器用带动量的随机梯度下降是稳妥选择动量设置常规值即可。训练轮数要比微调多因为从零开始需要更多迭代才能收敛。早停策略要配合验证集使用防止过拟合。超参数建议范围说明初始学习率0.05 - 0.1配合预热使用预热轮数3 - 5稳定初期训练批大小尽量大受显存限制权重衰减1e-4 左右抑制过拟合训练轮数200 以上从零训练需要更多迭代4.4 训练过程的监控与调优训练过程中要重点盯几个指标。训练损失和验证损失的走势是否同步如果验证损失早早回升而训练损失还在降说明过拟合了需要加强正则或减少模型容量。分类损失和回归损失的比例是否合理如果某一项长期居高不下说明权重设置需要调整。还有一个容易被忽略的点是梯度范数。从零训练时梯度爆炸的风险比微调高定期打印梯度范数能帮你及时发现异常。如果梯度范数突然飙升多半是学习率太大或者某个批次的样本有问题。5. 常见问题与排查实录5.1 训练不收敛或损失震荡这是从零训练最常见的问题。原因通常有几个学习率太大、初始化不当、批归一化统计不稳定。排查时先把学习率降一个数量级试试如果还是震荡检查初始化方式是否符合密集连接的要求。批归一化在批大小太小时统计量不准也会导致震荡这时候可以尝试用更大的批或者换用对批大小不敏感的归一化方式。5.2 过拟合严重数据量小的时候从零训练很容易过拟合。应对手段包括加强数据增强、提高权重衰减、减少密集块层数或增长率、引入随机深度等。我自己的经验是随机深度对密集连接网络特别有效训练时随机丢弃一些层相当于训练了多个子网络的集成泛化能力明显提升。5.3 小目标检测效果差DSOD的多尺度特征分配如果和数据集的目标尺度分布不匹配小目标效果会很差。解决办法是调整检测头使用的特征层把更高分辨率的浅层纳入预测同时检查数据增强是否把原本的小目标裁得更小了。另外小目标的标注质量往往参差不齐标注框偏差几个像素对训练影响就很大清洗标注数据有时比调模型更管用。5.4 显存不足密集连接的显存开销是硬伤。缓解手段包括减小批大小、使用梯度检查点、降低增长率、减少密集块层数。如果这些都不够可以考虑把输入分辨率适当降低但要注意这会影响小目标检测。问题现象可能原因排查方向损失不降学习率过大、初始化错误降学习率、检查初始化验证损失回升过拟合加强正则、减容量小目标漏检特征层选择不当调整预测层、清洗标注显存溢出密集连接开销大减批大小、梯度检查点5.5 几个容易踩的坑第一个坑是盲目照搬分类网络的超参数。检测任务的损失构成和分类完全不同学习率、权重衰减这些都需要重新调。第二个坑是忽略数据增强的强度。从零训练对数据多样性的依赖远高于微调增强不够网络学到的特征就很局限。第三个坑是过早下结论。从零训练的收敛曲线往往比微调更曲折前期看起来没希望多训几十轮可能就起来了耐心很重要。6. 影响范围与适用边界6.1 对轻量化与边缘部署的意义DSOD这套思路对边缘部署的价值不在于它本身有多轻而在于它证明了结构设计可以替代预训练权重。在边缘设备上模型大小和计算量是硬约束预训练权重往往意味着更大的骨干。如果能用更紧凑的结构从零训练出可用的检测器部署时的灵活度就高很多。后来的很多轻量检测工作在结构设计上都或多或少借鉴了这种“用结构换预训练”的思路。6.2 在特殊领域的应用潜力在医学影像、工业质检、遥感这些领域通用预训练权重的迁移效果经常不理想。DSOD提供的从零训练范式让这些领域可以针对自己的数据特点设计网络而不必迁就通用骨干的结构。当然这不意味着从零训练一定更好而是多了一种选择在预训练迁移效果差的时候可以尝试。6.3 局限性与不适用场景DSOD也不是万能的。当数据量非常充足时预训练加微调的精度上限通常更高从零训练的优势就不明显了。当任务对精度要求极高、且允许使用大模型时也没必要刻意追求从零训练。另外从零训练对超参数更敏感调参成本更高工程上要权衡投入产出。我个人在实际操作中的体会是DSOD最大的价值不是让你从此不用预训练而是逼着你去思考网络里每一层到底在干什么。当你把结构设计想清楚了用不用预训练反而成了一个可以根据场景灵活选择的选项而不是必须遵守的教条。这个思路上的转变比省下来的那点预训练成本值钱得多。

相关新闻

TIA/EIA-568-B.2布线标准核心解析:从SRL到PSELFEXT的信号完整性实战指南

TIA/EIA-568-B.2布线标准核心解析:从SRL到PSELFEXT的信号完整性实战指南

简介:本资源为美国TIA/EIA官方发布的《TIA/EIA-568-B.2》通信布线核心标准PDF原版文档,面向网络工程设计师、综合布线施工人员、弱电系统集成商及高校通信/智能建筑相关专业师生。该标准聚焦商业建筑中平衡双绞线组件(含超五类、六类、增强六…

2026/10/12 23:29:24 阅读更多 →
论文写作辅助工具怎么选?文理医工8款工具组合实测与避坑指南

论文写作辅助工具怎么选?文理医工8款工具组合实测与避坑指南

1. 为什么写论文的工具选型,比论文本身更讲究我是从去年开始密集接触各类论文写作辅助工具的。起因很简单,手头有篇跨学科的综述拖了快两个月,光是文献格式就反复改了四遍。后来在某个学术交流群里看到有人分享了一套工具组合,才意…

2026/10/12 23:29:24 阅读更多 →
高校科技成果转化关键路径:从实验室到市场的实战策略

高校科技成果转化关键路径:从实验室到市场的实战策略

1. 成果转化这件事,到底卡在哪里先说一个我这些年反复看到的判断:很多高校课题组其实并不缺好成果,缺的是把成果从实验室带到市场的“最后一公里”推力。不少科研人员天然认为,成果做出来、论文发出来、专利拿到手,转化…

2026/10/12 23:28:23 阅读更多 →

最新新闻

SpringBoot+Vue打造装饰工程管理系统:完整实战解析

SpringBoot+Vue打造装饰工程管理系统:完整实战解析

做装饰工程项目管理的系统,很多老牌软件公司都停留在单体架构时代,界面老旧、流程僵硬,项目经理宁可自己拉Excel表格也不愿意用。而新团队上来就搞微服务分布式,对一家中小型装饰公司来说,根本就是杀鸡用牛刀&#xff…

2026/10/13 7:36:08 阅读更多 →
Dalsa Nano相机C++采集例程:从接口配置到稳定抓帧

Dalsa Nano相机C++采集例程:从接口配置到稳定抓帧

简介:面向机器视觉、自动化检测与工业现场应用的 Dalsa Nano 相机 C 使用例程,以 GigE Vision 千兆网通信协议为基础,完整演示了相机从连接初始化、参数配置到实时图像采集、显示与保存的整个流程。压缩包内含 77 个文件,主体为 C…

2026/10/13 7:36:08 阅读更多 →
Hive多DISTINCT性能优化:用COLLECT_SET替代COUNT(DISTINCT)

Hive多DISTINCT性能优化:用COLLECT_SET替代COUNT(DISTINCT)

1. 项目概述:为什么“多个DISTINCT”会成为Hive SQL里的隐形性能杀手你有没有写过这样的Hive SQL?SELECT dt,COUNT(DISTINCT user_id) AS dau,COUNT(DISTINCT CASE WHEN is_paying 1 THEN user_id END) AS paying_users,COUNT(DISTINCT CASE WHEN chann…

2026/10/13 7:36:08 阅读更多 →
Flutter鸿蒙化适配:floor_generator桥接SQLite的完整实践与踩坑记录

Flutter鸿蒙化适配:floor_generator桥接SQLite的完整实践与踩坑记录

上个月在适配一个跨平台进销存项目到鸿蒙平台时,数据库层成了第一个硬骨头。这个项目的十几个业务模块全部建立在 floor 这个 ORM 框架之上,实体类、DAO 接口、查询条件都是现成的,本来以为 Flutter 能在鸿蒙环境跑起来,floor 也能…

2026/10/13 7:36:08 阅读更多 →
虚拟机 ens33 没有 IP?ifconfig 不显示网卡地址的排查与修复

虚拟机 ens33 没有 IP?ifconfig 不显示网卡地址的排查与修复

刚装好的虚拟机,开机之后敲下 ifconfig ,屏幕上来回翻的输出里只有 lo 和那个让人无语的 ens33 ,连个 IPv4 地址都不给。这个画面我见过太多次,第一次踩坑时还以为是虚拟网卡坏了,把虚拟机删了重建两回&#xff…

2026/10/13 7:36:08 阅读更多 →
E2B沙箱实战:AI智能体代码执行失控防护与排障指南

E2B沙箱实战:AI智能体代码执行失控防护与排障指南

1. 先搞清楚:AI 智能体为什么总能把沙箱跑崩上周有个朋友找我,说他的 AI 智能体项目出事了:Agent 在沙箱里跑了一段“看似无害”的 Python 脚本,结果沙箱彻底失联,连宿主机都差点被拖挂,最后只能重启整台机…

2026/10/13 7:35:07 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/12 21:05:09 阅读更多 →
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/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →