简介这是一份基于深度学习的路网Transformer模型设计源码以毕业设计形式呈现适合智能交通方向研究者、深度学习初学者及准备相关课题的在校学生。模型聚焦路网数据内在结构与动态特征利用Transformer架构完成高效分析与状态识别可应用于拥堵预测、流量分析、出行时间估计等智能交通场景。资源包共26个文件包含3个Python源代码文件覆盖数据预处理、模型定义、训练流程与结果输出21张PNG可视化图片展示损失函数变化、准确率提升等关键过程另有txt说明与Markdown文档梳理项目结构、参数配置及使用指南整体约15.1MB轻量易获取目录清晰便于按需查阅。已有316人学习说明具备一定参考价值。通过源码与图片读者能理解路网数据预处理思路掌握Transformer路网建模的落地方案并借鉴训练评估与可视化方法有助于快速启动自己的毕业设计或科研探索。1. 路网Transformer是什么图状路网和序列模型之间差了一整套编码设计路网Transformer不是一个固定的开源模型名而是一类把路段级交通数据组织成序列、再用多头注意力建模时空依赖的方案。交通数据和图像、文本最大的差别在于路网没有规则的网格结构路口之间距离长短不一、上下游关系有向且带方向、红绿灯相位会让相邻路段出现完全不同的速度波动。你带着Transformer来做这件事核心目标是把过去一小时、几百条路段的流量和速度一次性预测出未来30到60分钟的路况变化。这套方案适合手里已有路段级历史数据、试过LSTM但精度卡住、又不想一头扎进图神经网络调参泥潭的工程师。下面这套最小源码骨架可以让你在一周数据上跑通训练闭环并看清路网数据喂给Transformer时真正的坑在哪里。2. 把路网喂给Transformer路段序列化、滑动窗口与三种空间编码2.1 路网不是序列先决定按什么顺序排路段Transformer本身是一个序列模型token的位置由位置编码表达。路网是个图把图塞进序列模型的第一步就是决定路段以什么顺序出现。常见的路子有三种按经纬度做空间填充曲线排序、用图游走算法生成一个访问顺序、以及不换顺序直接给每段路一个可学习的图编码。我一般选第三种原因很简单——前两种排序方式虽然保住了局部相邻性但换一个起点、换一张路网排序就完全变了模型学到的位置信息无法迁移测试时一换数据就崩。实践中更稳妥的做法是把路段ID顺序固定下来写入配置文件然后让模型不依赖绝对位置而是依赖两两之间的空间关系。也就是把“第17号路段在第3号路段旁边”这种绝对位置信息换成“这两条路相距800米、中间隔着2个路口”这种相对关系。相对关系才是路网预测里真正稳定的知识也是后面空间偏置层的理论基础。2.2 最小可跑的数据预处理代码特征矩阵、邻接矩阵和滑动窗口先把数据整理成模型能吃的形状。假设你有一张速度表行是时间切片5分钟一个点列是路段ID。路段级特征通常包括速度、流量、占有率为控制篇幅下面代码用速度做示例特征通道数可以自行扩展。# dataset.py import numpy as np import pandas as pd from collections import deque # speed: DataFrame, index时间步, columnsroad_id # 先补缺失值路网数据常见跳点用前后向填充 speed speed.fillna(methodffill).fillna(methodbfill).values T, N speed.shape # T 个时间片, N 个路段 F 1 # 特征通道数后续可扩展流量/占有率 feat speed.reshape(T, N, F).astype(np.float32) def make_samples(feat, seq_len12, horizon6, step6): 滑窗采样。step6 表示相邻两个样本的时间起点相隔半小时 避免训练集和验证集共用过多重叠窗口。 T, N, F feat.shape X, Y [], [] for t in range(0, T - seq_len - horizon 1, step): x feat[t : t seq_len] # [seq_len, N, F] y feat[t seq_len : t seq_len horizon, :, 0] # 只预测速度 X.append(x) Y.append(y.transpose(1, 0)) # [N, horizon] return np.stack(X), np.stack(Y) # X: [样本数, 12, N, 1]这段代码的关键是参数step。很多初学者用step1滑窗结果训练集和验证集里大量样本只有5分钟之差验证指标虚高得离谱。step6的意思是每隔6个时间片采样一次也就是每半小时取一个样本减少了样本间的信息重叠测试结果才接近真实水平。seq_len12对应1小时历史horizon6预测未来半小时。接下来算邻接矩阵和拓扑跳数。路网拓扑可以从地图数据里拿一个最粗粒度的做法是按路口经纬度距离生成邻接关系。# 计算物理距离邻接矩阵再算拓扑跳数 from scipy.spatial.distance import cdist coords np.array(road_coords, dtypenp.float32) # [[lng, lat], ...] dist cdist(coords, coords) # [N, N] adj (dist 500).astype(np.float32) # 500米内算邻居 adj np.eye(N) # 自环保留让注意力能看到自己 def compute_hops(adj, max_hop8): N adj.shape[0] hops np.full((N, N), max_hop, dtypenp.int64) for i in range(N): queue deque([(i, 0)]) visited {i} while queue: node, hop queue.popleft() hops[i, node] hop if hop max_hop - 1: continue for j in np.where(adj[node] 0)[0]: if j not in visited: visited.add(j) queue.append((j, hop 1)) return hops邻接矩阵的物理距离阈值是玄学没有绝对正确的值。我一般先看路网的路段平均长度如果平均每段路300米阈值500米可能让大量相邻路段成为一阶邻居如果是快速路平均路段长度上千米阈值要放宽到1500米。另一个更稳的做法是取KNN每个路段固定连最近的8个邻居避免有的路段邻居稀疏、有的路段邻居稠密。compute_hops的max_hop同时是后续注意力偏置的截断值超过8跳的信息在这个模型里默认不可用注意力分数会被压到极小。这一步值得多花时间验证把邻接矩阵画出来看是否有孤立路段或度数异常的路段。2.3 三种空间编码距离、角度与拓扑跳数路段两两之间可以提取三类先验物理距离、道路方向夹角、拓扑跳数。物理距离越近相关性通常越强方向夹角决定两条路是同向并行还是十字交叉拓扑跳数则表达“能不能直接到达”。三种信息对路网预测都有用但标准Transformer是完全不知道这些的需要显式编码进去。# 在数据集构建阶段预计算角度特征 # 假设每条路段有一个主方向向量 [dx, dy]可从起终点坐标算 def compute_angle_feature(seg_vectors): N len(seg_vectors) angle_feat np.zeros((N, N, 2), dtypenp.float32) for i in range(N): vi seg_vectors[i] / (np.linalg.norm(seg_vectors[i]) 1e-6) for j in range(N): vj seg_vectors[j] / (np.linalg.norm(seg_vectors[j]) 1e-6) angle_feat[i, j, 0] np.dot(vi, vj) # cos 夹角 angle_feat[i, j, 1] np.cross(vi, vj) # sin 夹角带方向 return angle_featcos表达两条路是否平行sin带上方向性区分“同向并行”和“对向交叉”。这个特征很多论文里直接用sin/cos再过一个线性层也有人只保留cos但实践下来带上sin对高架上下口这种方向敏感场景帮助更大。物理距离建议做归一化不要直接用米数因为不同城市的路网尺度差异很大。2.4 时间特征不能少日周期、周周期怎么进入embedding交通流有极强的日周期和周周期早高峰、晚高峰、周末和节假日完全是不同模式。如果只把“第几个时间片”喂给模型它会傻傻地以为第100个时间片和第700个时间片的规律相同实际一个是早上8点、一个是晚上8点状态天差地别。所以输入特征里必须带上时间描述。常见做法是把每个时间片对应的“一天内分钟数”和“星期几”编码成连续特征拼在路段特征后面。比如5分钟粒度下一天有288个时间片用minute_of_day / 288归一化星期几用one-hot的7维向量。这些特征和速度、流量一起过输入线性层模型自己能学会在不同时段切换注意力模式。如果你有节假日表再加一个0/1的节假日标志位对预测长假的流量有奇效。时间特征看似简单却是最容易让模型提升1到2个点RMSE的部分属于性价比最高的特征工程。3. 模型主体设计带空间偏置的路网Transformer编码器3.1 为什么标准Transformer会在路网上翻车位置编码和形状噪声拿标准的nn.TransformerEncoder直接跑路网数据第一轮实验往往能收敛但指标到不了满意水平。问题出在位置编码上。默认的sinusoid位置编码假设序列有连续顺序第3个token和第4个token在位置上相邻。而路网的第3个路段和第4个路段可能隔了十条街绝对位置完全没有物理意义。可学习位置编码稍微好一点但依然要求路网顺序固定换个城市就作废。更隐蔽的问题是“形状噪声”。路网里某个交叉口的5条路段经纬度差异可能只有几十米在距离阈值下是一阶邻居同一批数据换一个坐标系或者加了扰动邻接关系可能就变了。模型如果把这种脆弱的邻接关系当作硬约束去学习测试时遇到真实噪声就会翻车。后面讲的空间偏置本质上是把“距离、角度、跳数”做成软先验加到注意力分数上让模型自己平衡“该信拓扑”还是“该信速度序列”而不是靠一个写死的邻接矩阵决定一切。3.2 核心代码Spatial Transformer Layer与它相比标准层的改动我们的做法是重写一个带空间偏置的Transformer编码层。相比标准层唯一改动是在注意力分数上加一个可学习的偏置项这个偏置由距离、角度、跳数通过一个小MLP生成。# model.py import torch import torch.nn as nn import torch.nn.functional as F import math class SpatialBias(nn.Module): 把距离/角度/拓扑跳数变成注意力偏置 def __init__(self, d_model64, max_hop8): super().__init__() self.dist_fc nn.Linear(1, 16) self.hop_embed nn.Embedding(max_hop, 16) self.angle_fc nn.Linear(2, 16) self.out nn.Sequential( nn.Linear(48, d_model // 2), nn.ReLU(), nn.Linear(d_model // 2, 1), ) def forward(self, dist, hop, angle): if dist.dim() 3: dist dist.unsqueeze(-1) h self.dist_fc(dist) self.hop_embed(hop) self.angle_fc(angle) return self.out(h) # [B, N, N, 1] class RoadTransformerLayer(nn.Module): 带空间偏置的多头注意力层 def __init__(self, d_model64, nhead4, dim_feedforward256, dropout0.1): super().__init__() self.head_dim d_model // nhead self.q nn.Linear(d_model, d_model) self.k nn.Linear(d_model, d_model) self.v nn.Linear(d_model, d_model) self.attn_drop nn.Dropout(dropout) self.ff nn.Sequential( nn.Linear(d_model, dim_feedforward), nn.ReLU(), nn.Dropout(dropout), nn.Linear(dim_feedforward, d_model), ) self.norm1 nn.LayerNorm(d_model) self.norm2 nn.LayerNorm(d_model) def forward(self, x, spatial_bias): B, N, D x.shape q self.q(x).reshape(B, N, -1, self.head_dim).permute(0, 2, 1, 3) k self.k(x).reshape(B, N, -1, self.head_dim).permute(0, 2, 1, 3) v self.v(x).reshape(B, N, -1, self.head_dim).permute(0, 2, 1, 3) scores torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim) bias spatial_bias.permute(0, 3, 1, 2) # [B, 1, N, N] scores scores bias attn torch.softmax(scores, dim-1) attn self.attn_drop(attn) out torch.matmul(attn, v) out out.permute(0, 2, 1, 3).reshape(B, N, D) x self.norm1(x self.attn_drop(out)) x self.norm2(x self.ff(x)) return x这里有一个关键设计空间偏置被加在注意力分数上而不是直接加到输入特征上。如果直接加在特征上每个路段只是“知道”自己和谁近但注意力仍然不知道“该给谁多少权重”。加在分数上则不同模型会在打分阶段直接抑制8跳以外的路段某种意义上是一个软性的图卷积先验但比GCN灵活得多。为什么要手写QKV而不是用nn.MultiheadAttention因为标准接口的attn_mask在不同PyTorch版本里对形状的要求不一致有的要[B*num_heads, L, S]有的又要[L, S]路网场景下还要在score上加可学习偏置直接魔改内部实现更省心。手写的注意力层少了一些优化但在N不到2000的情况下性能差距可以忽略。3.3 整体模型先时间建模再空间建模完整的路网Transformer骨架我一般按“先时间、后空间”组织先让每个路段自己在时间维上做Transformer把12个时间步压缩成一个包含历史信息的向量再做路段间的空间注意力。这个顺序的理由是先时间建模每个token只代表一条路段语义纯净如果先空间建模每个token是某个时刻的全路网快照时间维上还要再建模模型会变得又重又难调。class RoadNetTransformer(nn.Module): def __init__(self, in_features1, seq_len12, d_model64, nhead4, num_layers3, horizon6, dropout0.1): super().__init__() self.input_fc nn.Linear(in_features, d_model) self.temporal_pos nn.Parameter( torch.randn(1, seq_len, 1, d_model) * 0.02 ) self.temporal_layer nn.TransformerEncoderLayer( d_modeld_model, nheadnhead, dim_feedforwardd_model * 4, dropoutdropout, batch_firstTrue ) self.spatial_layers nn.ModuleList([ RoadTransformerLayer(d_model, nhead, d_model * 4, dropout) for _ in range(num_layers) ]) self.head nn.Sequential( nn.Linear(d_model, d_model), nn.ReLU(), nn.Dropout(dropout), nn.Linear(d_model, horizon), ) def forward(self, x, dist, hop, angle): # x: [B, T, N, F] B, T, N, F x.shape x self.input_fc(x) self.temporal_pos # [B, T, N, D] x x.permute(0, 2, 1, 3).reshape(B * N, T, -1) # 时间维 Transformer x self.temporal_layer(x) x x[:, -1, :].reshape(B, N, -1) # 取最后时间步 bias SpatialBias()(dist, hop, angle) # [B, N, N, 1] for layer in self.spatial_layers: x layer(x, bias) return self.head(x) # [B, N, horizon]需要指出的是SpatialBias()在完整代码里应该作为__init__中的成员避免每次forward重新实例化。这里拆开是为了让代码结构更直观。输入x的形状是[B, T, N, F]T是历史时间步数N是路段数F是特征维度。输出[B, N, horizon]直接给每个路段预测未来6个时间片的速度。如果你把dist、hop、angle视为静态数据它们不依赖batch和time。实际写代码时应该在建Dataset时就预计算好每次训练迭代直接从内存里取而不是在模型forward里现算。基础路网的这三个矩阵在N800时大约是800*800*3个浮点数几十MB完全可以驻留内存。3.4 关键参数怎么设d_model、n_heads、层数与dropout的落地区间下面这组参数我在N200到2000的路网上反复试过属于比较稳的起点d_model64nhead4num_layers3dim_feedforward256dropout0.2seq_len12horizon6。N超过2000时d_model提到128nhead提到8但层数尽量不要超过4。路网数据本身噪声大层数一多模型先开始记住那些特定路段的尖峰泛化反而变差。dropout值得单独说。交通数据的噪声来自检测器误差和偶发事件0.1的dropout明显不够我见过很多人用0.1训到50轮后验证loss开始抬头。dropout0.2到0.3会稳很多代价是训练收敛变慢。dim_feedforward取d_model的4倍即可再大收益不大显存涨得很快。3.5 预测头怎么接多步直出比自回归更省心预测头有三种接法单步循环解码、多步直出、以及自回归加教师强制。对路网这种场景我强烈建议多步直出。自回归在测试时是一步错步步错15分钟预测还行到45分钟后误差会指数放大。多步直出的做法就是上面代码里的nn.Linear(d_model, horizon)一次性输出未来6个时间步。虽然理论上误差也会累积但累积的是输出头自身的误差而不是模型在推理时把错误预测又当成输入稳定性好很多。如果你要同时预测流量和速度把head的输出维度改成horizon * 2再在loss里分别加权即可。多任务学习的指标通常比单独预测速度更稳因为流量和速度的异常模式可以互相约束。4. 训练与评估折腾人的细节loss函数、数据归一化与指标对齐4.1 最小训练脚本从数据加载到反向传播训练代码不长但有几个点值得提前说明。路网数据里不同路段的数值范围差异很大城市支路速度常年20km/h快速路速度常年80km/h如果直接用MSE模型会把绝大部分容量花在拟合快速路上支路预测几乎放弃。一个有效的做法是按类别给路段加权快速路权重大一点但不等于让loss被它主导。# train.py import torch from torch.utils.data import DataLoader, TensorDataset def train_epoch(model, loader, optimizer, device): model.train() total_loss 0.0 for x, dist, hop, angle, y in loader: x x.to(device) dist, hop, angle dist.to(device), hop.to(device), angle.to(device) y y.to(device) pred model(x, dist, hop, angle) # [B, N, horizon] loss torch.nn.functional.huber_loss( pred, y, delta1.0, reductionmean ) optimizer.zero_grad() loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 5.0) optimizer.step() total_loss loss.item() * x.size(0) return total_loss / len(loader.dataset)huber_loss比MSE强的地方在于对离群点不敏感。路网数据里总会有检测器故障产生的极端值比如速度瞬间从60跳到0MSE会为这一个点付出巨大梯度Huber在delta1.0之后把梯度限制住了。clip_grad_norm_这行是必须的尤其当num_layers开到3以上时梯度范数动辄几十不裁剪的话前几轮loss就可能发散。优化器我默认用Adam初始学习率1e-3配CosineAnnealingLR。路网数据的时间相关性很强warmup阶段不建议太长前5个epoch之内让学习率从1e-4爬回1e-3就够。温度太高会直接跳过好的局部最优。4.2 归一化与反归一化先fit训练集预测值还原后再算指标数据归一化是路网预测最容易出问题的地方而且问题出得很隐蔽。错误做法是把整张速度表丢进StandardScaler再划分训练集和测试集。测试集的信息已经通过均值和方差混进模型线上效果必然打折。正确做法是只用训练集的时间范围去fit scaler然后应用到验证集和测试集。# 正确写法 from sklearn.preprocessing import StandardScaler scaler StandardScaler() scaler.fit(speed[train_start:train_end]) # 只用训练期数据 # 预测完成后算指标前必须反归一化 pred_inv scaler.inverse_transform(pred.cpu().numpy().reshape(-1, 1)).reshape(B, N, H) y_inv scaler.inverse_transform(y.cpu().numpy().reshape(-1, 1)).reshape(B, N, H)反归一化这个步骤很多人会漏。如果不还原RMSE数值会显得很小但业务方看不懂也无法和基线比较。在评估脚本里所有指标都必须在原始速度单位上计算。另外如果按路段分别做归一化每个路段有自己的最大值那么某个路段预测值永远不可能超过该路段历史最大值这对拥堵场景是硬性限制我一般只做全局scaler不做路段级scaler。4.3 评估指标RMSE、MAPE之外为什么要加一个拥堵阈值命中率交通业务方最关心的不是RMSE降到多少而是“你说堵到底堵没堵”。所以除了RMSE和MAPE我每次都会加一个拥堵命中率预测速度低于20km/h且真实速度也低于20km/h的样本占比。def congestion_hit_rate(y_true, y_pred, threshold20.0): pred_cong y_pred threshold true_cong y_true threshold return (pred_cong true_cong).sum() / max(true_cong.sum(), 1)这个指标的意义在于模型经常会为了拉低整体RMSE而牺牲极端拥堵场景把拥堵路段预测成缓行。RMSE好看但业务方实际使用时会觉得模型“不靠谱”。拥堵命中率低于50%的模型哪怕RMSE再低也不建议上线。这个指标也适合用来调loss权重如果命中率上不去可以试试在loss里对拥堵样本加权两倍。4.4 早停与EMA防止模型在峰值路段上过拟合路网数据里总有那么几条路段工作日和周末的曲线差异极大。模型训练后期loss曲线会变得很平但验证指标在缓慢恶化典型的过拟合信号。我习惯在每个epoch结束算验证loss连续8个epoch不下降就停掉然后加载验证loss最低的checkpoint。这一段看代码很简单但比多调几个超参更管用。另外一个推荐的做法是EMA指数移动平均保持一份模型权重的滑动平均每个epoch把当前模型权重按0.999的比例并进平均权重。推理时用平均权重而不是当前权重。这个做法的效果基本等于免费把模型ensemble了5次尤其对N比较大、噪声比较高的路网场景能稳定带来1%左右的指标提升。5. 路网Transformer的常见问题与避坑清单5条血泪经验5.1 坑1路段顺序一换指标崩了现象同一份代码、同一套超参把路段ID的顺序打乱后重新训练验证RMSE直接变差5%以上。排错很久才发现原来不是数据变了只是行列顺序变了。原因模型里如果用了可学习的位置编码或默认的sinusoid编码就会记住“第i个token在第i个位置”这种绝对信息。路网的路段顺序是人为指定的跟物理结构无关模型学到的顺序规律完全没有迁移性。解决要么固定路段顺序并在配置文件中记录下来要么干脆不用绝对位置编码。更彻底的做法是像前面代码那样把空间偏置作为唯一的位置信息来源模型只依赖两两之间的距离和跳数。换路网时空间偏置会自动重新学习不用改代码。5.2 坑2MAE很低但预测曲线全是历史均值现象验证集MAPE看起来挺不错把预测曲线画出来发现基本是一条平线模型把大部分路段预测成了历史平均速度。RMSE和“用昨天同一时刻做预测”的基线算法差不多但业务方一眼就看出来预测没价值。原因loss被畅通时段的样本主导。畅通时段速度波动小模型只要预测一个中间值就能稳稳拿到低loss而拥堵尖峰样本占比小模型没有动力去学。解决把目标从“预测速度”改成“预测速度的差分”或者对拥堵样本在loss里加权。最直接的检测方法是看pred.std()和y.std()如果前者小于后者的60%模型大概率在偷懒。这也解释了为什么拥堵命中率这个指标不能省。5.3 坑3前15分钟预测准45分钟后误差爆炸现象多步直出模型在前两个时间步误差很小到第4个时间步之后误差快速放大甚至比直接用历史均值更差。原因输出头对远端时间步的监督信号不足。模型把大部分容量用在了拟合近端时间步远端步的梯度被近端步淹没。特别是在数据噪声大的路段远端预测本质上更难模型干脆选择输出一个保守值。解决对远端时间步的loss加权。比如horizon6时近端3步权重0.4远端3步权重0.6或者直接用分段的Huber loss近端用小delta远端用大delta。还可以试试两段式输出先预测未来15分钟再用这个结果作为额外特征预测后30分钟比一步直出6个步要稳。5.4 坑4显存爆掉批量训练直接OOM现象N1500个路段seq_len12batch_size64一跑起来就OOM。有人把batch_size降到8但又太慢。原因路网Transformer的空间注意力是N×N复杂度1500个token的注意力矩阵每个head一份4个头就是4×1500×1500个浮点数batch一放大显存立刻吃紧。解决三个手段叠加使用。第一注意力只允许hop2的路段互相交互把hop2的位置在score上加一个很大的负数掩码相当于稀疏注意力显存直接降到原来的零头第二用梯度累积代替一次性大batch每16个样本算一次梯度但每64个样本才更新一次参数第三开混合精度AMP在A100这类卡上能把显存再省一半。顺序上先做稀疏注意力效果最明显。5.5 坑5测试集比训练集指标还好模型像偷看了答案现象验证集和测试集的MAPE比训练集低一大截但线上效果却很差。翻数据才发现测试集的时间段和训练集有所重叠或者归一化时用了全量数据。原因数据切分没有严格按时间走。如果随机划分样本路网数据的滑窗样本之间重叠严重训练集和测试集里会出现几乎一样的样本。另一个泄漏源就是前一章提到的scaler测试集的均值已经参与了训练。解决严格按时间顺序切分训练集占70%验证集占15%测试集占15%滑窗采样的step至少等于horizon保证相邻样本之间没有共用未来窗口。测试集第一个批次的起始时间必须晚于所有训练样本的最后时间步。这一步做好模型的好坏才能真正体现出来。6. 上手一周要做的三件事先证明这个方向值得投入再谈调参如果你刚拿到路网Transformer的源码骨架不要急着堆数据、调头。第一周只做三件事跑通最小训练闭环、和基线比一次、加一个可视化。第一件事用一周时间切片的数据把模型完整训一遍。一周的5分钟粒度数据有2016个时间片切成训练验证测试后样本量大概够一个小实验。代码跑通的标准是loss持续下降验证集MAPE低于直接复制昨天同时刻数据的基线。第二件事建立一个“历史平均值”基线、一个单层LSTM基线再跑你的路网Transformer。如果Transformer比历史平均连5%的RMSE优势都没有大概率是数据或编码有问题不要怀疑模型架构先回到第2章检查邻接矩阵和第4章检查归一化。第三件事把预测结果画在路网图上热力图一眼就能看出模型是不是把拥堵区域预测在了相邻路段——这种空间错位是数值指标看不见的。经过一周验证如果模型在RMSE上能比LSTM提升3%以上、比历史平均提升8%以上这个方向就值得继续投入后续再去做特征扩展和多模型融合。如果提升不到这个数我建议回到GCN或XGBoost不要因为Transformer听起来新就强行使用。我自己的经验是空间编码的坑比模型结构的坑多十倍很多“Transformer不行”的结论最后都查出来是邻接矩阵算错了。把第2章和第5章反复过一遍再回头调模型你会少走很多弯路。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?