UNet 기반 얼굴 합성 모델의 해석 가능성: 계층별 피처 맵의 역할과 시각화 기법

UNet 아키텍처와 얼굴 합성(Face Fusion)의 결합

딥러닝 기반의 이미지 생성 및 편집 작업에서 UNet은 강력한 인코딩-디코딩 성능으로 인해 널리 사용됩니다. 특히 얼굴 합성(Face Fusion) 시스템에서는 원본 이미지의 정체성(Identity)을 유지하면서 목표 이미지의 자세, 조명, 배경과 자연스럽게 융합하는 조건부 이미지 간 변환(Conditional Image-to-Image Translation) 역할을 수행합니다. 이는 Pix2Pix나 CycleGAN의 생성기 구조와 유사한 맥락에서 작동합니다.

하지만 이러한 시스템은 종종 '블랙박스'로 간주되어, 모델 내부에서 피처가 어떻게 추출되고 재구성되는지 파악하기 어렵습니다. 본 글에서는 UNet의 각 계층별 피처 맵(Feature Map)이 얼굴 합성 과정에서 수행하는 구체적인 역할을 분석하고, 모델의 해석 가능성(Interpretability)을 높이는 방안을 다룹니다.

인코더 단계: 픽셀에서 고수준 의미론적 피처로

UNet의 인코더는 다운샘플링을 통해 공간 해상도를 낮추고 채널 수를 늘리며 계층적 피처를 추출합니다.

초기 계층 (C1 ~ C2): 저수준 시각적 정보

  • C1 (64 채널, 약 H/2 × W/2 해상도): 원본 이미지에 가장 가까운 계층으로, 수평/수직/대각선 엣지, 명암 경계, 초기 질감 등을 감지합니다. 얼굴 이미지에서는 눈썹, 입술, 코 등의 윤곽선이 강조됩니다.
  • C2 (128 채널, 약 H/4 × W/4 해상도): 풀링을 통해 수용 영역(Receptive Field)이 확대됩니다. 눈이나 입과 같은 로컬 파츠의 구조, 피부와 비피부 영역의 구분, 대략적인 조명 방향 등을 포착하기 시작합니다.

심층 계층 (C3 ~ C5): 고수준 정체성 및 의미론

계층해상도주요 추출 정보
C3H/8얼굴 기관의 상대적 위치, 대략적인 표정 분류
C4H/16얼굴형 윤곽, 헤어스타일의 거시적 분포
C5H/32고수준 정체성 임베딩(Face Identity Embedding), 전체적인 자세 추정

C4와 C5 계층의 피처 맵은 인간의 눈으로 '얼굴'을 식별하기 어려울 정도로 추상화되어 있지만, '누구인가'를 결정하는 핵심적인 정체성 정보를 인코딩하고 있습니다. 이는 교차 이미지 얼굴 합성이 성립할 수 있는 기반이 됩니다.

스킵 커넥션과 디코더의 재구성 메커니즘

스킵 커넥션(Skip Connection)은 단순한 데이터 복사가 아닌, 다중 스케일 정보의 융합을 담당합니다. 저수준 피처(C1-C2)는 융합 후 이목구비의 정확한 공간적 위치를 보장하며, 고수준 피처(C4-C5)는 '어떻게 보여야 하는지'에 대한 의미론적 가이드를 제공합니다.

디코더는 업샘플링을 통해 해상도를 복원하고, 인코더의 대응하는 피처 맵과 채널 차원에서 결합(Concatenate)합니다.

# 디코더의 피처 융합 의사 코드
upsampled_tensor = F.interpolate(decoder_input, scale_factor=2, mode='bilinear')
skip_connection_data = encoder_outputs['layer_3']
merged_features = torch.cat([upsampled_tensor, skip_connection_data], dim=1)
refined_output = convolutional_block(merged_features)

최종 출력층은 일반적으로 단일 컨볼루션 커널과 Tanh 활성화 함수를 사용하여 피처를 [-1, 1] 범위의 RGB 이미지 공간으로 매핑합니다.

PyTorch Hook을 활용한 피처 맵 추출 및 시각화

모델의 내부 동작을 분석하기 위해 PyTorch의 Hook 메커니즘을 사용하여 순전파(Forward Pass) 중간의 활성화 값을 추출할 수 있습니다. 다음은 재구성된 피처 수집기 구현 예시입니다.

import torch
import torch.nn as nn
import matplotlib.pyplot as plt

class ActivationCollector:
    def __init__(self, network):
        self.network = network
        self.activations = {}
        self._hook_handles = []
        self._attach_hooks()

    def _attach_hooks(self):
        for layer_name, module in self.network.named_modules():
            if isinstance(module, nn.Conv2d):
                handle = module.register_forward_hook(self._create_hook_fn(layer_name))
                self._hook_handles.append(handle)

    def _create_hook_fn(self, layer_name):
        def hook_fn(module, inp, out):
            self.activations[layer_name] = out.detach().cpu()
        return hook_fn

    def clear_hooks(self):
        for handle in self._hook_handles:
            handle.remove()

# 사용 예시
face_fusion_net = load_pretrained_unet()
collector = ActivationCollector(face_fusion_net)

input_batch = preprocess_input_image("target_face.jpg")
with torch.no_grad():
    _ = face_fusion_net(input_batch)

# 특정 계층의 활성화 맵 확인
target_layer_activation = collector.activations['encoder.stage1.conv']
print(f"Activation shape: {target_layer_activation.shape}")

# 채널별 시각화
fig, axes = plt.subplots(2, 3, figsize=(12, 6))
for idx, ax in enumerate(axes.flatten()):
    ax.imshow(target_layer_activation[0, idx, :, :], cmap='viridis')
    ax.set_title(f"Channel {idx}")
    ax.axis('off')
plt.tight_layout()
plt.show()

collector.clear_hooks()

해석 가능성을 통한 모델 디버깅 및 최적화

각 계층의 피처 맵 역할을 이해하면, 얼굴 합성 시스템의 2차 개발 및 문제 해결에 실질적인 이점을 얻을 수 있습니다.

결함 진단 및 품질 개선

문제 현상예상되는 피처 이상 원인
융합된 이목구비의 위치 어긋남저수준 피처(C1-C2)의 공간 정렬 실패
얼굴 디테일 뭉개짐 및 흐림고수준 피처(C4-C5) 표현력 부족 또는 스킵 커넥션 손실
피부 톤 및 색상 불일치디코더 후단부의 색상 채널 재구성 오류
부자연스러운 경계선 및 합성 흔적중간 계층의 매끄러운 전환(Smooth Transition) 메커니즘 부재

모델 경량화 및 커스텀 손실 함수 설계

엣지 디바이스 배포를 위해 모델 경량화가 필요한 경우, 피처의 중요도에 따라 불필요한 채널을 가지치기(Pruning)할 수 있습니다. 예를 들어, 기본 구조와 정체성 표현을 위해 C1, C2, C5와 해당 스킵 커넥션은 유지하면서, 중간 컨볼루션 채널 수를 줄여 추론 속도를 높일 수 있습니다.

또한, 계층별 의미론을 활용하여 다중 스케일 감독 손실(Multi-scale Supervised Loss)을 설계하면 합성의 자연스러움을 크게 향상시킬 수 있습니다.

# 중간 계층 L1 손실(지각적 일관성) 추가 예시
intermediate_loss = nn.L1Loss()(
    collector.activations['decoder.stage2'],
    target_network_activations['decoder.stage2']
)
total_objective = primary_loss + 0.25 * intermediate_loss

태그: UNet FaceFusion PyTorch FeatureVisualization ModelInterpretability

8월 22일 19:31에 게시됨