You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TensorFlow中CNN最后层配置SVM的方法及训练异常排查

问题描述

偶然看到一篇名为《Deep Learning using Support Vector Machines》的论文,观看YouTube教程后,尝试在基于FER2013数据集的人脸表情识别模型中实现该方法。模型代码如下:

model = keras.Sequential([
    
    layers.Reshape((48, 48, 1), input_shape=(2304,)),
    
    layers.BatchNormalization(),
    layers.Conv2D(filters=64, kernel_size=3, activation='relu' ),
    layers.AveragePooling2D(pool_size=(2, 2)),
    layers.Dropout(0.5),
    
    layers.BatchNormalization(),
    layers.Conv2D(filters=128, kernel_size=3, activation='relu'),
    layers.AveragePooling2D(pool_size=(2, 2)),
    layers.Dropout(0.5),
    
    layers.BatchNormalization(),
    layers.Conv2D(filters=128, kernel_size=3, activation='relu'),
    layers.AveragePooling2D(pool_size=(2, 2)),
    layers.Dropout(0.5),
    
    layers.BatchNormalization(),
    layers.Conv2D(filters=512, kernel_size=3, activation='relu'),
    layers.AveragePooling2D(pool_size=(2, 2)),
    layers.Dropout(0.5),
    
    layers.Flatten(),
    
    layers.BatchNormalization(),
    layers.Dense(128, activation='relu'),
    layers.Dropout(0.3),
    
    layers.BatchNormalization(),
    layers.Dense(256, activation='relu'),
    layers.Dropout(0.3),
    
    layers.Dense(7, kernel_regularizer=tf.keras.regularizers.l2(0.01),activation
             ='softmax')
])

model.compile(
    optimizer='adam',
    loss = 'squared_hinge',
    metrics=['accuracy'],
)

但训练结果不符合预期,准确率接近随机猜测(7类随机准确率约14%):

Epoch 47/50
202/202 [==============================] - 4s 19ms/step - loss: 0.3469 - accuracy: 0.1308 - val_loss: 0.3508 - val_accuracy: 0.1299
Epoch 48/50
202/202 [==============================] - 4s 19ms/step - loss: 0.3469 - accuracy: 0.1326 - val_loss: 0.3508 - val_accuracy: 0.1378
Epoch 49/50
202/202 [==============================] - 4s 19ms/step - loss: 0.3469 - accuracy: 0.1617 - val_loss: 0.3508 - val_accuracy: 0.2426
Epoch 50/50
202/202 [==============================] - 4s 19ms/step - loss: 0.3469 - accuracy: 0.1594 - val_loss: 0.3508 - val_accuracy: 0.1291

同时附上训练曲线截图。请问哪里出错了?该如何解决?


问题分析与解决方案

核心错误:损失函数与输出激活不匹配

你使用了SVM专属的squared_hinge损失,但最后一层用了softmax激活,这是完全冲突的组合:

  • squared_hinge损失要求输出是未归一化的原始分数(logits),用于计算样本到分类超平面的距离;
  • softmax会将输出归一化为概率分布,彻底破坏了SVM损失的计算逻辑,导致模型无法有效学习特征与类别之间的关联。

其他关键问题

  1. 正则化过度:几乎每一层后都叠加了Dropout(0.5/0.3)和BatchNorm,在FER2013这类中等规模数据集上,过强的正则化直接导致模型欠拟合,无法捕捉到人脸表情的关键特征。
  2. 池化层选择不当:连续使用AveragePooling2D会快速模糊面部细节(比如眼角、嘴角的细微变化),而人脸表情识别高度依赖这类细粒度特征,MaxPooling2D更适合保留关键特征信息。
  3. 多分类SVM标签格式问题:Keras的squared_hinge损失用于多分类时,需要标签为{-1,1}格式或适配的one-hot编码,若你的数据集标签是0-6的整数索引,也会导致损失计算异常。

具体修正步骤

  1. 修复损失与输出层的匹配

    • 方案一(遵循SVM逻辑):移除softmax激活,直接输出logits,同时调整标签格式为{-1,1}或适配多分类的one-hot:
      # 最后一层改为无激活,输出原始logits
      layers.Dense(7, kernel_regularizer=tf.keras.regularizers.l2(0.01))
      # 若标签是0-6的整数,需转换为{-1,1}的多分类格式,例如:
      # y_train = 2 * tf.one_hot(y_train, depth=7) - 1
      
    • 方案二(快速验证):改用分类任务标准搭配,保留softmax并替换损失函数:
      model.compile(
          optimizer='adam',
          # 若标签是one-hot编码用categorical_crossentropy,整数索引用sparse_categorical_crossentropy
          loss='sparse_categorical_crossentropy',
          metrics=['accuracy'],
      )
      
  2. 削减正则化强度

    • 降低Dropout比例:卷积层后Dropout从0.5调整为0.2-0.3,全连接层后从0.3调整为0.1-0.2;
    • 减少BatchNorm使用:仅在卷积层之后或全连接层之前添加,避免每一层都叠加;
    • 降低L2正则化系数:从0.01降至0.001或0.0001,减少对模型参数的约束。
  3. 替换池化层
    将所有AveragePooling2D替换为MaxPooling2D,保留更多面部关键特征:

    layers.MaxPooling2D(pool_size=(2, 2))
    
  4. 检查数据集预处理

    • 确认像素值已正确归一化(例如缩放到[0,1]或[-1,1]);
    • FER2013存在类别不平衡问题,可给少数类添加样本权重,或加入随机翻转、亮度调整等数据增强。
  5. 简化模型结构
    可移除中间的256维全连接层,或减少卷积层的滤波器数量(比如把512改为256),避免模型过大导致正则化压力过高。

验证建议

先优先修正损失与激活的匹配问题,训练5-10个epoch观察准确率是否提升,再逐步调整其他参数。若仍欠拟合,继续降低正则化强度;若出现过拟合,再适当增加正则化。

内容的提问来源于stack exchange,提问作者Wolgwang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 13:38:21