TensorFlow花卉图像分类模型准确率无法突破20%问题排查
问题背景
求职面试时被要求使用Python基于TensorFlow搭建神经网络,完成花卉图像数据集分类任务。按常规逻辑编写的代码理论上应该正常生效,但模型准确率始终无法提升到20%以上。
运行环境
Python Version: 3.8.13
TensorFlow Version: 2.4.1
题目约束
面试官提供了固定的数据预处理方法,不允许修改预处理函数的内部逻辑,仅允许调整IMG_SIZE、BATCH_SIZE两个参数,给定的预处理代码如下:
# create datase IMG_SIZE = 160 BATCH_SIZE = 32 AUTOTUNE = tf.data.experimental.AUTOTUNE def _parse_data(x,y): image = tf.io.read_file(x) image = tf.image.decode_jpeg(image, channels=3) image = tf.cast(image, dtype=tf.float32) image = tf.math.l2_normalize(image) image = tf.image.resize(image, (IMG_SIZE, IMG_SIZE)) return image,y def _input_fn(x,y): ds = tf.data.Dataset.from_tensor_slices((x,y)) ds = ds.map(_parse_data) ds = ds.shuffle(buffer_size=data_size) ds = ds.repeat() ds = ds.batch(BATCH_SIZE) ds = ds.prefetch(buffer_size=AUTOTUNE) return ds train_ds = _input_fn(x_train, y_train) validation_ds = _input_fn(x_valid, y_valid)
生成的训练集与验证集属性如下:
return (<PrefetchDataset shapes: ((None, 160, 160, 3), (None,)), types: (tf.float32, tf.int32)> )
网络搭建要求必须使用model_seq.add()方式堆叠网络层,仅输出层为固定内容不允许修改:
model_seq.add(layers.Dense(len(label_names), activation='softmax'))
当时搭建的Sequential网络结构如下:
from tensorflow.keras import datasets, layers, models model_seq = models.Sequential() model_seq.add(layers.experimental.preprocessing.RandomFlip("horizontal",input_shape=(IMG_SIZE,IMG_SIZE,3))) model_seq.add(layers.experimental.preprocessing.RandomRotation(0.2)) model_seq.add(layers.experimental.preprocessing.Rescaling(1./255)) model_seq.add(layers.Conv2D(16, 3, padding='same', activation='relu')) model_seq.add(layers.MaxPooling2D()) model_seq.add(layers.Conv2D(32, 3, padding='same', activation='relu')) model_seq.add(layers.MaxPooling2D()) model_seq.add(layers.Conv2D(64, 3, padding='same', activation='relu')) model_seq.add(layers.MaxPooling2D()) model_seq.add(layers.Dropout(0.2)) model_seq.add(layers.Flatten()) model_seq.add(layers.Dense(128, activation='relu')) model_seq.add(layers.Dense(len(label_names), activation='softmax')) model_seq.summary()
模型编译使用的参数:
model_seq.compile(optimizer="Adam", loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=['accuracy'])
模型训练调用代码:
history = model_seq.fit(train_ds,epochs=20, validation_data = validation_ds, steps_per_epoch=100,validation_steps=100)
已尝试的无效调整方案
- 更换不同的数据增强方法,或直接移除网络中的全部数据增强模块
- 调整BATCH_SIZE、IMG_SIZE参数取值
- 新增Dropout层试图优化过拟合问题
- 加入EarlyStopping回调,对应实现代码如下:
callback = tf.keras.callbacks.EarlyStopping( monitor='val_loss', min_delta=0, patience=3, verbose=0, mode='auto',baseline=None, restore_best_weights=True) history = model_seq.fit(train_ds,epochs=20, validation_data = validation_ds, steps_per_epoch=100,validation_steps=100, callbacks = [callback])
所有调整完成后模型依然无法获得正常分类效果,需要定位问题根源并总结相关经验。
核心问题定位
准确率上不去是两个非常容易忽略的API细节错误导致的,和网络深度、参数大小没有关系:
- 损失函数参数配置完全错误
输出层明确使用了softmax激活,输出的已经是取值在0-1之间、总和为1的合法概率分布,但编译时给SparseCategoricalCrossentropy传了from_logits=True参数。这个参数的作用是告诉框架“当前模型输出的是没有经过激活的原始logit值”,框架会在内部额外做一次softmax计算损失,等于对输出连续做了两次softmax变换,损失计算逻辑完全错乱,梯度更新方向根本不对,模型自然学不到有效规律。 - 重复归一化导致输入数值分布异常
给定的预处理函数里已经调用tf.math.l2_normalize(image)对图像像素做了L2归一化,结果又在网络开头加了一层Rescaling(1./255),等于把已经归一化到固定范围的像素值再缩小255倍,输入数值整体小了三个数量级,卷积层根本提取不到有效特征。
修正方案
在不改动给定预处理代码、不改动固定输出层的前提下,仅需要两处修改就能让模型准确率快速升到85%以上:
- 删除网络中多余的
Rescaling(1./255)层,预处理阶段已经完成归一化,不需要重复做像素缩放 - 编译模型时将损失函数的
from_logits=True改为from_logits=False,或者直接删除该参数使用默认值False,和输出层的softmax激活逻辑匹配即可
经验总结
- 使用TensorFlow交叉熵损失时,必须先确认输出层的激活配置:输出层带softmax/sigmoid激活就必须设置
from_logits=False,只有输出层直接输出无激活的原始值时才设from_logits=True,这个参数配置错误的话模型基本不可能正常收敛 - 拿到固定预处理代码时,先逐行确认每一步对数据做了什么变换,绝对不要重复做归一化、标准化类的操作,输入数值范围不符合预期的话,再复杂的网络结构也没用
- 面试场景下遇到“不准修改给定预处理逻辑”的要求,不要直接套用平时写代码的习惯随便加预处理层,很容易踩到重复操作的坑
内容的提问来源于stack exchange,提问作者Virgal K
相关产品推荐
相关产品推荐

