import math from functools import partial import torch import torch.nn as nn import torch.nn.functional as F def get_inplanes(): return [64, 128, 256, 512] def conv3x3x3(in_planes, out_planes, stride=1): return nn.Conv3d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False) def conv1x1x1(in_planes, out_planes, stride=1): return nn.Conv3d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False) class BasicBlock(nn.Module): expansion = 1 def __init__(self, in_planes, planes, stride=1, downsample=None): super().__init__() self.conv1 = conv3x3x3(in_planes, planes, stride) self.bn1 = nn.BatchNorm3d(planes) self.relu = nn.ReLU(inplace=True) self.conv2 = conv3x3x3(planes, planes) self.bn2 = nn.BatchNorm3d(planes) self.downsample = downsample self.stride = stride def forward(self, x): residual = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) if self.downsample is not None: residual = self.downsample(x) out += residual out = self.relu(out) return out class Bottleneck(nn.Module): expansion = 4 def __init__(self, in_planes, planes, stride=1, downsample=None): super().__init__() self.conv1 = conv1x1x1(in_planes, planes) self.bn1 = nn.BatchNorm3d(planes) self.conv2 = conv3x3x3(planes, planes, stride) self.bn2 = nn.BatchNorm3d(planes) self.conv3 = conv1x1x1(planes, planes * self.expansion) self.bn3 = nn.BatchNorm3d(planes * self.expansion) self.relu = nn.ReLU(inplace=True) self.downsample = downsample self.stride = stride def forward(self, x): residual = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) out = self.relu(out) out = self.conv3(out) out = self.bn3(out) if self.downsample is not None: residual = self.downsample(x) out += residual out = self.relu(out) return out class ResNet(nn.Module): def __init__(self, block, layers, block_inplanes, n_input_channels=3, conv1_t_size=7, conv1_t_stride=1, no_max_pool=False, shortcut_type='B', widen_factor=1.0): super().__init__() block_inplanes = [int(x * widen_factor) for x in block_inplanes] self.in_planes = block_inplanes[0] self.no_max_pool = no_max_pool self.conv1 = nn.Conv3d(n_input_channels, self.in_planes, kernel_size=(conv1_t_size, 7, 7), stride=(conv1_t_stride, 2, 2), padding=(conv1_t_size // 2, 3, 3), bias=False) self.bn1 = nn.BatchNorm3d(self.in_planes) self.relu = nn.ReLU(inplace=True) self.maxpool = nn.MaxPool3d(kernel_size=3, stride=2, padding=1) self.layer1 = self._make_layer(block, block_inplanes[0], layers[0], shortcut_type) self.layer2 = self._make_layer(block, block_inplanes[1], layers[1], shortcut_type, stride=2) self.layer3 = self._make_layer(block, block_inplanes[2], layers[2], shortcut_type, stride=2) self.layer4 = self._make_layer(block, block_inplanes[3], layers[3], shortcut_type, stride=2) self.avgpool = nn.AdaptiveAvgPool3d((1, 1, 1)) # self.fc = nn.Linear(block_inplanes[3] * block.expansion, n_classes) for m in self.modules(): if isinstance(m, nn.Conv3d): nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') elif isinstance(m, nn.BatchNorm3d): nn.init.constant_(m.weight, 1) nn.init.constant_(m.bias, 0) def _downsample_basic_block(self, x, planes, stride): out = F.avg_pool3d(x, kernel_size=1, stride=stride) zero_pads = torch.zeros(out.size(0), planes - out.size(1), out.size(2), out.size(3), out.size(4)) if isinstance(out.data, torch.cuda.FloatTensor): zero_pads = zero_pads.cuda() out = torch.cat([out.data, zero_pads], dim=1) return out def _make_layer(self, block, planes, blocks, shortcut_type, stride=1): downsample = None if stride != 1 or self.in_planes != planes * block.expansion: if shortcut_type == 'A': downsample = partial(self._downsample_basic_block, planes=planes * block.expansion, stride=stride) else: downsample = nn.Sequential( conv1x1x1(self.in_planes, planes * block.expansion, stride), nn.BatchNorm3d(planes * block.expansion)) layers = [] layers.append( block(in_planes=self.in_planes, planes=planes, stride=stride, downsample=downsample)) self.in_planes = planes * block.expansion for i in range(1, blocks): layers.append(block(self.in_planes, planes)) return nn.Sequential(*layers) def forward(self, x): x = self.conv1(x) x = self.bn1(x) x = self.relu(x) if not self.no_max_pool: x = self.maxpool(x) x = self.layer1(x) x = self.layer2(x) x = self.layer3(x) x = self.layer4(x) x = self.avgpool(x) x = x.view(x.size(0), -1) return x def generate_model(model_depth, **kwargs): assert model_depth in [10, 18, 34, 50, 101, 152, 200] if model_depth == 10: model = ResNet(BasicBlock, [1, 1, 1, 1], get_inplanes(), **kwargs) elif model_depth == 18: model = ResNet(BasicBlock, [2, 2, 2, 2], get_inplanes(), **kwargs) elif model_depth == 34: model = ResNet(BasicBlock, [3, 4, 6, 3], get_inplanes(), **kwargs) elif model_depth == 50: model = ResNet(Bottleneck, [3, 4, 6, 3], get_inplanes(), **kwargs) elif model_depth == 101: model = ResNet(Bottleneck, [3, 4, 23, 3], get_inplanes(), **kwargs) elif model_depth == 152: model = ResNet(Bottleneck, [3, 8, 36, 3], get_inplanes(), **kwargs) elif model_depth == 200: model = ResNet(Bottleneck, [3, 24, 36, 3], get_inplanes(), **kwargs) return model class Bottleneck(nn.Module): def __init__(self, inplanes, planes, stride=1): super(Bottleneck, self).__init__() self.bn1 = nn.BatchNorm3d(inplanes) self.relu = nn.ReLU() self.bn2 = nn.BatchNorm3d(planes) self.conv1 = nn.Conv3d(inplanes, planes, kernel_size=3, stride=stride, padding=1) self.conv2 = nn.Conv3d(planes, planes, kernel_size=3, stride=stride, padding=1) self.stride = stride def forward(self, x): residual = x out = self.bn1(x) out = self.relu(out) out = self.conv1(out) out = self.bn2(x) out = self.relu(out) out = self.conv2(out) out += residual return out class ResNet3D(nn.Module): def __init__(self, num_classes=2, input_shape=(1,110,110,110)): # input: input_shape: [num_of_filters, kernel_size] (e.g. [256, 25]) super(ResNet3D, self).__init__() #stage 1 self.conv1 = nn.Sequential( nn.Conv3d( in_channels=input_shape[0], out_channels=32, kernel_size=(3,3,3), padding=1 ), nn.BatchNorm3d(32), nn.ReLU(), nn.Conv3d( in_channels=32, out_channels=32, kernel_size=(3,3,3), padding=1 ), nn.BatchNorm3d(32), nn.ReLU(), nn.Conv3d( in_channels=32, out_channels=64, kernel_size=(3,3,3), stride=2, padding=1 ) ) #stage 2 self.bot2=Bottleneck(64,64,1) #stage 3 self.bot3=Bottleneck(64,64,1) #stage 4 self.conv4=nn.Sequential( nn.BatchNorm3d(64), nn.Conv3d( in_channels=64, # input height out_channels=64, # n_filters kernel_size=(3,3,3), # filter size padding=1, stride=2 ) ) #stage 5 self.bot5=Bottleneck(64,64,1) #stage 6 self.bot6=Bottleneck(64,64,1) #stage 7 self.conv7=nn.Sequential( nn.BatchNorm3d(64), nn.Conv3d( in_channels=64, # input height out_channels=128, # n_filters kernel_size=(3,3,3), # filter size padding=1, stride=2 ) ) #stage 8 self.bot8=Bottleneck(128,128,1) #stage 9 self.bot9=Bottleneck(128,128,1) #stage 10 self.conv10=nn.Sequential( nn.MaxPool3d(kernel_size=(7,7,7))) fc1_output_features=128 self.fc1 = nn.Sequential( nn.Linear(512, 128), nn.ReLU() ) self.fc2 = nn.Sequential(nn.Linear(fc1_output_features, 3)) def forward(self, x, drop_prob=0.8): x = self.conv1(x) #print(x.shape) x = self.bot2(x) #print(x.shape) x = self.bot3(x) #print(x.shape) x = self.conv4(x) #print(x.shape) x = self.bot5(x) #print(x.shape) x = self.bot6(x) #print(x.shape) x = self.conv7(x) #print(x.shape) x = self.bot8(x) #print(x.shape) x = self.bot9(x) #print(x.shape) x = self.conv10(x) #print(x.shape) x = x.view(x.size(0), -1) # flatten the output of conv2 to (batch_size, num_filter * w * h) #print(x.shape) # x = self.fc1(x) # x = self.fc2(x) #prob = self.out(x) # probability return x def main(): # Create a random 3D tensor with dimensions (C, D, H, W) channels = 3 depth = 64 height = 128 width = 128 random_tensor = torch.randn((channels, depth, height, width)) # Replace with torch.rand() if you want values in [0, 1) # You can also reshape the tensor if needed (example: 1 x C x D x H x W) tensor_data = random_tensor.unsqueeze(0) # Add batch dimension (1, C, D, H, W) # Instantiate the ResNet model model_depth = 18 # Replace with the desired ResNet model depth (e.g., 18, 34, 50, etc.) model = generate_model(10, n_input_channels=1, n_classes=1000) # Example number of classes # Pass the tensor through the ResNet model model.eval() with torch.no_grad(): output = model(tensor_data) print("Model output shape:", output.shape) if __name__ == "__main__": main()