带类别权重的ResNet模型表现异常的原因及相关技术咨询
模型训练结果与技术疑问
带类别权重版本的准确率和损失曲线图


无权重版本的准确率和损失曲线图


实现代码
两个版本的唯一区别在于是否调用类别权重字典(本人刚入门,欢迎对整体架构提出建议):
from tensorflow import keras from keras import optimizers from keras.applications.resnet_v2 import ResNet50V2 from tensorflow.keras.preprocessing import image from tensorflow.keras.models import Model from tensorflow.keras import layers from tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Rescaling, Conv2D, MaxPool2D, Flatten #Create datasets train_ds = tf.keras.preprocessing.image_dataset_from_directory( '/content/drive/MyDrive/Colab Notebooks/train/All classes/', labels="inferred", label_mode="int", validation_split=0.2, seed=1337, subset="training", ) val_ds = tf.keras.preprocessing.image_dataset_from_directory( '/content/drive/MyDrive/Colab Notebooks/train/All classes/', labels="inferred", label_mode="int", validation_split=0.2, seed=1337, subset="validation", ) test_ds = tf.keras.preprocessing.image_dataset_from_directory( '/content/drive/MyDrive/Colab Notebooks/test/All classes/', labels="inferred", label_mode="int", ) #Import ResNet base_model = ResNet50V2(weights='imagenet', include_top=False) #Create basic network to append to ResNet above x = base_model.output x = Rescaling(1.0 / 255)(x) x = Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=(256,256,3), padding="same")(x) x = MaxPool2D(pool_size=(2, 2), strides=2)(x) x = Conv2D(64, kernel_size=(3, 3), activation='relu')(x) x = MaxPool2D(pool_size=(2, 2), strides=2)(x) x = GlobalAveragePooling2D()(x) predictions = Dense(units=5, activation='softmax')(x) #Merge the models model = Model(inputs=base_model.input, outputs=predictions) #Freeze ResNet layers for layer in base_model.layers: layer.trainable = False #Compile model.compile(optimizer=keras.optimizers.Adam(1e-3), loss='sparse_categorical_crossentropy', metrics=['accuracy']) #These are the weights. They are derived here from their numbers in the train dataset -- there are 25,811 #files in class 0, 2444 files in class 1, etc. This dictionary was not called for the unweighted version. class_weight = {0: 1.0, 1: 25811.0/2444.0, 2: 25811.0/5293.0, 3: 25811.0/874.0, 4: 25811.0/709.0} #Training the model model_checkpoint_callback = tf.keras.callbacks.ModelCheckpoint( filepath='/content/drive/MyDrive/Colab Notebooks/ResNet/', save_weights_only=False, mode='auto', save_best_only=True, save_freq= 'epoch') history = model.fit( x=train_ds, epochs=30, class_weight=class_weight, validation_data=val_ds, callbacks=[model_checkpoint_callback] ) #Evaluating loss, acc = model.evaluate(test_ds) print("Accuracy", acc)
技术疑问
- metrics=['accuracy']是否应替换为metrics=['sparse_categorical_accuracy']?
- class_weight=class_weight是否应替换为sample_weight=sample_weight?查阅文档未明确区别,但多数示例使用class_weight。
- 仅在一个Conv2D层使用padding(为了编译通过的临时处理),是否应统一为所有Conv2D层添加padding?
- 附加在ResNet后的简易CNN子模型(即predictions部分)是否有更合理的架构设计?
- 未按照Keras官方ResNet指南预处理数据,是否值得优化?无权重模型准确率过高是否与此相关?
解答
1. 关于metrics选择
不用替换。Keras会根据你使用的损失函数自动匹配对应的准确率指标:当使用sparse_categorical_crossentropy时,metrics=['accuracy']会自动映射为sparse_categorical_accuracy,两者效果完全一致,直接写accuracy更简洁通用。
2. class_weight vs sample_weight
两者适用场景不同:
class_weight按类别分配权重,适合类别不平衡的场景,你当前类别样本数量差异极大,用这个完全正确,也是此类场景的常规选择。sample_weight给每个样本单独分配权重,适合单类别内样本重要性不同的场景(比如部分样本标注质量低)。你现在的需求是平衡类别,用class_weight足够,无需替换。
3. Conv2D的padding问题
建议统一添加padding="same"。你当前仅给第一个Conv2D加padding,会导致特征图尺寸计算逻辑不一致,甚至可能在输入尺寸非偶数时出现后续层尺寸不匹配的问题。统一加padding能保证卷积后特征图尺寸不变,逻辑更清晰,也能避免潜在的编译问题。另外,第一个Conv2D上的input_shape=(256,256,3)参数可以删除,因为输入来自ResNet的输出,该参数会被忽略。
4. ResNet后附加子模型的优化
当前的简易CNN存在冗余:ResNet本身已经完成了大量高阶语义特征提取,后续无需再叠加Conv+MaxPool结构,建议调整为:
- 直接在ResNet输出后接
GlobalAveragePooling2D(),再连接全连接层,这样更高效且不会破坏ResNet提取的特征。 - 若想微调特征,可以在GlobalAveragePooling2D后加1-2个全连接层(比如
Dense(256, activation='relu')),同时加入Dropout层(比如Dropout(0.5))防止过拟合,最后接分类输出层。 - 另外,
Rescaling(1.0 / 255)可以放到数据集预处理阶段(比如用train_ds = train_ds.map(lambda x, y: (tf.keras.layers.Rescaling(1./255)(x), y))),让预处理在数据加载时完成,提升训练效率。
5. 数据预处理的优化
非常值得优化,这可能是无权重模型准确率异常高的核心原因之一:
- ResNet50V2预训练时使用的是特定预处理逻辑(将像素值缩放到[-1,1]区间),你当前用的
Rescaling(1.0/255)得到的是[0,1]区间,与预训练的特征分布不匹配,会削弱模型的特征提取能力。正确做法是使用tf.keras.applications.resnet_v2.preprocess_input对图像进行预处理。 - 无权重模型准确率过高,本质是类别不平衡导致的:模型倾向于预测样本最多的类别(class 0),在准确率计算上得到高分,但实际泛化能力极差。你可以通过混淆矩阵验证这一点。使用类别权重后,模型会更关注少数类别,准确率可能下降,但F1-score、召回率等更能反映模型真实性能的指标会更合理。
额外架构建议
- 可以尝试解冻ResNet的顶层几层(比如最后10层)进行微调:先训练完附加的全连接层,再降低学习率(比如1e-5)微调ResNet层,避免破坏预训练的通用特征。
- 训练时加入数据增强(随机翻转、旋转、缩放等),能有效提升模型泛化能力,尤其缓解少数类别样本不足的问题。
内容的提问来源于stack exchange,提问作者brosefzai
相关产品推荐
相关产品推荐

