You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 07:17:01