ResNet18实战教程:食品识别营养分析系统
1. 引言
1.1 学习目标
本文将带你从零开始,构建一个基于ResNet-18的食品图像识别与营养分析系统。你将掌握: - 如何使用 TorchVision 加载预训练模型 - 构建轻量级 WebUI 实现图像上传与可视化推理 - 将通用物体识别能力迁移到食品场景,并结合营养数据库实现智能分析 - 针对 CPU 环境进行性能优化,确保低延迟、高稳定性服务部署
最终成果是一个可本地运行、无需联网验证、支持离线识别的完整 AI 应用系统。
1.2 前置知识
建议具备以下基础: - Python 编程基础 - 了解深度学习基本概念(如卷积神经网络、分类任务) - 熟悉 Flask 或其他 Web 框架者更佳
本教程适用于 AI 初学者和希望快速落地图像分类项目的开发者。
1.3 教程价值
不同于调用云 API 的黑盒方案,本文提供全栈可定制化实现路径: - 模型原生集成,避免“权限不足”或“模型不存在”等报错 - 支持扩展为食品专属识别器 + 营养推荐引擎 - 提供 CPU 友好型设计,适合边缘设备或低成本服务器部署
2. 环境准备与项目结构
2.1 依赖安装
创建虚拟环境并安装核心库:
python -m venv resnet-env source resnet-env/bin/activate # Windows: resnet-env\Scripts\activate pip install torch torchvision flask pillow numpy pandas gunicorn⚠️ 注意:若在无 GPU 环境下运行,请确保安装的是 CPU 版 PyTorch:
bash pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
2.2 项目目录结构
food-nutrition-analyzer/ ├── app.py # Flask 主程序 ├── static/ │ └── uploads/ # 用户上传图片存储 ├── templates/ │ └── index.html # Web 页面模板 ├── models/ │ └── resnet18_model.py # 模型加载与推理逻辑 ├── data/ │ └── food_nutrition.csv # 食品营养数据库(模拟) └── requirements.txt # 依赖清单3. 核心功能实现
3.1 加载 ResNet-18 预训练模型
models/resnet18_model.py
import torch import torchvision.models as models from torchvision import transforms from PIL import Image import os # 初始化设备 device = torch.device("cpu") # 兼容性优先选择 CPU # 加载官方 ResNet-18 模型(自动下载权重) model = models.resnet18(weights='IMAGENET1K_V1') model.eval() # 推理模式 model.to(device) # 图像预处理管道 transform = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ]) # ImageNet 类别标签(可通过 torchvision 获取) with open("data/imagenet_classes.txt", "r") as f: classes = [line.strip() for line in f.readlines()] def predict_image(image_path, top_k=3): """输入图像路径,返回 Top-K 预测结果""" image = Image.open(image_path).convert("RGB") image_t = transform(image).unsqueeze(0).to(device) with torch.no_grad(): outputs = model(image_t) probabilities = torch.nn.functional.softmax(outputs[0], dim=0) top_probs, top_indices = torch.topk(probabilities, top_k) results = [] for i in range(top_k): idx = top_indices[i].item() label = classes[idx] prob = top_probs[i].item() results.append({"label": label, "probability": round(prob * 100, 2)}) return results✅优势说明:直接使用
weights='IMAGENET1K_V1'自动加载官方权重,无需手动管理.pth文件,极大提升稳定性。
3.2 构建 Flask WebUI
app.py
from flask import Flask, request, render_template, redirect, url_for import os from models.resnet18_model import predict_image app = Flask(__name__) UPLOAD_FOLDER = 'static/uploads' os.makedirs(UPLOAD_FOLDER, exist_ok=True) app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER @app.route('/', methods=['GET', 'POST']) def index(): if request.method == 'POST': if 'file' not in request.files: return redirect(request.url) file = request.files['file'] if file.filename == '': return redirect(request.url) if file: filepath = os.path.join(app.config['UPLOAD_FOLDER'], file.filename) file.save(filepath) # 执行预测 predictions = predict_image(filepath) # 提取食品相关类别并补充营养信息 nutrition_info = get_nutrition_info(predictions) return render_template('index.html', uploaded_image=file.filename, predictions=predictions, nutrition=nutrition_info) return render_template('index.html') def get_nutrition_info(predictions): """根据预测结果匹配营养数据(简化版)""" food_keywords = { 'hamburger': {'calories': 295, 'protein': '17g', 'fat': '12g'}, 'pizza': {'calories': 266, 'protein': '11g', 'fat': '10g'}, 'apple': {'calories': 52, 'protein': '0.3g', 'fat': '0.2g'}, 'hotdog': {'calories': 290, 'protein': '10g', 'fat': '20g'} } for pred in predictions: label = pred['label'].lower() if label in food_keywords: info = food_keywords[label].copy() info['matched_label'] = pred['label'] info['confidence'] = pred['probability'] return info return None if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=False)3.3 Web 前端界面设计
templates/index.html
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <title>食品识别与营养分析</title> <style> body { font-family: Arial, sans-serif; margin: 40px; } .upload-box { border: 2px dashed #ccc; padding: 20px; text-align: center; } img { max-width: 300px; margin: 20px 0; } .result { background: #f0f8ff; padding: 15px; margin-top: 20px; } table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } </style> </head> <body> <h1>👁️ AI 食品识别与营养分析系统</h1> <form method="POST" enctype="multipart/form-data" class="upload-box"> <input type="file" name="file" required /> <br><br> <button type="submit" style="padding: 10px 20px; font-size: 16px;">🔍 开始识别</button> </form> {% if uploaded_image %} <img src="{{ url_for('static', filename='uploads/' + uploaded_image) }}" alt="上传图片"/> <div class="result"> <h3>📌 识别结果 (Top-3)</h3> <table> <tr><th>类别</th><th>置信度 (%)</th></tr> {% for pred in predictions %} <tr><td>{{ pred.label }}</td><td>{{ pred.probability }}</td></tr> {% endfor %} </table> </div> {% if nutrition %} <div class="result" style="background:#e6ffe6;"> <h3>🥗 营养信息(基于 "{{ nutrition.matched_label }}")</h3> <p><strong>热量:</strong>{{ nutrition.calories }} kcal</p> <p><strong>蛋白质:</strong>{{ nutrition.protein }}</p> <p><strong>脂肪:</strong>{{ nutrition.fat }}</p> <p><em>置信度:{{ nutrition.confidence }}%</em></p> </div> {% endif %} {% endif %} </body> </html>4. 性能优化与实践技巧
4.1 CPU 推理加速策略
尽管 ResNet-18 本身已很轻量,但仍可通过以下方式进一步优化:
启用 TorchScript 编译(JIT)
# 在模型加载后添加 scripted_model = torch.jit.script(model) scripted_model.save("resnet18_scripted.pt")后续加载时直接使用编译后模型,减少解释开销。
使用 ONNX Runtime(可选)
将模型导出为 ONNX 格式,在 CPU 上获得更高推理速度:
dummy_input = torch.randn(1, 3, 224, 224) torch.onnx.export(model, dummy_input, "resnet18.onnx", opset_version=11)然后使用onnxruntime替代 PyTorch 进行推理。
4.2 内存与启动优化
- 模型缓存复用:Flask 多请求共享同一模型实例,避免重复加载
- 限制上传大小:防止大图导致 OOM
# 在 app.py 中增加校验 MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB if len(file.read()) > MAX_FILE_SIZE: return "文件过大", 400 file.seek(0) # 重置指针4.3 食品类别增强策略
ImageNet 的 1000 类中包含部分食品,但不够全面。可通过以下方式增强:
- 微调(Fine-tuning):在 Food-101 数据集上继续训练 ResNet-18
- 映射表扩展:建立
ImageNet label → 食品名称映射字典,提升语义可读性 - 多模型融合:引入专精食品识别的小模型(如 MobileNetV3-Food)做二次判断
5. 测试与部署
5.1 本地测试
启动服务:
python app.py访问http://localhost:5000,上传一张汉堡包图片,预期输出:
识别结果: 1. hamburger (68.2%) 2. hotdog (12.1%) 3. pizza (8.7%) 营养信息: - 热量:295 kcal - 蛋白质:17g - 脂肪:12g5.2 容器化部署(Docker)
Dockerfile
FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 5000 CMD ["gunicorn", "-b", "0.0.0.0:5000", "app:app"]构建并运行:
docker build -t food-resnet18 . docker run -p 5000:5000 food-resnet186. 总结
6.1 学习路径建议
完成本教程后,你可以进一步探索: - 使用Transfer Learning训练自己的食品分类器 - 接入真实营养数据库(如 USDA FoodData Central) - 添加语音播报、移动端适配等功能 - 部署到树莓派等嵌入式设备,打造智能厨房助手
6.2 资源推荐
- TorchVision Models Documentation
- ImageNet Class Labels
- Food-101 Dataset
- ONNX Model Zoo - ResNet
💡获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。