一、项目背景与目标
街区餐饮场景中,存在大量的菜品图片、门店招牌图片、食材图片等视觉数据。传统人工分类标注效率低、成本高,而卷积神经网络(CNN) 作为计算机视觉领域的核心算法,具备强大的图像特征提取与分类能力。
本实践以街区餐饮图片分类为目标,构建基于CNN的识别模型,实现三大类餐饮图片的自动分类:
- 菜品类:火锅、烧烤、奶茶、快餐等细分菜品;
- 门店招牌类:中餐厅、西餐厅、小吃店等门店标识;
- 食材类:蔬菜、肉类、海鲜等原材料。
通过该模型,可应用于街区餐饮智能推荐、食品安全巡检、餐饮大数据统计等场景。
二、技术选型与环境准备
1. 核心技术栈
| 技术模块 | 选型 | 说明 |
|---|---|---|
| 深度学习框架 | TensorFlow 2.x/Keras | 高层API简洁易用,适合快速构建CNN模型 |
| 数据预处理 | OpenCV + PIL | 实现图像的读取、缩放、增强等操作 |
| 模型训练 | GPU(NVIDIA Tesla T4) | 加速CNN卷积层、池化层的计算 |
| 部署工具 | Flask | 搭建轻量级API服务,支持图片在线识别 |
2. 环境配置(Linux/Windows通用)
(1)安装依赖库
bash
# 安装TensorFlow与Keras
pip install tensorflow==2.15.0 keras==2.15.0
# 安装图像处理库
pip install opencv-python pillow matplotlib
# 安装数据处理与部署库
pip install pandas numpy flask
(2)验证环境
python
import tensorflow as tf
import cv2
print("TensorFlow版本:", tf.__version__)
print("GPU是否可用:", tf.config.list_physical_devices('GPU'))
print("OpenCV版本:", cv2.__version__)
三、数据集构建与预处理
1. 数据集采集
本项目采用自建数据集+公开数据集的混合方案:
- 公开数据集:下载Food-101、UEC-Food100等公开餐饮数据集,提取与街区餐饮相关的类别;
- 自建数据集 :实地拍摄街区餐饮门店的菜品、招牌、食材图片,共采集 3000张有效图片,按类别划分如下:
| 分类 | 子类别 | 样本数量 |
|---|---|---|
| 菜品类 | 火锅、烧烤、奶茶、快餐 | 1200张 |
| 门店招牌类 | 中餐厅、西餐厅、小吃店 | 1000张 |
| 食材类 | 蔬菜、肉类、海鲜 | 800张 |
数据集目录结构
street_food_dataset/
├── train/
│ ├── dish/
│ │ ├── hotpot/
│ │ ├── barbecue/
│ │ ├── milk_tea/
│ │ └── fast_food/
│ ├── signboard/
│ │ ├── chinese_restaurant/
│ │ ├── western_restaurant/
│ │ └── snack_bar/
│ └── ingredient/
│ ├── vegetable/
│ ├── meat/
│ └── seafood/
└── test/
├── dish/
├── signboard/
└── ingredient/
- 训练集与测试集按 7:3 比例划分,避免数据泄露。
2. 数据预处理
CNN模型对输入图像的尺寸、格式有严格要求,需执行以下预处理步骤:
(1)图像尺寸标准化
将所有图片统一缩放为 224×224×3(RGB三通道),匹配CNN模型输入尺寸。
(2)数据增强(解决过拟合)
对训练集图像执行随机增强,提升模型泛化能力:
- 随机旋转(±15°)
- 随机水平翻转(概率50%)
- 随机亮度调整(±10%)
- 像素值归一化(将像素值从0-255缩放到0-1)
(3)标签编码
将类别标签转换为one-hot编码,例如:
- 火锅 → [1,0,0,0,0,0,0,0,0]
- 中餐厅 → [0,0,0,0,1,0,0,0,0]
3. 数据加载
使用Keras的ImageDataGenerator实现数据批量加载:
python
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# 训练集数据增强配置
train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=15,
width_shift_range=0.1,
height_shift_range=0.1,
horizontal_flip=True,
fill_mode='nearest'
)
# 测试集仅做归一化
test_datagen = ImageDataGenerator(rescale=1./255)
# 加载训练集
train_generator = train_datagen.flow_from_directory(
'street_food_dataset/train',
target_size=(224, 224),
batch_size=32,
class_mode='categorical' # 多分类任务
)
# 加载测试集
test_generator = test_datagen.flow_from_directory(
'street_food_dataset/test',
target_size=(224, 224),
batch_size=32,
class_mode='categorical'
)
# 获取类别标签映射
class_indices = train_generator.class_indices
labels = dict((v,k) for k,v in class_indices.items())
print("类别标签映射:", labels)
四、CNN模型构建与训练
1. 模型架构设计
采用迁移学习 方案,基于预训练模型MobileNetV2构建轻量化CNN模型(适合边缘设备部署),相比从零构建CNN,迁移学习可大幅提升训练效率和识别精度。
模型架构分为三部分:
- 特征提取层:使用MobileNetV2预训练权重,冻结前90%的层,仅训练顶层网络;
- 特征融合层:添加全局平均池化层(GAP),减少参数数量;
- 分类层:添加全连接层+Dropout层(防止过拟合)+Softmax输出层。
2. 模型代码实现
python
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.layers import GlobalAveragePooling2D, Dense, Dropout
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
# 加载预训练模型MobileNetV2
base_model = MobileNetV2(
input_shape=(224, 224, 3),
include_top=False, # 不包含顶层分类层
weights='imagenet' # 加载ImageNet预训练权重
)
# 冻结特征提取层(仅训练顶层)
for layer in base_model.layers[:int(len(base_model.layers)*0.9)]:
layer.trainable = False
# 构建顶层分类网络
x = base_model.output
x = GlobalAveragePooling2D()(x) # 全局平均池化
x = Dense(512, activation='relu')(x) # 全连接层
x = Dropout(0.5)(x) # Dropout层,抑制过拟合
predictions = Dense(9, activation='softmax')(x) # 9分类输出
# 构建完整模型
model = Model(inputs=base_model.input, outputs=predictions)
# 模型编译
model.compile(
optimizer=Adam(learning_rate=0.0001), # 小学习率微调
loss='categorical_crossentropy', # 多分类损失函数
metrics=['accuracy'] # 评估指标:准确率
)
# 查看模型结构
model.summary()
3. 模型训练
设置训练参数,启动模型训练,并使用ModelCheckpoint和EarlyStopping回调函数优化训练过程:
python
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
# 回调函数配置
checkpoint = ModelCheckpoint(
'street_food_cnn_model.h5',
monitor='val_accuracy',
save_best_only=True,
mode='max',
verbose=1
)
early_stopping = EarlyStopping(
monitor='val_accuracy',
patience=5, # 5轮验证集准确率无提升则停止训练
mode='max',
verbose=1
)
# 启动训练
history = model.fit(
train_generator,
epochs=30, # 最大训练轮数
validation_data=test_generator,
callbacks=[checkpoint, early_stopping]
)
4. 训练结果可视化
绘制训练过程中的准确率和损失曲线,分析模型训练状态:
python
import matplotlib.pyplot as plt
# 绘制准确率曲线
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Train Accuracy')
plt.plot(history.history['val_accuracy'], label='Val Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
# 绘制损失曲线
plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Train Loss')
plt.plot(history.history['val_loss'], label='Val Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.savefig('training_history.png')
plt.show()
5. 模型评估
使用测试集评估模型最终性能:
python
# 加载最优模型
from tensorflow.keras.models import load_model
best_model = load_model('street_food_cnn_model.h5')
# 评估模型
test_loss, test_acc = best_model.evaluate(test_generator)
print(f"测试集损失:{test_loss:.4f}")
print(f"测试集准确率:{test_acc:.4f}")
本实践中,模型最终在测试集上的准确率可达 92.5%,满足街区餐饮图片识别的实际需求。
五、模型部署与实际应用
1. 单张图片识别测试
编写预测函数,实现单张街区餐饮图片的识别:
python
import numpy as np
from PIL import Image
def predict_image(image_path, model, labels):
# 加载并预处理图片
img = Image.open(image_path).resize((224, 224))
img_array = np.array(img) / 255.0
img_array = np.expand_dims(img_array, axis=0) # 增加batch维度
# 模型预测
predictions = model.predict(img_array)
predicted_class = np.argmax(predictions, axis=1)[0]
confidence = np.max(predictions, axis=1)[0]
# 返回识别结果
return labels[predicted_class], confidence
# 测试单张图片
image_path = "test_images/hotpot.jpg"
class_name, confidence = predict_image(image_path, best_model, labels)
print(f"识别结果:{class_name},置信度:{confidence:.2%}")
2. 搭建Flask在线识别服务
将模型部署为Web服务,支持用户上传图片并返回识别结果:
(1)编写Flask服务代码(app.py)
python
from flask import Flask, request, jsonify
from tensorflow.keras.models import load_model
import numpy as np
from PIL import Image
import io
app = Flask(__name__)
# 加载模型和标签
model = load_model('street_food_cnn_model.h5')
labels = {0: 'hotpot', 1: 'barbecue', 2: 'milk_tea', 3: 'fast_food',
4: 'chinese_restaurant', 5: 'western_restaurant', 6: 'snack_bar',
7: 'vegetable', 8: 'meat', 9: 'seafood'}
@app.route('/predict', methods=['POST'])
def predict():
if 'file' not in request.files:
return jsonify({'error': '未上传图片'})
file = request.files['file']
img = Image.open(io.BytesIO(file.read())).resize((224, 224))
img_array = np.array(img) / 255.0
img_array = np.expand_dims(img_array, axis=0)
predictions = model.predict(img_array)
predicted_class = np.argmax(predictions, axis=1)[0]
confidence = float(np.max(predictions, axis=1)[0])
return jsonify({
'class_name': labels[predicted_class],
'confidence': confidence
})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=False)
(2)启动服务并测试
bash
# 启动Flask服务
python app.py
# 使用curl测试接口
curl -X POST -F "file=@test_images/bbq.jpg" http://localhost:5000/predict
返回结果示例:
json
{"class_name": "barbecue", "confidence": 0.9685}
3. 实际应用场景
- 街区餐饮智能推荐:结合识别结果,向用户推荐附近的火锅、奶茶等门店;
- 食品安全巡检:自动识别食材类别,判断是否存在变质风险;
- 餐饮大数据统计:统计街区内各类型餐饮的占比,为商业规划提供数据支持。
六、模型优化与改进方向
- 数据层面:增加更多样本(尤其是小众菜品),引入更多数据增强策略(如随机裁剪);
- 模型层面:尝试更轻量的模型(如MobileNetV3),或使用模型量化技术(TensorFlow Lite),降低部署资源消耗;
- 功能层面:扩展模型识别类别(如新增甜品、饮品),实现多标签识别(一张图片同时识别菜品和食材)。
七、总结
本实践基于CNN迁移学习技术,成功构建了街区餐饮图片识别模型,实现了菜品、门店招牌、食材三大类别的高精度分类。通过迁移学习,大幅缩短了模型训练时间;通过Flask部署,实现了模型的快速落地应用。该方案可广泛应用于街区餐饮智能化管理场景,提升运维效率和用户体验。
我可以帮你整理该项目的完整代码清单,包含数据预处理、模型训练和部署的全部脚本,需要吗?