十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

轨道交通客流预测系统实战:从数据清洗到模型融合

轨道交通客流预测系统实战:从数据清洗到模型融合 简介本资源是一套面向城市轨道交通运营管理人员与数据科学学习者的Python客流预测实践方案聚焦于利用历史客流数据构建多算法融合的智能预测模型解决运力调度、班次优化与节假日运能预判等实际问题。压缩包共32个文件含22个核心Python源码文件涵盖数据清洗、特征工程、时序建模、集成学习及ECharts可视化模块、5个备份文件.zbak、2个Git忽略配置与2份Markdown文档含API接口说明与项目README整体仅32KB轻量易读结构清晰体现Django框架机器学习流水线的典型工程组织方式。目前已有32人学习下载读者可直接复用完整建模流程代码包括Pandas/NumPy数据预处理、Scikit-learn多模型对比实验、随机森林与深度神经网络的调参逻辑以及基于真实运营场景设计的时段、季节、节假日等关键特征构造方法具备教学示范性与工程参考价值。1. 项目需求与技术选型思路1.1 客流预测到底解决什么问题做轨道交通客流预测系统之前得先想明白这个系统的用户是谁。调度员要提前知道下一小时哪条线路断面会挤爆车站站长要判断明天早高峰需不需要加开临时限流运营公司管理层要看周报里的客流趋势来调整发车间隔。这些岗位需要的不是一堆学术模型而是一个能按时产出结果、结果能解释、错误可追溯的工程化系统。我当初接这个项目时运营方给了三个硬指标预测未来7天的全日客运量、预测次日早晚高峰时段的分站进站量、预测未来1小时的全网客流用于动态调度。这三个指标分别对应日粒度、15分钟粒度、小时粒度核心难点不在于某个模型有多惊艳而在于同一套系统里要同时处理好三种时间尺度的预测任务还要保证每天凌晨定时产出结果供早班调度会用。这个系统的价值可以拆成三块。第一是排班优化准确的全日客运量预测可以直接决定次日各线路配车数量和发车间隔按经验估算预测误差每降低2个百分点一条日均客流50万的线路每天能少开4到6班空载列车一年下来运营成本节省非常可观。第二是安全管控高峰时段分站进站量预测能提前识别可能触发限流的站点给现场人员留出30到60分钟的应急准备时间。第三是设备维保计划掌握了未来7天的客流趋势就能把自动售票机、闸机、扶梯的维护窗口错峰安排减少对乘客的影响。不过要说清楚一个容易混淆的点客流预测不是预测一个精确数字而是预测一个合理的区间。模型给出的预测值固然重要但置信区间往往才是调度决策真正需要的参数。比如系统预测某站明天早高峰进站量是1.2万人次调度更关心的是超过1.4万人次的可能性有多大这决定了要不要提前安排限流措施。因此这个系统的模型输出不只是点预测值还要带上间隔估计这一点在设计之初就要定下来。1.2 技术栈选型为什么用Python全家桶选Python不是因为它最先进而是因为它把数据摄取、特征处理、模型训练、服务部署这条链路串得最顺。轨道交通领域的数据源非常杂AFC刷卡流水存在Oracle里断面客流数据在SCADA系统里天气数据要调第三方API节假日安排是运营部提供的Excel表。用Python做这套系统pandas做数据清洗、numpy做数值计算、scikit-learn和LightGBM做机器学习建模、TensorFlow或PyTorch做深度学习、Flask或FastAPI做服务接口全程不需要切换语言开发效率高得多。我第一次搭建开发环境时也踩过一些坑特别是依赖版本冲突。这里直接给一套我验证过多次的安装顺序按这个顺序操作基本不会出问题# 创建独立虚拟环境避免污染系统Python python -m venv traffic_flow_env source traffic_flow_env/bin/activate # Windows下用 traffic_flow_env\Scripts\activate # 先装基础数值库 pip install numpy1.24.3 pandas2.0.3 # 再装科学计算和可视化 pip install scipy1.10.1 matplotlib3.7.2 seaborn0.12.2 # 装机器学习库 pip install scikit-learn1.3.0 lightgbm4.0.0 # 装深度学习库CPU版本即可满足预测场景 pip install tensorflow2.13.0 # 最后装服务接口库 pip install flask2.3.2 flask-cors4.0.0 # 如果还需要按天调度任务 pip install apscheduler3.10.1这里强调三点。第一一定要用虚拟环境不要直接往系统Python里塞包否则后续安装或升级某个库时很容易破坏其他项目的依赖。第二numpy和pandas要先装因为后续所有库都依赖它们版本不对后面全崩。第三如果没有GPU服务器TensorFlow装CPU版就够了预测模型对显存要求不高CPU推理延迟也完全在可接受范围内。1.3 整体系统架构设计系统按数据流转方向分成五层每层边界清晰便于独立开发和测试。数据接入层负责对接上游系统的数据接口。AFC刷卡数据每天凌晨通过批处理任务同步到本地数据库天气数据每6小时调一次第三方API节假日表由运营部按年维护。考虑到上游接口不稳定我在这一层做了数据落盘校验一旦发现当天数据缺失或字段异常自动触发重拉任务并记录日志。特征工程层把原始数据转换成模型可用的特征矩阵。原始刷卡流水只有卡号、闸机编号、进出站时间这些字段必须经过聚合才能变成某站某时段进站人数这样的结构化样本。这一层同时负责外生特征的对齐和拼接是整条链路里最花时间的部分后面专门用一节来讲。模型层是核心采用多模型并存、加权融合的策略。日粒度预测用LightGBM为主小时粒度和15分钟粒度用LSTM同时各配一个SARIMA做对照。模型训练统一封装成train()接口输入特征矩阵输出模型文件和评估报告这样后续替换或增加模型不影响上层服务。服务层通过Flask提供RESTful API接口按预测维度分为三个/api/v1/daily/forecast、/api/v1/hourly/forecast、/api/v1/station/forecast。所有接口接收线路编号、车站编号、预测日期范围等参数返回预测值和置信区间。服务层内部做了缓存同样的请求参数在缓存有效期内直接从内存返回不重复计算避免高并发时压垮模型推理服务。展示层是一个纯前端的可视化看板通过HTTP请求调用服务层接口用ECharts绘制预测曲线、误差热力图和车站拥挤度排行榜。前端只负责展示不涉及任何计算逻辑这样即使模型升级或更换前端代码也不需要改。这套架构的核心理念是模型无关。各层之间通过标准化的输入输出格式对接模型层无论跑的是统计模型还是深度学习模型对服务层来说都只是一个返回预测结果的函数。这个设计在后期迭代模型时省了大量时间我大概每两个月会更新一次模型每次都只需要重启模型容器其他层完全不受影响。2. 数据清洗与特征工程实战2.1 AFC刷卡数据怎么整理成可用样本AFC刷卡流水是最核心的数据源但原始数据直接拿来建模是行不通的。一条典型的刷记录包含卡号、闸机编号、进出站类型、交易时间、线路编号、车站编号等十几个字段数据量巨大单日全网刷卡记录轻则几百万条重则上千万条。直接建模对内存和计算资源都是灾难必须先做聚合。聚合逻辑按业务指标来定如果要预测全日客运量就把全天所有刷卡记录按进站时间和车站编号分组计数如果要预测高峰小时进站量就按小时分桶再计数。我用pandas的resample方法完成时间分桶这是整个数据准备阶段最高频的操作import pandas as pd # 读取原始刷卡流水只保留需要的字段 df_raw pd.read_csv(afc_2023_01.csv, usecols[card_id, station_id, entry_time, exit_time], parse_dates[entry_time, exit_time]) # 按15分钟粒度聚合进站客流 df_entry df_raw.set_index(entry_time).groupby(station_id)[card_id] \ .resample(15min).count().reset_index() df_entry.columns [station_id, time_slot, entry_count] # 按小时粒度聚合全网客运量 df_network df_raw.set_index(entry_time)[card_id] \ .resample(1h).count().reset_index() df_network.columns [time_slot, network_flow]聚合之后的数据量大幅缩减15分钟粒度的全网客流一天只有96个时间点一年也才3.5万行完全可以直接放在内存里做分析和建模。数据清洗的重点在于识别异常值。实际数据里常见的坏数据有三类。第一类是重复记录同一张卡在极短时间内被重复计数通常是闸机故障或刷卡重试造成的处理方法是按卡号时间车站去重。第二类是测试数据运维人员用测试卡刷出来的流水特征是卡号以特定前缀开头把这些卡号直接过滤掉。第三类是极端异常值比如某个站点某天客流突然暴涨到平时的5倍可能是传输错误也可能是真实的大客流事件这时候需要人工核对确认而不是机械地删除或保留。遇到异常数据时我的原则是宁可多花时间确认也不要轻易用删除的方式处理。因为客流数据具有强周期性一个站点一天的异常值如果被当成真实数据喂给模型会影响后续好几天同一时段的预测结果。2.2 外生特征怎么构造客流不是孤立产生的它受很多外部因素影响。我在项目里把外生特征分成四类每一类都有自己的构造逻辑。第一类是时间日历特征。星期几、是否周末、是否节假日、是否节假日前后一天这些特征直接反映人们的出行习惯。工作日早晚高峰明显周末午间出现客流小高峰节假日前后一天往往会有离返程客流高峰。构造方法很简单直接用pandas的dt访问器提取节假日表需要单独维护并merge进来# 构造时间维度特征 df[weekday] df[time_slot].dt.weekday df[is_weekend] df[weekday].isin([5, 6]).astype(int) df[hour] df[time_slot].dt.hour df[month] df[time_slot].dt.month # 合并节假日特征 df_holiday pd.read_csv(holiday_list.csv, parse_dates[date]) df_holiday[is_holiday] 1 df df.merge(df_holiday[[date, is_holiday]], left_ondf[time_slot].dt.date, right_ondate, howleft).fillna({is_holiday: 0})第二类是天气特征。温度、降水、空气质量指数都会影响人们的出行意愿尤其是降水对地面交通的影响会直接传导到轨道交通。比如暴雨天很多人不开车改乘地铁全网客流可能上升5%到10%。天气特征需要按小时对齐到客流数据上接第三方天气API的数据时要注意时区问题统一用北京时间。第三类是上一时段的客流滞后特征。客流具有很强的自相关性今天早高峰的客流和昨天早高峰的客流高度相关。因此要把前一天同一时段、前一周同一时段的客流作为特征拼进来这些滞后特征对短期预测的贡献往往比天气特征大得多# 构造24小时前、168小时前的客流作为滞后特征 df[flow_lag_24h] df.groupby(station_id)[entry_count].shift(24) df[flow_lag_168h] df.groupby(station_id)[entry_count].shift(168)第四类是大型活动特征。演唱会、体育赛事、展会等大型活动会在短时间内往特定站点灌入大量客流。这类特征比较难提前布局只能靠运营方提供活动计划和场地坐标再把活动影响范围映射到最近的地铁站上。我当时的做法是维护一张活动信息表按季度更新每条记录包含活动日期、影响站点、预计增加人次构造特征时按站点和时间匹配。2.3 数据对齐与滑窗划分特征构造完之后最关键的步骤是数据对齐。不同来源的数据粒度不一致AFC数据是15分钟粒度天气数据可能只有小时粒度活动信息是日粒度。处理原则是向最细粒度对齐。15分钟的客流样本天气特征取该15分钟所在小时的值活动特征取当天的标记滞后特征取对应时间点精确到小时的数值。洗数据时最容易犯的错是未来数据泄露。比如构造滞后特征时如果用未来时刻的数据来填充当前样本模型的评估结果会虚高上线后预测精度会断崖式下跌。我的建议是构造特征时时刻牢记当前时刻只能看到过去的信息这条铁律每个特征都问一句这个值在预测时点能拿到吗拿不到就不能用。训练集、验证集、测试集的划分不能用随机打乱的方式因为时间序列数据一旦打乱就破坏了时间依赖性。正确做法是按时间顺序切分比如用前两年的数据做训练接下来的3个月做验证最后3个月做测试。这样能真实模拟模型在实际使用中的表现因为模型上线后预测的永远是它没见过的未来数据。对于多步预测我采用递归预测和直接预测结合的方式。递归预测是先用已知数据预测下一个15分钟然后把预测值当作历史值继续预测下下个15分钟这种方式简单但误差会累积。直接预测是训练多个模型分别预测未来1步、2步、4步、8步虽然训练成本高但每个模型只专注一个预测步长精度更稳定。实际项目中未来1小时内的预测我用了直接预测超过1小时的用递归预测两者结合达到了不错的平衡。3. 核心建模分析与模型调参3.1 基线模型SARIMA把问题讲清楚比一上来就堆模型重要。在做复杂模型之前我先用SARIMA建立基线预测目的有两个一是验证数据质量如果连SARIMA都跑不出合理的精度说明数据准备阶段有潜在问题二是给后续机器学习模型设定一个必须超越的基准如果LightGBM和LSTM连SARIMA都打不过那说明特征工程做得不到位。SARIMA全称是季节性差分自回归移动平均模型核心假设是时间序列的当前值与其历史值、历史残差还有季节性周期有关。轨道交通客流有明确的周期模式24小时周期对应一天内的早晚高峰168小时周期对应一周内的通勤模式。因此我用SARIMA(2,1,2)(1,1,1,24)作为小时粒度客流的基线配置其中24表示24小时为一个周期。模型参数选择不能全靠经验拍脑袋我用pmdarima库的auto_arima函数来自动搜索最优参数from pmdarima import auto_arima # 取某条线路全网小时客流做基线 train_series df_all.loc[df_all[time_slot] 2023-10-01, network_flow] # 自动搜索SARIMA参数设定季节周期为24 model_sarima auto_arima(train_series, seasonalTrue, m24, start_p0, max_p5, d1, max_d2, start_q0, max_q5, traceTrue, error_actionignore, suppress_warningsTrue) # 输出最优参数并预测未来24小时 print(model_sarima.summary()) forecast_sarima model_sarima.predict(n_periods24)跑完之后把SARIMA的预测结果做个可视化对比你会发现它能捕捉到客流的基本日周期形态但缺点是它对突发事件不敏感遇到临时限流、天气突变这类情况时预测值会明显偏离实际。这是因为SARIMA本质上是线性模型无法利用天气、活动等外部特征。SARIMA建模过程中有个容易踩的坑数据需要先做平稳性检验。客流的均值在一天内起伏很大明显非平稳所以必须设置差分参数d1来消除趋势。如果设置过小的d模型会不收敛设置过大又会丢失重要信息。一种有效判断方法是画出原始序列和一阶差分后的自相关图ACF图如果ACF衰减速度明显加快说明差分阶数选对了。3.2 机器学习模型LightGBMSARIMA验证了数据质量之后我开始上机器学习模型。选LightGBM而不是普通的随机森林或XGBoost主要三个原因一是训练速度快面对百万级的样本量也能在几分钟内完成训练二是能自动处理缺失值对不同量纲的特征不敏感三是对非线性关系拟合能力强能充分挖掘多维特征之间的相互作用。LightGBM在这里解决的核心问题不是时间序列本身而是用当前可观测的信息去回归未来的客流值。本质上它是一个回归任务输入特征包括时间特征、天气特征、滞后特征、活动特征输出是未来某个时段的客流。这个思路完全绕开了SARIMA对时间序列平稳性和周期性的严苛要求模型能从数据里自己学习这些模式。训练流程中最重要的参数是时间类的交叉验证。滚动时间窗口交叉验证是标配import lightgbm as lgb # 定义特征列 feature_cols [weekday, hour, is_weekend, is_holiday, temperature, precipitation, flow_lag_24h, flow_lag_168h] target_col entry_count X_train df_train[feature_cols].values y_train df_train[target_col].values # 构造LightGBM数据集设置类别特征 train_data lgb.Dataset(X_train, labely_train) valid_data lgb.Dataset(X_valid[feature_cols].values, labeldf_valid[target_col].values) params { objective: regression, metric: mae, boosting_type: gbdt, num_leaves: 63, learning_rate: 0.05, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 5, verbose: -1, n_jobs: -1, max_depth: 7 } model_lgb lgb.train(params, train_data, num_boost_round1000, valid_sets[valid_data], callbacks[lgb.early_stopping(50)])用lightgbm做客流预测特征重要性分析非常值得做。我在项目中输出过一次特征重要性排名排名前几的几乎是滞后特征滞后24小时客流和滞后168小时客流贡献了绝大部分预测能力天气特征贡献排在中间活动特征因为出现频率低排名较后。这不奇怪客流本身高度依赖历史惯性也说明特征工程方向是对的。但LightGBM有个明显的短板对突发性客流变化的响应滞后。如果某天下午突然下暴雨LGB模型预测晚上高峰客流时因为特征里没有反映降雨刚开始这个事件预测仍会按照晴天模式走导致低估实际客流。针对这类问题我后来在特征里加了过去3小时累计降雨量和当前是否正在降雨两个特征小幅改善了恶劣天气下的预测精度但根本性解决得靠模型融合和上线后的实时修正。3.3 深度模型LSTM序列预测15分钟粒度的短时客流预测我用了LSTM。LSTM擅长捕捉序列中的长期依赖关系而客流序列恰好有很强的长周期模式。用LSTM和SARIMA做15分钟粒度预测的对比整体精度LSTM略高一筹尤其在尖峰位置的追赶能力明显更好。LSTM建模最核心的步骤是构造带时间步的样本数据。假设我们用历史上48个时段的客流即12小时来预测未来1个时段的客流那么每个样本就是[48, feature_num]的三维张量import numpy as np from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from sklearn.preprocessing import MinMaxScaler # 归一化到[0,1]区间 scaler MinMaxScaler() scaled_data scaler.fit_transform(df_15min[[entry_count, weekday, hour, is_holiday]]) # 构造滑窗样本lookback48个时段 def create_sequences(data, lookback48, horizon1): X, y [], [] for i in range(len(data) - lookback - horizon 1): X.append(data[i : i lookback]) y.append(data[i lookback : i lookback horizon, 0]) # 只预测entry_count return np.array(X), np.array(y) X_train, y_train create_sequences(scaled_data[:train_len], 48, 1) model_lstm Sequential([ LSTM(64, return_sequencesTrue, input_shape(48, X_train.shape[2])), Dropout(0.2), LSTM(32), Dense(16, activationrelu), Dense(1) ]) model_lstm.compile(optimizeradam, lossmse, metrics[mae]) history model_lstm.fit(X_train, y_train, epochs30, batch_size128, validation_split0.1, verbose1)这几个超参数是调出来的结果不是一次定的。LSTM的隐藏层单元数量和层数我建议从少量开始64个单元加两层LSTM已经能覆盖大部分客流序列模式调太高会有过拟合风险。学习率默认0.001如果训练loss下降太慢可以改成0.0005或0.01但改动幅度不要太大。LSTM有一个跟LightGBM不一样的使用要点因为LSTM是连续滑窗训练样本之间存在重叠关系直接随机划分训练集和验证集会导致数据泄漏评估结果虚高。这里也必须按时间顺序严格划分前70%的数据做训练后30%做验证。另外预测结果需要反归一化才能比较绝对值误差# 反归一化预测值 y_pred_inverse scaler.inverse_transform( np.concatenate([y_pred_raw, np.zeros((len(y_pred_raw), X_train.shape[2] - 1))], axis1) )[:, 0]反归一化这步看起来简单但如果忘了把其他特征的列拼回去结果会上误差明显偏大这是新手最常踩的坑之一。3.4 模型评估与选择逻辑模型好坏不能只看一个指标我在项目里统一用MAE、MAPE、RMSE三个指标来评估它们的含义不一样。MAE平均绝对误差单位与人次相同反映平均偏离程度最直观。比如全日客流预测的MAE是8000人次意味着平均每天偏差8000人次。MAPE平均绝对百分比误差消除了量级影响方便不同站点、不同线路之间的横向对比。MAPE低于5%才算优秀水平。RMSE均方根误差对大误差更敏感如果RMSE明显大于MAE说明预测中存在个别极大的偏离点。我跑过一遍日粒度客流预测三种模型的对比结果如下模型MAE万人次MAPE%RMSE万人次SARIMA3.216.84.15LightGBM2.184.62.89LSTM2.355.13.02LGBLSTM融合1.974.12.56可以看出LightGBM整体优于LSTM这主要是因为日粒度数据量相对有限LSTM的优势没有完全发挥出来。但融合模型进一步提升了精度它的原理很简单LightGBM擅长拟合特征表格信息LSTM擅长拟合序列时序信息两者互补性很强加权平均后误差互为抵消MAPE直接压到了4.1%。模型融合的权重不是拍脑袋定的我用网格搜索确定两个模型的最优权重from sklearn.metrics import mean_absolute_error best_mae float(inf) best_weight 0.5 for w in np.arange(0.1, 1.0, 0.05): y_ensemble w * y_pred_lgb (1 - w) * y_pred_lstm mae mean_absolute_error(y_true, y_ensemble) if mae best_mae: best_mae mae best_weight w print(f最优权重LGB {best_weight:.2f}, LSTM {1 - best_weight:.2f}, MAE: {best_mae:.4f})这里有一个容易被忽视的经验融合模型的收益在模型间相关性较低时才明显。如果LSTM和LightGBM预测结果高度一致融合等于白做。所以在动手融合前我通常会先画一张两个模型预测误差的散点图看看误差之间是否存在互补性如果误差相关性太高就直接选单一模型上线了。4. 系统实现与上线部署4.1 预测服务封装模型训练完之后不能躺在硬盘里吃灰必须封装成可供上层调用的服务。我用FastAPI封装了预测模型对外提供统一的RESTful接口。接口设计遵循一个原则请求参数尽量简单服务内部负责查数据和调模型响应结果按统一格式返回。接口的核心逻辑是先取特征再推理。用户在请求中传预测日期和站名服务先去数据库取对应站点的历史客流特征拼好特征矩阵再喂给模型推理。之所以要把这一步封装到服务内部是因为如果让前端自己拼特征一旦特征逻辑变了所有前端都要跟着改维护成本太高。完整接口代码结构如下from fastapi import FastAPI, HTTPException from pydantic import BaseModel import joblib import pandas as pd app FastAPI() # 加载训练好的模型和特征变换器 model joblib.load(models/lgb_daily_v3.pkl) class ForecastRequest(BaseModel): station_id: str start_date: str end_date: str app.post(/api/v1/daily/forecast) def daily_forecast(req: ForecastRequest): try: # 从数据库获取历史特征 feature_df fetch_features(req.station_id, req.start_date, req.end_date) # 模型推理 pred model.predict(feature_df) # 返回标准格式 return {code: 0, data: [ {date: d.strftime(%Y-%m-%d), flow: round(float(p), 2)} for d, p in zip(pd.date_range(req.start_date, req.end_date), pred) ]} except Exception as e: raise HTTPException(status_code500, detailstr(e))服务的线上部署我用了uvicorngunicorn的组合。gunicorn是多进程uvicorn是异步加速器两者配合能提供不错的并发能力。启动命令如下gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app --bind 0.0.0.0:8000这里有一个工程化的关键点模型和特征之间的版本管理。模型文件我在命名里加了版本号如lgb_daily_v3.pkl特征工程的代码也做了版本快照。上线新模型时我会先跑一遍回测确认精度没有下降再切换同时保留旧版本的模型文件一旦新模型出问题可以快速回滚。4.2 可视化看板搭建预测结果如果不能直观展示业务方很难信任。我做了一个轻量级可视化看板不依赖重型前端框架纯HTMLECharts就能跑因为看板只需要展示三类图全网客运量趋势曲线、分站热力图、精度监控散点图。全网客运量趋势曲线用折线图展示画上历史实际值和未来预测值两条曲线连续衔接业务方一眼就能看出预测走势是否合理// 实际值与预测值拼接展示 const option { title: { text: 未来7天全网客运量预测 }, tooltip: { trigger: axis }, legend: { data: [历史实际, 预测值] }, xAxis: { type: category, data: dates }, series: [ { name: 历史实际, type: line, data: histData, smooth: true, lineStyle: { color: #5470c6 } }, { name: 预测值, type: line, data: forecastData, smooth: true, lineStyle: { color: #ee6666 } } ] };分站热力图用二维矩阵图展示横轴是站点纵轴是时段颜色深浅代表进站量大小。这个图对车站运营人员最有价值他们能快速定位哪些站点在哪些时段会拥挤提前安排工作人员到岗。精度监控散点图的作用是回看历史预测精度。当天的预测值和实际值在同一个坐标系里对比偏差太远的点自动标红。运营方偶尔会问你们这个预测靠不靠谱直接把这张图调出来展示每天的误差分布比用任何解释都管用。我建了一个每天自动生成预测报告的任务早上5点跑完预测后把结果同步到看板对应的JSON文件里。这个过程中有两点要注意一是预测数据要带上生成时间戳防止看板缓存旧数据二是接口要支持按线路、按站点维度查询方便不同角色的用户只看自己关心的范围。4.3 模型更新与告警机制模型不是一劳永逸的客流模式和外部环境都在不断变化。我设计了每日自动评估和每周自动重训机制。每日凌晨预测任务跑完后拿前一天的实际客流跟预测值对账计算当日MAPE。如果连续3天MAPE超过阈值比如日粒度超过6%15分钟粒度超过8%系统自动发告警邮件给模型负责人。告警机制的本质是让模型问题在早期暴露而不是等到业务方投诉了才后知后觉。每周一凌晨系统自动把所有近期累积的实际客流数据并入训练集重新训练一次模型并对比新旧模型在最近一个月的回测精度。如果精度提升超过0.3个百分点就自动替换线上模型如果精度没有明显变化甚至下降则保留旧模型并输出诊断报告。这个流程让模型能缓慢适应客流季节变化而不需要人工干预。模型重训的自动逻辑这里简单说一下def weekly_retrain(history_data, current_model_path): # 合并最新数据并重新训练 retrained_model train_model(history_data) new_mae evaluate(retrained_model, last_30d_data) old_mae evaluate(load_model(current_model_path), last_30d_data) # 精度提升超过阈值才替换 if new_mae 0.003 old_mae: save_model(retrained_model, models/lgb_daily_v4.pkl) update_route(models/lgb_daily_v4.pkl) return model replaced else: return model kept告警和重训逻辑都应该放到独立的任务调度队列里不能在预测接口的服务里跑否则预测耗时会不稳定。我用APScheduler做定时任务调度每天凌晨2点启动数据同步、凌晨3点跑全量特征工程、凌晨5点执行预测、早上6点前把预测结果写入看板数据库。整套流程跑下来大概40分钟完全赶得上早班调度会之前出结果。5. 常见问题排查与实战心得5.1 数据口径不一致问题这个项目里我遇到最多的坑不是模型参数调不好而是不同数据源对客流的统计口径不一致。AFC系统统计的是刷卡次数清分系统统计的是全路网换乘人次数问卷调查的口径又是乘客出行次数三个口径之间差距很大如果不加区分直接混用模型预测出来的数字根本没法解释。解决方法是建立统一的口径定义文档并在数据接入层做标识。在特征表里加一列flow_type值域只有entry、exit、transfer、network四种。后续任何模型在取数时都必须指定flow_type参数特征工程代码直接拒绝无口径特征这样从源头杜绝了数据混用问题。时间口径也同样容易出问题。有人按自然日统计有人按运营日统计首班车到末班车还有人按早5点到次日早5点统计。地铁每天凌晨还会停运几个钟头自然日和运营日的边界不重合。这个在节假日和新线开通时要尤其注意我建议项目初期就明确统一采用运营日口径即从当日首班车时刻到末班车时刻减少边界模糊带来的误差。5.2 节假日与突发客流怎么处理节假日是客流预测模型最容易翻车的地方。平时工作日的预测误差能控制在4%以内一到五一、国庆误差飙到10%以上是常有的事。原因是节假日客流模式跟普通周完全不同历史数据里一年只有十几次节假日样本模型很难学到规律。我用的其中一种办法是同类日期匹配法。把预测日按类型打标签如春节、国庆、五一、元旦、普通周末、普通工作日然后只提取历史同类型日期的客流作为参考而不是让模型自己从全部数据里挖掘规律。例如预测春节当天的客流就只看历史春节的客流数据结合当年列车开行计划的调整情况做修正。具体实现逻辑是通过节假日参数表来控制预测模式。如果预测日期落在节假日表中就调用节假日专用预测分支特征中加入距假期开始天数、假期第几天、是否假期最后一天等累计变量这些变量对节假日客流模式的刻画很有帮助# 判断预测日期是否处于节假日区间 holiday_ranges holiday_df[[holiday_start, holiday_end]].values for start, end in holiday_ranges: if start target_date end: holiday_type holiday_df.loc[ (holiday_df[holiday_start] start) (holiday_df[holiday_end] end), holiday_name ].values[0] days_from_start (target_date - start).days days_to_end (end - target_date).days # 给特征矩阵加入节日编号和相对天数 break突发客流是另一个难点比如演唱会散场后附近地铁站瞬时客流暴增这种强度高、频率低的事件任何模型都很难提前预测准。我的策略是实时修正而非事前预测。当系统监测到某站点实际客流超过当日预测上限的20%时自动触发预警并把最新实际客流作为初始状态滚动更新未来1到2小时的预测。这样虽然不能提前预警但能及时修正短期预测为现场客流组织争取时间。5.3 性能优化与工程细节模型服务上线后我遇到的最直接的问题是内存和推理耗时。LSTM模型虽然精度不差但如果对全网几百个站点逐一做15分钟粒度预测单次推理的耗时不可忽视。我把全网预测拆成多线程并行每个线程处理一部分站点整体耗时压缩到原来的四分之一以内。Python的多线程受GIL限制CPU密集型任务效率不高但LSTM推理是C库调用GIL释放后能并行所以仍然有效。特征工程阶段的内存泄漏问题也值得一提。pandas在反复合并大表时如果不释放中间变量内存会飙升到几十GB然后被系统杀掉。我的经验是分块处理、及时gcimport gc # 处理完一个线路的数据后立即释放内存 for line_id in all_line_ids: line_df load_line_data(line_id) features build_features(line_df) features.to_parquet(ffeatures/line_{line_id}.parquet) del line_df, features gc.collect() # 显式触发垃圾回收模型更新时的“模型与特征对齐”也是一个容易忽略的细节。LightGBM有版本升级后不同版本之间对类别特征的编码方式可能变化训练时用的是v3特征线上推理如果用v4特征工程逻辑维度不一致就会报错。所以每次特征工程代码变更时必须同步打版本号并在模型文件名中带上特征版本号比如lgb_daily_f3_v4.pkl推理时指定版本号加载避免新旧特征逻辑混用。最后想说的是客流预测系统这个方向模型永远是配角数据和工程才是主角。把数据口径统一、特征工程做扎实、服务稳定可靠模型精度自然会上去。我在调LSTM时花了大半个月把超参数调到满意但真正让MAPE从6%降到4%的是一次数据清洗规则的修正。所以如果你正准备做类似的预测系统我的第一个建议是先把数据底子打牢再谈模型选型。本文还有配套的精品资源点击获取
返回列表