
1. NaN이란 무엇인가?
NaN은 Not 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. 대규모 노드 환경에서 특히 주의할 점
현재 대규모 노드에서 학습 중이시라면, 다음 상황을 염두에 두어야 합니다.
- 배치 사이즈(Batch Size): 노드가 많으면 전체 배치 사이즈가 매우 커집니다. 이 경우 학습률(Learning Rate)도 함께 높여 잡는 경우가 많은데, 이때 수치적 불안정성이 생기기 쉽습니다.
- 분산 학습 동기화: 특정 노드에서 데이터 로딩 오류로 이상한 값이 들어오면 전체 모델로 NaN이 전염됩니다.
- 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배 이상 튀는 것을 실시간으로 목격했다면:
- 즉시 중단: NaN이 찍히기 전이라도 학습을 멈추는 것이 좋습니다. (이미 모델 가중치가 망가졌을 확률이 높습니다.)
- 체크포인트 복구: 문제가 생기기 직전의 체크포인트(가장 최근에 저장된 모델 파일)에서 다시 시작하세요.
- Gradient Clipping 적용: 코드를 수정할 수 있다면 torch.nn.utils.clip_grad_norm_ 같은 함수를 써서 기울기가 일정 수준(예: 1.0) 이상 커지지 않게 강제로 눌러주세요.
- Learning Rate 낮추기: 튀었던 지점 근처에서 학습률을 절반 정도로 낮춰서 다시 시도해 보세요.
'[GPUaaS] > GPUmgt' 카테고리의 다른 글
| [Syslog] mlx5_core ... Detected insufficient power on the PCIe slot (27W) (0) | 2026.02.21 |
|---|---|
| [GPU서버 HW문제] NaN 발생 가능성 !! (0) | 2026.02.21 |
| [분산 학습] 운영 관점에서 NaN vs Inf 차이 !! (0) | 2026.02.21 |
| [분산 학습] NaN 반드시 알아야 하는 개념 !! (0) | 2026.02.21 |
| [GPU] 스크립트 사용 프로세스 확인 및 삭제 방법!! (0) | 2026.02.19 |
| [nvidia-healthmon] 파일 위치 및 주의사항 !! (0) | 2026.02.19 |
| nvidia-smi (System Management Interface) VS nvidia-healthmon (Tesla Health Monitor) !! (0) | 2026.02.19 |
| Ceph vs Lustre 차이 !! (0) | 2026.02.18 |
댓글