简介:本资源是一套面向本科毕业设计与人工智能课程实践的YOLO交通智能分析系统实现方案,聚焦交通流量实时统计与压线、逆行、违停等典型违章行为检测两大核心任务。资源包共119个文件,含46个Python主程序与工具脚本(如run_app.bat等启动入口)、53个编译后pyc模块、3个PyQt界面文件(.ui)及yolov4-tiny.pt模型权重、cfg配置与names类别定义等关键深度学习组件,整体64.38MB,结构完整覆盖数据预处理、模型推理、GUI交互与结果可视化全流程。已有111人学习下载,适合具备Python基础与PyTorch入门经验的学习者开展端到端项目复现。读者可直接运行获得带GUI的交通监控演示系统,获取含视频流接入、目标轨迹追踪、流量热力图生成及违章截图存证的完整功能链,并参考README.md与SQL数据库设计理解工程落地逻辑。
1. 项目缘起:从“看见”到“理解”的交通监控升级
最近在整理硬盘,翻到了一个几年前做的老项目压缩包,名字就叫“基于YOLO的交通流量统计、违章行为检测.zip”。点开一看,里面代码、模型、数据集混杂在一起,文档也几乎没有,典型的“一次性项目”遗迹。但仔细想想,这个项目背后涉及的技术栈和思路,恰恰是当前很多智能交通、安防监控甚至工业质检项目的核心骨架。与其让它继续吃灰,不如借着这个机会,把它彻底拆解、重构一遍,把当时踩过的坑、摸索出来的门道,以及这几年技术演进带来的新思路,系统地梳理出来。
这个项目的核心目标很明确:利用计算机视觉技术,让监控摄像头不仅能“看见”车流,更能“理解”交通行为。具体来说,就是实现两个核心功能:一是实时统计通过某个路口的车辆数量(流量统计),二是自动识别诸如闯红灯、压线、逆行、违停等违章行为。听起来像是智慧城市大屏上那些酷炫系统的简化版,但麻雀虽小,五脏俱全。从视频流接入、目标检测、目标跟踪、轨迹分析,到行为规则判断和结果可视化,整个链路一个不少。
YOLO(You Only Look Once)作为这个项目的基石,其选择在当时和现在都极具代表性。它平衡了速度和精度,非常适合对实时性要求高的视频分析场景。但仅仅把YOLO模型跑起来,框出车辆和行人,距离真正的“流量统计”和“违章检测”还差得远。这里面涉及到跟踪算法(如DeepSORT、ByteTrack)的选型与集成、虚拟检测线或区域的设置、轨迹点的时间序列分析、以及针对不同违章行为的规则引擎设计。每一个环节都有其技术细节和调优空间。
所以,这篇文章不是简单地复现那个老项目,而是以它为蓝本,结合最新的实践认知(比如YOLOv8/v11的便捷性、更高效的跟踪算法、以及一些工程化部署的考量),手把手带你搭建一个可运行、可调试、可扩展的交通分析系统原型。无论你是想学习视频分析的全流程,还是需要为一个具体的场景(比如小区出入口管理、工地安全监控)快速搭建原型,相信这些内容都能给你提供直接的参考。
2. 核心组件选型与技术栈搭建:为什么是它们?
在动手写代码之前,技术选型决定了项目的天花板和地板。一个合理的选型能让后续开发事半功倍,反之则可能步步维艰。回顾我那个老项目和一些新的实践,以下这套技术栈是经过验证的、比较稳妥的组合。
2.1 目标检测器:YOLO系列的演进与v8的“甜点”选择
目标检测是整个流程的第一步,也是精度和速度的基石。YOLO系列发展迅速,从v5到v11,各有侧重。
- YOLOv5:生态极其丰富,资料和社区解决方案最多,是很多项目的起点。它的PyTorch实现非常友好,训练和部署的教程铺天盖地。
- YOLOv8:Ultralytics公司出品,可以看作是v5的“官方升级版”。它最大的优势是统一且优雅的API,分类、检测、分割、姿态估计任务都用同一套接口,大大降低了学习成本。其预训练模型性能优秀,并且自带完善的训练、验证、预测和导出(到ONNX、TensorRT等)流程。对于快速原型开发和新手来说,v8是目前综合体验最好的“甜点”。
- YOLOv9/v10/v11:学术界的前沿,在结构上持续创新(如v9的可编程梯度信息PGI,v11的新主干网)。但对于工程落地,尤其是我们这种集成项目,它们的稳定性和周边工具链(如跟踪算法适配、部署优化)可能还不如v8成熟。除非有极致的精度追求,否则v8或v5是更务实的选择。
我的选择与理由:本次重构我将基于YOLOv8。原因很简单:它降低了从训练到部署的全流程门槛,让我们能把更多精力放在跟踪和行为分析逻辑上。使用它的Python SDK,几行代码就能完成模型加载和推理,并且其输出格式(边框、置信度、类别ID)非常规范,便于后续处理。
# 示例:使用YOLOv8进行预测的基础代码 from ultralytics import YOLO import cv2 # 加载预训练模型(例如官方的yolov8n.pt,或你自己训练的模型) model = YOLO('yolov8n.pt') # 读取视频帧 frame = cv2.imread('test_image.jpg') # 进行预测 results = model(frame)[0] # results是一个Results对象 # 解析结果 boxes = results.boxes.xyxy.cpu().numpy() # 边框坐标 [x1, y1, x2, y2] confidences = results.boxes.conf.cpu().numpy() # 置信度 class_ids = results.boxes.cls.cpu().numpy().astype(int) # 类别ID # 可以根据类别ID过滤出车辆(COCO数据集中,car=2, truck=7, bus=5等) vehicle_indices = [i for i, cls_id in enumerate(class_ids) if cls_id in [2, 5, 7]] vehicle_boxes = boxes[vehicle_indices]注意:YOLOv8默认使用COCO数据集预训练,其中包含了‘car’, ‘truck’, ‘bus’等类别。如果你的场景有特殊车辆(如工程车、三轮车),可能需要在自己的数据集上微调模型。
2.2 多目标跟踪器:连接帧与帧的“记忆”
检测只能告诉我们每一帧里有什么,而跟踪负责将不同帧中的同一个目标关联起来,形成随时间变化的轨迹。这是流量统计(同一个车不能重复计数)和行为分析(需要连续轨迹判断行为)的关键。
- SORT/DeepSORT:经典组合。SORT基于卡尔曼滤波预测和匈牙利算法匹配,速度极快但ID切换频繁。DeepSORT在SORT基础上加入了外观特征(Re-ID)模型,大大减少了ID切换,是过去几年的主流。
- ByteTrack:2022年提出的新方法,核心思想是充分利用低分检测框。传统的跟踪会用一个高阈值(如0.5)过滤检测框,只跟踪高分框。ByteTrack认为,那些低分框(如0.1-0.5)也可能是真实目标,只是可能被遮挡或模糊。它先匹配高分框,再用低分框进行二次匹配和恢复,在保持高速度的同时,取得了比DeepSORT更好的性能,特别是在拥挤场景下。它几乎成为了新的默认选择。
- OC-SORT:针对遮挡场景做了特别优化,在目标频繁相互遮挡的场景下表现更鲁棒。
我的选择与理由:对于交通场景,车辆运动相对规律,遮挡情况中等。ByteTrack在精度和速度上取得了很好的平衡,且实现相对简单,不需要单独训练外观模型(DeepSORT需要)。我们将采用一个开源的ByteTrack Python实现,并将其与YOLOv8的输出进行对接。
跟踪器的输入是每一帧的检测结果[x1, y1, x2, y2, score, class_id],输出是带有唯一ID的跟踪框[x1, y1, x2, y2, track_id, class_id]。后续的所有分析都基于track_id。
2.3 其他工具与框架
- OpenCV:计算机视觉的“瑞士军刀”,用于视频流的读取、解码、显示、绘制(画框、画线、写字)以及一些简单的图像处理。必不可少。
- NumPy/Pandas:进行高效的数值计算和轨迹数据(时间戳、位置、ID)的临时存储与分析。
- 可视化与交互:可以使用OpenCV的
imshow进行实时显示,或者用Matplotlib进行事后分析绘图。对于需要调整虚拟检测线位置的应用,可以写一个简单的鼠标回调函数来实现交互式绘制。
技术栈总结:我们将以YOLOv8 (检测) + ByteTrack (跟踪) + OpenCV (视频处理)为核心,用Python脚本将它们串联起来,构建整个系统。这个组合兼顾了性能、易用性和现代性。
3. 从检测到跟踪:构建稳定的目标轨迹
有了选型,接下来就是实现。第一步是把检测和跟踪模块无缝衔接,输出稳定的、带ID的目标轨迹。
3.1 视频流处理与检测循环
无论是处理离线视频文件还是实时RTSP流,核心都是一个循环:读取帧 -> 检测 -> 跟踪 -> 分析/显示 -> 写入结果。
import cv2 from ultralytics import YOLO # 假设我们已经有一个ByteTrack的跟踪器类 `ByteTracker` from bytetracker import ByteTracker # 初始化 model = YOLO('yolov8n.pt') tracker = ByteTracker() # 需要传入一些参数,如track_thresh, match_thresh等 cap = cv2.VideoCapture('your_traffic_video.mp4') while cap.isOpened(): ret, frame = cap.read() if not ret: break # YOLOv8 检测 results = model(frame, verbose=False)[0] # verbose=False关闭控制台日志 detections = [] if results.boxes is not None: boxes = results.boxes.xyxy.cpu().numpy() scores = results.boxes.conf.cpu().numpy() class_ids = results.boxes.cls.cpu().numpy().astype(int) # 只保留车辆类别的检测结果(根据COCO ID) for box, score, cls_id in zip(boxes, scores, class_ids): if cls_id in [2, 5, 7]: # car, bus, truck # 格式化为 [x1, y1, x2, y2, score, class] detections.append([*box, score, cls_id]) if len(detections) > 0: detections = np.array(detections) else: detections = np.empty((0, 6)) # ByteTrack 更新 # 注意:ByteTrack的update方法通常接受 [x1, y1, x2, y2, score] 格式,类别信息可能需要额外处理 online_targets = tracker.update(detections[:, :5], detections[:, 5], frame.shape[:2]) # online_targets 应包含 [x1, y1, x2, y2, track_id, class_id, ...] 等信息 # 此时,online_targets 就是当前帧的跟踪结果 # 接下来可以基于这些结果进行流量统计和违章分析(见下一节) # 可视化:在帧上绘制跟踪框和ID for t in online_targets: x1, y1, x2, y2, track_id = int(t[0]), int(t[1]), int(t[2]), int(t[3]), int(t[4]) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, f'ID:{track_id}', (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) cv2.imshow('Traffic Analysis', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()3.2 跟踪器的调参与“幽灵”轨迹处理
跟踪器不是开箱即用就完美的,有几个关键参数直接影响效果:
track_thresh:检测框得分阈值,高于此值才被认为是有效检测并初始化为跟踪轨迹。通常设得比纯检测的置信度阈值低一点(如0.4),以捕捉更多可能目标。match_thresh:轨迹和检测框的匹配阈值(对于ByteTrack,可能涉及IoU和外观特征等)。值太高匹配不上,ID切换多;值太低容易错误关联。frame_rate:告诉跟踪器视频的帧率,用于调整卡尔曼滤波的运动模型参数。如果与实际帧率不符,预测会不准。
一个常见的坑是“幽灵轨迹”:当一个目标(比如一辆车)离开画面后,跟踪器可能因为惯性预测,还会在画面外维持几帧这个轨迹。如果此时你的流量统计检测线设在画面边缘,就可能错误地再计数一次。解决办法是设置一个轨迹生命计数器。当跟踪器连续多帧(比如30帧,约1秒)没有为某个track_id匹配到任何检测框时,就认为该目标已消失,将其从活跃跟踪列表和后续分析中移除。好的跟踪器实现(如ByteTrack)内部通常有这个机制,但你需要了解并可能调整这个“最大丢失帧数”参数。
4. 交通流量统计:如何让计数准确可靠?
流量统计听起来简单——目标穿过一条线就加一。但实现起来,要避免重复计数、漏计数,就需要一些技巧。
4.1 虚拟检测线与方向判断
我们通常在感兴趣的区域画一条“虚拟线”或一个“虚拟区域”。最常见的是在车道中央画一条垂直于车流的计数线。
- 定义线:用两个点
(line_start, line_end)定义这条线。 - 计算轨迹中心点:对于每个跟踪目标,我们取其边界框的底部中心点
(cx, cy) = ((x1+x2)/2, y2)作为代表点。用底部中心是因为它更贴近地面,受车辆姿态(如刹车抬头)影响小。 - 判断穿越:核心是判断代表点从线的一侧运动到了另一侧。不能只判断当前帧的点是否在线“上”,因为一帧可能直接跨过去。我们需要结合历史轨迹。
- 方法:记录每个
track_id上一帧的代表点位置prev_point。 - 计算当前点
curr_point和prev_point与检测线的关系。可以用计算点到直线的有向距离,或者更简单地,对于水平线,比较prev_y和curr_y与line_y的大小。 - 如果
prev_point在线上方且curr_point在线下方(或反之),并且运动是垂直向下的(符合车流方向),则认为发生了一次穿越。
- 方法:记录每个
# 假设有一条水平计数线 y = line_y line_y = 300 # 用一个字典存储每个track_id的上一帧中心点 track_history = {} for t in online_targets: track_id = int(t[4]) cx = int((t[0] + t[2]) / 2) cy = int(t[3]) # 底部y坐标 if track_id in track_history: prev_cy = track_history[track_id] # 判断穿越:上一帧在线以上,当前帧在线以下,且是向下运动(防止计数回退) if prev_cy < line_y and cy >= line_y and cy > prev_cy: vehicle_count += 1 print(f"Vehicle ID:{track_id} crossed the line. Total: {vehicle_count}") # 更新历史位置 track_history[track_id] = cy # 清理很久未出现的轨迹历史(避免内存泄漏) # ...4.2 多车道与方向过滤
实际路口可能有多个车道,双向车流。我们需要更精细的管理:
- 多车道:可以为每个车道定义一条计数线。在判断穿越时,还需要判断车辆的中心点
cx是否落在车道的水平范围内[lane_left, lane_right]。 - 方向过滤:对于双向车道,我们只统计某个方向的车流。可以通过判断穿越时
curr_point和prev_point的位移向量方向来实现。例如,只统计从北向南(y坐标增加)的车流。
提高计数准确性的经验:
- 设置“计数缓冲区”:不要用一条绝对细的线。可以设置一个“缓冲区带”(如
line_y -5到line_y +5)。当目标进入这个带,标记为“待计数状态”,离开时再根据方向完成计数。这可以避免目标在线附近抖动导致的重复计数。 - 延迟计数:不要目标一触线就立刻计数。可以等目标完全穿过线(比如中心点从线前10像素移动到线后10像素)再计数,更稳定。
- 处理停滞车辆:如果车辆在计数线附近长时间停留(比如等红灯),我们的逻辑可能会重复计数。解决方法是在计数后,给该
track_id打上一个“已计数”标签,在它离开视野或离开缓冲区之前,不再对其进行计数判断。
5. 违章行为检测:定义规则与实现逻辑
违章检测是更高阶的分析,需要基于目标的连续轨迹和时空关系。这里我们以“闯红灯”和“违停”为例,拆解实现逻辑。
5.1 闯红灯检测:状态机与区域判断
闯红灯检测不能只看车辆是否在红灯时越过了停止线,因为可能存在“黄灯抢行”“刚变红灯时车身已过线”等复杂情况。一个相对鲁棒的简化方案是使用区域判断+状态机。
定义关键区域:
- 停车线区域:一个矩形区域,覆盖停止线及前方一小段距离。
- 路口冲突区域:停车线后的路口中央区域。
- 信号灯状态:这是一个外部输入。对于demo,我们可以模拟或手动标注视频中红灯的时间段。实际系统需要接入信号机数据或通过视觉识别信号灯。
设计车辆状态机:
- 状态1:接近:车辆进入停车线区域前方一定范围。
- 状态2:在停车线内:车辆的中心点或边界框与停车线区域有重叠。
- 状态3:进入路口:车辆完全越过停车线,进入路口冲突区域。
违章判断逻辑:
- 当信号灯为红灯时:
- 如果车辆从“状态1”进入“状态2”,记录为“在红灯时抵达停止线”。
- 如果车辆在“状态2”停留极短时间(如小于0.5秒,可能是误判或车身摆动),忽略。
- 如果车辆从“状态2”进入“状态3”,则判定为“闯红灯”。因为这意味着它在红灯期间越过了停止线并进入了路口。
- 当信号灯为绿灯或黄灯时,重置相关车辆的状态。
- 当信号灯为红灯时:
# 伪代码逻辑 red_light = False # 实际中应从信号源获取 stop_line_zone = [(100, 300), (500, 320)] # 停车线矩形区域 intersection_zone = [(80, 320), (520, 500)] # 路口区域 vehicle_states = {} # track_id -> {'state': 'approaching/at_line/in_intersection', 'last_state_time': timestamp} for t in online_targets: track_id, cx, cy = ... box = [t[0], t[1], t[2], t[3]] # 判断车辆在哪个区域 in_stop_line = is_point_in_rect((cx, cy), stop_line_zone) in_intersection = is_point_in_rect((cx, cy), intersection_zone) if track_id not in vehicle_states: vehicle_states[track_id] = {'state': 'outside', 'last_state_time': current_time} prev_state = vehicle_states[track_id]['state'] # 状态转移判断 if red_light: if not in_stop_line and not in_intersection: new_state = 'approaching' elif in_stop_line and not in_intersection: new_state = 'at_line' elif in_intersection: new_state = 'in_intersection' # 关键判断:如果上一个状态是‘at_line’,且在当前状态下,则闯红灯 if prev_state == 'at_line': time_at_line = current_time - vehicle_states[track_id]['last_state_time'] if time_at_line > 0.5: # 排除短暂停留的误判 print(f"ALERT: Vehicle ID:{track_id} ran a red light!") # 触发报警、截图、记录轨迹等操作 # 更新状态和时间 if new_state != prev_state: vehicle_states[track_id]['state'] = new_state vehicle_states[track_id]['last_state_time'] = current_time else: # 绿灯或黄灯,重置该车辆的状态(或直接删除记录) if track_id in vehicle_states: del vehicle_states[track_id]5.2 违停检测:时间阈值与动态ROI
违停检测相对直接:在禁止停车的区域(ROI, Region of Interest),检测是否有车辆静止超过规定时间(如60秒)。
- 定义违停区域:用多边形或矩形标出禁止停车的区域,如公交站台、路口黄网格线、消防通道等。
- 计算车辆运动速度:对于每个
track_id,记录其连续多帧的中心点位置。计算其平均位移速度(像素/秒)。由于摄像头可能不是正射,像素速度与实际速度不成比例,但对于判断“是否静止”足够了。 - 判断逻辑:
- 如果车辆的中心点在违停区域内,并且其连续N帧(对应时间T)的平均速度低于一个极小阈值(如每秒5个像素),则触发“疑似违停”警报。
- 为了避免短暂停车(如上下客)误报,需要持续监测。只有“疑似违停”状态持续超过法规规定的时间(如60秒),才最终判定为“违停”,并记录开始时间、位置和车辆ID(如果车牌清晰,可结合车牌识别)。
- 动态ROI管理:违停区域可能随时间段变化(如夜间允许停车)。系统需要支持ROI和判定规则的动态加载。
实现细节:
- 速度计算可以使用简单的移动平均,比如存储最近10帧的位置,计算这10帧内的总位移除以总时间。
- 对于静止判断,阈值要设得足够低,以过滤掉车辆缓慢蠕动(如堵车)的情况。
- 需要维护一个
violation_records字典,记录每个track_id进入疑似状态的时间点。
6. 工程化思考:从Demo到可用的系统
把上面的模块拼在一起,就能得到一个在视频文件上运行的演示程序。但要让其成为一个“系统”,还需要考虑很多工程问题。
6.1 性能优化:让它在实际场景中跑得快
Python + OpenCV + YOLO 在单路视频上实时(>25 FPS)运行是有挑战的,尤其是使用较大的YOLO模型时。
- 模型选择与量化:在精度可接受的前提下,选择更小的模型(如YOLOv8n, YOLOv8s)。可以使用PyTorch的量化(Quantization)或导出为TensorRT/OpenVINO等推理引擎格式,能获得数倍的加速。
- 推理批处理:如果处理多路视频,可以将多帧图片堆叠成一个批次(batch)送入模型推理,能更充分利用GPU计算资源。
- 跳帧处理:对于非关键实时场景,可以每2帧或3帧做一次目标检测,中间帧只做跟踪。因为跟踪的计算量远小于检测。这是平衡精度和速度的常用技巧。
- 视频解码:使用OpenCV的
cv2.VideoCapture解码可能不是最快的。对于高性能需求,可以探索使用FFmpeg库(如PyAV)或硬件解码(如NVIDIA的Video Codec SDK)。
6.2 鲁棒性提升:应对复杂场景
- 光照与天气:模型在训练数据未覆盖的夜间、雨雪、强光逆光下可能失效。解决方案包括:使用在多样天气数据上训练过的模型;在图像预处理阶段加入自动白平衡、对比度增强;或者部署红外摄像头应对夜间。
- 遮挡与密集:严重遮挡下,检测和跟踪都会失效。ByteTrack一定程度上缓解了这个问题。更进一步的方案是使用更强大的检测器(如YOLOv9)或引入注意力机制。对于流量统计,在极度拥挤时,可以切换到基于光流或背景减除的宏观估计方法作为补充。
- 相机抖动:手持或安装不牢的相机会导致画面抖动,使虚拟检测线“相对移动”,造成误判。需要加入视频稳像(Video Stabilization)模块,或者使用更稳定的轨迹滤波算法。
6.3 数据流与结果输出
一个完整的系统不能只显示在屏幕上。
- 结构化输出:需要将统计结果(时间、车道、流量、速度)和违章事件(时间、地点、类型、车辆ID/车牌截图、证据视频片段)以结构化的格式(如JSON、CSV)写入数据库或消息队列(如Kafka、RabbitMQ)。
- 可视化与告警:提供Web界面或大屏看板,实时显示流量热力图、违章事件列表。违章事件可触发声音告警、弹窗,并自动保存前后10秒的视频片段作为证据。
- 系统集成:通过API与其他系统对接,比如将违章数据推送给交警业务平台,将流量数据推送给交通信号控制系统进行自适应配时。
7. 模型训练与数据:如果预训练模型不够用怎么办?
COCO预训练的YOLOv8模型能检测常见车辆,但如果你的场景有特殊车辆(电瓶车、三轮车、特定工程车)、或者需要更精细的类别(小轿车、SUV、卡车)、或者需要检测车牌(用于违章取证),你就需要定制训练。
数据收集与标注:
- 来源:从你的目标场景摄像头录制视频,并抽取关键帧。确保覆盖不同时段、天气、光照条件。
- 标注工具:使用LabelImg、CVAT、Roboflow等工具进行边界框标注。标注的类别要与你关心的行为相关(例如,为了检测“行人闯红灯”,你需要标注‘person’;为了检测“货车禁行”,你需要区分‘car’和‘truck’)。
- 数据格式:YOLO格式的标签是每个图像对应一个
.txt文件,每行内容为class_id center_x center_y width height,坐标是归一化后的(0-1之间)。
模型训练:
- 环境:最好有GPU。可以使用Google Colab的免费GPU,或者租用云服务器。
- 步骤:
# 安装Ultralytics pip install ultralytics # 准备数据集,按YOLO格式组织 # dataset/ # ├── images/ # │ ├── train/ # │ └── val/ # └── labels/ # ├── train/ # └── val/ # 创建一个 dataset.yaml 配置文件 # path: /path/to/dataset # train: images/train # val: images/val # nc: 5 # 你的类别数 # names: ['car', 'bus', 'truck', 'motorcycle', 'person'] # 开始训练(从预训练模型yolov8n.pt开始微调) yolo task=detect mode=train model=yolov8n.pt data=dataset.yaml epochs=50 imgsz=640 - 关键参数:
epochs(迭代轮数)、imgsz(输入图像大小,越大精度可能越高但越慢)、batch(批大小,根据GPU内存调整)。训练过程会自动验证,并保存最佳模型best.pt。
评估与部署:训练完成后,在独立的测试集上评估模型精度(mAP)。满意后,将模型导出为ONNX或TensorRT格式,以便在部署环境中获得更优性能。
避坑提示:训练数据中,目标大小、角度、遮挡情况的分布要尽量与真实场景一致。如果真实场景中电瓶车很多,但训练数据里很少,模型就很可能漏检。这就是所谓的“数据分布不一致”问题。
本文还有配套的精品资源,点击获取