ARTICLE DETAIL

资讯详情

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

时空图神经网络交通预测实践:建图、建模与参数调优

时空图神经网络交通预测实践:建图、建模与参数调优 简介这是一份关于基于时空图神经网络的交通预测技术资料适合从事智慧交通、城市大脑相关研究与开发的算法工程师、数据建模人员阅读。内容以PDF学术文章呈现共1个文件压缩包大小仅1.48MB方便下载与快速查阅。资料从城市大脑的总体架构出发系统梳理了交通流数据接入、数据挖掘、预测干预与动态调整的完整链路重点阐释时空图神经网络如何利用路口间流量与速度的传播规律建立动态交通流预测模型。同时覆盖低延时、高并发计算平台及开源开放生态等工程落地要点能够帮助读者快速把握交通预测场景中的建模思路与系统设计方法。目前已有292人浏览学习适合作为入门综述或技术报告参考。1. 交通预测为什么绕不开时空图神经网络从一次晚高峰迟到说起晚高峰的路况预测从数据角度看其实是两个问题叠在一起空间上某个匝道的车流异常会在十几分钟后波及几公里外的出入口时间上过去半小时的拥堵趋势又会持续影响下一段时间。传统的时间序列模型擅长抓住时间趋势却很难把路网里“谁影响谁”的拓扑关系表达清楚。看到「基于时空图神经网络的交通预测.pdf」这个标题时我的第一反应是这条路走对了它把路网抽象成图用图卷积处理空间依赖用时间卷积或循环结构处理时间依赖正好对应交通预测的两个核心难点。这篇笔记的目标很直接就是带着你从数据格式、邻接矩阵、模型实现到参数调节和避坑检查把这条技术路线完整落地。适合正在做车辆调度、信号配时、路况预警的算法工程师和研究生照着文中步骤能很快搭出一个可训练的基线模型。2. 把路网变成图交通数据才能喂给时空图神经网络邻接矩阵与输入张量的构造不管选哪种时空图神经网络数据准备大概要占整个项目六成以上的时间。建模的第一步不是直接model STGNN()而是先把路网表示成一张图再把历史序列切成监督样本。这一节我按自己搭交通预测基线的顺序把这三步完整走一遍。2.1 路网抽象传感器是节点邻接矩阵是邻居关系交通检测数据通常来自卡口、地磁线圈或微波检测器每一个检测点就是一个图节点。设路网里有 N 个检测点邻接矩阵 A 的形状就是 N×N。A[i][j] 非零表示节点 i 和 j 之间存在空间关联图卷积可以在它们之间传递消息。常见的邻接矩阵构造方式包括按路网距离、按实际道路连通性、按行程时间相似度三种。实际项目中最常用的是距离加高斯核因为它不需要复杂地图匹配只要拿到坐标就能动手。# 构造带权重的路网邻接矩阵 def build_adjacency(coords, threshold_km1.5, sigma0.7): coords: (N, 2) 平面坐标单位公里 返回对称归一化后的邻接矩阵 AA[i][j] 表示节点 j 对 i 的空间影响权重 import numpy as np n coords.shape[0] dist np.zeros((n, n)) for i in range(n): for j in range(n): dist[i][j] np.linalg.norm(coords[i] - coords[j]) A np.exp(-(dist ** 2) / (sigma ** 2)) A[dist threshold_km] 0.0 # 超过物理距离阈值就不再视为邻居 np.fill_diagonal(A, 0.0) # 自环后面由图卷积自己补这里先去掉 row_sum A.sum(axis1, keepdimsTrue) d_inv_sqrt np.power(row_sum, -0.5) d_inv_sqrt[np.isinf(d_inv_sqrt)] 0.0 D_inv_sqrt np.diag(d_inv_sqrt[:, 0]) A_norm D_inv_sqrt A D_inv_sqrt return A_norm这段代码的逻辑是先算节点两两距离用高斯核把距离转成连续权重距离越近权重越大然后对超过阈值的位置强制置零保证图不是全连通的最后做对称归一化避免图卷积时节点度过大导致数值不稳定。两个核心参数是threshold_km和sigma。如果threshold_km过大整个图会变成稠密矩阵空间卷积退化成全连接层如果过小会出现孤立节点消息传不进去梯度也没法更新。我一般让threshold_km 2 * sigma这样距离超过两倍标准差时高斯核权重已经降到 exp(-4)剪掉基本不影响。城市路网 sigma 取 0.7 公里附近快速路可以放大到 1.5 公里。这里有一个容易踩的细节坐标必须转成投影后的平面坐标不能直接用经纬度算欧式距离。经纬度在不同纬度下的实际距离不一样拿它算出来的邻接矩阵会让人觉得模型表现飘忽不定其实问题出在图本身。2.2 序列到监督样本模型输入到底长什么形状邻接矩阵解决了空间关系下一步是把连续时间序列切成监督样本。假设数据采样间隔是 5 分钟历史窗口取 12 步也就是过去 1 小时预测未来 3 步也就是 15 分钟。用滑动窗口构造输入输出对即可。def sliding_window(data, n_his12, n_pred3): data: (T, N, F) 按时间排序的流量/速度矩阵 返回 X: (样本数, n_his, N, F), Y: (样本数, n_pred, N, F) X, Y [], [] for t in range(data.shape[0] - n_his - n_pred 1): X.append(data[t: t n_his]) Y.append(data[t n_his: t n_his n_pred]) return np.stack(X), np.stack(Y)从代码可以看到每个样本是连续切出来的时间片段X 代表过去 n_his 个时间步的路网状态Y 代表未来 n_pred 个时间步。step参数默认是 1也就是每个时间步都滑动一次样本数量大时可以把 step 调成 2 或 5减少训练集冗余。对交通数据我建议保留 step1因为交通状态变化快样本越多模型对突发情况的覆盖越好。这里要特别统一张量形状。大部分时空图神经网络代码习惯用(B, T, N, F)作为输入B 是批次T 是历史时间步N 是路网节点数F 是特征数。输出可以是(B, P, N, F)P 就是预测步数。如果特征包含流量、平均速度、占有率三个字段F 就是 3。模型内部会再做转置但前后端的数据接口最好保持这个四维约定后面排错会轻松很多。张量形状含义X(B, T, N, F)历史交通时空片段Y(B, P, N, F)未来 P 步的预测目标A(N, N)邻接矩阵的空间权重如果业务目标是预测未来 15 分钟平均速度P 就是 3如果要做信号灯动态配时P 可能需要 6 到 12。多步预测不是简单把 P 调大就行预测误差会随步数累积后面第 4 章会讲到怎么调整时间卷积感受野来缓解。2.3 训练集、验证集、测试集按时间切不能让未来数据渗进历史窗口交通数据是强时序的训练集、验证集、测试集不能随机打乱切分。如果模型在训练时见过测试时段附近的样本验证集和测试集分数都会虚高上线后才发现预测效果完全不是那么回事。这个坑我刚开始做时也踩过损失曲线漂亮得很换到新数据立刻翻车。def split_by_time(data, train_ratio0.7, val_ratio0.15): n data.shape[0] idx_train int(n * train_ratio) idx_val int(n * (train_ratio val_ratio)) train, val, test data[:idx_train], data[idx_train:idx_val], data[idx_val:] return train, val, test按时间顺序切分的做法是前 70% 做训练中间 15% 做验证最后 15% 做测试。验证集用来挑模型参数和早停测试集只做最终评估。另一个同样重要的问题是归一化参数只能从训练集计算。有些人习惯对全量数据做均值方差再切分这等于让未来数据参与了训练是一种不容易被察觉的泄漏。mean train.reshape(-1).mean() std train.reshape(-1).std() train_norm (train - mean) / std val_norm (val - mean) / std test_norm (test - mean) / std这段代码看起来简单但它是整个数据管线里最容易被忽略的一环。记住一个原则所有统计量包括均值、方差、min-max 的上下界都只能用训练集计算。验证集和测试集只能做变换不能参与统计量计算。还有一个细节如果后面切了滑动窗口要保证窗口数据不跨切分边界否则验证集里会混入训练时段的信息。常见做法是先切分原始时间序列再在每一段上单独调用sliding_window。3. 搭建一个最小的时空图卷积预测模型切比雪夫图卷积与门控时间卷积结合数据准备好了接下来选模型。标题里的“时空图神经网络”是一个大类最经典也最容易复现的路线是 STGCN 的思路用切比雪夫多项式图卷积处理空间用带门控的时间卷积处理时间两层时空块叠起来就能得到一个效果不错的基线。3.1 空间图卷积用切比雪夫多项式代替全谱分解图卷积最早从谱域定义需要做拉普拉斯矩阵的特征分解代价非常高。切比雪夫多项式逼近的好处是不用求特征向量只靠迭代计算拉普拉斯矩阵的幂次就能近似 K 阶邻域的信息传播。K 越大每个节点能感知到的空间范围越广但这并不是越大越好后面参数章节会专门讨论。下面是一段演示用的一阶切比雪夫空间卷积实现方便理解内部结构class ChebSpatialConv(nn.Module): def __init__(self, in_c, out_c, K3): super().__init__() self.K K self.W nn.Parameter(torch.randn(in_c, out_c, K 1)) self.bias nn.Parameter(torch.zeros(out_c)) def forward(self, x, L): # x: (B, T, N, in_c)L: 归一化拉普拉斯矩阵 (N, N) B, T, N, C x.shape x x.reshape(B * T, N, C) # 展成图节点特征 Xs [x] if self.K 1: Xs.append(2 * torch.matmul(L, x) - x) for k in range(2, self.K 1): Xs.append(2 * torch.matmul(L, Xs[k - 1]) - Xs[k - 2]) stack torch.stack(Xs, dim-1) # (B*T, N, C, K1) out torch.einsum(bnci,coi-bno, stack, self.W) out out self.bias return out.reshape(B, T, N, -1)这段代码里Xs依次保存切比雪夫多项式的每一项L是归一化拉普拉斯矩阵。第一项就是原始特征第二项由 L 乘原始特征得到后面每一项依靠递推公式。W的最后一个维度是 K1意思是每个多项式阶数都有自己的一组权重最后做加权求和。K3时每个节点可以看到三步之内的邻居信息是大多数交通预测项目的起步配置。实际生产代码里我更推荐直接用torch_geometric.nn.ChebConv它做了稀疏矩阵优化在大规模路网上的计算速度和显存占用都比稠密实现好很多。安装方式就是pip install torch_geometric然后from torch_geometric.nn import ChebConv。不过得提醒一句用现成库之前最好先动手写一遍上面这种简化实现否则你真的很难判断图卷积算出来的结果到底是不是自己想要的也很难在出问题时做单步调试。3.2 时间卷积门控线性单元让模型自己决定保留什么空间图卷积只能处理某一时刻的路网状态交通预测还需要跨时间步建模。用一个普通的一维卷积跨时间维滑动可以提取局部时间依赖但卷积本身是线性操作表达能力有限。STGCN 使用门控线性单元 GLU让模型学习在每一个时间步上应该保留多少信息一部分输出作为门控制另一部分信息的保留程度。class TemporalConv(nn.Module): def __init__(self, in_c, out_c, kernel_size3): super().__init__() self.conv nn.Conv2d(in_c, out_c * 2, kernel_size(kernel_size, 1), padding(kernel_size // 2, 0)) def forward(self, x): # x: (B, T, N, in_c) y x.permute(0, 3, 1, 2) # (B, in_c, T, N) y self.conv(y) # (B, 2*out_c, T, N) a, b torch.chunk(y, 2, dim1) gated a * torch.sigmoid(b) # GLU 门控 return gated.permute(0, 2, 3, 1) # (B, T, N, out_c)这里的关键是把卷积核设置成(kernel_size, 1)也就是只在时间维度上滑动空间维度保持不动。空间信息的聚合交给图卷积时间卷积只负责时间建模。卷积输出的通道数是 out_c 的两倍前一半作为普通线性输出 a后一半经过 sigmoid 变成门 b两者相乘就是门控输出。参数kernel_size3意味着每个时刻参考前后各一个时刻这是一个比较保险的起步值。如果数据采样频率是 5 分钟3 就是 15 分钟的时间关联范围。时间维度上的感受野特别重要模型要预测未来 3 步历史建模至少要能覆盖过去 12 步所以后面往往会叠加更多层或使用膨胀卷积。3.3 堆叠两层时空块跑通最小训练循环把时间卷积和空间卷积交替堆叠就得到一个 STGNN 的时空块。常见结构是时间卷积 → 空间图卷积 → 时间卷积中间可以加残差连接让梯度更快回流。为了演示我写一个可运行的最小模型它接受(B, T, N, F)输入输出未来 P 步预测。class STGNN(nn.Module): def __init__(self, in_c, hidden_c, out_c, K3, kernel_size3): super().__init__() self.t1 TemporalConv(in_c, hidden_c, kernel_size) self.sp1 ChebSpatialConv(hidden_c, hidden_c, K) self.t2 TemporalConv(hidden_c, hidden_c, kernel_size) self.out nn.Linear(hidden_c, out_c) def forward(self, x, L): # x: (B, T, N, in_c) h self.t1(x) # (B, T, N, hidden_c) h self.sp1(h, L) # (B, T, N, hidden_c) h self.t2(h) # (B, T, N, hidden_c) h h[:, -1, :, :] # 取最后一个历史时刻的隐状态 out self.out(h) # (B, N, out_c) return out训练时out_c要设置成n_pred * F比如预测 3 步、特征只有速度 1 个字段out_c 就是 3。最终输出在计算损失时再 reshape 成(B, P, N, F)和标签对得上。这里我取了最后一个时间步作为未来窗口的输入编码是为了让最小模型先跑通。更严谨的做法是输出多个时刻的预测并用独立的输出头但作为基线这个简化完全够用。model STGNN(in_cF, hidden_c64, out_cn_pred * F, K3, kernel_size3) optimizer torch.optim.Adam(model.parameters(), lr0.001) criterion torch.nn.L1Loss() # MAE 损失对异常值更稳 for epoch in range(100): model.train() epoch_loss 0.0 for X_batch, Y_batch in train_loader: optimizer.zero_grad() pred model(X_batch, L_tensor).view(-1, N, n_pred, F) loss criterion(pred, Y_batch) loss.backward() optimizer.step() epoch_loss loss.item() print(fepoch {epoch}, loss {epoch_loss / len(train_loader):.4f})这段训练循环的要点是损失函数先选了 L1 损失也就是 MAE。它比 MSE 对极端拥堵时刻的噪声更鲁棒不会因为某个检测器突然出现一个异常大值就把整个模型拉偏。学习率 0.001、隐藏通道 64 是常规起步值。如果 loss 长时间不下降优先检查邻接矩阵和拉普拉斯矩阵是否有问题而不是先调学习率。4. 时空图神经网络交通预测的四个必调参数邻接矩阵阈值、切比雪夫阶数与时间卷积核模型能跑起来只是第一步真正拉开效果差距的是参数选型。这一节我挑四个影响最大、也最容易被忽略的参数讲清楚它们各自的作用和调试方向。4.1 邻接矩阵阈值图太密退化成全连接太疏则节点收不到消息邻接矩阵的阈值直接决定图的稀疏程度。我见过有同学直接用所有节点两两连接也就是阈值设得无穷大结果图卷积的每个节点聚合了全图几百个节点的信息模型训练极慢预测结果还高度同质化。反过来阈值太小会导致部分路段检测点成为孤岛孤立节点的特征在整个训练过程中几乎不更新。实际调试时我一般先统计邻接矩阵的每个节点平均邻居数量这个值最好控制在 5 到 15 之间。对于快速路网节点本来稀疏阈值可以放大对于城市核心区道路密集阈值要相应缩小。还有一个做法是直接参考路网上最短路径连通性把距离近但不连通的节点关系剪掉但这种方案需要路网拓扑数据工程成本高初期不建议上。场景阈值经验值说明快速路检测点间距 2-3 公里3 公里以上空间依赖强需要跨立交交互城市主干道检测点密1-1.5 公里保留局部相邻关系即可混合路网先按 2 公里起步观察度分布后调整4.2 切比雪夫阶数 K不是越大越好过平滑会让所有节点预测趋同K 决定图卷积的感受野范围。K2 表示每个节点聚合一阶邻居和二阶邻居K3 就能覆盖三步邻域。交通拥堵的传播通常会跨越多个路口所以 K 太小时模型只能看到局部无法建模远端事件的影响。但 K 也不能无限增大因为多层图传播后节点特征会趋于一致这就是常说的过平滑最终结果是所有路段预测出几乎相同的数值。我的调参经验是从 K3 开始然后试 K4 或 K5对比验证集 MAE。如果 K 增大后验证集下降明显说明空间依赖范围确实被低估了如果验证集反而变差大概率是过平滑。注意 K 还要和模型层数配合堆叠两层时空块每层 K3相当于实际感受野叠加总空间范围远大于单层 K3。层数越多K 可以适当减小避免两层加起来直接全图连通。4.3 时间卷积核大小与膨胀感受野必须覆盖历史窗口时间卷积核kernel_size控制每个时间卷积看多长的时间窗口。默认 3 只覆盖前后各一个时间步如果只堆一层模型很难从过去 1 小时的数据里学到长周期节奏。常见做法是在第二层时间卷积中使用膨胀系数 2 或 4让不看中间步也能扩大感受野。感受野计算公式大约是R (kernel_size - 1) * dilation 1每层叠加后是累加关系。比如输入历史 12 步、kernel_size3、dilation1 的第一层感受野是 3第二层 dilation2 后感受野是 5两层累计还不够 12。这时候要么加层要么把 kernel_size 提到 5。我通常会把目标订到“感受野超过 n_his 的 80%”比如历史 12 步至少覆盖 10 步模型才有足够上下文。如果感受野不够你会发现预测曲线总是比真实曲线慢半拍尤其是早晚高峰的突变时刻。4.4 优化器、批大小与早停稳定训练比追求最低 loss 更重要时空图神经网络参数量不大但训练稳定性敏感。优化器用 Adam初始学习率 0.001 是安全起点。批大小建议 32 或 64过小会导致梯度抖动过大会让显存压力增大而且交通数据里本身就含噪声大批次反而容易把噪声学进去。学习率调度用 ReduceLROnPlateau当验证 loss 连续多个 epoch 不再下降时把学习率乘以 0.5。早停是必须加的。交通数据集一旦切了时间序列不同月份的数据分布差异很大训练太久模型会在验证集上过拟合。我一般设置 patience 为 10 到 15 个 epoch连续没提升就停止并保存验证集最优的模型文件作为最终模型。这里给一段早停的模板best_val_loss float(inf) patience 15 wait 0 for epoch in range(max_epochs): train_one_epoch() val_loss evaluate(val_loader) if val_loss best_val_loss: best_val_loss val_loss torch.save(model.state_dict(), best_model.pt) wait 0 else: wait 1 if wait patience: break这段代码的核心是只有验证集提升时才保存模型连续 patience 次没有提升就终止训练。不要用训练集 loss 来决定保存时机也不要等到所有 epoch 跑完再取最后一个模型。交通预测很吃泛化能力保存验证集最优模型相当于给自己留了一颗后悔药。5. 时空图神经网络交通预测避坑排查数据泄漏、孤立节点和训练翻车的四个注意点模型和参数都定了剩下的就是排坑。我把这几年在交通预测项目里遇到最多的问题整理成四类每一条都按现象、原因、解决的顺序写清楚。5.1 验证集 loss 从头到尾不下降第一件事检查邻接矩阵连通性现象模型训练时 loss 一开始就不动或者只在前几个 epoch 微降后完全卡住。换学习率、换优化器都没用。原因邻接矩阵里很多节点是孤立的图卷积的消息永远传不到这些节点更常见的情况是邻接矩阵构造错误比如经纬度没转平面坐标算出来的“邻居关系”根本不是真实的道路关系。还有一个可能是拉普拉斯矩阵里有 NaN 或 INF导致前向传播梯度全部坏掉。解决先打印邻接矩阵的非零个数计算每个节点的度看孤立节点比例是否超过 5%。再检查拉普拉斯矩阵是否能正常求和确认对角线为零。我习惯在训练前画一张节点度分布图一眼就能发现大部分节点度是否为 0。如果确认图有问题回头重新构造邻接矩阵而不是盲目调模型。注意邻接矩阵的构造错误是时空图神经网络训练失败的隐形杀手排查优先级要排在学习率和模型结构之前。5.2 随机切分数据集导致验证集虚高上线后效果明显缩水现象训练和验证时 loss 都很低模型看起来完美但部署到新一天的真实数据后MAE 比验证时差了 30% 以上。原因随机打乱切分让同一时段的样本同时出现在训练和验证里模型相当于提前见过测试答案。交通数据有很强的短时自相关性前后几分钟的数据高度相似随机切分等于变相数据泄漏。解决严格按时间顺序切分并确保滑动窗口不跨切分边界。对归一化统计量也要同样处理只用训练段计算均值和方差。想更严谨一点可以做多折滚动验证把数据按周切用前四周训练、第五周验证再滚动到下一周这样得到的评估结果更接近真实上线效果。5.3 MAPE 损失在凌晨流量归零时爆炸数值变成 NaN现象训练一会后 loss 突然变成 NaN或者验证集上 MAPE 出现异常巨大的值几乎不可用。原因MAPE 的分母是真实值交通流量在凌晨会变成 00 作为分母直接导致除零。即使加了很小的 epsilon流量很小的时候 MAPE 也会被放大到离谱的量级模型被迫去死磕凌晨的低流量数据反而忽略了早晚高峰最重要的预测。解决损失函数建议直接用 MAE 或 MSE不要上来就用 MAPE。如果业务指标必须要 MAPE可以在计算时只统计流量大于某个阈值的样本比如只算速度大于 0、流量大于 100 辆/小时的样本并且给分母加一个保护项。我自己一般把 MAE 当损失MAPE 当评估指标两者分开。5.4 切比雪夫阶数过大导致过平滑所有节点预测结果趋同现象模型预测出的路网速度场是一整片均匀的颜色不同路段的预测值几乎一样完全看不出拥堵分布在哪个区域。原因图卷积层数多或 K 值大信息在图上反复传播节点特征逐渐趋同。这是图神经网络特有的过平滑问题不是模型训练不够而是结构上把空间信息磨平了。解决把 K 调回 3同时减少时空块层数。如果还想增加空间依赖范围优先用膨胀时间卷积扩大时间感受野而不是一味加深图卷积层。另一个可行手段是在空间卷积之后加残差连接保留原始节点特征抑制过平滑。验证方法很简单取测试集某个时刻的模型输出统计所有路段预测值的方差如果方差接近 0基本可以断定过平滑。5.5 测试集只选容易时段评估结果好看但业务不可用现象模型在测试集上 MAE 很低但业务方反馈早晚高峰预测完全不能看。原因如果测试集落在平峰时段或者周末模型不需要处理剧烈拥堵变化评估结果自然偏乐观。交通预测业务真正关心的是早晚高峰和突发事件平峰时段的误差即使很小对调度决策也没意义。解决评估时把测试集按时间段拆开统计分别计算早高峰7-9 点、晚高峰17-19 点、平峰和夜间的 MAE。我一般在验证阶段就加入分时段指标模型调参时优先看晚高峰的误差。如果晚高峰误差是平峰的两倍以上说明模型对突发聚集车流的建模还不到位需要增大时间核或加入外部特征比如天气和事件。6. 让交通预测结果能上业务MAE、RMSE、MAPE 怎么选再加一个小技巧模型不是拿个 loss 低就能交差。业务侧需要知道预测到底准不准以及哪些场景可以信任、哪些场景要人工干预。我习惯用一套固定的验证集评估流程来回答这个问题。6.1 三个指标别混用每一种都只回答一个问题MAE 反映平均绝对偏差最直观也最稳定RMSE 对大误差更敏感适合需要避免严重误判的场景MAPE 是相对误差但前面说过流量越低越容易失真。做交通预测的评估报告时我会三个指标一起列但对外沟通时以 MAE 为主因为它和速度单位一致业务侧更容易理解。指标偏好适用场景MAE平均绝对误差汇报总体精度最常用RMSE大误差放大明显预警系统、事故检测MAPE相对误差高峰时段单独评估避开低流量6.2 在早晚高峰切片里看残差比盯着总指标更管用具体做法是把测试集预测值和真实值按时间切片计算分时段的 MAE再把早晚高峰的样本单独拿出来画一张预测残差图。如果残差在拥堵形成阶段系统性为负也就是模型低估拥堵说明模型对突发拥堵反应太慢如果残差在拥堵消散阶段持续为正说明模型还沿用旧的拥堵状态响应偏保守。这些判断只看总 MAE 是看不到的。我自己的习惯是每轮实验都会在固定周几跑一次“早晚高峰专项测试”对比模型在不同入口匝道上的预测偏差异常。上线的模型不一定是最低 MAE 的那一个但一定是在早晚高峰切片里误差分布最稳的那一个。这套流程帮我在不止一个项目里避免了“验证集好看、业务不可用”的尴尬。现在每次拿到一批新路网数据我都会先建图、跑基线、再分时段看残差一步步从能跑变成能信。希望这个思路对你也能有点帮助。本文还有配套的精品资源点击获取
返回列表