Keras多标签分类模型准确率偏低的排查与优化咨询
问题背景
我基于Keras搭建了一个多标签图像分类模型,但准确率一直卡在30%-40%,试过多种优化方法都没达到预期效果,想咨询可能的改进方向。
模型实现流程
1. 数据读取与预处理
- 读取含0/1矩阵的CSV文件:
df = pd.read_csv(url) print(df.head()) print(df.columns) - 读取并调整图像尺寸,归一化后存入X2:
image_directory = 'Directory/' dir_list = os.listdir(path) print("Files and directories in '", image_directory, "' :") # print the list print(dir_list)SIZE = 200 X_dataset = [] for i in tqdm(range(df.shape[0])): img2 = cv2.imread("Cell{}.png".format(i), cv2.IMREAD_UNCHANGED) img = tf.keras.preprocessing.image.load_img(image_directory +df['ID'][i], target_size=(SIZE,SIZE,3)) #numpy array of each image at size 200, 200, 3 (color) img = np.array(img) img = img/255. X_dataset.append(img) X2 = np.array(X_dataset) print(X2.shape) - 构建标签数据y2,形状为(1000,16):
y2 = np.array(df.drop(['Outcome', 'ID'], axis=1)) print(y2.shape) - 划分训练集与测试集:
X_train2, X_test2, y_train2, y_test2 = train_test_split(X2, y2, random_state=10, test_size=0.3)
2. 模型构建与编译
- 搭建CNN模型,包含4个Conv2D层和2个全连接层,输出层用sigmoid激活:
model2 = Sequential() model2.add(Conv2D(filters=16, kernel_size=(10, 10), activation="relu", input_shape=(SIZE,SIZE,3))) model2.add(BatchNormalization()) model2.add(MaxPooling2D(pool_size=(5, 5))) model2.add(Dropout(0.2)) model2.add(Conv2D(filters=32, kernel_size=(5, 5), activation='relu')) model2.add(MaxPooling2D(pool_size=(2, 2))) model2.add(BatchNormalization()) model2.add(Dropout(0.2)) model2.add(Conv2D(filters=64, kernel_size=(5, 5), activation="relu")) model2.add(MaxPooling2D(pool_size=(2, 2))) model2.add(BatchNormalization()) model2.add(Dropout(0.2)) model2.add(Conv2D(filters=128, kernel_size=(3, 3), activation='relu')) model2.add(MaxPooling2D(pool_size=(2, 2))) model2.add(BatchNormalization()) model2.add(Dropout(0.2)) model2.add(Flatten()) model2.add(Dense(512, activation='relu')) model2.add(Dropout(0.5)) model2.add(Dense(128, activation='relu')) model2.add(Dropout(0.5)) model2.add(Dense(16, activation='sigmoid')) #Do not use softmax for multilabel classification #Softmax is useful for mutually exclusive classes, either cat or dog but not both. #Also, softmax outputs all add to 1. So good for multi class problems where each #class is given a probability and all add to 1. Highest one wins. #Sigmoid outputs probability. Can be used for non-mutually exclusive problems. #like multi label, in this example. #But, also good for binary mutually exclusive (cat or not cat). model2.summary() - 使用自定义Adamax优化器,损失函数为binary_crossentropy:
opt = tf.keras.optimizers.Adamax( learning_rate=0.02, beta_1=0.8, beta_2=0.9999, epsilon=1e-9, name='Adamax') model2.compile(optimizer=opt, loss='binary_crossentropy', metrics=['accuracy', 'mse' ])
3. 模型训练
- 设置样本权重(以类别0的样本数为基准计算):
sample_weight = { 0:1, 1:0.5197368421, 2:0.4385964912, 3:0.2324561404, 4:0.2302631579, 5:0.399122807, 6:0.08114035088, 7:0.5723684211, 8:0.08552631579, 9:0.2061403509, 10:0.3815789474, 11:0.125, 12:0.08333333333, 13:0.1206140351, 14:0.1403508772, 15:0.4824561404 } - 启动训练:
history = model2.fit(X_train2, y_train2, epochs=25, validation_data=(X_test2, y_test2), batch_size=64, class_weight = sample_weight, shuffle = False)
核心问题
模型准确率最高仅30%-40%,部分模型倾向于预测单一类别,效果较好的模型虽有概率分布但表现仍不理想。同时怀疑样本权重设置是否倒置(类别0样本量最大,是不是应该让少数类权重更高)。
已尝试的优化手段
- 调整batch_size至16或8,训练耗时增加,准确率略有提升;
- 降低学习率,需要更多epoch才能获得小幅准确率提升;
- 增加epoch至100,准确率在20轮左右进入平台期;
- 增加模型参数(如卷积核数量、全连接层神经元数),导致内存占用过高且无明显效果;
- 更换优化器为Adam、RAdam,效果无显著提升;
- 清洗CSV数据与图像数据,因担心数据丢失未大量删除。
训练日志示例
14/14 [==============================] - 79s 6s/step - loss: 0.4528 - accuracy: 0.2592 - mse: 0.1594 - val_loss: 261.8521 - val_accuracy: 0.3881 - val_mse: 0.1416 Epoch 2/25 14/14 [==============================] - 85s 6s/step - loss: 0.2817 - accuracy: 0.3188 - mse: 0.1310 - val_loss: 22.7037 - val_accuracy: 0.3881 - val_mse: 0.1416 ... Epoch 13/25 12/14 [========================>.....] - ETA: 10s - loss: 0.2092 - accuracy: 0.3945 - mse: 0.1044
改进建议
1. 修正类别权重设置
你的权重完全搞反了:类别0样本量最大,权重应该最低;少数类样本量少,权重应该更高。正确的计算方式可以用总样本数/(类别样本数×类别总数),或者直接取类别0样本数/目标类别样本数,让少数类权重远大于1,强迫模型重视这些样本。
2. 优化数据预处理
- 删掉冗余的
cv2.imread代码,你只用到了load_img的结果,避免代码混淆。 - 核对标签有效性:确认每个样本的标签是标准的0/1多热编码,检查是否存在某类标签全0/全1的情况,这类问题会直接导致模型学习偏差。
- 加入数据增强:用
tf.keras.layers.RandomFlip、RandomRotation、RandomZoom等内置层,或者ImageDataGenerator,对训练图像做随机翻转、旋转、亮度调整等操作,扩充数据量,缓解过拟合。
3. 调整模型结构
- 优化池化策略:第一个MaxPooling用(5,5)太激进,直接把200×200的图像压缩到40×40,丢失大量细节。建议改成(2,2),后续池化也适当缩小步长,保留更多特征。
- 简化全连接层:512+128的神经元数量对于16类多标签任务冗余度高,容易过拟合。可以改成256+64,或者用
GlobalAveragePooling2D替代Flatten,减少参数同时保留全局特征。 - 尝试迁移学习:用ResNet50、EfficientNetB0等预训练模型做特征提取,冻结底层权重只训练顶层分类层,预训练模型已经学习了通用图像特征,能大幅提升小数据集的效果。
4. 优化训练策略
- 开启数据打乱:把
fit里的shuffle=False改成True,让模型每次epoch都随机读取数据,避免陷入局部最优。 - 加入学习率调度器:用
ReduceLROnPlateau回调,当验证损失不再下降时自动降低学习率,比手动调参更高效。示例代码:from tensorflow.keras.callbacks import ReduceLROnPlateau lr_scheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, min_lr=1e-5) # 在fit中加入callbacks=[lr_scheduler] - 换用贴合任务的评估指标:多标签任务中普通
accuracy意义不大(需所有标签预测正确才算对),改用precision、recall、f1-score或hamming_loss评估,这些指标更能反映模型实际表现,可通过sklearn.metrics计算。
5. 彻底清洗数据
不要怕数据丢失:直接删除标签错误、图像模糊/损坏的样本,这类脏数据会严重干扰模型学习。同时统计各类别样本分布,对样本量极少(如个位数)的类别,要么补充数据,要么暂时剔除,避免模型被极端样本带偏。
内容的提问来源于stack exchange,提问作者I_Write_Bad_Code123
相关产品推荐
相关产品推荐

