Download code/mr/model.py from odurugol/vlm3d-beyond-normals: direct link, hf CLI and curl.
- Browser
- Download file 2.61 kB
-
https://huggingface.co/odurugol/vlm3d-beyond-normals/resolve/main/code/mr/model.py
- Command line
-
hf download hf://odurugol/vlm3d-beyond-normals/code/mr/model.py
-
curl -L -o model.py https://huggingface.co/odurugol/vlm3d-beyond-normals/resolve/main/code/mr/model.py
2.61 kB
| """3D ResNet over the 8-channel atlas volume (4 MR types + 4 presence channels).""" | |
| from __future__ import annotations | |
| import math | |
| import torch.nn as nn | |
| def _norm(c: int) -> nn.Module: | |
| # small per-GPU batches: GroupNorm with the largest group count <= 32 that divides c | |
| return nn.GroupNorm(math.gcd(c, 32), c) | |
| class BasicBlock(nn.Module): | |
| def __init__(self, cin: int, cout: int, stride: int = 1): | |
| super().__init__() | |
| self.conv1 = nn.Conv3d(cin, cout, 3, stride, 1, bias=False) | |
| self.n1 = _norm(cout) | |
| self.conv2 = nn.Conv3d(cout, cout, 3, 1, 1, bias=False) | |
| self.n2 = _norm(cout) | |
| self.act = nn.SiLU(inplace=True) | |
| self.down = None | |
| if stride != 1 or cin != cout: | |
| self.down = nn.Sequential(nn.Conv3d(cin, cout, 1, stride, bias=False), _norm(cout)) | |
| def forward(self, x): | |
| idt = x if self.down is None else self.down(x) | |
| x = self.act(self.n1(self.conv1(x))) | |
| x = self.n2(self.conv2(x)) | |
| return self.act(x + idt) | |
| class ResNet3D(nn.Module): | |
| """(B, 8, D, H, W) -> (B, num_classes) logits. Total stride 16 (stride-2 stem + three stride-2 stages).""" | |
| def __init__(self, in_ch: int = 8, num_classes: int = 74, width: int = 48, | |
| blocks: tuple[int, ...] = (2, 2, 2, 2), dropout: float = 0.2): | |
| super().__init__() | |
| w = width | |
| self.stem = nn.Sequential( | |
| nn.Conv3d(in_ch, w, 3, 2, 1, bias=False), _norm(w), nn.SiLU(inplace=True), | |
| nn.Conv3d(w, w, 3, 1, 1, bias=False), _norm(w), nn.SiLU(inplace=True), | |
| ) | |
| stages, cin = [], w | |
| for i, (cout, n) in enumerate(zip([w, w * 2, w * 4, w * 8], blocks)): | |
| layers = [BasicBlock(cin, cout, stride=2 if i > 0 else 1)] | |
| layers += [BasicBlock(cout, cout) for _ in range(n - 1)] | |
| stages.append(nn.Sequential(*layers)) | |
| cin = cout | |
| self.stages = nn.Sequential(*stages) | |
| self.head = nn.Sequential(nn.AdaptiveAvgPool3d(1), nn.Flatten(), nn.Dropout(dropout), | |
| nn.Linear(cin, num_classes)) | |
| for m in self.modules(): | |
| if isinstance(m, nn.Conv3d): | |
| nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu") | |
| def forward(self, x): | |
| return self.head(self.stages(self.stem(x))) | |
| ARCHS = { | |
| "r18": dict(width=48, blocks=(2, 2, 2, 2)), | |
| "r18w": dict(width=64, blocks=(2, 2, 2, 2)), | |
| "r26w": dict(width=64, blocks=(2, 3, 4, 2)), | |
| } | |
| def build_model(arch: str = "r18", **kw) -> ResNet3D: | |
| return ResNet3D(**ARCHS[arch], **kw) | |