资讯动态

基于LSTM的共享单车需求预测系统实战:从数据清洗到部署

发布时间:2026/10/22 18:39:05 来源:尧图企业网站定制
前阵子我接到一个共享单车需求预测系统的开发任务核心就是用LSTM神经网络去预测不同站点未来几个小时的借还车需求。这个项目挺典型的做智能交通、做城市数据分析的人应该都会遇到类似的场景车总是潮汐式地从一个地方消失又在另一个地方堆积调度车永远在救火。预测做不好调度就是盲人摸象。这篇博客打算把我从数据清洗、特征构建、模型训练到最终部署的完整过程都写出来包括踩过的坑和实测有效的方法。适合正在做时序预测、想用LSTM解决实际业务问题的朋友也适合想了解共享单车调度系统是怎么运作的人。我会尽量把每一步为什么这么做讲清楚而不是只给结论。1. 先搞清楚看起来是调度问题本质上是预测问题1.1 共享单车的潮汐效应到底有多夸张共享单车最让人头疼的就是早晚高峰的潮汐效应。早高峰住宅区的车在半小时内被骑走一大半而地铁口、写字楼门口的车多到停不下晚高峰正好反过来。你可以想象一下一个热门住宅站点早上七点半的库存是80辆到了八点十分就空了而三百米外的地铁站此时已经有上百辆滞留车。调度员如果不知道接下来会发生什么就只能等站点满了或者空了才派车这种模式在运营上叫事后调度永远慢半拍。所以这个项目的第一个关键判断是与其花大力气去优化调度路径算法不如先把预测这一步做扎实。只要能在高峰到来前的1到3个小时内大致知道每个站点会缺多少车、多多少车调度车就可以提前行动。预测的精度每提高一点调度效率的提升是成倍放大的。这也是为什么需求预测系统会成为整个调度系统的中枢模块。1.2 为什么是LSTM而不是ARIMA或者XGBoost做时序预测很容易纠结选什么模型。我最开始也把常见方案都过了一遍ARIMA、SARIMA、XGBoost、LightGBM、LSTM甚至Transformer也看了一眼。如果只是预测总量SARIMA其实够用它能把日周期和周周期都建模出来。但问题是这个项目需要做到站点级预测一个城市几百个站点每个站点的波动规律都不一样有的站点工作日和周末完全两个形态有的站点会受天气影响剧烈波动。ARIMA这种统计模型在面对多站点、多特征交叉影响的时候调参成本高到离谱。XGBoost这类树模型也能做而且在小样本上表现经常比神经网络还稳。但树模型做时序预测有一个天然的劣势它本身不理解顺序这件事。你要靠手工构造滞后特征、滑动窗口统计量去告诉它昨天这个时候有多少人借车过去三小时的平均用车量是多少。LSTM不一样它内部有门控机制能自己从历史序列里学会记住什么、忘掉什么。数据质量一般、特征工程不想做得太重的时候LSTM更容易端到端跑通。实测下来在站点级预测这个任务上LSTM比XGBoost的RMSE低了大概12%到15%尤其是面对突发天气变化时LSTM的响应更平滑不会出现预测值剧烈跳变的情况。2. 系统整体设计与技术选型思路2.1 系统分层数据怎么进门、模型在哪儿跑、结果往哪儿去整个系统我分成了三层数据层、预测层、应用层。这个分层不是拍脑袋定的而是为了方便后面独立迭代。数据层负责把多源数据汇总到一起。主要包括三块历史骑行订单记录、天气数据、日历特征数据。订单记录的粒度是单次骑行流水包含站点ID、借车时间、还车时间、借车站点、还车站点天气数据按小时记录包含温度、湿度、风速、天气现象编码日历特征就是是否工作日、是否节假日、是否周末这一类标志位。数据层统一清洗后写入数据库按小时粒度聚合成每个站点的借车量和还车量。预测层是核心。它包含离线训练和在线推理两条链路。离线训练每天凌晨跑一次用过去30天的数据重新训练模型在线推理是一个常驻服务接收站点ID当前时间的请求返回未来1到6小时每个小时的预测借还量。推理结果会缓存起来因为同一个站点在五分钟内被频繁查询没必要每次重新计算。应用层面向运营人员。调度大屏上展示每个站点的供需热力图颜色越红表示即将缺车越严重颜色越蓝表示即将堆积越严重。调度工单系统会根据预测结果自动生成建议某站点在某个时段预计缺多少车建议调度车在什么时间窗口内调度多少辆车过去。2.2 技术栈怎么选为什么这么选技术栈的选择我遵循一个原则够用、好调试、生态成熟。开发语言用Python这个没有悬念深度学习生态基本都在Python这边。深度学习框架我选了PyTorch而不是TensorFlow主要是因为动态图机制调试起来太方便了训练中断了想打断点看中间张量直接就可以操作不用像静态图那样先把图编译一遍。而且PyTorch在时序模型上有大量现成组件可以使用。数据库用了PostgreSQL。这里有个小考虑虽然订单数据是典型的时序数据理论上用时序数据库更合适但考虑到项目中还要存站点元数据、调度工单、模型版本信息这些关系型数据用PostgreSQL一把梭能省掉很多数据同步的麻烦。PostgreSQL的窗口函数在处理滑动窗口统计时也非常顺手比如计算过去7天同一时段平均借车量一条SQL就能搞定。推理服务用FastAPI轻量、异步支持好模型加载到内存后直接用PyTorch的CPU推理单次预测在毫秒级。Redis用来做结果缓存TTL设置5分钟。可视化用了Grafana因为它的时序图表做得最好而且支持从PostgreSQL直接拉数据省去自己写前端图表的成本。整个系统跑在一台32核64G的服务器上CPU训练完全够用因为模型结构不大、数据量也就几十万条序列样本不需要上GPU。3. LSTM模型原理与需求预测的适配逻辑3.1 把LSTM的门控机制讲得通俗一点可能有人对LSTM不太熟悉我用一个简单的方式解释一下。普通的RNN在处理长序列的时候有个问题信息在传递过程中会逐渐丢失传到后面几步最开始的关键信息早就被冲淡了。你可以把它想象成一个人听一段很长的会议内容听到最后开头讲了什么都忘了。LSTM专门解决这个问题它在内部加了一个记忆通道也叫细胞状态可以理解为一个笔记本。网络可以选择往笔记本上写内容、保留内容、或者擦除内容。往笔记本上写多少新东西由输入门控制之前的记忆保留多少由遗忘门控制最终输出什么由输出门控制。这三个门都是可以学习的模型在训练过程中会自动摸索出一套规律。放到共享单车场景里就很好理解模型会学会记住这个站点工作日早高峰的借车高峰大概在7点半到8点半之间这种周期性记忆同时会学会在新的天气信息飘进来的时候比如突然下雨把昨天这个时候的借车量高这个记忆适当地忘掉一部分因为下雨天大家不太愿意骑车。3.2 时间步与特征设计LSTM吃什么、吐什么LSTM的输入是一个三维张量形状是(batch_size, time_step, feature_dim)。time_step是回溯窗口长度也就是用过去多少个时间点的数据来做预测feature_dim是每个时间点上的特征数量。在这个项目里时间粒度是小时time_step取的是24也就是用过去24小时的数据预测未来。之所以不取更短或者更长是经过了实际对比的time_step小于12的时候模型缺少对上一整天同一时刻的记忆预测值明显滞后取48甚至72虽然训练集增加了但对RMSE的改善很小训练时间却翻倍性价比不高。特征数量大概有15个左右分成了三大类。第一类是历史序列特征包括目标站点过去24小时每个小时的借车量、还车量、当前车辆库存量。第二类是时间特征包括小时序号0到23、是否周末、是否节假日。第三类是天气特征包括温度、湿度、风速、天气现象编码0代表晴、1代表多云、2代表小雨、3代表大雨。这里有个细节值得说小时序号不能直接当作数值喂给模型因为23点和0点虽然数值上相差很大但在时间意义上只差一小时直接用原始数值会让模型学出错误的关系。我的做法是做正弦余弦编码把小时拆成sin和cos两个分量这样23点和0点在编码空间里就是相邻的点。模型的输出根据预测视界决定。如果只预测未来1小时输出就是两个值借车量和还车量。如果预测未来6小时可以选择两种方案一种是把输出层神经元数设为12直接一步输出6个小时的借还量另一种是用序列生成的方式把上一个小时的预测结果作为输入喂给模型滚动往下推。我实测下来6小时以内直接一步输出更稳定因为滚动生成会累积误差越滚越偏。所以最终方案是对1小时、3小时、6小时分别训练了三个模型而不是一个模型干所有事。3.3 站点数量太多怎么让模型训得动一个城市可能有几百甚至上千个站点。如果给每个站点单独训练一个LSTM模型训练和部署的成本都会爆炸如果所有站点混在一起训练一个模型又很难学出每个站点的个性化规律。我采用的折中方案是先聚类、再分组建模。具体做法是根据每个站点过去30天的历史骑行曲线提取几个统计特征早晚高峰的形状、平峰期用车量水平、周末工作日差异程度、以及站点周边环境类型居民区、办公区、地铁站、商圈。用KMeans把这些站点聚成几类在这个项目里聚成了6类。然后每一类训练一个模型模型的输入特征里会加一个额外的站点嵌入向量用Embedding层把一个站点ID映射成一个8维向量。这样做的好处是同一类里不同站点之间可以互相学习数据量少的站点也能借用同类站点的共性规律有效缓解了小站点样本不足的问题。实测下来聚类分组模型的整体RMSE比不分组的全局模型低了约10%比每个站点单独建模低了 примерно 5个百分点主要收益来自中小型站点的预测稳定性。4. 实操全流程从数据清洗到模型训练的关键细节4.1 数据清洗时序数据最怕乱处理这个环节看着不起眼实际上对最终效果的影响比模型结构还大。我踩过的第一个坑就是缺失值处理。某站点某天的数据因为设备离线一整天全没有如果直接把这一行删掉时间序列就断了一截LSTM在滑动窗口取样时会把这个断点误认为是突然没人用车了预测结果会出现诡异的低谷。正确做法是先用插值把缺失位置补上。对于单点缺失用前后几个小时的均值填充对于连续大段缺失用上周同一天同一时段的数值来填充。异常值也要小心处理。比如某个站点因为有人批量维保或者活动集中投放单小时借车量突然飙到正常值的好几倍。这类异常值对模型训练的影响很大因为LSTM用的是均方误差做损失函数离群点会给梯度带来巨大的拉动。我统计了每个站点历史分布的95分位数和99分位数把超过99.5分位数的值截断到99.5分位数。注意是截断而不是直接删除因为时间序列的长度不能随意变短截断能保留数据点的存在性只是把极端值拉回到合理范围。训练集、验证集、测试集的划分也有讲究。时序数据绝对不能随机打乱后再划分必须按时间顺序切分。我用的是前70%做训练中间15%做验证最后15%做测试。验证集用来做早停和调整超参数测试集只在最终评估时碰一次这样得到的评估指标才有说服力。4.2 特征工程与数据集构建数据集构建的核心是滑动窗口。我写了一个简单的窗口生成器遍历每个站点每一天以每个小时为预测起点向后取24小时的历史数据作为输入向前取未来1小时或3小时、6小时的借还量作为标签。滑动步长是1小时也就是说数据在时间上是高度重叠的。这种重叠会让训练样本之间存在相关性理论上对模型泛化有影响实践中影响不大但会让验证集的评估结果显得偏乐观心里要有数。除了模型直接用的特征我还额外构造了一个强特征过去7天同一时段的历史平均借车量。这个特征非常有效因为它相当于给模型提供了一个季节性基线LSTM只需要学习这个基线之外的偏差就可以了。训练的时候我把这个特征也放进输入序列让LSTM自己去决定参考权重。归一化我用的是RobustScaler而不是常见的MinMaxScaler。原因是共享单车的需求数据有长尾分布某些热门站点在特殊事件期间的峰值远高于普通时段MinMaxScaler会被这些极端值拉偏导致普通时段的差异被压缩到很窄的区间里。RobustScaler使用中位数和四分位距对异常值不那么敏感训练出来的模型在峰值时段的预测也更稳健。4.3 模型结构与训练参数配置模型结构没有做得特别花哨就是一个标准的双层LSTM加全连接输出层。具体参数是隐藏层维度64层数2dropout设为0.2输入特征维度15输出维度根据预测视界决定1小时预测就是2个值6小时预测就是12个值。优化器用AdamW初始学习率0.001batch size是64训练最多跑80个epoch同时用早停机制验证集损失连续10个epoch没有下降就停止训练。这里说一下为什么用AdamW而不是普通Adam。AdamW把权重衰减和梯度更新解耦了在LSTM这种带大量正则项的结构里训练稳定性更好一些尤其是在学习率需要调低的时候不容易崩。dropout加在LSTM层之间而不是输入输出层这个位置很关键。如果加在输入层会把历史序列的关键信息随机丢弃模型反而学不好加在层间可以防止对训练数据的死记硬背。训练过程中的收敛曲线也值得看一眼前5个epoch训练loss下降很快从初始的3.8左右降到0.9左右后面进入平台期loss下降变得很缓慢到第20个epoch之后验证集loss开始出现微小波动早停在epoch 43的位置保存的模型参数是验证集loss最低的那一份。这里有个很多人容易犯的错保存最后一个epoch的模型而不是验证集最优的模型。因为LSTM训练到后期可能已经在过拟合了最后一个epoch的模型往往不如第43个epoch的好。4.4 模型评估与效果对比评估指标我用了三个RMSE、MAE、MAPE。RMSE对大的预测误差很敏感适合衡量极端情况MAE反映平均偏差MAPE用于百分比理解。拿6小时预测视界的模型来说在测试集上的表现是RMSE约11.4辆/小时MAE约7.8辆/小时MAPE约23%。对于共享单车需求预测这个任务这个精度已经能支撑运营决策了。调度人员不需要精确到这个站点会少3辆车只需要知道这个站点在一小时后会缺大约10到15辆车。我还做了一个横向对比实验。同样的特征和数据拿ARIMA和XGBoost分别训练结果是ARIMA的RMSE是18.7XGBoost是14.2LSTM是11.4。这个对比数据很多项目文档里都会用到实际跑出来的结论也确实如此。但要注意这个差距不是绝对的如果站点数量少、规律简单XGBoost可能反超LSTM。做工程选型不能只看模型排名要结合自己的数据体量来定。5. 常见问题与排查技巧实录5.1 预测结果总是滞后一拍怎么破我最初训练出来的模型做1小时预测时效果还行但一旦做3小时以上的预测预测曲线的形状总是比真实曲线慢半拍。比如真实需求在早上8点达到峰值我的预测峰值却在8点半甚至9点才出现。这个问题很典型本质原因有两个一是滑动窗口里过去24小时的历史数据占主导模型倾向于把最近的趋势延续下去所以变化都被推迟了二是时间特征不够强模型没有充分学到8点这个时刻本身就该有高峰。解决办法有两个方向。第一把小时的正弦余弦编码加进输入让模型知道现在是一天中的哪个时刻这是最直接有效的。第二增加过去7天同一时刻均值这个特征相当于给模型一个明确的周期性先验。两种方法叠加后预测滞后的现象基本消失了峰值时刻的误差从将近1小时缩小到15分钟以内。5.2 部分站点预测值和实际相差很大为什么测试的时候我发现某些特定站点的预测效果特别差误差是同类站点的两倍以上。排查后发现这些站点都有一个共同点周边有大型活动场所比如体育馆或者会展中心。平时需求很平稳一旦有演唱会、球赛散场时段的借车需求会瞬间暴涨5倍以上。这种事件驱动型的突变在历史数据里出现的次数太少LSTM根本学不到规律。这类问题靠模型本身很难彻底解决。我的处理方式是在系统里加了一个活动事件修正模块数据层维护一个活动日历当预测时段附近有大型活动时对模型的预测值做规则化修正。比如散场时段加一个固定增量增量的数值根据历史同类活动的平均影响来设定。这样既不改变模型的通用能力又能覆盖特殊场景。5.3 训练loss不稳定、动不动就nan有段时间训练loss在中间几个epoch突然变成nan排查了好久。最后发现是数据里有个站点的特征值出现了一个极端大的数值因为有一个外部数据源的天气字段在个别行里存了异常大的温度值比如999。这种情况在任何机器学习项目里都会遇到但LSTM对梯度很敏感NaN一旦出现基本上整个训练就废了。解决办法就是在数据入口处做严格的范围校验温度范围限定在-30到50摄氏度之间超出范围直接标记为缺失值进入插值流程。另外还有一个训练不稳定的常见原因学习率太大。我试过学习率调到0.01训练刚开始的loss疯狂震荡根本没法收敛。调到0.005勉强能收敛但精度较差最后的0.001才稳定下来。如果是更大规模的数据可以尝试学习率预热加余弦退火的调度策略但在这个项目里简单的固定学习率就够用了。5.4 新站点没有历史数据怎么预测运营方往往会在某个区域投放一批新站点这些站点刚上线时没有任何历史骑行数据标准模型完全没办法处理。我用的方案是冷启动迁移预测根据新站点的地理位置和周边环境特征先从已有的6类站点中找最相似的一类。具体方法是计算新站点周边500米范围内的POI分布比如有多少个小区、多少个写字楼、多少个地铁口和每一类站点的典型POI分布做相似度匹配。匹配完成后直接把这一类站点的模型迁移过来同时把站点Embedding向量初始化成所在类别的中心向量。新站点上线几天后积累了真实数据再用增量训练的方式把Embedding向量和模型参数做微调。这个方法实测效果不错新站点上线第一天的预测MAPE大概在35%左右三天后能降到25%以内一周后基本达到普通站点水平。纯靠规则预测的话第一天的误差可能会超过50%。6. 部署上线与运营侧的落地效果复盘6.1 离线训练与在线推演怎么衔接整个系统上线后我把它部署成了一套每天自动运行的流水线。每天凌晨2点定时任务启动拉取前一天的订单数据、天气数据重新构建特征增量训练模型。训练完成后自动评估验证集loss如果优于线上当前版本就更新模型文件。这个验证集决定是否上线的机制很重要能防止某天脏数据导致模型变差结果线上直接被替换成烂模型。在线推理服务加载模型文件后常驻内存对外提供两个接口一个是站点需求预测输入站点ID和预测起点时间返回未来1、3、6小时每个小时预测的借车量和还车量另一个是区域供需热力输入一个经纬度范围返回该范围所有站点的预测快照。第二个接口会先查Redis缓存缓存命中的话直接返回没命中才走模型推理实测单次推理加上序列化返回P95响应时间在180毫秒左右完全满足大屏实时刷新的需求。6.2 预测系统上线后的真实效果在试运行的一个月里我对比了上线前后的运营指标。核心改善体现在两个方面一是早高峰时段站点无车可借的时长占比下降了大约30%意味着用户到了站点却找不到车的糟糕体验少了很多二是晚高峰时段站点车满无法还车的工单量减少了约25%。调度车的工作方式也从接到报警再出发变成了根据预测结果提前调度单车平均调度里程反而下降了。这里要强调一个认知预测系统不是为了取代调度人员而是帮他们做决策。运营人员看到预测曲线之后仍然需要结合自己的经验判断是否要派车比如某个站点虽然预测缺车但距离调度场站太远、过去一趟成本太高这时候预测系统的作用就变成了帮助决策者权衡调度优先级。另外预测结果我也故意做了分位数输出而不是只给均值因为运营上需要知道最坏情况下可能缺多少车用这个数值来定安全库存更稳妥。6.3 预测误差如何转化成调度安全边界调度决策不能直接拿预测均值去用。我花了很长时间和运营同事对需求最后定了一套调度阈值逻辑当站点预测未来1小时的需求量减去当前库存量大于10辆车时判定为即将缺车大于20辆车时判定为严重缺车。阈值不是拍脑袋定的而是根据模型在验证集上的MAE约7.8来推导的。如果直接把阈值设成5辆系统会因为预测误差频繁触发调度工单调度车跑个不停实际上很多工单是误报设成10辆误报率大幅下降漏报率还在可接受范围内。这个预测误差驱动调度阈值的思路我觉得是所有预测类项目最值得借鉴的一点不要追求预测值绝对准确而是要理解预测的误差分布把误差变成决策的输入参数之一。做完这个项目我最大的体会是LSTM这种模型并没有多神秘真正决定项目成败的往往在数据清洗、特征设计和预测结果的工程化包装上。如果你也要做类似的预测系统我建议你前期多花点时间在理解业务上搞清楚运营人员拿到预测值之后到底要做什么决策这比调参更有价值。另外一个小的实战经验是把预测结果用可视化的方式暴露给运营人员观察他们在实际使用中提出的反馈能帮你快速发现很多离线评估发现不了的问题。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑