第四章:模型架构设计——EEGNet、DeepConvNet、ShallowConvNet、Transformer for EEG

好,咱们进入正题。模型架构设计这块,说实话,是深度学习做EEG信号处理最核心的一环。你想想看,EEG信号本身信噪比低、样本量小、个体差异大,选不对模型,后面再怎么调参都是白搭。

我个人习惯把EEG模型分成两类:一类是轻量级、适合实时部署的,比如EEGNet;另一类是深度堆叠、追求极致精度的,比如DeepConvNet。当然,还有Transformer这种“外来物种”,这两年也开始在EEG领域大放异彩。咱们一个一个聊。

4.1 EEGNet——小而美的典范

EEGNet是我在项目里用得最多的模型。为什么?因为它真的轻。我记得第一次在嵌入式设备上跑EEGNet,模型大小才几十KB,推理延迟不到5ms。嗯,这种体验,谁用谁知道。

它的核心思路其实很简单:先用一个时间卷积提取每个通道的时序特征,再用一个深度可分离卷积做空间滤波。说白了,就是把时间和空间两个维度分开处理,参数少,效果好。

核心结构:

  • 输入层:C × T(通道数 × 时间点)
  • Conv2D:2D卷积,kernel=(1, 64),学习时间滤波器
  • DepthwiseConv2D:每个通道独立卷积,kernel=(C, 1),学习空间滤波器
  • SeparableConv2D:可分离卷积,进一步提取高层特征
  • 分类层:全连接 + Softmax
# EEGNet 核心代码(PyTorch风格)
class EEGNet(nn.Module):
    def __init__(self, chans=64, samples=128, classes=4):
        super().__init__()
        # 时间卷积:学习时间滤波器
        self.conv1 = nn.Conv2d(1, 8, kernel_size=(1, 64), padding=(0, 32))
        # 深度可分离:空间滤波
        self.depthwise = nn.Conv2d(8, 16, kernel_size=(chans, 1), groups=8)
        # 可分离卷积:特征组合
        self.separable = nn.Conv2d(16, 16, kernel_size=(1, 16), padding=(0, 8))
        # 分类头
        self.classifier = nn.Linear(16 * (samples // 4), classes)

    def forward(self, x):
        x = self.conv1(x)
        x = self.depthwise(x)
        x = self.separable(x)
        x = x.view(x.size(0), -1)
        return self.classifier(x)

避坑指南:我曾经在EEGNet里把时间卷积的kernel size设得太大(比如128),结果模型过拟合得一塌糊涂。后来我总结了一个经验:对于128Hz的采样率,kernel size取64就够,相当于0.5秒的窗口。太小了抓不到特征,太大了容易过拟合。

4.2 DeepConvNet——深度堆叠的力量

如果你追求的是分类精度,DeepConvNet是个不错的选择。它借鉴了VGGNet的设计思路,用多个卷积块堆叠,逐层提取更抽象的特征。

但说实话,DeepConvNet在EEG上有个致命问题:参数量太大。我试过在只有几百个样本的数据集上训练DeepConvNet,结果验证集准确率还不如EEGNet。为什么会这样?因为EEG数据量太小,深度模型很容易过拟合。

模型 参数量 推理时间(CPU) 推荐场景
EEGNet ~3K <5ms 实时系统、嵌入式设备
DeepConvNet ~200K ~20ms 离线分析、高精度需求
ShallowConvNet ~50K ~10ms 平衡方案

注意:DeepConvNet需要配合大量数据增强才能发挥效果。我建议至少准备5000个以上的样本,否则别碰它。

4.3 ShallowConvNet——折中之道

ShallowConvNet是DeepConvNet的“瘦身版”。它只有两个卷积块,但保留了空间滤波和时间滤波的核心设计。我个人觉得,ShallowConvNet是“性价比”最高的模型——参数不多,精度不差。

它的结构是这样的:

  • 第一层:时间卷积(kernel=25),提取低频特征
  • 第二层:空间卷积(kernel=C×1),做通道融合
  • 平均池化 + 全连接分类

你想想看,ShallowConvNet为什么适合EEG?因为EEG信号的主要能量集中在低频段(0.5-40Hz),浅层网络就能捕捉到这些特征。太深的网络反而会学到噪声。

4.4 Transformer for EEG——新思路

Transformer这两年火得不行,从NLP杀到了CV,现在又杀到了EEG。它的核心优势是长距离依赖建模——EEG信号的时间序列很长,Transformer的Self-Attention机制能捕捉到相隔很远的两个时间点之间的关系。

但我要泼一盆冷水:Transformer在EEG上并不总是好用。我踩过一个坑:直接把ViT(Vision Transformer)搬到EEG上,结果训练了三天都不收敛。后来才发现,EEG的时序结构跟图像完全不同,需要做专门的适配。

EEG Transformer 的关键改进:

  • 位置编码:用相对位置编码替代绝对位置编码,因为EEG的时序关系是相对的
  • 通道注意力:在Self-Attention中加入通道维度,让模型学会关注哪些通道更重要
  • 局部窗口:用滑动窗口限制Attention范围,避免计算量爆炸
# EEG Transformer 核心模块(简化版)
class EEGTransformerBlock(nn.Module):
    def __init__(self, d_model=64, nhead=4):
        super().__init__()
        self.attention = nn.MultiheadAttention(d_model, nhead)
        self.norm1 = nn.LayerNorm(d_model)
        self.ffn = nn.Sequential(
            nn.Linear(d_model, d_model * 4),
            nn.GELU(),
            nn.Linear(d_model * 4, d_model)
        )
        self.norm2 = nn.LayerNorm(d_model)

    def forward(self, x):
        # x: (seq_len, batch, d_model)
        attn_out, _ = self.attention(x, x, x)
        x = self.norm1(x + attn_out)
        ffn_out = self.ffn(x)
        x = self.norm2(x + ffn_out)
        return x

我的建议:如果你的数据量超过1万条,可以试试Transformer。否则,老老实实用EEGNet或ShallowConvNet。别问我怎么知道的——都是泪的教训。

4.5 模型选择决策流程

说了这么多,到底该选哪个?我画了一张流程图,帮你快速决策:

开始:EEG数据 数据量 > 5000? EEGNet 轻量、实时、稳定 需要高精度? DeepConvNet 高精度、需大量数据 时序依赖强? Transformer 长距离依赖、大模型 ShallowConvNet 平衡方案、性价比高

这张图是我在实际项目中总结出来的经验。说白了,没有最好的模型,只有最合适的模型。你想想看,如果数据量只有几百条,硬上Transformer,那不是自找麻烦吗?

4.6 实战建议

最后,我给出几条实战建议:

  1. 从EEGNet开始:不管什么任务,先用EEGNet跑个baseline。它训练快、调参简单,能帮你快速验证数据质量。
  2. 数据增强是王道:EEG数据太少了。我常用的增强方法有:加噪声、时间偏移、通道随机丢弃。效果立竿见影。
  3. 迁移学习:如果你有公开数据集(比如BCI Competition IV),可以先在上面预训练,再微调到你的数据上。我试过,准确率能提升5-10%。
  4. 别迷信Transformer:它确实强,但前提是你有足够的数据和算力。否则,CNN依然是EEG领域最可靠的选择。

总结一句话:EEGNet保底,ShallowConvNet进阶,DeepConvNet冲高,Transformer尝鲜。按这个顺序来,准没错。

专注资料整理