인공지능 비전 연구실 학부 연구생

데이터 누수 발생 원인 분석

sungendary 2026. 6. 18. 15:59

들어가며

개인적으로 계속 하고 싶었던 실험을 바쁜 학기가 끝나고 실험을 진행할 수 있었다. 드론 또는 CCTV로 촬영한 이미지 데이터로 군중 수를 파악해서 현재 군중이 얼마나 밀집해 있고 그 군중 수 또는 군중의 행동 유형을 파악해서 현장 관리자에게 상태를 보고하는 시스템을 만들어 보고 싶었다. 동기는 이태원 참사와 관련하여 CCTV 관리자가 군중 상태를 파악하는 AI모델을 융합해서 실시간 보고를 받고 대응했다면 참사를 사전에 예방할 수 있었을 것이라 생각했고 이를 프로젝트로 구현해 보고 싶었다. 이를 전공 수업(SW오픈소스) 프로젝트로 진행하려고 했지만, 한정된 시간 내에 해내기에는 나도 마찬가지지만 팀원들도 이에 관해 무지했기에 진행하지 못 했었다.

 

우선 CCTV, 드론 선택 지중 연구실에 드론이 있어 드론으로 선택을 했다.

실제 드론을 사용하기 앞서 Visdrone의 DroneCrowd 데이터셋을 활용하여 드론이 촬영한 영상에서 자동으로 군중 수를 파악하는 AI 모델을 만드는 것이 이 실험의 목적이다. 이후 실제 드론 영상을 활용할 수 있다면 할 계획이다.

 

군중 계수(Crowd Counting) 모델을 학습했을 때 MAE=3.45라는 믿기 어려운 성능이 나왔다. 공식 논문에서 같은 모델(CSRNet)이 MAE=19.8을 기록한 것과 비교하면 약 6배 차이다. 원인을 분석하니 데이터 분할 방식에서 치명적인 데이터 누수가 발생하고 있었다.

 

MAE 결과
출처: "Detection, Tracking, and Counting Meets Drones in Crowds: A Benchmark" Paper
평가 지표 비교


실험 환경

데이터셋: DroneCrowd (드론 촬영 군중 영상)
구성: 82개 훈련 시퀀스, 시퀀스당 30 프레임 → 총 2,460 프레임
모델: CSRNet (VGG16 기반 density map 추정)
태스크: 이미지 한 장에서 사람 수 예측

 

 


데이터 누수가 발생한 원인

핵심 원인: 분할 단위의 불일치
이 데이터셋의 샘플은 독립적이지 않다. 하나의 시퀀스(영상 클립)에서 추출된 30개의 프레임은 서로 시간적으로 연속된 장면이다.


시퀀스 00001 (같은 장소, 같은 카메라):
  프레임 01.jpg  → 군중 104명
  프레임 02.jpg  → 군중 104명  ← 거의 동일한 이미지
  프레임 03.jpg  → 군중 105명
  ...
  프레임 30.jpg  → 군중 108명


random_split은 이 30개의 프레임을 개별 독립 샘플로 취급하고 랜덤하게 섞는다. 그 결과:
시퀀스 00001의 30개 프레임이 분할된 상황:
  train → 프레임 01, 03, 04, 06, 07, ...  (27개)
  val   → 프레임 02, 05, 29              ( 3개)
val에 들어간 프레임 02는 train에 들어간 프레임 01, 03과 사실상 같은 이미지다.

시각적으로 표현하면

[실제 기대한 분할]
  시퀀스 A (30프레임) ──────────────────→ 전부 train
  시퀀스 B (30프레임) ──────────────────→ 전부 train
  시퀀스 C (30프레임) ──────────────────→ 전부 val ← 완전히 새로운 장면

[실제 발생한 분할]
  시퀀스 A: 프레임 1,2,4,5,... → train
            프레임 3,6,...     → val   ← train과 거의 동일한 장면!
  시퀀스 B: 프레임 1,3,5,...   → train
            프레임 2,4,...     → val   ← train과 거의 동일한 장면!
  시퀀스 C: 프레임 1,2,...     → train
            프레임 3,...       → val   ← train과 거의 동일한 장면!

모델 입장에서 벌어지는 일
모델은 훈련 중 시퀀스 A의 프레임 1, 3, 4, ...를 학습한다. 검증 단계에서 프레임 2가 들어오면, 모델은 훈련에서 본 프레임 1과 거의 동일한 이미지를 만나는 셈이다. 실제 새로운 장면에 대한 일반화 능력이 아니라, 사실상 기억에 가까운 성능을 측정하게 된다.

 


올바른 분할 방법


분할 단위를 샘플이 아닌 시퀀스(그룹)로 바꿔야 한다.

시퀀스 00001 ──── 30 프레임 전부 ────→ train
시퀀스 00002 ──── 30 프레임 전부 ────→ train
시퀀스 00003 ──── 30 프레임 전부 ────→ train
...
시퀀스 00074 ──── 30 프레임 전부 ────→ train
────────────────────────────────────────────
시퀀스 00075 ──── 30 프레임 전부 ────→ val
시퀀스 00076 ──── 30 프레임 전부 ────→ val
...
시퀀스 00082 ──── 30 프레임 전부 ────→ val

결과:
  train 2,220 프레임 ← 74개 시퀀스만 포함
  val     240 프레임 ← 8개 시퀀스만 포함, train과 완전히 다른 장소

 

즉, 같은 시퀀스에서 나온 프레임들은 독립적이지 않다. 분할은 반드시 독립적인 단위, 즉 시퀀스 기준으로 해야 val이 모델에게 진짜 새로운 데이터가 된다는 점을 알게 되었다.

 


 

핵심 교훈

샘플 간에 시간적·공간적 상관관계가 있는 데이터는 반드시 그 단위로 분할해야 한다는 점이다.

데이터 유형 잘못된 분할 단위 올바른 분할 단위
비디오 프레임 프레임 시퀀스(클립)
시계열 데이터 타임스텝 시간 구간
의료 이미지 이미지 슬라이스 환자
텍스트 데이터 문장 문서/대화
위성 이미지 패치 촬영 지역



공통점은 하나다. 하나의 상위 단위에서 나온 하위 샘플들은 독립적이지 않다. random_split은 이 사실을 모른다는 것이 치명적이다.