AnirudhShashikumar's picture
Deploy SatQuery AI ZeroGPU bridge
2407511
Raw History Blame Contribute Delete
3.27 kB
"""Pure-PyTorch IA-ResNetV1c-18 backbone for ChangerEx.
Module names intentionally match the official Open-CD checkpoint. Architecture
is adapted from Apache-2.0 licensed Open-CD and MMSegmentation ResNet sources.
"""
from __future__ import annotations
import torch
from torch import nn
from .interaction import ChannelExchange, SpatialExchange, TwoIdentity
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, in_channels: int, out_channels: int, stride: int = 1) -> None:
super().__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, 3, stride=stride, padding=1, bias=False)
self.bn1 = nn.SyncBatchNorm(out_channels)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(out_channels, out_channels, 3, padding=1, bias=False)
self.bn2 = nn.SyncBatchNorm(out_channels)
self.downsample: nn.Module | None = None
if stride != 1 or in_channels != out_channels:
self.downsample = nn.Sequential(
nn.Conv2d(in_channels, out_channels, 1, stride=stride, bias=False),
nn.SyncBatchNorm(out_channels),
)
def forward(self, tensor: torch.Tensor) -> torch.Tensor:
identity = tensor if self.downsample is None else self.downsample(tensor)
output = self.relu(self.bn1(self.conv1(tensor)))
output = self.bn2(self.conv2(output))
return self.relu(output + identity)
class IAResNetV1c18(nn.Module):
"""Exact four-stage, deep-stem interaction ResNet-18."""
def __init__(self) -> None:
super().__init__()
self.stem = nn.Sequential(
nn.Conv2d(3, 32, 3, stride=2, padding=1, bias=False),
nn.SyncBatchNorm(32),
nn.ReLU(inplace=True),
nn.Conv2d(32, 32, 3, padding=1, bias=False),
nn.SyncBatchNorm(32),
nn.ReLU(inplace=True),
nn.Conv2d(32, 64, 3, padding=1, bias=False),
nn.SyncBatchNorm(64),
nn.ReLU(inplace=True),
)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.layer1 = self._make_layer(64, 64, 1)
self.layer2 = self._make_layer(64, 128, 2)
self.layer3 = self._make_layer(128, 256, 2)
self.layer4 = self._make_layer(256, 512, 2)
self.ccs = nn.ModuleList(
[TwoIdentity(), SpatialExchange(0.5), ChannelExchange(0.5), ChannelExchange(0.5)]
)
@staticmethod
def _make_layer(in_channels: int, out_channels: int, stride: int) -> nn.Sequential:
return nn.Sequential(
BasicBlock(in_channels, out_channels, stride),
BasicBlock(out_channels, out_channels),
)
def forward(self, first: torch.Tensor, second: torch.Tensor) -> tuple[torch.Tensor, ...]:
first = self.maxpool(self.stem(first))
second = self.maxpool(self.stem(second))
outputs: list[torch.Tensor] = []
for layer, interaction in zip(
(self.layer1, self.layer2, self.layer3, self.layer4), self.ccs
):
first = layer(first)
second = layer(second)
first, second = interaction(first, second)
outputs.append(torch.cat((first, second), dim=1))
return tuple(outputs)