본문 바로가기
[GPUaaS]/GPUmgt

[Not a Number] NaN이란 무엇인가?

by METAVERSE STORY 2026. 2. 21.
반응형

 

 

 

1. NaN이란 무엇인가?

NaNNot a Number의 약자로, 컴퓨터 과학에서 '숫자가 아님'을 나타내는 값입니다. 수학적으로 정의할 수 없거나 표현 불가능한 연산 결과가 나왔을 때 발생합니다.

딥러닝 학습 중에 NaN이 떴다는 것은 **"모델의 수치 계산이 완전히 망가져서 더 이상 학습을 진행할 수 없다"**는 사망 선고와 같습니다.

왜 발생하나요? (수학적 원인)

  • 0으로 나누기: 어떤 값을 0으로 나눌 때.
  • 무한대 연산: $\infty - \infty$ 또는 $0 \times \infty$ 같은 연산.
  • 로그 함수의 함정: $\log(0)$이나 $\log(-1)$ 처럼 정의되지 않는 값을 계산할 때.
  • 너무 큰 숫자: 부동 소수점 데이터 타입(float32 등)이 표현할 수 있는 범위를 넘어서는 큰 값($10^{38}$ 이상 등)이 생길 때 (이를 Overflow라고 합니다).

2. 학습 중 NaN이 발생하는 주요 시나리오

보통 학습 초기보다는 어느 정도 진행되다가 갑자기 튀어나오는 경우가 많습니다.

  • Gradient Exploding (기억기 폭주): 가중치(Weight)를 업데이트하는 값(Gradient)이 너무 커져서, 다음 단계의 가중치가 무한대($\infty$)로 튀어버리는 현상입니다.
  • 잘못된 학습률(Learning Rate): 학습률이 너무 높으면 최적의 지점을 찾지 못하고 숫자가 날뛰다가 NaN이 됩니다.
  • 데이터 문제: 입력 데이터에 이미 NaN이 포함되어 있거나, 정규화(Normalization)가 되지 않아 비정상적으로 큰 값이 들어올 때 발생합니다.
  • 손실 함수(Loss Function) 설계 오류: 예를 들어 Softmax를 거치지 않은 값을 Log에 바로 넣으면 아주 작은 확률값 때문에 $\log(0)$이 되어버릴 수 있습니다.

3. 대규모 노드 환경에서 특히 주의할 점

현재 대규모 노드에서 학습 중이시라면, 다음 상황을 염두에 두어야 합니다.

  1. 배치 사이즈(Batch Size): 노드가 많으면 전체 배치 사이즈가 매우 커집니다. 이 경우 학습률(Learning Rate)도 함께 높여 잡는 경우가 많은데, 이때 수치적 불안정성이 생기기 쉽습니다.
  2. 분산 학습 동기화: 특정 노드에서 데이터 로딩 오류로 이상한 값이 들어오면 전체 모델로 NaN이 전염됩니다.
  3. Mixed Precision (FP16): 학습 속도를 위해 FP16(16비트 부동소수점)을 쓰신다면, FP32보다 표현 범위가 좁아 NaN이 훨씬 자주 발생합니다. (이때는 Loss Scaling 기법이 필수입니다.)

4. NaN을 예방하는 팁 (초보자를 위한 체크리스트)

아직 발생하지 않았다니 다행입니다! 미리 대비하려면 다음을 확인해 보세요.

  • Gradient Clipping: 그래디언트의 크기가 일정 수준을 넘지 못하도록 강제로 깎아버리는 설정입니다. (가장 효과적인 방어책입니다.)
  • 데이터 검수: df.isnull().sum() 처럼 데이터셋에 빈값이 없는지 미리 확인하세요.
  • 작은 학습률부터 시작: 처음부터 너무 높은 학습률보다는 Warm-up 전략을 써서 서서히 높이는 것이 안전합니다.
  • Epsilon 추가: 로그 연산 등을 할 때 아주 작은 값($1e-7$ 등)을 더해줘서 0이 되는 것을 방지합니다.

꿀팁: 학습 로그에 Loss가 갑자기 평소보다 10배 이상 튀기 시작한다면, 곧 NaN이 발생할 징조일 수 있으니 주의 깊게 살펴보세요!

 

 

=================================

 

 

대규모 환경에서는 이 신호를 놓치면 수 시간, 수일간의 연산 자원이 순식간에 낭비될 수 있죠.

실제 학습 로그(TensorBoard나 Weights & Biases 같은 툴, 혹은 터미널 출력)에서 어떤 식으로 나타나는지 단계별 예시로 설명해 드릴게요.


1. 정상적인 학습 로그 (Stable)

정상적인 상태에서는 Loss가 소폭 진동하며 완만하게 우하향합니다.

Epoch Step Loss Learning Rate 비고
10 5000 2.451 0.0001 안정적
10 5100 2.432 0.0001 점진적 하락
10 5200 2.445 0.0001 약간의 노이즈

2. NaN 발생 직전의 징후 (Exploding)

문제가 생기기 시작하면 Loss 숫자가 갑자기 단위 자체가 달라집니다. 이때가 골든타임입니다.

Epoch Step Loss 상태 분석
11 6000 2.410 평온함
11 6100 25.842 [주의] 갑자기 10배 상승 (Gradient가 튀기 시작)
11 6200 842.15 [경고] 수치가 기하급수적으로 폭주
11 6300 15402.9 [위험] 시스템이 감당 가능한 수치 끝단에 도달
11 6400 NaN [사망] 연산 결과가 숫자로 표현 불가능해짐

3. 로그를 볼 때 유심히 봐야 할 "신호"

① Loss의 갑작스러운 점프 (Spike)

보통 Loss는 $2.1 \rightarrow 2.0 \rightarrow 1.9$ 식으로 변해야 하는데, 갑자기 $2.0 \rightarrow 20.0$이 되었다면 특정 데이터 배치(Batch)에서 모델이 감당 못 할 정도로 큰 오차가 발생했다는 뜻입니다.

② 분산 학습 시 '특정 노드'의 문제

분산 학습 로그를 개별적으로 볼 수 있다면, 전체 평균 Loss는 멀쩡해 보이는데 특정 노드의 Loss만 튀는 경우가 있습니다.

  • 원인: 특정 노드가 읽어들인 데이터 파일이 깨졌거나, 해당 노드의 GPU 메모리(VRAM) 수치 연산에 오류가 생긴 경우입니다.
  • 결과: 결국 All-reduce(가중치 평균화) 과정을 통해 다른 63개 노드의 멀쩡한 값들까지 모두 NaN으로 물들여 버립니다.

③ Gradient Norm (기울기 노름) 확인

만약 로그에 grad_norm 항목을 출력하고 계신다면, Loss보다 이걸 먼저 보세요. Loss가 튀기 전에는 반드시 Gradient Norm 값이 평소보다 수십 배 커지는 현상이 선행됩니다.


4. 이 상황에서 어떻게 대처해야 하나요?

만약 로그에서 10배 이상 튀는 것을 실시간으로 목격했다면:

  1. 즉시 중단: NaN이 찍히기 전이라도 학습을 멈추는 것이 좋습니다. (이미 모델 가중치가 망가졌을 확률이 높습니다.)
  2. 체크포인트 복구: 문제가 생기기 직전의 체크포인트(가장 최근에 저장된 모델 파일)에서 다시 시작하세요.
  3. Gradient Clipping 적용: 코드를 수정할 수 있다면 torch.nn.utils.clip_grad_norm_ 같은 함수를 써서 기울기가 일정 수준(예: 1.0) 이상 커지지 않게 강제로 눌러주세요.
  4. Learning Rate 낮추기: 튀었던 지점 근처에서 학습률을 절반 정도로 낮춰서 다시 시도해 보세요.

 

 

 

반응형

댓글