最近在指导几位同学的计算机毕业设计发现很多同学都对“基于图片识别的交通场景分析”这个方向感兴趣。想法都很好比如识别交通标志、检测车辆行人、统计车流量等等但真正动手做起来从模型训练到系统部署一路磕磕绊绊最后项目往往停留在“能跑通Demo”的阶段离一个完整、可用的系统还有距离。我自己也经历过这个阶段所以今天想结合AI辅助开发的思路聊聊怎么把这个毕设做扎实、做出彩。1. 先聊聊大家做毕设时常见的“坑”我总结了一下同学们在做这类项目时普遍会遇到下面几个难题数据之困公开的交通场景数据集如TT100K、COCO Traffic虽然质量高但直接拿来训练自己的模型往往因为场景、天气、标志类型不同而导致效果不佳。自己标注数据又费时费力数据增强技巧掌握不熟很容易陷入数据不足的窘境。模型之惑一看论文各种模型眼花缭乱——Faster R-CNN、YOLO系列、SSD、CenterNet……该选哪个想用最新的YOLOv7、v8又担心自己电脑带不动或者代码太复杂看不懂。最后可能选了个模型训练出来发现速度慢如蜗牛或者精度惨不忍睹。部署之痛这可能是最大的拦路虎。在Jupyter Notebook里模型跑得好好的一放到Web服务里就各种报错。环境依赖冲突、模型格式转换PyTorch - ONNX - TensorRT、前后端交互、服务器配置……每一步都能卡住很久。更别提考虑多用户并发访问时的性能问题了。2. 模型选型要精度还是要速度对于毕设来说我们通常需要在有限的算力可能只有一台带入门级显卡的电脑下取得精度和速度的平衡。经过实践对比我推荐重点考察下面几个轻量级模型1. YOLOv5s这是目前社区最活跃、文档最丰富的模型之一。它的“s”small版本在速度和精度上取得了很好的平衡。对于交通标志识别在TT100K数据集上YOLOv5s可以达到大约85%的mAP在NVIDIA GTX 1660 Ti上推理速度能超过100 FPS。它的PyTorch实现非常清晰非常适合学习。2. MobileNetV2-SSD如果你更看重在CPU或移动设备上的速度这是一个经典选择。SSDSingle Shot MultiBox Detector检测框架搭配轻量级骨干网络MobileNetV2模型体积可以压缩到几MB。在CPU上也能达到实时30 FPS的检测速度当然精度会比YOLOv5s稍低一些。3. EfficientDet-LiteGoogle推出的EfficientDet的轻量级版本在精度-效率曲线上表现非常出色。它的结构比YOLO稍复杂但提供了从Lite0到Lite4不同规模的模型可以灵活选择。适合对最新架构感兴趣且愿意多花点时间调试的同学。简单对比一下基于公开数据集和常见硬件环境模型精度 (mAP)速度 (FPS)模型大小适用场景YOLOv5s~85%100 (GPU) / ~15 (CPU)~14 MB平衡之选学习首选MobileNetV2-SSD~78%30 (CPU)~5 MB极致轻量CPU/边缘部署EfficientDet-Lite3~83%~40 (GPU) / ~8 (CPU)~12 MB追求最新技术精度稍优建议对于大多数毕设直接选择YOLOv5s。它的生态完善从数据准备、训练、验证到导出都有成熟的脚本和教程能帮你避开很多坑。3. 从训练到API端到端核心流程选定模型后我们来看如何把它变成一个可调用的服务。这里采用PyTorch训练 - ONNX导出 - Flask封装API的流水线。第一步使用PyTorch训练与导出ONNX假设我们已经用YOLOv5训练好了权重文件best.pt。ONNX是一种开放的模型格式可以让你的模型在不同的推理引擎如ONNX Runtime, TensorRT上运行。# export_to_onnx.py import torch from models.experimental import attempt_load def export_yolov5_to_onnx(weights_path, imgsz640): 将YOLOv5 PyTorch模型导出为ONNX格式。 Args: weights_path: 训练好的.pt权重文件路径 imgsz: 输入图片的尺寸必须是32的倍数 # 加载模型 device torch.device(cuda if torch.cuda.is_available() else cpu) model attempt_load(weights_path, devicedevice) model.eval() # 设置为评估模式 # 创建一个示例输入张量批量大小通道高宽 example_input torch.randn(1, 3, imgsz, imgsz, devicedevice) # 定义输入输出名 input_names [images] output_names [output] # 导出模型 onnx_path weights_path.replace(.pt, .onnx) torch.onnx.export( model, example_input, onnx_path, verboseFalse, opset_version12, input_namesinput_names, output_namesoutput_names, dynamic_axes{images: {0: batch_size}, output: {0: batch_size}} ) print(fModel exported successfully to {onnx_path}) if __name__ __main__: export_yolov5_to_onnx(./weights/best.pt)第二步用Flask构建轻量级推理APIONNX模型导出后我们可以用ONNX Runtime来加载和推理并用Flask快速搭建一个Web服务。# app.py import io import time from PIL import Image import numpy as np import onnxruntime as ort from flask import Flask, request, jsonify import cv2 app Flask(__name__) # 1. 初始化ONNX Runtime会话 MODEL_PATH ./weights/best.onnx providers [CUDAExecutionProvider, CPUExecutionProvider] # 优先使用CUDA session ort.InferenceSession(MODEL_PATH, providersproviders) input_name session.get_inputs()[0].name # 2. 图像预处理函数需与训练时保持一致 def preprocess_image(image_bytes): 将上传的图片字节流处理为模型输入格式 image Image.open(io.BytesIO(image_bytes)).convert(RGB) # 调整大小并填充为正方形 imgsz 640 image np.array(image) h, w image.shape[:2] scale min(imgsz / h, imgsz / w) new_h, new_w int(h * scale), int(w * scale) resized cv2.resize(image, (new_w, new_h)) # 创建画布并填充 canvas np.full((imgsz, imgsz, 3), 114, dtypenp.uint8) canvas[:new_h, :new_w, :] resized # 转换格式HWC - CHW, BGR - RGB, 归一化 canvas canvas[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, HWC to CHW canvas np.ascontiguousarray(canvas) canvas canvas.astype(np.float32) / 255.0 canvas np.expand_dims(canvas, axis0) # 添加批次维度 return canvas, (w, h), scale # 3. 后处理函数解析YOLO输出 def postprocess(prediction, orig_shape, scale, conf_threshold0.5): 将模型输出转换为边界框、类别和置信度 # 这里简化处理实际需根据YOLO输出格式进行解析 # 通常包括非极大值抑制(NMS)步骤 boxes [] scores [] class_ids [] # ... (具体的解析和NMS代码因篇幅省略) return boxes, scores, class_ids # 4. 定义API端点 app.route(/predict, methods[POST]) def predict(): start_time time.time() # 基础校验 if file not in request.files: return jsonify({error: No file part}), 400 file request.files[file] if file.filename : return jsonify({error: No selected file}), 400 try: # 读取并预处理图片 img_bytes file.read() input_tensor, orig_shape, scale preprocess_image(img_bytes) # 模型推理 outputs session.run(None, {input_name: input_tensor}) # 后处理 boxes, scores, class_ids postprocess(outputs[0], orig_shape, scale) # 组织返回结果 results [] for box, score, cls_id in zip(boxes, scores, class_ids): results.append({ bbox: box.tolist(), # [x1, y1, x2, y2] confidence: float(score), class_id: int(cls_id), class_name: fclass_{cls_id} # 应替换为实际类别名 }) inference_time (time.time() - start_time) * 1000 # 毫秒 return jsonify({ success: True, predictions: results, inference_ms: round(inference_time, 2) }) except Exception as e: return jsonify({error: str(e)}), 500 if __name__ __main__: # 生产环境应使用Gunicorn等WSGI服务器 app.run(host0.0.0.0, port5000, debugFalse)4. 性能与安全让系统更健壮系统能跑起来只是第一步我们还需要关注它的表现和安全性。性能测试要点推理延迟分别测试在纯CPU如Intel i7和GPU如NVIDIA T4环境下的单张图片推理时间。目标CPU下500msGPU下50ms。内存占用使用memory_profiler监控API服务进程的内存增长确保没有内存泄漏。特别是处理大图或并发请求时。并发能力使用locust或wrk工具模拟多个用户同时上传图片观察响应时间变化和服务器负载。Flask本身是同步的高并发下需考虑换用异步框架如FastAPI或使用Gunicorn多Worker。安全性考量输入校验除了检查文件是否存在还应验证文件类型通过Magic Number而非后缀名、文件大小限制单张图片大小如5MB防止上传恶意文件耗尽磁盘。防DDoS/资源耗尽实现简单的速率限制如flask-limiter限制单个IP在单位时间内的请求次数。对图片进行预处理时如果尺寸过大应先进行缩放避免超大图片导致处理时间过长、内存暴涨。错误处理像上面代码中那样用try-except包裹核心逻辑避免服务器因未处理的异常而崩溃同时给客户端返回友好的错误信息而非堆栈跟踪。5. 生产环境避坑指南如果你想把项目部署到云服务器上以下几点需要特别注意1. 模型冷启动第一次加载ONNX模型尤其是大模型可能需要几秒到十几秒。这会导致第一个请求特别慢。解决方法是在服务启动后立即用一张示例图片“预热”一下模型让所有初始化工作提前完成。2. 并发竞争上面的简单Flask应用是单线程的如果两个请求同时修改某个全局变量虽然我们的代码里没有可能会出问题。使用Gunicorn启动多个Worker进程是解决并发的基础。更进阶一点对于完全无状态的模型推理服务可以水平扩展多个实例。3. 路径硬编码代码里不要出现绝对路径如C:\Users\...\best.onnx。应该使用配置文件、环境变量或者相对于项目根目录的路径。# 好的做法 import os MODEL_PATH os.getenv(MODEL_PATH, ./weights/best.onnx)4. 日志与监控给关键步骤如收到请求、开始推理、发生错误加上日志方便出了问题排查。可以记录每个请求的推理时间用于后续性能分析。写在最后通过这样一套从模型选型、训练导出到API封装、性能安全优化的组合拳一个原本只存在于笔记本中的“玩具模型”就变成了一个可以通过HTTP调用的、有一定健壮性的“服务”。这本身就是一次非常宝贵的AI工程化实践。当然这只是一个起点。基于这个图片识别服务你可以进一步思考扩展至实时视频流前端可以使用WebSocket后端使用OpenCV逐帧读取视频流调用你的识别API再将结果推回前端。这会涉及到帧率控制、结果去重、性能优化等一系列新挑战。尝试边缘设备部署将ONNX模型用TensorRT进一步优化部署到Jetson Nano或树莓派加神经计算棒这样的边缘设备上实现低功耗、低延迟的本地识别这会是另一个充满乐趣的方向。希望这篇笔记能为你点亮一盏灯。AI辅助开发的核心思路就是利用好现有的、成熟的工具链和最佳实践把有限的精力聚焦在解决核心问题上而不是重复造轮子或者陷入环境泥潭。建议你对照着文中的步骤亲手复现一遍过程中遇到的问题和解决的方法都会成为你毕设答辩中最闪亮的亮点。