6、从STEW到DEAP:跨数据集CNN情感分类模型的泛化困境与调优实践
1. 跨数据集情感分类的挑战与现状在脑电信号EEG情感分类领域STEW和DEAP是两个被广泛使用的基准数据集。我最近在这两个数据集上尝试用自建的CNN模型进行情感分类时遇到了一个典型问题模型在训练集上能达到90%以上的准确率但在测试集上却只有60%左右。这种情况在STEW数据集上已经出现过现在迁移到DEAP数据集问题依然存在。这个问题其实反映了跨数据集情感分类的核心挑战。EEG信号本身就具有很高的个体差异性不同数据集采集环境、实验范式、被试群体都存在差异。比如DEAP数据集使用音乐视频作为情感诱发素材而STEW数据集则采用认知任务诱发压力状态。这种数据分布差异导致在一个数据集上训练良好的模型在另一个数据集上表现往往不尽如人意。我尝试过多种解决方案。首先是数据预处理方面除了常规的归一化处理这一步确实很关键不做归一化的模型精度会差很多还尝试了DWT和PSD特征提取。模型架构上参考了文献中常见的时域-空间域卷积组合加入了BN层和Dropout层防止过拟合。有趣的是我发现把BN层放在激活函数之后效果更好这与某些论文的建议一致。2. 从PyTorch到TensorFlow的迁移实验最初在STEW数据集上的实验是用PyTorch实现的为了验证问题的普遍性我在DEAP数据集上改用TensorFlow重新实现了模型。这里有个技术细节值得分享同样的模型结构用不同框架实现时性能表现确实会有差异。我记得在某个技术讨论中看到过这个现象可能与框架底层对计算图的优化方式有关。在TensorFlow实现中我设计了32个并行的1D-CNN子网络每个网络处理一个EEG通道的数据。基本结构包含4个卷积块每块含Conv1DBNReLU然后是Flatten层和全连接层。为了增强泛化能力我在最后一个全连接层前加入了Dropout层rate0.5。def create_models(dense_par20, sub_signals12, metrics METRICS): sample_size int(8064/sub_signals) models [0]*32 for i in range(32): models[i] Sequential() #block 1 models[i].add(Conv1D(filters32, kernel_size5,strides 3, input_shape(sample_size, 1))) models[i].add(BatchNormalization()) models[i].add(tf.keras.layers.Activation(relu)) #block 2-4 类似结构... #fc-1 models[i].add(Flatten()) models[i].add(Dense(dense_par, activationrelu)) models[i].add(Dropout(rate 0.5)) #输出层 if (inp_choice b): #二分类 models[i].add(Dense(2, activation softmax)) models[i].compile(optimizer tf.keras.optimizers.Adam(lr1e-4), loss tf.keras.losses.BinaryCrossentropy(), metrics metrics)3. 数据增强与模型调优实践面对训练集和测试集表现的巨大差距我首先怀疑的是数据量不足。DEAP数据集虽然有32名被试但每名被试只有1280个样本。为此我尝试了滑动窗口数据增强将原始8064长度的EEG信号分割为12段672长度的子信号。def process_input(instances, sub_signals): samples int(8064/sub_signals) transformed [] for i in range(instances.shape[0]): transformed.append(np.reshape(instances[i], (-1,samples,1))) transformed np.array(transformed) return transformed X_train_12 process_input(channel_wise, 12) y_train_12 np.repeat(y_train, 12, axis 0)数据增强后训练样本量扩大了12倍但测试准确率提升有限。于是我又尝试调整模型结构修改卷积核大小和步长确保最后一层池化输出的维度合理调整全连接层的神经元数量尝试了从40到100不等改变学习率从1e-3到1e-5都试过添加了ReduceLROnPlateau回调函数动态调整学习率reduce_lr ReduceLROnPlateau(monitorval_loss, factor0.2, patience5, min_lr1e-6) history model.fit(..., callbacks[reduce_lr])4. 跨数据集泛化问题的深度分析经过多次实验我总结了几个可能导致跨数据集泛化能力差的原因数据分布差异STEW数据集关注压力检测DEAP关注情绪识别两个数据集使用的EEG设备可能不同实验环境和被试群体存在差异模型架构局限性简单的CNN可能无法捕捉EEG信号的跨通道关系时域卷积对频域特征的提取不够充分没有考虑EEG信号的时间动态特性训练策略问题直接合并不同数据集训练可能导致模型混淆简单的数据归一化不足以消除数据集间差异类别不平衡问题在两个数据集表现不同为了验证这些假设我做了以下对比实验特征可视化用t-SNE降维显示两个数据集的特征分布域适应实验尝试在模型中加入域分类器混合训练用部分DEAP数据微调在STEW上预训练的模型# 特征可视化示例 from sklearn.manifold import TSNE tsne TSNE(n_components2) features model.predict(X_test) tsne_results tsne.fit_transform(features)5. 提升跨数据集性能的实用技巧基于这些实验我总结了几种可能改善跨数据集性能的方法数据层面更精细的频带划分θ、α、β、γ波分别处理使用共空间模式(CSP)等特征提取方法尝试域适应技术如CORAL对齐模型层面引入注意力机制捕捉重要时间点和通道尝试图卷积网络(GCN)建模通道间关系使用残差连接防止深层网络退化训练策略分阶段训练先预训练再微调使用对抗训练减小域间差异设计专门的正则化项约束特征分布一个改进的模型结构示例def build_adv_model(input_shape): # 共享特征提取层 inputs Input(shapeinput_shape) x Conv1D(32, 5, activationrelu)(inputs) x BatchNormalization()(x) # 任务分类头 y Flatten()(x) y Dense(2, activationsoftmax, nametask)(y) # 域分类头 d Flatten()(x) d Dense(1, activationsigmoid, namedomain)(d) return Model(inputs, [y, d])6. 实际项目中的经验教训在这个项目中我最大的体会是跨数据集EEG情感分类远比想象中复杂。即使采用了看似合理的模型结构和训练策略仍然可能遇到各种意料之外的问题。几个特别值得注意的教训不要过度依赖训练集准确率我的模型在训练集上能达到90%的准确率很容易让人误以为模型已经足够好实际上这可能掩盖了严重的泛化问题。数据预处理至关重要EEG信号的振幅范围、噪声特性在不同数据集间差异很大。简单的z-score归一化可能不够需要更精细的预处理流程。模型复杂度要适中太简单的模型无法捕捉EEG的复杂特征太复杂的模型又容易过拟合。需要通过大量实验找到平衡点。评估指标要全面除了准确率还要关注混淆矩阵、AUC等其他指标有时准确率掩盖了模型在某些类别上的严重缺陷。领域知识很重要纯数据驱动的方法在EEG分析中效果有限结合神经科学知识设计特征和模型往往更有效。7. 未来可能的改进方向虽然目前的结果还不尽如人意但我看到了几个有潜力的改进方向多模态融合结合生理信号如心率、皮肤电加入面部表情或语音信息利用实验刺激内容作为辅助信息自监督学习利用大量无标签EEG数据预训练设计适合EEG的对比学习任务学习更具泛化能力的表征个性化建模为不同被试建立个性化模型使用迁移学习快速适配新用户考虑被试的生理和心理特征一个有趣的现象是在多次实验中我发现模型在某些被试数据上表现明显好于其他被试。这可能暗示着存在某些影响模型性能的个体因素值得进一步研究。