| 7 | warnings.filterwarnings('ignore') |
| 8 | |
| 9 | class GraphEncoder(nn.Module): |
| 10 | |
| 11 | |
| 12 | def __init__(self, out_dim: int, args=None, hidden: int = 128, n_layers: int = 2, dropout: float = 0.1): |
| 13 | super().__init__() |
| 14 | self.out_dim = out_dim |
| 15 | self.hidden = hidden |
| 16 | self.n_layers = n_layers |
| 17 | self.dropout = nn.Dropout(p=dropout) |
| 18 | |
| 19 | self.type_embeddings = nn.ParameterDict() |
| 20 | |
| 21 | self.diag_emb = nn.Embedding(len(DIAGNOSIS_VOCAB) + 1, hidden) |
| 22 | self.desc_emb = nn.Embedding(len(DESCRIPTOR_VOCAB) + 1, hidden) |
| 23 | self.node_norm = nn.LayerNorm(hidden) |
| 24 | |
| 25 | self.convs = nn.ModuleList([ |
| 26 | HeteroGraphConv({}, aggregate='sum') for _ in range(n_layers) |
| 27 | ]) |
| 28 | |
| 29 | self.proj = nn.Linear(hidden, out_dim) |
| 30 | |
| 31 | def _ensure_type_embeddings(self, g: dgl.DGLHeteroGraph, device): |
| 32 | for ntype in g.ntypes: |
| 33 | key = f"emb_{ntype}" |
| 34 | if key not in self.type_embeddings: |
| 35 | param = nn.Parameter(torch.zeros(self.hidden, device=device, dtype=torch.float32)) |
| 36 | nn.init.xavier_uniform_(param.unsqueeze(0)) |
| 37 | self.type_embeddings[key] = param |
| 38 | |
| 39 | def _ensure_convs(self, g: dgl.DGLHeteroGraph, device): |
| 40 | for layer_idx in range(self.n_layers): |
| 41 | conv: HeteroGraphConv = self.convs[layer_idx] |
| 42 | if len(conv.mods) == 0: |
| 43 | mods = {} |
| 44 | for srctype, etype, dsttype in g.canonical_etypes: |
| 45 | mods[etype] = GraphConv(in_feats=self.hidden, out_feats=self.hidden, norm='both', allow_zero_in_degree=True) |
| 46 | conv.mods = nn.ModuleDict(mods) |
| 47 | self.convs[layer_idx] = conv.to(device) |
| 48 | |
| 49 | def _infer_batch_size(self, g: dgl.DGLHeteroGraph) -> int: |
| 50 | if hasattr(g, 'batch_size'): |
| 51 | return g.batch_size |
| 52 | try: |
| 53 | if len(g.ntypes) > 0 and hasattr(g, 'batch_num_nodes'): |
| 54 | base_ntype = g.ntypes[0] |
| 55 | return len(g.batch_num_nodes(base_ntype)) |
| 56 | except Exception: |
| 57 | pass |
| 58 | return 1 |
| 59 | |
| 60 | def _safe_mean_nodes(self, g: dgl.DGLHeteroGraph, ntype: str): |
| 61 | if ntype not in g.ntypes or g.num_nodes(ntype) == 0: |
| 62 | return None |
| 63 | pooled = dgl.mean_nodes(g, 'h', ntype=ntype) |
| 64 | if pooled is None: |
| 65 | return None |
| 66 | pooled = torch.nan_to_num(pooled, nan=0.0, posinf=0.0, neginf=0.0).float() |
nothing calls this directly
no outgoing calls
no test coverage detected