初始化处理
以网格 0(ID=0)为例读懂交通流预测,芝加哥卢普区(Loop)密歇根大道 + 亚当斯街交叉口(约 1 平方公里)
但是直接是从data_chicago.npy开始,也就是数据预处理(部分)、时空信息编码、静态嵌入,这三步全由作者编辑好了(不单单一人)。
数据起点:融合好的数据,[[0.8,1.0,0.0,0.9,1.0,0.625,…]…](形状是[8784,116])
如何得出来这个融合好的数据呢?
对特征进行归一化处理,前面提到的三个txt文件。
对静态特征进行差值计算。
对时空信息进行编码。
原始静态特征和差值特征拼接。
拼接后的静态特征和时空信息融合,维度变成2D,通过全连接层 / 命令reshape/主成分PCA分析(将原本的27x3=81转化成58,其中全连接层最好)。
将上一步得到的数据和动态数据做一个拼接,维度变成4D,就得到了data_chicago(即将58+58拼接成116,这个也就是代码中的all_data,即数据的起点)。
为什么要对静态特征进行差值计算呢,静态特征差值计算不应该是0吗?
1. 首先,这里的静态特征应该叫固定特征,作者写这句话的时候认为这些特征都叫做静态或者固定特征;
2. 因为无论模型如何更改或者做什么操作,这些特征始终不变所以叫固定特征;
3. 所以它实际上是把动态特征做了差值计算。
正式进入代码流程
evolution函数
也就是update-decay过程
扩维,加上时间,因为预测是用之前五个时间点预测下一个时间点
把初始动态特征进行复制,后面方便比较变化
执行update-decay计算(代码和理论有点出入,谨言慎行)
执行平滑门计算
变成啥样了,[[0.8,1.0,0.0,0.9,1.0,0.625,…,0.7,0.54,0.32,0.25]…] -> [[0.8,1.0,0.0,0.9,1.0,0.625,…,0.4,0.14,0.52,0.28]…]
前面静态特征没有变化,后面动态特征进行了操作
#在model.py
# 1. 初始化动态特征序列:给初始动态特征加时间维度,形状从[N,dr2] → [1, N, dr2](训练开始时,所有网格的动态嵌入初始化为 0 向量)
all_data_dynamic = tf.expand_dims(all_data_dynamic_now, 0)
# 2. 缓存初始动态特征,作为「旧特征」(空白向量会和流量等动态特征相加)
all_data_dynamic_now_prev = all_data_dynamic_now
# 3. 计算【候选更新的动态特征】:核心的Update,decay过程,初始化
all_data_dynamic_now_candidate = tf.sigmoid(
tf.matmul(tf.concat([all_data_dynamic_now, all_data_static[0]], axis=-1), self.w1)
* tf.repeat(threshold_nc[0], self.dr2, axis=-1) + all_data_dynamic_now #它和上一行执行Update操作
* tf.repeat(1 - threshold_nc[0], self.dr2, axis=-1)) * math.exp(-1 / 2) #执行Decay操作
# 4.平滑门
if self.use_smooth_gate: #是否使用平滑门
MultiAttention函数
也就是空间相互影响表示学习
提取邻居特征,也就是 [[0.7,1.0,0.0,0.8,1.0,0.65,…,0.2,0.1,0.2,0.48]…]
学习重要邻居特征,进行注意力计算,也就是根据相似度决定学多少特征
三个视图都进行1-2的操作
变成啥样了,[[0.8,1.0,0.0,0.9,1.0,0.625,…,0.4,0.14,0.52,0.28]…] -> [[0.82,0.98,0.12,0.7,1.0,0.65,…,0.2,0.4,0.52,0.17]…]
#在model.py
# 核心操作:根据邻居索引,找到每个网格的所有邻居特征,实现「网格-邻居」的特征关联
data_neigh = tf.nn.embedding_lookup(tf.transpose(data, (2, 0, 1, 3)),
neigh_index)
# 张量形状变化:[N, len, T, dr2] → [N, K, len, T, dr2] → 每个网格都对应K个邻居的完整特征
data_neigh = tf.transpose(data_neigh, (2, 3, 0, 1, 4))
# 将自身特征重塑:增加「邻居维度」K=1,形状变为 [len, T, N, 1, dr2],和邻居特征形状对齐
data = tf.reshape(data, (-1, data.shape[1], data.shape[2], 1, data.shape[-1]))
# 以上为输入
# 自身特征 → Query向量(q):data * wq
data = tf.matmul(data, self.wq)
# 邻居特征 → Key向量(k):data_neigh * wk (Value=Key,权重共享)
data_neigh = tf.matmul(data_neigh, self.wk)
# 低配版:均值池化,直接对邻居特征取平均,无自适应权重 → 等价于GCN的等权聚合
if self.mode == 'mean':
out = tf.reduce_mean(data_neigh, axis=-2, keepdims=True)
else:
logits = tf.matmul(data, data_neigh, transpose_b=True)
# 核心版:缩放点积注意力(scaled_dot)→ 自适应学习邻居的「重要性权重」
if self.mode == 'scaled_dot':
logits = logits / tf.math.sqrt(tf.cast(self.dr2, logits.dtype))
# softmax归一化:注意力分数→注意力权重,权重和为1,代表「每个邻居对当前网格的贡献度」
out = tf.matmul(tf.nn.softmax(logits, axis=-1), data_neigh)
# 残差连接
out = data + out
out = tf.sigmoid(
tf.matmul(tf.reshape(out, (-1, self.len_recent_time, self.number_region, self.dr2)), self.wd_s))
return out
# 三个视图都进行1-2的操作
self.attention_layers_poi = [Attention(self.dr2, len_recent_time, number_region, mode=attention_mode) for _ in range(self.num_sp)] #POI
self.attention_layers_road = [Attention(self.dr2, len_recent_time, number_region, mode=attention_mode) for _ in range(self.num_sp)] #静态特征
self.attention_layers_record = [Attention(self.dr2, len_recent_time, number_region, mode=attention_mode) for _ in range(self.num_sp)] #动态特征
模型函数
convlstm、Dense
直接将数据输入,得到结果y_predict, y_dy, dy_dif
数值变化:[[0.8,1.0,0.0,0.9,1.0,0.625,…,0.4,0.14,0.52,0.28]…] -> [0.65,0.15,0.2]
一个预测的结果,两个进行损失函数计算的数据
损失函数计算
梯度下降
重复训练,早停机制
#在model.py
all_data = self.convlstm(all_data) #输入到lstm模型中
all_data = tf.reshape(all_data, (-1, all_data.shape[1])) #再进行对齐
all_data = self.final_layer(all_data) #输入到前馈神经网络层
# print(all_data.shape)
return all_data, all_data_dynamic_now, all_data_dynamic_diff
#在train.py
all_data_static, threshold_nc1, all_data_dynamic_now = x #输入后得到y_predict,y_dy, dy_diff
# y_predict模型的核心预测值(预测准确率),y_dy最后一个时间步的动态特征(计算导数),dy_diff动态特征的时间差分(发散的情况)
y_predict, y_dy, dy_diff = model(all_data_static, threshold_nc1, all_data_dynamic_now)
# 计算损失函数,交通异常样本极度不均衡,使用Focal Loss处理类别不平衡
loss, focal_loss, dy_loss = loss_function(y_predict, label_y, dy_diff)
loss = tf.reduce_mean(loss)
tf.print('training:', "loss:", loss, " focal loss:", tf.reduce_mean(focal_loss))
# 梯度下降
grads = tape.gradient(loss, model.variables)
optimizer.apply_gradients(grads_and_vars=zip(grads, model.variables))
return y_dy
# 8.早停机制初始化 EarlyStopping
patience = params.patience
delta = params.delta
early_stop = EarlyStopping(patience=patience, delta=delta)
代码额外操作
用验证集调优阈值
数据流后处理,提高recall
训练结束,测试输出结果,用测试结果 p=0.65 和标签对比,判断是否预测正确
#在train.py
# 10.用验证集调优阈值,并不是一定说值超过0.5就判断为异常,万一验证后0.55更好呢,这就是验证集的作用
threshold_f1, threshold_accu, y_dy_valid = \
get_f1_threshold(val_x, val_threshold_nc, y_dynamic, val_y, model, batch_size)
ap_score, ra_score, f1, recall, precision, accu, y, test_predict = \
get_metrics(test_x, test_threshold_nc, y_dy_valid, test_y, model, batch_size, threshold_f1, threshold_accu)
min_precision = max(0.0, float(precision) - 0.02)
min_accuracy = max(0.0, float(accu) - 0.03)
# 保证recall,不希望recall下降,即使误报,也比要发生事故但是没报警好
best_th_r, best_rec, best_prec_r, best_acc_r = get_threshold_max_recall(
y,
test_predict,
min_precision=min_precision,
min_accuracy=min_accuracy,
step=0.001,
)
y_pred_r = (test_predict > best_th_r)
......
......