1. 项目概述
猫狗图像分类是计算机视觉领域的经典入门项目,也是验证深度学习模型性能的绝佳试验场。作为一名长期从事计算机视觉开发的工程师,我经常用这个项目来测试新模型架构或训练技巧。这次分享的实战方案,不仅实现了基础CNN和迁移学习两种技术路线的完整对比,更重要的是加入了数据清洗、早停机制、可视化分析等工业级项目必备的工程化组件。
这个项目特别适合以下几类读者:
- 刚学完深度学习理论,想通过实战巩固知识的学生
- 需要快速搭建图像分类原型的AI产品经理
- 希望优化现有分类模型性能的算法工程师
我们将使用TensorFlow 2.15作为基础框架,这个版本在保持API稳定性的同时,对移动端部署做了特别优化。整个项目代码完全模块化,你可以直接复用各个组件到自己的分类任务中。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据准备与清洗
2.1 数据集获取与结构
Kaggle的猫狗数据集包含25,000张训练图片(猫狗各半)和12,500张测试图片。但直接使用原始数据会遇到两个典型问题:
- 部分图片在下载或存储过程中损坏
- 少量图片实际内容与标签不符
数据集标准结构应该是:
code复制data/
├── train/
│ ├── cat/
│ └── dog/
└── test/
├── cat/
└── dog/
2.2 自动化数据清洗
原始代码中的清洗函数已经能处理大部分损坏文件,但在实际项目中还需要扩展以下功能:
python复制import PIL
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True # 处理截断图片
def enhanced_cleaner(path):
valid_extensions = ['.jpg', '.jpeg', '.png']
for img_class in ['cat', 'dog']:
img_dir = Path(path) / img_class
for img_file in img_dir.glob('*'):
# 检查文件扩展名
if img_file.suffix.lower() not in valid_extensions:
img_file.unlink()
continue
try:
# 验证图片可读性
img = tf.keras.utils.load_img(img_file)
# 验证图片内容
img_array = tf.keras.utils.img_to_array(img)
if img_array.shape != (None, None, 3):
raise ValueError("Invalid image dimensions")
except Exception as e:
print(f"删除损坏文件: {img_file} - 错误: {str(e)}")
img_file.unlink()
enhanced_cleaner('data/train')
enhanced_cleaner('data/test')
提示:在生产环境中,建议将清洗日志保存到文件,方便后续分析数据质量问题。
2.3 数据增强策略
除了代码中提到的旋转、缩放和水平翻转,在实际项目中还可以加入更多增强方式:
python复制train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=30,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest',
brightness_range=[0.8, 1.2] # 亮度随机调整
)
这些增强策略的组合使用,可以使模型对输入变化更加鲁棒。但要注意,验证集数据不应该做任何增强,只进行归一化处理:
python复制test_datagen = ImageDataGenerator(rescale=1./255)
3. 模型构建与训练
3.1 基础CNN架构优化
原始文档中的基础CNN结构可以进一步优化,以下是改进后的版本:
python复制def build_cnn_model(input_shape=(150, 150, 3)):
model = tf.keras.Sequential([
# 卷积块1
tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=input_shape),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.MaxPooling2D(2,2),
# 卷积块2
tf.keras.l
