从模型训练到网页上线:手把手带你走通YOLOv5+Flask全链路,避开那些‘坑’
从模型训练到网页上线YOLOv5与Flask全链路部署实战指南当你完成YOLOv5模型的训练得到那个珍贵的best.pt文件时真正的挑战才刚刚开始。如何让非技术同事也能通过简单操作测试你的模型如何将实验室成果转化为团队可用的工具这篇文章将带你走通从PyTorch模型到Web服务的完整链路分享那些只有实战才会遇到的坑。1. 模型准备与优化不只是导出那么简单拿到训练好的YOLOv5模型后直接扔进Flask应用这可能是第一个陷阱。我们先解决模型层面的适配问题。模型格式选择PyTorch原生格式.pt开发调试最方便但依赖特定PyTorch版本ONNX格式跨框架通用但对某些操作支持有限TorchScript格式PyTorch官方推荐的生产环境格式# 将YOLOv5模型导出为TorchScript格式 import torch model torch.hub.load(ultralytics/yolov5, custom, pathbest.pt) model.eval() traced_model torch.jit.trace(model, torch.rand(1, 3, 640, 640)) traced_model.save(yolov5_ts.pt)常见问题排查版本冲突训练环境和部署环境的PyTorch版本差异可能导致加载失败输入尺寸确保Web应用中的预处理与训练时保持一致设备兼容处理CPU/GPU自动切换的优雅方案提示在模型导出后务必编写测试脚本验证导出的模型能否正常推理避免把问题带到Web层2. Flask服务架构设计不只是跑通Demo一个生产可用的Flask应用需要考虑更多因素而不仅仅是实现基本功能。推荐的项目结构yolo_flask/ ├── app/ │ ├── __init__.py │ ├── models/ # 模型相关代码 │ │ └── yolo_wrapper.py │ ├── routes/ # 路由定义 │ │ └── detection.py │ ├── static/ # 静态文件 │ └── templates/ # 前端模板 ├── config.py # 配置文件 ├── requirements.txt # 依赖列表 └── wsgi.py # WSGI入口关键代码封装yolo_wrapper.pyclass YOLOv5Detector: def __init__(self, model_path, deviceauto): self.device self._select_device(device) self.model self._load_model(model_path) self.names self.model.module.names if hasattr( self.model, module) else self.model.names def _select_device(self, device): if device auto: return torch.device(cuda:0 if torch.cuda.is_available() else cpu) return torch.device(device) def _load_model(self, path): model torch.jit.load(path) if path.endswith(.pt) else \ torch.hub.load(ultralytics/yolov5, custom, pathpath) return model.to(self.device).eval() def predict(self, img_array, conf_thres0.25): # 预处理逻辑 img self._preprocess(img_array) # 推理 with torch.no_grad(): pred self.model(img)[0] # 后处理 return self._postprocess(pred, conf_thres)3. 视频流处理的艺术从基础实现到性能优化处理视频流是计算机视觉Web应用的核心挑战需要考虑实时性和资源消耗的平衡。三种视频处理方案对比方案优点缺点适用场景逐帧Base64实现简单带宽占用高低分辨率测试MJPEG流实时性好无音频支持监控类应用WebSocket全双工通信实现复杂交互式应用优化后的视频路由实现app.route(/video_feed) def video_feed(): # 使用生成器减少内存占用 def generate(): cap cv2.VideoCapture(0) while True: ret, frame cap.read() if not ret: break # 异步处理避免阻塞 processed_frame detector.predict(frame) _, jpeg cv2.imencode(.jpg, processed_frame) yield (b--frame\r\n bContent-Type: image/jpeg\r\n\r\n jpeg.tobytes() b\r\n) return Response(generate(), mimetypemultipart/x-mixed-replace; boundaryframe)性能优化技巧使用线程池处理高耗时的模型推理实现帧缓存机制避免重复计算动态调整检测频率如每3帧检测一次4. 生产环境部署从开发服务器到稳健服务Flask自带的开发服务器不适合生产环境我们需要更专业的部署方案。部署方案对比方案安装难度性能适合规模Waitress★★☆★★★小型应用Gunicorn★★★★★★★中型应用uWSGINginx★★★★★★★★★大型应用使用Gunicorn部署的示例# 安装 pip install gunicorn # 启动4个工作进程 gunicorn -w 4 -b :5000 wsgi:app # 使用gevent提高并发 gunicorn -k gevent -w 8 -b :5000 wsgi:app必须添加的监控功能内存使用监控防止视频流处理导致内存泄漏请求日志记录异常请求和性能数据健康检查端点供运维系统监控服务状态app.route(/health) def health_check(): return jsonify({ status: healthy, memory: psutil.virtual_memory().percent, gpu_usage: get_gpu_utilization() })5. 那些年我们踩过的坑实战问题汇编版本地狱OpenCV的imencode在不同版本中参数要求可能不同PyTorch的half()精度在部分显卡上可能导致NaN视频处理陷阱# 错误示例未释放视频资源 def process_video(path): cap cv2.VideoCapture(path) while cap.isOpened(): ret, frame cap.read() # ...处理逻辑... # 忘记cap.release()会导致内存泄漏 # 正确写法 def process_video(path): cap cv2.VideoCapture(path) try: while cap.isOpened(): ret, frame cap.read() if not ret: break # ...处理逻辑... finally: cap.release()前端适配问题不同浏览器对视频流的支持差异移动端横竖屏切换时的布局问题大文件上传时的进度显示6. 扩展思路超越基础部署完成基础部署后可以考虑以下增强功能实用扩展功能结果存储将检测结果保存到数据库批处理模式支持上传视频文件异步处理模型热更新不重启服务切换模型多模型并行AB测试不同版本的模型性能提升方案使用TensorRT加速推理实现模型量化FP16/INT8采用微服务架构分离Web和推理服务# TensorRT加速示例 def load_engine(engine_path): with open(engine_path, rb) as f: runtime trt.Runtime(TRT_LOGGER) return runtime.deserialize_cuda_engine(f.read()) def trt_inference(context, input_buffer): # 设置输入输出绑定 bindings [None]*2 bindings[0] input_buffer.ptr # 执行推理 context.execute_v2(bindingsbindings) return output_buffer在真实项目中我们最终采用了Docker容器化部署方案将Flask应用、模型文件和Nginx打包在一起通过Kubernetes实现自动扩缩容。当并发请求量突增时系统能自动启动新的Pod来处理负载模型推理部分则使用了TensorRT加速将延迟从最初的120ms降低到了45ms。