在计算机视觉领域,EfficientNet系列模型因其卓越的性能与效率平衡而广受欢迎。但很多开发者在使用自定义数据集训练时会遇到各种实际问题。本文将分享我从零开始训练EfficientNet-B0模型的全过程实录,包含从数据准备到模型部署的完整链路。
我最近在一个工业质检项目中使用EfficientNet-B0处理3000张自定义的零件缺陷图像,最终在测试集上达到了94.3%的准确率。整个过程踩过不少坑,也积累了一些实用技巧。下面就从最基础的环境配置开始,逐步拆解每个关键环节。
推荐使用Python 3.8+和TensorFlow 2.4+环境。我实际测试中发现,较新的CUDA 11.2与cuDNN 8.1组合在RTX 3060上能获得最佳性能:
bash复制pip install tensorflow-gpu==2.6.0
pip install efficientnet
注意:如果遇到CUDA版本冲突,可以尝试
conda install cudatoolkit=11.2指定版本
自定义数据集建议采用如下目录结构:
code复制dataset/
train/
class1/
img1.jpg
img2.jpg
class2/
img1.jpg
val/
class1/
img3.jpg
class2/
img3.jpg
关键点:
针对小样本数据集(<10k张),推荐使用以下增强组合:
python复制from tensorflow.keras.preprocessing.image import ImageDataGenerator
train_datagen = ImageDataGenerator(
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
实测发现:对于工业缺陷检测,垂直翻转可能会改变缺陷特征,建议禁用vertical_flip
使用EfficientNet的预训练权重时,需要注意冻结策略:
python复制from efficientnet.tfkeras import EfficientNetB0
base_model = EfficientNetB0(weights='imagenet', include_top=False)
# 冻结前100层(根据实际需求调整)
for layer in base_model.layers[:100]:
layer.trainable = False
# 添加自定义分类头
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(1024, activation='relu')(x)
predictions = Dense(num_classes, activation='softmax')(x)
采用余弦退火+热重启的学习率策略效果显著:
python复制initial_learning_rate = 0.001
lr_schedule = tf.keras.optimizers.schedules.CosineDecayRestarts(
initial_learning_rate,
first_decay_steps=1000,
t_mul=2.0,
m_mul=0.9
)
python复制model.compile(optimizer=Adam(lr_schedule),
loss='categorical_crossentropy',
metrics=['accuracy'])
history = model.fit(
train_generator,
steps_per_epoch=len(train_generator),
epochs=50,
validation_data=val_generator,
callbacks=[
EarlyStopping(patience=5),
ModelCheckpoint('best_model.h5')
]
)
经验值:batch_size建议设为32或64,太大容易导致GPU显存溢出
在支持Tensor Core的GPU上启用混合精度:
python复制policy = tf.keras.mixed_precision.Policy('mixed_float16')
tf.keras.mixed_precision.set_global_policy(policy)
实测在RTX 30系列显卡上可提速40%,显存占用减少35%。
EfficientNet默认输入尺寸为224x224,但可以根据需求调整:
python复制# 使用更大的输入尺寸(需要调整模型)
model = EfficientNetB0(input_shape=(300, 300, 3), weights=None)
# 或者保持原结构但resize输入
img = tf.image.resize(img, [224, 224])
注意:改变输入尺寸需要重新训练全部层,不能直接使用预训练权重
可能原因及解决方案:
典型过拟合应对方案:
优化建议:
tf.data.Dataset替代ImageDataGeneratorprefetch和cache:python复制dataset = dataset.cache().prefetch(buffer_size=tf.data.AUTOTUNE)
将模型转换为TensorRT格式:
python复制conversion_params = trt.TrtConversionParams(
precision_mode=trt.TrtPrecisionMode.FP16
)
converter = trt.TrtGraphConverterV2(
input_saved_model_dir='saved_model',
conversion_params=conversion_params
)
converter.convert()
converter.save('trt_model')
使用TFLite进行量化:
python复制converter = tf.lite.TFLiteConverter.from_saved_model('saved_model')
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()
在工业质检项目中,量化后的模型大小从86MB减小到23MB,推理速度提升3倍。
对于追求极致性能的场景,可以尝试:
我在实际项目中结合知识蒸馏和量化,最终将模型准确率从94.3%提升到95.1%,同时推理速度达到每秒120帧。