基于DenseNet的胸片疾病分类模型验证精度瓶颈优化咨询
胸片疾病分类模型优化求助
本人刚入门AI,基于NIH ChestXray数据集构建胸片疾病分类模型,选用DenseNet121架构(对比EfficientNet、MobileNet表现最优),调整Adam优化器学习率(当前为1e-5)与clipnorm(当前为20.0),每轮epoch保存模型,但验证精度始终徘徊在0.44左右。尝试将学习率降至1e-6后,首epoch验证精度达0.457,但后续又回落至0.44左右,认为继续降学习率不可行,此前因出现Nan损失使用clipnorm,本次尝试已移除。
已针对数据集类别不平衡问题,通过计算类别权重惩罚少数类,代码如下:
clasification_data = pd.read_csv("/mnt/d/CXR8/PruneCXR/miccai2023_nih-cxr-lt_labels_train.csv",sep=",").values.tolist() training_frequencies = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] for clases in clasification_data: for i in range(0,20): training_frequencies[i] = training_frequencies[i] + clases[i+1] training_labels = [] for clases in clasification_data: training_labels.append(clases[1:-1]) total_samples = sum(training_frequencies) # Calculate weights (inverse of frequency) weights = [total_samples / freq for freq in training_frequencies] # Normalize weights (optional) normalized_weights = np.array(weights) / sum(weights) * len(training_frequencies) class_weight_dict = {i: weight for i, weight in enumerate(normalized_weights)} print("Class Weights:", class_weight_dict)
模型结构与编译代码
model = DenseNet121(weights=None, include_top=False) model = Model(inputs=model.input, outputs=Dense(20, activation="sigmoid")(GlobalAveragePooling2D()(model.output))) model.load_weights('./checkpoints/weights_epoch_24.weights.h5') adam_optimizer = Adam( learning_rate=0.00001, # Default: 0.001 clipnorm = 20.0 ) model.compile(optimizer=adam_optimizer, loss='binary_crossentropy', metrics=[Precision(),Accuracy()])
数据加载代码
def create_dynamic_loaded_data(csv_path): dummy = pd.read_csv(csv_path,sep=",").values.tolist() img_names = tf.convert_to_tensor([label[0] for label in dummy]) dummy = np.array([np.array(label[1:-1]) for label in dummy], dtype=np.float32) general_img_path = "/mnt/d/CXR8/images/" def preprocess_image(img_name, label): image = tf.io.read_file(general_img_path + img_name) image = tf.image.decode_jpeg(image, channels=3) # Ensure 3 channels image = tf.image.resize(image, [IMG_SIZE, IMG_SIZE]) # Resize to match input size image = image / 255.0 # Normalize to [0, 1] return image, label #create shuffle dummy_data = tf.data.Dataset.from_tensor_slices((img_names, dummy)) dummy_data = dummy_data.map(preprocess_image, num_parallel_calls=tf.data.AUTOTUNE) dummy_data = dummy_data.shuffle(buffer_size=1000, seed=42) dummy_data = dummy_data.batch(20).prefetch(tf.data.AUTOTUNE) return dummy_data train_dataset = create_dynamic_loaded_data("/mnt/d/CXR8/PruneCXR/miccai2023_nih-cxr-lt_labels_train.csv") validation_dataset = create_dynamic_loaded_data("/mnt/d/CXR8/PruneCXR/miccai2023_nih-cxr-lt_labels_val.csv")
训练代码
checkpoint_dir = "./checkpoints" os.makedirs(checkpoint_dir, exist_ok=True) # Define the checkpoint callback checkpoint_path = os.path.join(checkpoint_dir, "weights_epoch_{epoch:02d}.weights.h5") checkpoint_callback = ModelCheckpoint( filepath=checkpoint_path, save_weights_only=True, # Save only the weights save_best_only=False, # Save after every epoch verbose=1 ) history = [] with tf.device('/GPU:0'): history = model.fit(train_dataset, validation_data=validation_dataset, epochs=128, verbose=1, class_weight=class_weight_dict, callbacks=[checkpoint_callback], initial_epoch=24 )
可尝试的泛化能力提升方案
1. 数据增强优化
当前数据预处理仅做了resize和归一化,可针对胸片图像添加针对性增强:
- 几何变换:左右翻转(符合胸片医学逻辑)、小范围平移、±10°以内轻微旋转
- 像素变换:随机亮度/对比度调整、高斯噪声注入(模拟采集噪声)
- 集成到预处理的示例代码:
def preprocess_image(img_name, label): image = tf.io.read_file(general_img_path + img_name) image = tf.image.decode_jpeg(image, channels=3) image = tf.image.resize(image, [IMG_SIZE, IMG_SIZE]) # 仅训练阶段应用增强 if tf.keras.backend.learning_phase(): image = tf.image.random_flip_left_right(image) image = tf.image.random_brightness(image, max_delta=0.1) image = tf.image.random_contrast(image, lower=0.9, upper=1.1) image = image / 255.0 return image, label
2. 迁移学习优化
当前模型未使用预训练权重,DenseNet121在ImageNet上的预训练权重可大幅提升特征提取能力:
model = DenseNet121(weights='imagenet', include_top=False) # 冻结前N层,先训练顶层,后续再解冻微调 for layer in model.layers[:-10]: layer.trainable = False
3. 正则化策略补充
- Dropout层:在全局平均池化后添加Dropout层抑制过拟合:
model = Model(inputs=model.input, outputs=Dense(20, activation="sigmoid")(Dropout(0.5)(GlobalAveragePooling2D()(model.output))))
- L2正则化:在输出层添加权重正则项:
Dense(20, activation="sigmoid", kernel_regularizer=tf.keras.regularizers.l2(1e-4))
4. 学习率动态调度
放弃固定学习率,用调度器根据验证指标自动调整:
lr_scheduler = tf.keras.callbacks.ReduceLROnPlateau( monitor='val_precision', factor=0.5, # 学习率减半 patience=5, # 5轮无提升则调整 min_lr=1e-7 ) # 训练时加入该回调 history = model.fit(..., callbacks=[checkpoint_callback, lr_scheduler])
5. 损失函数调整
针对多标签不平衡问题,用Focal Loss替代二元交叉熵,降低易分类样本权重:
def focal_loss(gamma=2., alpha=0.25): def focal_loss_fixed(y_true, y_pred): pt_1 = tf.where(tf.equal(y_true, 1), y_pred, tf.ones_like(y_pred)) pt_0 = tf.where(tf.equal(y_true, 0), y_pred, tf.zeros_like(y_pred)) return -K.sum(alpha * K.pow(1. - pt_1, gamma) * K.log(pt_1)) - K.sum((1 - alpha) * K.pow(pt_0, gamma) * K.log(1. - pt_0)) return focal_loss_fixed model.compile(optimizer=adam_optimizer, loss=focal_loss(gamma=2), metrics=[Precision(),Accuracy()])
6. 数据采样优化
除类别权重外,可尝试加权随机采样、过采样少数类或欠采样多数类,进一步平衡训练分布。
内容的提问来源于stack exchange,提问作者No0bYebY
相关产品推荐
相关产品推荐

