Skip to content

Instantly share code, notes, and snippets.

@masaponto
Created January 4, 2021 00:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save masaponto/2d235fe63c98e659fa69c6b807ed0a9a to your computer and use it in GitHub Desktop.
Save masaponto/2d235fe63c98e659fa69c6b807ed0a9a to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Using backend: pytorch\n"
]
}
],
"source": [
"import dgl\n",
"import torch\n",
"import numpy as np\n",
"\n",
"n_users = 1000\n",
"n_items = 500\n",
"n_follows = 3000\n",
"n_clicks = 5000\n",
"n_dislikes = 500\n",
"n_hetero_features = 10\n",
"n_user_classes = 5\n",
"n_max_clicks = 10\n",
"\n",
"follow_src = np.random.randint(0, n_users, n_follows)\n",
"follow_dst = np.random.randint(0, n_users, n_follows)\n",
"click_src = np.random.randint(0, n_users, n_clicks)\n",
"click_dst = np.random.randint(0, n_items, n_clicks)\n",
"dislike_src = np.random.randint(0, n_users, n_dislikes)\n",
"dislike_dst = np.random.randint(0, n_items, n_dislikes)\n",
"\n",
"hetero_graph = dgl.heterograph({\n",
" ('user', 'follow', 'user'): (follow_src, follow_dst),\n",
" ('user', 'followed-by', 'user'): (follow_dst, follow_src),\n",
" ('user', 'click', 'item'): (click_src, click_dst),\n",
" ('item', 'clicked-by', 'user'): (click_dst, click_src),\n",
" ('user', 'dislike', 'item'): (dislike_src, dislike_dst),\n",
" ('item', 'disliked-by', 'user'): (dislike_dst, dislike_src)})\n",
"\n",
"hetero_graph.nodes['user'].data['feature'] = torch.randn(n_users, n_hetero_features)\n",
"hetero_graph.nodes['item'].data['feature'] = torch.randn(n_items, n_hetero_features)\n",
"hetero_graph.nodes['user'].data['label'] = torch.randint(0, n_user_classes, (n_users,))\n",
"hetero_graph.edges['click'].data['label'] = torch.randint(1, n_max_clicks, (n_clicks,)).float()\n",
"# randomly generate training masks on user nodes and click edges\n",
"hetero_graph.nodes['user'].data['train_mask'] = torch.zeros(n_users, dtype=torch.bool).bernoulli(0.6)\n",
"hetero_graph.edges['click'].data['train_mask'] = torch.zeros(n_clicks, dtype=torch.bool).bernoulli(0.6)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{('item', 'clicked-by', 'user'): {}, ('item', 'disliked-by', 'user'): {}, ('user', 'click', 'item'): {'label': tensor([5., 4., 1., ..., 4., 2., 8.]), 'train_mask': tensor([False, True, True, ..., True, False, False])}, ('user', 'dislike', 'item'): {}, ('user', 'follow', 'user'): {}, ('user', 'followed-by', 'user'): {}}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"hetero_graph.edata"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Graph(num_nodes={'item': 500, 'user': 1000},\n",
" num_edges={('item', 'clicked-by', 'user'): 5000, ('item', 'disliked-by', 'user'): 500, ('user', 'click', 'item'): 5000, ('user', 'dislike', 'item'): 500, ('user', 'follow', 'user'): 3000, ('user', 'followed-by', 'user'): 3000},\n",
" metagraph=[('item', 'user', 'clicked-by'), ('item', 'user', 'disliked-by'), ('user', 'item', 'click'), ('user', 'item', 'dislike'), ('user', 'user', 'follow'), ('user', 'user', 'followed-by')])"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"hetero_graph"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['clicked-by', 'disliked-by', 'click', 'dislike', 'follow', 'followed-by']"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"hetero_graph.etypes"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"torch.Size([1000, 10])"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"hetero_graph.nodes[\"user\"].data[\"feature\"].shape"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"import dgl.nn as dglnn\n",
"import torch\n",
"import torch.nn as nn\n",
"import torch.nn.functional as F"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"class StochasticTwoLayerRGCN(nn.Module):\n",
" def __init__(self, in_feat, hidden_feat, out_feat, rel_names):\n",
" super().__init__()\n",
" self.conv1 = dglnn.HeteroGraphConv({\n",
" rel : dglnn.GraphConv(in_feat, hidden_feat, norm='right')\n",
" for rel in rel_names\n",
" })\n",
" self.conv2 = dglnn.HeteroGraphConv({\n",
" rel : dglnn.GraphConv(hidden_feat, out_feat, norm='right')\n",
" for rel in rel_names\n",
" })\n",
"\n",
" def forward(self, blocks, x):\n",
" x = self.conv1(blocks[0], x)\n",
" x = self.conv2(blocks[1], x)\n",
" return x"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"class ScorePredictor(nn.Module):\n",
" def forward(self, edge_subgraph, x):\n",
" with edge_subgraph.local_scope():\n",
" edge_subgraph.ndata['h'] = x\n",
" for etype in edge_subgraph.canonical_etypes:\n",
" edge_subgraph.apply_edges(\n",
" dgl.function.u_dot_v('h', 'h', 'score'), etype=etype)\n",
" return edge_subgraph.edata['score']\n",
"\n",
"class Model(nn.Module):\n",
" def __init__(self, in_features, hidden_features, out_features, num_classes,\n",
" etypes):\n",
" super().__init__()\n",
" self.rgcn = StochasticTwoLayerRGCN(\n",
" in_features, hidden_features, out_features, etypes)\n",
" self.pred = ScorePredictor()\n",
"\n",
" def forward(self, positive_graph, negative_graph, blocks, x):\n",
" x = self.rgcn(blocks, x)\n",
" pos_score = self.pred(positive_graph, x)\n",
" neg_score = self.pred(negative_graph, x)\n",
" return pos_score, neg_score"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['clicked-by', 'disliked-by', 'click', 'dislike', 'follow', 'followed-by']"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"hetero_graph.etypes"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5000"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"hetero_graph.num_edges(\"click\")"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5000"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"hetero_graph.num_edges(\"clicked-by\")"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"tensor([ 0, 1, 2, ..., 4997, 4998, 4999])"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"torch.arange(hetero_graph.num_edges(\"click\"))"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"train_eid_dict = {\"click\": torch.arange(hetero_graph.num_edges(\"click\")),\n",
" \"clicked-by\": torch.arange(hetero_graph.num_edges(\"clicked-by\")),\n",
" \"dislike\": torch.arange(hetero_graph.num_edges(\"dislike\")),\n",
" \"disliked-by\": torch.arange(hetero_graph.num_edges(\"disliked-by\")),\n",
" \"follow\": torch.arange(hetero_graph.num_edges(\"follow\")),\n",
" \"followed-by\": torch.arange(hetero_graph.num_edges(\"followed-by\"))}\n",
"\n",
"sampler = dgl.dataloading.MultiLayerFullNeighborSampler(2)\n",
"dataloader = dgl.dataloading.EdgeDataLoader(\n",
" hetero_graph, train_eid_dict, sampler,\n",
" exclude='reverse_types',\n",
" reverse_etypes={'click': 'clicked-by', 'clicked-by': 'click', \n",
" \"dislike\": \"disliked-by\", \"disliked-by\":\"dislike\" ,\n",
" \"follow\": \"followed-by\", \"followed-by\": \"follow\"},\n",
" negative_sampler=dgl.dataloading.negative_sampler.Uniform(5),\n",
" batch_size=1024,\n",
" shuffle=True,\n",
" drop_last=False,\n",
" num_workers=4)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"def compute_loss(pos_score, neg_score, canonical_etypes):\n",
" # Margin loss\n",
" all_losses = []\n",
" for given_type in canonical_etypes:\n",
" n_edges = pos_score[given_type].shape[0]\n",
" if n_edges == 0:\n",
" continue\n",
" all_losses.append((1 - neg_score[given_type].view(n_edges, -1) + pos_score[given_type].unsqueeze(1)).clamp(min=0).mean())\n",
" return torch.stack(all_losses, dim=0).mean()\n"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"start\n",
"0 1.6091350317001343\n",
"1 1.3787392377853394\n",
"2 1.1582247018814087\n",
"3 1.1167367696762085\n",
"4 0.9966425895690918\n",
"5 0.9260237812995911\n",
"6 0.9521988034248352\n",
"7 0.856490433216095\n",
"8 0.9148068428039551\n",
"9 0.8671050071716309\n",
"10 0.8588862419128418\n",
"11 0.8536972999572754\n",
"12 0.8532518744468689\n",
"13 0.8321673274040222\n",
"14 0.7932301163673401\n",
"15 0.8196800351142883\n",
"16 0.8536006808280945\n",
"17 0.7718626856803894\n",
"18 0.7967429161071777\n",
"19 0.7815446257591248\n",
"20 0.7920117378234863\n",
"21 0.76273113489151\n",
"22 0.8009796738624573\n",
"23 0.7884901165962219\n",
"24 0.7748621106147766\n",
"25 0.7341139316558838\n",
"26 0.7425366044044495\n",
"27 0.7425730228424072\n",
"28 0.7284682393074036\n",
"29 0.7452893257141113\n",
"30 0.7855545878410339\n",
"31 0.7431566119194031\n",
"32 0.7610621452331543\n",
"33 0.7399067282676697\n",
"34 0.751685619354248\n",
"35 0.7471165657043457\n",
"36 0.7297484278678894\n",
"37 0.7272677421569824\n",
"38 0.7470314502716064\n",
"39 0.6765744686126709\n",
"40 0.7257323861122131\n",
"41 0.6735861301422119\n",
"42 0.7471463084220886\n",
"43 0.6931560635566711\n",
"44 0.698866605758667\n",
"45 0.7085348963737488\n",
"46 0.7206322550773621\n",
"47 0.7228112816810608\n",
"48 0.7575018405914307\n",
"49 0.7389569282531738\n"
]
}
],
"source": [
"in_features = 10\n",
"hidden_features = 50\n",
"out_features = 10\n",
"num_classes = None\n",
"etypes = hetero_graph.etypes\n",
"\n",
"model = Model(in_features, hidden_features, out_features, num_classes, etypes)\n",
"#model = model.cuda()\n",
"opt = torch.optim.Adam(model.parameters())\n",
"\n",
"print(\"start\")\n",
"i= 0\n",
"epoch = 50\n",
"for i in range(epoch):\n",
" for input_nodes, positive_graph, negative_graph, blocks in dataloader:\n",
"\n",
" #blocks = [b.to(torch.device('cuda')) for b in blocks]\n",
" # positive_graph = positive_graph.to(torch.device('cuda'))\n",
" #negative_graph = negative_graph.to(torch.device('cuda'))\n",
" input_features = blocks[0].srcdata['feature']\n",
" \n",
" pos_score, neg_score = model(positive_graph, negative_graph, blocks, input_features)\n",
" loss = compute_loss(pos_score, neg_score, hetero_graph.canonical_etypes)\n",
" opt.zero_grad()\n",
" loss.backward()\n",
" opt.step()\n",
" \n",
" print(i, loss.item())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"n_users = 1000\n",
"n_apps = 1000\n",
"n_shops = 1000\n",
"\n",
"n_appuses = 5000\n",
"n_pays = 3000\n",
"n_hetero_features = 10\n",
"\n",
"####\n",
"#n_users = 3\n",
"#n_apps = 10\n",
"#n_shops = 10\n",
"\n",
"#n_appuses = 6\n",
"#n_pays = 5\n",
"#n_hetero_features = 10\n",
"####\n",
"\n",
"\n",
"appuse_src = np.random.randint(0, n_users, n_appuses)\n",
"appuse_dst = np.random.randint(0, n_apps, n_appuses)\n",
"pay_src = np.random.randint(0, n_users, n_shops)\n",
"pay_dst = np.random.randint(0, n_pays, n_shops)\n",
"\n",
"\n",
"user_graph = dgl.heterograph({\n",
" ('user', 'appuse', 'app'): (appuse_src, appuse_dst),\n",
" ('app', 'usedby', 'user'): (appuse_dst, appuse_src),\n",
" \n",
" # ('user', 'pay', 'shop'): (pay_src, pay_dst),\n",
" #('shop', 'payedby', 'user'): (pay_dst, pay_src)\n",
"})\n",
"\n",
"user_graph.nodes['user'].data['feature'] = torch.randn(n_users, n_hetero_features)\n",
"user_graph.nodes['app'].data['feature'] = torch.randn(n_apps, n_hetero_features)\n",
"#user_graph.nodes['shop'].data['feature'] = torch.randn(n_shops, n_hetero_features)\n",
"\n",
"#hetero_graph.nodes['user'].data['label'] = torch.randint(0, n_user_classes, (n_users,))\n",
"#hetero_graph.edges['click'].data['label'] = torch.randint(1, n_max_clicks, (n_clicks,)).float()\n",
"# randomly generate training masks on user nodes and click edges\n",
"#hetero_graph.nodes['user'].data['train_mask'] = torch.zeros(n_users, dtype=torch.bool).bernoulli(0.6)\n",
"#hetero_graph.edges['click'].data['train_mask'] = torch.zeros(n_clicks, dtype=torch.bool).bernoulli(0.6)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Graph(num_nodes={'app': 1000, 'user': 1000},\n",
" num_edges={('app', 'usedby', 'user'): 5000, ('user', 'appuse', 'app'): 5000},\n",
" metagraph=[('app', 'user', 'usedby'), ('user', 'app', 'appuse')])"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"user_graph"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['usedby', 'appuse']"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"user_graph.etypes"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"train_eid_dict = {\"appuse\": torch.arange(user_graph.num_edges(\"appuse\")),\n",
" \"usedby\": torch.arange(user_graph.num_edges(\"usedby\")),\n",
" #\"pay\": torch.arange(user_graph.num_edges(\"pay\")),\n",
" #\"payedby\": torch.arange(user_graph.num_edges(\"payedby\"))\n",
" }\n",
"\n",
"sampler = dgl.dataloading.MultiLayerFullNeighborSampler(2)\n",
"dataloader = dgl.dataloading.EdgeDataLoader(\n",
" user_graph, train_eid_dict, sampler,\n",
"\n",
" exclude='reverse_types',\n",
" reverse_etypes={'appuse': 'usedby', 'usedby' :'appuse', \n",
" #\"pay\": \"payedby\", \"payedby\": \"pay\",\n",
" #\"follow\": \"followed-by\", \"followed-by\": \"follow\"\n",
" },\n",
" negative_sampler=dgl.dataloading.negative_sampler.Uniform(5),\n",
" batch_size=1024,\n",
" shuffle=True,\n",
" drop_last=False,\n",
" num_workers=4)"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[('app', 'usedby', 'user'), ('user', 'appuse', 'app')]"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"user_graph.canonical_etypes"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"start\n",
"===blocks\n",
"Block(num_src_nodes={'app': 997, 'user': 997},\n",
" num_dst_nodes={'app': 989, 'user': 994},\n",
" num_edges={('app', 'usedby', 'user'): 4478, ('user', 'appuse', 'app'): 4479},\n",
" metagraph=[('app', 'user', 'usedby'), ('user', 'app', 'appuse')])\n",
"Block(num_src_nodes={'app': 989, 'user': 994},\n",
" num_dst_nodes={'app': 791, 'user': 757},\n",
" num_edges={('app', 'usedby', 'user'): 3528, ('user', 'appuse', 'app'): 3653},\n",
" metagraph=[('app', 'user', 'usedby'), ('user', 'app', 'appuse')])\n",
"===positive\n",
"Graph(num_nodes={'app': 791, 'user': 757},\n",
" num_edges={('app', 'usedby', 'user'): 507, ('user', 'appuse', 'app'): 517},\n",
" metagraph=[('app', 'user', 'usedby'), ('user', 'app', 'appuse')])\n",
"===negative\n",
"Graph(num_nodes={'app': 791, 'user': 757},\n",
" num_edges={('app', 'usedby', 'user'): 507, ('user', 'appuse', 'app'): 517},\n",
" metagraph=[('app', 'user', 'usedby'), ('user', 'app', 'appuse')])\n",
"0 0.9861730337142944\n",
"1 0.98542320728302\n",
"2 0.9932470321655273\n",
"3 0.9804465770721436\n",
"4 0.9751787185668945\n",
"5 0.9605979919433594\n",
"6 0.9659152030944824\n",
"7 0.9588730335235596\n",
"8 0.9457771182060242\n",
"9 0.9311240911483765\n",
"10 0.9498293399810791\n",
"11 0.9599283933639526\n",
"12 0.9256765246391296\n",
"13 0.9242886900901794\n",
"14 0.9002523422241211\n",
"15 0.9019483327865601\n",
"16 0.9321237802505493\n",
"17 0.9701608419418335\n",
"18 0.9530501961708069\n",
"19 0.9441486597061157\n",
"20 0.9451694488525391\n",
"21 0.9319849014282227\n",
"22 0.9293418526649475\n",
"23 0.9047495126724243\n",
"24 0.9070008993148804\n",
"25 0.9395350217819214\n",
"26 0.9406099319458008\n",
"27 0.9003127813339233\n",
"28 0.9299486875534058\n",
"29 0.9051764011383057\n",
"30 0.9139848947525024\n",
"31 0.8974647521972656\n",
"32 0.9230623245239258\n",
"33 0.940125584602356\n",
"34 0.9325525760650635\n",
"35 0.9404858350753784\n",
"36 0.9220913648605347\n",
"37 0.949921190738678\n",
"38 0.9244025945663452\n",
"39 0.9241498708724976\n",
"40 0.9190338253974915\n",
"41 0.9269808530807495\n",
"42 0.8995455503463745\n",
"43 0.9594205617904663\n",
"44 0.9050592184066772\n",
"45 0.9337030649185181\n",
"46 0.9489398002624512\n",
"47 0.8954428434371948\n",
"48 0.9267255663871765\n",
"49 0.9470422267913818\n",
"50 0.9353700876235962\n",
"51 0.9405981302261353\n",
"52 0.8956066370010376\n",
"53 0.932508111000061\n",
"54 0.9318647384643555\n",
"55 0.9398298859596252\n",
"56 0.9388526082038879\n",
"57 0.9342505931854248\n",
"58 0.9066287279129028\n",
"59 0.9176690578460693\n",
"60 0.922206699848175\n",
"61 0.9074845910072327\n",
"62 0.9343910217285156\n",
"63 0.9248802661895752\n",
"64 0.9088714122772217\n",
"65 0.9249729514122009\n",
"66 0.9145314693450928\n",
"67 0.9406793117523193\n",
"68 0.891103208065033\n",
"69 0.9191558361053467\n",
"70 0.8796985149383545\n",
"71 0.9460409879684448\n",
"72 0.889326810836792\n",
"73 0.9306645393371582\n",
"74 0.9206209778785706\n",
"75 0.9006335735321045\n",
"76 0.9094328880310059\n",
"77 0.9488924145698547\n",
"78 0.9614546298980713\n",
"79 0.9205442667007446\n",
"80 0.9324371814727783\n",
"81 0.9419518709182739\n",
"82 0.9413037300109863\n",
"83 0.9125614166259766\n",
"84 0.9074254631996155\n",
"85 0.9183988571166992\n",
"86 0.9366220235824585\n",
"87 0.9232483506202698\n",
"88 0.9524273872375488\n",
"89 0.9082925319671631\n",
"90 0.9233659505844116\n",
"91 0.924187421798706\n",
"92 0.9294463396072388\n",
"93 0.9238454103469849\n",
"94 0.921082615852356\n",
"95 0.9481202960014343\n",
"96 0.9158851504325867\n",
"97 0.8704276084899902\n",
"98 0.9221229553222656\n",
"99 0.8926336765289307\n"
]
}
],
"source": [
"in_features = 10\n",
"hidden_features = 50\n",
"out_features = 10\n",
"num_classes = None\n",
"etypes = user_graph.etypes\n",
"\n",
"model = Model(in_features, hidden_features, out_features, num_classes, etypes)\n",
"#model = model.cuda()\n",
"opt = torch.optim.Adam(model.parameters())\n",
"\n",
"print(\"start\")\n",
"i= 0\n",
"epoch = 100\n",
"for i in range(epoch):\n",
" for j, (input_nodes, positive_graph, negative_graph, blocks) in enumerate(dataloader):\n",
" if i == 0 and j == 0:\n",
" \n",
" print(\"===blocks\")\n",
" print(blocks[0])\n",
" print(blocks[1])\n",
" print(\"===positive\")\n",
" print(positive_graph)\n",
" print(\"===negative\")\n",
" print(negative_graph)\n",
" #blocks = [b.to(torch.device('cuda')) for b in blocks]\n",
" # positive_graph = positive_graph.to(torch.device('cuda'))\n",
" #negative_graph = negative_graph.to(torch.device('cuda'))\n",
" input_features = blocks[0].srcdata['feature']\n",
" \n",
" pos_score, neg_score = model(positive_graph, negative_graph, blocks, input_features)\n",
" #loss = compute_loss(pos_score, neg_score, [('user', 'appuse', 'app')])\n",
" loss = compute_loss(pos_score, neg_score, user_graph.canonical_etypes)\n",
" opt.zero_grad()\n",
" loss.backward()\n",
" opt.step()\n",
" \n",
" print(i, loss.item())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 332,
"metadata": {},
"outputs": [],
"source": [
"\n",
"n_users = 3\n",
"n_apps = 10\n",
"n_shops = 10\n",
"\n",
"n_appuses = 20\n",
"n_pays = 5\n",
"n_hetero_features = 10\n",
"\n",
"appuse_src = np.random.randint(0, n_users, n_appuses)\n",
"appuse_dst = np.random.randint(0, n_apps, n_appuses)\n",
"pay_src = np.random.randint(0, n_users, n_shops)\n",
"pay_dst = np.random.randint(0, n_pays, n_shops)\n",
"\n",
"\n",
"user_graph = dgl.heterograph({\n",
" ('user', 'appuse', 'app'): (appuse_src, appuse_dst),\n",
" ('app', 'usedby', 'user'): (appuse_dst, appuse_src),\n",
" \n",
" # ('user', 'pay', 'shop'): (pay_src, pay_dst),\n",
" #('shop', 'payedby', 'user'): (pay_dst, pay_src)\n",
"})\n",
"\n",
"user_graph.nodes['user'].data['feature'] = torch.randn(n_users, n_hetero_features)\n",
"user_graph.nodes['user'].data['id'] = torch.arange(n_users)\n",
"\n",
"user_graph.nodes['app'].data['feature'] = torch.randn(n_apps, n_hetero_features)\n",
"user_graph.nodes['app'].data['id'] = torch.arange(n_apps)\n",
"\n",
"\n",
"#user_graph.nodes['shop'].data['feature'] = torch.randn(n_shops, n_hetero_features)\n",
"\n",
"#hetero_graph.nodes['user'].data['label'] = torch.randint(0, n_user_classes, (n_users,))\n",
"#hetero_graph.edges['click'].data['label'] = torch.randint(1, n_max_clicks, (n_clicks,)).float()\n",
"# randomly generate training masks on user nodes and click edges\n",
"#hetero_graph.nodes['user'].data['train_mask'] = torch.zeros(n_users, dtype=torch.bool).bernoulli(0.6)\n",
"#hetero_graph.edges['click'].data['train_mask'] = torch.zeros(n_clicks, dtype=torch.bool).bernoulli(0.6)"
]
},
{
"cell_type": "code",
"execution_count": 333,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['app', 'user']"
]
},
"execution_count": 333,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"user_graph.ntypes"
]
},
{
"cell_type": "code",
"execution_count": 334,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Graph(num_nodes={'app': 10, 'user': 3},\n",
" num_edges={('app', 'usedby', 'user'): 20, ('user', 'appuse', 'app'): 20},\n",
" metagraph=[('app', 'user', 'usedby'), ('user', 'app', 'appuse')])"
]
},
"execution_count": 334,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"user_graph"
]
},
{
"cell_type": "code",
"execution_count": 335,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(tensor([9]), tensor([1]))"
]
},
"execution_count": 335,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"user_graph.find_edges(1, 'usedby')"
]
},
{
"cell_type": "code",
"execution_count": 336,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(tensor([2]), tensor([1]))"
]
},
"execution_count": 336,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"user_graph.find_edges(3, 'usedby')"
]
},
{
"cell_type": "code",
"execution_count": 337,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
]
},
"execution_count": 337,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"user_graph.nodes('app')"
]
},
{
"cell_type": "code",
"execution_count": 338,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"tensor([0, 1, 2])"
]
},
"execution_count": 338,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"user_graph.nodes('user')"
]
},
{
"cell_type": "code",
"execution_count": 339,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"NodeSpace(data={'feature': tensor([[-0.0763, 0.2470, 0.2405, -0.9275, 1.1873, 0.7811, -0.7117, -0.0335,\n",
" 0.6010, -1.5579],\n",
" [-2.9575, 0.1453, 1.3627, 1.2785, -1.4339, 0.8969, -0.2493, 0.3634,\n",
" -0.3548, 0.6159],\n",
" [ 0.2646, 1.0808, 1.6442, -0.0442, 1.2733, 0.6496, 0.4562, -0.2015,\n",
" -0.7876, 0.7678]]), 'id': tensor([0, 1, 2])})"
]
},
"execution_count": 339,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"user_graph.nodes['user']"
]
},
{
"cell_type": "code",
"execution_count": 340,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"tensor([0, 1, 2])"
]
},
"execution_count": 340,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"user_graph.nodes('user')"
]
},
{
"cell_type": "code",
"execution_count": 345,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'_TYPE': tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1]), '_ID': tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2])}"
]
},
"execution_count": 345,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dgl.to_homogeneous(user_graph).ndata"
]
},
{
"cell_type": "code",
"execution_count": 346,
"metadata": {},
"outputs": [],
"source": [
"import networkx as nx\n",
"homo_G = dgl.to_homogeneous(user_graph, ndata=['id'])\n",
"nx_G = homo_G.to_networkx(node_attrs=['id']).to_undirected()\n",
"pos = nx.kamada_kawai_layout(nx_G)"
]
},
{
"cell_type": "code",
"execution_count": 369,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"NodeDataView({0: {'id': tensor(0)}, 1: {'id': tensor(1)}, 2: {'id': tensor(2)}, 3: {'id': tensor(3)}, 4: {'id': tensor(4)}, 5: {'id': tensor(5)}, 6: {'id': tensor(6)}, 7: {'id': tensor(7)}, 8: {'id': tensor(8)}, 9: {'id': tensor(9)}, 10: {'id': tensor(0)}, 11: {'id': tensor(1)}, 12: {'id': tensor(2)}})"
]
},
"execution_count": 369,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"nx_G.nodes(data=True)"
]
},
{
"cell_type": "code",
"execution_count": 372,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(0, {'id': tensor(0)})\n",
"(1, {'id': tensor(1)})\n",
"(2, {'id': tensor(2)})\n",
"(3, {'id': tensor(3)})\n",
"(4, {'id': tensor(4)})\n",
"(5, {'id': tensor(5)})\n",
"(6, {'id': tensor(6)})\n",
"(7, {'id': tensor(7)})\n",
"(8, {'id': tensor(8)})\n",
"(9, {'id': tensor(9)})\n",
"(10, {'id': tensor(0)})\n",
"(11, {'id': tensor(1)})\n",
"(12, {'id': tensor(2)})\n"
]
}
],
"source": [
"for x in nx_G.nodes(data=True):\n",
" print(x)"
]
},
{
"cell_type": "code",
"execution_count": 377,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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7jkcDjIyMjhx4gRPPPGE6TgNqOMTERGP+Oabb3A6nSxbtozExETTcX6iwiciIh6za9curr/+erZt28bgwYNNxwG01SkiIh7kdDpJT09nypQpFBcXm44DqOMTEREvSEtLIzc3ly1btvz0DD838DGQB5QCocClwDg825Wp8ImIiMfV1NQwYcIEhg4dymNPPcVqYDHwHVYBrAECfvgKBtKAmcCvPJBFhU9ERLyiuLiYy267jZL163EHBnKmmbXBWEVwA3C1zTlU+ERExCt2A3G1tZQ5HNDCs33BwBvARBtzqPCJiIjHnQQGAd+34b0hwKfAf9mURVOdIiLicc8DZU1dLC6GpCQIDYWoKHj99XqXK4D/szGLOj4REfGoauBimun2pk+HujpYsQL27oXrr4e//x1+ce4vCCgALrIhjzo+ERHxqM1YU5uNOnMG1q+HhQshLAx++1uYPBlWr663rBPwsk15VPhERMSjdgGnm7r45ZcQEAD9+//82tChcOBAvWVlwHab8qjwiYiIR/27uYsuF4SH138tPBxONyyVdt33RYVPREQ8KrS5i2FhUFpa/7XSUujatcHSEJvyqPCJiIhH9QECm7rYvz/U1MA///nza//4R73BFrCK1aU25dFUp4iIeFQBVtGqbGrBzTeDwwGZmdZU58SJDaY6Q7E+4xthQx51fCIi4lG9gATA0dSCjAwoL4cePayjDX/6U4OOLxJ7ih6o4xMRES/YAUygmUPszQgFMoDbbMqijk9ERDwuDriX1g+oBAPXAbfamEUdn4iIeIUbmAOspGWdXwjWkxmygQtszKGOT0REvMIBLANeAKKBMBr/3K8r0AN4HOuxRHYWPVDHJyIiBriBncASYD/gwtrW7Af8N3At1vP4PEGFT0RE/Iq2OkVExK+o8ImIiF9R4RMREb+iwiciIn5FhU9ERPyKCp+IiPgVFT4REfErKnwiIuJXVPhERMSvqPCJiIhf+X8iCYwvjZmF2AAAAABJRU5ErkJggg==\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"\n",
"cls1color = '#00FFFF'\n",
"cls2color = '#FF00FF'\n",
"colors = [cls1color if n == 0 else cls2color for n in homo_G.ndata[\"_TYPE\"]]\n",
"\n",
"label_dic = {k: int(v[\"id\"]) for k, v in nx_G.nodes(data=True)}\n",
"\n",
"nx.draw(nx_G, pos, with_labels=True, node_color=colors, labels=label_dic)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 390,
"metadata": {},
"outputs": [],
"source": [
"def show_graph(hetero_g):\n",
" homo_G = dgl.to_homogeneous(hetero_g, ndata=['id'])\n",
" nx_G = homo_G.to_networkx(node_attrs=['id']).to_undirected()\n",
" pos = nx.kamada_kawai_layout(nx_G)\n",
" print(hetero_g.nodes(\"user\"))\n",
" print(hetero_g.nodes(\"app\"))\n",
"\n",
" cls1color = '#00FFFF'\n",
" cls2color = '#FF00FF'\n",
" colors = [cls1color if n == 0 else cls2color for n in homo_G.ndata[\"_TYPE\"]]\n",
" \n",
" label_dic = {k: int(v[\"id\"]) for k, v in nx_G.nodes(data=True)}\n",
" \n",
" nx.draw(nx_G, pos, with_labels=True, node_color=colors, \n",
" labels=label_dic\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": 391,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"tensor([0, 1, 2])\n",
"tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])\n"
]
},
{
"data": {
"image/png": "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7jkcDjIyMjhx4gRPPPGE6TgNqOMTERGP+Oabb3A6nSxbtozExETTcX6iwiciIh6za9curr/+erZt28bgwYNNxwG01SkiIh7kdDpJT09nypQpFBcXm44DqOMTEREvSEtLIzc3ly1btvz0DD838DGQB5QCocClwDg825Wp8ImIiMfV1NQwYcIEhg4dymNPPcVqYDHwHVYBrAECfvgKBtKAmcCvPJBFhU9ERLyiuLiYy267jZL163EHBnKmmbXBWEVwA3C1zTlU+ERExCt2A3G1tZQ5HNDCs33BwBvARBtzqPCJiIjHnQQGAd+34b0hwKfAf9mURVOdIiLicc8DZU1dLC6GpCQIDYWoKHj99XqXK4D/szGLOj4REfGoauBimun2pk+HujpYsQL27oXrr4e//x1+ce4vCCgALrIhjzo+ERHxqM1YU5uNOnMG1q+HhQshLAx++1uYPBlWr663rBPwsk15VPhERMSjdgGnm7r45ZcQEAD9+//82tChcOBAvWVlwHab8qjwiYiIR/27uYsuF4SH138tPBxONyyVdt33RYVPREQ8KrS5i2FhUFpa/7XSUujatcHSEJvyqPCJiIhH9QECm7rYvz/U1MA///nza//4R73BFrCK1aU25dFUp4iIeFQBVtGqbGrBzTeDwwGZmdZU58SJDaY6Q7E+4xthQx51fCIi4lG9gATA0dSCjAwoL4cePayjDX/6U4OOLxJ7ih6o4xMRES/YAUygmUPszQgFMoDbbMqijk9ERDwuDriX1g+oBAPXAbfamEUdn4iIeIUbmAOspGWdXwjWkxmygQtszKGOT0REvMIBLANeAKKBMBr/3K8r0AN4HOuxRHYWPVDHJyIiBriBncASYD/gwtrW7Af8N3At1vP4PEGFT0RE/Iq2OkVExK+o8ImIiF9R4RMREb+iwiciIn5FhU9ERPyKCp+IiPgVFT4REfErKnwiIuJXVPhERMSvqPCJiIhf+X8iCYwvjZmF2AAAAABJRU5ErkJggg==\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"show_graph(user_graph)"
]
},
{
"cell_type": "code",
"execution_count": 392,
"metadata": {},
"outputs": [],
"source": [
"train_eid_dict = {\"appuse\": torch.arange(user_graph.num_edges(\"appuse\")),\n",
" \"usedby\": torch.arange(user_graph.num_edges(\"usedby\")),\n",
" #\"pay\": torch.arange(user_graph.num_edges(\"pay\")),\n",
" #\"payedby\": torch.arange(user_graph.num_edges(\"payedby\"))\n",
" }\n",
"\n",
"sampler = dgl.dataloading.MultiLayerFullNeighborSampler(2)\n",
"\n",
"dataloader = dgl.dataloading.EdgeDataLoader(\n",
" user_graph, train_eid_dict, sampler,\n",
"\n",
" exclude='reverse_types',\n",
" reverse_etypes={'appuse': 'usedby', 'usedby' :'appuse', \n",
" #\"pay\": \"payedby\", \"payedby\": \"pay\",\n",
" #\"follow\": \"followed-by\", \"followed-by\": \"follow\"\n",
" },\n",
" negative_sampler=dgl.dataloading.negative_sampler.Uniform(1),\n",
" batch_size=4,\n",
" shuffle=True,\n",
" drop_last=False,\n",
" num_workers=4)"
]
},
{
"cell_type": "code",
"execution_count": 393,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<dgl.dataloading.pytorch.EdgeDataLoader at 0x7f9e044ff4d0>"
]
},
"execution_count": 393,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dataloader"
]
},
{
"cell_type": "code",
"execution_count": 406,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Graph(num_nodes={'app': 6, 'user': 2},\n",
" num_edges={('app', 'usedby', 'user'): 1, ('user', 'appuse', 'app'): 3},\n",
" metagraph=[('app', 'user', 'usedby'), ('user', 'app', 'appuse')])\n",
"tensor([0, 1])\n",
"tensor([0, 1, 2, 3, 4, 5])\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"for i, (input_nodes, positive_graph, negative_graph, blocks) in enumerate(dataloader):\n",
" \n",
" if i == 1:\n",
" break\n",
" \n",
" print(positive_graph)\n",
" show_graph(positive_graph)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 407,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Graph(num_nodes={'app': 4, 'user': 2},\n",
" num_edges={('app', 'usedby', 'user'): 2, ('user', 'appuse', 'app'): 2},\n",
" metagraph=[('app', 'user', 'usedby'), ('user', 'app', 'appuse')])"
]
},
"execution_count": 407,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#print(negative_graph)\n",
"negative_graph\n",
"#show_graph(negative_graph)"
]
},
{
"cell_type": "code",
"execution_count": 408,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Block(num_src_nodes={'app': 9, 'user': 3},\n",
" num_dst_nodes={'app': 8, 'user': 3},\n",
" num_edges={('app', 'usedby', 'user'): 18, ('user', 'appuse', 'app'): 17},\n",
" metagraph=[('app', 'user', 'usedby'), ('user', 'app', 'appuse')]), Block(num_src_nodes={'app': 8, 'user': 3},\n",
" num_dst_nodes={'app': 4, 'user': 2},\n",
" num_edges={('app', 'usedby', 'user'): 12, ('user', 'appuse', 'app'): 9},\n",
" metagraph=[('app', 'user', 'usedby'), ('user', 'app', 'appuse')])]\n"
]
}
],
"source": [
"print(blocks)"
]
},
{
"cell_type": "code",
"execution_count": 410,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Block(num_src_nodes={'app': 9, 'user': 3},\n",
" num_dst_nodes={'app': 8, 'user': 3},\n",
" num_edges={('app', 'usedby', 'user'): 18, ('user', 'appuse', 'app'): 17},\n",
" metagraph=[('app', 'user', 'usedby'), ('user', 'app', 'appuse')])\n"
]
},
{
"ename": "DGLError",
"evalue": "Expect number of features to match number of nodes (len(u)). Got 23 and 24 instead.",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mDGLError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-410-e4efec1adcc6>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mblocks\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mshow_graph\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mblocks\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-390-4059b7a7946d>\u001b[0m in \u001b[0;36mshow_graph\u001b[0;34m(hetero_g)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mshow_graph\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mhetero_g\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mhomo_G\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdgl\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto_homogeneous\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mhetero_g\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mndata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'id'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mnx_G\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mhomo_G\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto_networkx\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode_attrs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'id'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto_undirected\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mpos\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkamada_kawai_layout\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnx_G\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mhetero_g\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnodes\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"user\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/opt/conda/lib/python3.7/site-packages/dgl/convert.py\u001b[0m in \u001b[0;36mto_homogeneous\u001b[0;34m(G, ndata, edata)\u001b[0m\n\u001b[1;32m 666\u001b[0m \u001b[0mcomb_ef\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcombine_frames\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mG\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_edge_frames\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mG\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0metypes\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcol_names\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0medata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 667\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcomb_nf\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 668\u001b[0;31m \u001b[0mretg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndata\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcomb_nf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 669\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcomb_ef\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 670\u001b[0m \u001b[0mretg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0medata\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcomb_ef\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/opt/conda/lib/python3.7/_collections_abc.py\u001b[0m in \u001b[0;36mupdate\u001b[0;34m(*args, **kwds)\u001b[0m\n\u001b[1;32m 839\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mMapping\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 840\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mkey\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 841\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 842\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"keys\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 843\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mkey\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkeys\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/opt/conda/lib/python3.7/site-packages/dgl/view.py\u001b[0m in \u001b[0;36m__setitem__\u001b[0;34m(self, key, val)\u001b[0m\n\u001b[1;32m 79\u001b[0m \u001b[0;34m'The HeteroNodeDataView has only one node type. '\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m\\\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 80\u001b[0m \u001b[0;34m'please pass a tensor directly'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 81\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_graph\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_set_n_repr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_ntid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_nodes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0mkey\u001b[0m \u001b[0;34m:\u001b[0m \u001b[0mval\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 82\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 83\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__delitem__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/opt/conda/lib/python3.7/site-packages/dgl/heterograph.py\u001b[0m in \u001b[0;36m_set_n_repr\u001b[0;34m(self, ntid, u, data)\u001b[0m\n\u001b[1;32m 3807\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mnfeats\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mnum_nodes\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3808\u001b[0m raise DGLError('Expect number of features to match number of nodes (len(u)).'\n\u001b[0;32m-> 3809\u001b[0;31m ' Got %d and %d instead.' % (nfeats, num_nodes))\n\u001b[0m\u001b[1;32m 3810\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mF\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcontext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mval\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3811\u001b[0m raise DGLError('Cannot assign node feature \"{}\" on device {} to a graph on'\n",
"\u001b[0;31mDGLError\u001b[0m: Expect number of features to match number of nodes (len(u)). Got 23 and 24 instead."
]
}
],
"source": [
"print(blocks[0])\n",
"show_graph(blocks[0])"
]
},
{
"cell_type": "code",
"execution_count": 280,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Block(num_src_nodes={'app': 9, 'user': 3},\n",
" num_dst_nodes={'app': 6, 'user': 3},\n",
" num_edges={('app', 'usedby', 'user'): 17, ('user', 'appuse', 'app'): 8},\n",
" metagraph=[('app', 'user', 'usedby'), ('user', 'app', 'appuse')])\n",
"{'_TYPE': tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3]), '_ID': tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2])}\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAb4AAAEuCAYAAADx63eqAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAgAElEQVR4nOzdd3yT5drA8V+6N7TQArKRqYiAoKIsQYagjLoZoriYogcU9YjoAV9QQVE4cDhHBREUlY2ACChDlkwZMpQpMksLpXQmed4/7rY2bZImbdIn4/ry6Qea505ytbS5cq/rNmiapiGEEEL4iQC9AxBCCCHKkiQ+IYQQfkUSnxBCCL8iiU8IIYRfkcQnhBDCr0jiE0II4Vck8QkhhPArkviEEEL4FUl8Qggh/EqQ3gEIIYQ/MQGHgcuAAagINEB6IWVJEp8QQpSBi8B/gY+ATCAw93YjEAP8AxgIxOkSnX8xSK1OIYRwHw0YD/xf7ueZNtpFAGbgPWB4GcTlzyTxCSGEm2jAIGAukO7gfSKAF4AJ7gpKyLCyEEK4y0ScS3rktv0YmOmWiARI4hNCCLe4CvyLYpLe779DWBj062dxczrwMraHRUXpSOITQgg3+BwHXmCHDoWWLa1e0oAFLo5JKJL4hBDCxTTgfYrp7c2fD+XLQ8eOVi+noYZKhetJ4hNCCBc7DyTZa5CaCm++CZMn232cwzg3PygcI4lPCCFcLBkIttdgzBh4+mmoXt3u44QAKS6MSyiygV0IIVws0N7FvXth7VrYs6fYx9GQF2l3kO+pEEK4WDyQZevi+vVw8iTUqKE+T0sDkwl++w1277ZoagRi3Ral/5IN7EII4Qa3AbutXUhPV3N8eSZNUolwxgyIj7doei+wxn0h+i3p8QkhhBu8iqq9mVb4QkSE+sgTFaX28hVKelHAaPeG6LekxyeEEG6QAyQAV0p4/xuAP5EViO4g31MhhHCDYGAeEF6C+4YD85EXaHeR76sQQrhJN2AGziW/CFTCbOOWiARI4hNCCLcaACwFqmsahrQ0DFZmlwxAJHAj8APQu0wj9D+S+IQQws06AR8sWsTNr73GfQYDIagXXwMQikp0a4Dfgbv1C9NvyKpOIYQoAx9NmcLYESN4KPfz7Ny/Q/QKyI9J4hNCCDfbtWsXp0+fplevXvm3ScLTjwx1CiGEm3300UcMGzaMoCDpa3gC2ccnhBBudP78eW666SaOHTtGbKwUIPME0uMTQgg3mjFjBo8++qgkPQ8iPT4hhHCTrKwsatasyU8//USjRo30Dkfkkh6fEEK4yfz582natKkkPQ8jM61uYAJWArOAs6ijReKAHsATQIx+oQkhyoimaUyZMoUJEyboHYooRBKfC2UAk4GPUGdxXSt0fQvwMvAYMBaoVZbBCSHK1KZNm8jIyKBz5856hyIKkTk+F7kEdERVXsgspm0g6siR74E73RyXEEIfiYmJdOrUicGDB+sdiihEEp8LpAEtgOOoo0gcFYnqBTZxR1BCCN2cOHGCli1bcurUKSIjI/UORxQii1tc4HngFFaSXr9+UKUKxMRA/frwyScWl68DnVFzgEII3zFt2jSeeuopSXoeSnp8pXQZqIaN4c2DB6FuXQgNhcOHoX17WLECbrstv0k08DlSjV0IX5GWlkbNmjXZvXs3NWvW1DscYYX0+ErpU1SFdatuvlklPQCDQX0cO2bR5BrwrhvjE0KUrc8//5x77rlHkp4Hk8RXSlNRqzltGjIEIiKgYUM17NmtW5Emv6KGSoUQ3s1sNvPRRx/x4osv6h2KsEMSXymdL67B9Olw7Rps2gSJiX/3AAsIRRKfEL7g+++/Jzo6mrvvllP1PJkkvlIw4+DClMBAaN0azpyBGTOsNrnuysCEELqYMmUKI0aMwGCwOQEiPIAkvlIIwMkztYzGInN8eaSaixDe7eDBg+zfv59HH31U71BEMSTxlVJdWxcuXoT58yEtDUwmWL0avvoKOnQo0jQTqO/OIIUQbvfxxx8zaNAgQq1MZwjPItsZSmk2MBy1id3CpUvw0EPw669gNkPNmvDCC/DssxbNDMADwNKyCFYI4RaXL1+mbt26HD58mEqVKukdjiiGJL5SygASsJL4HBQJrALauCwiIURZmzhxIocPH2b27Nl6hyIcIInPBcYAH+L8ApVg4GZgN3b2AgohPFpOTg516tRh2bJlNGvWTO9whANkjs8F3gbuAcKduE8QUAFYjSQ9IbzZ4sWLqVOnjiQ9LyKJzwUCgEVAT9TQZXEM6enUBnahhkmFEN4rbwuD8B6S+FwkGPgSlQA7AmGojel5AlFJsUZODtqIEfzfypXcUPZhCiFc6JdffuHcuXP07NlT71CEE2SOz01Oo5LgeSAbiAfao87fe+D++9m1axfnzp3TL0AhRKn17duX5s2bM3LkSL1DEU6QxKeDK1euULFiRaZMmcKwYcP0DkcIUQJnz56lcePGHD9+nPLly+sdjnCCDHXqoHz58jz77LO8+uqrmM1mvcMRQpTA9OnT6dOnjyQ9LyQ9Pp0YjUZiYmIYPHgwkydP1jscIYQTMjMzqVmzJps2baJ+fam75G2kx6eToKAg3n77baZOnUpaWkm3vwsh9PDll1/SokULSXpeSnp8OouPj6d9+/Z8++23eocihHCApmnceuutTJo0ic6dO+sdjigB6fHpbMaMGSxatIgzZ87oHYoQwgHr16/HaDTSqVMnvUMRJSQ9Pg9Qr149KlWqxM8//6x3KEKIYvTs2ZNu3brx/PPP6x2KKCFJfB5g+/bttGrVil9++YUWLVroHY4QwoZjx45x5513curUKSIiIvQOR5SQJD4P0apVK5KTkzly5IjeoQghbHjxxRcJCwtj4sSJeociSkESn4c4ffo0tWrVYuHChfTu3VvvcIQQhaSmplK7dm327t1L9erV9Q5HlIIsbvEQNWrUoFevXgwaNEjvUIQQVsyaNYt7771Xkp4PkMTnQebMmUNycjIffPCB3qEIIQowmUxMnTpVTmHwEZL4PEhUVBRDhgxhzJgxGI1GvcMRQuRauXIlcXFxtGrVSu9QhAtI4vMwkydPxmAw8NJLL+kdihAiV96ZewaDHBvtCyTxeZigoCDGjx/Pf/7zH1JTU/UORwi/t3//fg4fPszDDz+sdyjCRWRVp4eqVKkSrVq1YsmSJXqHIoRfe+aZZ6hVqxZvvPGG3qEIF5HE56GWLl1K7969OXbsGLVr19Y7HCH80qVLl6hfvz5Hjx4lPj5e73CEi0ji82ANGzakXLlybN++Xe9QhPBL77zzDsePH+fTTz/VOxThQpL4PNju3btp0aIFmzdvltVkQpSxnJwcatWqxapVq2jSpIne4QgXksUtHqx58+a0bt2afv366R2KEH5nwYIFNGjQQJKeD5LE5+Hmz5/PyZMn+frrr/UORQi/kreFQfgeGer0Ao899hhr164lKSlJ71CE8Avbtm2jb9++HD16lMDAQL3DES4mPT4v8Nlnn5GamsqECRP0DkUIvzBlyhSGDx8uSc9HSY/PS7zyyitMnTqVa9euERQUpHc4QvisM2fO0KRJE06cOEG5cuX0Dke4gfT4vMTEiRMJCgpi2LBheocihE/797//Tf/+/SXp+TDp8XmRGTNmMHz4cJKSkihfvrze4Qjhc9LT06lZsyZbt26lbt26eocj3ER6fF5k8ODBJCQk0LdvX71DEcInzZs3j1atWknS83GS+LzMp59+yqpVq/j999/1DkUIn6JpGh999JFsYfADMtTphRo3bkxYWBg7d+7UOxQhfMbatWt56aWX2Ldvnxw/5OOkx+eF5s+fz+7du9m4caPeoQjhvQq95Zcz9/yH9Pi8VIcOHTh+/DgnT57UOxQhvEM2sBB4HzgEZAFBQAIkPZxEmy/asPvP3YSHh+sZpSgDkvi81Pnz56latSqzZs3iiSee0DscITyXBrwLTMj997WiTbIDszFgIDgxGP4HyE4GnyaJz4v169ePFStWcPnyZQICZNRaiCJMQD9gGZDuQPtQoCrwM1DFjXEJXcmrpRf75JNPSE9PZ9y4cXqHIoRnGo7jSQ/U8OdpoD1We4bCN0ji82JhYWGMGjWKCRMmkJ2drXc4QniWLcDnFEl605hGC1oQSihP8mTR+xmBU4C8n/RZMtTp5cxmM7GxsSQmJjJr1iy9wxHCcyQCSyiyenMRiwgggNWsJoMMZjPb+v1jgIuo4U/hU6TH5+UCAgKYMmUKc+bMkWOLhMhzCVhFkaQHkEgivehFBSrYfwwNWOSG2ITuJPH5gKeeeoqqVavSp08fvUMRwjMsA0p7otA14DMXxCI8jiQ+HzF79mzWrl3LoUOH9A5FCP1dADJd8DjnXPAYwuNI4vMRHTp0oEmTJjz22GN6hyKE/oyA2UWPI3yOJD4f8tVXX7F//37WrVundyhC6EbTNM5nnccYWPqstevELrp06cL48eM5cOCAC6ITnkASnw9p1KgRnTp14sknn9Q7FCHKjMlkYvfu3UyZMoXExEQSEhJ46vOnMGkmq+2NGMkkE1Pun0wyMVrp2hlDjPx1619cvnyZDz74gCZNmhAYGEhCQgJt2rThlVdeYePGjZjNruhairIk2xl8TFJSEpUrV2bmzJk8/fTTeocjhMtlZWWxc+dONm7cyMaNG9myZQvVqlWjTZs2tG3bljZt2lC9enVoCvxa9P5v8RZv87bFbWMZy1u8ZdkwFLWZPUF9ajab2bp1K8uWLWPr1q0cOXKEpKQkNE2jXLly1KlTh5YtW3LffffRpUsXwsLC3PDVC1eQxOeDBg4cyMKFC0lJSZFSZl4uC7gChKDKR/rj/+b169fZunUrGzduZNOmTezYsYMGDRrQtm1b2rZtS+vWrYmPjy9yv8NvHaba29WIIsr5JzUAvXBoO8PBgwdZtmwZGzZs4LfffuP8+fPk5OQQGRlJjRo1aNasGffeey89e/YkLi7O+ViEy0ni80HZ2dnExMQwcuRI3nnnHb3DEU7KAOYD7wFHUUnPjNpW1gMYBdyBem32RSkpKfz8889s2rSJjRs3sn//fpo1a5af6O666y5iYmJs3t9oNNKvXz8Wf72Yw1GHqZVVC0OOk9+tKGAH0LBkX8PZs2dZsmQJP/74I/v27ePPP/8kMzOT0NBQqlatyi233EL79u3p3bs3NWvWLNmTiBKTxOej/vWvf/HOO+9w9epVGXLxEhrwATAWldTSrLQJAMJRdZQXALeUWXTuc/78+fwkt3HjRo4fP86dd96ZP2x5xx13OHxU0I8//siDDz6I0Wjkq6++4v477ofbUNsbHK3qFwEsBzqU7OuxJTU1leXLl7NmzRp2797NyZMnuXbtGkFBQVSqVIlGjRrRunVrevbsSdOmTV375MKCJD4fZTabqVixIt26dWPu3Ll6hyOKoQGDgS9wvJ5yJLASaOuuoNxA0zROnTqVn+Q2btxIUlISrVu3zk90zZs3Jzg42KnHNRqNPProoyxevJgHHniAb7/9lpCQEHXxMtANOIj65tp6xYsGglHf1DtK+AU6KTs7m7Vr17Jq1Sq2b9/OsWPHSElJwWAwEBcXR/369bnzzjvp3r077du3l6kLF5HE58Pmzp3LgAED+Ouvv6hcubLe4Qg7xgKTcDzp5YkCtgM3uTwi19A0jcOHD+cnuU2bNpGdnZ0/bNm2bVsaN25cqhf0NWvW8NBDDwHwzTff0KVLFyuBAFtR3+SVQMFBkGygPjAaVd9T59qcZrOZHTt2sGzZMjZv3syRI0e4dOkSJpOJcuXKUbt2bVq0aEHXrl257777iIiI0DdgLySJz8fVqlWL2rVr89NPP+kdirDhNNAAG4VG2reHbdsgKEh9XrUqHDmSf9kAtAI2uztIB5lMJn799df8JLdp0yYiIyMtEl3dunUxGEo/Q5mdnc2DDz7IihUr6N27N19//TVBed8ne5KAI8BV1LBmdeDGUofjdkeOHGHp0qVs2LCBAwcOcP78ebKzs4mIiKBGjRo0bdqUjh070qtXLypWrKh3uB5NEp+P27hxI+3bt2fv3r00adJE73CEFa8AH2FjCqp9e+jXD555xub9w4D9QF13BFeMvK0FeXN0W7Zs4YYbbshPcvlbC1zsu+++4/HHHycoKIiFCxfSoYOLJ+S8xPnz51m6dCnr1q3j119/5c8//yQjI4PQ0FCqVKlC48aNad++PYmJidSuXVvvcIvYifrZ34Oa045Edb5fQB2J6K4FXJL4/ECLFi3IyMjg4MGDeociCslCbRNLtdXAgcQXDAwCPnZ1cFZcv36dbdu25Q9dOrq1wFUyMzPp1asXP/zwA48++ihffPGFY708P5KWlsaKFStYvXo1u3bt4uTJk6SmpuZvvm/UqBF33303PXr0oHnz5rrMGy4E3kCNdmRiWV3OgEqAscDrwPO4PgFK4vMDx44do169enz33Xd069ZN73BEARuBBygm8R08CJoGDRrAO++o2wqpjHvqKaekpLB58+b8RHfgwAGaNm2av1n8rrvuoly5cm545qIWL15Mv379CAsLY+nSpbRu3bpMntcXGI1G1q1bx8qVK9m+fTt//PEHycnJAMTFxVG3bl1atWpFt27duOeee9z2ZkJDTaX+G8fmsyNQW3jmoN7guYokPj/RvXt39uzZw9mzZ/UORRSwCHgKO4lv+3a46SYICYH582HYMNi7F260nJQKx/mFMdZY21pwxx135PfonNla4Crp6en06NGDH3/8kf79+zNr1ixZ3egCZrOZ3bt3s3TpUjZv3szhw4e5ePEiJpOJmJgYatWqxW233UbXrl25//77XbKIZiwwGbjuxH0iUGuO5uC6np8kPj9x5coVKlasyMcff8yQIUP0DscvGYEVwBrUtrIQ1AvAWpx4IejaFbp3h+HDLW4OQ218d0bBrQV5ye7ixYv5Wwvatm1boq0FrvTNN9/w5JNPEhkZyXfffccdd5TRPgM/duzYMZYsWcL69evZv38/586dIzs7m/DwcKpXr86tt95Kx44d6d27NwkJCQ4/7hagE1beoE2bBrNnw/798Pjj6t+FRAKfAK46e0YSnx8ZPHgwc+fO5erVq/KOuQxdRg3tfIxawHKtwLUAnDw957771McLL1jcHA9cLOaumqZx5MgRiz10rt5a4CppaWl0796dTZs2MXDgQP773/96RFz+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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"print(blocks[1])\n",
"show_graph(blocks[1])"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [],
"source": [
"n_users = 1000\n",
"n_apps = 1000\n",
"n_shops = 1000\n",
"\n",
"n_appuses = 5000\n",
"n_pays = 3000\n",
"\n",
"appuse_src = np.random.randint(0, n_users, n_appuses)\n",
"appuse_dst = np.random.randint(0, n_apps, n_appuses)\n",
"pay_src = np.random.randint(0, n_users, n_shops)\n",
"pay_dst = np.random.randint(0, n_shops, n_shops)\n",
"\n",
"user_graph = dgl.heterograph({\n",
" ('user', 'appuse', 'app'): (appuse_src, appuse_dst),\n",
" ('app', 'usedby', 'user'): (appuse_dst, appuse_src),\n",
" ('user', 'pay', 'shop'): (pay_src, pay_dst),\n",
" ('shop', 'payedby', 'user'): (pay_dst, pay_src)\n",
"})\n",
"\n",
"user_graph.nodes['user'].data['feature'] = torch.randn(n_users, n_hetero_features)\n",
"user_graph.nodes['app'].data['feature'] = torch.randn(n_apps, n_hetero_features)\n",
"user_graph.nodes['shop'].data['feature'] = torch.randn(n_shops, n_hetero_features)\n",
"\n",
"#hetero_graph.nodes['user'].data['label'] = torch.randint(0, n_user_classes, (n_users,))\n",
"#hetero_graph.edges['click'].data['label'] = torch.randint(1, n_max_clicks, (n_clicks,)).float()\n",
"# randomly generate training masks on user nodes and click edges\n",
"#hetero_graph.nodes['user'].data['train_mask'] = torch.zeros(n_users, dtype=torch.bool).bernoulli(0.6)\n",
"#hetero_graph.edges['click'].data['train_mask'] = torch.zeros(n_clicks, dtype=torch.bool).bernoulli(0.6)"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Graph(num_nodes={'app': 1000, 'shop': 1000, 'user': 1000},\n",
" num_edges={('app', 'usedby', 'user'): 5000, ('shop', 'payedby', 'user'): 1000, ('user', 'appuse', 'app'): 5000, ('user', 'pay', 'shop'): 1000},\n",
" metagraph=[('app', 'user', 'usedby'), ('user', 'app', 'appuse'), ('user', 'shop', 'pay'), ('shop', 'user', 'payedby')])"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"user_graph"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [],
"source": [
"train_eid_dict = {\"appuse\": torch.arange(user_graph.num_edges(\"appuse\")),\n",
" \"usedby\": torch.arange(user_graph.num_edges(\"usedby\")),\n",
" \"pay\": torch.arange(user_graph.num_edges(\"pay\")),\n",
" \"payedby\": torch.arange(user_graph.num_edges(\"payedby\"))\n",
" }\n",
"\n",
"sampler = dgl.dataloading.MultiLayerFullNeighborSampler(2)\n",
"dataloader = dgl.dataloading.EdgeDataLoader(\n",
" user_graph, train_eid_dict, sampler,\n",
" exclude='reverse_types',\n",
" reverse_etypes={'appuse': 'usedby', 'usedby' :'appuse', \n",
" \"pay\": \"payedby\", \"payedby\": \"pay\",\n",
" #\"follow\": \"followed-by\", \"followed-by\": \"follow\"\n",
" },\n",
" negative_sampler=dgl.dataloading.negative_sampler.Uniform(5),\n",
" batch_size=1024,\n",
" shuffle=True,\n",
" drop_last=False,\n",
" num_workers=4)"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"start\n",
"0 1.0042177438735962\n",
"1 0.9913958311080933\n",
"2 0.9594790935516357\n",
"3 0.9345883727073669\n",
"4 0.9463564157485962\n",
"5 0.8870338201522827\n",
"6 0.8888425230979919\n",
"7 0.8598660826683044\n",
"8 0.8556907176971436\n",
"9 0.8531943559646606\n",
"10 0.8615914583206177\n",
"11 0.8251054286956787\n",
"12 0.8400945663452148\n",
"13 0.8354456424713135\n",
"14 0.8102348446846008\n",
"15 0.814399778842926\n",
"16 0.802259624004364\n",
"17 0.8318575620651245\n",
"18 0.7923575043678284\n",
"19 0.8325405716896057\n",
"20 0.8271968960762024\n",
"21 0.8054969906806946\n",
"22 0.8147201538085938\n",
"23 0.7913467288017273\n",
"24 0.7965254783630371\n",
"25 0.7753124237060547\n",
"26 0.7986363768577576\n",
"27 0.8199523687362671\n",
"28 0.7809064984321594\n",
"29 0.7893482446670532\n",
"30 0.7893662452697754\n",
"31 0.7680327296257019\n",
"32 0.8147146701812744\n",
"33 0.8083506226539612\n",
"34 0.7754764556884766\n",
"35 0.7904293537139893\n",
"36 0.7884194850921631\n",
"37 0.8081418871879578\n",
"38 0.7823575735092163\n",
"39 0.8116244673728943\n",
"40 0.8163399696350098\n",
"41 0.7872242331504822\n",
"42 0.7838910222053528\n",
"43 0.7678976655006409\n",
"44 0.7785499095916748\n",
"45 0.7835447788238525\n",
"46 0.7548676133155823\n",
"47 0.772226095199585\n",
"48 0.7840331792831421\n",
"49 0.7743468284606934\n",
"50 0.7767171263694763\n",
"51 0.7715997099876404\n",
"52 0.7703453302383423\n",
"53 0.7826955318450928\n",
"54 0.7683325409889221\n",
"55 0.7519842386245728\n",
"56 0.7937618494033813\n",
"57 0.7870801687240601\n",
"58 0.7822519540786743\n",
"59 0.7406740784645081\n",
"60 0.7858375310897827\n",
"61 0.7813568115234375\n",
"62 0.7796692252159119\n",
"63 0.737754762172699\n",
"64 0.7549033761024475\n",
"65 0.7692295908927917\n",
"66 0.7557080388069153\n",
"67 0.7579880952835083\n",
"68 0.7367973327636719\n",
"69 0.770021915435791\n",
"70 0.7784019708633423\n",
"71 0.8126579523086548\n",
"72 0.7770805358886719\n",
"73 0.7531522512435913\n",
"74 0.7973572015762329\n",
"75 0.7904865145683289\n",
"76 0.769869327545166\n",
"77 0.7686647772789001\n",
"78 0.7522403001785278\n",
"79 0.7502648830413818\n",
"80 0.7981303334236145\n",
"81 0.7639758586883545\n",
"82 0.7369908690452576\n",
"83 0.760507345199585\n",
"84 0.7594852447509766\n",
"85 0.7753238081932068\n",
"86 0.8034272789955139\n",
"87 0.7260960340499878\n",
"88 0.783507227897644\n",
"89 0.7923946976661682\n",
"90 0.7648294568061829\n",
"91 0.8086757063865662\n",
"92 0.7398483157157898\n",
"93 0.7571491599082947\n",
"94 0.7814540863037109\n",
"95 0.7343556880950928\n",
"96 0.7834877967834473\n",
"97 0.7532148361206055\n",
"98 0.7671966552734375\n",
"99 0.7945557832717896\n"
]
}
],
"source": [
"in_features = 10\n",
"hidden_features = 50\n",
"out_features = 10\n",
"num_classes = None\n",
"etypes = user_graph.etypes\n",
"\n",
"model = Model(in_features, hidden_features, out_features, num_classes, etypes)\n",
"#model = model.cuda()\n",
"opt = torch.optim.Adam(model.parameters())\n",
"\n",
"print(\"start\")\n",
"i= 0\n",
"epoch = 100\n",
"for i in range(epoch):\n",
" for input_nodes, positive_graph, negative_graph, blocks in dataloader:\n",
"\n",
" #blocks = [b.to(torch.device('cuda')) for b in blocks]\n",
" # positive_graph = positive_graph.to(torch.device('cuda'))\n",
" #negative_graph = negative_graph.to(torch.device('cuda'))\n",
" input_features = blocks[0].srcdata['feature']\n",
" \n",
" pos_score, neg_score = model(positive_graph, negative_graph, blocks, input_features)\n",
" loss = compute_loss(pos_score, neg_score, user_graph.canonical_etypes)\n",
" opt.zero_grad()\n",
" loss.backward()\n",
" opt.step()\n",
" \n",
" print(i, loss.item())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment