简介:基于深度学习的地铁客流实时监测PDF文档,面向轨道交通运营管理人员、智能交通从业者及深度学习目标检测方向的研究人员,针对传统人工统计、红外感应、三辊闸等客流监测方式精度低、影响通行等问题,提出以SSD算法为核心、MobileNet为主干网络并结合KCF目标跟踪的实时客流检测方案。文档采用量化对比方式分析深度可分离卷积在计算量上的显著优势,并给出Ubuntu系统下基于caffe框架、RTX 2080 Ti显卡的实验环境与深圳地铁站口真实视频数据测试结果,mAP可达87.9%。资源为单份PDF文件,大小1.56MB,包含公式推导、算法对比与实验结果分析,内容紧凑可直接阅读,适合作为课题组参考文献、技术方案设计参考或专业课程辅助材料。已有160人学习,文档从方法原理到实验验证的完整链条对提升客流统计精度与系统实时性有实际参考价值。
1. 深度学习客流监测:这篇 PDF 到底能帮你解决什么问题
地铁站台的客流统计,表面是计数问题,本质是基于深度学习的目标检测与跟踪问题。这篇论文拿 SSD 做检测框架,把主干网络从 VGG16 换成 MobileNet,再在检测后接 KCF 目标跟踪,在深圳地铁站口摄像头数据上把 mAP 做到 87.9%,同时保住了实时推算能力。它对应解决的是人工数人主观误差大、红外容易漏数、三辊闸挡通行这三个传统方案都覆盖不了的问题。适合三类人:做毕设或课题的在校生,需要论证视频客流方案可行性的地铁信息化从业者,以及刚接触目标检测落地、想找一条完整技术路线照着走的工程师。下载这份 PDF 拿到的不只是一篇文献,还有一条能从算法选型走到训练评估的完整过程,值得照着复现一遍。
2. 算法选型:SSD+MobileNet 的真实账本和两个取舍
地铁客流检测的难点在于:场景固定,人流流动,每个站的摄像头安装角度、闸机口排队密度差别很大。论文在选型上做了三个决定:用 one-stage 的 SSD 而不是 two-stage 的 Faster R-CNN;用 MobileNet 替换 VGG16;在检测后挂 KCF 跟踪。这三个决定分别对应速度、算力、稳定性三笔账。下面把每一笔账算清楚,你才知道复现时哪里能改、哪里不能动。
2.1 one-stage 与 two-stage:实时性这笔账怎么算
目标检测领域把算法分成两大类。two-stage 类算法先提候选区域,再对每个候选区做分类和位置精修,典型代表是 Faster R-CNN,精度高但慢。one-stage 类算法直接在特征图的每个位置预测边界框和类别,典型代表是 SSD 和 YOLO,速度快但精度上要吃点亏。客流监测量大、路数多,每路都要保持实时,论文选 one-stage 是合理的,这点不用纠结。
SSD 的特殊之处在于它融合了两边的优点:借鉴 YOLO 的回归思路,同时引入 Faster R-CNN 的 Anchor 机制,用全图不同位置、不同尺度的特征图做回归。这样既保留了 one-stage 的速度,也把窗口预测精度往 two-stage 拉近。更关键的是,SSD 在多个不同尺度的特征图上分别做预测,大特征图负责小目标,小特征图负责大目标,这和地铁场景天然契合——站台远端的人很小,近处的人很大,单一尺度特征图根本接不住这种跨度。
| 算法类型 | 代表 | 速度 | 精度 | 适合场景 |
|---|---|---|---|---|
| two-stage | Faster R-CNN | 慢 | 高 | 对实时性要求低的精细检测 |
| one-stage | SSD / YOLO | 快 | 中上 | 实时视频流、嵌入式设备 |
YOLO 虽然也能实时,但论文点出了它的两个缺陷:容易漏检,物体尺度变化大时泛化能力下降。换句话讲,YOLO 在"人挨着人、远近差距大"的站台场景里容易翻车,SSD 的多尺度特征图设计更稳。所以选 SSD 不是因为它比 YOLO 高级,而是因为这个场景里尺度多样性是主要矛盾。
2.2 主干网络换血:VGG16 到 MobileNet 的计算量对比
SSD 原版的主干网络是 VGG16,论文明确说它有大约 140M 参数量,对内存需求大,在追求实时性的场景里表现欠佳。换主干网络是这篇文章的核心改动,也是复现时最值得关注的一步。
MobileNet 借鉴了 Inception 的思路,提出深度可分离卷积。普通卷积是对所有通道同时做卷积,深度可分离卷积把它拆成两步:第一步叫深度卷积,特征图的每一个通道各自用一个卷积核做卷积,通道之间互不干扰;第二步叫点卷积,用 1×1 的卷积核把前面的结果在通道维度上融合起来。效果和标准卷积基本一致,但参数量和计算量大幅下降。
假设卷积核大小为 Dk,输入特征图大小为 Dout,通道数为 M,卷积核个数为 N,论文给出了五条公式,核心结论在第 5 条:深度可分离卷积和标准卷积的计算量之比约等于卷积核大小平方的倒数。Dk 取 3×3 时,计算量降为原来的 1/9。这意味着同样的视频分辨率下,网络跑完一次前向的时间大幅缩短,GPU 负载和功耗也随之下调。
| 卷积类型 | 计算量公式 | 3×3 核相对计算量 |
|---|---|---|
| 标准卷积 | Dk² × Dout² × M × N | 9 |
| 深度卷积 | Dk² × Dout² × M | 1 |
| 点卷积 | Dout² × M × N | N(按通道数放大) |
| 深度可分离卷积 | 深度卷积 + 点卷积 | 约 1(总和) |
复现时要注意,MobileNet 替换 VGG16 不是简单换一个网络名字,SSD 的默认框配置、特征图层的选择都要跟着调。原版 SSD 用 VGG16 的 conv4_3、conv7、conv8_2 等层做预测,换成 MobileNet 后,这些层的名称和输出尺寸全部变了,需要在 prototxt 里重新指定特征提取层。我一般会先打印每层输出的 shape,再对照原版 SSD 的 anchor 设置逐层修改,这一步没有捷径,纯靠对着日志调。
2.3 KCF 跟踪:检测后加一层"缓存",减少漏检和功耗
论文在检测之后接了 KCF 目标跟踪,这个设计很多人第一次看会觉得多余,其实它是实时方案里很关键的一笔。目标检测对每一帧都做全图推理,计算量固定,而 KCF 是一种相关滤波跟踪算法,计算量远小于一次完整的目标检测。常见的做法是检测器每隔若干帧跑一次全图,中间帧用 KCF 跟上一步的检测框做跟踪,把目标"接住"。
这样做有两个直接收益。第一,检测不必每帧都跑,系统整体运行时间下降,帧率上去,实时性更有保障;第二,当目标被短暂遮挡或检测器偶发漏检时,KCF 的预测框能把目标续上,防止检测丢失,同时降低整个系统的运行功耗。论文原文里明确提到了降低功耗这一点,说明这套方案的落点不只是实验室精度,而是考虑到实际部署的设备负载。
KCF 的局限也要清楚:它对目标的尺度变化和形变比较敏感,站台上一个人从直立变为弯腰、转身,跟踪框就可能漂移。所以它只能当"缓存",不能当主力。正确姿势是检测定期校正跟踪框,跟踪框反馈给检测做关联,两者互相兜底。这个设计是后面第四章训练和第五章避坑的重要背景,先记住这个结构。
3. 复现路径:caffe 环境、视频抽帧与 lmdb 数据制作
论文的实验环境是 Ubuntu 16.04、caffe 开源框架、GPU 为 RTX 2080 Ti、CPU 为 Intel Xeon E5-2440 v2,训练数据来自深圳地铁各站口摄像头的视频。严格复现意味着你得把这一套环境重新搭起来。caffe 现在已经基本不更新了,新项目多数转向 PyTorch,但既然论文基于 caffe 写的,我建议先按原样跑通一遍,把数据流程吃透,再考虑迁移到别的框架。这里讲的是能直接照做的步骤。
3.1 Ubuntu 环境:依赖、caffe 编译和两个易错点
先装系统依赖,Ubuntu 16.04 下用 apt 装齐。NV 驱动和 CUDA 建议按显卡驱动版本配套来,2080 Ti 用 CUDA 9.0 或 10.0 都常见,但 caffe 对 CUDA 10 的兼容需要验证,稳妥起见先上 CUDA 9.0。
sudo apt-get update && sudo apt-get install -y \ libprotobuf-dev libleveldb-dev libsnappy-dev \ libopencv-dev libhdf5-serial-dev protobuf-compiler \ libgflags-dev libgoogle-glog-dev liblmdb-dev \ libatlas-base-dev装完依赖后进入 caffe 源码目录,复制 Makefile.config 模板再改。这里是编译阶段最常翻车的两个点:一是没有开启 USE_CUDNN,GPU 利用率上不去;二是 BLAS 选了 MKL 但机器上只有 ATLAS,链接直接报错。
cp Makefile.config.example Makefile.config # 打开 Makefile.config,确保以下几项被正确配置: # USE_CUDNN := 1 # BLAS := atlas # CUDA_DIR := /usr/local/cuda-9.0 make -j8编译完成后跑一下make runtest确认框架可用,再编译 Python 接口make pycaffe,后续抽帧脚本要用到 OpenCV 而不一定用到 pycaffe,但 val 脚本会用。整个过程如果哪一步缺包,报错信息基本会点名到具体库,对照 apt 装上再重跑即可。
提示:caffe 的 Makefile.config 里还有一行
OPENCV_VERSION := 3,如果用 OpenCV 3.x 必须打开,否则编译时接口不匹配会报一堆链接错误。
3.2 视频抽帧与打标:从站口录像到 Pascal VOC
论文说得很简短:把视频数据读取为图片数据,然后打标,制作成 lmdb 格式。实际操作分两步。第一步是抽帧,这里的关键是间隔怎么选。站台人流动慢,连续帧之间目标位移小,10 帧抽 1 帧都有大量冗余;如果是早晚高峰、人流快速移动,建议 5 帧抽 1 帧。抽太密浪费时间,抽太稀会丢掉目标出现和消失的瞬间,影响后续跟踪训练。
import cv2 import os video_path = "shenzhen_station_01.mp4" frame_dir = "frames/st01" os.makedirs(frame_dir, exist_ok=True) cap = cv2.VideoCapture(video_path) interval = 5 # 人流较快时取 5,平峰可取 10 idx = 0 saved = 0 while True: ret, frame = cap.read() if not ret: break if idx % interval == 0: cv2.imwrite(f"{frame_dir}/st01_{saved:06d}.jpg", frame) saved += 1 idx += 1 cap.release() print("saved:", saved)抽完帧后用 LabelImg 打标,这是最耗时的一步。论文的场景只有一类目标,就是人。如果做的是毕设,建议只标 person 一类,不要顺手把闸机、屏蔽门也标进去,类别越多,模型需要区分的维度越多,收敛越慢。打标导出的是 Pascal VOC 格式的 XML 文件,每个 XML 对应一张图片,记录目标框的类别和坐标。
在密集人群场景里,打标的标准要统一。我一般定三条规则:遮挡超过一半的不标;小于 20 像素的目标不标,标了模型也学不到有效特征;正在进出画面、只露出一半的也不标,避免给模型喂半截样本。规则不统一,后面训练出来的模型在同样密集场景下会乱。
3.3 生成 lmdb:create_list.py 与 create_data.sh 的三个改动点
打标完成后需要把 VOC 格式转换成 caffe-SSD 需要的 lmdb。先建好 VOC 标准目录结构,把图片放进 JPEGImages,XML 放进 Annotations,再准备 trainval.txt、test.txt、labelmap 文件。trainval.txt 每行是图片相对路径和 XML 路径,labelmap 里定义类别 ID。
# 假设数据放在 $CAFFE_ROOT/data/VOCdevkit/VOC2007 下 ./data/VOC2007/create_list.sh ./data/VOC2007/create_data.sh这两个脚本拿来即用基本不行,必须改三个地方。第一,数据集路径写死了 VOC2007/2012,要改成自己的目录名;第二,labelmap.prototxt 里的类别要改成 person,原模板有 airplane、car 等一堆类,不改会报类别数不一致;第三,图片尺寸处理,SSD 默认 300×300,如果要提速就维持 300,要提精度就改 512,但算力和显存占用同步上升。
item { name: "none_of_the_above" label: 0 display_name: "background" } item { name: "person" label: 1 display_name: "person" }三个改动点里最容易忽略的是 labelmap 的 label 必须从 0 开始,0 固定给 background,person 从 1 开始。如果把 person 写成 0,caffe 会把所有人当作背景,训练出来的模型对谁都检测不出来。这个错我在早期复现时犯过一次,血泪教训,后面第五章专门讲这类坑。
4. 训练与评估:把 mAP 87.9% 拆开看参数怎么设
论文给出的最终结果是算法模型的 mAP 达到 87.9%,实验在 Ubuntu 16.04 下完成,GPU 是 RTX 2080 Ti。要复现这个数字,数据构成和训练参数都得对齐。论文没有公开全部超参数,下面这套是我按 SSD-MobileNet 常规做法补全的,和论文结果可比性最好。
4.1 训练集构成与类别设计
数据来自深圳地铁各站口摄像头,内容决定了模型的"眼界"。只用一个站的数据训练,另一个站直接测,精度大概率会掉,因为摄像头俯仰角、站台宽度、光照都不同。我一般会在训练时把多个站点的数据混在一起,并刻意保留一部分远距离和拥挤场景的样本。
类别设计就一个词:单类。地铁客流检测只需要人这一个类别。单类的好处是模型不用在人与人、人与物之间做区分,梯度更新更集中。打标时把行人都框进来,包括排队、行走、站立三种姿态;坐轮椅、推行李箱的也属于人,要框,别漏。漏标太多,模型会把这一部分人当背景,推理时自然检测不出。
论文提到"除远距离、过于密集目标检测不出外,基本能满足客流统计要求",这句话反过来就是数据缺口的主要来源:远距离的小目标和过于密集的遮挡目标。补数据时优先补这两类场景,比单纯增加样本总量更有效。
4.2 solver.prototxt 里的关键超参数
caffe 的训练配置集中在 solver.prototxt。我常用的起点配置如下,和论文的硬件环境匹配,2080 Ti 上 batch size 16 单卡能跑动,如果显存小就降到 8。
net: "models/MobileNet/SSD_300x300/train.prototxt" test_iter: 200 test_interval: 2000 base_lr: 0.0005 momentum: 0.9 weight_decay: 0.0005 lr_policy: "multistep" gamma: 0.1 stepvalue: 40000 stepvalue: 80000 max_iter: 120000 solver_mode: GPU几个参数的选择逻辑说明一下。base_lr 用 0.0005,这个值在 SSD 系列里比原版 0.001 略低,因为 MobileNet 的参数量小,学习率太高容易震荡,导致 loss 曲线上下乱跳。stepvalue 分两段降学习率,前 4 万步用 5e-4 做粗调,4 万到 8 万步降十倍做精调,8 万步之后再降十倍收尾。120000 步在单卡 2080 Ti 上大概要跑十八到二十个小时,如果时间紧,可以把 max_iter 压到 60000,但 mAP 会掉 2-3 个点。
训练期间要盯的指标不只是 mAP,loss 曲线更有诊断价值。如果 loss 前几千步不降,先检查数据是不是空的、label 是不是从 0 开始的;如果 loss 降得很猛但 mAP 不涨,大概率是过拟合或验证集与训练集分布差太多。这一层的排查逻辑比盲目调参重要得多。
4.3 评估指标:mAP 看整体,单类 AP 看场景短板
mAP 是各类别 AP 的平均,这里只有 person 一类,所以 mAP 就等于 person 类的 AP。87.9% 在 VOC 口径下是一个相当能用的数字,说明检测框和真实框的 IoU 在 0.5 阈值下能稳定命中。但 mAP 有它的盲区:它是一个整体指标,一个站台表现 95%、另一个站台表现 70%,平均下来还是 87.9%,你很难从数字里看出短板在哪。
复现时我会额外做两个维度的评估。第一个维度是按距离分组算 AP:近景目标单独算,中景单独算,远景单独算,对比哪一组掉得厉害。第二个维度是按人流密度分组算 AP:稀疏场景一组,拥挤场景一组。这样你就知道该补视频源里面哪一类数据,而不是盲目加样本。下表是典型的场景表现分布,和论文提的"远距离、过于密集检测不出"一致:
| 场景 | 典型 AP 范围 | 主要失败模式 |
|---|---|---|
| 近景单人 | 0.95 以上 | 基本稳定 |
| 中景小群体 | 0.85 左右 | 相互遮挡导致漏检 |
| 远景低像素 | 0.70 以下 | 目标太小,特征不足 |
| 密集人流 | 0.75 左右 | 漏检和误检同时出现 |
理解了这层,论文里"除远距离、过于密集目标检测不出外"这句话就不是一句简单的免责说明,而是给后续优化划出了方向:要么补数据,要么调 anchor 的尺度分布,要么换更高分辨率输入。后面避坑章节会展开讲。
5. 避坑:客流检测复现中五个高频翻车点
论文里一句话带过的问题,往往就是真正动手时卡你一周的坑。以下五条来自我自己的复现经历和同行的交流,每一条都按现象、原因、解决的顺序写,建议你复现时对照排查。
5.1 远距离乘客检测不出:训练样本里缺小目标
现象:近处的乘客框得很稳,站台远端的人一个都不出框,或者出了框但置信度很低。
原因:训练数据里远景小目标的占比太低。SSD 在小尺寸特征图上负责预测大目标,在大尺寸特征图上负责预测小目标,但如果样本里根本没有多少小目标,那些大特征图上的 anchor 从来没有学到过"小的人"长什么样。论文里明确说远距离目标检测不出,根源就在这里。
解决:一是回去补抽帧,专门截取站台远端、候车区域的大场景画面,让打标样本里小目标占比提高到 20% 以上;二是把输入分辨率从 300×300 提到 512×512,小目标的像素数变多,特征更明显;三是检查 train.prototxt 里 anchor 的 min_size 和 max_size 设置,确保有足够小的 default box 去匹配小目标。
5.2 密集人群漏检严重:NMS 阈值一调就崩
现象:早晚高峰时段,模型把人检测成一片,框和框重叠率高,NMS 一压,输出就只剩几个孤零零的框,中间的人全没了。
原因:NMS 阈值设得过高,重叠框被大量保留,检测结果里全是近似重复的框;阈值设得过低,密集人群里本来挨得近的真实目标又被误杀。漏检在人群密集时最容易爆发,因为站台场景的粘连程度远高于一般行人检测数据集。
解决:把 NMS 阈值从默认的 0.6 往 0.45 方向调,保留靠前排名的框;同时在打标阶段把遮挡严重的样本也标进去,让模型在训练时见过"人被挡住一半"仍然是一个框的情况。如果调整后误检变多,就把置信度阈值从 0.5 提到 0.6,把低分框压掉。密集场景下,NMS 和置信度是一对互锁的旋钮,要一起调,不要只动一边。
5.3 KCF 跟踪框漂移:把同一个人计了两次
现象:人从站台一端走向另一端,检测框偶尔消失,KCF 框漂到背景上,等检测框重新出现时,系统把同一个人当成新目标计数。
原因:KCF 是相关滤波跟踪,它靠目标外观建模,遇到遮挡、快速转身、尺度骤变时跟踪点会漂。论文用 KCF 的本意是防检测丢失,但如果你只跟不校,跟踪框一旦漂走就再也拉不回来。
解决:给跟踪加两个约束。第一,每一帧计算 KCF 跟踪框和最近一次检测框的 IoU,低于 0.3 就判定跟踪丢失,强制回归检测结果;第二,设置跟踪寿命上限,比如连续跟踪超过 60 帧且没有新的检测匹配,就主动终止,避免长漂移积累。计数逻辑上,用目标 ID 的首次出现位置和最后消失位置做匹配,同一个 ID 只计一次,从源头杜绝重复计数。
5.4 帧率虚高:GPU 没喂饱,CPU 解码拖后腿
现象:模型推理单帧只要 20ms,理论上 50 帧每秒,但实际视频处理只有十几帧每秒,延迟感明显。
原因:只看模型推理时间,忽略了视频解码和图像预处理。OpenCV 读视频依赖 CPU 解码,RTSP 流更是如此,CPU 解码跟不上 GPU 推理速度,GPU 只能在那边空转等待。论文降低功耗的目标没问题,但解码这个瓶颈不解决,实时性就是纸面数字。
解决:先确认瓶颈在哪个环节。最简单的办法是把视频抽帧放到独立线程,用队列缓冲,模型推理线程从队列拿图,解码线程只负责填队列。如果瓶颈在解码本身,就换硬解码方案。NVIDIA 平台用 Jetson 的硬解单元,x86 平台可以试 FFmpeg 的 CUVID 解码。验帧率时不能只看模型侧,要看从摄像头到输出计数这个完整链路。
5.5 换摄像头视角后 mAP 骤降:没有做视角适配
现象:训练和验证都用一个站的摄像头,指标好看;换到另一个站测试,mAP 直接掉十个点,漏检和误检同时出现。
原因:每个站台的摄像头俯仰角、安装高度、站台宽度、光照方向都不一样,模型学的特征和新视角不匹配。论文数据来自深圳地铁多个站口,但如果训练集里各站点占比不均,模型就会偏向样本多的那一类视角。
解决:新站点上线前,抽半小时视频,用现有模型做一次预标注,再人工修正,把这个站的数据加入训练集做微调。视角差异大的场景,建议在训练时加入多角度数据增强:上下翻转、小角度旋转、亮度抖动,让模型对不同视角更鲁棒。微调时学习率设 0.0001,迭代 20000 步以内就够,不要从头训。
6. 上线验证:给模型做一次站台体检的检查清单
模型在测试集上 mAP 达标,不等于在站台上能用。上线前我会强制自己过一遍体检流程,结构就三条:必测场景、计数口径、跟踪稳定性,每一项都直接对应一个容易翻车的点。
必测场景按人流密度分三档。平峰时段,站台人少、目标分散,重点验证有没有漏检,尤其是坐在候车座椅上、身体姿态低的人;高峰时段,目标密集,重点验证 NMS 参数和跟踪丢失率,看同一个目标有没有被重复计数;早晚逆光时段,摄像头画面对比度低,重点验证模型在低照度下的表现。这三个场景分别对应论文提到的高精度、实时性和外界因素干扰,一个都不能省。
计数口径要提前定死。站在运营角度,需要的是"进入付费区的人数"或"站台滞留人数",而模型输出的是每帧的检测框,两者之间隔着一条时间线。我一般会画一条虚拟计数线,目标检测和跟踪框的质心跨过这条线才计入一次,质心在线的同一侧来回抖动不算。置信度阈值建议设在 0.5 到 0.6 之间,定太低误检多,定太高漏检多。这套口径在现场要拿人工计数抽检十分钟做校准,误差在正负 5% 以内才能算通过。
提示:置信度阈值和 NMS 阈值是两个不同的参数,置信度管"这是不是人",NMS 管"两个框是不是同一人"。调参时要分开调,混在一起容易越调越乱。
最后是跟踪稳定性验证。选一段三分钟高峰视频,逐帧回放,统计每个目标 ID 的存活时长和跳变次数。正常行走的人,ID 应该连续,不会在走到一半时突然换成新 ID;如果频繁跳变,说明检测框和跟踪框的匹配逻辑有问题,要先修匹配再谈精度。这套体检做完,我才敢把模型接进实际的客流统计系统。
从那以后,我每换一个新站点的数据,都强制自己按这套流程走一遍,先体检、再上线,不再因为 mAP 数字好看就直接部署。客流计数的准确率不在训练日志里,而在每一个真实站台的角落里。希望这套检查清单帮你在复现和落地的路上少踩几个坑。
本文还有配套的精品资源,点击获取