Spaces:
Running on Zero
Running on Zero
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from .util import SameBlock2d, DownBlock2d, ResBlock3d | |
| class ModulatedConv3d(nn.Module): | |
| """ | |
| 参考 StyleGAN2 的 3D 版本示例,用于替代原先的 Conv3d + InstanceNorm3d + AdaIN。 | |
| """ | |
| def __init__(self, | |
| in_channels, | |
| out_channels, | |
| latent_size, | |
| kernel_size=3, | |
| stride=1, | |
| padding=1, | |
| bias=False, | |
| eps=1e-8): | |
| super().__init__() | |
| self.eps = eps | |
| self.in_channels = in_channels | |
| self.out_channels = out_channels | |
| self.kernel_size = kernel_size if isinstance(kernel_size, tuple) else (kernel_size,)*3 | |
| self.stride = stride if isinstance(stride, tuple) else (stride,)*3 | |
| self.padding = padding if isinstance(padding, tuple) else (padding,)*3 | |
| self.bias = bias | |
| # 卷积权重:维度 [out_channels, in_channels, kD, kH, kW] | |
| # 这里初始化方式可以参考 kaiming_normal 或者 stylegan2 原项目 | |
| self.weight = nn.Parameter(torch.randn( | |
| out_channels, in_channels, *self.kernel_size) * 0.01) | |
| # 风格全连接,把 latent 映射到 in_channels | |
| self.style_fc = nn.Linear(latent_size, in_channels, bias=True) | |
| if bias: | |
| self.bias_param = nn.Parameter(torch.zeros(out_channels)) | |
| else: | |
| self.bias_param = None | |
| def forward(self, x, latent): | |
| """ | |
| x: [N, inC, D, H, W] | |
| latent: [N, latent_size] | |
| """ | |
| N, _, D, H, W = x.shape | |
| # 1) 计算对 inC 进行的调制系数 scale => [N, inC] | |
| style = self.style_fc(latent) # => [N, inC] | |
| style = style.unsqueeze(-1).unsqueeze(-1).unsqueeze(-1) # => [N, inC, 1, 1, 1] | |
| # 2) 对卷积权重做调制 => w' = w * scale | |
| # 原始 w.shape = [outC, inC, kD, kH, kW] | |
| # 调整后 w_mod.shape = [N, outC, inC, kD, kH, kW] | |
| w = self.weight.unsqueeze(0) # => [1, outC, inC, kD, kH, kW] | |
| w_mod = w * style[:, None, :, :, :, :] # 广播到 [N, outC, inC, kD, kH, kW] | |
| # 3) Demodulation | |
| # 每个样本、每个输出通道的范数,用于对 w_mod 做归一化 | |
| # norm.shape = [N, outC, 1, 1, 1, 1] | |
| demod = torch.rsqrt((w_mod**2).sum(dim=(2,3,4,5), keepdim=True) + self.eps) | |
| w_mod = w_mod * demod # => [N, outC, inC, kD, kH, kW] | |
| # 4) 组卷积 (group = N),把 batch 维度展开成 group | |
| # x => [1, N*inC, D, H, W] | |
| # w_mod => [N*outC, inC, kD, kH, kW] (先把 outC 合并到第一维度) | |
| x = x.view(1, N*self.in_channels, D, H, W) | |
| w_mod = w_mod.view(N*self.out_channels, self.in_channels, *self.kernel_size) | |
| out = F.conv3d( | |
| x, | |
| w_mod, | |
| bias=None, # 暂时先不加 bias;如果需要则要同样做拆分 | |
| stride=self.stride, | |
| padding=self.padding, | |
| groups=N # 分成 N 组 | |
| ) | |
| # out.shape = [1, N*outC, D, H, W] | |
| # 还原回 [N, outC, D, H, W] | |
| out = out.view(N, self.out_channels, D, H, W) | |
| # 如果需要 bias,则加上 | |
| if self.bias_param is not None: | |
| out = out + self.bias_param.view(1, -1, 1, 1, 1) | |
| return out | |
| class ModulatedConv2d(nn.Module): | |
| """ | |
| 类似上面 2D 版本,用于替代原先的 Conv2d + InstanceNorm2d + AdaIN。 | |
| """ | |
| def __init__(self, | |
| in_channels, | |
| out_channels, | |
| latent_size, | |
| kernel_size=3, | |
| stride=1, | |
| padding=1, | |
| bias=False, | |
| eps=1e-8): | |
| super().__init__() | |
| self.eps = eps | |
| self.in_channels = in_channels | |
| self.out_channels = out_channels | |
| self.kernel_size = kernel_size if isinstance(kernel_size, tuple) else (kernel_size,)*2 | |
| self.stride = stride if isinstance(stride, tuple) else (stride,)*2 | |
| self.padding = padding if isinstance(padding, tuple) else (padding,)*2 | |
| self.bias = bias | |
| # 卷积权重 | |
| self.weight = nn.Parameter(torch.randn( | |
| out_channels, in_channels, *self.kernel_size) * 0.01) | |
| # 风格全连接 | |
| self.style_fc = nn.Linear(latent_size, in_channels, bias=True) | |
| if bias: | |
| self.bias_param = nn.Parameter(torch.zeros(out_channels)) | |
| else: | |
| self.bias_param = None | |
| def forward(self, x, latent): | |
| """ | |
| x: [N, inC, H, W] | |
| latent: [N, latent_size] | |
| """ | |
| N, _, H, W = x.shape | |
| # 1) 计算 scale => [N, inC] | |
| style = self.style_fc(latent) # => [N, inC] | |
| style = style.unsqueeze(-1).unsqueeze(-1) # => [N, inC, 1, 1] | |
| # 2) 调制权重 | |
| w = self.weight.unsqueeze(0) # => [1, outC, inC, kH, kW] | |
| w_mod = w * style[:, None, :, :, :] # => [N, outC, inC, kH, kW] | |
| # 3) Demodulation | |
| demod = torch.rsqrt((w_mod**2).sum(dim=(2,3,4), keepdim=True) + self.eps) | |
| w_mod = w_mod * demod # => [N, outC, inC, kH, kW] | |
| # 4) 组卷积 | |
| x = x.view(1, N*self.in_channels, H, W) | |
| w_mod = w_mod.view(N*self.out_channels, self.in_channels, *self.kernel_size) | |
| out = F.conv2d( | |
| x, | |
| w_mod, | |
| bias=None, | |
| stride=self.stride, | |
| padding=self.padding, | |
| groups=N | |
| ) | |
| out = out.view(N, self.out_channels, out.shape[2], out.shape[3]) | |
| if self.bias_param is not None: | |
| out = out + self.bias_param.view(1, -1, 1, 1) | |
| return out | |
| class ResnetBlock_StyleGAN2_3D(nn.Module): | |
| def __init__(self, dim=32, latent_size=512, activation=nn.ReLU(True)): | |
| super().__init__() | |
| self.dim = dim | |
| self.act = activation | |
| # 两次 ModulatedConv3d | |
| self.conv1 = ModulatedConv3d( | |
| in_channels=dim, | |
| out_channels=dim, | |
| latent_size=latent_size, | |
| kernel_size=3, | |
| padding=1, | |
| bias=True # 是否加bias,看你需要 | |
| ) | |
| self.conv2 = ModulatedConv3d( | |
| in_channels=dim, | |
| out_channels=dim, | |
| latent_size=latent_size, | |
| kernel_size=3, | |
| padding=1, | |
| bias=True | |
| ) | |
| def forward(self, x, dlatents_in_slice): | |
| """ | |
| x: [N, C, D, H, W] | |
| dlatents_in_slice: [N, latent_size] | |
| """ | |
| y = self.conv1(x, dlatents_in_slice) # => [N, C, D, H, W] | |
| y = self.act(y) | |
| y = self.conv2(y, dlatents_in_slice) # => [N, C, D, H, W] | |
| return x + y # ResNet 残差 | |
| class ResnetBlock_StyleGAN2_2D(nn.Module): | |
| def __init__(self, dim=512, latent_size=512, activation=nn.ReLU(True)): | |
| super().__init__() | |
| self.dim = dim | |
| self.act = activation | |
| self.conv1 = ModulatedConv2d( | |
| in_channels=dim, | |
| out_channels=dim, | |
| latent_size=latent_size, | |
| kernel_size=3, | |
| padding=1, | |
| bias=True | |
| ) | |
| self.conv2 = ModulatedConv2d( | |
| in_channels=dim, | |
| out_channels=dim, | |
| latent_size=latent_size, | |
| kernel_size=3, | |
| padding=1, | |
| bias=True | |
| ) | |
| def forward(self, x, dlatents_in_slice): | |
| y = self.conv1(x, dlatents_in_slice) | |
| y = self.act(y) | |
| y = self.conv2(y, dlatents_in_slice) | |
| return x + y | |
| class transfer_model(nn.Module): | |
| def __init__(self, latent_dim=512, n_blocks=4, padding_type='reflect'): | |
| super(transfer_model, self).__init__() | |
| activation = nn.ReLU(True) | |
| # 3D in | |
| BN_in = [] | |
| for i in range(3): | |
| BN_in += [ | |
| ResnetBlock_StyleGAN2_3D(dim=32, latent_size=latent_dim, activation=activation) | |
| ] | |
| self.BottleNeck_3din = nn.Sequential(*BN_in) | |
| # 2D | |
| BN = [] | |
| for i in range(n_blocks): | |
| BN += [ | |
| ResnetBlock_StyleGAN2_2D(dim=512, latent_size=latent_dim, activation=activation) | |
| ] | |
| self.BottleNeck_2d = nn.Sequential(*BN) | |
| # 3D out | |
| BN_out = [] | |
| for i in range(3): | |
| BN_out += [ | |
| ResnetBlock_StyleGAN2_3D(dim=32, latent_size=latent_dim, activation=activation) | |
| ] | |
| self.BottleNeck_3dout = nn.Sequential(*BN_out) | |
| self.resblocks_3d = torch.nn.Sequential() | |
| for i in range(3): | |
| self.resblocks_3d.add_module('3dr' + str(i), ResBlock3d(32, kernel_size=3, padding=1)) | |
| def forward(self, x, dlatents): | |
| # x => [N, 32, D, H, W] 假设是这样 | |
| # 1) 3D in | |
| for i in range(len(self.BottleNeck_3din)): | |
| x = self.BottleNeck_3din[i](x, dlatents) | |
| # 2) reshape to 2D => [N, 32*D, H, W] | |
| bs, c, d, h, w = x.shape | |
| x = x.view(bs, c*d, h, w) | |
| # 2D blocks | |
| for i in range(len(self.BottleNeck_2d)): | |
| x = self.BottleNeck_2d[i](x, dlatents) | |
| # reshape back => [N, 32, D, H, W] | |
| x = x.view(bs, c, d, h, w) | |
| # 3) 3D out | |
| for i in range(len(self.BottleNeck_3dout)): | |
| x = self.BottleNeck_3dout[i](x, dlatents) | |
| x = self.resblocks_3d(x) | |
| return x | |
| if __name__ == "__main__": | |
| model = transfer_model() | |
| total_params = sum(p.numel() for p in model.parameters()) | |
| print("total parameters:", total_params) |