| from typing import Optional |
|
|
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
|
|
|
|
| class InferenceGraphSAGE(nn.Module): |
| def __init__(self, in_dim: int = 768, hidden_dim: int = 256, num_layers: int = 2, dropout: float = 0.1): |
| super().__init__() |
| self.dropout = float(dropout) |
|
|
| self.linears = nn.ModuleList() |
| cur_in = in_dim |
| for _ in range(num_layers): |
| self.linears.append(nn.Linear(cur_in * 2, hidden_dim)) |
| cur_in = hidden_dim |
|
|
| self.class_wheeze = nn.Linear(cur_in, 1) |
| self.class_crackle = nn.Linear(cur_in, 1) |
| self.act = nn.ReLU() |
|
|
| def forward(self, x: torch.Tensor, adj: torch.Tensor): |
| for lin in self.linears: |
| deg = adj.sum(dim=-1, keepdim=True).clamp(min=1.0) |
| neighbor_mean = torch.matmul(adj, x) / deg |
| h = torch.cat([x, neighbor_mean], dim=-1) |
| h = self.act(lin(h)) |
| h = F.dropout(h, p=self.dropout, training=self.training) |
| x = h |
|
|
| wheeze = torch.sigmoid(self.class_wheeze(x).squeeze(-1)) |
| crackle = torch.sigmoid(self.class_crackle(x).squeeze(-1)) |
| return wheeze, crackle |
|
|
|
|
| def load_checkpoint_flexible(model: nn.Module, path: str, map_location: Optional[str] = "cpu") -> nn.Module: |
| ck = torch.load(path, map_location=map_location) |
| if isinstance(ck, dict) and ("state_dict" in ck or "model_state_dict" in ck): |
| state = ck.get("state_dict", ck.get("model_state_dict")) |
| else: |
| state = ck |
|
|
| try: |
| model.load_state_dict(state, strict=False) |
| return model |
| except Exception: |
| ms = model.state_dict() |
| incoming = {} |
| for k, v in state.items(): |
| k2 = k[7:] if k.startswith("module.") else k |
| incoming[k2] = v |
|
|
| resolved = {} |
| for k, v in incoming.items(): |
| if k in ms and ms[k].shape == v.shape: |
| resolved[k] = v |
|
|
| for k_in, v in incoming.items(): |
| if k_in in resolved: |
| continue |
| for k_model, v_model in ms.items(): |
| if k_model in resolved: |
| continue |
| if v_model.shape == v.shape and k_model.endswith(k_in.split(".")[-1]): |
| resolved[k_model] = v |
| break |
|
|
| ms.update(resolved) |
| model.load_state_dict(ms) |
| return model |
|
|