HiGAN bedroom — v2 report

침실 GAN의 잠재공간을
CAM으로 해석하기

v1: Colab 노트북 3개에서 시작한 GAN inversion 실험.
v2: gradient가 끊긴 인버전을 고치고, 도메인 특화 인코더를 만들고,
잠재공간이 이미지의 어디에 작용하는지 픽셀 saliency로 직접 시각화한 .py 파이프라인.

25sections
15analysis methods
∂²I/∂α²higher-order
JKIIT '26후속 논문 게재
SCROLL

Related publication · 후속 논문 한국정보기술학회논문지 (JKIIT) · 2026 · Vol. 24, No. 4, pp. 191–203
StyleGAN 잠재 공간 분석과 Diffusion 모델을 통한 조명 배치 및 시각화 시스템
Lighting Placement and Visualization System via StyleGAN Latent Space Analysis and Diffusion Models
하동우 · 이승재 · 안지홍 · 유지혜 · 홍규린 · 이정현 · 유길상
StyleGAN latent space analysis SVM Grad-CAM Diffusion / Paint-by-Example lighting placement
이 보고서가 정리한 HIGAN 침실 잠재공간 분석은 위 후속 논문의 사전 작업에 해당합니다. 논문은 SVM과 Grad-CAM으로 잠재공간에서 최적 조명 위치를 식별하고, Paint-by-Example diffusion으로 공간 구조를 유지하면서 조명을 합성 — 본 보고서의 *backward gradient saliency*(§06), *per-layer 분해*(§07), *intermediate Grad-CAM*(§15) 같은 도구가 그 논문의 분석 기반. 기존 방식 대비 9–15× 정확도 향상을 보고함.
KCI 논문 페이지 →

한 보고서가 아니라, 다섯 라운드의 대화

이 페이지는 한 번에 만들어지지 않았다. 사용자의 질문이 매 단계 분석을 낳았고, 그 결과가 다음 질문을 낳는 식으로 5번의 라운드를 거쳤다. 아래는 *왜 어떤 분석이 등장했는지*의 시간순 흐름. 각 step은 해당 섹션으로 점프 가능.

Phase 1 — v1 분석 → v2 빌드
01
v1 노트북 분석 → autograd 단절 발견
"GAN의 잠재공간을 CAM으로 본다"는 가설로 시작. v1 노트북 3개를 읽다가 easy_synthesize가 numpy detach로 gradient를 끊어놓는 사실 확인.
02
미분 가능 wrapper + .py 패키지 + 인코더 학습
로컬 GPU(RTX 3070)용 모듈 분리. fp16 NaN 회피, 합성 supervision 인코더 20k → 40k iter.
03
forward perturbation으로 첫 saliency
±δ로 픽셀 차이 측정. 8개 boundary 모두 의미상 정확한 위치에 활성. 하지만 다음 라운드에서 이 방식의 한계가 드러남.
Phase 2 — 사용자 질문이 framing pivot 트리거
04
"역전파 개념이 없는데?" → JVP backward saliency (헤드라인)
"근데 cam처럼 보자는거잖아 역전파 개념이 없는데?"
사용자가 forward perturbation은 mechanistically Grad-CAM이 아니라 finite-difference라고 정확히 지적. torch.func.jvp로 generator 통과하는 진짜 backward 추가. per-sample saliency가 *각 침실의 lamp/창문/우드 프레임을 정확히 pinpoint* — 헤드라인 결과.
Phase 3 — "더 깊이 파보자" 라운드 1
05
Per-layer 분해 + 8×14 매트릭스
"어느 layer가 attribute를 만드는가?". indoor_lighting은 layer 10/11(fine), view는 layer 1/2(coarse). 8 attribute × 14 layer 완전 매트릭스로 boundary가 layer-specific하게 학습됐음을 입증.
06
Disentanglement / local edit / encoder attention
8×8 saliency 상관 매트릭스 (view만 직교, 나머지 7개 mutually entangled). saliency를 mask로 쓰는 국소 편집. 인코더 reverse-mode attention (mirror question — image→wp는 global, wp→image는 local).
07
Random direction discovery — HiGAN 8개 외 일반화
"이 도구가 HiGAN의 boundary에만 묶인 거 아닌가?". W+ 공간 임의 단위 방향 96개 → top-5% mass concentration으로 sort → layer 1-4의 구조적 hotspot(창문/문/벽)을 학습 0으로 발견.
Phase 4 — "결과 잘 나오니까 더 가보자" 라운드 2
08
Compositional / robustness / intermediate / taxonomy
두 attribute 동시 편집의 선형/비선형성 (corr 0.55–0.97), 32-sample consistency, conv block 활성화의 ∂(act)/∂α (진정한 Grad-CAM 메커니즘), 256개 random direction의 K-means 자동 분류.
09
CLIP zero-shot — 발견된 cluster의 의미 자동 라벨링
cluster 2 (n=37, layer 1)이 "a view through a window"로 자동 식별. HiGAN의 view boundary를 라벨/분류기 0개로 재발견. unsupervised attribute discovery 완성.
Phase 5 — 학습 동역학 + 비선형성 + OOD 검증
10
Encoder ckpt evolution + ∂²I/∂α² + 시각적 입증
학습 1k→40k에서 saliency-GT 상관이 45배 향상 (recon MSE는 18%만). view의 비선형성 비율 23.2 (다른 attribute의 40배) — §13 합성 간섭의 직접 원인. 21프레임 sweep에서 saliency 자체가 변하는 모습으로 시각 입증.
11
실제 LSUN bedroom 사진으로 OOD 검증
진짜 침실 4장에 전체 파이프라인. optim inversion은 OOD에서도 LPIPS 0.028–0.051, encoder는 0.41–0.59. *합성 supervision의 한계 + saliency는 reconstruction quality보다 robust*.
읽는 순서 가이드

처음 보시면 §02 (v1 발견) → §06 (헤드라인 saliency) → §17 (CLIP rediscovery)만 따라가도 핵심 흐름 파악 가능. 더 깊이 들어가려면 §13 (compositional) ↔ §19 (∂²I 설명), §15 (intermediate Grad-CAM), §22 (process journal 시간순 7 walls)을 추천.


사고 과정: 무엇을 알고 싶었는가

HiGAN bedroom256은 침실 이미지를 생성하는 StyleGAN 변형이다. 부수적으로 학습된 semantic boundary들은 잠재공간 안에 indoor_lighting, wood, view 같은 의미 방향이 존재함을 보여준다. v1 노트북은 거기서 멈췄다. v2는 한 단계 더 묻는다.

Q1 · 인버전
실제 침실 사진을 잠재공간 어디로 매핑할 수 있는가?
최적화 기반과 인코더 기반, 두 방식의 정확도와 속도를 비교한다.
Q2 · 편집
매핑된 잠재코드에 의미 방향을 더하면 이미지가 어떻게 변하는가?
indoor_lighting · wood · view 등 8개 boundary에 대한 ±d 편집 sweep을 시각화한다.
Q3 · 해석
의미 방향이 이미지의 어디에 작용하는가?
분류기 없이, 잠재공간 perturbation만으로 픽셀 saliency를 누적하는 CAM-style 분석.
HiGAN bedroom256 generator sanity check — 8 random samples
FIG 0. HiGAN bedroom256 생성기 8장 무작위 샘플 (256×256, RTX 3070, 1초 미만)

최적화 인버전이 사실은 동작하지 않고 있었다

v1 노트북의 최적화 기반 인버전은 매 step마다 easy_synthesize(latent_np, ...)를 호출했다. 이 함수는 내부적으로 get_value()로 결과를 detach + numpy uint8 변환하는데, 그 순간 잠재→이미지의 autograd 그래프가 끊긴다. 손실에 .requires_grad_()를 다시 붙여도 그건 optimizer가 추적하는 latent_code무관한 새 텐서다. 결과적으로 backward()가 produced하는 latent_code의 gradient는 0이 되고, "최적화"는 random 초기화 10번 중 운 좋은 하나를 고르는 셈이 되었다.

v1 · 끊긴 gradient

noteboook_encoder.ipynb

매 step에서 latent_code.detach().cpu().numpy()로 변환 후 generator에 투입. 반환값을 torch.tensor(...)로 감싸 .requires_grad_()를 호출하지만, 이는 새 leaf tensor일 뿐 latent와 그래프가 연결되어 있지 않다.

v2 · 보존된 gradient

HiGANGenerator.synthesize()

genforce의 G.net.synthesis(wp)를 직접 호출하는 얇은 래퍼. nn.Module로서 frozen 상태로 유지하면서 wp 입력 → 이미지 출력 경로를 그대로 노출. 잠재의 .grad가 실제로 채워진다.

# v1 — easy_synthesize는 detach + numpy로 그래프를 끊는다
latent_code_np = latent_code.detach().cpu().numpy()
generated     = model.easy_synthesize(latent_code_np, latent_space_type='wp')
img_t = torch.tensor(generated['image'].transpose(0,3,1,2))/255.0
img_t.requires_grad_()   # 새 leaf — latent_code와 무관함
loss.backward()          # latent_code.grad == None

# v2 — synthesis()를 직접 호출, gradient flow 유지
class HiGANGenerator(nn.Module):
    def synthesize(self, wp):                # wp.requires_grad=True OK
        return self._net.synthesis(wp)     # torch tensor, autograd alive

wp = G.sample_wp(1).requires_grad_(True)
img = G.synthesize(wp)
loss = (img - target).pow(2).mean()
loss.backward()
assert wp.grad.norm() > 0          # 0.30 in our test
검증

같은 입력 이미지(generator self-test)에 대해 v1 레시피는 1500 step × 10 init 후에도 loss가 5.6 → 2.5 (54% 감소) 수준에서 멈추는 반면, v2 인버전은 1000 step × 1 init만으로 5.6 → 0.027 (99.5% 감소)까지 떨어졌다. 시각적으로도 거의 완벽한 재구성.

target image
target (synthetic, GT exists)
reconstruction by v2 optim inversion
v2 optim recon — 1000 step

.py 패키지로 다시 짜기

Colab 의존성을 제거하고, 모듈 단위로 책임을 분리했다. RTX 3070 8 GB에 맞춰 배치/메모리를 조정하고, fp16 autocast가 StyleGAN 합성 op에서 NaN을 만드는 사실을 발견해 fp32로 고정했다 (그래도 5.4 GB만 사용).

01
Generator
frozen
differentiable
w/wp/image API
02
Optim Inversion
Adam on W+
lr ramp schedule
baseline 측정
03
Encoder
ResNet50 backbone
+ 3 scale necks
+ 14 style heads
04
합성 supervision
G(wp)→encoder→wp_pred
w_mse + image loss
05
Manipulate
torch-native
boundary 8개
distance sweep
06
CAM
±δ perturbation
pixel diff 누적
분류기 불필요
higan_dev/ ├── higan_dev/ │ ├── config.py # dataclass + YAML │ ├── generator.py # 미분 가능한 HiGAN wrapper (이번 v2의 핵심 수정) │ ├── losses.py # VGG perceptual / LPIPS / TV / 통합 loss │ ├── manipulate.py # torch-native boundary 조작 │ ├── encoder/ │ │ ├── model.py # ResNet50 + multi-scale necks + 14 style heads │ │ └── train.py # 합성 supervision 학습 루프 │ ├── inversion/ │ │ ├── optim.py # gradient가 흐르는 W+ 인버전 │ │ └── encode.py # 학습된 인코더로 1-shot 인버전 │ └── cam/ │ └── diff_map.py # 잠재 perturbation → 픽셀 saliency ├── scripts/ # 01 download → 06 cam → 09 edit_real → 10 final figure ├── configs/ default.yaml └── data/, out/ # 자산 + 산출물
3070
RTX 8 GB
target hardware
5.4
GB peak (B=8)
fp32, generator + encoder + LPIPS
0.32
sec/iter
학습 속도 측정값
1h 47m
20k iter 소요
예측 ↔ 실측 일치
디버그 노트

fp16 autocast로 학습을 시작했더니 image·lpips·perceptual loss가 모두 NaN. isolated 테스트 결과 StyleGAN bedroom의 synthesis op이 fp16에서 inf를 토하는 패턴. fp32로 전환하니 메모리는 4.4 → 5.4 GB로 늘었지만 8 GB 안에 충분히 들어가고 NaN은 사라졌다.


두 인버전 방식, 정확도 vs 속도

같은 4장의 침실 이미지(generator로 생성, GT wp 존재)에 대해 (a) 1000 step Adam 최적화와 (b) 학습된 인코더 1-pass를 비교했다. 정확도는 최적화가 압승, 속도는 인코더가 600배 우위. 실시간 편집이나 대량 처리에는 인코더가, 단발 고정확도 매핑에는 최적화가 적합.

inversion comparison: target | optim | encoder
FIG 4. 행 = 4개 테스트 이미지, 열 = target | optim 1000step | encoder 1-pass. 인코더는 공간 구조는 잘 잡지만 색·텍스처가 평균화됨 (under-fit 신호).
imagepixel MSE (optim)pixel MSE (enc)LPIPS (optim)LPIPS (enc)
img_000.02580.08640.03140.3087
img_010.00830.03300.03130.3754
img_020.00530.03880.02680.4403
img_030.01770.09850.03270.3423
mean0.01430.06420.03050.3667
A
optim 1000-step: pixel MSE 0.014, LPIPS 0.031, ~30 s / 장. v1이 풀지 못했던 정확 재구성 — gradient flow 한 줄 고친 결과.
B
encoder 1-pass (40k iter): pixel MSE 0.064, LPIPS 0.367, ~0.05 s / 장. 600× 빠르지만 색·디테일은 미달. 20k → 40k 학습 시 LPIPS 0.40 → 0.37로 개선됨. 200k까지 가면 ~0.20대 도달 예상.
×
v1 pSp encoder (FFHQ): 도메인 mismatch로 침실에 부적합. v2에서는 제거.
encoder reconstruction at iteration 3000
iter 3000 — 공간 구조만 잡힘
encoder reconstruction at iteration 20000
iter 20000 — 색·구조 모두 개선

각 패널: 좌 4장 = target / 우 4장 = encoder reconstruction


잠재 방향이 이미지의 어디에 작용하는가 — 첫 번째 답

각 boundary 방향 b에 대해 잠재코드를 ±δ만큼 흔들고, 결과 이미지 차이를 64개 샘플에 걸쳐 평균. 코드가 짧고 backward가 필요 없어서 가장 단순한 분석. 여기서 spatial 패턴이 분명히 드러나지만, 다음 섹션의 진짜 gradient 결과와 비교하면 차이가 큼.

# forward-only finite difference (no backward)
for i in range(N):
    wp = G.sample_wp(1)
    img_pos = G.synthesize(wp + δ * boundary)
    img_neg = G.synthesize(wp - δ * boundary)
    abs_diff += |img_pos - img_neg|              # 픽셀별 변화량
saliency = abs_diff / N                          # (H, W) 히트맵
indoor_lighting saliency (perturbation)
indoor_lighting
상단 중앙 — 조명·천장
view saliency (perturbation)
view
좌·우 상단 수직 띠 — 창문
wood saliency (perturbation)
wood
중앙 띠 — 침대 프레임·바닥
carpet saliency (perturbation)
carpet
하단 — 바닥
glossy saliency (perturbation)
glossy
중앙 — 침구·반사면
정직한 한계 — "CAM-style"이 정확한 표현이 아니다

위 결과는 backward를 쓰지 않는다. forward 두 번을 빼서 픽셀 차이를 본 것뿐. 분류기를 안 쓰니 Grad-CAM과는 다른 종류의 분석이고, 정확히 말하면 occlusion-style 또는 finite-difference 민감도에 가깝다. 1차 근사로는 직접 gradient를 계산한 것과 같은 양을 측정하지만(아래 등식 참조), 실제로 "역전파"는 흐르지 않는다. 이걸 진짜 gradient로 바꾸려면 — generator를 미분 가능하게 만들었으니 — 한 단계만 더 가면 된다. §06에서 그 한 단계 진행.


진짜 역전파를 흘려서 같은 질문을 다시 답하기

Boundary 방향 b를 따라 잠재공간에서 한 발 움직이는 양을 스칼라 α로 두고, 출력 이미지의 모든 픽셀에 대해 ∂I[c,h,w]/∂α를 직접 계산한다. 1입력(α) → 다출력(3·H·W) 구조이므로 forward-mode 자동미분(JVP)이 효율적이고, 이는 generator의 backward graph를 한 번 통과하는 연산이다. 분류기 score 자리에 "잠재 방향"이 들어간 Grad-CAM의 정신적 자매.

from torch.func import jvp

# α: scalar perturbation along boundary direction
def G_along(α):
    wp_p = wp + α.view(B, 1, 1) * b_layered     # (B, L, D)
    return G.synthesize(wp_p)                  # (B, 3, H, W)

# JVP: forward-mode autodiff. tangent direction = unit step in α.
# Result is the *exact* per-pixel sensitivity ∂I/∂α at α=0.
img, dimg_dα = jvp(G_along, (torch.zeros(B),), (torch.ones(B),))

saliency = dimg_dα.abs().mean(dim=1)         # (B, H, W) per-pixel
수학적 동치

finite-diff ≈ true gradient (1차)

I(wp + δb) − I(wp − δb)
 ≈  2δ · (∂I/∂wp) · b
 =  2δ · ∂I/∂α

1차 근사로는 같지만, 실제로 backward가 흘러야 — (1) noise가 사라지고, (2) generator 내부의 비선형이 정확히 반영되며, (3) δ 같은 hyperparameter도 사라진다.

왜 forward-mode가 맞는가

1입력 → 다출력

α는 sample당 스칼라 1개. 출력은 픽셀 3·256·256 ≈ 200k. reverse-mode는 vector → scalar 그래디언트에 효율적(우린 그 반대), forward-mode JVP는 입력 차원이 작을 때 단 한 번의 backward graph traversal로 전 픽셀 그래디언트를 얻는다.

엔지니어링 노트 — JVP 통과 패치

genforce StyleGAN의 synthesis.forwardself.lod.cpu().tolist()가 있어 JVP의 dual tensor를 깨뜨린다. 침실256은 lod=0.0 고정이므로 파이썬 float로 캐싱하는 5줄 monkey-patch로 우회. 한 번 패치하면 8개 boundary × 64샘플 분석이 RTX 3070에서 ~10초.

개별 샘플별 saliency — 진짜 헤드라인 결과

각 행 = 4개의 무작위 침실. 컬럼: 원본 | gradient saliency | overlay. 역전파 신호가 각 침실의 실제 lamp/창문/우드 프레임을 정확히 짚어낸다.

indoor_lighting per-sample gradient saliency
FIG 6.1 · indoor_lighting   침실마다 켜진 램프/창문/조명 영역에 정확히 hot spot. 같은 boundary가 각 scene에 맞춰 다른 위치를 가리킴.
wood per-sample gradient saliency
FIG 6.2 · wood   침대 헤드보드, 침대 프레임, 우드 패널 윤곽선이 선명하게 잡힘.
view per-sample gradient saliency
FIG 6.3 · view   창문/문 형상 + 그 너머의 풍경 경계에 saliency 집중. layer 0–4(저주파) 조작 결과.
v2의 진짜 결론

perturbation map은 평균적인 spatial 패턴만 보여줬다. 진짜 gradient는 scene-specific 객체를 pinpoint한다 — "이 침실의 indoor_lighting boundary는 저 램프를 통제한다" 수준의 답이 가능. 잠재공간의 의미 방향이 이미지의 어디에 어떻게 작용하는가의 질문에 분류기 없이 차원 정확성으로 답한 결과. 중간 conv layer의 ∂(activation)/∂α는 → §15에서 진짜 Grad-CAM 메커니즘.


"이 attribute는 어느 layer가 만드는가"

indoor_lighting boundary는 layer 6–11에 영향을 준다 (HiGAN 메타데이터). 그렇다면 그 6개 layer 중 어느 것이 *어디*를 만드는가? JVP에 layer mask만 바꿔 한 layer에만 boundary 방향을 두고 6번 다시 돌렸다. 추가 학습 0.

indoor_lighting per-layer saliency
FIG 7.1 · indoor_lighting   layer 6→11. 낮은 layer는 약하고, layer 10·11(가장 fine)이 lighting 효과의 대부분을 carry. StyleGAN 계층의 "fine = 색·텍스처" 가설과 정렬.
wood per-layer saliency
FIG 7.2 · wood   layer 9·10이 침대 프레임 윤곽·우드톤을 만들고, 11은 표면 grain. mid–fine 영역.
view per-layer saliency
FIG 7.3 · view   coarse layer 0–4. 0은 거의 정보 없고, layer 1·2가 창문 형상의 핵심 윤곽선, 3·4가 벽·문 같은 큰 구조.
새 결과

"보일러플레이트 attribute → 하나의 saliency"가 아니라 "attribute → 6개 layer × 6개 saliency"의 분해가 가능하다. 같은 indoor_lighting인데 layer 10이 만드는 효과(전체 분위기)와 layer 11이 만드는 효과(국소 highlight)가 다르다. 이건 우리가 알기로는 페이지가 처음 시각화하는 각도 — 공개된 HiGAN 결과는 모두 layer 통합형.


모든 attribute를 모든 layer에 적용해보면?

HiGAN 메타데이터는 각 attribute의 canonical layer 범위만 알려준다 (예: indoor_lighting → layers 6–11). 하지만 boundary 방향을 다른 layer에 강제로 적용하면 어떻게 될까? 8 attribute × 14 layer = 112 cell의 완전 매트릭스. 초록 테두리 = canonical layers, 빨강 = peak intensity layer.

complete 8 attribute by 14 layer saliency matrix
FIG 8.1   각 cell = 해당 attribute boundary 방향을 layer i에만 적용했을 때의 saliency. 모두 같은 base latent 16개 평균.
놀라운 발견 — boundary는 layer-specific하다

거의 모든 attribute의 peak intensity layer가 layer 0(StyleGAN의 constant input layer). 그런데 layer 0의 saliency는 전 화면을 무차별적으로 흔들 뿐, attribute 의미를 만들지 않음 — 단순히 큰 effect만 줌. HiGAN 저자가 손으로 잡은 canonical layers 6–11 (또는 view의 0–4)에서 saliency는 magnitude는 작아도 의미상 정렬된 spatial 패턴이 명확히 등장. 즉 "boundary 벡터는 layer-specific하게 학습된 것" — 같은 512차원 벡터를 다른 layer에 갖다 붙이면 효과는 크지만 의미는 사라짐.

실용적 함의

이는 GAN inversion / boundary 편집에 직접 함의 — boundary를 다른 layer 범위에 옮길 때 magnitude만 보고 "효과가 크다"고 판단하면 안 됨. 실제 attribute 의미는 layer 6–11 범위에서 더 작은 saliency로 표현됨.


8개 attribute는 정말 독립적인가?

HiGAN의 8 boundary가 의미적으로 깔끔하게 분리돼 있는지 정량 측정. 32개 공유 latent에 대해 각 boundary의 grad-saliency를 계산하고, 모든 쌍의 픽셀별 Pearson 상관계수를 측정. 높을수록 entangled.

8x8 absolute saliency correlation matrix
FIG 8.1   |abs| saliency 픽셀별 Pearson. 진한 빨강 = 강한 entanglement. view 행/열만 옅음 → 다른 7개와 분리됨. 7×7 우측 블록은 모두 0.6+ 의 강한 상관 (표면 텍스처 류 attribute들이 같은 픽셀들에서 작동).
most entangled (top 5)

표면 텍스처 클러스터

cluttered_space ↔ glossy   0.83
dirt         ↔ glossy   0.81
carpet       ↔ wood     0.77
cluttered_space ↔ dirt     0.76
carpet       ↔ indoor_lighting 0.75

모두 침대·바닥·벽 같은 surface region에서 작동하는 attribute들. 대체로 하나를 켜면 다른 것도 따라 변함.

most disentangled (lowest)

view만 진정한 직교

view ↔ scary                 0.34
view ↔ cluttered_space    0.33
view ↔ indoor_lighting     0.32

view는 layer 0–4 (coarse, 구도/창문 형상)에서만 작동. 나머지는 layer 6–11 (fine, 텍스처). layer 분리가 곧 의미 분리.

새 분석 결과

HiGAN의 8개 boundary는 "view"와 "그 외 7개"의 두 클러스터로 갈린다. 7개 클러스터 내부에서는 의미 단어(carpet/wood/dirt/glossy 등)가 다르더라도 saliency map은 0.6–0.83의 강한 상관 — 즉 "같은 픽셀 영역에 다른 라벨이 붙은 것"에 가깝다. 이는 HiGAN 원논문이 다룬 disentanglement 평가의 forward-mode JVP 버전. 추가 학습 0. 다만 정적 disentanglement ≠ 동시 편집 시 disentanglement — §13에서 구별.


"램프만 켜고 침구는 그대로" — saliency를 마스크로 쓰기

grad-saliency map을 가우시안 블러 + threshold로 soft mask로 변환한 뒤, attribute 편집을 그 마스크 안에서만 적용: I_local = I_edited · m + I_orig · (1 − m). 글로벌 편집의 부작용(다른 영역 변색 등)을 즉시 제거. saliency가 단순 시각화가 아니라 *실용 마스크*로 동작함을 보임.

saliency-guided local edit grid
FIG 9.1   동일 침실에 indoor_lighting / wood / view boundary 적용. 컬럼: original / saliency / soft mask / global edit (전 픽셀) / local edit (saliency 영역만). indoor_lighting 행에서 글로벌 편집은 화면 전체가 따뜻해지지만, local edit은 lamp 영역만 바뀌고 침구는 보존.
기능적 의미

Inversion → saliency → masked edit 의 완성된 도구 체인이 만들어짐. 실제 사진을 받아서 "lamp 밝기만 +3" / "wood 톤만 –2" 같은 부분 편집을 한 번의 forward + backward pass로 수행. 이는 GAN inversion + boundary editing 라인의 표준 산출물 — 통상 attribute별 마스크를 따로 학습해야 했던 것을 saliency map으로 0-shot 대체.


인코더는 입력의 어디를 보고 wp를 결정하는가

Generator-side로 봤던 같은 도구를 인코더에 거꾸로 적용. s(x) = ⟨E(x)manipulate_layers, b̂⟩의 스칼라를 계산하고, reverse-mode autograd로 ∂s/∂x를 측정. "boundary 방향으로 인코더가 wp를 옮기게 만드는 입력 픽셀"의 위치.

encoder attention indoor_lighting
indoor_lighting
encoder attention view
view
비대칭 발견

Generator saliency는 좁고 sharp (램프, 침대 프레임 등 핵심 객체에 집중). 그런데 encoder attention은 훨씬 diffuse — 인코더가 입력 전체를 두루 보고 latent를 추정하기 때문. 즉 두 방향이 비대칭: "이미지 → wp"는 global 통합, "wp → 이미지"는 local 작용. 또한 인코더가 under-fit 상태(LPIPS 0.37)라 attention이 더욱 노이지한 측면도 있음.


학습 없이 새 의미 방향 찾기

HiGAN의 8 boundary 외에 W+ 공간에서 96개 임의 단위 방향을 뽑고 각각의 grad-saliency 계산. "top-5% 픽셀에 mass가 얼마나 집중되는가" 메트릭으로 정렬해서, spatially 의미 있는 방향만 추림. 학습 없이, 라벨 없이, attribute 분류기 없이.

discovered random directions gallery
FIG 11.1   96개 후보 중 가장 spatial 집중 8개. 컬럼: heatmap / overlay. 거의 전부 layer 1–4 (coarse 구조)에서 추출되며, 결과는 창문/문 윤곽, 벽 모서리, 침대 라인 같은 구조적 위치를 자동으로 짚음.
의의

이 도구가 HiGAN의 손 큐레이팅 boundary에만 의존하지 않음을 보임. W+ 공간 어디든 찔러보면 그 방향이 이미지 어디에 작용하는지 즉시 답이 나옴. 미래 계획은 이미 실현됨: §16에서 K-means clustering으로 8개 attribute family를 자동 분리하고, §17에서 CLIP zero-shot으로 라벨 부여까지 완성.


두 attribute를 동시에 켜면 saliency는 더해질까, 간섭할까?

α·b₁ + β·b₂ 로 동시 편집한 결과의 saliency를 (1) 직접 JVP 계산한 값과 (2) 개별 saliency의 단순 합과 비교. 픽셀별 Pearson 상관계수로 정량 측정. 0.95+면 "선형 합성", 그 이하면 "비선형 간섭".

compositional editing analysis grid
FIG 12.1   5개 attribute 쌍에 대해, 각 행: sal(a) | sal(b) | sal(a+b) actual | sal(a)+sal(b) expected | |actual−expected|.
선형 합성 (corr > 0.95)

같은 layer cluster 내부

indoor_lighting + wood       0.977
carpet           + wood                  0.970

둘 다 W+ layers 6–11에서 작동. 같은 활성화 영역에서 독립 변수처럼 합산. 안전한 다중 편집.

비선형 간섭 (corr < 0.6)

cluster를 가로지르는 쌍

wood           + view                  0.547
indoor_lighting + view       0.518

view(layer 0–4)와 표면 텍스처(layer 6–11)는 다른 synthesis depth에서 작동. 동시 편집 시 generator 내부 비선형이 saliency를 새 패턴으로 만듦.

Disentanglement 매트릭스와의 차이

섹션 08의 정적 disentanglement에서 view는 모든 attribute와 직교(corr 0.32). 그런데 동시 편집에서는 view ↔ wood/lighting의 상관이 0.55로 올라감. "독립이라고 측정된 attribute도 함께 편집하면 비선형 간섭이 발생". 이건 그동안 GAN 편집 논문에서 따로 측정되지 않은 각도. 비선형성의 원인은 → §19 ∂²I/∂α² 분석.


attribute별 saliency가 침실에 따라 얼마나 흔들리는가

같은 base latent 32개 위에서 각 attribute의 saliency map을 따로 계산하고, 32×32 sample-pair 상관계수의 평균(consistency)과 픽셀별 std/mean(CV)를 측정. consistency 높음 = "이 attribute의 spatial 패턴이 침실 종류와 무관하게 안정적".

attributeconsistencyCV (std/mean)
glossy+0.0451.05
cluttered_space+0.0401.23
dirt+0.0341.06
wood+0.0311.20
indoor_lighting+0.0261.16
carpet+0.0221.14
scary+0.0151.24
view+0.0112.05
per-attribute saliency mean and std across 32 samples
FIG 13.1   위 행: 각 attribute의 32-sample 평균 saliency. 아래 행: 픽셀별 std (변동성). view 열의 std가 가장 다이내믹하고 거의 다른 패턴.
관찰

모든 attribute의 consistency가 0.011 ~ 0.045 — 즉 saliency는 본질적으로 scene-specific. 이는 섹션 06의 헤드라인 결과(scene-specific 객체 pinpoint)와 일관됨. view의 CV가 압도적으로 높은 건 창문 위치가 scene마다 천차만별이라 그렇다 — view boundary는 해석은 동일하지만 작용 위치는 매번 다름.


진짜 Grad-CAM에 가장 가까운 형태 — 중간 conv 활성화의 미분

Grad-CAM은 분류기의 conv feature map에 대한 gradient를 본다. 우리 generator는 분류기는 아니지만, 중간 conv block의 활성화에 대한 ∂(activation)/∂α를 계산할 수 있다 — 같은 mechanism. StyleGAN bedroom의 7개 block (4×4 → 8 → 16 → 32 → 64 → 128 → 256)에서 attribute가 *언제* spatial로 등장하는지 추적.

intermediate-layer saliency for 3 attributes across 7 blocks
FIG 14.1   각 행 = attribute, 각 열 = synthesis block (4 → 256 px). 모든 셀은 같은 표시 크기로 업샘플.
indoor_lighting / wood

block 0–2: 비활성

이 두 attribute는 W+ layer 6–11을 사용. block 0(4px)~block 2(16px)는 W+ layer 0–5만 입력 받으므로 perturbation 효과가 정확히 0. block 3(32px)부터 saliency 등장, block 5(128px)에서 가장 sharp.

view

block 0부터 spatial

view는 W+ layer 0–4 사용. block 0(4×4)에서 이미 fuzzy한 spatial 패턴이 등장하고 (전체 구도), block 1–2(8/16px)에서 창문/벽 형상이 분명해지고, 이후 block들은 이를 정제·확대.

결론 — StyleGAN의 coarse-to-fine 흐름이 saliency depth에 그대로 보임

이는 단지 W+ layer 인덱스의 메타데이터가 아니라, generator의 깊이 방향을 따라 attribute가 어떻게 진화하는지의 연속적인 그림. 이게 *진정한 의미의 Grad-CAM 메커니즘* — feature map gradient를 따라간 결과. JVP는 forward 한 번 + backward 한 번.


학습 없이, 라벨 없이 GAN이 만들 수 있는 attribute family를 자동 발견

W+ 공간에서 임의 단위 방향 256개를 샘플링하고, 각각의 grad-saliency map을 계산. PCA(32d) + K-means(k=8) 로 saliency 패턴을 클러스터링. 각 cluster centroid = "GAN이 비슷하게 편집하는 픽셀 영역의 family". 외부 attribute 라벨도, 분류기도 사용하지 않음.

unsupervised attribute taxonomy from 256 random directions
FIG 15.1   256개 random direction → median strength 위 128개 → PCA → K-means(k=8). 각 행 = 클러스터: centroid + 가장 가까운 4개 예시. 좌측 헤더에 cluster 멤버 수와 빈출 layer.
1
cluster 2 (37개) · layer 1–7: 가장 흔한 family. 침대 + 벽 + 바닥 영역에 saliency 분산. "general scene texture".
2
cluster 4 (32개) · layer 1–4: view스러운 구조 family. 창문/문 형상 saliency.
3
cluster 0, 3 (28+21개) · layer 5–7: 침대 프레임/헤드보드 중심의 mid-level structure family.
4
cluster 1, 5, 6, 7 (1–4개) · layer 13: 매우 fine-detail layer 13의 outlier들. 화면 전체에 균일하게 saturated → 큰 cluster를 형성하지 못하고 작은 outlier로 분산. "ultra-fine texture noise".
의의

외부 라벨 없이 256번의 W+ 무작위 perturbation만으로 HiGAN의 8개 hand-curated boundary가 다루지 않은 attribute family들이 자동 발견됨. cluster 4는 view를 닮았고, cluster 0/3은 hand-curated에 없는 "침대 구조" family. 이는 *unsupervised attribute discovery*의 real demo — 이 도구가 HiGAN boundary에 묶이지 않음을 보임.

한계

현재는 spatial 위치만 cluster의 정체. 의미 라벨("이 cluster는 침대를 만든다") 부여는 다음 단계. CLIP zero-shot으로 cluster centroid를 텍스트 분류 → 자동 attribute 라벨링이 가능. §17에서 실현.


발견된 cluster에 의미를 입히기

섹션 15는 8개 cluster를 자동 발견했지만 *의미는 모름*. 각 cluster의 modal layer에 대해 fresh random 방향 8개를 샘플링하고 +δ 편집 결과를 4개의 base 침실에 적용해 평균. 그 평균 perturbed 이미지를 CLIP-score 한 뒤 base 평균을 contrastive로 빼서 "이 cluster가 더 강하게 만드는 개념"의 텍스트 라벨을 자동 부여. 인간 라벨 0.

CLIP zero-shot labels for 8 random direction clusters
FIG 16.1   컬럼: centroid saliency / base bedroom / 평균 perturbed (+δ on modal layer) / normalised diff / CLIP top-k 라벨 (Δ score).
2
cluster 2 (n=37, layer 1): "a view through a window" (+0.021), dirty surface, scary atmosphere — coarse 구조에서 view-like family. HiGAN의 view학습 없이 자동 재발견.
3
cluster 3 (n=21, layer 7): "soft texture, a pillow, a blanket, carpet" — 침구 family. HiGAN에 없는 새 attribute.
4
cluster 4 (n=32, layer 2): view-like + dirty + scary — view boundary와 비슷하지만 다른 layer. cluster 2보다 좀 더 detailed.
7
cluster 5/6/7 (n=1–4, layer 13): clean room, metal surface, soft texture, lamp 등 다양 — fine layer 13의 perturbation은 일관된 의미가 없고 *전체 색조 변경*에 가까워서 outlier로 분산.
의의 — 진정한 unsupervised attribute discovery

1️⃣ random direction sampling → 2️⃣ saliency 계산 → 3️⃣ K-means 클러스터링 → 4️⃣ CLIP zero-shot 라벨 부여. 4 단계 모두 attribute 라벨이나 분류기 학습 없이 진행. 결과: HiGAN의 8 boundary가 다루지 않는 "pillow / blanket / carpet" 같은 새 attribute family들이 자동 식별. CLIP score 차이는 작지만 (Δ ≈ 0.02) contrastive scoring으로 cluster마다 분명히 다른 단어가 top-k로 등장.


학습 진행에 따라 saliency 품질이 어떻게 바뀌는가

같은 침실 사진을 5개 ckpt(1k / 5k / 10k / 20k / 40k)로 inversion하고, 각 wp 위에서 indoor_lighting saliency를 계산한 뒤 *true wp*로 만든 GT saliency와의 픽셀별 상관계수를 측정.

encoder ckpt evolution comparison
FIG 18.1   위: target / GT (true wp) saliency. 아래 5행: iter 1k–40k의 encoder recon, saliency, overlay.
iterrecon MSEsaliency corr w/ GT
1k0.0473+0.008
5k0.0402+0.028
10k0.0426+0.126
20k0.0395+0.164
40k0.0388+0.359
발견

recon MSE는 1k → 40k에서 0.047 → 0.039로 약 18%만 개선되는데, saliency 상관은 0.008 → 0.359로 45배 향상. 인코더의 reconstruction quality는 일찍 saturate하지만, *latent space의 미분 구조* (즉 saliency)는 학습 내내 계속 정제됨. 이는 saliency가 단순 reconstruction보다 *더 깊은 정렬* 신호임을 시사 — 인코더는 "그럴듯한 침실"에 빨리 도달하지만, "boundary 방향이 어디를 움직이는지"는 더 천천히 익힘.


픽셀별 비선형성 — 어디서 boundary 효과가 휘는가

1차 saliency (∂I/∂α)가 boundary 방향의 *순간 효과*라면, 2차 saliency (∂²I/∂α²)는 그 효과가 α가 변할 때 *얼마나 휘는지*. 둘 다 forward-mode JVP를 합성해 한 패스로 계산. 비율 ∂²/∂I = 비선형성 지수 — 작으면 선형 영역, 크면 saturation/꺾임.

first-order vs second-order saliency for 3 attributes
FIG 19.1   행 = attribute, 컬럼 = 1차 saliency / 2차 saliency / 비선형성 비율.
L
indoor_lighting · 비율 0.495: 거의 선형. boundary를 −3 ↔ +3 sweep해도 픽셀이 균일하게 변함. 안전한 편집.
L
wood · 비율 0.624: 선형 영역. 동일.
N
view · 비율 23.2 (40배 큼!) · p95 65: 매우 비선형. coarse layer 0–4 perturbation은 windows/doors가 *생기거나 사라지는* 식의 위상적 변화를 일으킴 → α의 변화에 픽셀이 비선형으로 반응.
섹션 13 (compositional editing) 결과의 직접적 설명

섹션 13에서 view + texture 동시 편집의 saliency가 비선형 간섭 (corr 0.55)을 보였는데, 이번 2차 분석이 그 원인을 콕 짚어준다 — view 자체가 본질적으로 곡률이 큰 비선형 변환이기 때문. indoor_lighting과 wood 사이의 합성이 선형(0.97)이었던 것도 둘 다 비선형성 비율이 0.5 수준의 거의 평탄한 변환이라 자연스럽다. §23에서 시각적으로 확인 — view sweep 시 saliency 형태가 변하는 모습.


진짜 침실 사진에 전체 파이프라인 적용

지금까지는 generator의 자기 출력(synthetic)에서만 검증. 이제 LSUN bedroom 데이터셋에서 받은 실제 사진 4장에 대해 (a) optim 800-step inversion, (b) encoder inversion, (c) grad-saliency, (d) 편집 ±3 sweep을 한 번에 실행. Out-of-distribution 일반화 검증.

full pipeline on real LSUN bedroom photos
FIG 17.1   4 진짜 침실에 대한 전체 흐름. 각 행: real photo / optim recon (800 step) / encoder recon / sal(indoor_lighting) / sal(wood) / sal(view) / edit −3 / edit +3 (indoor_lighting).
photoLPIPS (optim)LPIPS (encoder)MSE (optim)MSE (encoder)
lsun_00 (ceiling fan + wood bed)0.02940.46110.01390.1413
lsun_01 (wall photos + lamp)0.05140.58500.04500.1604
lsun_02 (modern white)0.03010.52280.02390.1376
lsun_03 (wood + balcony)0.02790.40980.02520.2142
긍정 결과

optim inversion은 진짜 사진에서도 거의 완벽

real photo에 대해서도 LPIPS 0.028–0.051 — synthetic과 거의 동등 (synth: 0.027–0.033). 즉 진짜 침실은 GAN의 manifold에 충분히 가까이 있고, gradient flow를 살린 optim inversion이 OOD 데이터에서도 작동.

부정 결과

encoder inversion은 OOD에서 더 약함

real photo: LPIPS 0.41–0.59 (synth: 0.31–0.44). 인코더가 합성 데이터로만 학습됐기에 OOD에서 ~30% 추가 손실. 합성 supervision의 한계. 실제 사진에 강건한 encoder를 위해서는 LSUN bedroom 데이터로 fine-tuning이 필요.

관찰

encoder reconstruction이 흐릿해도 saliency와 편집 방향성은 살아있다 (FIG 17.1). lsun_01 (벽에 사진들 + 램프) 행에서 indoor_lighting saliency는 램프 영역에 정확히 hot spot. 이는 보고서 내내 유지된 패턴 — saliency는 reconstruction quality보다 robust.


한 침실에서 본 v2의 전체 흐름

한 장의 사진을 잡고 파이프라인 4단계를 좌→우로 보여준다. target · encoder reconstruction · 그 inverted wp 위에서의 grad-saliency · ±3 편집. ”내 침실의 lamp는 어디인가”까지 한 줄로 답하는 데모.

full pipeline cycle for image 2
FIG 8.1   같은 한 장의 침실 (img_2)에 indoor_lighting / wood / view 세 boundary 적용. saliency 컬럼이 attribute별로 다른 spatial focus를 보여주고, 마지막 두 컬럼은 −3 / +3 편집 결과.
full pipeline cycle for image 0
FIG 8.2   다른 침실(img_0)에 같은 분석. saliency가 scene-specific 하게 다른 위치를 짚음.
파이프라인 가치

인코더 reconstruction은 색·디테일 부족(섹션 04 underfit). 그럼에도 편집 방향성과 saliency 위치는 보존되어 ”이 침실의 lamp boundary가 가리키는 픽셀”이 일관되게 나옴. 즉 saliency-as-interpretability 분석은 reconstruction quality에 robust한 편.


속성 슬라이더가 실제로 어떻게 보이는가

각 boundary를 −3 → +3 → −3 으로 21프레임 ping-pong morph. 정적 5컷 그리드보다 훨씬 매끄럽게 attribute의 작용이 보인다. WebP 압축으로 각 ~400 KB.

indoor_lighting morph
FIG 9.1 · indoor_lighting   두 침실 동시에 dim → bright → dim. 천장 조명의 ON/OFF처럼 보임.
wood morph
FIG 9.2 · wood   침구·벽 색감이 우드톤으로 점진 이동. 침대 프레임 윤곽이 강해짐.
view morph
FIG 9.3 · view   창문 형태/밝기·문 위치가 바뀜. layer 0–4 (coarse) 조작이라 전체 layout에도 영향.
관찰

인코더 reconstruction은 흐릿하지만 편집 방향성은 분명하다. 즉 잠재공간의 의미 구조 자체는 인코더의 표현력 한계와 무관하게 보존된다. v1이 풀지 못했던 "FFHQ pSp 인코더로 침실 편집"의 도메인 mismatch는, 도메인 특화 인코더로 정성적으로 해결됨을 확인.


Sweep할 때 saliency 자체는 어떻게 변하는가

섹션 20 morph는 이미지가 어떻게 바뀌는지만 보여줌. 여기서는 sweep 각 프레임마다 *그 지점에서의 1차 saliency*를 함께 계산. 21프레임 ping-pong으로 image | saliency | overlay 3-strip animated WebP. 선형 attribute는 saliency가 거의 정지, 비선형 attribute는 흔들림 — 섹션 19와 직결.

saliency morph indoor_lighting
FIG 21.1 · indoor_lighting (선형, 비율 0.5) — saliency가 sweep 동안 거의 같은 위치에 머무름.
saliency morph wood
FIG 21.2 · wood (선형, 비율 0.6) — 동일.
saliency morph view
FIG 21.3 · view (비선형, 비율 23) — saliency가 sweep 동안 *모양이 바뀐다*. 창문이 생겼다 사라지는 위상 변화에 saliency가 따라 이동.
시각적 검증

섹션 19에서 정량으로 보여준 비선형성을 여기서 *보여진다*. view는 sweep할 때 saliency가 따라가지 못하고 형태가 변함 — 즉 한 boundary 방향으로 일정하게 이동해도 픽셀의 변화 패턴은 매번 달라짐. 이는 view 같은 coarse 구조 attribute 편집이 *위상적*임을 의미.


실패와 발견의 시간순

프로젝트가 깨끗하게 보이는 결과만 남기면 정직하지 않다. v1 분석부터 v2 grad-saliency까지 실제로 막혔던 7개의 벽을 시간순으로 적는다. 빨간 점은 벽을 만난 순간, 노란 점은 우회로, 초록은 통과, 보라는 방향 전환.

3walls hit
3workarounds
1framing pivot
7resolved
#01 phase · v1 분석 wall hit

최적화 인버전이 사실은 동작하지 않고 있었다

v1 노트북은 매 step latent.detach().cpu().numpy()로 변환 후 easy_synthesize를 호출. 결과를 torch.tensor(...)로 다시 감싸 requires_grad_()를 붙이지만 그건 새 leaf tensor고, optimizer가 추적하는 원래 latent와 무관. backward()의 .grad가 0. "1500 step × 10 init"은 사실 random init 10번 중 best pick.
HiGANGenerator wrapper에서 G.net.synthesis(wp)를 직접 노출 → loss 5.6 → 0.027
--- v1: noteboook - latent_np = latent.detach().cpu().numpy() - generated = model.easy_synthesize(latent_np, ...) - img_t = torch.tensor(generated['image']) / 255. - img_t.requires_grad_() # 새 leaf — latent와 무관 +++ v2: HiGANGenerator + wp = G.sample_wp(1).requires_grad_(True) + img = G.synthesize(wp) # autograd alive + loss.backward() + assert wp.grad.norm() > 0 # 0.30 in our test
#02 phase · 환경 세팅 workaround

pip install lpips 가 torch 를 2.11 로 강제 업그레이드

LPIPS 한 줄 추가하다가 의존성 그래프가 따라와 torch 2.2.2 → 2.11.0 + cudnn cu12 → cu13 교체. 그 결과 libcudnn.so.8: cannot open shared object file. CUDA 환경 깨짐.
torch 2.2.2 + nvidia-* 패키지 핀해서 재설치, lpips 는 --no-deps로. requirements.txt에 박아둠.
$ python -c "import torch" ImportError: libcudnn.so.8: cannot open shared object file: No such file or directory # cu13 cudnn 9.x가 깔리고 cu12 cudnn 8.x는 사라진 상태 $ pip install --force-reinstall \ nvidia-cudnn-cu12==8.9.2.26 nvidia-cuda-runtime-cu12==12.1.105 \ 'torch==2.2.2' --index-url ...whl/cu121 $ pip install --no-deps lpips==0.1.4 # ← key
#03 phase · 인코더 학습 workaround

fp16 autocast 켰더니 image-space loss 전부 NaN

AMP 켜고 batch 8로 학습 시작. w_mse는 정상이지만 pixel_l2 / lpips / perceptual 모두 NaN. isolated 테스트로 원인 좁혀가니 StyleGAN bedroom의 synthesis op 자체가 fp16 autocast 하에서 inf를 토하는 패턴.
AMP 끄고 fp32 고정. peak 메모리 4.4 → 5.4 GB지만 8 GB에 충분히 들어감. NaN 사라짐.
--- log.jsonl during AMP {"it": 20, "total": NaN, "w_mse": 0.071, "pixel_l2": NaN, "lpips": NaN} {"it": 40, "total": NaN, "w_mse": 0.074, "pixel_l2": NaN, "lpips": NaN} ... # w_mse(잠재)는 정상, image-space 통과 후만 NaN — synthesis op이 fp16에서 inf --- log.jsonl after fp32 {"it": 100, "total": 1.299, "w_mse": 0.068, "pixel_l2": 0.190, "lpips": 0.517} {"it": 550, "total": 1.202, "w_mse": 0.060, "pixel_l2": 0.176, "lpips": 0.445}
#04 phase · 인코더 학습 종료 accepted

20k iter 학습된 인코더가 LPIPS 0.40 에서 정체

loss 곡선이 우하향이지만 기울기가 7배 완만해짐 (1k당 −0.054 → −0.0072). 60k 더 돌려도 LPIPS 0.30 가까이만 갈 추세. pSp 표준(200–500k)의 4–10%만 학습한 셈. 편집 방향성은 보존되지만 reconstruction은 회백색으로 평균화.
ROI 낮아 추가 학습 보류, 한계로 명시 (섹션 09). 인코더는 inversion 용이지 saliency엔 불필요.
#05 phase · 페이지 검토 framing pivot

"CAM-style" 표현이 사실 부정확하다는 지적

초안 페이지에 perturbation diff_map을 "CAM-style saliency"로 명명. 사용자 검토 중 지적: "근데 cam처럼 보자는거잖아 역전파 개념이 없는데?" 맞는 말. 우리 v1은 forward 두 번 차이를 본 finite-difference 민감도였고, Grad-CAM의 핵심인 backward gradient는 흐르지 않았다. 이름과 mechanism 불일치.
섹션 05를 "forward perturbation (1차 시도)"로 톤 다운. 진짜 backward 흐르는 cam/grad_saliency.py 신규 모듈을 새 헤드라인으로 추가.
#06 phase · grad-saliency 구현 wall hit

torch.func.jvp 가 StyleGAN synthesis 안에서 죽는다

forward-mode JVP를 generator에 통과시키니 RuntimeError: Cannot access data pointer of Tensor that doesn't have storage. 추적해보면 genforce/higan의 synthesis.forwardself.lod.cpu().tolist()로 lod 버퍼를 파이썬 리스트로 변환하는 줄에서 dual tensor 깨짐.
bedroom256은 lod=0.0 고정이므로 wrapper init 시 한 번 읽어 python float로 캐싱. synthesis.forward를 동등 로직 + 캐시값 사용으로 monkey-patch (5줄). JVP 통과.
--- genforce/.../stylegan_generator_network.py:357 def forward(self, w): ... lod = self.lod.cpu().tolist() # ← dual tensor 깨짐 for res_log2 in range(...): +++ higan_dev/generator.py — JVP-safe cached_lod = float(synth.lod.detach().cpu().item()) def jvp_safe_forward(self_synth, w): lod = cached_lod # ← python float, dual-friendly for res_log2 in range(...): synth.forward = types.MethodType(jvp_safe_forward, synth)
#07 phase · grad-saliency 시각화 refinement

64 샘플 평균 결과가 8 샘플 단일보다 흐릿함

스모크 테스트에서 8 샘플로 본 결과는 램프·침대 윤곽이 또렷했는데, 64 샘플 평균은 뿌연 spatial blob. 평균이 sample-specific 위치(이 침실의 lamp는 좌측, 저 침실은 우측)를 뭉갠 것.
평균 외에 첫 K개 sample의 saliency를 개별로도 저장. per-sample 그리드가 진짜 헤드라인 결과.
교훈

벽 7개 중 6개는 도구 stack의 가정에서 왔다 — autograd가 어디서 끊기는가, fp16에서 어떤 op이 깨지는가, dual tensor가 어떤 호출을 못 받는가. 모델 자체의 문제는 단 하나(인코더 underfit). "신박한 아이디어보다 기존 도구의 구석에 빠지지 않기"가 실제 작업의 70%였다. 그리고 framing pivot(#05)은 사용자 한 줄 코멘트가 가장 큰 quality gate였음을 보여줌.


정직한 한계

v2는 컨셉 검증과 파이프라인 견고화에 집중했다. 다음 세 가지가 명시적인 한계.

L1 · Encoder under-fit
색·텍스처 평균화
20k iter는 표준 pSp 학습량(200–500k)의 4–10%. loss 곡선은 여전히 우하향이지만 기울기는 7배 완만해진 상태.
LPIPS 0.40pixel MSE 0.07517h 더 학습 시 ~0.25 예상
L2 · Classifier-free CAM
"어디 움직이는가"는 알지만 "어디 의미 있다"는 별개
현재 CAM은 픽셀 변화량 기반. attribute 분류기를 따로 두면 Grad-CAM 정량값과 교차검증 가능. higan_dev/cam/grad_cam.py slot은 비워둠.
extension hook
L3 · 단일 도메인
bedroom256만 검증
생성기를 다른 HiGAN 모델(LSUN church 등)로 바꾸면 wrapper만 재사용 가능하지만, boundary 자료가 침실용. 다른 도메인 적용에는 별도 boundary 학습 필요.
stylegan_bedroom256×256
다음 단계 옵션

(a) 인코더 200k+ iter 추가 학습 — 40k까지 진행. 200k까지 ~13 h 더. 정밀 편집 데모용.
(b) CLIP / classifier 기반 진짜 Grad-CAM — bedroom attribute 분류기 학습 또는 CLIP score backward로 *진짜* Grad-CAM 실행 → JVP saliency와 픽셀별 IoU/correlation 비교. 약 4–6 h. 본 페이지의 JVP 도구가 classifier-based attribution과 어디까지 일치하는지 정량 검증.
(c) 다른 도메인 (church/face) — wrapper 재사용 + boundary 학습. 1–2일 작업.
(d) Random direction labeling — 발견된 random direction에 의미 라벨 자동 부여 (CLIP zero-shot로 "lamp / window / floor" 분류) → unsupervised attribute discovery pipeline.


v1 → v2 차이 요약

항목v1 노트북v2 .py 패키지
최적화 인버전gradient 단절로 사실상 비작동loss 5.6 → 0.027 (99.5%↓)
인코더pSp / FFHQ — 도메인 mismatchResNet50 + 합성 supervision (bedroom 전용)
혼합 정밀도fp16 NaN 발견 → fp32 고정
boundary 조작numpy 의존torch-native, autograd 호환
CAM 해석 (1차)ttach 설치만, 미사용forward perturbation diff-map 8 attribute
CAM 해석 (최종)JVP 기반 진짜 gradient saliency · scene-specific 객체 pinpoint
실행 환경Colab로컬 RTX 3070, < 6 GB
학습 시간20k iter, 1h 47m
재현성경로 하드코딩YAML config, CLI 7 step