
vgg系列模型,如vgg16和vgg19,以其简洁的架构和在图像分类任务上的卓越性能而闻名。然而,从零开始训练这些深度模型常常面临诸多挑战,尤其是在数据量相对有限或数据集特性与imagenet等预训练数据集差异较大时。常见的训练问题包括模型收敛缓慢、准确率停滞不前甚至不学习。与参数量相对较小的alexnet相比,vgg模型更深,对初始权重、学习率、优化器选择以及数据预处理的敏感度更高。当模型在训练过程中准确率始终接近随机猜测(例如,对于160个类别的分类任务,准确率停留在0.005到0.008之间),这通常表明模型根本没有从数据中学习到有效特征。
在复现基于掌纹识别的CNN模型训练时,观察到AlexNet能够达到95%以上的测试准确率,而VGG16和VGG19模型在训练过程中准确率却始终无法突破0.1,表现出明显的学习失败。尽管尝试了原始VGG架构和论文中建议的简化版,结果依然如此。值得注意的是,使用预训练的VGG16权重进行迁移学习时,模型却能正常工作并达到高准确率。这暗示问题可能出在从零开始训练时的模型构建或数据处理环节。
经过仔细排查,问题最终被定位在模型定义中数据增强和归一化层的应用方式上。以下是原始VGG16模型构建代码片段:
def make_vgg16_model(input_shape, num_classes):
inputs = keras.Input(shape=input_shape)
# Block 1
x = data_augmentation(inputs) # 应用数据增强,结果赋值给x
x = layers.Rescaling(1.0 / 255)(inputs) # 应用归一化,但这里错误地再次使用了原始inputs,结果覆盖了上一步的x
x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(inputs) # 再次错误地使用了原始inputs
x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x)
x = layers.MaxPooling2D((2, 2), strides=(2, 2))(x)
# ... 后续层省略 ...问题解析:
在上述代码中,Block 1 的前三行存在逻辑错误:
影响:
由于模型接收到的是未经处理的原始图像数据,其梯度计算和参数更新将变得极其不稳定,导致模型无法有效收敛,表现为准确率始终停留在接近随机猜测的水平。
要解决此问题,只需确保数据在流经模型时,每个处理步骤都以前一个步骤的输出作为输入。
修正后的VGG16模型构建代码:
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
def make_vgg16_model_corrected(input_shape, num_classes):
inputs = keras.Input(shape=input_shape)
# 确保数据增强和归一化层按顺序作用于前一个层的输出
x = data_augmentation(inputs) # 首先应用数据增强
x = layers.Rescaling(1.0 / 255)(x) # 接着对增强后的数据进行归一化
# Block 1
x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x) # 卷积层现在接收的是已增强和归一化的数据
x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x)
x = layers.MaxPooling2D((2, 2), strides=(2, 2))(x)
# Block 2
x = layers.Conv2D(64, (3, 3), activation='relu', padding='same')(x)
x = layers.Conv2D(64, (3, 3), activation='relu', padding='same')(x)
x = layers.MaxPooling2D((2, 2), strides=(2, 2))(x)
# Block 3
x = layers.Conv2D(96, (3, 3), activation='relu', padding='same')(x)
x = layers.Conv2D(96, (3, 3), activation='relu', padding='same')(x)
x = layers.Conv2D(96, (3, 3), activation='relu', padding='same')(x)
x = layers.MaxPooling2D((2, 2), strides=(2, 2))(x)
# Block 4
x = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(x)
x = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(x)
x = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(x)
x = layers.MaxPooling2D((2, 2), strides=(2, 2))(x)
# Block 5
x = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(x)
x = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(x)
x = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(x)
x = layers.MaxPooling2D((2, 2), strides=(2, 2))(x)
# Flatten and Fully Connected Layers
x = layers.Flatten()(x)
x = layers.Dense(4096, activation='relu')(x)
x = layers.Dropout(0.5)(x)
x = layers.Dense(4096, activation='relu')(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(num_classes, activation='softmax')(x)
return keras.Model(inputs, outputs)
# 示例数据增强层定义(与原问题一致)
data_augmentation = keras.Sequential(
[
layers.RandomFlip("horizontal"),
layers.RandomRotation(0.1),
layers.RandomZoom(0.1),
layers.RandomContrast(0.1),
layers.RandomTranslation(0.1, 0.1),
layers.RandomHeight(0.1),
layers.RandomWidth(0.1),
]
)
# 使用修正后的模型进行训练
# model = make_vgg16_model_corrected(input_shape=image_size, num_classes=num_classes)
# model.compile(...)
# model.fit(...)注意事项:
VGG16和VGG19等深度卷积神经网络在从零开始训练时,对数据预处理的依赖性非常高。本案例突出显示了一个常见的、但容易被忽视的错误:数据预处理层(如数据增强和归一化)的输入连接错误,导致模型实际上接收到的是未经处理的原始数据。正确的数据流和适当的数据预处理是确保深度学习模型成功训练和有效收敛的基础。在构建复杂模型时,仔细检查每一层的输入输出,确保数据按预期方式流动,是避免此类问题的关键。
以上就是深度卷积神经网络VGG模型训练不收敛问题与数据预处理层应用解析的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号