What is feat_dict and how is it accessed?

Forgive me for my ignorance, but I can’t seem to figure out what feat_dict is or how to access it in the example on https://doc.dgl.ai/tutorials/basics/5_hetero.html:

    def forward(self, G, feat_dict):
        # The input is a dictionary of node features for each type
        funcs = {}
        for srctype, etype, dsttype in G.canonical_etypes:
            # Compute W_r * h
            Wh = self.weight[etype](feat_dict[srctype])
            # Save it in graph for message passing
            G.nodes[srctype].data['Wh_%s' % etype] = Wh
            # Specify per-relation message passing functions: (message_func, reduce_func).
            # Note that the results are saved to the same destination feature 'h', which
            # hints the type wise reducer for aggregation.
            funcs[etype] = (fn.copy_u('Wh_%s' % etype, 'm'), fn.mean('m', 'h'))
            ...

I don’t see it explicitly used in the forward call:

    def forward(self, G):
        h_dict = self.layer1(G, self.embed)
        ...

I tried modifying the code from that example for my own data but keep getting KeyError: 'h' when I run logits = model(g). I suspect it has something to do with the data from the example having some kind of node or edge data associated with it that my data didn’t add yet (e.g., with something like G.nodes['author'].data['data_type'] = data_tensor).

When I tried to access feat_dict running through the example code (which runs fine for me), I can’t seem to figure out how to get it. I did notice, however, that the “author” nodes in the example code has 2 tensors of data:

>>> G.nodes['author'].data
{'Wh_writing': tensor([[-0.1177,  0.0685,  0.1610,  ...,  0.1161, -0.2778, -0.0606],
        [-0.1201,  0.0551,  0.1668,  ...,  0.1063, -0.2688, -0.0790],
        [-0.1154,  0.0608,  0.1625,  ...,  0.1147, -0.2693, -0.0691],
        ...,
        [-0.1147,  0.0511,  0.1690,  ...,  0.0934, -0.2735, -0.0687],
        [-0.1150,  0.0465,  0.1714,  ...,  0.0888, -0.2744, -0.0668],
        [-0.1069,  0.0594,  0.1608,  ...,  0.1047, -0.2769, -0.0605]],
       grad_fn=<AddmmBackward>), 'h': tensor([[ 0.1134,  0.0760, -0.1769,  ..., -0.0628,  0.2023, -0.2436],
        [ 0.1136,  0.0733, -0.1780,  ..., -0.0668,  0.2072, -0.2475],
        [ 0.1119,  0.0793, -0.1668,  ..., -0.0486,  0.2022, -0.2488],
        ...,
        [ 0.1181,  0.0576, -0.1734,  ..., -0.0683,  0.2064, -0.2453],
        [ 0.1169,  0.0723, -0.1631,  ..., -0.0627,  0.2070, -0.2407],
        [ 0.1130,  0.0713, -0.1882,  ..., -0.0726,  0.1963, -0.2378]],
       grad_fn=<CopyReduceBackward>)}

While my data only has one:

>>> my_g.nodes['author'].data
{'Wh_reviews': tensor([[ 0.2737,  0.2431, -0.0131,  ...,  0.0392,  0.2737,  0.1629],
        [ 0.2742,  0.2428, -0.0123,  ...,  0.0359,  0.2758,  0.1602],
        [ 0.2729,  0.2412, -0.0140,  ...,  0.0388,  0.2742,  0.1618],
        ...,
        [ 0.2744,  0.2422, -0.0111,  ...,  0.0374,  0.2754,  0.1589],
        [ 0.2752,  0.2439, -0.0137,  ...,  0.0381,  0.2743,  0.1613],
        [ 0.2738,  0.2447, -0.0124,  ...,  0.0357,  0.2757,  0.1607]],
       device='cuda:0', grad_fn=<AddmmBackward>)}

But I suspect that’s also because in the example graph “author” appears in two canonical edge types but in my graph “author” only appears once.

Thanks in advance for your tips and helpful feedback!

Here’s a gist of my full code, including functions to load my data and get it into my heterograph object (in case I am correct about the node/edge data part I mentioned above): https://gist.github.com/AlexMRuch/4dee519e47eb489df1cdceab0c68da1d.

Oh, wow – the feat_dict in the example is self.embed #facepalm. Still trying to figure out the missing 'h' key in my data, though.

Yup… G.nodes["author"].data in the example has the following keys: ‘h’, ‘Wh_writing’. my_g.nodes["author"].data in my graph has ‘Wh_reviews’. My other node type, “products”, has the 'h' key, though. I’m guessing the missing 'h' for my authors nodes is probably because I gave my products nodes “political” data, but my authors nodes only have edge data (for review ratings):

>>> my_g.nodes["author"].data
{'Wh_reviews': tensor([[ 0.0929, -0.0324,  0.1405,  ..., -0.0088, -0.0074, -0.0327],
        [ 0.0935, -0.0281,  0.1421,  ..., -0.0119, -0.0028, -0.0319],
        [ 0.0935, -0.0313,  0.1403,  ..., -0.0113, -0.0055, -0.0317],
        ...,
        [ 0.0916, -0.0311,  0.1413,  ..., -0.0115, -0.0055, -0.0308],
        [ 0.0933, -0.0317,  0.1403,  ..., -0.0107, -0.0054, -0.0283],
        [ 0.0926, -0.0295,  0.1420,  ..., -0.0124, -0.0035, -0.0317]],
       grad_fn=<AddmmBackward>)}
>>> my_g.nodes["product"].data
{'h': tensor([[ 0.0436,  0.1830, -0.0364,  ...,  0.1504, -0.0518, -0.3296],
        [ 0.0436,  0.1844, -0.0372,  ...,  0.1489, -0.0502, -0.3308],
        [-0.0486,  0.2140, -0.1773,  ...,  0.1607, -0.0466, -0.2990],
        ...,
        [-0.0488,  0.2140, -0.1771,  ...,  0.1608, -0.0464, -0.2982],
        [-0.0485,  0.2140, -0.1769,  ...,  0.1607, -0.0466, -0.2988],
        [-0.0490,  0.2142, -0.1774,  ...,  0.1611, -0.0457, -0.2990]],
       grad_fn=<SumBackward2>), 'political': tensor([0., 0., 0.,  ..., 0., 0., 0.]), 'Wh_related': tensor([[-0.0487,  0.2143, -0.1771,  ...,  0.1609, -0.0459, -0.2986],
        [-0.0488,  0.2141, -0.1771,  ...,  0.1609, -0.0465, -0.2988],
        [-0.0487,  0.2141, -0.1776,  ...,  0.1605, -0.0459, -0.2990],
        ...,
        [-0.0484,  0.2142, -0.1767,  ...,  0.1612, -0.0462, -0.2991],
        [-0.0494,  0.2141, -0.1777,  ...,  0.1604, -0.0461, -0.2979],
        [-0.0486,  0.2140, -0.1779,  ...,  0.1609, -0.0464, -0.2986]],
       grad_fn=<AddmmBackward>)}

I’m guessing this is why in some examples they assigned nodes with data like degree or one-hot vector (e.g., https://docs.dgl.ai/en/latest/tutorials/basics/1_first.html#step-2-assign-features-to-nodes-or-edges).

I figured out that the error I’m hitting actually has to do with my_g.multi_update_all(funcs, 'sum') only adding 'h' to my_g.nodes["product"].data and not my_g.nodes["author"].data – so return {ntype : g.nodes[ntype].data['h'] for ntype in g.ntypes} hits the KeyError when the “authors” ntype arises.

I’m guessing I need to run this on an undirected version of my network to fix this. Presently, my g.canonical_etypes is

[('author', 'reviews', 'product'), ('product', 'related', 'product')]

I presume 'h' isn’t getting passed to my “authors” nodes here because messages are passes from source to destination nodes – which are “product” nodes in both etypes.

Yikes. What a learning experience! Guess it’s good to know that this script will only work when each ntype is a destination at least once.

For what it’s worth, my problem was caused the the directedness of the graph. Making it undirected solved the problem.

2 Likes