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

资讯详情

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

深度学习项目实战:基于Python的共享单车预测与调度方案解析

深度学习项目实战:基于Python的共享单车预测与调度方案解析 简介本资源是一套完整的共享单车时空需求预测与智能调度解决方案源代码面向计算机、人工智能、数据科学等相关专业的本科生及课程设计、毕业设计实践者聚焦城市短途出行场景下的动态供需建模与优化调度问题。压缩包共32个文件含22个Python脚本覆盖Geohash区域解码、POI辅助分区、多维度需求统计、BP神经网络预测、蚁群算法调度优化等核心模块、8个npy格式的预处理与训练数据集以及项目说明文档整体仅1.58MB轻量易部署。已有250人学习下载项目经本地全流程验证答辩平均分97.5分具备完整可运行性与教学示范性。读者可直接复现从数据预处理、深度学习建模到调度策略生成的全链路流程亦可基于模块化结构如独立的训练数据保存、误差计算、最终调度表生成等脚本开展二次开发与算法对比实验。 从期末作业到完整方案基于Python的共享单车预测与调度项目全拆解每年期末都会有一批朋友来问同一个问题深度学习大作业到底做什么题才能既不太难、又能拿高分、还能在答辩时讲清楚。我手里反复被问到的一个方向就是“深度学习期末作业-基于python开发的共享单车预测与调度解决方案源代码”。这个标题听起来很长拆开来看其实就两件事第一用深度学习模型预测某个共享单车站点未来一段时间内的租车量和还车量第二基于预测结果给出调度建议比如哪些站点该提前调车、调多少辆。这两个问题串起来就是一套完整的“预测-决策”链路恰好能把神经网络、时序建模、特征工程这些深度学习核心知识点全部覆盖而且场景非常贴近真实城市交通。这篇文章就把这个项目从数据到模型再到调度策略完整拆一遍适合正在做课程设计的同学也适合想快速上手时序预测实战的开发者。1. 项目整体设计与思路拆解1.1 共享单车预测的本质一个时空序列问题共享单车需求的波动从来不是随机的它背后有很强的规律早高峰时地铁站周边的单车需求暴增晚高峰时又反过来下雨天骑行量会明显下降周末景区和商圈附近的租还车模式和工作日完全不同。这些规律叠加在一起就让需求预测变成了一个典型的时空序列问题——每个站点在不同时刻的需求既跟过去一段时间自身的租还车趋势有关也跟天气、节假日、周边场所类型等外部因素有关。所以这个项目的第一步不是急着写模型而是先想清楚预测目标是什么是预测单个站点未来一小时的租车量还是预测整个片区的总需求量目标不同建模方式完全不同。期末作业里最常见的做法是选择一个片区或者几个热门站点按小时粒度统计租车量和还车量然后预测未来1到6小时的需求。这种设定既不会让数据量爆炸又足够展示深度学习的价值。为什么不用传统的时间序列方法呢ARIMA这类统计模型对线性关系、单变量序列处理得比较好但共享单车数据是非线性、强周期、多因素影响的ARIMA要手动做差分、定阶而且很难把天气、节假日这些外部特征自然融进去。机器学习模型如XGBoost虽然能处理多特征但对序列的前后依赖关系建模能力弱需要手动构造一堆滞后特征才能勉强模拟时间信息。深度学习的优势在于它可以端到端地学习“过去一段时间的序列模式 外部条件”到“未来需求”的映射不用人肉做那么多特征工程。1.2 方案选型为什么LSTM是期末作业的黄金起点谈到深度学习和时间序列LSTM几乎是绕不开的选择。我想先说一个观点期末作业不提倡一上来就上Transformer或者很复杂的多分支网络因为你需要在有限的时间内把整条链路跑通并讲清楚。LSTM的优势非常契合这个目标门控机制能处理长程依赖。共享单车需求有很强的日周期今天的早高峰和昨天的早高峰有相关性LSTM能记住这种跨时间的模式而普通RNN会遗忘。支持多变量输入。你可以把小时、星期几、是否节假日、温度、降水这些特征和租车量序列一起喂进去模型会自己学习它们和需求之间的关系。代码实现简洁。Keras和PyTorch都有现成接口几十行就能搭建一个可训练的模型调试门槛低。训练速度快。小时粒度的单车需求数据量通常不会太大CPU上跑几十个epoch也只要几分钟这对没有GPU的学生党非常友好。如果想让项目更有亮点可以试试CNN-LSTM的组合前面加一层Conv1D对序列做局部特征提取把局部模式比如连续几小时的上升趋势抽象成更高级的特征再交给LSTM建模长程依赖。相比纯LSTM这种结构在某些数据上效果会更好而且能给你答辩时多一个“为什么这样设计”的谈资。1.3 调度模块思路从预测结果到调度指令整个项目里最容易忽略但老师最爱问的部分就是调度模块的闭环。预测做得再漂亮如果没有落到调度决策上项目就缺了“解决方案”四个字。调度模块的核心可以简化成三步对每个站点预测未来1小时或2小时的租车量和还车量。计算净需求租车量 - 还车量。净需求为正说明这个站点车会变少可能要提前补充车辆净需求为负说明车会积压需要把车调走。把净需求与站点容量做比较。当某个站点的预测剩余车辆超过容量的某个阈值比如低于20%或高于90%时触发调度建议从富余站点调车到紧缺站点并给出数量建议和优先级。这个设计虽然比不上真实的车辆路径规划VRP那样运筹优化但作为期末作业完全够用而且逻辑非常清晰。重点是讲明白“预测驱动调度”的思路调度不是拍脑袋而是基于模型预测做的有条件决策。我在实操时一般会先做一个贪心版本的调度模拟再考虑多站点之间的配置。这种循序渐进的做法能让代码保持可读性也让答辩时的讲解有层次。2. 核心细节解析与实操要点2.1 数据获取与预处理质量决定上限这个项目最容易被低估的环节是数据预处理。很多同学拿着原始数据就开始训练模型结果出来一堆NaN或者预测曲线全是平的问题往往就出在数据没洗干净。共享单车领域有几类公开数据集可以拿来练手比如纽约的Citi Bike、伦敦的Santander Cycles国内部分城市也有开放平台。以Citi Bike为例原始数据字段大致包括骑行起始时间、结束时间、起始站点ID、结束站点ID、骑行时长等。要把它变成训练数据需要做下面几件事第一时间粒度聚合。原始数据是每一条骑行记录我们需要按小时统计每个站点的租车量和还车量得到“时间-站点-租车数-还车数”这种形式。聚合之后的数据量会小很多但信息密度更高更符合预测任务的需求。第二异常值清洗。骑行时长小于60秒的记录一般是开锁后发现车有问题或者误操作直接过滤掉。站点维修、系统维护导致的零值记录不能简单地当作真实需求为0建议用前后时段的中位数填充或者直接剔除再插值。第三时间戳对齐。天气数据的时间和骑行数据的时间一定要严格对齐到同一个时区。我见过有同学把UTC时间直接和本地时间join结果预测曲线整体偏移了两小时排查了很久才发现是时区问题。第四归一化。LSTM对输入尺度非常敏感。温度是0到30租车量可能是0到几百如果原样喂给模型数值大的特征会主导梯度更新。建议用MinMaxScaler或者StandardScaler做归一化。这里有一个关键原则scaler只能用训练集的数据来fit然后用同一个scaler去transform验证集和测试集。一旦你用全量数据fit就相当于把测试集的信息泄露到了训练阶段测试指标会虚高答辩时容易被问住。2.2 特征工程模型不直接吃时间戳LSTM不认识“2024-05-20 08:00:00”这种时间戳必须把它数值化。我推荐的黄金特征组合分三组时间特征小时0-23、星期几0-6、是否工作日0/1、是否节假日0/1。这些特征是共享单车需求的核心驱动因素。如果你用的是节假日数据建议额外加一个“节假日类型”的特征比如春节和中秋的出行模式完全不同。天气特征温度、体感温度、湿度、风速、降水量。这些字段通常可以从气象接口或历史天气网站拿到。要注意的是天气特征最好用“预测时间段的天气预报值”而不是“当前时刻的实测值”因为调度场景需要的是对未来时段的需求预测输入里不能包含未来才有的信息。历史需求特征过去24小时每个小时的租车量、还车量。这是LSTM最核心的输入——让模型看到最近一段时间的真实需求模式。另外我强烈建议加两个滞后特征过去1小时的租车量和过去24小时同时刻的租车量。前者捕捉即时惯性后者捕捉日周期这对预测效果有明显提升。实操的时候我会把上述特征做成一个二维表格每一行对应“某个站点、某个小时”然后按站点分组生成时间滑窗序列。这个“滑窗”操作是时序项目的核心后面会专门讲代码。2.3 站点粒度选择单站点还是分组建模几百个站点如果逐一训练模型工作量巨大而且很多站点数据稀疏训练出来的模型会过拟合或者完全预测不出来。我见过最典型的翻车现场就是有人拿所有站点数据拼成一个超大表去训练一个模型然后模型把所有站点的平均行为学了一遍结果每个站点都预测不准。比较务实的做法有三种选热门站点单独建模。把租还量Top10或Top20的站点挑出来每个站点单独训练一个模型。这些站点数据量大、规律明显模型效果好、可视化的曲线也好看调度演示起来非常有说服力。按区域聚合。把站点按地理片区比如市中心、商圈、居民区、景区分组组内租还量加总后再预测。这样数据平滑度好模型稳健适合展示整体趋势。站点聚类后分组建模。用K-Means对站点做聚类把行为模式相近的站点归为一组然后对每个组建模。这个方法比前两种更“高级”答辩时能体现你对数据挖掘的理解缺点是代码量稍大。我的建议是期末作业选第一种或第二种。理由很直白你需要在有限时间内把故事讲完整数据质量直接决定模型复杂度能不能压得住。3. 实操过程与核心环节实现3.1 环境准备与依赖清单先交代一下我实测下来最稳的环境组合Python 3.9或3.10TensorFlow 2.10以上Keras模式或者PyTorch 2.0以上配合pandas、numpy、scikit-learn、matplotlib。如果只是做小时粒度的预测数据量一般不会超过几十万行CPU训练完全够用不一定需要配CUDA环境。很多同学在Python环境上花掉大量时间其实大可不必。如果你要从零开始装环境我的建议是直接用Anaconda创建虚拟环境避免把系统Python搞乱conda create -n bike python3.9 conda activate bike pip install tensorflow pandas numpy scikit-learn matplotlib # 如果要用 PyTorch 版本就把 tensorflow 换成 torch pip install streamlit flask # 后面做展示界面会用到安装完成后跑一下python -c import tensorflow as tf; print(tf.__version__)确认环境正常。实测中常见的坑是Python 3.11和某些TensorFlow版本不兼容所以如果你不是特别需要新版特性用3.9或3.10最省心。3.2 数据序列化的关键代码将原始骑行记录转化为模型可用的序列数据是整个项目的核心一步。下面这段代码是我常用的处理流程读入原始数据按小时聚合生成滑动窗口样本。import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler # 读取原始骑行记录 df pd.read_csv(citibike_trips.csv, parse_dates[start_time]) # 按站点 小时聚合统计租车量和还车量 df[hour] df[start_time].dt.floor(h) rental df.groupby([start_station_id, hour]).size().rename(rental_cnt).reset_index() return_df df.groupby([end_station_id, hour]).size().rename(return_cnt).reset_index() # 合并租还车数据 data rental.merge(return_df, left_on[start_station_id, hour], right_on[end_station_id, hour], howouter) data[rental_cnt] data[rental_cnt].fillna(0) data[return_cnt] data[return_cnt].fillna(0) data data.sort_values([start_station_id, hour]).reset_index(dropTrue) # 生成时间特征 data[hour_of_day] data[hour].dt.hour data[day_of_week] data[hour].dt.dayofweek data[is_weekend] (data[day_of_week] 5).astype(int)接下来是关键函数把时间序列切成“过去seq_len小时 - 未来pred_len小时”的监督学习样本。def create_sequences(data, station_id, seq_len24, pred_len1): station_data data[data[start_station_id] station_id].copy() station_data station_data.sort_values(hour) feature_cols [rental_cnt, return_cnt, hour_of_day, day_of_week, is_weekend, temperature, precipitation] scaler MinMaxScaler() scaled scaler.fit_transform(station_data[feature_cols]) X, y [], [] for i in range(len(scaled) - seq_len - pred_len 1): X.append(scaled[i : i seq_len]) # 预测目标是未来 pred_len 小时的租车量注意这是归一化后的值 y.append(scaled[i seq_len : i seq_len pred_len, 0]) return np.array(X), np.array(y), scaler这里有一个非常重要的细节特征列的顺序。我在feature_cols里把rental_cnt放在第一列这样预测目标y取第0列就是租车量。如果你把特征顺序调换了后面取预测目标时也要跟着改否则模型训练出来完全对不上。另一个容易被忽略的点是切分训练集和测试集时一定要按时间顺序不能随机打乱。时序任务和图像分类不同图像可以随机打乱样本顺序时序数据一旦打乱模型就学到了“未来”的信息测试集评估就失去了意义。我习惯的做法是80%作为训练集10%作为验证集10%作为测试集按行号顺序切分。train_size int(len(X) * 0.8) val_size int(len(X) * 0.1) X_train, y_train X[:train_size], y[:train_size] X_val, y_val X[train_size:train_size val_size], y[train_size:train_size val_size] X_test, y_test X[train_size val_size:], y[train_size val_size:]3.3 模型构建与训练调参Keras搭建LSTM模型非常直接。我贴一个实测可跑的版本import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping model Sequential([ LSTM(units64, return_sequencesTrue, input_shape(X_train.shape[1], X_train.shape[2])), Dropout(0.2), LSTM(units32, return_sequencesFalse), Dropout(0.2), Dense(units16, activationrelu), Dense(units1) # 预测目标维度 ]) model.compile(optimizeradam, lossmse, metrics[mae]) model.summary() early_stop EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue) history model.fit( X_train, y_train, validation_data(X_val, y_val), epochs50, batch_size64, callbacks[early_stop], verbose1 )关于参数选择我踩过不少坑之后总结出这样一套经验units从64起步如果你的数据量不大几千到几万条样本64到128足够了不要一上来就256dropout设0.2到0.3主要防止过拟合损失函数用mse因为这是回归任务优化器用adam学习率用默认的0.001如果loss震荡再降到0.0005。还有一个细节是early stopping。期末作业阶段很多人喜欢把epochs设成100甚至200然后干等训练但其实模型在20多个epoch就可能收敛了后面只是在过拟合。EarlyStopping能自动在验证集loss不再下降时停止训练并恢复最优权重这是最省心的做法。如果要加CNN层代码改动也不大from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten model Sequential([ Conv1D(filters32, kernel_size3, activationrelu, input_shape(X_train.shape[1], X_train.shape[2])), MaxPooling1D(pool_size2), LSTM(units64, return_sequencesFalse), Dropout(0.2), Dense(units16, activationrelu), Dense(units1) ])Conv1D的作用是先用卷积核在时间维度上滑动提取连续的局部特征比如“连续三小时稳定上升”这种模式然后交给LSTM处理更长程的依赖。kernel_size3表示卷积核覆盖3个时间步pool_size2表示对时间维度做2倍下采样减少后续LSTM的计算量。3.4 评估指标与可视化训练完成后除了看loss曲线还要在测试集上算几个直观的指标。我用得最多的是RMSE、MAE和R²RMSE均方根误差对大误差比较敏感。如果有人预测得特别离谱RMSE会迅速变大适合衡量模型的稳定性。MAE平均绝对误差单位很直观就是“辆”。比如MAE3意味着平均每个小时预测值与真实值相差3辆车。R²决定系数衡量模型解释了数据多少方差越接近1越好。评估代码很简单from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score y_pred model.predict(X_test) # 注意要反归一化才能和真实值比较 y_pred_real scaler.inverse_transform(np.concatenate([y_pred, np.zeros((len(y_pred), len(columns)-1))], axis1))[:, 0] y_test_real scaler.inverse_transform(np.concatenate([y_test, np.zeros((len(y_test), len(columns)-1))], axis1))[:, 0] rmse np.sqrt(mean_squared_error(y_test_real, y_pred_real)) mae mean_absolute_error(y_test_real, y_pred_real) r2 r2_score(y_test_real, y_pred_real) print(fRMSE: {rmse:.2f}, MAE: {mae:.2f}, R2: {r2:.4f})这里也藏着一个常见的坑y是归一化后的数值评估时不能直接拿它和真实值比较。你需要把预测结果反归一化回去或者预测前就记录下租车量这一列的scaler。反归一化的代码我上面用了一个技巧把预测值和零矩阵拼起来用scaler.inverse_transform整体还原然后取第一列。这个方法有点hack但很好用。可视化方面我习惯画两张图真实值 vs 预测值的时间序列曲线能直观看到模型是否跟住了趋势。误差分布直方图能看出预测误差集中在哪个范围有没有系统性偏大或偏小。画曲线时建议只展示最近200到300小时的数据否则几十万个小时全部画出来曲线挤成一团什么都看不清。3.5 调度模拟核心逻辑实现调度模块我不建议做得太复杂能跑通一个合理的贪心逻辑就够了。下面是一段简化版的调度建议生成代码def generate_dispatch_suggestions(predictions, station_capacity, threshold0.2): predictions: dict, {station_id: {rental: 预测租车量, return: 预测还车量}} station_capacity: dict, {station_id: 站点最大容量} threshold: 低于容量20%触发补车高于容量80%触发调走 suggestions [] surplus_stations [] deficit_stations [] for station_id, pred in predictions.items(): net_demand pred[rental] - pred[return] # 正数表示车会减少 current_stock station_capacity[station_id] * 0.5 # 简化假设当前有50%的车 predicted_stock current_stock - net_demand capacity_ratio predicted_stock / station_capacity[station_id] if capacity_ratio threshold: deficit_stations.append((station_id, station_capacity[station_id] * threshold - predicted_stock)) elif capacity_ratio 1 - threshold: surplus_stations.append((station_id, predicted_stock - station_capacity[station_id] * (1 - threshold))) # 贪心匹配紧缺站点从富余站点调车 deficit_stations.sort(keylambda x: x[1], reverseTrue) # 最紧缺的优先 surplus_stations.sort(keylambda x: x[1], reverseTrue) # 最富余的优先 i j 0 while i len(deficit_stations) and j len(surplus_stations): station_id, need deficit_stations[i] surplus_id, surplus surplus_stations[j] transfer min(need, surplus) if transfer 0: suggestions.append({ from: surplus_id, to: station_id, bikes: int(transfer), reason: f站点{station_id}预计车辆低于容量{int(threshold*100)}% }) need - transfer surplus - transfer if need 0: i 1 if surplus 0: j 1 # 更新剩余数据 deficit_stations[i] (station_id, need) surplus_stations[j] (surplus_id, surplus) return suggestions注意这段代码是演示核心思路用的实际项目中站点当前车辆数最好用一个真实状态表而不是统统按50%容量估算。你在答辩时要把这个简化假设讲清楚老师会很认可这种“明确自己在何处做了简化”的表述。如果想让调度更真实可以引入站点间的距离矩阵限制调车距离在一个范围内比如只允许调度500米以内的相邻站点。不过这个增加了不少代码量期末作业时间有限我把这个作为扩展点写进README就可以。4. 常见问题与排查技巧实录4.1 数据泄露时序任务的头号杀手现象训练集loss很低验证集也很低但测试结果一看就是不对的或者测试集结果好得不真实。最常见的原因是预处理时用了全量数据的信息。我在带这个项目时见过三个典型的数据泄露场景归一化时用全量数据fit MinMaxScaler导致测试集的最大最小值混进了训练阶段。正确做法是只fit训练集再transform验证集和测试集。切分数据集之前没按时间排序函数内部做了随机打乱。时序数据必须保持时间顺序。特征构造时不小心用到了未来值比如用“未来1小时的实际租车量”作为滞后特征这等于直接把答案喂给了模型。排查方法很简单打印训练集、验证集、测试集的时间范围确认没有交叉检查scaler的fit操作是否只作用在训练集上把特征表打印出来肉眼扫一遍有没有未来信息。4.2 训练loss不收敛或震荡这个问题几乎每个做LSTM的人都会遇到。我大概归纳为四类原因第一学习率太大。adam默认的0.001通常没问题但如果你发现loss曲线上下乱跳可以降到0.0005或者0.0001试试。第二特征尺度不一致。温度、降水量、租车量函数值域差异巨大不归一化会导致梯度更新不稳定。检查一下训练数据里有没有极端值没被scaler处理掉。第三batch_size太小。batch_size16时梯度噪声大容易震荡我一般从64开始调数据量大可以设128。第四序列长度和网络容量不匹配。如果seq_len很长但LSTM units很少模型可能学不动。可以先缩短seq_len到12或者24验证模型结构没问题再加长。另外有个容易被忽视的细节是标准化后的数据如果还有缺失值NaN训练loss会直接变成NaN。使用np.isnan(X).sum()检查一下发现问题先处理数据再训练。4.3 预测结果滞后一拍这是时间序列预测里非常经典的现象预测曲线相比真实曲线整体“慢半拍”高峰和低谷都错后一段时间。原因是模型发现最简单省事的方式是“复制上一个时刻的值”特别是序列本身自相关性很强时模型会倾向于输出一个平滑的、近似于昨天或上一时刻的预测值。缓解方法有三条我强烈建议先试第二条减少输入窗口长度。如果过去24小时全喂进去模型更依赖近期值可以试试只用过去6小时或12小时。把预测目标从绝对值改成差分值也就是预测“t时刻与t-1时刻的差值”最后再累加还原。这样模型的学习目标从“预测水平”变成了“预测变化”通常能明显缓解滞后。增加周期特征并给模型更强的约束。比如把“过去24小时同时刻的值”作为强特征输入让模型有明确的参考点。4.4 LSTM输入维度报错“ValueError: Input 0 of layer lstm is incompatible”这个报错几乎每个初次接触Keras LSTM的人都会遇到。原因很简单LSTM期望的输入是三维数组(samples, timesteps, features)但你喂进去的可能是二维数组。排查方法print(X_train.shape) # 应该是 (样本数, 序列长度, 特征数) # 例如 (5000, 24, 7)如果shape是(5000, 24)说明特征维度丢了需要reshapeX_train X_train.reshape((X_train.shape[0], X_train.shape[1], 1))很多同学在构造序列时忘了把每个时间步的多特征保留下来压成了一维。记住create_sequences里的X每个元素应该是一个(seq_len, feature_dim)的二维数组整个X才是三维。4.5 数据中的NaN和Inf问题天气数据经常有缺失值站点数据也会因为车辆维护出现空值。NaN直接喂给模型loss很快就变NaN。Inf则更隐蔽比如风速字段某个值全是0做除法标准化时产生无穷大。我的处理习惯是数值型缺失值先用前后时间的插值pandas的interpolate()填充实在找不到的再用列中位数。填充完之后一定要检查assert np.isfinite(data.values).all(), 数据中存在NaN或Inf这个assert能在训练前把问题暴露出来避免你花一晚上盯着一个NaN训练结果发呆。5. 从期末作业到可展示项目再加两个亮点5.1 用Streamlit做个可视化demo如果代码仓库里只有训练脚本和预测脚本这个项目看起来就是“能跑”而已。但如果你能打开一个网页左侧选站点、选日期右边立刻显示预测曲线和调度建议那整个项目的完成度会提升一个档次。我用Streamlit做过一个demo代码量很小import streamlit as st import pandas as pd import matplotlib.pyplot as plt st.title(共享单车需求预测与调度建议) station st.selectbox(选择站点, station_list) date st.date_input(选择日期) hour st.slider(预测起始小时, 0, 23, 8) # 这里调用你的预测函数 pred_df predict_station(station, date, hour) fig, ax plt.subplots() ax.plot(pred_df[hour], pred_df[predicted_rental], label预测租车量) ax.plot(pred_df[hour], pred_df[predicted_return], label预测还车量) ax.legend() st.pyplot(fig) st.subheader(调度建议) for s in generate_dispatch_suggestions(pred_df): st.write(f从站点 {s[from]} 调 {s[bikes]} 辆车到站点 {s[to]})Streamlit的好处是纯Python写界面不用学HTML和JavaScript。启动时运行streamlit run app.py浏览器自动打开整个过程非常顺畅。这个脚本对答辩演示来说效果比我见过的大部分PPT好得多。5.2 用SQLite和Flask提供查询接口如果你还想再工程化一点可以把每次预测的结果存到SQLite然后用Flask提供一个简单的REST接口比如from flask import Flask, jsonify, request import sqlite3 app Flask(__name__) app.route(/api/prediction, methods[GET]) def get_prediction(): station_id request.args.get(station_id) date request.args.get(date) conn sqlite3.connect(predictions.db) cur conn.cursor() cur.execute( SELECT hour, rental, return FROM predictions WHERE station_id? AND date?, (station_id, date) ) rows cur.fetchall() conn.close() return jsonify([{hour: r[0], rental: r[1], return: r[2]} for r in rows]) if __name__ __main__: app.run(debugTrue)这样项目就从一个运维视角变成了服务视角预测结果可以被人或者其他系统调用。虽然作为期末作业稍显超纲但如果你对数据库和Web有基础这会是一个非常加分的扩展点。我在实际带学生做这类项目时最大的体会是最后拿高分的作品往往不是模型最复杂的而是能清楚讲明白“为什么这么设计”的。预测部分用LSTM也好、GRU也好都是工具箱里的选择关键是把数据处理流程、评估指标含义、调度触发逻辑讲透。如果你能在答辩里把“为什么不能随机切分数据”和“为什么选择MAE而不是MSE”这两个问题答出来就已经超过90%的同组项目了。最后再分享一个小技巧提交代码前把模型的summary、loss曲线、预测误差分布图都导出成图片放到代码仓库的docs目录里再用pip freeze requirements.txt锁定依赖版本。这样无论老师还是未来的你自己来复现都不会因为环境不一致而翻车。共享单车预测与调度这个选题往深了做可以接实时数据流、做多目标优化作为期末作业把“预测-调度”这条链路跑通并讲透就已经是一份很扎实的成果了。本文还有配套的精品资源点击获取
返回列表