dmlc / dgl

Python package built to ease deep learning on graph, on top of existing DL frameworks.
http://dgl.ai
Apache License 2.0
13.45k stars 3.01k forks source link

Issues in converting block to homo graph to use HGTConv #4482

Open Fiffy-lin opened 2 years ago

Fiffy-lin commented 2 years ago

šŸ› Bug

Hi, there.

I wish to use HGTConv on blocks generated by dataloader.

My graph actually has 2 type of nodes, named A and B. It seems HGTConv accepts only homo graph input, as it will assign node embedding tensor (not dict) directly to graph ndata.

So i tried to convert my block to homo graph using dgl.to_homogeneous(), but then it has 4 node types and doubled node numbers. Tries to make it work, as node numbers get doubled, i need to concat twice my node input like h = torch.cat([h, h]) and adjust num_ntypes = num_ntypes * 2, which is quite weird.

I know in a block source nodes and dest nodes are marked differently, but i have no clue how can i use HGTConv properly when input is a block.

Any help is appreciated.

To Reproduce

import torch
from dgl.nn.pytorch.conv import HGTConv

src = torch.randint(0, 1000, (100000,)).int()
dst = torch.randint(0, 1000, (100000,)).int()

hg = {('A', 'AB', 'B'): (src, dst),
      ('B', 'BA', 'A'): (dst, src)}
hg = dgl.heterograph(hg)

sampler = dgl.dataloading.MultiLayerNeighborSampler([10, 5])
sampler = dgl.dataloading.as_edge_prediction_sampler(
    sampler, negative_sampler=dgl.dataloading.negative_sampler.Uniform(5))
edge_indices = {etype: torch.arange(0, end=hg.num_edges(etype), dtype=torch.int32) for etype in hg.canonical_etypes}

hg.ndata['x'] = {'A': torch.rand(1000, 64), 'B': torch.rand(1000, 64)}

loader = dgl.dataloading.DataLoader(
        hg, edge_indices, sampler,
        batch_size=1024,
        shuffle=False,
        drop_last=False,
        num_workers=0)

hgt_layer = HGTConv(in_size=64,
                   head_size=16,
                   num_heads=4,
                   num_ntypes=4,  # 4 works, not 2
                   num_etypes=4, 
                   dropout=0.2,
                   use_norm=False)

for input_nodes, positive_graph, negative_graph, blocks in loader:
    input_features = blocks[0].srcdata['x']
    block = dgl.to_homogeneous(blocks[0])
    x = torch.cat(list(input_features.values()), dim=0)
    x = hgt_layer(g=block, ntype=block.ndata[dgl.NTYPE], etype=block.edata[dgl.ETYPE], x=torch.cat([x, x]), presorted=True) # weird

Expected behavior

Environment

Additional context

jermainewang commented 2 years ago

Unfortunately, HGTConv currently does not support bipartite graph, i.e., where the source nodes and destination nodes are separated (may have different quantity or feature dimensions). This is a reasonable ask and should be implemented in my opinion. For example, HGTConv should allow input to be a pair of tensors (x_src, x_dst).

For the other question to convert a heterogeneous DGLBlock graph to homogeneous, it is indeed not easy at the moment. Let me back to you later once I have time.

Fiffy-lin commented 2 years ago

Unfortunately, HGTConv currently does not support bipartite graph, i.e., where the source nodes and destination nodes are separated (may have different quantity or feature dimensions). This is a reasonable ask and should be implemented in my opinion. For example, HGTConv should allow input to be a pair of tensors (x_src, x_dst).

For the other question to convert a heterogeneous DGLBlock graph to homogeneous, it is indeed not easy at the moment. Let me back to you later once I have time.

Much thanks for your reply, gonna try other implementations