satoshiNakomoroReal's picture
Deploy shared three-method Gradio app (part 4)
0d99394 verified
Raw
History Blame Contribute Delete
9.53 kB
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)