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