gcn.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#import numpy as np
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
#from tensorflow.contrib.rnn import RNNCell
#tf.compat.v1.nn.rnn_cell.RNNCell
#tf.compat.v1.nn.rnn_cell.DeviceWrapper

from u import calculate_laplacian

class tgcnCell(tf.compat.v1.nn.rnn_cell.RNNCell):
"""Temporal Graph Convolutional Network """

def call(self, inputs, **kwargs):
pass

def __init__(self, num_units, adj, num_nodes, input_size=None,#gru_units=num_units
act=tf.nn.tanh, reuse=None):

super(tgcnCell, self).__init__(_reuse=reuse)
self._act = act
self._nodes = num_nodes
self._units = num_units
self._adj = []
self._adj.append(calculate_laplacian(adj))


@property
def state_size(self):
return self._nodes * self._units

@property
def output_size(self):
return self._units

def __call__(self, inputs, state, scope=None):

with tf.variable_scope(scope or "tgcn"):
with tf.variable_scope("gates"):
value = tf.nn.sigmoid(
self._gc(inputs, state, 2 * self._units, bias=1.0, scope=scope))
r, u = tf.split(value=value, num_or_size_splits=2, axis=1)
with tf.variable_scope("candidate"):
r_state = r * state
c = self._act(self._gc(inputs, r_state, self._units, scope=scope))
new_h = u * state + (1 - u) * c
return new_h, new_h


def _gc(self, inputs, state, output_size, bias=0.0, scope=None):
## inputs:(-1,num_nodes)
inputs = tf.expand_dims(inputs, 2)
## state:(batch,num_node,gru_units)
state = tf.reshape(state, (-1, self._nodes, self._units))
## concat
x_s = tf.concat([inputs, state], axis=2)
input_size = x_s.get_shape()[2].value
## (num_node,input_size,-1)
x0 = tf.transpose(x_s, perm=[1, 2, 0])
x0 = tf.reshape(x0, shape=[self._nodes, -1])

scope = tf.get_variable_scope()
with tf.variable_scope(scope):
for m in self._adj:
x1 = tf.sparse_tensor_dense_matmul(m, x0)
# print(x1)
x = tf.reshape(x1, shape=[self._nodes, input_size,-1])
x = tf.transpose(x,perm=[2,0,1])
x = tf.reshape(x, shape=[-1, input_size])
weights = tf.get_variable(
#'weights', [input_size, output_size], initializer=tf.contrib.layers.xavier_initializer())

'weights', [input_size, output_size], initializer=tf.glorot_uniform_initializer())
x = tf.matmul(x, weights) # (batch_size * self._nodes, output_size)
biases = tf.get_variable(
"biases", [output_size], initializer=tf.constant_initializer(bias, dtype=tf.float32))
x = tf.nn.bias_add(x, biases)
x = tf.reshape(x, shape=[-1, self._nodes, output_size])
x = tf.reshape(x, shape=[-1, self._nodes * output_size])
return x

这段代码实现了一个叫做Temporal Graph Convolutional Network的模型。这个模型是基于RNNCell的,它的输入是一个二维的tensor,其中第一维是batch size,第二维是节点数。模型的核心是_gc函数,它实现了图卷积操作。在_gc函数中,输入和状态被拼接在一起,然后通过稀疏矩阵乘法计算出新的特征向量。这个特征向量被送入一个全连接层,最终输出新的状态。这个模型的优点是可以处理动态图,因为它的邻接矩阵是动态计算的。

(这个模型并没有明确的层数,它使用了tgcnCell类来实现卷积操作和状态更新。在_gc方法中,它执行了一次卷积操作,但是它使用了Laplacian矩阵来表示图结构,并在循环中执行了稀疏矩阵乘法。因此,可以说这个模型的层数是动态的,取决于输入数据的图结构。)

1
2
3
4
5
6
7
8
9
10
def __init__(self, num_units, adj, num_nodes, input_size=None,#gru_units=num_units
act=tf.nn.tanh, reuse=None):

super(tgcnCell, self).__init__(_reuse=reuse)
self._act = act
self._nodes = num_nodes
self._units = num_units
self._adj = []
self._adj.append(calculate_laplacian(adj))

这段代码定义了一个名为tgcnCell的类,它继承了tf.compat.v1.nn.rnn_cell.RNNCell。在初始化函数中,它接受num_units、adj、num_nodes、input_size、act和reuse等参数。其中,num_units表示GRU隐藏单元的数量,adj是邻接矩阵,num_nodes是节点数量,input_size是输入的大小,act是激活函数,reuse表示是否重用变量。在初始化函数中,它调用了calculate_laplacian函数计算了邻接矩阵的拉普拉斯矩阵,并将其存储在_adj列表中。

tf.nn.tanh是一种激活函数,它将输入值映射到-1到1之间的范围内。reuse参数是一个布尔值,用于控制是否重用变量。如果reuse为True,则会重用先前创建的变量,否则会创建新的变量。

1
2
3
4
5
6
7
@property
def state_size(self):
return self._nodes * self._units

@property
def output_size(self):
return self._units

这段代码是TGNN模型中的一个类tgcnCell的两个属性函数。其中state_size函数返回的是该类的状态大小,即节点数乘以单元数;output_size函数返回的是该类的输出大小,即单元数。这两个属性函数是用于定义模型的输入输出大小的。

1
2
3
4
5
6
7
8
9
10
11
12
def __call__(self, inputs, state, scope=None):

with tf.variable_scope(scope or "tgcn"):
with tf.variable_scope("gates"):
value = tf.nn.sigmoid(
self._gc(inputs, state, 2 * self._units, bias=1.0, scope=scope))
r, u = tf.split(value=value, num_or_size_splits=2, axis=1)
with tf.variable_scope("candidate"):
r_state = r * state
c = self._act(self._gc(inputs, r_state, self._units, scope=scope))
new_h = u * state + (1 - u) * c
return new_h, new_h

这段代码是一个自定义的RNN单元,名为tgcnCell,实现了一个时间图卷积网络。在call方法中,它接受输入inputs和状态state,并返回新的状态new_h。在实现中,它首先将inputs和state拼接起来,然后通过一个门控单元计算出r和u,再通过一个候选单元计算出c。最后,它将u和c加权相加,得到新的状态new_h。其中,门控单元和候选单元都是通过_gc方法实现的,它们分别使用了不同的权重矩阵。在_gc方法中,它首先将inputs和state拼接起来,然后通过一个稀疏矩阵乘法计算出x1,再通过一个全连接层计算出x,最后将x reshape成合适的形状返回。这个代码实现了一个时间图卷积网络的核心部分,可以用于处理时间序列数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def _gc(self, inputs, state, output_size, bias=0.0, scope=None):
## inputs:(-1,num_nodes)
inputs = tf.expand_dims(inputs, 2)
## state:(batch,num_node,gru_units)
state = tf.reshape(state, (-1, self._nodes, self._units))
## concat
x_s = tf.concat([inputs, state], axis=2)
input_size = x_s.get_shape()[2].value
## (num_node,input_size,-1)
x0 = tf.transpose(x_s, perm=[1, 2, 0])
x0 = tf.reshape(x0, shape=[self._nodes, -1])

scope = tf.get_variable_scope()
with tf.variable_scope(scope):
for m in self._adj:
x1 = tf.sparse_tensor_dense_matmul(m, x0)
# print(x1)
x = tf.reshape(x1, shape=[self._nodes, input_size,-1])
x = tf.transpose(x,perm=[2,0,1])
x = tf.reshape(x, shape=[-1, input_size])
weights = tf.get_variable(
#'weights', [input_size, output_size], initializer=tf.contrib.layers.xavier_initializer())

'weights', [input_size, output_size], initializer=tf.glorot_uniform_initializer())
x = tf.matmul(x, weights) # (batch_size * self._nodes, output_size)
biases = tf.get_variable(
"biases", [output_size], initializer=tf.constant_initializer(bias, dtype=tf.float32))
x = tf.nn.bias_add(x, biases)
x = tf.reshape(x, shape=[-1, self._nodes, output_size])
x = tf.reshape(x, shape=[-1, self._nodes * output_size])
return x

这段代码是一个叫做_gc的函数,它是tgcnCell类中的一个私有函数。这个函数的作用是对输入的inputs和state进行图卷积操作,得到输出x。具体来说,它首先将inputs和state在第三个维度上进行拼接,然后将结果进行转置和reshape操作,得到一个形状为(num_node, input_size, -1)的张量x0。接着,它对x0进行图卷积操作,得到一个形状为(num_node, input_size, -1)的张量x1。最后,它将x1进行转置和reshape操作,得到一个形状为(-1, num_nodes * output_size)的张量x,并返回它。在图卷积操作中,它使用了一个稀疏矩阵m,这个矩阵是在tgcnCell类的构造函数中计算得到的拉普拉斯矩阵。在图卷积操作中,它还使用了权重矩阵weights和偏置向量biases,它们都是通过tf.get_variable函数创建的。其中,权重矩阵的形状为(input_size, output_size),偏置向量的形状为(output_size,)。在创建权重矩阵时,它使用了Glorot均匀分布进行初始化。在创建偏置向量时,它使用了常数初始化,并将偏置值设为bias。最后,它将x进行reshape操作,得到一个形状为(-1, num_nodes, output_size)的张量,并返回它。

dushu.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# %load acell.py
"""
Created on Thu Sep 9 16:05:41 2021

@author: Administrator
"""

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Jun 10 17:10:22 2019

@author: dhh
"""

import numpy as np
import pandas as pd

import tensorflow as tf
dim = 20

def load_assist_data(dataset):

sz_adj = pd.read_csv('网络流量阈值.csv', header=None,dtype='float32'
) ######这里是邻接矩阵,文件里都有
adj = np.mat(sz_adj)
data = pd.read_csv('1001-1400.csv',dtype='float32',
) #######这里放你的流量
data_1 = np.mat(data)
return data_1, adj

data, adj = load_assist_data('sz')
time_len = data.shape[0]
num_nodes = data.shape[1]
def preprocess_data(data1, time_len, train_rate, seq_len, pre_len):
train_size = int(time_len * train_rate)
train_data = data1[0:train_size]
test_data = data1[train_size:time_len]

trainX, trainY, testX, testY = [], [], [], []
for i in range(len(train_data) - seq_len - pre_len):
a1 = train_data[i: i + seq_len + pre_len]
a = np.row_stack((a1[0:seq_len]))
trainX.append(a)
trainY.append(a1[seq_len : seq_len + pre_len])
for i in range(len(test_data) - seq_len -pre_len):
b1 = test_data[i: i + seq_len + pre_len]
b = np.row_stack((b1[0:seq_len]))
testX.append(b)
testY.append(b1[seq_len : seq_len + pre_len])

trainX1 = np.array(trainX)
trainY1 = np.array(trainY)
testX1 = np.array(testX)
testY1 = np.array(testY)
print(trainX1.shape)
print(trainY1.shape)
print(testX1.shape)
print(testY1.shape)

return trainX1, trainY1, testX1, testY1

这段代码主要是用于数据预处理的,包括将数据分为训练集和测试集(8:2),将数据按照一定的时间序列长度和预测长度进行切分,最终返回训练集和测试集的输入和输出。其中,load_assist_data函数用于读取数据,preprocess_data函数用于数据预处理。具体来说,load_assist_data函数读取了两个csv文件,一个是邻接矩阵,一个是流量数据,返回的是邻接矩阵和流量数据的矩阵形式。preprocess_data函数将数据按照一定的时间序列长度和预测长度进行切分(3:1),将切分后的数据分别存储在trainX, trainY, testX, testY四个列表中,并将其转换为numpy数组形式,最终返回trainX1, trainY1, testX1, testY1四个数组。

ev.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# MSE, MAE, R2, RMSE法一

import pandas as pd
import numpy as np
import math
import os
import numpy.linalg as la
from sklearn.metrics import mean_squared_error,mean_absolute_error


def evaluatio(a,b):
rmse = math.sqrt(mean_squared_error(a,b))
mae = mean_absolute_error(a, b)
F_norm = la.norm(a-b,'fro')/la.norm(a,'fro')
r2 = 1-((a-b)**2).sum()/((a-a.mean())**2).sum()
var = 1-(np.var(a-b))/np.var(a)
return rmse, mae, 1-F_norm, r2, var

这段代码定义了一个名为evaluatio的函数,该函数接受两个参数a和b(这行代码中的 test_label 和 test_output 分别是测试集的真实标签和模型的预测输出。 ),并返回五个值:RMSE(均方根误差),MAE(平均绝对误差),1-Frobenius范数,R2(决定系数)和VAR(方差)。这些值是通过使用sklearn.metrics和numpy.linalg库中的函数计算得出的。RMSE和MAE是衡量模型预测误差的两个常用指标,Frobenius范数是矩阵的一种范数,R2是衡量模型拟合优度的指标,VAR是衡量模型预测方差的指标。这个函数可以用于评估模型的性能。

u.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# -*- coding: utf-8 -*-
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
import scipy.sparse as sp
import numpy as np


def normalized_adj(adj):
adj = sp.coo_matrix(adj)
rowsum = np.array(adj.sum(1))
d_inv_sqrt = np.power(rowsum, -0.5).flatten()
d_inv_sqrt[np.isinf(d_inv_sqrt)] = 0.
d_mat_inv_sqrt = sp.diags(d_inv_sqrt)
normalized_adj = adj.dot(d_mat_inv_sqrt).transpose().dot(d_mat_inv_sqrt).tocoo()
normalized_adj = normalized_adj.astype(np.float32)
return normalized_adj

def sparse_to_tuple(mx):
mx = mx.tocoo()
coords = np.vstack((mx.row, mx.col)).transpose()
L = tf.SparseTensor(coords, mx.data, mx.shape)
return tf.sparse_reorder(L)

def calculate_laplacian(adj, lambda_max=1):
adj = normalized_adj(adj + sp.eye(adj.shape[0]))
adj = sp.csr_matrix(adj)
adj = adj.astype(np.float32)
return sparse_to_tuple(adj)

def weight_variable_glorot(input_dim, output_dim, name=""):
init_range = np.sqrt(6.0 / (input_dim + output_dim))
initial = tf.random_uniform([input_dim, output_dim], minval=-init_range,
maxval=init_range, dtype=tf.float32)

return tf.Variable(initial,name=name)

这段代码定义了几个函数,用于计算图的拉普拉斯矩阵和权重变量。其中,normalized_adj函数将邻接矩阵进行归一化处理,得到归一化邻接矩阵;sparse_to_tuple函数将稀疏矩阵转换为TensorFlow稀疏张量;calculate_laplacian函数计算图的拉普拉斯矩阵,其中邻接矩阵进行了归一化处理;weight_variable_glorot函数用于初始化权重变量,采用了Glorot初始化方法。这些函数都是用于图卷积神经网络的实现。

main.ipynb

这段代码主要是一个时空图卷积神经网络(TGCN)的实现,用于时间序列预测。代码中包含了数据预处理、模型构建、自注意力机制等部分。其中,load_assist_data函数用于加载数据,preprocess_data函数用于数据预处理,TGCN函数是模型的核心部分,包含了一个时空图卷积层和一个自注意力机制,self_attention1函数是自注意力机制的具体实现。整个模型的输入是一个时间序列,输出是对未来一段时间的预测。