satoshiNakomoroReal's picture
Deploy shared three-method Gradio app (part 4)
0d99394 verified
Raw
History Blame Contribute Delete
28.2 kB
import torch
import torch.nn as nn
import torch.nn.functional as F
# 输入特征 (Bx32x16x64x64)
# |
# |-- Identity Encoder --> Bx128x2x8x8
# |
# |-- Attribute Encoder --> Bx512x8x32x32
# |
# |-- Decoder (融合 Identity 和 Attribute) --> Bx32x16x64x64
class ResidualBlock3D_DSep(nn.Module):
def __init__(self, in_channels, out_channels):
super(ResidualBlock3D_DSep, self).__init__()
self.conv1 = DepthwiseSeparableConv3D(in_channels, out_channels)
self.conv2 = DepthwiseSeparableConv3D(out_channels, out_channels)
self.shortcut = nn.Conv3d(in_channels, out_channels, kernel_size=1) if in_channels != out_channels else nn.Identity()
self.relu = nn.ReLU()
def forward(self, x):
identity = self.shortcut(x)
out = self.conv1(x)
out = self.conv2(out)
out = out + identity
out = self.relu(out)
return out
class DepthwiseSeparableConv3D(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size=3, padding=1):
super(DepthwiseSeparableConv3D, self).__init__()
self.depthwise = nn.Conv3d(in_channels, in_channels, kernel_size=kernel_size, padding=padding, groups=in_channels)
self.pointwise = nn.Conv3d(in_channels, out_channels, kernel_size=1)
self.bn = nn.SyncBatchNorm(out_channels)
self.relu = nn.ReLU()
def forward(self, x):
x = self.depthwise(x)
x = self.pointwise(x)
x = self.bn(x)
x = self.relu(x)
return x
class SEBlock(nn.Module):
"""Squeeze-and-Excitation Block"""
def __init__(self, channels, reduction=16):
super(SEBlock, self).__init__()
self.fc1 = nn.Linear(channels, channels // reduction, bias=False)
self.relu = nn.ReLU(inplace=True)
self.fc2 = nn.Linear(channels // reduction, channels, bias=False)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
b, c, d, h, w = x.size()
y = x.view(b, c, -1).mean(dim=2) # 全局平均池化
y = self.fc1(y)
y = self.relu(y)
y = self.fc2(y)
y = self.sigmoid(y).view(b, c, 1, 1, 1)
return x * y.expand_as(x)
class ResidualBlock3D_SE(nn.Module):
"""带有Squeeze-and-Excitation的3D残差块,使用InstanceNorm3d"""
def __init__(self, in_channels, out_channels):
super(ResidualBlock3D_SE, self).__init__()
self.conv1 = nn.Conv3d(in_channels, out_channels, kernel_size=3, padding=1)
self.in1 = nn.InstanceNorm3d(out_channels, affine=True)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv3d(out_channels, out_channels, kernel_size=3, padding=1)
self.in2 = nn.InstanceNorm3d(out_channels, affine=True)
self.se = SEBlock(out_channels)
self.shortcut = nn.Conv3d(in_channels, out_channels, kernel_size=1) if in_channels != out_channels else nn.Identity()
def forward(self, x):
identity = self.shortcut(x)
out = self.relu(self.in1(self.conv1(x)))
out = self.in2(self.conv2(out))
out = self.se(out)
out += identity
out = self.relu(out)
return out
class ResidualBlock3D(nn.Module):
def __init__(self, in_channels, out_channels):
super(ResidualBlock3D, self).__init__()
self.conv1 = nn.Conv3d(in_channels, out_channels, kernel_size=3, padding=1)
self.bn1 = nn.SyncBatchNorm(out_channels)
self.relu = nn.ReLU()
self.conv2 = nn.Conv3d(out_channels, out_channels, kernel_size=3, padding=1)
self.bn2 = nn.SyncBatchNorm(out_channels)
self.shortcut = nn.Conv3d(in_channels, out_channels, kernel_size=1) if in_channels != out_channels else nn.Identity()
def forward(self, x):
identity = self.shortcut(x)
out = self.relu(self.bn1(self.conv1(x)))
out = self.bn2(self.conv2(out))
out = out + identity
out = self.relu(out)
return out
class SEBlock(nn.Module):
"""Squeeze-and-Excitation Block"""
def __init__(self, channels, reduction=16):
super(SEBlock, self).__init__()
self.fc1 = nn.Linear(channels, channels // reduction, bias=False)
self.relu = nn.ReLU(inplace=True)
self.fc2 = nn.Linear(channels // reduction, channels, bias=False)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
b, c, d, h, w = x.size()
y = x.view(b, c, -1).mean(dim=2) # 全局平均池化
y = self.fc1(y)
y = self.relu(y)
y = self.fc2(y)
y = self.sigmoid(y).view(b, c, 1, 1, 1)
return x * y.expand_as(x)
class ResidualBlock3D_SE(nn.Module):
"""带有Squeeze-and-Excitation的3D残差块,使用InstanceNorm3d"""
def __init__(self, in_channels, out_channels):
super(ResidualBlock3D_SE, self).__init__()
self.conv1 = nn.Conv3d(in_channels, out_channels, kernel_size=3, padding=1)
self.in1 = nn.InstanceNorm3d(out_channels, affine=True)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv3d(out_channels, out_channels, kernel_size=3, padding=1)
self.in2 = nn.InstanceNorm3d(out_channels, affine=True)
self.se = SEBlock(out_channels)
self.shortcut = nn.Conv3d(in_channels, out_channels, kernel_size=1) if in_channels != out_channels else nn.Identity()
def forward(self, x):
identity = self.shortcut(x)
out = self.relu(self.in1(self.conv1(x)))
out = self.in2(self.conv2(out))
out = self.se(out)
out += identity
out = self.relu(out)
return out
class IdentityEncoder(nn.Module):
def __init__(self, input_channels=32, latent_channels=128):
super(IdentityEncoder, self).__init__()
# 第1层
self.conv1 = nn.Conv3d(input_channels, 64, kernel_size=3, stride=1, padding=1) # Bx64x16x64x64
self.bn1 = nn.SyncBatchNorm(64)
self.relu1 = nn.ReLU()
self.res_block1 = ResidualBlock3D_DSep(64, 64)
# 第2层
self.conv2 = nn.Conv3d(64, 128, kernel_size=3, stride=2, padding=1) # Bx128x8x32x32
self.bn2 = nn.SyncBatchNorm(128)
self.relu2 = nn.ReLU()
self.res_block2 = ResidualBlock3D_DSep(128, 128)
# 第3层
self.conv3 = nn.Conv3d(128, 256, kernel_size=3, stride=2, padding=1) # Bx256x4x16x16
self.bn3 = nn.SyncBatchNorm(256)
self.relu3 = nn.ReLU()
self.res_block3 = ResidualBlock3D_DSep(256, 256)
# 第4层
self.conv4 = nn.Conv3d(256, latent_channels, kernel_size=3, stride=2, padding=1) # Bxlatent_channelsx2x8x8
self.bn4 = nn.SyncBatchNorm(latent_channels)
self.relu4 = nn.ReLU()
self.res_block4 = ResidualBlock3D_DSep(latent_channels, latent_channels)
def forward(self, x):
# 第1层
x = self.conv1(x)
x = self.bn1(x)
x = self.relu1(x)
x = self.res_block1(x)
# 第2层
x = self.conv2(x)
x = self.bn2(x)
x = self.relu2(x)
x = self.res_block2(x)
# 第3层
x = self.conv3(x)
x = self.bn3(x)
x = self.relu3(x)
x = self.res_block3(x)
# 第4层
x = self.conv4(x)
x = self.bn4(x)
x = self.relu4(x)
x = self.res_block4(x)
return x # 输出形状: Bxlatent_channelsx2x8x8
class AttributeEncoder(nn.Module):
def __init__(self, input_channels=32, output_channels=512):
super(AttributeEncoder, self).__init__()
# 第一层卷积
self.conv1 = nn.Conv3d(input_channels, 128, kernel_size=3, stride=2, padding=1) # Bx128x8x32x32
self.relu1 = nn.ReLU()
self.bn1 = nn.SyncBatchNorm(128)
# 第二层卷积
self.conv2 = nn.Conv3d(128, 256, kernel_size=3, stride=1, padding=1) # Bx256x8x32x32
self.relu2 = nn.ReLU()
self.bn2 = nn.SyncBatchNorm(256)
# 第三层卷积
self.conv3 = nn.Conv3d(256, output_channels, kernel_size=3, stride=1, padding=1) # Bx512x8x32x32
self.relu3 = nn.ReLU()
self.bn3 = nn.SyncBatchNorm(output_channels)
def forward(self, x):
x = self.conv1(x)
x = self.relu1(x)
x = self.bn1(x)
# 按照同样的方式检查每一层
x = self.conv2(x)
x = self.relu2(x)
x = self.bn2(x)
x = self.conv3(x)
x = self.relu3(x)
x = self.bn3(x)
return x
class Decoder(nn.Module):
def __init__(self, latent_dim_identity=128, latent_dim_attribute=512, output_channels=32):
super(Decoder, self).__init__()
# 上采样身份特征: Bx128x2x8x8 -> Bx128x8x32x32
self.upsample_identity = nn.ConvTranspose3d(
in_channels=latent_dim_identity,
out_channels=latent_dim_identity,
kernel_size=(4, 4, 4),
stride=(4, 4, 4),
padding=0
)
self.bn_identity = nn.SyncBatchNorm(latent_dim_identity)
self.relu_identity = nn.ReLU()
# 特征融合后处理
self.fuse_conv = nn.Sequential(
nn.Conv3d(
in_channels=latent_dim_identity + latent_dim_attribute,
out_channels=256,
kernel_size=3,
padding=1
),
nn.SyncBatchNorm(256),
nn.ReLU(),
ResidualBlock3D(256, 256)
)
# 上采样到16x64x64
self.upsample = nn.ConvTranspose3d(
in_channels=256,
out_channels=128,
kernel_size=2,
stride=2,
padding=0
)
self.bn_up = nn.SyncBatchNorm(128)
self.relu_up = nn.ReLU()
self.res_up = ResidualBlock3D(128, 128)
# 最终卷积层
self.final_conv = nn.Sequential(
nn.Conv3d(
in_channels=128,
out_channels=output_channels,
kernel_size=3,
padding=1
),
nn.SyncBatchNorm(output_channels),
nn.ReLU() # 确保没有使用原地操作
)
def forward(self, identity, attribute):
"""
identity: Bx128x2x8x8
attribute: Bx512x8x32x32
"""
# 上采样身份特征
identity_upsampled = self.relu_identity(self.bn_identity(self.upsample_identity(identity))) # Bx128x8x32x32
# 特征融合
fused = torch.cat((identity_upsampled, attribute), dim=1) # Bx640x8x32x32
fused = self.fuse_conv(fused) # Bx256x8x32x32
# 上采样到16x64x64
upsampled = self.relu_up(self.bn_up(self.upsample(fused))) # Bx128x16x64x64
upsampled = self.res_up(upsampled) # Bx128x16x64x64
# 最终卷积
out = self.final_conv(upsampled) # Bx32x16x64x64
return out
class EnhancedIdentityEncoder(nn.Module):
def __init__(self, input_channels=32, latent_channels=128):
super(EnhancedIdentityEncoder, self).__init__()
# 第1层
self.conv1 = nn.Conv3d(input_channels, 64, kernel_size=3, stride=1, padding=1) # Bx64x16x64x64
self.in1 = nn.InstanceNorm3d(64, affine=True)
self.relu1 = nn.ReLU(inplace=True)
self.res_block1 = ResidualBlock3D_SE(64, 64)
# 第2层
self.conv2 = nn.Conv3d(64, 128, kernel_size=3, stride=2, padding=1) # Bx128x8x32x32
self.in2 = nn.InstanceNorm3d(128, affine=True)
self.relu2 = nn.ReLU(inplace=True)
self.res_block2 = ResidualBlock3D_SE(128, 128)
self.res_block3 = ResidualBlock3D_SE(128, 128)
# 第3层
self.conv3 = nn.Conv3d(128, 256, kernel_size=3, stride=2, padding=1) # Bx256x4x16x16
self.in3 = nn.InstanceNorm3d(256, affine=True)
self.relu3 = nn.ReLU(inplace=True)
self.res_block4 = ResidualBlock3D_SE(256, 256)
self.res_block5 = ResidualBlock3D_SE(256, 256)
# 第4层
self.conv4 = nn.Conv3d(256, latent_channels, kernel_size=3, stride=2, padding=1) # Bx128x2x8x8
self.in4 = nn.InstanceNorm3d(latent_channels, affine=True)
self.relu4 = nn.ReLU(inplace=True)
self.res_block6 = ResidualBlock3D_SE(latent_channels, latent_channels)
self.res_block7 = ResidualBlock3D_SE(latent_channels, latent_channels)
def forward(self, x):
# 第1层
x = self.conv1(x)
x = self.in1(x)
x = self.relu1(x)
x = self.res_block1(x)
# 第2层
x = self.conv2(x)
x = self.in2(x)
x = self.relu2(x)
x = self.res_block2(x)
x = self.res_block3(x)
# 第3层
x = self.conv3(x)
x = self.in3(x)
x = self.relu3(x)
x = self.res_block4(x)
x = self.res_block5(x)
# 第4层
x = self.conv4(x)
x = self.in4(x)
x = self.relu4(x)
x = self.res_block6(x)
x = self.res_block7(x)
return x # 输出形状: Bxlatent_channelsx2x8x8
# 定义完整模型
class FaceFeatureDecouplingModel(nn.Module):
def __init__(self, input_channels=32, latent_dim_identity=128, latent_dim_attribute=512, output_channels=32):
super(FaceFeatureDecouplingModel, self).__init__()
self.identity_encoder = IdentityEncoder(input_channels, latent_dim_identity)
self.attribute_encoder = AttributeEncoder(input_channels, latent_dim_attribute)
self.decoder = Decoder(latent_dim_identity, latent_dim_attribute, output_channels)
def forward(self, x):
identity = self.identity_encoder(x) # Bx128x2x8x8
attribute = self.attribute_encoder(x) # Bx512x8x32x32
decoded_feature = self.decoder(identity, attribute) # Bx32x16x64x64
return decoded_feature, identity, attribute
class EnhancedAttributeEncoder(nn.Module):
def __init__(self, input_channels=32, output_channels=512):
super(EnhancedAttributeEncoder, self).__init__()
# 第一层卷积
self.conv1 = nn.Conv3d(input_channels, 128, kernel_size=3, stride=2, padding=1) # Bx128x8x32x32
self.in1 = nn.InstanceNorm3d(128, affine=True)
self.relu1 = nn.ReLU(inplace=True)
self.res_block1 = ResidualBlock3D_SE(128, 128)
# 第二层卷积
self.conv2 = nn.Conv3d(128, 256, kernel_size=3, stride=2, padding=1) # Bx256x4x16x16
self.in2 = nn.InstanceNorm3d(256, affine=True)
self.relu2 = nn.ReLU(inplace=True)
self.res_block2 = ResidualBlock3D_SE(256, 256)
# 第三层卷积
self.conv3 = nn.Conv3d(256, output_channels, kernel_size=3, stride=2, padding=1) # Bx512x2x8x8
self.in3 = nn.InstanceNorm3d(output_channels, affine=True)
self.relu3 = nn.ReLU(inplace=True)
self.res_block3 = ResidualBlock3D_SE(output_channels, output_channels)
self.res_block4 = ResidualBlock3D_SE(output_channels, output_channels)
def forward(self, x):
x = self.conv1(x)
x = self.in1(x)
x = self.relu1(x)
x = self.res_block1(x)
x = self.conv2(x)
x = self.in2(x)
x = self.relu2(x)
x = self.res_block2(x)
x = self.conv3(x)
x = self.in3(x)
x = self.relu3(x)
x = self.res_block3(x)
x = self.res_block4(x)
return x # 输出形状: Bx512x2x8x8
class EnhancedDecoder(nn.Module):
def __init__(self, latent_dim_identity=128, latent_dim_attribute=512, output_channels=32):
super(EnhancedDecoder, self).__init__()
# 上采样身份特征: Bx128x2x8x8 -> Bx128x4x16x16
self.upsample_identity1 = nn.ConvTranspose3d(
in_channels=latent_dim_identity,
out_channels=latent_dim_identity,
kernel_size=(2, 4, 4),
stride=(2, 4, 4),
padding=0
)
self.in_identity1 = nn.InstanceNorm3d(latent_dim_identity, affine=True)
self.relu_identity1 = nn.ReLU(inplace=True)
self.res_block_identity1 = ResidualBlock3D_SE(latent_dim_identity, latent_dim_identity)
# 再次上采样到 Bx128x8x32x32
self.upsample_identity2 = nn.ConvTranspose3d(
in_channels=latent_dim_identity,
out_channels=latent_dim_identity,
kernel_size=(2, 4, 4),
stride=(2, 4, 4),
padding=0
)
self.in_identity2 = nn.InstanceNorm3d(latent_dim_identity, affine=True)
self.relu_identity2 = nn.ReLU(inplace=True)
self.res_block_identity2 = ResidualBlock3D_SE(latent_dim_identity, latent_dim_identity)
# 特征融合后处理
self.fuse_conv = nn.Sequential(
nn.Conv3d(
in_channels=latent_dim_identity + latent_dim_attribute,
out_channels=512,
kernel_size=3,
padding=1
),
nn.InstanceNorm3d(512),
nn.ReLU(inplace=True),
ResidualBlock3D_SE(512, 512),
ResidualBlock3D_SE(512, 256)
)
# 上采样到 Bx128x16x64x64
self.upsample1 = nn.ConvTranspose3d(
in_channels=256,
out_channels=128,
kernel_size=(2, 2, 2),
stride=(2, 2, 2),
padding=0
)
self.in_up1 = nn.InstanceNorm3d(128, affine=True)
self.relu_up1 = nn.ReLU(inplace=True)
self.res_up1 = ResidualBlock3D_SE(128, 128)
# 上采样到 Bx64x32x128x128
self.upsample2 = nn.ConvTranspose3d(
in_channels=128,
out_channels=64,
kernel_size=(2, 2, 2),
stride=(2, 2, 2),
padding=0
)
self.in_up2 = nn.InstanceNorm3d(64, affine=True)
self.relu_up2 = nn.ReLU(inplace=True)
self.res_up2 = ResidualBlock3D_SE(64, 64)
# 最终卷积层
self.final_conv = nn.Sequential(
nn.Conv3d(
in_channels=64,
out_channels=output_channels,
kernel_size=3,
padding=1
),
nn.InstanceNorm3d(output_channels),
nn.ReLU(inplace=True)
)
def forward(self, identity, attribute):
"""
identity: Bx128x2x8x8
attribute: Bx512x2x8x8
"""
# 上采样身份特征到 Bx128x4x16x16
identity_upsampled = self.relu_identity1(self.in_identity1(self.upsample_identity1(identity))) # Bx128x4x16x16
identity_upsampled = self.res_block_identity1(identity_upsampled)
# 再次上采样到 Bx128x8x32x32
identity_upsampled = self.relu_identity2(self.in_identity2(self.upsample_identity2(identity_upsampled))) # Bx128x8x32x32
identity_upsampled = self.res_block_identity2(identity_upsampled)
# 特征融合
fused = torch.cat((identity_upsampled, attribute), dim=1) # Bx640x8x32x32
fused = self.fuse_conv(fused) # Bx256x8x32x32
# 上采样到 Bx128x16x64x64
upsampled = self.relu_up1(self.in_up1(self.upsample1(fused))) # Bx128x16x64x64
upsampled = self.res_up1(upsampled) # Bx128x16x64x64
# 上采样到 Bx64x32x128x128
upsampled = self.relu_up2(self.in_up2(self.upsample2(upsampled))) # Bx64x32x128x128
upsampled = self.res_up2(upsampled) # Bx64x32x128x128
# 最终卷积
out = self.final_conv(upsampled) # Bx32x32x128x128
# 调整到 Bx32x16x64x64
out = F.interpolate(out, size=(16, 64, 64), mode="trilinear", align_corners=False) # Bx32x16x64x64
return out
class SimplifiedIdentityEncoder(nn.Module):
def __init__(self, input_channels=32, latent_channels=128):
super(SimplifiedIdentityEncoder, self).__init__()
# 第1层卷积:下采样并增加通道数
self.conv1 = nn.Conv3d(input_channels, 128, kernel_size=3, stride=2, padding=1) # Bx128x8x32x32
self.in1 = nn.InstanceNorm3d(128, affine=True)
self.relu1 = nn.ReLU(inplace=True)
self.res_block1 = ResidualBlock3D_SE(128, 128)
# 第2层卷积:下采样并增加通道数
self.conv2 = nn.Conv3d(128, 256, kernel_size=3, stride=2, padding=1) # Bx256x4x16x16
self.in2 = nn.InstanceNorm3d(256, affine=True)
self.relu2 = nn.ReLU(inplace=True)
self.res_block2 = ResidualBlock3D_SE(256, 256)
# self.res_block3 = ResidualBlock3D_SE(256, 256)
# 第3层卷积:下采样并增加通道数
self.conv3 = nn.Conv3d(256, latent_channels, kernel_size=3, stride=2, padding=1) # Bx128x2x8x8
self.in3 = nn.InstanceNorm3d(latent_channels, affine=True)
self.relu3 = nn.ReLU(inplace=True)
self.res_block4 = ResidualBlock3D_SE(latent_channels, latent_channels)
# self.res_block5 = ResidualBlock3D_SE(latent_channels, latent_channels)
def forward(self, x):
# 第1层
x = self.conv1(x)
x = self.in1(x)
x = self.relu1(x)
x = self.res_block1(x)
# 第2层
x = self.conv2(x)
x = self.in2(x)
x = self.relu2(x)
x = self.res_block2(x)
# x = self.res_block3(x)
# 第3层
x = self.conv3(x)
x = self.in3(x)
x = self.relu3(x)
x = self.res_block4(x)
# x = self.res_block5(x)
return x # 输出形状: Bx128x2x8x8
class SimplifiedAttributeEncoder(nn.Module):
def __init__(self, input_channels=32, output_channels=128):
super(SimplifiedAttributeEncoder, self).__init__()
# 第1层卷积:下采样并增加通道数
self.conv1 = nn.Conv3d(input_channels, 64, kernel_size=3, stride=2, padding=1) # Bx64x8x32x32
self.in1 = nn.InstanceNorm3d(64, affine=True)
self.relu1 = nn.ReLU(inplace=True)
self.res_block1 = ResidualBlock3D_SE(64, 64)
# 第2层卷积:保持空间维度,增加通道数
self.conv2 = nn.Conv3d(64, 128, kernel_size=3, stride=1, padding=1) # Bx128x8x32x32
self.in2 = nn.InstanceNorm3d(128, affine=True)
self.relu2 = nn.ReLU(inplace=True)
self.res_block2 = ResidualBlock3D_SE(128, 128)
# 第3层卷积:进一步增加通道数
self.conv3 = nn.Conv3d(128, output_channels, kernel_size=3, stride=1, padding=1) # Bx512x8x32x32
self.in3 = nn.InstanceNorm3d(output_channels, affine=True)
self.relu3 = nn.ReLU(inplace=True)
self.res_block3 = ResidualBlock3D_SE(output_channels, output_channels)
def forward(self, x):
# 第1层
x = self.conv1(x) # Bx64x8x32x32
x = self.in1(x)
x = self.relu1(x)
x = self.res_block1(x)
# 第2层
x = self.conv2(x) # Bx128x8x32x32
x = self.in2(x)
x = self.relu2(x)
x = self.res_block2(x)
# 第3层
x = self.conv3(x) # Bx512x8x32x32
x = self.in3(x)
x = self.relu3(x)
x = self.res_block3(x)
return x # 输出形状: Bx512x8x32x32
class SimplifiedDecoder(nn.Module):
def __init__(self, latent_dim_identity=128, latent_dim_attribute=128, output_channels=32):
super(SimplifiedDecoder, self).__init__()
# 上采样身份特征: Bx128x2x8x8 -> Bx128x4x16x16
self.upsample_identity1 = nn.ConvTranspose3d(
in_channels=latent_dim_identity,
out_channels=latent_dim_identity,
kernel_size=2,
stride=2,
padding=0
)
self.in_identity1 = nn.InstanceNorm3d(latent_dim_identity, affine=True)
self.relu_identity1 = nn.ReLU(inplace=True)
self.res_block_identity1 = ResidualBlock3D_SE(latent_dim_identity, latent_dim_identity)
# 再次上采样身份特征到 Bx128x8x32x32
self.upsample_identity2 = nn.ConvTranspose3d(
in_channels=latent_dim_identity,
out_channels=latent_dim_identity,
kernel_size=2,
stride=2,
padding=0
)
self.in_identity2 = nn.InstanceNorm3d(latent_dim_identity, affine=True)
self.relu_identity2 = nn.ReLU(inplace=True)
self.res_block_identity2 = ResidualBlock3D_SE(latent_dim_identity, latent_dim_identity)
# 特征融合后处理
self.fuse_conv = nn.Sequential(
nn.Conv3d(
in_channels=latent_dim_identity + latent_dim_attribute,
out_channels=256,
kernel_size=3,
padding=1
),
nn.InstanceNorm3d(256, affine=True),
nn.ReLU(inplace=True),
ResidualBlock3D_SE(256, 256)
)
# 上采样到 Bx256x8x32x32 -> Bx128x16x64x64
self.upsample1 = nn.ConvTranspose3d(
in_channels=256,
out_channels=128,
kernel_size=2,
stride=2,
padding=0
)
self.in_up1 = nn.InstanceNorm3d(128, affine=True)
self.relu_up1 = nn.ReLU(inplace=True)
self.res_up1 = ResidualBlock3D_SE(128, 128)
# 上采样到 Bx128x16x64x64 -> Bx64x32x128x128
self.upsample2 = nn.ConvTranspose3d(
in_channels=128,
out_channels=64,
kernel_size=2,
stride=2,
padding=0
)
self.in_up2 = nn.InstanceNorm3d(64, affine=True)
self.relu_up2 = nn.ReLU(inplace=True)
self.res_up2 = ResidualBlock3D_SE(64, 64)
# 最终卷积层,调整通道数到输出通道
self.final_conv = nn.Sequential(
nn.Conv3d(
in_channels=64,
out_channels=output_channels,
kernel_size=3,
padding=1
),
nn.InstanceNorm3d(output_channels, affine=True),
nn.ReLU(inplace=True)
)
def forward(self, identity, attribute):
"""
identity: Bx128x2x8x8
attribute: Bx512x8x32x32
"""
# 上采样身份特征到 Bx128x4x16x16
identity_upsampled = self.relu_identity1(self.in_identity1(self.upsample_identity1(identity))) # Bx128x4x16x16
identity_upsampled = self.res_block_identity1(identity_upsampled)
# 再次上采样身份特征到 Bx128x8x32x32
identity_upsampled = self.relu_identity2(self.in_identity2(self.upsample_identity2(identity_upsampled))) # Bx128x8x32x32
identity_upsampled = self.res_block_identity2(identity_upsampled)
# 特征融合
fused = torch.cat((identity_upsampled, attribute), dim=1) # Bx128+512=640x8x32x32
fused = self.fuse_conv(fused) # Bx256x8x32x32
# 上采样到 Bx256x8x32x32 -> Bx128x16x64x64
upsampled = self.relu_up1(self.in_up1(self.upsample1(fused))) # Bx128x16x64x64
upsampled = self.res_up1(upsampled) # Bx128x16x64x64
# 上采样到 Bx128x16x64x64 -> Bx64x32x128x128
upsampled = self.relu_up2(self.in_up2(self.upsample2(upsampled))) # Bx64x32x128x128
upsampled = self.res_up2(upsampled) # Bx64x32x128x128
# 最终卷积
out = self.final_conv(upsampled) # Bx32x32x128x128
# 如果需要调整到 Bx32x16x64x64,可以通过下采样
out = F.interpolate(out, size=(16, 64, 64), mode="trilinear", align_corners=False) # Bx32x16x64x64
return out
# 测试 AttributeEncoder
if __name__ == "__main__":
# 假设输入的形状是 Bx32x16x64x64
input_tensor = torch.randn(2, 32, 16, 64, 64) # Batch size: 8
model = FaceFeatureDecouplingModel()
output_tensor = model(input_tensor)
print(output_tensor[0].shape) # 期待输出 Bx512x8x32x32