다음 글자 맞히기: 세기만으로 루나가 말을 할까
이 장의 물음
휴대폰 자판에 「내일 몇」까지 치면 「시에」가 먼저 떠오르고, 대화형 언어 모델은 긴 답을 한 토큰씩 이어 쓴다. 둘 다 지금까지 나온 글을 보고 그다음을 내놓는다는 점이 같은데, 다음에 올 수 있는 글은 대개 하나가 아니다. 「Once upon a ti」 다음은 거의 틀림없이 「m」이지만, 「Lily saw a 」 다음에는 「b」도 「c」도 「r」도 올 수 있다. 그렇다면 언어 모델이 내놓는 것은 답 하나일까, 아니면 다른 무엇일까?
그런 모델을 가장 간단하게 만드는 방법은 많은 글에서 어떤 글자 다음에 어떤 글자가 오는지 세는 것이다. 센 표만으로 글을 쓰게 할 수 있다면, 그 글은 어떤 모습일까?
이 장은 다음 물음에 차례로 답한다.
- 「다음 글자를 맞힌다」는 말을 확률로 적으면 무엇이 되고, 글 전체의 확률과는 어떻게 이어질까?
- 바로 앞 글자 하나만 보고 다음 글자를 맞히는 표는 어떻게 세고, 그 표에는 무엇이 담길까?
- 표에서 한 글자씩 뽑아 글을 쓰려면 어떻게 뽑아야 하고, 늘 가장 흔한 글자만 고르면 어떻게 될까?
언어 모델: 다음 글자에 확률을 매기는 함수
실습 수업 첫 시간, 선생님은 칠판에 한 줄을 적었다. 「학기 말까지: 빈 모델에서 출발해 직접 학습한 언어 모델 하나, 그리고 그 모델 속에서 찾아낸 것 하나.」 두 명씩 조를 짜라는 말이 끝나기도 전에 서연은 조 편성표의 한 칸에 두 이름을 나란히 적어 두었고, 민준은 자기 이름이 이미 적힌 것을 보고서야 고개를 끄덕였다.

첫 주에는 GPU 가 필요 없어요. 노트북으로 글자를 세어 오세요. 어떤 글자 다음에 어떤 글자가 오는지요.

…너 이거 알고 있었지? 어제 라운지에서 똑같은 말 했잖아.

공지에 첫 주 내용 다 있었어. 너만 안 읽었지.
이 장이 끝나면 루나는 타이니스토리즈의 글자 쌍을 센 표 하나로 첫 옹알이를 한다.
옛 루나의 대사도 결국 다음 글자 맞히기였다면, 새 루나가 맨 먼저 할 줄 알아야 하는 것도 그것이다. 그런데 「맞힌다」를 그대로 받아들이면 곤란해진다. 「Lily saw a 」 다음에 오는 글자는 글마다 다르므로, 모델이 글자 하나만 내놓으면 그 글자가 틀린 글에서는 할 말이 없다. 다음에 올 수 있는 글자마다 얼마나 그럴듯한지를 함께 내놓아야, 흔한 이어짐과 드문 이어짐을 모두 담을 수 있다.
앞 글을 안다고 놓은 확률
「앞 글이 이러할 때 다음 글자가 무엇일 확률」은 앞 글을 안다고 놓고 잰 조건부 확률이어서, 서연이 먼저 그 강의를 떠올렸다.
언어 모델에서 세로 막대 오른쪽에 오는 것은 지금까지 나온 글이다. 글의 t번째 글자를 xt, 그 앞의 글자 전부를 x<t로 적으면, 모델이 위치마다 내놓는 것은 p(xt ∣ x<t)라는 분포 하나다. 어휘(모델이 아는 글자 전체)에 글자가 V개 있으면 이 분포는 합이 1인 수 V개의 목록이다. 이렇게 앞 글을 받아 다음 글자의 분포를 내놓는 함수를 언어 모델 (다음 글자의 확률을 매기는 함수 / language model)이라 한다.
다음 글자의 분포를 위치마다 알면 글 전체의 확률도 따라 나온다. 첫 글자의 확률에, 첫 글자를 안다고 놓은 둘째 글자의 확률을 곱하고, 다시 앞의 둘을 안다고 놓은 셋째 글자의 확률을 곱해 가면 된다. 선생님은 이 곱셈을 짧게 다시 살펴봤다.
곱의 형태라서 언어 모델에게 맡기는 일은 언제나 같다. 위치 t에서 앞 글 x<t를 보고 다음 글자의 분포를 내놓는 일 하나만 잘하면, 글 전체의 확률을 재는 일도 글을 새로 쓰는 일도 모두 그 분포에서 나온다.
무엇을 글자로 볼까
실습에 쓸 글은 타이니스토리즈(TinyStories)다. 네 살배기도 알아들을 만한 낱말로 GPT-3.5 와 GPT-4 가 쓴 짧은 영어 동화를 모은 데이터로, 로넨 엘단(Ronen Eldan)과 리위안즈(Yuanzhi Li)가 2023년에 공개했다. 우리는 GPT-4 가 쓴 글만 모은 V2 판을 쓰고, 학습 파일이 2,227,753,162 바이트, 검증 파일이 22,502,601 바이트다. 라이선스는 CDLA-Sharing-1.0 이라 고쳐 쓰고 나눌 수 있으며, 이 데이터로 만든 것을 다시 나눌 때는 같은 조건을 따른다.
글자를 무엇으로 셀지도 정해야 한다. 영어 동화는 거의 전부 ASCII(영문자·숫자·문장 부호의 128가지 기본 문자) 안에 있어서, 파일의 바이트 하나를 글자 하나로 봐도 거의 어긋나지 않는다.
검증 파일 22,493,387 글자 중 ASCII 밖 글자 4,613 개 (0.0205%)
그래서 당분간 루나는 바이트 하나를 글자 하나로 읽고, 어휘 크기 V는 바이트가 가질 수 있는 값의 수인 256이다. 한글은 글자 하나가 UTF-8 로 세 바이트라 이 방법이 맞지 않는데, 글을 어떤 조각으로 자를지는 토큰을 다룰 때 다시 고민한다.
ML에서: 큰 모델도 위치마다 표 한 줄을 내놓는다
GPT 같은 트랜스포머도 하는 일은 같다. 앞 토큰들을 읽고, 어휘의 토큰마다 점수를 하나씩 매긴 뒤 소프트맥스로 합이 1인 분포로 바꾼다. 어휘가 바이트 256개가 아니라 토큰 수만 개라 표의 한 줄이 길 뿐이고, 학습할 때는 실제로 나온 다음 토큰에 모델이 준 확률을 키우도록 파라미터를 고친다. 글을 쓸 때는 그 분포에서 토큰 하나를 고르고, 고른 토큰을 앞 글에 붙여 다시 분포를 낸다. 이 장의 표 세기와 신경망의 차이는 분포를 어떻게 얻느냐에 있고, 분포를 내놓는다는 약속은 처음부터 끝까지 바뀌지 않는다.
문제 1. 사흘 내리 비가 올 확률
어느 도시에서 아무 날이나 고르면 비가 올 확률은 0.3이다. 그런데 이 도시는 비가 이어지는 편이라, 전날 비가 왔으면 다음 날 비가 올 확률은 0.6이다. (가) 월·화·수 사흘 내리 비가 올 확률은? (나) 날씨가 날마다 서로 상관없다고 보고 0.3을 세 번 곱하면 얼마가 나오고, 그 곱은 왜 (가)와 다른가?

(가)는 월요일 0.3, 화요일은 월요일에 비가 왔으니 0.6, 수요일도 0.6이에요. 곱하면 0.3 × 0.6 × 0.6 = 0.108이요.

(나)는 0.3³ = 0.027이야. (가)의 4분의 1이네.

둘 가운데 이 도시에 맞는 값은 어느 쪽이에요?

(가)요. (나)는 화요일 확률을 월요일 날씨와 상관없이 0.3으로 썼는데, 이 도시에서는 월요일에 비가 오면 화요일 확률이 0.6으로 올라가니까요.

글자도 같아요. 「q」가 나왔으면 다음 글자의 확률은 아무 데서나 고른 글자의 확률과 전혀 달라요. 그래서 글의 확률은 글자 확률을 그냥 곱하지 않고, 앞을 안다고 놓은 확률을 곱해요.
문제 2. 300글자 동화의 확률
루나가 300글자짜리 동화 하나에 대해, 글자마다 실제 다음 글자에 확률 0.3을 주었다고 하자. (가) 동화 전체의 확률은 대략 10의 몇 제곱인가? (나) 파이토치의 기본 소수 형식(float32)은 0보다 큰 가장 작은 수가 약 1.4 × 10⁻⁴⁵이다. 300개의 확률을 이 형식으로 차례로 곱하면 어떻게 되는가? (다) 그럼 글 전체의 확률을 어떻게 다뤄야 하는가?

일단 곱해 볼게요. 0.3³⁰⁰이니까… log₁₀ 0.3이 −0.523쯤이라 300을 곱하면 −156.9예요. 10⁻¹⁵⁷ 근처요.

float32 로 곱하면요?

10⁻⁴⁵ 아래로는 못 내려가니까, 90글자쯤 곱하면 0이 돼요. 0.3⁹⁰이 10⁻⁴⁷ 근처라서요. 그 뒤로는 뭘 곱해도 0이고요.

그럼 300글자짜리 동화 둘을 비교할 수가 없잖아. 둘 다 0이면.

곱이 문제라면, 곱을 다른 연산으로 바꿀 방법은 없을까요?

로그요. 로그를 씌우면 곱이 합이 되니까, 300 × log 0.3이라는 보통 크기의 음수가 나와요. 회상 상자에 있던 로그 합이 이거였네요.

그래요. 언어 모델의 학습과 평가는 확률을 곱하지 않고 로그 확률을 더해요. 글자마다 더해지는 이 −log 값이 무엇을 재는지는 루나가 얼마나 잘 맞히는지를 잴 때 다시 보게 돼요.