MobileNetV2在CIFAR-10/100上精度未达预期的技术咨询
MobileNetV2在CIFAR数据集上精度不达预期的问题分析与解决
问题背景
基于MobileNetV2在CIFAR-10、CIFAR-100数据集开展实验,因MobileNetV2适配ImageNet(约224×224),通过tf.image.resize将CIFAR图像调整为96×96,但验证集精度未超80%(CIFAR-10约80%,CIFAR-100约55%),使用tf.applications.MobileNetV2结果一致。但资料显示该模型在这些数据集上精度通常超90%,现提出以下疑问:
- 我的代码是否存在错误?
- 资料中90%+精度是否基于CIFAR-10的10000张测试集?
- 仅用模型结构未调优是否是精度不足原因?需哪些调优方法?
- 为何官方MobileNetV2也无法达到预期精度?
问题解答
1. 代码中的潜在问题
你的代码存在多个影响精度的问题:
- 缺失数据增强:仅做了归一化和尺寸缩放,CIFAR数据集样本量小,数据增强是提升模型泛化能力的核心手段。
- 输入尺寸适配问题:MobileNetV2原设计针对224×224输入,96×96的小尺寸会大幅削弱深度可分离卷积的特征提取能力,无法捕捉足够的图像细节。
- 训练配置不完善:
- 未设置学习率衰减,固定Adam学习率在训练后期易导致模型震荡,难以收敛到最优解。
- 未添加正则化(L2正则、Dropout),即使MobileNetV2参数较少,在小数据集上仍可能出现过拟合。
- 直接用测试集做验证,未划分独立验证集,无法准确监控模型的泛化状态。
2. 资料中90%+精度的测试集说明
是的,资料中MobileNetV2在CIFAR-10上的90%+精度均基于官方的10000张测试集。但需注意,这类结果通常是在加载ImageNet预训练权重+精细调优、配合完善数据增强策略的前提下得到的,并非从零开始训练的结果。
3. 未调优是精度不足的核心原因,推荐调优方法
仅使用原始模型结构不做针对性调优,在CIFAR这类小数据集上很难达到预期精度,推荐以下调优手段:
- 数据增强:使用
ImageDataGenerator添加随机翻转、裁剪、旋转、亮度调整等操作,扩充训练样本多样性:
训练时改用datagen = ImageDataGenerator( rotation_range=15, width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True, zoom_range=0.1 ) datagen.fit(train_images)datagen.flow传入数据。 - 迁移学习:加载ImageNet预训练的MobileNetV2权重,先冻结底层特征提取层仅训练顶层分类器,再逐步解冻部分层精细调优:
base_model = tf.keras.applications.MobileNetV2( input_shape=(96,96,3), include_top=False, weights='imagenet' ) base_model.trainable = False x = base_model.output x = GlobalAveragePooling2D()(x) outputs = Dense(10, activation='softmax')(x) model = Model(inputs=base_model.input, outputs=outputs) - 学习率调度:使用
ReduceLROnPlateau或余弦退火学习率,让模型在训练后期稳步收敛:lr_scheduler = tf.keras.callbacks.ReduceLROnPlateau( monitor='val_loss', factor=0.5, patience=5, min_lr=1e-6 ) - 正则化与Dropout:在顶层分类器添加Dropout层,或在卷积层添加L2正则抑制过拟合:
x = Dense(1280, kernel_regularizer=tf.keras.regularizers.l2(0.001))(x) x = Dropout(0.5)(x) - 调整输入尺寸:尽量使用128×128或160×160的输入尺寸,更贴近MobileNetV2的设计初衷,提升特征提取效果。
4. 官方MobileNetV2无法达到预期精度的原因
- 预训练权重适配性差:官方MobileNetV2的预训练权重基于ImageNet的224×224图像训练,直接迁移到96×96的CIFAR图像上,底层特征(边缘、纹理)的分布差异较大,无法发挥预训练权重的优势。
- 训练策略差异:论文或官方示例中的结果使用了更完善的训练流程,包括更长的训练周期、精细化数据增强、学习率调度等,而你仅使用了基础训练配置。
- 数据集规模差异:ImageNet是百万级大型数据集,CIFAR是小数据集,模型的泛化能力需要针对性调优,直接套用官方模型结构和参数无法适配小数据集。
实验代码
#!/usr/bin/env python # coding: utf-8 # In[1]: test_list_acc = [0.7975999712944031] import statistics print(statistics.mean(test_list_acc)) # In[2]: import os os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' os.system("clear") from tensorflow import keras from tensorflow import keras from keras import layers, models, datasets, activations from keras.layers import Conv2D, Dense, Dropout, Flatten from keras.models import Sequential from keras.preprocessing.image import ImageDataGenerator from keras.models import Sequential from keras.layers import Conv2D from keras.layers import MaxPooling2D from keras.layers import GlobalMaxPooling2D from keras.layers import Dense from keras.layers import Flatten from matplotlib import pyplot from keras import datasets from keras.layers.core import Dense, Dropout, Activation, Flatten import numpy as np from keras.utils.np_utils import to_categorical import matplotlib.pyplot as plt # In[3]: from tensorflow.keras.layers import Conv2D, DepthwiseConv2D, ReLU, BatchNormalization, add,Softmax, AveragePooling2D, Dense, Input, GlobalAveragePooling2D from tensorflow.keras.models import Model # In[4]: import tensorflow as tf # from tensorflow import datasets print(tf.__version__) from tensorflow.python.client import device_lib device_lib.list_local_devices() # In[5]: import tensorflow as tf gpu_devices = tf.config.experimental.list_physical_devices('GPU') print(gpu_devices) #tf.config.experimental.set_memory_growth(gpu_devices[0], True) # In[6]: import os os.environ["CUDA_VISIBLE_DEVICES"] = "0" # In[7]: #import data (train_images,train_labels),(test_images,test_labels) = keras.datasets.cifar10.load_data() print("Training Images Shape (x train shape) :", train_images.shape) print("Label of training images (y train shape) :",train_labels.shape) print("Test Images Shape (x test shape) :",test_images.shape) print("Label of test images (y test shape) :",test_labels.shape) # In[8]: # np.reshape(train_images, (-1,224,224,3)) # train_images = train_images.reshape(50000,224,224,3) # available 96,128,160,192 train_images = tf.image.resize(train_images,[96,96]) test_images = tf.image.resize(test_images,[96,96]) print("Training Images Shape (x train shape) :", train_images.shape) print("Label of training images (y train shape) :",train_labels.shape) print("Test Images Shape (x test shape) :",test_images.shape) print("Label of test images (y test shape) :",test_labels.shape) # In[9]: train_images, test_images = train_images / 255, test_images / 255 # In[10]: def expansion_block(x,t,filters,block_id): prefix = 'block_{}_'.format(block_id) total_filters = t*filters x = layers.Conv2D(total_filters,1,padding='same',use_bias=False, name = prefix +'expand')(x) x = layers.BatchNormalization(name=prefix +'expand_bn')(x) x = layers.ReLU(6,name = prefix +'expand_relu')(x) return x def depthwise_block(x,stride,block_id): prefix = 'block_{}_'.format(block_id) x = layers.DepthwiseConv2D(3,strides=(stride,stride),padding ='same', use_bias = False, name = prefix + 'depthwise_conv')(x) x = layers.BatchNormalization(name=prefix +'dw_bn')(x) x = layers.ReLU(6,name=prefix +'dw_relu')(x) return x def projection_block(x,out_channels,block_id): prefix = 'block_{}_'.format(block_id) x = layers.Conv2D(filters = out_channels,kernel_size = 1,padding='same',use_bias=False,name= prefix + 'compress')(x) x = layers.BatchNormalization(name=prefix +'compress_bn')(x) return x # In[11]: def Bottleneck(x,t,filters, out_channels,stride,block_id): y = expansion_block(x,t,filters,block_id) y = depthwise_block(y,stride,block_id) y = projection_block(y, out_channels,block_id) if y.shape[-1]==x.shape[-1]: y = layers.add([x,y]) return y # In[14]: def MobileNetV2(input_image = (96,96,3), n_classes=10): # input = keras.Input(input_image) input = keras.Input(shape = input_image) x = layers.Conv2D(32,kernel_size=3,strides=(2,2),padding = 'same', use_bias=False)(input) x = layers.BatchNormalization(name='conv1_bn')(x) x = layers.ReLU(6, name = 'conv1_relu')(x) # 17 Bottlenecks x = depthwise_block(x,stride=1,block_id=1) x = projection_block(x, out_channels=16,block_id=1) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 24, stride = 2,block_id = 2) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 24, stride = 1,block_id = 3) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 32, stride = 2,block_id = 4) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 32, stride = 1,block_id = 5) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 32, stride = 1,block_id = 6) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 64, stride = 2,block_id = 7) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 64, stride = 1,block_id = 8) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 64, stride = 1,block_id = 9) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 64, stride = 1,block_id = 10) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 96, stride = 1,block_id = 11) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 96, stride = 1,block_id = 12) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 96, stride = 1,block_id = 13) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 160, stride = 2,block_id = 14) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 160, stride = 1,block_id = 15) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 160, stride = 1,block_id = 16) x = Bottleneck(x, t = 6, filters = x.shape[-1], out_channels = 320, stride = 1,block_id = 17) #1*1 conv x = layers.Conv2D(filters = 1280,kernel_size = 1,padding='same',use_bias=False, name = 'last_conv')(x) x = layers.BatchNormalization(name='last_bn')(x) x = layers.ReLU(6,name='last_relu')(x) #AvgPool 7*7 x = layers.GlobalAveragePooling2D(name='global_average_pool')(x) output = layers.Dense(n_classes,activation='softmax')(x) model = Model(input, output) return model # In[15]: n_classes = 10 input_shape = (96,96,3) model = MobileNetV2(input_shape,n_classes) model.summary() # In[16]: model.compile(loss="sparse_categorical_crossentropy", optimizer="Adam", metrics=["accuracy"]) # In[17]: #Fit the model hist= model.fit(train_images, train_labels, batch_size = 256, epochs=100, validation_data = (test_images, test_labels)) # In[18]: test_loss, test_acc = model.evaluate(test_images, test_labels, batch_size = 256) print("test loss : ", test_loss) print("test acc : ", test_acc) # In[19]: #loss curve plt.figure(figsize=[6,4]) plt.plot(hist.history['loss'], 'black', linewidth=2.0) plt.plot(hist.history['val_loss'], 'green', linewidth=2.0) # 어떤 그래프가 어떤 것인지 표시해준다 -> legend plt.legend(['Training Loss', 'validation Loss'], fontsize=14) plt.xlabel('Epochs', fontsize = 10) plt.ylabel('Loss', fontsize=10) plt.title('Loss Curves', fontsize =12) # In[20]: #Accuracy Curve plt.figure(figsize = [6,4]) plt.plot(hist.history['accuracy'], 'black', linewidth=2.0) plt.plot(hist.history['val_accuracy'], 'blue', linewidth=2.0) # 어떤 그래프가 어떤 것인지 표시해준다 -> legend plt.legend(['Training Accuracy', 'Validation Accuracy'], fontsize=14) plt.xlabel('Epochs', fontsize = 10) plt.ylabel('Accuracy', fontsize=10) plt.title('Accuracy Curves', fontsize = 12) # In[21]: prediction = model.predict(test_images) prediction prediction.shape history_dict = hist.history print(history_dict.keys())
内容的提问来源于stack exchange,提问作者danny lee
相关产品推荐
相关产品推荐

