VGG16肺部影像三分类:验证准确率异常波动/固定问题求助
COVID、肺炎、正常肺部三分类VGG16实现及优化问题
数据加载与预处理代码
# importing necessary packages import cv2,os import numpy as np import seaborn as sns import matplotlib.pyplot as plt from tensorflow.keras.applications import VGG19 from tensorflow.keras.layers import AveragePooling2D from tensorflow.keras.layers import Dropout from tensorflow.keras.layers import Flatten from tensorflow.keras.layers import Dense from tensorflow.keras.layers import Input from tensorflow.keras.models import Model from tensorflow.keras.optimizers import Adam from tensorflow.keras.optimizers import RMSprop from tensorflow.keras.optimizers import SGD from tensorflow.keras.applications.vgg19 import preprocess_input from tensorflow.keras.preprocessing.image import img_to_array from tensorflow.keras.preprocessing.image import load_img from sklearn.preprocessing import OneHotEncoder from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report from sklearn.metrics import confusion_matrix from sklearn.metrics import plot_confusion_matrix from tensorflow.keras import models, layers from google.colab import drive drive.mount('/content/drive') import zipfile with zipfile.ZipFile("New_1300_dataset.zip","r") as zip_ref: zip_ref.extractall("DataSet") datapath = "/content/DataSet/New_1300_dataset" categories = os.listdir(datapath) label_cat = [] for i in range(len(categories)): label_cat.append(i) print(categories) print(label_cat) # initializing the parameters like initial learning rate, number of epochs, batch size initial_LR = 1e-3 num_epochs = 50 b_size = 32 # iterating through all images and appending them to list print("[Information] Loading All the Images..") data = [] labels = [] for category in categories: path = os.path.join(datapath, category) img_names = os.listdir(path) for img in img_names: img_path = os.path.join(path, img) image = load_img(img_path, target_size=(64, 64)) image = img_to_array(image) image = preprocess_input(image) data.append(image) labels.append([category]) # one hot encoding the output .. encoder = OneHotEncoder(sparse=False) labels = encoder.fit_transform(labels) data = np.array(data, dtype="float32") labels = np.array(labels) (x_train, x_test, y_train, y_test) = train_test_split(data, labels, test_size=0.1, stratify=labels, random_state=42) # Normalizing the data (i.e.. zero mean and unit variance) mean = np.mean(x_train, axis = (0,1,2,3)) std = np.std(x_train, axis = (0,1,2,3)) x_train = (x_train-mean)/(std + 1e-7) x_test = (x_test-mean)/(std + 1e-7)
定义模型代码
model = models.Sequential([ layers.Conv2D(filters=64, kernel_size=(3, 3), padding='same', input_shape=(64,64,3)), layers.BatchNormalization(), layers.Activation('relu') , layers.Conv2D(filters=64, kernel_size=(3, 3), padding='same'), layers.BatchNormalization(), layers.Activation('relu'), layers.MaxPooling2D((2, 2)), layers.Dropout(0.5), layers.Conv2D(filters=128, kernel_size=(3, 3), padding='same'), layers.BatchNormalization(), layers.Activation('relu'), layers.Conv2D(filters=128, kernel_size=(3, 3), padding='same'), layers.BatchNormalization(), layers.Activation('relu'), layers.MaxPooling2D((2, 2)), layers.Dropout(0.5), layers.Conv2D(filters=256, kernel_size=(3, 3), padding='same'), layers.BatchNormalization(), layers.Activation('relu'), layers.Conv2D(filters=256, kernel_size=(3, 3), padding='same'), layers.BatchNormalization(), layers.Activation('relu'), layers.Conv2D(filters=256, kernel_size=(3, 3), padding='same'), layers.BatchNormalization(), layers.Activation('relu'), layers.MaxPooling2D((2, 2)), layers.Dropout(0.5), layers.Conv2D(filters=512, kernel_size=(3, 3), padding='same'), layers.BatchNormalization(), layers.Activation('relu'), layers.Conv2D(filters=512, kernel_size=(3, 3), padding='same'), layers.BatchNormalization(), layers.Activation('relu'), layers.Conv2D(filters=512, kernel_size=(3, 3), padding='same'), layers.BatchNormalization(), layers.Activation('relu'), layers.MaxPooling2D((2, 2)), layers.Dropout(0.5), layers.Conv2D(filters=512, kernel_size=(3, 3), padding='same'), layers.BatchNormalization(), layers.Activation('relu'), layers.Conv2D(filters=512, kernel_size=(3, 3), padding='same'), layers.BatchNormalization(), layers.Activation('relu'), layers.Conv2D(filters=512, kernel_size=(3, 3), padding='same'), layers.BatchNormalization(), layers.Activation('relu'), layers.MaxPooling2D((2, 2)), layers.Dropout(0.5), layers.Flatten(), layers.Dense(512, activation='relu'), layers.Dense(512, activation='relu'), layers.Dense(3, activation='softmax') ]) opt = RMSprop(learning_rate=1e-4) model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
问题描述
针对COVID、肺炎、正常肺部三分类任务,每个类别各1300张图像。实现了VGG16结构的模型,在每个Conv2D层后添加BatchNormalization,每个MaxPooling层后添加0.5比例的Dropout以抑制过拟合。训练后得到的准确率曲线显示:训练准确率逐步上升至接近100%,但验证准确率波动极大,在30%-90%区间反复震荡。尝试更换Adam、SGD等优化器后,验证准确率固定在33.24%无变化,调整学习率也未改善效果。
改进建议
修正数据预处理流程:
代码中先使用了preprocess_input(适配VGG模型的预处理,将像素值转换为[-127.5,127.5]范围),后续又做了自定义的均值/std归一化,这会破坏预训练模型期望的数据分布,导致模型无法有效学习。直接删除自定义归一化的代码块。调整模型结构:
- 输入尺寸修改:VGG系列模型的标准输入尺寸为224x224,当前使用64x64输入,经过5次MaxPooling后特征图尺寸仅为2x2,无法捕捉肺部X射线的全局特征。将
load_img的target_size改为(224,224),同时修改模型的input_shape为(224,224,3)。 - 降低Dropout比例:0.5的Dropout在每个MaxPool后添加,过度抑制了特征学习,建议将Dropout比例调整为0.2-0.3,或仅在全连接层后添加Dropout。
- 简化全连接层:输入尺寸改为224x224后,Flatten后的特征维度足够,可去掉一个512的全连接层,减少参数冗余。
- 输入尺寸修改:VGG系列模型的标准输入尺寸为224x224,当前使用64x64输入,经过5次MaxPooling后特征图尺寸仅为2x2,无法捕捉肺部X射线的全局特征。将
优化训练策略:
- 检查标签正确性:验证准确率固定在33%(随机猜测的准确率),首先确认标签与图像类别是否正确对应,排除数据标注错误的可能。
- 调整优化器与学习率:使用SGD优化器时,初始学习率设为1e-2,配合
ReduceLROnPlateau回调函数,当验证损失停滞时自动降低学习率,比Adam/RMSprop更稳定;使用Adam时,初始学习率设为1e-5尝试。 - 添加早停机制:使用
EarlyStopping回调函数,监控val_loss,当连续3-5个epoch验证损失未下降时停止训练,避免无效迭代。 - 加入数据增强:针对肺部X射线数据,使用
ImageDataGenerator实现随机水平翻转、小角度旋转、缩放等增强操作,提升模型泛化能力。示例代码:from tensorflow.keras.preprocessing.image import ImageDataGenerator datagen = ImageDataGenerator( horizontal_flip=True, rotation_range=10, zoom_range=0.1 ) datagen.fit(x_train) # 训练时使用datagen.flow history = model.fit(datagen.flow(x_train, y_train, batch_size=b_size), epochs=num_epochs, validation_data=(x_test, y_test))
采用迁移学习:
代码中导入了VGG19但未使用,直接从头训练VGG-like模型效率低。建议加载预训练的VGG16权重,冻结前几层卷积层,仅训练顶部的分类层,示例:base_model = VGG16(weights='imagenet', include_top=False, input_shape=(224,224,3)) # 冻结基础模型 for layer in base_model.layers: layer.trainable = False # 添加自定义分类头 x = base_model.output x = Flatten()(x) x = Dense(512, activation='relu')(x) x = Dropout(0.3)(x) predictions = Dense(3, activation='softmax')(x) model = Model(inputs=base_model.input, outputs=predictions) model.compile(optimizer=Adam(learning_rate=1e-4), loss='categorical_crossentropy', metrics=['accuracy'])
内容的提问来源于stack exchange,提问作者vamsi bharadwaj
相关产品推荐
相关产品推荐

