EEGNet精读:紧凑卷积网络高效分类脑电信号的设计与复现

最近处理脑机接口数据时,又把EEGNet这篇经典论文翻出来做了几轮细读。作为一篇提出紧凑型卷积神经网络用于EEG信号分类的文献,它虽然在今天看来结构不算复杂,但其中的设计思路和实验分析方式,对后来做BCI相关研究或者工程落地的人都很有借鉴意义。这篇精读会逐层拆解EEGNet的网络设计、训练细节、实验对比和复现时最容易踩的坑,适合正在入门EEG深度学习的同学,也适合那些想快速搭建基线模型但不想走弯路的研究者。

我读论文的习惯是先不看结论,先看它要解决的痛点。EEG信号在普遍情况下信噪比极低、跨受试者差异大,传统分类器在这个领域长期占据主导。EEGNet的主要贡献在于,用深度可分离卷积把网络规模压到极小,却能在多种不同的BCI范式下取得有竞争力的结果。这篇精读会顺着这条主线展开,把这个紧凑网络为什么能成立、怎么复现、什么时候会失效,讲明白。

1. 为什么要把EEGNet反复嚼碎:研究动机与核心贡献

1.1 这个问题为什么值得读

脑机接口领域的核心任务之一,是让人脑产生的脑电信号能被计算机稳定识别。EEG信号本身只有微伏级别,混在眼电、肌电和大把环境噪声里,想直接套用图像领域的卷积神经网络,往往会得到"过拟合严重,泛化很差"的模型。早期很多工作把CNN当成一个黑盒,输入一段原始EEG,输出分类结果,模型动辄几十万参数,但实际效果并不比手工特征的线性分类器好太多。

EEGNet给出的方案很有意思:与其盲目堆参数,不如把卷积拆成"时域滤波"和"空间滤波"两个阶段。这种设计跟脑电分析里经典的空域模式识别思路不谋而合——先用时域卷积提取频率相关的特征,再用空间卷积融合多通道信息,最后用深度可分离卷积减少冗余计算。读这篇论文时,我会强烈建议你先抛开深度学习里的传统思维,把它当成一个"可学习的信号处理流程"来理解。

1.2 核心贡献:小模型不一定弱

EEGNet的另一个贡献是证明了紧凑模型在脑机接口场景下的优越性。模型参数量级通常在几千到几万之间,比常见CNN小好几个数量级,却能在运动想象、事件相关电位、错误相关电位等多种不同类型的数据集上稳定工作。这不是论文的噱头,而是在多个公共数据集上跑出来的统计结果。

从实际工程角度看,小模型意味着更低的部署成本、更快的推理速度,也更适合在线脑机接口场景。光是这一点,就值得读透它为什么能做到"小而精",而不是简单抄一个结构就跑。

2. 网络设计深度拆解:从普通卷积到深度可分离卷积的迁移

2.1 输入信号的建模方式

EEGNet的输入不是二维图像,是一个二维矩阵,形状可以理解成"通道数×采样点数"。例如64个电极、256Hz采样率下采集1秒数据,输入就是64×256的张量。跟图像不同,这里的高度方向是脑电通道,宽度方向是时间。

论文把输入处理成一个通道数为1的二维矩阵,也就是说初始输入形状类似于单通道的"图像"——空间轴是电极位置排列,时间轴是波形。后续卷积不会把电极位置当成空间邻居来做传统二维卷积,而是用一种精心设计的组合来提取脑电特有的特征。

2.2 模块一:时域卷积层

第一层卷积的本质是一组可学习的带通滤波器。这一步在时间维度上做一维卷积,卷积核大小通常设置为64个采样点(对应250Hz采样率下的256ms窗口)。它的作用是从原始波形中筛选出不同频率成分的信息,比如mu节律、beta节律等与任务相关的频段。

这一层在设计时需要注意一个细节:卷积核的大小决定了模型关注的"时间尺度"。如果窗口太短,只能捕捉局部毛刺;如果太长,可能会把不同阶段的脑电模式混在一起。原论文通过实验选择了跨样本比较合适的尺度,但在自己的数据上,通常还是需要根据采样率微调。我第一次复现时,固定使用原参数在低采样率数据上效果不佳,后来按比例缩小卷积核才恢复性能。

2.3 模块二:深度卷积与空间滤波

时域滤波之后,进入深度卷积层。这里有一个关键操作:每个时域特征图都独立进行空间卷积,也就是在电极维度上一次性将所有通道融合。这种操作本质上是在学习一个空间滤波器,相当于传统BCI算法里的空间模式。

论文这一步使用了一个巧妙的"二维卷积"技巧:卷积核大小设为(1, 通道数),即一次覆盖所有电极。因为EEG的电极位置本身并不具备标准的二维拓扑结构,常规的二维卷积核假设局部空间相关性,这在稀疏电极排列下不一定成立。EEGNet选择把整个空间维度一次性卷积,避免了人为假设空间邻近关系的问题。这一步输出就是每个时间特征对应的空间滤波结果。

2.4 模块三:深度可分离卷积

在空间滤波之后,还套了一个深度可分离卷积层:先逐通道做时间卷积,再用逐点卷积混合通道特征。这里引入深度可分离卷积的最大好处,是大幅减少了可学习参数和计算量。比如一个普通的3×3卷积需要9×C×D个参数,而深度可分离卷积只需要3×C个深度卷积参数再加上C×D个逐点卷积参数,对于大通道数的数据更明显。

从信号处理角度看,这种设计把"时间特征"和"通道组合特征"的学习过程解耦了,模型更容易收敛,也不太容易在小样本数据集上严重过拟合。这个思路和后来很多轻量化网络非常一致,但在脑电这个场景里EEGNet算是比较早的尝试。

2.5 模块四:分类头与可视化

特征经过平均池化后,展平成一个向量,再送入全连接层输出分类概率。论文中还有一个值得一提的设计:它把网络中间的特征做了可视化,展示不同类别在空间特征上的差异。如果你在做自己的项目,建议也把这层拿出来画图,往往能帮你发现模型到底学到了什么频段、哪些电极贡献最大。

3. 训练细节中的门道:正则化、数据增强与超参数选择

3.1 样本量太小怎么办:数据增强的重要性

EEG数据集的样本量通常不大,一场实验可能只有几十到几百个试次。如果不做任何数据增强,即使EEGNet参数少,训练过程依然容易抖动。论文里使用了滑动窗口切分的方式增加训练样本,具体来说会在每个试次中滑动多个窗口,并加上一些随机裁剪。

我自己复现时的体验是:滑动窗口的确立是关键。如果窗口太短会丢失任务相关的慢波信息,太长又会引入很多重叠冗余。一种比较稳妥的做法是,先根据事件标签确定任务相关时间范围,再在这个范围内以一定步长滑动。步长越小,样本越多,但相邻样本高度相似,反而可能让模型记住"天线的位置"而不是特征。我个人会先做一个简单相关性检查,把相似度太高的重叠样本去掉一部分。

3.2 正则化的具体形式

EEGNet在原文中使用了两种正则化:Dropout和L2权重衰减。Dropout加在全连接层之前,一般取值0.25到0.5之间。L2衰减则加在卷积层的核上,用来限制权重的范数,防止某个电极通道主导整个预测。

这里给一个参考范围:如果训练样本少于500,Dropout建议0.5;如果样本量在500到2000之间,Dropout可以降到0.25甚至关闭。L2权重衰减的初始值可以从0.001到0.01尝试,观察验证集损失的变化。需要注意的是,EEG信号的个体差异很大,跨被试训练时正则化的作用会更重要,否则模型很容易只记住某一个被试的整体模式。

3.3 损失函数与优化器选择

论文使用的就是标准的交叉熵损失函数。优化器方面,原文采用Adam,学习率设置在0.01附近。不过在复现时,我发现学习率0.01对于很多数据集都偏高了,特别是在样本量小的情况下,损失曲线容易震荡。更稳妥的做法是学习率从0.001开始,配合学习率衰减或早停策略。

如果你用的是PyTorch,可以用CosineAnnealingLR或者ReduceLROnPlateau。我的习惯是先用ReduceLROnPlateau,如果验证损失连续五个epoch不下降,就把学习率乘以0.5,这个策略在多个数据集上都很稳定。

3.4 批次大小与训练轮次

批次大小一般取16或32。脑电数据如果批次太大,会让模型优化方向被多数类样本主导;如果批次太小,归一化层统计不稳定,也会影响效果。EEGNet由于结构简单,训练很快,上百个epoch也不会花费太久。关键要关注的是验证集上的性能是否出现过拟合,不要只看训练集收敛。

4. 对比实验说了什么:验证集上的性能与模型复杂度

4.1 与浅层线性模型的对比

论文中对比的基线包括正则化线性判别分析、支持向量机、以及一些早期的卷积结构。结果很明确:在大多数数据集上,EEGNet的性能明显优于线性方法,特别是在处理非平稳的脑电信号时,卷积结构自动学习的时空滤波器比手工特征鲁棒性更好。

但这不代表浅层方法没有参考价值。实际上,在线性方法表现尚可的情况下,EEGNet的增益可能只有几个百分点,这点提升是否值得增加计算复杂度,需要结合具体场景判断。论文给出的信息是,即便模型大幅缩小,分类表现仍然领先基线,这个结论才是重点。

4.2 与大型CNN的对比

另外一个直观的发现是,盲目加深网络并不一定能提升EEG分类效果。有些对比模型动辄几百万参数,在EEG数据上反而出现严重的过拟合,验证集性能远低于EEGNet。这说明在BCI这个场景中,模型的先验结构比容量更重要。EEGNet正是把信号处理知识内嵌进网络结构,才实现了"用最小的模型抓住最核心的特征"。

4.3 跨范式泛化能力

EEGNet一个让人印象深刻的点,是同一个网络结构无需大改动即可用于P300、运动想象、错误相关电位等不同范式。这在工程上非常有用,意味着你可以维护一个统一的EEG处理模型,只需要更换输入数据和输出节点,而不必为每个任务单独设计网络。当然这里也有个隐含前提:输入数据的维度最好保持一致,或者至少采样率和电极数不要差异过大,否则需要调整卷积核大小。

4.4 指标以外的东西:可解释性

论文还给出了空间滤波器的可视化结果,不同类别对应的空间权重分布有明显差异。这一点非常有价值,因为它让研究者能检查模型学习到的空间模式是否符合认知神经科学知识。我建议任何想在BCI方向发论文或者做产品的人,都要留一个空间特征可视化的环节,这不仅能帮你发现数据问题,也能增加结果的可信度。

5. 复现过程中的关键代码结构和调整心得

5.1 核心网络结构的伪代码级拆解

下面给出一段简化的PyTorch实现,展示EEGNet的核心层组织方式。这段代码不是唯一的写法,但足够用来理解结构。

import torch import torch.nn as nn class EEGNet(nn.Module): def __init__(self, chs=64, samples=256, num_classes=4, dropout=0.5, kernel_length=64, depth=2): super().__init__() # 第一层:时域卷积 self.temporal_conv = nn.Sequential( nn.Conv2d(1, depth, kernel_size=(1, kernel_length), padding=(0, kernel_length//2), bias=False), nn.BatchNorm2d(depth) ) # 第二层:空间卷积(覆盖所有通道) self.spatial_conv = nn.Sequential( nn.Conv2d(depth, depth * 2, kernel_size=(chs, 1), bias=False), nn.BatchNorm2d(depth * 2), nn.ELU(), nn.AvgPool2d(kernel_size=(1, 4)), nn.Dropout(p=dropout) ) # 第三层:深度可分离卷积 self.separable_conv = nn.Sequential( nn.Conv2d(depth * 2, depth * 2, kernel_size=(1, 16), padding=(0, 8), groups=depth * 2, bias=False), nn.Conv2d(depth * 2, depth * 2, kernel_size=1, bias=False), nn.BatchNorm2d(depth * 2), nn.ELU(), nn.AvgPool2d(kernel_size=(1, 8)), nn.Dropout(p=dropout) ) # 分类层 self.classifier = nn.Sequential( nn.Flatten(), nn.Linear(self._calc_flatten(chs, samples), num_classes) ) def _calc_flatten(self, chs, samples): dummy = torch.zeros(1, 1, chs, samples) with torch.no_grad(): x = self.temporal_conv(dummy) x = self.spatial_conv(x) x = self.separable_conv(x) return x.numel() def forward(self, x): x = self.temporal_conv(x) x = self.spatial_conv(x) x = self.separable_conv(x) x = self.classifier(x) return x

5.2 输入维度换算的注意点

一个经常被卡住的坑是Flatten层输出维度的计算。不同采样率和池化尺度会直接影响全连接层输入大小。我不建议手算,而是像上面的代码那样写一个小函数,用一张假数据把中间层的输出维度跑出来。这种方法虽然有一点点运行时开销,但在频繁改超参数时最省心。

5.3 训练效果验证的步骤建议

复现后先不要直接上复杂数据集。我一般的流程是:

  • 先加载一个二分类公开数据子集,例如运动想象左手/右手,跑30个epoch,看训练集是否能够过拟合甚至达到接近100%准确率。如果连训练集都学不动,多半是代码结构有问题。
  • 再切一个小的验证集,观察验证集准确率是否在提升。这里特别要注意数据划分不要泄漏:同一个试次的滑动窗口必须完全放在训练集或验证集,绝对不能交叉,否则结果虚高。
  • 最后再和论文报告的性能对比,如果差距不大(比如3-5个点以内),就说明复现基本成功。

5.4 复现中最容易踩的坑

第一个坑是归一化。EEG数据每名受试者的信号幅值差异很大,我习惯按被试进行z-score标准化,而不是把所有被试数据混在一起归一化。否则模型会倾向于根据幅值来分类,而不是根据脑电模式。

第二个坑是池化顺序。空间卷积之后的平均池化只应该在时间维度上进行,如果你误把空间维度也池化掉,模型会丢失电极位置信息,性能立刻下降。很多人把池化核写成(2,4),其实需要根据输入尺寸确认是否影响了通道维度。

第三个坑是类别不平衡。BCI实验的类别样本数量往往接近,但如果你自己采集的数据有缺失,就要在损失函数里加权重,否则网络预测会偏向样本多的类,尤其在小样本时非常明显。

6. 现实应用中的边界:EEGNet的适用场景与硬性局限

6.1 适合使用的条件

EEGNet适合在以下情况使用:数据量中等或偏小、采样率在128Hz到512Hz之间、电极数量从8到128都可以接受、任务属于典型的分类任务(运动想象、P300、SSVEP的部分设置也类似)。它特别适合作为新数据的基线模型,因为结构简单,训练快,部署到嵌入式设备也没有压力。

6.2 不适合使用的场景

如果脑电数据是连续流式的,且需要实时调整分类阈值,EEGNet还需要配合滑动窗口和在线归一化策略,单纯用一个离线训练好的网络直接处理在线数据,效果会大打折扣。另外,如果任务涉及超高频信号,比如高频SSVEP,时域卷积核可能需要重新设计,原始的固定尺寸卷积核不一定能捕捉到几赫兹窄带特征。

6.3 在现有基础上的改进方向

虽然EEGNet已经足够紧凑,但仍然有一些常见的改进手段。例如用空域注意力机制增强空间滤波的灵活度;或者在时域卷积之前添加一个带通滤波预处理层,让网络更聚焦于感兴趣的频段。不过要注意,任何改动都应该在验证集上横向对比,不要为了刷分而不断加复杂模块,否则又会回到大模型的老路上去。

原论文发布已有一段时间,期间学术界提出了许多扩展版本,比如融合LSTM或Transformer模块来做时序建模。但无论如何,EEGNet作为基准框架的价值依然存在。我自己做的多个项目里,第一步基本都是把EEGNet跑通,得到一个稳定且可解释的基线,然后再去探索更复杂的模型是否真的有增益。实践下来,很多场景里加上复杂模块提升并不明显,反而EEGNet本身足够用了。

如果你也打算在自己的EEG数据上试一试,建议从公开数据集开始,先用最小结构跑通训练流程,再逐步调整卷积核大小和正则化参数。在踩过几次坑之后你会逐渐形成一个判断:脑电深度学习项目的失败,往往不是因为模型不够复杂,而是因为数据处理和训练设置的细节没有做到位。EEGNet简化了模型端的变量,反而让研究者能把更多注意力放在数据端,这也许正是它值得反复精读的重要原因。

相关新闻

钢轨表面缺陷检测实战:基于Yolov5的数据集、训练与部署

钢轨表面缺陷检测实战:基于Yolov5的数据集、训练与部署

钢轨表面缺陷检测这些年一直是工业视觉里"看着简单、落地要命"的典型场景。我在某轨道交通检测项目中折腾过一整套基于Yolov5的识别流程,从数据采集、标注、训练到上小车实测,踩坑不少。这篇就按我实际做法,把数据集怎么攒、模型怎…

2026/10/19 18:01:23 阅读更多 →
STM32写入FLASH后读回数据不一致?擦除、Cache与写保护全解析

STM32写入FLASH后读回数据不一致?擦除、Cache与写保护全解析

前两天有人在嵌入式交流群里贴了一段代码,项目标题就是“STM32向FLASH写入数据后,重新读出的数据和原写入数据不一致”。他说自己照着教程抄了一套Flash读写函数,往0x08020000写了128个字节,然后读回来逐字节比对,结果…

2026/10/19 18:00:22 阅读更多 →
Java面试实战:核心技术栈与业务场景深度解析

Java面试实战:核心技术栈与业务场景深度解析

最近一年,我陪跑了不少朋友的Java面试,自己也复盘了多轮一线互联网公司的面试过程。一个很明显的感受是:大厂面试官的提问方式已经从“你知道什么”慢慢转向“你在什么场景下用什么方案解决了什么问题”。标题里那句“核心技术栈与业务场景深…

2026/10/19 18:00:22 阅读更多 →

最新新闻

钢材表面缺陷检测实战:从YOLOv5到YOLOv10的选型与部署

钢材表面缺陷检测实战:从YOLOv5到YOLOv10的选型与部署

钢材表面质量把控这件事,在很长一段时间里都靠质检员肉眼盯。带钢在产线上以每分钟几百米的速度跑,缺陷转瞬即逝,人盯久了必然疲劳,漏检几乎无法避免。这也是基于深度学习的表面缺陷检测这几年在金属加工行业里反复被讨论的原因。…

2026/10/19 19:32:49 阅读更多 →
PDF转Excel不乱格式:从表格识别到数据校验的完整实操指南

PDF转Excel不乱格式:从表格识别到数据校验的完整实操指南

1. 为什么PDF转Excel,表格结构总是乱七八糟先放过工具推荐,聊一个更扎心的问题:为什么PDF转Excel,十次有八次转出来是乱的?我接触过大量做财务、做数据整理的同行,大家对这种场景应该都不陌生——收到一份上…

2026/10/19 19:31:48 阅读更多 →
TensorFlow 分布式 FFT 实战:数据切分、跨设备转置与性能优化

TensorFlow 分布式 FFT 实战:数据切分、跨设备转置与性能优化

做大规模谱分析的人,很多都会在某个阶段撞上同一堵墙:数据越算越大,FFT 开始成为整条流水线里最慢的环节。TensorFlow 里的分布式快速傅里叶变换,乍一看就是把tf.signal.fft2d放到多张卡上跑,实际做起来才会发现&#…

2026/10/19 19:31:48 阅读更多 →
石鱼的世界模拟:基于ROS与Python的水下机器人仿真平台解析

石鱼的世界模拟:基于ROS与Python的水下机器人仿真平台解析

简介:面向机器人、生物学仿真与Python开发者的石鱼生态环境模拟项目,基于ROS Noetic分支构建,可在虚拟三维场景中还原石鱼行为、环境互动及多物种交互,涉及环境建模、物理模拟、行为算法、事件驱动与多线程等技术点,适…

2026/10/19 19:31:48 阅读更多 →
从形式主义到基础设施:开放式Code Review机制设计与落地

从形式主义到基础设施:开放式Code Review机制设计与落地

1. 先聊聊为什么很多团队的Code Review形同虚设我参与过大大小小不下十个项目的评审流程改造,见过最典型的一个场景是:团队嘴上喊着"我们要做Code Review",GitHub仓库的PR也确实挂了一堆,但点进去一看,评论全…

2026/10/19 19:31:48 阅读更多 →
自建代码审查基础设施:open-code-review设计部署与优化

自建代码审查基础设施:open-code-review设计部署与优化

去年我在主导一个中台服务重构,那阵子代码评审基本处于失控状态:PR平均合并时间从3小时拖到近3天,核心代码的评审意见大量集中在缩进和命名上,而真正的接口设计问题、事务边界问题反而经常放过去,等上了灰度环境才炸出…

2026/10/19 19:30:47 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 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 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 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 阅读更多 →