import torch import torch.nn as nn import torch.nn.functional as F CONFIG = { "model": { "in_channels": 4, "out_channels": 4, "base_channels": 32, "embed_dim": 768, "num_heads": 12, "depth": 6, "patch_size": 16, } } class RotaryEmbedding(nn.Module): def __init__(self, dim, max_seq_len=20000): super().__init__() inv_freq = 1.0 / (10000 ** (torch.arange(0, dim, 2).float() / dim)) self.register_buffer("inv_freq", inv_freq) self.max_seq_len = max_seq_len self.cached_cos = None self.cached_sin = None def forward(self, x, seq_len): if seq_len > self.max_seq_len: self.max_seq_len = seq_len self.cached_cos = None if self.cached_cos is None or self.cached_cos.size(2) < seq_len: t = torch.arange(self.max_seq_len, device=x.device, dtype=self.inv_freq.dtype) freqs = torch.einsum("i,j->ij", t, self.inv_freq) emb = torch.cat([freqs, freqs], dim=-1) self.cached_cos = emb.cos()[None, None, :, :] self.cached_sin = emb.sin()[None, None, :, :] return self.cached_cos[:, :, :seq_len, :], self.cached_sin[:, :, :seq_len, :] def rotate_half(x): x1, x2 = x.chunk(2, dim=-1) return torch.cat([-x2, x1], dim=-1) def apply_rotary_pos_emb(q, k, cos, sin): return (q * cos + rotate_half(q) * sin), (k * cos + rotate_half(k) * sin) class RoPESelfAttention(nn.Module): def __init__(self, dim, num_heads=8, dropout=0.1): super().__init__() self.num_heads = num_heads self.head_dim = dim // num_heads self.scale = self.head_dim ** -0.5 self.qkv = nn.Linear(dim, dim * 3) self.proj = nn.Linear(dim, dim) self.rope = RotaryEmbedding(self.head_dim) def forward(self, x): B, N, C = x.shape qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4) q, k, v = qkv[0], qkv[1], qkv[2] cos, sin = self.rope(q, seq_len=N) q, k = apply_rotary_pos_emb(q, k, cos, sin) attn = (q @ k.transpose(-2, -1)) * self.scale attn = attn.softmax(dim=-1) x = (attn @ v).transpose(1, 2).reshape(B, N, C) return self.proj(x) class TransformerBlock(nn.Module): def __init__(self, dim, num_heads, dropout=0.1): super().__init__() self.norm1 = nn.LayerNorm(dim) self.attn = RoPESelfAttention(dim, num_heads, dropout) self.norm2 = nn.LayerNorm(dim) self.mlp = nn.Sequential( nn.Linear(dim, dim * 4), nn.GELU(), nn.Linear(dim * 4, dim), nn.Dropout(dropout) ) def forward(self, x): x = x + self.attn(self.norm1(x)) x = x + self.mlp(self.norm2(x)) return x class UNETRAdvanced(nn.Module): def __init__(self, config): super().__init__() c = config["model"] self.patch_size = c["patch_size"] self.embed_dim = c["embed_dim"] self.patch_embed = nn.Conv3d( c["in_channels"], self.embed_dim, kernel_size=self.patch_size, stride=self.patch_size ) self.blocks = nn.ModuleList([ TransformerBlock(self.embed_dim, c["num_heads"]) for _ in range(c["depth"]) ]) # Decoder self.up1 = nn.ConvTranspose3d(self.embed_dim, 128, kernel_size=2, stride=2) self.up2 = nn.ConvTranspose3d(128, 64, kernel_size=2, stride=2) self.up3 = nn.ConvTranspose3d(64, 32, kernel_size=2, stride=2) self.up4 = nn.ConvTranspose3d(32, 16, kernel_size=2, stride=2) self.out_head = nn.Conv3d(16, c["out_channels"], kernel_size=1) # Deep supervision heads (CORRECTED CHANNEL SIZES) # ds_head1 takes input from up3 (32 channels) # ds_head2 takes input from up2 (64 channels) self.ds_head1 = nn.Conv3d(32, c["out_channels"], kernel_size=1) self.ds_head2 = nn.Conv3d(64, c["out_channels"], kernel_size=1) def forward(self, x): x = self.patch_embed(x).flatten(2).transpose(1, 2) for blk in self.blocks: x = blk(x) B, N, C = x.shape D = int(round(N ** (1/3))) x = x.transpose(1, 2).reshape(B, C, D, D, D) x1 = self.up1(x) # 128 channels x2 = self.up2(x1) # 64 channels x3 = self.up3(x2) # 32 channels x4 = self.up4(x3) # 16 channels # During inference, only return main output if self.training: return self.out_head(x4), self.ds_head1(x3), self.ds_head2(x2) else: return self.out_head(x4)