혼합 정밀도

편집 역사 토론
수치해석 소프트웨어 마지막 수정: 2026-08-23 04:13:26

1. 개요[편집]

혼합 정밀도
Mixed precision
핵심 아이디어싼 정밀도로 일하고, 비싼 정밀도로 채점한다
주요 형식FP64 · FP32 · TF32 · FP16 · BF16 · FP8
대표 기법반복 정련 · 손실 스케일링 · 보상 합
이론적 근거후진 오차 해석, 조건수
벤치마크HPL-MxP (구 HPL-AI)
실패 신호잔차 바닥 · inf/NaN · 재현 불가

혼합 정밀도(mixed precision)는 하나의 계산을 여러 부동소수점 형식에 나눠 맡기는 전략으로, 비용이 큰 대부분의 연산은 낮은 정밀도로 처리하고 정확도를 결정하는 소수의 지점만 높은 정밀도로 붙잡아 최종 결과의 품질은 고정밀 계산과 동등하게 유지하는 것을 목표로 한다. 표어로 줄이면 이렇다 — 싼 정밀도로 일하고, 비싼 정밀도로 채점한다.

동기는 하드웨어 쪽에서 왔다. GPU 컴퓨팅 하드웨어에서 FP16/BF16 행렬 곱의 처리량은 FP64의 수십 배이고, 무엇보다 데이터가 절반으로 줄어든다. 메모리 대역폭이 병목인 스텐실·희소 커널에서 정밀도를 반으로 낮추는 것은 대역폭을 두 배로 늘리는 것과 같다. 문제는 정밀도를 낮추면 부동소수점 연산의 반올림오차가 그만큼 커진다는 것. 그래서 혼합 정밀도의 기술적 내용물은 전부 “어디를 낮춰도 답이 안 상하는가”를 판정하는 일이고, 그 판정의 언어가 후진 오차 해석조건수다.1

2. 형식 동물원 — 지수와 가수의 배분[편집]

부동소수점 형식은 결국 주어진 비트를 지수(동적 범위)와 가수(유효숫자)에 어떻게 나눠 주느냐의 문제다. 같은 16비트라도 FP16과 BF16은 이 배분이 정반대다.

형식부호·지수·가수유효비트단위 반올림 uu최대 정규수최소 정규수
FP64 (double)1·11·52532531.1×10162^{-53}\approx1.1\times10^{-16}1.8×103081.8\times10^{308}2.2×103082.2\times10^{-308}
FP32 (single)1·8·23242246.0×1082^{-24}\approx6.0\times10^{-8}3.4×10383.4\times10^{38}1.2×10381.2\times10^{-38}
TF321·8·10112114.9×1042^{-11}\approx4.9\times10^{-4}3.4×10383.4\times10^{38}1.2×10381.2\times10^{-38}
FP16 (half)1·5·10112114.9×1042^{-11}\approx4.9\times10^{-4}65504655046.1×1056.1\times10^{-5}
BF161·8·78283.9×1032^{-8}\approx3.9\times10^{-3}3.4×10383.4\times10^{38}1.2×10381.2\times10^{-38}
FP8 (E4M3)1·4·34246.3×1022^{-4}\approx6.3\times10^{-2}4484481.6×1021.6\times10^{-2}
FP8 (E5M2)1·5·2323=0.1252^{-3}=0.12557344573446.1×1056.1\times10^{-5}

읽는 법은 이렇다.

  • BF16은 FP32를 뒤에서 잘라낸 형식이다. 지수 8비트가 그대로라 동적 범위가 FP32와 같고, FP32 ↔ BF16 변환이 비트 자르기/붙이기로 끝난다. 대신 유효숫자가 8비트(십진 두 자리 남짓)뿐이다. 범위를 사고 정밀도를 판 것.
  • FP16은 정밀도를 사고 범위를 팔았다. 유효숫자는 BF16보다 세 비트 넉넉한데 최대값이 65504에 불과하다. 딥러닝 학습에서 기울기 제곱합이 슬쩍 오버플로해 inf가 나는 사고가 여기서 나온다.
  • TF32는 FP16의 가수 + FP32의 지수다. 32비트 레지스터에 담기되 유효비트는 19개만 쓰는 텐서코어 전용 내부 형식으로, 사용자 코드를 한 줄도 안 고치고 FP32 GEMM을 몰래 저정밀로 돌리는 용도다. 그래서 “왜 답이 미묘하게 달라졌지?”의 흔한 범인이기도 하다.2
  • FP8은 두 벌로 온다. E4M3은 순전파 활성값·가중치용으로 범위를 좁히고 정밀도를 챙겼으며 무한대 인코딩을 포기했고, E5M2는 역전파 기울기용으로 범위를 챙겼다. 유효숫자 서너 비트짜리 형식이 쓸모가 있는 이유는 텐서코어 내부 누산이 FP32이기 때문이다.

여기서 혼합 정밀도의 가장 중요한 원칙이 나온다. 저정밀은 저장·전송·곱셈에 쓰고, 누산(accumulation)은 반드시 고정밀로 한다. FP16 입력 × FP16 입력 → FP32 누산이 텐서코어 MMA의 기본 계약인 것이 그래서다. 곱셈 한 번의 오차는 uu지만 nn개를 더하는 동안의 오차는 nunu로 자라기 때문에, 길이 4096짜리 내적을 FP16으로 누산하면 유효숫자가 통째로 증발한다.

3. 반복 정련 — 저정밀 LU를 고정밀 해로 끌어올리기[편집]

수치선형대수에서 혼합 정밀도의 심장은 반복 정련(iterative refinement)이다. LU 분해를 싼 정밀도로 한 번만 해 두고, 잔차를 비싼 정밀도로 재서 보정을 반복한다.

L^U^=PA (정밀도 uf),{ri=bAxi(정밀도 ur)U^1L^1Pdi=ri(정밀도 uf)xi+1=xi+di(정밀도 u)\hat{L}\hat{U} = PA \ (\text{정밀도}\ u_f), \qquad \begin{cases} r_i = b - A x_i & (\text{정밀도}\ u_r)\\[2pt] \hat{U}^{-1}\hat{L}^{-1}P\,d_i = r_i & (\text{정밀도}\ u_f)\\[2pt] x_{i+1} = x_i + d_i & (\text{정밀도}\ u) \end{cases}

비싼 것은 O(n3)O(n^3)의 분해뿐이고 반복 한 번은 O(n2)O(n^2)이므로, 분해를 FP16으로 하고 잔차만 FP64로 재면 거의 FP16 값으로 FP64 정확도를 산다. 고전적 형태는 윌킨슨(J. H. Wilkinson)이 1960년대 초에 정리했고 — 분해를 작업 정밀도로, 잔차를 그 제곱 정밀도로 계산하는 형태다 — 스킬(Skeel, 1980)은 잔차까지 작업 정밀도로만 계산해도 한 번의 정련으로 성분별 후진 안정성이 회복됨을 보였다. 자세한 오차론은 후진 오차 해석반복 개선 쪽 이야기다.

문제는 수렴 조건이 조건수에 걸린다는 것이다. 표준 반복 정련은 대략

κ(A)uf1\kappa_\infty(A) \lesssim u_f^{-1}

일 때만 수렴한다. FP16으로 분해하면 uf12×103u_f^{-1}\approx 2\times10^{3} — 즉 조건수가 천 단위만 넘어도 정련이 안 붙는다. 실무 행렬 대부분이 이 문턱 위에 있으니, 이대로면 쓸모가 없다.

GMRES-IR(Carson–Higham, 2017–2018)이 이 문턱을 밀어낸 방법이다. 보정계 Ad=rAd = r을 저정밀 LU로 직접 풀지 말고, 그 LU 인수를 전처리기로 삼아 GMRES로 푼다. 전처리된 행렬 U^1L^1PA\hat U^{-1}\hat L^{-1}PA는 원 행렬이 아무리 나빠도 단위행렬 근처에 몰려 있어 GMRES가 몇 번에 끝나고, 행렬-벡터 곱만 고정밀로 하면 저정밀 분해의 오차가 증폭되지 않는다. 그 결과 허용 조건수가 대략 uf2u_f^{-2} 급, FP16 분해 기준 10610^{6}~10710^{7} 대까지 올라간다. “싼 분해를 해로 쓰지 말고 전처리기로 써라” — 혼합 정밀도 선형대수의 한 줄 요약이다.3

이 구도는 크리로프 부분공간법 전반으로 일반화된다. 켤레기울기법다중격자법 전처리기를 FP32로 돌리고 바깥 반복만 FP64로 두는 것은 오래된 관행이고, 이론적 근거도 같다 — 전처리기는 근사여도 되지만 잔차는 정확해야 한다.

4. HPL-MxP — 벤치마크가 된 반복 정련[편집]

TOP500의 전통적 HPL은 n107n\sim10^7짜리 조밀계를 FP64로 푼 성능을 잰다. 여기에 대응해 2019년 도입된 HPL-MxP(도입 당시 이름은 HPL-AI)는 같은 문제를 저정밀 LU + 반복 정련으로 풀되, 최종 해가 HPL과 동일한 잔차 기준을 통과해야 인정한다. 즉 “정확도는 FP64, 계산은 FP16”이라는 혼합 정밀도의 주장을 벤치마크로 못 박은 것.

효과는 극적이다. 상위 기계들이 보고한 HPL-MxP 성능은 같은 기계의 HPL(FP64) 기록보다 대략 한 자릿수 배 높다 — 후가쿠가 2 EFlop/s급, 프론티어가 10 EFlop/s 안팎을 보고했는데 같은 기계의 FP64 HPL은 각각 0.44, 1.2 EFlop/s 수준이었다. 다만 이 배수를 그대로 실제 응용에 옮겨 적을 수는 없다. HPL이 푸는 행렬은 조건수가 얌전한 난수 행렬이고, 실제 CAE 행렬은 그렇지 않다.4

5. 딥러닝 쪽 — 손실 스케일링과 마스터 가중치[편집]

학습 쪽 혼합 정밀도의 표준 레시피(Micikevicius 외, 2018)는 세 조각이다.

  1. FP32 마스터 가중치. 순전파·역전파는 FP16으로 하되 옵티마이저가 갱신하는 원본은 FP32로 따로 둔다. 갱신량 ηg\eta g가 가중치의 2112^{-11}보다 작으면 FP16에서는 더해도 값이 안 변한다(스토킹당한 반올림). 학습률이 작아지는 후반부에 학습이 조용히 멈추는 사고가 이것.
  2. 손실 스케일링. FP16 기울기는 작아서 최소 정규수 6×1056\times10^{-5} 아래로 언더플로하는 성분이 많다. 손실에 상수 SS(보통 2k2^k)를 곱해 역전파하고 옵티마이저 직전에 1/S1/S로 되돌린다. 동적 손실 스케일링inf/NaN이 뜨면 SS를 반으로 줄여 그 스텝을 버리고, 일정 스텝 동안 무사하면 SS를 두 배로 올린다 — 오버플로 문턱을 자동 탐색하는 셈이다.
  3. 연산별 정밀도 분리. 행렬 곱·합성곱은 FP16, 소프트맥스·정규화 계층의 평균·분산 누산·손실 계산은 FP32. 층 정규화배치 정규화의 통계 누산을 FP32로 유지하는 것은 지금도 사실상 예외 없는 관행이다.

BF16이 등장하면서 1·3은 남고 2는 대체로 사라졌다. 지수가 FP32와 같으니 언더플로/오버플로가 거의 안 나기 때문. 대신 유효숫자 8비트의 대가는 다른 데서 받는다 — 큰 값에 작은 값을 계속 더하는 누산에서 BF16은 FP16보다 훨씬 빨리 무뎌진다. 추론 단계에서 정밀도를 더 낮추는 이야기는 신경망 양자화, 활성값을 낮춰 메모리를 아끼는 이야기는 그래디언트 체크포인팅·역전파 쪽에 있다.

6. 보상 합 — 누산을 정밀도 없이 지키는 법[편집]

정밀도를 올리지 않고도 누산 오차를 잡는 고전 기법이 카한 보상 합(Kahan summation, 1965)이다. 매 덧셈에서 잘려나간 하위 비트를 보정 변수 cc에 담아 다음 항에 되먹인다.

y=xic,t=s+y,c=(ts)y,s=ty = x_i - c,\qquad t = s + y,\qquad c = (t - s) - y,\qquad s = t

순진한 누적의 오차 한계가 nuxinu\sum\lvert x_i\rvert인 데 비해 보상 합은 (2u+O(nu2))xi(2u + O(nu^2))\sum\lvert x_i\rvert로, 항의 개수에 거의 무관해진다. 사실상 배정밀도 하나를 공짜로 얻는 셈이라 FP32 누산기 하나로 FP64 누산에 근접할 수 있다. 노이마이어(Neumaier, 1974) 변형은 xix_iss보다 큰 경우까지 처리해 더 견고하다. 컴파일러가 cc의 계산을 “수학적으로 0”이라며 최적화로 날려버리는 것이 유일하고도 영원한 함정이다.5

계산 순서를 바꾸는 것만으로 얻는 것도 있다. 쌍대합(pairwise summation)은 재귀적으로 반씩 나눠 더해 오차를 ulog2nu\log_2 n으로 낮추며, 비용은 순진한 합과 사실상 같다. NumPy의 sum이 이걸 쓴다. 병렬 컴퓨팅에서 리덕션 트리의 모양이 달라지면 답의 마지막 자리가 달라지는 재현성 문제도 같은 뿌리이며, 저정밀을 섞으면 그 흔들림이 눈에 보이는 자리까지 올라온다.

7. CFD·구조에서 어디까지 낮춰도 되나[편집]

실무 감각을 규칙으로 적으면 이렇다. 정밀도를 낮추기 전에 재야 할 것은 속도가 아니라 조건수다. 상대오차의 대략적 하한이 κ(A)u\kappa(A)\cdot u이므로, 목표 정확도 ε\varepsilon에 대해 uε/κ(A)u \lesssim \varepsilon/\kappa(A)를 만족하는 가장 싼 형식이 답이다.

  • 명시적 유동 솔버는 FP32가 대체로 안전하다. 시간 전진이 국소적이고 CFL 조건이 스텝을 묶어 두므로 오차가 지수적으로 자라지 않는다. 격자 볼츠만 방법은 특히 유명한 성공 사례인데, 다만 분포함수 fif_i를 그대로 저장하지 말고 평형 가중치를 뺀 증분 fiwif_i - w_i로 저장하는 것이 정석이다 — 안 그러면 충돌 단계의 fifieqf_i - f_i^{\rm eq}에서 자리수 손실이 정면으로 터진다.
  • 압력 포아송·암시적 계는 바깥 반복을 FP64로 남긴다. 전처리기(다중격자 V-사이클, ILU)는 FP32로 돌려도 수렴 횟수가 거의 안 변하지만, 잔차 계산과 내적은 FP64로 해야 한다. FP32로 전부 돌리면 상대 잔차가 10610^{-6}~10710^{-7} 부근에서 더 안 내려가는 정밀도 바닥을 만난다. 이걸 “수렴이 정체됐다”고 읽고 완화계수를 만지는 것이 이 바닥의 고전적 삽질.
  • 좌표와 기하는 FP64를 고수한다. 도메인이 수 km인데 격자 간격이 mm라면 좌표 차에서 유효숫자가 통째로 날아간다. 메시 생성과 기하 판정(요소 뒤집힘, 교차 판정)에서 저정밀은 그냥 위상 오류가 된다.
  • 구조해석의 희소 직접법은 낮추기 어렵다. 쉘·빔 요소가 섞인 강성행렬은 회전 자유도와 병진 자유도의 스케일 차이만으로도 조건수가 10810^{8}~101210^{12}에 이른다. 여기서는 FP32 분해가 그 자체로 답을 망친다. 대신 FP32로 분해하고 FP64로 정련하는 구도는 성립하며, 이것이 상용 코드가 조용히 채택하고 있는 절충이다.
  • 장시간 누적은 정밀도를 살려 둔다. 분자동역학에서 힘 계산은 FP32로 해도 되지만 위치·에너지 누산은 FP64나 고정소수점을 쓰는 것이 표준이다. 수백만 스텝을 곱하면 uu가 작다는 말이 아무 의미가 없어진다.

마지막으로 검증 및 확인의 관점. 혼합 정밀도는 정밀도를 바꿔 가며 같은 케이스를 돌려 보는 것 자체가 훌륭한 검증 도구다. FP32와 FP64의 답이 공학적 관심 물리량에서 갈린다면, 그건 정밀도 문제이기 이전에 그 물리량이 애초에 그만큼 민감하다는 신호다. 정밀도를 낮춰 시간을 벌었으면 번 시간의 일부는 그 검증에 되돌려 쓰자.

8. 관련 문서[편집]

9. Footnotes[편집]

  1. 혼합 정밀도가 “요즘 것”처럼 보이지만 발상 자체는 반세기 넘었다. 메모리가 킬로바이트 단위이던 시절에도 사람들은 배정밀도를 아껴 쓰려고 같은 고민을 했다. 달라진 것은 동기다 — 예전엔 메모리가 없어서, 지금은 실리콘이 저정밀 곱셈기를 훨씬 싸게 찍어낼 수 있어서.

  2. 그래서 각종 프레임워크에 allow_tf32 같은 스위치가 달려 있다. 기본값이 켜져 있던 시절 “GPU를 바꿨더니 회귀 테스트가 깨졌다”는 제보가 쏟아졌다. 테스트가 부동소수점 값을 ==로 비교하고 있었다면 그건 GPU 탓이 아니라는 반론도 함께 쏟아졌다.

  3. 이 아이디어의 예쁜 점은 저정밀 분해가 틀려도 상관없다는 데 있다. 전처리기의 유일한 임무는 스펙트럼을 모아 주는 것이고, 그 임무에는 유효숫자 세 자리면 충분하다. 정답을 책임지는 것은 고정밀로 계산한 잔차뿐이다. 대충 하는 놈과 채점하는 놈의 역할 분담이 명확한, 보기 드물게 건강한 조직 구조.

  4. HPL 계열 벤치마크가 실제 응용을 대표하지 못한다는 비판은 HPL-MxP 이전부터 있었고 HPCG가 그 대안으로 나왔다. HPL-MxP는 그 비판을 해결한 게 아니라 “저정밀 하드웨어의 이론 성능을 정직하게(잔차 검사를 통과시키며) 보여 주자”는 다른 목적의 물건이다. 숫자를 인용할 때는 그 목적까지 같이 인용하는 것이 예의다.

  5. -ffast-math(혹은 /fp:fast)를 켜면 컴파일러는 실수 산술이 결합법칙을 만족한다고 가정하고 c=(ts)yc = (t-s)-y00으로 접어 버린다. 카한 합이 컴파일러 최적화에 의해 조용히 삭제되는 이 현상은 하도 유명해서, 진지한 코드는 volatile을 붙이거나 아예 컴파일러 배리어를 넣는다. 반올림오차를 잡으려고 쓴 코드가 반올림오차를 무시하는 최적화에 지워진다니, 이 바닥의 시적 정의라 할 만하다.