一、项目背景与目标
街区餐饮场景中,存在大量的菜品图片、门店招牌图片、食材图片等视觉数据。传统人工分类标注效率低、成本高,而卷积神经网络(CNN)作为计算机视觉领域的核心算法,具备强大的图像特征提取与分类能力。
本实践以街区餐饮图片分类为目标,构建基于CNN的识别模型,实现三大类餐饮图片的自动分类:
- 菜品类:火锅、烧烤、奶茶、快餐等细分菜品;
- 门店招牌类:中餐厅、西餐厅、小吃店等门店标识;
- 食材类:蔬菜、肉类、海鲜等原材料。
通过该模型,可应用于街区餐饮智能推荐、食品安全巡检、餐饮大数据统计等场景。
二、技术选型与环境准备
1. 核心技术栈
| 技术模块 | 选型 | 说明 |
|---|---|---|
| 深度学习框架 | TensorFlow 2.x/Keras | 高层API简洁易用,适合快速构建CNN模型 |
| 数据预处理 | OpenCV + PIL | 实现图像的读取、缩放、增强等操作 |
| 模型训练 | GPU(NVIDIA Tesla T4) | 加速CNN卷积层、池化层的计算 |
| 部署工具 | Flask | 搭建轻量级API服务,支持图片在线识别 |
2. 环境配置(Linux/Windows通用)
(1)安装依赖库
# 安装TensorFlow与Keraspipinstalltensorflow==2.15.0keras==2.15.0# 安装图像处理库pipinstallopencv-python pillow matplotlib# 安装数据处理与部署库pipinstallpandas numpy flask(2)验证环境
importtensorflowastfimportcv2print("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实现数据批量加载:
fromtensorflow.keras.preprocessing.imageimportImageDataGenerator# 训练集数据增强配置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)fork,vinclass_indices.items())print("类别标签映射:",labels)四、CNN模型构建与训练
1. 模型架构设计
采用迁移学习方案,基于预训练模型MobileNetV2构建轻量化CNN模型(适合边缘设备部署),相比从零构建CNN,迁移学习可大幅提升训练效率和识别精度。
模型架构分为三部分:
- 特征提取层:使用MobileNetV2预训练权重,冻结前90%的层,仅训练顶层网络;
- 特征融合层:添加全局平均池化层(GAP),减少参数数量;
- 分类层:添加全连接层+Dropout层(防止过拟合)+Softmax输出层。
2. 模型代码实现
fromtensorflow.keras.applicationsimportMobileNetV2fromtensorflow.keras.layersimportGlobalAveragePooling2D,Dense,Dropoutfromtensorflow.keras.modelsimportModelfromtensorflow.keras.optimizersimportAdam# 加载预训练模型MobileNetV2base_model=MobileNetV2(input_shape=(224,224,3),include_top=False,# 不包含顶层分类层weights='imagenet'# 加载ImageNet预训练权重)# 冻结特征提取层(仅训练顶层)forlayerinbase_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回调函数优化训练过程:
fromtensorflow.keras.callbacksimportModelCheckpoint,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. 训练结果可视化
绘制训练过程中的准确率和损失曲线,分析模型训练状态:
importmatplotlib.pyplotasplt# 绘制准确率曲线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. 模型评估
使用测试集评估模型最终性能:
# 加载最优模型fromtensorflow.keras.modelsimportload_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. 单张图片识别测试
编写预测函数,实现单张街区餐饮图片的识别:
importnumpyasnpfromPILimportImagedefpredict_image(image_path,model,labels):# 加载并预处理图片img=Image.open(image_path).resize((224,224))img_array=np.array(img)/255.0img_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]# 返回识别结果returnlabels[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)
fromflaskimportFlask,request,jsonifyfromtensorflow.keras.modelsimportload_modelimportnumpyasnpfromPILimportImageimportio 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'])defpredict():if'file'notinrequest.files:returnjsonify({'error':'未上传图片'})file=request.files['file']img=Image.open(io.BytesIO(file.read())).resize((224,224))img_array=np.array(img)/255.0img_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])returnjsonify({'class_name':labels[predicted_class],'confidence':confidence})if__name__=='__main__':app.run(host='0.0.0.0',port=5000,debug=False)(2)启动服务并测试
# 启动Flask服务python app.py# 使用curl测试接口curl-X POST -F"file=@test_images/bbq.jpg"http://localhost:5000/predict返回结果示例:
{"class_name":"barbecue","confidence":0.9685}3. 实际应用场景
- 街区餐饮智能推荐:结合识别结果,向用户推荐附近的火锅、奶茶等门店;
- 食品安全巡检:自动识别食材类别,判断是否存在变质风险;
- 餐饮大数据统计:统计街区内各类型餐饮的占比,为商业规划提供数据支持。
六、模型优化与改进方向
- 数据层面:增加更多样本(尤其是小众菜品),引入更多数据增强策略(如随机裁剪);
- 模型层面:尝试更轻量的模型(如MobileNetV3),或使用模型量化技术(TensorFlow Lite),降低部署资源消耗;
- 功能层面:扩展模型识别类别(如新增甜品、饮品),实现多标签识别(一张图片同时识别菜品和食材)。
七、总结
本实践基于CNN迁移学习技术,成功构建了街区餐饮图片识别模型,实现了菜品、门店招牌、食材三大类别的高精度分类。通过迁移学习,大幅缩短了模型训练时间;通过Flask部署,实现了模型的快速落地应用。该方案可广泛应用于街区餐饮智能化管理场景,提升运维效率和用户体验。
我可以帮你整理该项目的完整代码清单,包含数据预处理、模型训练和部署的全部脚本,需要吗?