맺음: 남은 물음

큰 모델의 학습 안정성: 손실이 튈 때

이 책은 학습이 무너지는 경우를 여럿 보았고, 그때마다 처방을 하나씩 얻었다. 학습률이 2/L 을 넘으면 가장 가파른 방향에서 튕겨 나가니 학습률을 그 아래로 잡는다. 지형의 좁은 벽에 닿은 스텝 하나가 학습을 날려 버릴 수 있으니 그래디언트 클리핑으로 스텝의 길이를 묶는다. 학습 초반에는 곡률이 크니 워밍업으로 학습률을 천천히 올리고, 층을 쌓으면 신호가 터지거나 사라지니 초기화와 잔차 연결로 크기를 지킨다. 폭이 바뀌어 같은 학습률이 듣지 않을 때는 μP 로 학습률과 초기화를 폭에 맞춘다. 이 처방을 모두 쓰고도 큰 모델의 손실은 왜 가끔 치솟을까?

역사: 대책을 찾지 못한 스무 번쯤의 튐

구글의 PaLM 논문(차우더리 외, 2022)은 그래디언트의 전체 노름을 1.0에서 자르는 클리핑을 켜 두었는데도, 가장 큰 모델을 학습하는 동안 손실이 치솟는 일이 스무 번쯤 있었다고 보고했다. 가장 큰 모델은 학습 비용이 너무 커서, 그 팀은 원리에 기댄 대책을 찾지 못했다고 적었다. 대신 손실이 튀기 시작하기 약 100스텝 전에 저장해 둔 파라미터(체크포인트)로 돌아가 그 언저리의 미니배치 200~500개를 건너뛰는 방법으로 피했다. 같은 미니배치를 다른 체크포인트에서 학습하면 튀지 않았으므로, 특정 데이터와 특정 파라미터 상태가 만났을 때 생기는 일로 보았다.

몇 해 뒤 반대쪽 보고가 나왔다. 문샷 AI 는 2025년 7월 파라미터 1조 개짜리 Kimi K2 를 Muon 에 어텐션 점수가 폭주하지 않게 막는 장치(QK-clip)를 더한 MuonClip 으로 15.5조 토큰 학습하는 동안 손실이 한 번도 치솟지 않았다고 밝혔다. PaLM 팀은 왜 튀는지 모른 채 피했고, 문샷 AI 는 튀지 않게 막는 장치를 하나 더했다. 그런데 두 보고 모두 「왜 튀는가」에 대한 일반적인 답은 아니다.

처방이 겨누는 것과 놓치는 것

처방들을 겨누는 곳으로 늘어놓으면 무엇이 비어 있는지 보인다. 학습률의 상한과 워밍업은 곡률이 정하는 벼랑을, 클리핑은 스텝의 길이를, 초기화와 잔차 연결과 μP 는 층을 지나는 신호와 스텝의 크기를 겨눈다. 그런데 클리핑을 다룰 때 본 것처럼, 스텝의 길이를 묶어도 묶인 스텝이 가는 방향이 나쁜 곳이 아닌지는 보지 못한다. 그리고 「샤프니스」에서 본 것처럼 실제 신경망은 학습률의 상한 근처를 넘나들며 학습하기도 한다. 2/L 은 「이 아래면 장담할 수 있다」는 경계이지 「넘으면 반드시 무너진다」는 경계가 아니었다. 그렇다면 튐은 벼랑을 넘은 스텝일까, 벼랑 근처를 걷는 학습에 원래 따라오는 일일까, 아니면 데이터 한 덩어리가 지형을 잠깐 바꾼 것일까? 지금의 처방들은 튐이 일어난 뒤 되돌리거나, 튈 만한 곳을 미리 하나씩 막는 데 머문다. 학습을 시작하기 전에 튈지 안 튈지를 예측하는 이론은 아직 없다.

짧게: 수치 정밀도

손실이 튀는 것은 숫자를 담는 그릇 자체 때문이기도 하다. 큰 모델은 계산을 빠르게 하려고 32비트 실수(float32) 대신 16비트 실수로 계산하는 일이 많고, 16비트에는 bfloat16 과 float16 두 형식이 흔히 쓰인다. 파이토치로 float32 와 함께 세 형식의 성질을 재면 다음과 같다.

형식 1 바로 위의 간격 가장 큰 수 1 × 10⁻⁸ 을 담으면
float32 1.19 × 10⁻⁷ 3.40 × 10³⁸ 1.00 × 10⁻⁸
bfloat16 0.0078 3.39 × 10³⁸ 1.00 × 10⁻⁸
float16 0.00098 65,504 0

bfloat16 은 float32 와 같은 범위의 수를 담지만, 1 바로 위의 간격이 0.0078(2⁻⁷)이라 1 둘레에서는 소수 둘째 자리쯤까지만 가린다. 그래서 가중치 1.0 에 학습률 0.001짜리 스텝을 더하면 결과는 그대로 1.0 이다. 스텝이 간격의 절반보다 작아 반올림에 사라진 것이다. 스텝 0.0001 을 1000번 더해도 bfloat16 의 가중치는 1.0 에서 움직이지 않는다(참값은 1.1, float32 로 더하면 1.100017). 학습률이 작아 보이는 것이 아니라 아예 학습이 일어나지 않는다. float16 은 1 둘레를 더 곱게 가리지만 가장 큰 수가 65,504 라 곧잘 넘치고, 가장 작은 쪽도 2⁻²⁴(약 6 × 10⁻⁸)보다 작은 수는 0이 된다. 머리말에서 물었던 Adam 의 eps 0.00000001 은 float16 에 담으면 0 이다.

파울리우스 미치케비치우스(Paulius Micikevicius)와 동료들은 2018년 국제 학습 표현 학회(ICLR)에 낸 「혼합 정밀도 학습(Mixed Precision Training)」에서 세 가지 처방을 냈는데, 여기서는 그 가운데 둘을 본다(나머지 하나는 16비트 수의 곱을 더할 때 합을 32비트로 모으는 것이다). 논문이 다룬 형식은 float16 이지만 생각은 bfloat16 에도 그대로 통한다. 첫째는 가중치의 32비트 사본을 따로 두고 스텝을 그 사본에 모은 뒤, 계산할 때만 16비트로 내려 쓰는 것이다. 위의 스텝 0.0001 을 1000번 float32 사본에 모으면 1.100017 이 되고, 이것을 bfloat16 으로 내리면 1.101562 다. 스텝 하나하나는 16비트로 담을 수 없어도 모인 스텝은 남는다. 둘째는 손실을 큰 수로 곱해 그래디언트를 키운 뒤 계산하고 나중에 다시 나누는 것이다. 그래디언트 1 × 10⁻⁸ 은 float16 에서 0 이 되지만, 손실을 1024배 해서 담으면 1.025 × 10⁻⁵ 로 남고 다시 1024로 나누면 1.0012 × 10⁻⁸ 이 된다. 둘 다 숫자를 담는 그릇이 작을 때 작은 변화가 반올림에 묻히지 않게 따로 챙기는 방법이다. 정밀도는 튐의 여러 원인 가운데 하나일 뿐이지만, 스텝이 사라지거나 수가 넘치는 일은 학습 곡선에서 다른 원인의 튐과 비슷하게 보인다는 데서 원인을 가리기 어렵게 만든다.