콘텐츠로 이동

실험 비판 보고서: FeDPM MVP Phase 1 (V1 Vanilla FeDPM)

작성일: 2026-04-15 대상 실험: experiments/distillation/v6_0415_fedpm_mvp.py (Phase 1, 3회 실행) 설계서: report/version6/lab-leader/v6_0415_exp6_design.md 종합 판정: REJECT -- 구조적 구현 오류 및 실험 공정성 문제로 결과 신뢰 불가


요약 (Executive Summary)

Phase 1의 3회 실행 모두 FAIL이나, 이 FAIL 판정 자체가 신뢰할 수 없다. 우리 FeDPM 구현은 원본과 최소 4가지 핵심 구조적 차이가 있으며, B1 baseline 비교에서 federation 규모 불일치와 loss function 불일치가 확인되었다. codebook utilization 극저(1~15%)는 동일 도메인 부적합 때문이 아니라 encoder 구조 결함(정보 병목 부재)에 기인할 가능성이 높다. 현 결과로 "FeDPM이 동일 도메인에서 부적합하다"고 결론내리는 것은 부당하다.


치명적 문제 (Critical Issues)

C1. Encoder 구조가 원본과 근본적으로 다름 -- 정보 병목이 없음

문제점: 우리 구현의 CNN Encoder는 same-padding Conv1d 2층으로, spatial dimension을 보존한다 ([B, D, 24] -> [B, D, 24]). 반면 원본 CNNEncoderBackbone은 strided convolution (stride=2)으로 4x~16x 다운샘플링 + ResidualStack + pre_vq_conv 구조이다 (Encoder_and_Retrieval.py line 18-36).

심각도: CRITICAL

근거: - 우리 코드 (src/fed_learning/fedpm.py line 159-164):

self.encoder = nn.Sequential(
    nn.Conv1d(embedding_dim, embedding_dim, kernel_size=3, padding=1),
    nn.ReLU(),
    nn.Conv1d(embedding_dim, embedding_dim, kernel_size=3, padding=1),
    nn.ReLU(),
)
- 원본 코드 (src/FedUnit-64D1/lib/models/Encoder_and_Retrieval.py line 18-36): stride=2 Conv1d 2층 + _conv_3 (stride=1) + ResidualStack + pre_vq_conv

왜 치명적인가: VQ-VAE의 codebook이 의미 있는 prototype을 학습하려면 encoder가 정보를 압축하여 bottleneck을 형성해야 한다. same-padding encoder는 입력 정보를 거의 그대로 통과시키므로, VQ lookup이 단순 identity에 가까워진다. 이것이 codebook utilization이 극히 낮은 (1~15%) 직접적 원인일 가능성이 높다. Encoder가 모든 patch를 거의 동일한 representation으로 인코딩하면 codebook의 소수 entry만 활성화된다.

개선 제안: 원본의 strided Conv1d encoder + ResidualStack + pre_vq_conv 구조를 포팅하거나, 최소한 FC encoder backbone (원본 FCEncoderBackbone, Encoder_and_Retrieval.py line 167-223)을 사용해야 한다. FC backbone은 patch_len -> embedding_dim 매핑 후 FC 2층을 거치므로 일정 수준의 정보 압축이 발생한다.


C2. Decoder 구조가 원본과 근본적으로 다름 -- 예측 메커니즘 불일치

문제점: 우리 Decoder는 CNN same-padding + patch-wise Linear(64, 1)로 각 patch를 독립적으로 1 스칼라로 매핑한다. 원본 XcodeYtimeDecoder는 전체 code sequence를 flatten한 후 Linear(d_model * seq_in_len, seq_out_len)으로 한번에 출력 시퀀스를 생성한다.

심각도: CRITICAL

근거: - 우리 코드 (src/fed_learning/fedpm.py line 168-178): - CNN Decoder: [B, D, 24] -> [B, D, 24] (same-padding) - pred_head = nn.Linear(embedding_dim, 1) -- patch-wise 독립 예측 - 원본 코드 (src/FedUnit-64D1/lib/models/decoder.py line 576): - self.linear_out = nn.Linear(d_model * seq_in_len, seq_out_len) -- 전체 시퀀스를 한번에 예측 - FC backbone: flatten(seq_in_len * d_model) -> FC layers -> reshape

왜 치명적인가: Patch-wise 독립 예측은 patch 간 시간적 의존성을 완전히 무시한다. 원본은 모든 code를 동시에 보고 출력 시퀀스를 생성하므로, 시간적 패턴(피크 위치, 추세)을 포착할 수 있다. 이 차이는 예측 정확도에 직접적 영향을 미치며, 특히 피크 예측(PAPE)에 치명적이다.


C3. MuStdModel 누락 -- 역정규화 메커니즘 불일치

문제점: 원본 FeDPM은 MuStdModel (MLP로 mean/std 예측)을 별도 모듈로 사용하여, encoder의 RevIN 정규화와 독립적으로 출력의 scale을 복원한다. 우리 구현은 단순 per-sample mean/std (line 201-203)만 사용한다.

심각도: CRITICAL

근거: - 원본 (Client.py line 131-135): MuStdModel(Tin, Tout, [512, 512], dropout=0.2) -- 입력 시계열로부터 출력 시계열의 mean/std를 예측하는 별도 MLP - 원본 loss 계산 (Client.py line 230-237): loss_decode + loss_mu + loss_std + loss_all + vq_loss (5개 loss 항) - 우리 loss (v6_0415_fedpm_mvp.py line 210): smooth_l1_loss + vq_loss (2개 loss 항)

왜 치명적인가: MuStdModel은 입력-출력 간 scale shift를 학습하여 denormalization 품질을 높인다. 특히 에너지 데이터에서는 입력 구간과 예측 구간의 평균/분산이 크게 다를 수 있어(주간/야간, 계절), 단순 per-sample 통계 복원은 systematic bias를 유발한다. 원본은 이를 인식하고 일부 dataset에서 MuStdModel을 비활성화하고 RevIN으로 대체하는 옵션도 제공한다 (clients_disable_mustd, line 122-129).


C4. B1 vs V1 비교 불공정 -- Federation 규모 불일치

문제점: 실행 3에서 V1은 50 clients로 federation 학습되었으나, B1 baseline은 5 clients (EVAL_HOUSEHOLDS만)로 학습되었다.

심각도: CRITICAL

근거: - v6_0415_fedpm_mvp.py line 733-738:

if args.phase == 1:
    b1_results = run_fedavg_dlinear(
        clients=eval_clients,       # 5 clients only
        eval_clients=eval_clients,
        ...
    )
- 반면 V1은 all_fed_clients (50 clients)로 학습 (line 749-760)

왜 치명적인가: 50 clients FeDPM vs 5 clients FedAvg는 공정한 비교가 아니다. FedAvg는 더 많은 클라이언트에서 학습할수록 일반적으로 성능이 향상된다. V1이 50 clients의 이점(더 많은 데이터)을 받으면서도 B1보다 나쁜 결과를 보인 것은 FeDPM 구조 자체의 문제를 시사하지만, 역으로 B1도 50 clients였다면 V1과의 격차가 더 컸을 수 있다. 어느 쪽이든 현재 비교 수치는 정확하지 않다.

개선 제안: B1과 V1을 동일 federation 규모 (5 clients 또는 50 clients)로 비교해야 한다.


주요 문제 (Major Issues)

M1. Training Loss 불일치 -- SmoothL1 vs MSE

문제점: V1 학습은 smooth_l1_loss를 사용하고, B1 학습은 MSELoss를 사용한다.

심각도: MAJOR

근거: - V1: v6_0415_fedpm_mvp.py line 210: nn.functional.smooth_l1_loss(y_hat.squeeze(-1), y.squeeze(-1)) - B1: line 337: criterion = nn.MSELoss()

영향: Loss function이 다르면 gradient landscape가 달라져 수렴 속도와 최종 수렴점이 변한다. SmoothL1은 이상치에 더 강건하나 MSE 대비 peak에 대한 penalty가 약하다. 동일 loss function으로 통일해야 공정 비교가 가능하다.


M2. Memory Alignment 구현 간소화 -- usage_count 기반 personalization 누락

문제점: 원본 Server (Server.py line 171-257)의 client_personalized 전략은 usage_count와 cross-client diversity score를 기반으로 개인화 prototype을 선택한다. 우리 MemoryAlignmentServer.align() (fedpm.py line 373-511)은 diversity score만 사용하고 usage_count를 반영하지 않는다.

심각도: MAJOR

근거: - 원본 (Server.py line 214-239): personalization_score = -avg_sim + lambda_weight * normalized_usage - usage 빈도가 높고 다른 클라이언트와 유사도가 낮은 prototype을 개인화 슬롯에 우선 배치 - 우리 구현 (fedpm.py line 489-497): diversity_score = 1.0 - max_sim_to_shared - usage 빈도 정보 없이 shared prototype과의 유사도만으로 선택

영향: Usage count 없이는 사용되지 않는 dead prototype이 개인화 슬롯을 점유할 수 있다. 특히 codebook utilization이 이미 극히 낮은 상황에서, 사용빈도 0인 prototype이 diversity score가 높다고 개인화 슬롯에 배치되면 의미 없는 noise가 codebook을 오염시킨다.


M3. B1 baseline 결과 부재 -- 실행 3의 B1 수치 미보고

문제점: 사용자 보고에서 "B1 MSE=0.505"라는 수치가 언급되지만, 이것이 어떤 실행에서 나온 것인지 불분명하며, 실행 3 (50 clients)에서 B1이 실제로 실행되었는지 확인할 수 없다.

심각도: MAJOR

근거: MLflow에 5개 run이 기록되어 있으나, B1 결과가 별도 run으로 기록되지 않고 V1 run 내에 b1_* prefix metrics로 기록된다. 실행 3 (50 clients)의 경우, 코드 구조상 B1은 5 clients로만 실행된다 (C4 참조).

개선 제안: B1 결과를 독립 run으로 기록하고, V1과 동일 조건 (동일 client 수)에서 실행해야 한다.


M4. 실행 간 하이퍼파라미터 비일관성 -- 3회 실행이 사실상 다른 실험

문제점: 3회 실행이 "동일 실험 3회 반복"이 아니라 서로 다른 하이퍼파라미터 조합이다.

심각도: MAJOR

근거: | 실행 | clients | M | lr | rounds | patience | |------|---------|---|----|--------|----------| | 1 | 5 | 64 | 1e-4 | 30 | 5 | | 2 | 5 | 256 | 1e-5 | 100 | 10 | | 3 | 50 | 256 | 1e-5 | 100 | 10 |

영향: 이는 ablation study가 아니라 ad-hoc 탐색이다. 한 번에 여러 변수를 변경하여 어떤 요인이 성능에 영향을 미치는지 분리할 수 없다. 실행 1->2는 (M, lr, rounds, patience)를 동시에 변경, 실행 2->3은 clients만 변경. 재현성 검증(같은 조건 multiple seeds)이 전혀 수행되지 않았다.


M5. val_loss 발산 원인 미분석 -- Round 3 이후 단조 증가

문제점: 실행 3에서 Round 3 이후 val_loss가 단조 증가(발산)한 근본 원인이 분석되지 않았다.

심각도: MAJOR

가능한 원인 분석: 1. Memory Alignment이 학습을 방해: 매 round마다 codebook이 overwrite되면서 이전 round에서 학습된 encoder-codebook alignment가 파괴된다. 특히 동일 도메인에서 대부분의 prototype이 유사하면 (sim > delta=0.7), 거의 모든 prototype이 하나의 cluster에 병합되어 diversity가 소멸한다. 2. Codebook collapse와 발산의 악순환: codebook utilization이 낮음 -> 소수 entry만 gradient를 받음 -> 나머지 entry가 점점 stale해짐 -> alignment 시 stale entry가 shared slot을 차지 -> 더 많은 entry가 무의미해짐 3. lr=1e-5가 VQ commitment loss 대비 과소: commitment_beta=0.25인데 lr=1e-5면 encoder가 codebook에 "commit"하는 속도가 너무 느려, VQ loss가 task loss를 지배하여 학습이 불안정해질 수 있다.

근거: 클러스터 수가 369->230으로 감소하는 추세는 prototype들이 점차 유사해짐을 의미하며, codebook collapse를 뒷받침한다.


경미한 문제 (Minor Issues)

N1. codebook_utilization 이중 계산

문제점: run_fedpm_training() 내에서 codebook utilization을 계산할 때, evaluate_val_loss()를 불필요하게 재호출하고 (line 544-547), 이후 별도로 utilization을 다시 계산한다 (line 550-558).

심각도: MINOR

근거: v6_0415_fedpm_mvp.py line 542-558. round_metricscodebook_utilization 키에 val_loss 값이 할당된 후, 곧바로 실제 utilization으로 덮어쓴다. 비효율적이지만 최종 값은 정확하다.


N2. RevIN denormalization에서 std 계산 불일치

문제점: 우리 FeDPMModel의 RevIN은 x.std() (unbiased=True, 기본값)를 사용하는 반면, 원본 RevIN은 torch.var(x, unbiased=False)를 사용한다.

심각도: MINOR

근거: - 우리 코드 (fedpm.py line 203): std = x.std(dim=1, keepdim=True) + 1e-8 - 원본 (revin.py line 42): torch.sqrt(torch.var(x, dim=dim2reduce, keepdim=True, unbiased=False) + self.eps)

영향: 배치 크기가 작을 때 biased/unbiased variance 차이가 약간의 수치적 차이를 만들 수 있지만, seq_len=96일 때 실질적 영향은 미미하다.


N3. evaluate_fedpm에서 MSE 계산이 역정규화 후 수행

문제점: 역정규화 후 kW 스케일에서 MSE를 계산하므로, 가구 간 MSE 스케일이 다르다.

심각도: MINOR

근거: v6_0415_fedpm_mvp.py line 280-282. 이는 B0/B1과 동일 방식이므로 비교 공정성에는 문제없으나, 가구 간 MSE를 단순 평균하는 것은 높은 전력 소비 가구에 편향된 평가가 된다.


인정되는 강점 (Acknowledged Strengths)

  1. 설계서 품질 우수: v6_0415_exp6_design.md의 Phase 분기 구조, Gate 판정 기준, 리스크 관리 항목이 체계적이며 실험 전 명확한 PASS/FAIL 기준을 설정한 점은 바람직하다.
  2. MLflow 추적 일관성: 5개 실행 모두 MLflow에 기록되어 있으며, 핵심 하이퍼파라미터(M, D, gamma, delta, lr 등)가 빠짐없이 로깅되었다.
  3. codebook utilization 추적: 원본에는 없는 codebook utilization 메트릭을 매 round 추적하여 codebook collapse를 조기 감지한 점은 좋은 모니터링 관행이다.
  4. Memory Alignment BFS 구현: cosine similarity 기반 BFS 클러스터링 알고리즘은 원본 Server의 로직과 동일한 접근 방식을 따르고 있으며, 구현 자체는 올바르다.
  5. 실패를 숨기지 않은 투명성: 3회 FAIL 결과를 정직하게 보고하고 비판적 리뷰를 요청한 점은 연구 윤리에 부합한다.

exp-expert에게 전달하는 필수 수정 사항

핵심 판단: "track-d 종료" 결정은 보류해야 한다

현 결과는 "FeDPM이 동일 도메인에서 부적합하다"가 아니라 "우리의 간소화된 FeDPM 구현이 동작하지 않는다"만 보여준다. 원본과의 4가지 핵심 구조적 차이(C1-C3) + 실험 공정성 문제(C4)를 해결하지 않고 track-d를 종료하는 것은 성급한 결론이다.

권고: 원본 구조 faithful 포팅 후 재실험

다음 중 하나를 선택해야 한다:

옵션 A (권장): 원본 코드 최소 변경 포팅 - src/FedUnit-64D1/Encoder, PMR, XcodeYtimeDecoder, MuStdModel을 우리 데이터 파이프라인에 직접 연결 - 장점: 구조적 차이에 의한 의심 완전 제거 - 단점: 코드 복잡도 증가, 원본이 multivariate 가정(channel-independent reshaping)이 있어 단변량 적응 필요

옵션 B: 핵심 차이만 수정 후 재실험 - (1) Encoder: FC backbone 사용 (원본의 FCEncoderBackbone 포팅, patch_len 기반 정보 압축) - (2) Decoder: flatten + Linear(D*num_patches, pred_len) 구조로 변경 - (3) MuStdModel: 별도 MLP 추가 또는, RevIN affine=True 사용하여 학습 가능한 scale 복원 - (4) B1을 V1과 동일 client 수로 재실행 - (5) Loss function 통일 (둘 다 SmoothL1 또는 둘 다 MSE)

옵션 C: track-d 종료 (단, 정당한 근거 명시 필요) - 원본 포팅 비용 대비 기대 이익이 낮다고 판단할 경우 - 단, 보고서에 "우리 간소화 구현이 실패한 것이지 FeDPM 자체가 부적합한 것은 아님"을 명시해야 함 - KIIE 마감(2026-04-30) 고려 시 시간 대비 효율이 낮을 수 있음


재실험 권고 체크리스트

구조 수정 (옵션 B 기준)

  • C1 해결: Encoder를 FC backbone 또는 strided CNN backbone으로 교체. 정보 병목 확인 (encoder 출력의 unique representation 수 확인)
  • C2 해결: Decoder를 flatten + Linear(D*L, pred_len) 구조로 교체. Patch-wise 독립 예측 제거
  • C3 해결: MuStdModel 추가 또는 RevIN affine=True 적용
  • C4 해결: B1 federation을 V1과 동일 client 수로 실행
  • M1 해결: B1과 V1의 학습 loss function 통일

실험 공정성

  • B0, B1, V1 모두 동일 train/val/test split 사용 확인
  • 동일 하이퍼파라미터로 최소 3회 반복 실행 (seed=42, 43, 44)
  • B1을 50 clients로도 실행하여 federation 규모 효과 분리

분석 항목

  • Encoder 출력의 cosine similarity 분포 확인 (모든 patch representation이 유사한지)
  • VQ commitment loss vs task loss 비율 추적 (학습 안정성 진단)
  • codebook utilization을 M=32, 64, 128, 256으로 sweep하여 최적 M 탐색
  • Round별 val_loss 곡선과 codebook utilization 곡선의 상관관계 분석

보고 요구

  • 구조 변경 전후 파라미터 수 비교표
  • 원본 FeDPM 논문의 Electricity dataset 결과와 우리 결과의 차이 분석
  • PAPE를 primary metric으로 보고 (FL MSE-PAPE tradeoff 발견 참조)

부록: 구조 비교 요약

구성 요소 원본 FeDPM 우리 구현 영향
Encoder Strided Conv1d (4x downsampling) + ResidualStack + pre_vq_conv Same-padding Conv1d 2층 + ReLU 정보 병목 부재 -> codebook 무력화
VQ PMR (nn.Embedding) VectorQuantizer (nn.Embedding) 동일 (STE 포함)
Decoder XcodeYtimeDecoder: flatten -> Linear(D*L, pred_len) CNN same-padding + Linear(D, 1) per patch 시간 의존성 무시
MuStdModel MLP(Tin+2, 2) -- mean/std 예측 없음 (per-sample statistics) Scale 복원 부정확
RevIN RevIN class (unbiased=False) Manual mean/std (unbiased=True) 미미한 수치 차이
Memory Alignment usage_count + diversity score diversity score only 개인화 품질 저하
Loss loss_decode + loss_mu + loss_std + loss_all + vq_loss smooth_l1 + vq_loss 학습 신호 부족