我正在尝试运行一个名为“STFGNN”的图神经网络模型(可在 GitHub 上获取 https://github.com/lwm412/STFGNN-Pytorch/tree/main?tab=readme-ov-file|| |) 在 Kaggle 上。但是,我遇到了几个问题: 1:运行时警告:除法返回(a-mu)/std0中遇到无效值
2:
我尝试使用以下标准化函数:
Traceback (most recent call last):
File "/kaggle/working/STFGNN/STFGNN-Pytorch-main/main.py", line 33, in <module>
executor.train(train_data, valid_data)
File "/kaggle/working/STFGNN/STFGNN-Pytorch-main/executor/multi_step_executor.py", line 82, in train
output = self.model(trainx)
File "/opt/conda/lib/python3.10/site-packages/torch/nn/modules/module.py", line 1518, in _wrapped_call_impl
return self._call_impl(*args, **kwargs)
File "/opt/conda/lib/python3.10/site-packages/torch/nn/modules/module.py", line 1527, in _call_impl
return forward_call(*args, **kwargs)
File "/kaggle/working/STFGNN/STFGNN-Pytorch-main/model/STFGNN.py", line 420, in forward
x = model(x, self.mask)
File "/opt/conda/lib/python3.10/site-packages/torch/nn/modules/module.py", line 1518, in _wrapped_call_impl
return self._call_impl(*args, **kwargs)
File "/opt/conda/lib/python3.10/site-packages/torch/nn/modules/module.py", line 1527, in _call_impl
return forward_call(*args, **kwargs)
File "/kaggle/working/STFGNN/STFGNN-Pytorch-main/model/STFGNN.py", line 207, in forward
data_left = torch.sigmoid(self.conv1(data_temp))
File "/opt/conda/lib/python3.10/site-packages/torch/nn/modules/module.py", line 1518, in _wrapped_call_impl
return self._call_impl(*args, **kwargs)
File "/opt/conda/lib/python3.10/site-packages/torch/nn/modules/module.py", line 1527, in _call_impl
return forward_call(*args, **kwargs)
File "/opt/conda/lib/python3.10/site-packages/torch/nn/modules/conv.py", line 310, in forward
return self._conv_forward(input, self.weight, self.bias)
File "/opt/conda/lib/python3.10/site-packages/torch/nn/modules/conv.py", line 306, in _conv_forward
return F.conv1d(input, weight, bias, self.stride,
RuntimeError: Expected 2D (unbatched) or 3D (batched) input to conv1d, but got input of size: [64, 64, 358, 12]
然而,我仍然遇到这些问题。下面是我正在使用的主要代码和模型(STFGNN)的片段。您能否就可能出现的问题提供一些指导?
def normalize(a):
mu=np.mean(a,axis=1,keepdims=True)
std=np.std(a,axis=1,keepdims=True)
return (a-mu)/std
Main:
STFGNN:
config = {}
for filename in ["config/PEMS03.json", "config/STFGNN.json"]:
with open(filename, "r") as f:
_config = json.load(f)
for key in _config:
if key not in config:
config[key] = _config[key]
dataset = STFGNNDataset(config)
train_data, valid_data, test_data = dataset.get_data()
data_feature = dataset.get_data_feature()
model_cache_file = 'cache/model_cache/PEMS03_STFGNN.m'
model = STFGNN(config, data_feature)
executor = STFGNNExecutor(config, model)
train = True #标识是否需要重新训练
if train or not os.path.exists(model_cache_file):
executor.train(train_data, valid_data)
executor.save_model(model_cache_file)
else:
executor.load_model(model_cache_file)
# 评估,评估结果将会放在 cache/evaluate_cache 下
executor.evaluate(test_data)
STFGNN:
class gcn_operation(nn.Module):
def __init__(self, adj, in_dim, out_dim, num_vertices, activation='GLU'):
"""
图卷积模块
:param adj: 邻接图
:param in_dim: 输入维度
:param out_dim: 输出维度
:param num_vertices: 节点数量
:param activation: 激活方式 {'relu', 'GLU'}
"""
super(gcn_operation, self).__init__()
self.adj = adj
self.in_dim = in_dim
self.out_dim = out_dim
self.num_vertices = num_vertices
self.activation = activation
assert self.activation in {'GLU', 'relu'}
if self.activation == 'GLU':
self.FC = nn.Linear(self.in_dim, 2 * self.out_dim, bias=True)
else:
self.FC = nn.Linear(self.in_dim, self.out_dim, bias=True)
def forward(self, x, mask=None):
"""
:param x: (3*N, B, Cin)
:param mask:(3*N, 3*N)
:return: (3*N, B, Cout)
"""
adj = self.adj
if mask is not None:
adj = adj.to(mask.device) * mask
x = torch.einsum('nm, mbc->nbc', adj.to(x.device), x) # 4*N, B, Cin
if self.activation == 'GLU':
lhs_rhs = self.FC(x) # 4*N, B, 2*Cout
lhs, rhs = torch.split(lhs_rhs, self.out_dim, dim=-1) # 4*N, B, Cout
out = lhs * torch.sigmoid(rhs)
del lhs, rhs, lhs_rhs
return out
elif self.activation == 'relu':
return torch.relu(self.FC(x)) # 3*N, B, Cout
class STSGCM(nn.Module):
def __init__(self, adj, in_dim, out_dims, num_of_vertices, activation='GLU'):
"""
:param adj: 邻接矩阵
:param in_dim: 输入维度
:param out_dims: list 各个图卷积的输出维度
:param num_of_vertices: 节点数量
:param activation: 激活方式 {'relu', 'GLU'}
"""
super(STSGCM, self).__init__()
self.adj = adj
self.in_dim = in_dim
self.out_dims = out_dims
self.num_of_vertices = num_of_vertices
self.activation = activation
self.gcn_operations = nn.ModuleList()
self.gcn_operations.append(
gcn_operation(
adj=self.adj,
in_dim=self.in_dim,
out_dim=self.out_dims[0],
num_vertices=self.num_of_vertices,
activation=self.activation
)
)
for i in range(1, len(self.out_dims)):
self.gcn_operations.append(
gcn_operation(
adj=self.adj,
in_dim=self.out_dims[i-1],
out_dim=self.out_dims[i],
num_vertices=self.num_of_vertices,
activation=self.activation
)
)
def forward(self, x, mask=None):
"""
:param x: (3N, B, Cin)
:param mask: (3N, 3N)
:return: (N, B, Cout)
"""
need_concat = []
for i in range(len(self.out_dims)):
x = self.gcn_operations[i](x, mask)
need_concat.append(x)
# shape of each element is (1, N, B, Cout)
need_concat = [
torch.unsqueeze(
h[self.num_of_vertices: 2 * self.num_of_vertices], dim=0
) for h in need_concat
]
out = torch.max(torch.cat(need_concat, dim=0), dim=0).values # (N, B, Cout)
del need_concat
return out
class STSGCL(nn.Module):
def __init__(self,
adj,
history,
num_of_vertices,
in_dim,
out_dims,
strides=4,
activation='GLU',
temporal_emb=True,
spatial_emb=True):
"""
:param adj: 邻接矩阵
:param history: 输入时间步长
:param in_dim: 输入维度
:param out_dims: list 各个图卷积的输出维度
:param strides: 滑动窗口步长,local时空图使用几个时间步构建的,默认为3
:param num_of_vertices: 节点数量
:param activation: 激活方式 {'relu', 'GLU'}
:param temporal_emb: 加入时间位置嵌入向量
:param spatial_emb: 加入空间位置嵌入向量
"""
super(STSGCL, self).__init__()
self.adj = adj
self.strides = strides
self.history = history
self.in_dim = in_dim
self.out_dims = out_dims
self.num_of_vertices = num_of_vertices
self.activation = activation
self.temporal_emb = temporal_emb
self.spatial_emb = spatial_emb
self.conv1 = nn.Conv1d(self.in_dim, self.out_dims[-1], kernel_size=(1, 2), stride=(1, 1), dilation=(1, 3))
self.conv2 = nn.Conv1d(self.in_dim, self.out_dims[-1], kernel_size=(1, 2), stride=(1, 1), dilation=(1, 3))
self.STSGCMS = nn.ModuleList()
for i in range(self.history - self.strides + 1):
self.STSGCMS.append(
STSGCM(
adj=self.adj,
in_dim=self.in_dim,
out_dims=self.out_dims,
num_of_vertices=self.num_of_vertices,
activation=self.activation
)
)
if self.temporal_emb:
self.temporal_embedding = nn.Parameter(torch.FloatTensor(1, self.history, 1, self.in_dim))
# 1, T, 1, Cin
if self.spatial_emb:
self.spatial_embedding = nn.Parameter(torch.FloatTensor(1, 1, self.num_of_vertices, self.in_dim))
# 1, 1, N, Cin
self.reset()
def reset(self):
if self.temporal_emb:
nn.init.xavier_normal_(self.temporal_embedding, gain=0.0003)
if self.spatial_emb:
nn.init.xavier_normal_(self.spatial_embedding, gain=0.0003)
def forward(self, x, mask=None):
"""
:param x: B, T, N, Cin
:param mask: (N, N)
:return: B, T-3, N, Cout
"""
if self.temporal_emb:
x = x + self.temporal_embedding
if self.spatial_emb:
x = x + self.spatial_embedding
#############################################
# shape is (B, C, N, T)
data_temp = x.permute(0, 3, 2, 1)
data_left = torch.sigmoid(self.conv1(data_temp))
data_right = torch.tanh(self.conv2(data_temp))
data_time_axis = data_left * data_right
data_res = data_time_axis.permute(0, 3, 2, 1)
# shape is (B, T-3, N, C)
#############################################
need_concat = []
batch_size = x.shape[0]
for i in range(self.history - self.strides + 1):
t = x[:, i: i+self.strides, :, :] # (B, 4, N, Cin)
t = torch.reshape(t, shape=[batch_size, self.strides * self.num_of_vertices, self.in_dim])
# (B, 4*N, Cin)
t = self.STSGCMS[i](t.permute(1, 0, 2), mask) # (4*N, B, Cin) -> (N, B, Cout)
t = torch.unsqueeze(t.permute(1, 0, 2), dim=1) # (N, B, Cout) -> (B, N, Cout) ->(B, 1, N, Cout)
need_concat.append(t)
mid_out = torch.cat(need_concat, dim=1) # (B, T-3, N, Cout)
out = mid_out + data_res
del need_concat, batch_size
return out
class output_layer(nn.Module):
def __init__(self, num_of_vertices, history, in_dim, out_dim,
hidden_dim=128, horizon=12):
"""
预测层,注意在作者的实验中是对每一个预测时间step做处理的,也即他会令horizon=1
:param num_of_vertices:节点数
:param history:输入时间步长
:param in_dim: 输入维度
:param hidden_dim:中间层维度
:param horizon:预测时间步长
"""
super(output_layer, self).__init__()
self.num_of_vertices = num_of_vertices
self.history = history
self.in_dim = in_dim
self.out_dim = out_dim
self.hidden_dim = hidden_dim
self.horizon = horizon
#print("#####################")
#print(self.in_dim)
#print(self.history)
#print(self.hidden_dim)
self.FC1 = nn.Linear(self.in_dim * self.history, self.hidden_dim, bias=True)
#self.FC2 = nn.Linear(self.hidden_dim, self.horizon , bias=True)
self.FC2 = nn.Linear(self.hidden_dim, self.horizon * self.out_dim, bias=True)
def forward(self, x):
"""
:param x: (B, Tin, N, Cin)
:return: (B, Tout, N)
"""
batch_size = x.shape[0]
x = x.permute(0, 2, 1, 3) # B, N, Tin, Cin
out1 = torch.relu(self.FC1(x.reshape(batch_size, self.num_of_vertices, -1)))
# (B, N, Tin, Cin) -> (B, N, Tin * Cin) -> (B, N, hidden)
out2 = self.FC2(out1) # (B, N, hidden) -> (B, N, horizon * 2)
out2 = out2.reshape(batch_size, self.num_of_vertices, self.horizon, self.out_dim)
del out1, batch_size
return out2.permute(0, 2, 1, 3) # B, horizon, N
# return out2.permute(0, 2, 1) # B, horizon, N
class STFGNN(nn.Module):
def __init__(self, config, data_feature):
"""
:param adj: local时空间矩阵
:param history:输入时间步长
:param num_of_vertices:节点数量
:param in_dim:输入维度
:param hidden_dims: lists, 中间各STSGCL层的卷积操作维度
:param first_layer_embedding_size: 第一层输入层的维度
:param out_layer_dim: 输出模块中间层维度
:param activation: 激活函数 {relu, GlU}
:param use_mask: 是否使用mask矩阵对adj进行优化
:param temporal_emb:是否使用时间嵌入向量
:param spatial_emb:是否使用空间嵌入向量
:param horizon:预测时间步长
:param strides:滑动窗口步长,local时空图使用几个时间步构建的,默认为4
"""
super(STFGNN, self).__init__()
self.config = config
self.data_feature = data_feature
self.scaler = data_feature["scaler"]
self.num_batches = data_feature["num_batches"]
adj = self.data_feature["adj_mx"]
history = self.config.get("window", 12)
num_of_vertices = self.config.get("num_nodes", None)
in_dim = self.config.get("input_dim", 1)
out_dim = self.config.get("output_dim", 1)
hidden_dims = self.config.get("hidden_dims", None)
first_layer_embedding_size = self.config.get("first_layer_embedding_size", None)
out_layer_dim = self.config.get("out_layer_dim", None)
activation = self.config.get("activation", "GLU")
use_mask = self.config.get("mask")
temporal_emb = self.config.get("temporal_emb", True)
spatial_emb = self.config.get("spatial_emb", True)
horizon = self.config.get("horizon", 12)
strides = self.config.get("strides", 4)
self.adj = adj
self.num_of_vertices = num_of_vertices
self.hidden_dims = hidden_dims
self.out_layer_dim = out_layer_dim
self.activation = activation
self.use_mask = use_mask
self.temporal_emb = temporal_emb
self.spatial_emb = spatial_emb
self.horizon = horizon
self.strides = 4
self.First_FC = nn.Linear(in_dim, first_layer_embedding_size, bias=True)
self.STSGCLS = nn.ModuleList()
#print("____________________")
#print(history)
self.STSGCLS.append(
STSGCL(
adj=self.adj,
history=history,
num_of_vertices=self.num_of_vertices,
in_dim=first_layer_embedding_size,
out_dims=self.hidden_dims[0],
strides=self.strides,
activation=self.activation,
temporal_emb=self.temporal_emb,
spatial_emb=self.spatial_emb
)
)
in_dim = self.hidden_dims[0][-1]
history -= (self.strides - 1)
#print("!!!!!!!!!!!!!!!!!!!")
#print(history)
for idx, hidden_list in enumerate(self.hidden_dims):
#print("?????? ", idx)
if idx == 0:
continue
#print("---------", idx)
self.STSGCLS.append(
STSGCL(
adj=self.adj,
history=history,
num_of_vertices=self.num_of_vertices,
in_dim=in_dim,
out_dims=hidden_list,
strides=self.strides,
activation=self.activation,
temporal_emb=self.temporal_emb,
spatial_emb=self.spatial_emb
)
)
history -= (self.strides - 1)
in_dim = hidden_list[-1]
self.predictLayer = nn.ModuleList()
#print("***********************")
#print(history)
for t in range(self.horizon):
self.predictLayer.append(
output_layer(
num_of_vertices=self.num_of_vertices,
history=history,
in_dim=in_dim,
out_dim = out_dim,
hidden_dim=out_layer_dim,
horizon=1
)
)
if self.use_mask:
mask = torch.zeros_like(self.adj)
mask[self.adj != 0] = self.adj[self.adj != 0]
self.mask = nn.Parameter(mask)
else:
self.mask = None
def forward(self, x):
"""
:param x: B, Tin, N, Cin)
:return: B, Tout, N
"""
x = torch.relu(self.First_FC(x)) # B, Tin, N, Cin
#print(1)
for model in self.STSGCLS:
x = model(x, self.mask)
# (B, T - 8, N, Cout)
#print(2)
need_concat = []
for i in range(self.horizon):
out_step = self.predictLayer[i](x) # (B, 1, N, 2)
need_concat.append(out_step)
#print(3)
out = torch.cat(need_concat, dim=1) # B, Tout, N, 2
del need_concat
return out
The error message you're seeing, "RuntimeError: Expected 2D (unbatched) or 3D (batched) input to conv1d, but got input of size: [64, 64, 358, 12]", tells us that your 1D convolutional layer (
conv1d
) is receiving a tensor with an unexpected shape. Let's break down why this is happening and how to fix it:
Understanding the Problem
-
1D Convolution Expectation: The
conv1d
layer in PyTorch is designed for input tensors that represent sequences. It expects the input to be in one of these formats:-
2D (Unbatched):
(sequence_length, features)
-
3D (Batched):
(batch_size, sequence_length, features)
-
2D (Unbatched):
-
Your Input Shape: Your error indicates you're feeding a tensor of shape
[64, 64, 358, 12]
toconv1d
. This looks like you have:-
batch_size = 64
-
sequence_length = 358
(or possibly 12, depending on your data's structure) -
features = 64
(or possibly 358 x 12) - An extra dimension: The extra dimension (the second '64') is causing the issue.
-
Solution: Reshaping Your Data
You need to reshape your input tensor to fit the expectations of
conv1d
. Here's the general approach:
- Identify the Correct Dimensions:
-
batch_size
: This is likely 64, as in your error message. -
sequence_length
: Determine the length of your sequences (e.g., time steps in time series data). -
features
: The number of features per time step. -
Reshape Using
view
orreshape
: Use PyTorch's tensor manipulation functions to achieve the desired 3D shape:
```python
# Assuming sequence_length = 358 and features = 64
new_shape = (batch_size, sequence_length, features)
input_tensor = input_tensor.view(new_shape) # or input_tensor.reshape(new_shape)
# ... rest of your model code ... ```
Addressing the Normalization Warning
The warning "RuntimeWarning: invalid value encountered in true_divide" suggests you might have zero values in the standard deviation (
std
) calculation within your
normalize
function. This leads to division by zero errors.
Improved Normalization Function:
def normalize(a):
mu = np.mean(a, axis=1, keepdims=True)
std = np.std(a, axis=1, keepdims=True)
std[std == 0] = 1e-8 # Replace zeros with a small value to avoid division by zero
return (a - mu) / std
Additional Tips:
- Data Understanding: Carefully examine the structure of your input data to confirm you're reshaping it correctly. Use print statements to check the shapes of tensors at different points in your code.
- Debugging: Use a debugger or strategically placed print statements to understand the flow of data through your model and identify the exact point where the shape mismatch occurs.
By reshaping your input tensor and handling potential zero values in your normalization, you'll address the
RuntimeError
and the warning, allowing your STFGNN model to process data as intended.