Open scalaboy opened 2 years ago
In general, you can utilize any GNN layer that can handle
edge_attr
as input, see here, e.g., theGINEConv
. You can simply call your layer viax = conv(x, edge_index, edge_attr)
Does that resolve your concerns?
hello, would other conv layers API like GatedGraphConv
, GCNConv
support this edge_attr
parameter?
It seems that in DGL library, GatedGraphConv
support edge_attr
.
I just looked into the DGL version of GatedGraphConv
and it does not look like they support edge features either.
Nonetheless, I'm more than happy to let more GNN layers support edge features. How would you like to add edge feature support to GatedGraphConv
?
I just looked into the DGL version of
GatedGraphConv
and it does not look like they support edge features either.Nonetheless, I'm more than happy to let more GNN layers support edge features. How would you like to add edge feature support to
GatedGraphConv
?
I learn this from devign model, the code is for source code vulnerability detection task by graph network, which is also what I'm doing.
In the vulnerability detection task. There are different types of edge in a graph. For example, control flow edge, data dependence edge. i.e, I hope to use edge_attr
to denote which type of edge it is. Maybe edge_attr
is a simple one-hot vector. If there are 4 types of edge. CFG edge, CPG edge, DDG edge, AST edge. [1, 0, 0, 0] represents the edge is CFG edge and [0, 1, 0, 0] represents CPG edge and so on.
In the code, the author define a GatedGraphConv
by self.ggnn = GatedGraphConv(in_feats=input_dim, out_feats=output_dim, n_steps=num_steps, n_etypes=max_edge_types)
and use like outputs = self.ggnn(graph, features, edge_types)
. There is an additional parameter edge_types
passed into ggnn layer.
I haven't used DGL yet. For now, I would like to support different types of edge by edge_attr
because I haven't seen another way.
I see. Thanks for clarifying. You can either integrate it that by simply concatenating the edge features to x_j
, e.g.:
def message(self, x_j, edge_attr):
return self.lin(torch.cat([x_j, edge_attr], dim=-1))
or by using edge types (similar to how DGL is doing it), e.g., via our HeteroLinear
layer:
self.hetero_lin = HeteroLinear(in_channels, out_channels, num_edge_types)
def message(self, x_j, edge_type):
return self.hetero_lin(x_j, edge_type)
I see. Thanks for clarifying. You can either integrate it that by simply concatenating the edge features to
x_j
, e.g.:def message(self, x_j, edge_attr): return self.lin(torch.cat([x_j, edge_attr], dim=-1))
or by using edge types (similar to how DGL is doing it), e.g., via our
HeteroLinear
layer:self.hetero_lin = HeteroLinear(in_channels, out_channels, num_edge_types) def message(self, x_j, edge_type): return self.hetero_lin(x_j, edge_type)
So, I should change the source code of GatedGraphConv
in torch_geometric.nn.conv
?
For now, the message
method of GatedGraphConv
is
def message(self, x_j: Tensor, edge_weight: OptTensor):
return x_j if edge_weight is None else edge_weight.view(-1, 1) * x_j
And after changing, the source code of message
method of GatedGraphConv
is
def message(self, x_j: Tensor, edge_weight: OptTensor, edge_type):
return self.hetero_lin(x_j, edge_type) if edge_weight is None else edge_weight.view(-1, 1) * self.hetero_lin(x_j, edge_type)
Am I right?
Yes, exactly :)
Yes, exactly :)
OK, thank you very much
I need an example,contains dataset,run fit code,train and test etc. not a api.....
I see. Thanks for clarifying. You can either integrate it that by simply concatenating the edge features to
x_j
, e.g.:def message(self, x_j, edge_attr): return self.lin(torch.cat([x_j, edge_attr], dim=-1))
or by using edge types (similar to how DGL is doing it), e.g., via our
HeteroLinear
layer:self.hetero_lin = HeteroLinear(in_channels, out_channels, num_edge_types) def message(self, x_j, edge_type): return self.hetero_lin(x_j, edge_type)
Hello dear @rusty1s ,
I 'd like to implement my algorithm also by concate the aggregated node feature with the edge feature. Like you mentioned before, torch.cat([x_j, edge_attr]
can be used, but how should I store the edge_attr? and I am still confusing that x_j should be node feature of node j right?
According to the doc Furthermore, tensors passed to [propagate()](https://pytorch-geometric.readthedocs.io/en/latest/modules/nn.html#torch_geometric.nn.conv.MessagePassing.propagate) can be mapped to the respective nodes and by appending _i or _j to the variable name, .e.g. x_i and x_j.
if I want to pass the message torch.cat[x_j,edge_attr]
,it should be return torch.cat([x_j, edge_attr_i], dim=-1)
? But when i print the edge_attr_i,it turns to be the whole edge_attr set,without index
It should be return torch.cat([x_j, edge_attr], dim=-1)
. Since edge_attr
is already an edge-level tensor, there is no need to appeng _i
to it. Now both x_j
and edge_attr
will have shape [num_edges, *]
.
Hello dear @rusty1s :
torch.cat([x_j, edge_attr], dim=-1)
I have the x in shape [25,15],25 is the node number, edge_attr in shape[56,6],56 is the number of edges, and using out = self.propagate(edge_index, x=node, norm=norm, edge_attr = edge_attr) to pass the message, in the message function, the shape of x_j is [81,15],but edge_attr is still[56,6],seems that it passed the whole edge_attr to message function instead of those of a specific edge. did I miss some steps?
It looks like your GNN layer adds self-loops to the graph, that's why you have x_j.size(0) = 25 + 56 = 81
. Can you remove this logic?
It looks like your GNN layer adds self-loops to the graph, that's why you have
x_j.size(0) = 25 + 56 = 81
. Can you remove this logic? Thanks dear @rusty1s :
I think that's the point, so x_j is the aggregated message from node j, then concatenate it with the edge_attr through torch.cat([x_j, edge_attr], dim=-1)
Thanks so much! I think it is solved,One more question,why is the shape of x_j always be 56? I build a parallel network which consist of two exact the same architecture.one passes node feature as x,edge_attr=edge_feature, the other passes edge feature as x, and the edge_att is node feature, but when I print the shape of x_j, both of them are in shape[56,x], in node network it is [56,15],in edge network it is [56,6]. I am a little bit confused.
It is only 56 in case you have 56 edges. The first dimension can be seen as the batch dimension here, as we share model weights across all edges.
It is only 56 in case you have 56 edges. The first dimension can be seen as the batch dimension here, as we share model weights across all edges. Thanks for reply, dear @rusty1s , In my case I have a parallel network,the first one takes node feature as x,which propagate like this:
self.propagate(edge_index, x=node_feature, edge_attr = edge_feature)
,noted as node network,the other one switches node feature and edge feature,in my case takes nodes as edges,edges as nodes,which propagates likeself.propagate(node_index, x=edge_feature, edge_attr = node_feature)
,noted as Edge-network,node feature has a dimension [25,m],edge feature has a dimension of [28,n],but when I print thex_j
in the message function, in both network, the dimension ofx_j
is [56,t], it is originally 25 nodes and 28 edges, I think I my a mistake in build adjacency matrix for Edge-network, I build the adjacency matrix by setting the corresponding position to 1 when two edges connected to the same node. and use coo_matrix to convert it to the adjacency matrix which Pyg takes, but the resulting matrix is in shape[2,78], which is not [2,2N_nodes],can't be used to concatenate the edge feature. maybe it is not a good idea to switch it...
For an edge network, you can take a look at transforms.LineGraph
and compare it to your implementation. It will treat edges as nodes and connects two nodes whenever there exists a node that connects the two edges in the original network. Let me know!
For an edge network, you can take a look at
transforms.LineGraph
and compare it to your implementation. It will treat edges as nodes and connects two nodes whenever there exists a node that connects the two edges in the original network. Let me know!
the original data is like Data(edge_index=[2, 56], edge_attr=[56, 6], node_feature=[25, 27], smiles='N[C@@H](Cc1c[nH]c2ccccc12)C(=O)Nc1ccc2ccccc2c1')
I called LineGraph data_edge = transform(data)
,then the new data is turned to:
Data(edge_index=[2, 134], node_feature=[25, 27], smiles='N[C@@H](Cc1c[nH]c2ccccc12)C(=O)Nc1ccc2ccccc2c1', x=[56, 6], num_nodes=56)
According to the adjacency matrix I caculated before ,I think the new edge_index is right. but I am confused to perform my algorithm throught this.
what I want to do is for a specific edge from node a to node b, the edge ab takes all aggregated edge features from the edges which connected to start node a, attached by its start node feature;
but for the transformed data,it takes edge as node,but according to the edge_index of it, the transformed 'edge' seems to have nothing to do with the real node
Isn't that what is already happening? If you also want to integrate node features into propagation, you can convert them to edge features first:
row, col = data.edge_index
data.edge_attr = torch.cat([data.x[row], data.x[col], data.edge_attr], dim=-1)
data = transform(data)
Isn't that what is already happening? If you also want to integrate node features into propagation, you can convert them to edge features first:
row, col = data.edge_index data.edge_attr = torch.cat([data.x[row], data.x[col], data.edge_attr], dim=-1) data = transform(data)
I think I got the point, thanks for all the help!
hi,all. I have a graph dataset which edge_attr is importance. where I can't get a example about how to train a graph base edge_attr .like mnist_nn_conv.py is not solver my problem