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损失的计算逻辑,导致模型无法有效学习特征与类别之间的关联。
其他关键问题
- 正则化过度:几乎每一层后都叠加了Dropout(0.5/0.3)和BatchNorm,在FER2013这类中等规模数据集上,过强的正则化直接导致模型欠拟合,无法捕捉到人脸表情的关键特征。
- 池化层选择不当:连续使用
AveragePooling2D会快速模糊面部细节(比如眼角、嘴角的细微变化),而人脸表情识别高度依赖这类细粒度特征,MaxPooling2D更适合保留关键特征信息。 - 多分类SVM标签格式问题:Keras的
squared_hinge损失用于多分类时,需要标签为{-1,1}格式或适配的one-hot编码,若你的数据集标签是0-6的整数索引,也会导致损失计算异常。
具体修正步骤
修复损失与输出层的匹配
- 方案一(遵循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'], )
- 方案一(遵循SVM逻辑):移除
削减正则化强度
- 降低Dropout比例:卷积层后Dropout从0.5调整为0.2-0.3,全连接层后从0.3调整为0.1-0.2;
- 减少BatchNorm使用:仅在卷积层之后或全连接层之前添加,避免每一层都叠加;
- 降低L2正则化系数:从0.01降至0.001或0.0001,减少对模型参数的约束。
替换池化层
将所有AveragePooling2D替换为MaxPooling2D,保留更多面部关键特征:layers.MaxPooling2D(pool_size=(2, 2))检查数据集预处理
- 确认像素值已正确归一化(例如缩放到[0,1]或[-1,1]);
- FER2013存在类别不平衡问题,可给少数类添加样本权重,或加入随机翻转、亮度调整等数据增强。
简化模型结构
可移除中间的256维全连接层,或减少卷积层的滤波器数量(比如把512改为256),避免模型过大导致正则化压力过高。
验证建议
先优先修正损失与激活的匹配问题,训练5-10个epoch观察准确率是否提升,再逐步调整其他参数。若仍欠拟合,继续降低正则化强度;若出现过拟合,再适当增加正则化。
内容的提问来源于stack exchange,提问作者Wolgwang
相关产品推荐
相关产品推荐

