本人也是刚接触深度学习,接了个项目要用到图神经网络,现在在学习相关代码和理论。STGCN是一个SOTA GNN 模型,其计算快、效果好,方法简单。因此将其作为我第一个仔细阅读代码的模型。如有不对之处欢迎大家批评指正!
完整代码:hazdzz/STGCN: The PyTorch version of STGCN. (github.com)
实现工具:colab T4 GPU
main.py
set_env(seed):
def set_env(seed):
# Set available CUDA devices
# This option is crucial for an multi-GPU device
os.environ['CUDA_VISIBLE_DEVICES'] = '0, 1'
# os.environ['CUDA_LAUNCH_BLOCKING'] = '1'
# os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':16:8'
os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':4096:8'
os.environ['PYTHONHASHSEED']=str(seed)
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.deterministic = True
torch.use_deterministic_algorithms(True)
set_env(seed)
函数的作用是设置和配置一些环境变量,以确保实验的稳定性和可重复性。这些环境变量的设置包括以下几个方面:
1. CUDA设备的选择:os.environ['CUDA_VISIBLE_DEVICES'] = '0, 1'
用于指定使用的GPU设备。这对于多GPU系统非常重要,它确保代码在指定的GPU设备上执行。如果不设置这个环境变量,PyTorch可能会在所有可用的GPU上分配任务,这可能会导致资源冲突和性能问题。
2. 随机种子的设置:random.seed(seed)
、np.random.seed(seed)
、torch.manual_seed(seed)
、torch.cuda.manual_seed(seed)
等语句用于设置伪随机数生成器的种子。通过设置相同的种子,可以确保在不同运行之间获得相同的随机数,从而使实验结果可重复。这对于实验结果的稳定性和比较非常重要。
3. 关于GPU使用的其他设置:torch.backends.cudnn.benchmark
、torch.backends.cudnn.deterministic
和torch.use_deterministic_algorithms
等语句用于控制在GPU上的操作。torch.backends.cudnn.benchmark
通常设置为False,以确保运行时不会对卷积算法进行优化,这对于稳定性很重要。torch.backends.cudnn.deterministic
和torch.use_deterministic_algorithms
用于启用确定性的CUDA操作,以确保相同输入下的结果相同,从而提高可重复性。
get_parameters():
def get_parameters():
parser = argparse.ArgumentParser(description='STGCN')
parser.add_argument('--enable_cuda', type=bool, default=True, help='enable CUDA, default as True')
parser.add_argument('--seed', type=int, default=42, help='set the random seed for stabilizing experiment results')
parser.add_argument('--dataset', type=str, default='metr-la', choices=['metr-la', 'pems-bay', 'pemsd7-m'])
parser.add_argument('--n_his', type=int, default=12)
parser.add_argument('--n_pred', type=int, default=3, help='the number of time interval for predcition, default as 3')
parser.add_argument('--time_intvl', type=int, default=5)
parser.add_argument('--Kt', type=int, default=3)
parser.add_argument('--stblock_num', type=int, default=2)
parser.add_argument('--act_func', type=str, default='glu', choices=['glu', 'gtu'])
parser.add_argument('--Ks', type=int, default=3, choices=[3, 2])
parser.add_argument('--graph_conv_type', type=str, default='cheb_graph_conv', choices=['cheb_graph_conv', 'graph_conv'])
parser.add_argument('--gso_type', type=str, default='sym_norm_lap', choices=['sym_norm_lap', 'rw_norm_lap', 'sym_renorm_adj', 'rw_renorm_adj'])
parser.add_argument('--enable_bias', type=bool, default=True, help='default as True')
parser.add_argument('--droprate', type=float, default=0.5)
parser.add_argument('--lr', type=float, default=0.001, help='learning rate')
parser.add_argument('--weight_decay_rate', type=float, default=0.0005, help='weight decay (L2 penalty)')
parser.add_argument('--batch_size', type=int, default=32)
parser.add_argument('--epochs', type=int, default=10000, help='epochs, default as 10000')
parser.add_argument('--opt', type=str, default='adam', help='optimizer, default as adam')
parser.add_argument('--step_size', type=int, default=10)
parser.add_argument('--gamma', type=float, default=0.95)
parser.add_argument('--patience', type=int, default=30, help='early stopping patience')
args = parser.parse_args()
print('Training configs: {}'.format(args))
# For stable experiment results
set_env(args.seed)
# Running in Nvidia GPU (CUDA) or CPU
if args.enable_cuda and torch.cuda.is_available():
# Set available CUDA devices
# This option is crucial for multiple GPUs
# 'cuda' ≡ 'cuda:0'
device = torch.device('cuda')
else:
device = torch.device('cpu')
Ko = args.n_his - (args.Kt - 1) * 2 * args.stblock_num
# blocks: settings of channel size in st_conv_blocks and output layer,
# using the bottleneck design in st_conv_blocks
blocks = []
blocks.append([1])
for l in range(args.stblock_num):
blocks.append([64, 16, 64])
if Ko == 0:
blocks.append([128])
elif Ko > 0:
blocks.append([128, 128])
blocks.append([1])
return args, device, blocks
get_parameters()
函数用于解析命令行参数,配置模型的各种超参数,以及确定是否使用CUDA(GPU加速)来运行模型。
1. 解析命令行参数:parser = argparse.ArgumentParser(description='STGCN')
创建一个解析器对象,用于解析命令行参数。description
参数指定了解析器的描述信息
2.定义各种命令行参数:
--enable_cuda
:是否启用CUDA,类型为布尔值,默认为True。--seed
:随机数种子,用于实验结果的稳定性,默认为42。--dataset
:数据集名称,有三种选择:'metr-la'、'pems-bay'和'pemsd7-m'。--n_his
:历史时间步数,表示用于预测的历史时间步数,默认为12。--n_pred
:预测时间步数,表示每个时间步需要预测的时间步数,默认为3。--time_intvl
:时间间隔,表示数据采样的时间间隔,默认为5。--Kt
:时间卷积核的大小,默认为3。--stblock_num
:STGCN中的ST块数量,默认为2。--act_func
:激活函数类型,有两种选择:'glu'和'gtu'。--Ks
:空间卷积核的大小,有两种选择:3和2。--graph_conv_type
:图卷积类型,有两种选择:'cheb_graph_conv'和'graph_conv'。--gso_type
:图的 Laplacian 矩阵类型,有四种选择:'sym_norm_lap'、'rw_norm_lap'、'sym_renorm_adj'和'rw_renorm_adj'。--enable_bias
:是否启用偏置,默认为True。--droprate
:dropout率,默认为0.5。--lr
:学习率,默认为0.001。--weight_decay_rate
:权重衰减率(L2正则化),默认为0.0005。--batch_size
:批大小,默认为32。--epochs
:训练轮数,默认为10000。--opt
:优化器类型,有三种选择:'adam'、'rmsprop'和'adamw'。--step_size
:学习率衰减的步数,默认为10。--gamma
:学习率衰减的因子,默认为0.95。--patience
:早停的耐心参数,默认为30。
3. 使用parser.parse_args()
解析命令行参数,并将结果存储在args
变量中。
4. 打印参数:通过print('Training configs: {}'.format(args))
将解析后的参数打印出来,以便用户知道配置了哪些参数.
5. 设置随机种子和GPU设备:根据参数中的enable_cuda
来决定是否使用CUDA(GPU加速)。如果启用CUDA且GPU可用,将device
设置为cuda
,否则设置为cpu
。此外,还设置了随机种子,以确保实验结果的稳定性。
data_preparate(args, device)
def data_preparate(args, device):
adj, n_vertex = dataloader.load_adj(args.dataset)
gso = utility.calc_gso(adj, args.gso_type)
if args.graph_conv_type == 'cheb_graph_conv':
gso = utility.calc_chebynet_gso(gso)
gso = gso.toarray()
gso = gso.astype(dtype=np.float32)
args.gso = torch.from_numpy(gso).to(device)
dataset_path = './data'
dataset_path = os.path.join(dataset_path, args.dataset)
data_col = pd.read_csv(os.path.join(dataset_path, 'vel.csv')).shape[0]
# recommended dataset split rate as train: val: test = 60: 20: 20, 70: 15: 15 or 80: 10: 10
# using dataset split rate as train: val: test = 70: 15: 15
val_and_test_rate = 0.15
len_val = int(math.floor(data_col * val_and_test_rate))
len_test = int(math.floor(data_col * val_and_test_rate))
len_train = int(data_col - len_val - len_test)
train, val, test = dataloader.load_data(args.dataset, len_train, len_val)
zscore = preprocessing.StandardScaler()
train = zscore.fit_transform(train)
val = zscore.transform(val)
test = zscore.transform(test)
x_train, y_train = dataloader.data_transform(train, args.n_his, args.n_pred, device)
x_val, y_val = dataloader.data_transform(val, args.n_his, args.n_pred, device)
x_test, y_test = dataloader.data_transform(test, args.n_his, args.n_pred, device)
train_data = utils.data.TensorDataset(x_train, y_train)
train_iter = utils.data.DataLoader(dataset=train_data, batch_size=args.batch_size, shuffle=False)
val_data = utils.data.TensorDataset(x_val, y_val)
val_iter = utils.data.DataLoader(dataset=val_data, batch_size=args.batch_size, shuffle=False)
test_data = utils.data.TensorDataset(x_test, y_test)
test_iter = utils.data.DataLoader(dataset=test_data, batch_size=args.batch_size, shuffle=False)
return n_vertex, zscore, train_iter, val_iter, test_iter
data_preparate(args, device)
函数用于数据准备,包括加载数据集的相关信息,生成图的邻接矩阵,划分训练、验证和测试数据集,进行数据标准化处理,并创建 PyTorch 数据加载器 (DataLoader) 以便后续训练和测试。
1. 加载邻接矩阵和图信息:adj, n_vertex = dataloader.load_adj(args.dataset)
用于加载数据集的邻接矩阵和节点数量。adj
是一个描述节点之间关系的邻接矩阵,n_vertex
是节点的数量。
2. 计算图的 Laplacian 矩阵:gso = utility.calc_gso(adj, args.gso_type)
使用 utility.calc_gso
函数计算图的 Laplacian 矩阵,args.gso_type
参数指定了 Laplacian 矩阵的类型,有四种选择:'sym_norm_lap'、'rw_norm_lap'、'sym_renorm_adj' 和 'rw_renorm_adj'。
3. 如果 graph_conv_type
为 'cheb_graph_conv',则计算 ChebNet 的 Laplacian 矩阵:gso = utility.calc_chebynet_gso(gso)
,这是一个额外的处理步骤,用于 ChebNet 图卷积的计算。
4. 转换邻接矩阵为 NumPy 数组:gso = gso.toarray()
将 Laplacian 矩阵从稀疏矩阵格式转换为 NumPy 数组。
5. 将 NumPy 数组的数据类型转换为 np.float32
:gso = gso.astype(dtype=np.float32)
以确保数据类型适用于后续的计算。
6. 使用 PyTorch 将 Laplacian 矩阵 gso
转换为张量格式,并将其发送到指定的 device
(GPU 或 CPU):args.gso = torch.from_numpy(gso).to(device)
。这将准备好后续模型的输入。
7. 加载数据集:根据指定的数据集路径,加载原始数据集。在示例中,数据集路径是 './data',并根据 args.dataset
参数指定特定的数据集。
8. 划分数据集:根据指定的划分比例,将数据集分为训练、验证和测试集。示例中使用了 len_train
、len_val
和 len_test
这些变量来确定每个数据集的大小。
9. 数据标准化处理:使用 preprocessing.StandardScaler
对数据进行标准化处理。首先对训练数据进行拟合(fit_transform
),然后使用相同的均值和方差对验证和测试数据进行转换(transform
)。
10. 数据转换:将标准化后的数据转换为模型所需的输入格式。使用 dataloader.data_transform
函数,将数据集划分为输入 x
和目标 y
。这些输入数据将用于模型训练和测试。
11. 创建 PyTorch 数据加载器:通过 utils.data.TensorDataset
创建训练、验证和测试数据集,并使用 utils.data.DataLoader
创建数据加载器,以便能够批量加载数据进行训练和测试。
train()
def train(loss, args, optimizer, scheduler, es, model, train_iter, val_iter):
for epoch in range(args.epochs):
l_sum, n = 0.0, 0 # 'l_sum' is epoch sum loss, 'n' is epoch instance number
model.train()
for x, y in tqdm.tqdm(train_iter):
y_pred = model(x).view(len(x), -1) # [batch_size, num_nodes]
l = loss(y_pred, y)
optimizer.zero_grad()
l.backward()
optimizer.step()
l_sum += l.item() * y.shape[0]
n += y.shape[0]
scheduler.step()
val_loss = val(model, val_iter)
# GPU memory usage
gpu_mem_alloc = torch.cuda.max_memory_allocated() / 1000000 if torch.cuda.is_available() else 0
print('Epoch: {:03d} | Lr: {:.20f} |Train loss: {:.6f} | Val loss: {:.6f} | GPU occupy: {:.6f} MiB'.\
format(epoch+1, optimizer.param_groups[0]['lr'], l_sum / n, val_loss, gpu_mem_alloc))
if es.step(val_loss):
print('Early stopping.')
break
train()
函数用于训练模型,它负责训练模型并在每个训练周期(epoch)之后评估模型性能。
1. 循环训练:for epoch in range(args.epochs):
开始一个训练周期的循环。args.epochs
指定了总共要进行的训练周期数。
2. 初始化损失和实例数:l_sum, n = 0.0, 0
分别表示当前训练周期中的总损失和训练实例数。这些变量用于在训练周期结束后计算平均损失。
3. 将模型设置为训练模式:model.train()
将模型设置为训练模式,这会启用一些在训练时需要的特性,例如启用 dropout 和批规范化(如果有的话)。
4. 批量训练:for x, y in tqdm.tqdm(train_iter):
开始一个批次的训练。x
是输入数据,y
是目标数据。tqdm提供了一个快速、可扩展的进度条,用于在命令行界面中显示循环操作的进度。tqdm
的名称来自“taqaddum”(阿拉伯语),意为“进展”。通过使用 tqdm
,可以在终端中看到循环操作的进度,包括当前迭代的百分比、已完成的迭代数、预计的剩余时间等信息。这对于长时间运行的任务、循环和数据处理非常有用,因为它可以让你清晰地了解操作的进展,以及估计操作完成的时间。 在上面的代码中,for x, y in tqdm.tqdm(train_iter):
使用了 tqdm
来创建一个循环,用于遍历训练数据集,并在命令行中显示进度条,以显示每个批次的训练进度。
5. 前向传播和计算损失:y_pred = model(x).view(len(x), -1)
执行前向传播,得到模型的预测结果 y_pred
。l = loss(y_pred, y)
计算模型的损失,这里使用的是均方误差(MSE)损失。
6. 清零梯度:optimizer.zero_grad()
清零模型参数的梯度,以准备进行反向传播。
7. 反向传播和参数更新:l.backward()
执行反向传播,计算模型参数的梯度。optimizer.step()
使用优化器更新模型参数,以减小损失。
8. 计算总损失和实例数:l_sum += l.item() * y.shape[0]
累积当前批次的损失。y.shape[0]
是当前批次中的实例数,用于将批次损失加权累积到总损失中。
9. 累积实例数:n += y.shape[0]
累积当前批次中的实例数。
10. 学习率调度:scheduler.step()
在每个训练周期结束后,调用学习率调度器来调整学习率。这有助于在训练的不同阶段动态调整学习率以获得更好的模型性能。
11. 打印训练信息:print('Epoch: {:03d} | Lr: {:.20f} |Train loss: {:.6f} | Val loss: {:.6f} | GPU occupy: {:.6f} MiB'.format(epoch+1, optimizer.param_groups[0]['lr'], l_sum / n, val_loss, gpu_mem_alloc))
打印每个训练周期的信息,包括当前周期数、学习率、训练损失、验证损失以及 GPU 内存占用情况。
12. 早停检测:if es.step(val_loss):
使用早停策略来检查验证损失是否已经停止改善。如果验证损失连续 patience
轮没有改善(小于 min_delta
),则早停被触发。
13. 早停终止:如果早停被触发,print('Early stopping.')
打印早停信息并跳出训练循环,提前终止训练。这是为了防止过拟合或在验证损失不再改善时节省时间。
@torch.no_grad()
@torch.no_grad()
是 PyTorch 中的一个装饰器(Decorator),用于在特定的代码块中关闭梯度跟踪(gradient tracking)。梯度跟踪是 PyTorch 中用于自动计算梯度的核心功能,它在训练神经网络时非常有用。然而,在某些情况下,你可能希望临时关闭梯度跟踪,以节省内存并提高运行速度。
在代码中,@torch.no_grad()
装饰器用于标记以下函数或代码块,以告诉PyTorch在其中不需要跟踪梯度:
-
@torch.no_grad() def val(model, val_iter):
函数val
用于在验证数据上评估模型的性能,这里关闭了梯度跟踪,因为在验证时通常不需要计算梯度。 -
@torch.no_grad() def test(zscore, loss, model, test_iter, args):
函数test
也用于在测试数据上评估模型,同样关闭了梯度跟踪。
这样可以确保在验证和测试阶段不会浪费计算资源来计算梯度,从而提高运行效率。
val(model, val_iter)
在验证数据集上计算模型的损失,以评估模型性能。
test(zscore, loss, model, test_iter, args)
在测试数据集上执行模型的测试,并计算测试数据集上的性能指标,如均方误差(MSE)、平均绝对误差(MAE)等。
main主函数
这部分代码是脚本的入口点,执行以下步骤:
- 获取参数和配置。
- 数据准备。
- 初始化模型、损失函数、优化器和学习率调度器。
- 执行训练。
- 进行测试,并输出性能指标。
script
utility.py
1.calc_gso(dir_adj, gso_type)
:
- 这个函数用于计算图的图结构关系矩阵(Graph Structure Operator,GSO),它接受两个参数:
dir_adj
表示一个有向邻接矩阵,gso_type
表示图结构操作类型。 - 首先,它将有向邻接矩阵
dir_adj
转换为对称邻接矩阵,以使图变得对称。 - 然后,根据给定的
gso_type
,它计算不同的图结构操作,包括对称归一化邻接矩阵(sym_norm_adj)、随机游走归一化邻接矩阵(rw_norm_adj)等。 - 返回计算得到的图结构操作矩阵
gso
。
2.calc_chebynet_gso(gso)
:
- 这个函数用于对图结构操作矩阵
gso
进行进一步的处理,以确保其特征值的范围。 - 如果输入的
gso
不对称且特征值较大,它会减去单位矩阵(id)。 - 如果
gso
对称,它会将其缩放到特征值的范围在 2 以内。 - 返回处理后的图结构操作矩阵
gso
3. cnv_sparse_mat_to_coo_tensor(sp_mat, device)
:
- 这个函数用于将稀疏矩阵(
sp_mat
)转换为 PyTorch 的稀疏张量(Sparse COO Tensor)。 - 它接受稀疏矩阵
sp_mat
和计算设备device
作为输入参数。 - 将稀疏矩阵转换为稀疏 COO 张量,并根据
sp_mat
的数据类型,将其转换为适当的 PyTorch 数据类型。 - 返回稀疏 COO 张量。
4. evaluate_model(model, loss, data_iter)
:
- 这个函数用于评估模型的性能,计算模型在给定数据集上的均方误差(MSE)。
- 它接受训练好的
model
、损失函数loss
和数据迭代器data_iter
作为输入。 - 在模型评估过程中,它会迭代数据迭代器,计算模型的预测结果和实际标签之间的均方误差。
- 返回均方误差(MSE)作为性能度量。
5. evaluate_metric(model, data_iter, scaler)
:
- 这个函数用于评估模型的性能,计算模型在给定数据集上的多个性能指标,包括平均绝对误差(MAE)、均方根误差(RMSE)、加权平均绝对百分比误差(WMAPE)等。
- 它接受训练好的
model
、数据迭代器data_iter
和标准化器scaler
作为输入。 - 在模型评估过程中,它会迭代数据迭代器,计算模型的预测结果和实际标签之间的误差,并计算多个性能指标。
- 返回 MAE、RMSE 和 WMAPE 作为性能度量。
model
models.py
models.py
中定义了两个不同的模型类:STGCNChebGraphConv
和 STGCNGraphConv
,它们用于处理空间-时间图卷积网络。这两个类主要包含了模型的结构以及前向传播的逻辑。
-
STGCNChebGraphConv
模型类:- 这个类基于 "TGTND TGTND TNFF" 结构构建,包含了多个层次的图卷积层。
- 模型使用 Chebyshev 多项式作为图卷积的滤波器,它有多个层次的 Gated Temporal Convolution Layer(GLU or GTU)和 Graph Convolution Layer(ChebGraphConv)。
- 图中的 T 表示 Gated Temporal Convolution Layer,G 表示 Graph Convolution Layer,N 表示 Layer Normolization,D 表示 Dropout。
- 这个模型适用于基于 Chebyshev 多项式的图卷积。
-
STGCNGraphConv
模型类:- 这个类也基于 "TGTND TGTND TNFF" 结构构建,包含了多个层次的图卷积层。
- 与
STGCNChebGraphConv
不同,这个模型使用标准的 GCN(Graph Convolution Network)进行图卷积,而不是 Chebyshev 多项式。 - 图中的 T 表示 Gated Temporal Convolution Layer,G 表示 Graph Convolution Layer,N 表示 Layer Normolization,D 表示 Dropout。
- 这个模型适用于使用 GCN 进行图卷积的情况,采用了标准的 renormalization 技巧,防止过度平滑。
这两个模型类的构造函数都接受一些参数,包括 args
(包含各种模型配置信息)、blocks
(表示模型中不同层次的配置)、n_vertex
(表示图中节点的数量)。模型类的主要组成部分包括:
STConvBlock
:这是定义了 STGCN 中的每个图卷积块的类,包括图卷积层、激活函数、规范化和丢弃层等。OutputBlock
:这是定义了模型输出层的类,其中包括全连接层等。
前向传播的逻辑包括将输入数据经过一系列的 STConvBlock 层,然后根据 Ko
(时间延迟步数)的不同值选择不同的输出处理方式。如果 Ko > 1
,则通过 output
层进行处理;如果 Ko == 0
,则使用全连接层进行处理。
layers.py
layers.py
中包含了用于构建 STGCN 模型中不同层次的模块和层定义。以下是这些层和模块的主要内容:
-
Align
模块:Align
模块用于调整输入的通道数,确保输入与输出的通道数一致。- 如果输入通道数大于输出通道数,它会应用 1x1 卷积层以减少通道数。
- 如果输入通道数小于输出通道数,它会在通道维度上填充零,以增加通道数。
- 这有助于保持一致的通道数以进行后续计算。
-
CausalConv1d
和CausalConv2d
模块:- 这两个模块实现了因果卷积(Causal Convolution),在处理时间序列数据时常用。
CausalConv1d
用于处理 1D 数据,而CausalConv2d
用于处理 2D 数据。- 这些模块的主要特点是具有 "因果性",即卷积操作不会使用未来的信息,只使用过去和当前的信息。
- 这是通过适当设置填充参数来实现的。
-
TemporalConvLayer
模块:TemporalConvLayer
是一种时间卷积层,它采用 "GLU"(Gated Linear Unit)或其他激活函数处理输入数据。- 该层通过时间维度执行卷积操作,并使用 "GLU" 或其他激活函数将卷积结果与输入数据组合起来。
- 可根据配置选择 "GLU"、ReLU 或 SiLU(Sigmoid-Weighted Linear Unit)激活函数。
- 这个模块用于处理时间维度上的卷积操作,通常用于时间序列数据。
-
ChebGraphConv
和GraphConv
模块:- 这两个模块分别实现了基于 Chebyshev 多项式的图卷积(ChebGraphConv)和标准图卷积(GraphConv)。
- 这些模块用于在图结构数据上执行卷积操作,其中
ChebGraphConv
使用 Chebyshev 多项式,而GraphConv
使用标准的卷积操作。 - 这些卷积层可以在图数据上传播信息,用于处理图数据的特征。
-
GraphConvLayer
模块:GraphConvLayer
包含了一个时间卷积层(TemporalConvLayer
)和一个图卷积层(ChebGraphConv
或GraphConv
)。- 这个模块用于处理时空图数据,它接受输入数据,先在时间维度上执行时间卷积,然后在图结构上执行图卷积。
- 这有助于在时空图数据中传播信息。
-
STConvBlock
模块:STConvBlock
表示一个完整的 ST-GCN 模型中的一个卷积块,包括了时间卷积、图卷积、激活函数和规范化。- 该块由多个卷积层组成,用于处理时空图数据。
- 通过多个卷积层的组合,可以在时空图数据中提取特征。
-
OutputBlock
模块:OutputBlock
用于模型的输出层,包括时间卷积、全连接层和激活函数。- 这个模块用于生成模型的最终输出,通常用于预测任务。
这些模块和层在 ST-GCN 模型中的组合和堆叠,用于处理时空图数据,提取特征,并生成模型的输出。这有助于模型在各种时空图数据的任务中表现出色。