루나에게 말 걸기: 같은 시작 문장에 왜 매번 다른 이야기가 나올까

이 장의 물음

대화형 언어 모델에게 같은 질문을 두 번 하면 답이 조금씩 다르게 나오고, 「다시 생성」 단추를 누를 때마다 다른 문장이 나온다. 학습이 끝난 모델은 파라미터가 바뀌지 않는데도 그런데, 반대로 설정에서 「온도」를 0으로 내리면 같은 질문에 늘 같은 답이 돌아온다. 모델이 내놓는 것은 다음 토큰의 확률표 하나뿐인데, 그 표에서 글 한 줄을 만들어 내는 과정의 어디에서 이런 차이가 생길까?

동화를 쓰는 작은 GPT 를 학습해 두었으니 이제 그 모델에게 말을 걸 수 있다. 시작 문장을 주고 이어 쓰게 하면 되는데, 이어 쓸 때마다 확률표에서 토큰을 하나씩 골라야 한다. 이 장은 다음 물음에 차례로 답한다.

그리디 선택: 늘 가장 그럴듯한 토큰만 고르면

라운지 창가 자리에서 민준은 노트북으로 자기 게이밍 GPU 에 접속해 둔 화면을 서연에게 돌려놓았다. 동화를 쓰는 루나는 생겼지만, 서연이 몰래 넣은 장난 말고는 「Once upon a time」만 넣어 봤을 뿐이었다.

김민준 M02
김민준

이제 말을 걸어 보자. 루나가 하는 건 결국 이어 쓰기니까, 우리가 첫 문장을 주면 되잖아.

이서연 S16
이서연

시작 문장은 내가 쓸게. 이번엔 장난 안 쳐. 너한테 맡기면 별 얘기만 넣을 거잖아.

서연이 이번에 적은 시작 문장은 「Once upon a time there was a boy named Minjun.」이었다. 이 장이 끝나면 루나는 같은 시작 문장에서 매번 다른 이야기를 쓰고, 여러 줄을 한꺼번에 쓸 때는 지금보다 여덟 배 빨리 쓰게 된다.

이어 쓰기 한 번은 지금까지의 토큰을 모델에 넣어 다음 토큰의 확률표를 받고, 표에서 토큰 하나를 골라 줄 끝에 붙이는 일이다. 붙인 토큰까지 넣어 다시 표를 받고, 또 하나를 고르기를 되풀이한다. 표를 받는 데까지는 학습한 모델이 하고, 표에서 하나를 고르는 규칙은 우리가 정한다. 가장 먼저 떠오르는 규칙은 표에서 확률이 가장 큰 토큰을 고르는 것이다.

이어 쓰기에서는 다음 표를 받으려면 토큰 하나가 줄 끝에 붙어 있어야 하므로, 토큰마다 표를 숫자 하나로 줄이는 일을 피할 수 없다. 그래서 물음은 「어떻게 줄일까」이고, 공용 모듈의 생성 함수는 줄이는 규칙을 한 곳에 모아 두었다.

# sources/common/sample.py 의 generate 에서 (발췌)
logits = logits[:, -1, :].float()              # 마지막 자리의 로짓 = 다음 토큰의 점수
probs = filter_logits(logits, temperature, top_k, top_p)
if probs is None:                              # temperature == 0: 그리디 선택
    nxt = logits.argmax(dim=-1, keepdim=True)
else:                                          # 거른 확률표에서 하나를 뽑는다
    nxt = torch.multinomial(probs, 1, generator=generator)
out = torch.cat([out, nxt], dim=1)             # 줄 끝에 붙이고 다음 걸음으로

argmax 줄처럼 매번 확률이 가장 큰 토큰을 고르는 규칙을 그리디 선택(greedy decoding)이라 한다. 다음 토큰 하나만 보고 가장 좋아 보이는 것을 고를 뿐 그 뒤를 생각하지 않는다는 뜻의 이름이다. 무작위가 없으므로 같은 시작 문장이면 늘 같은 글이 나온다. 타이니스토리즈로 학습한 27,579,392 파라미터 모델에 서연의 시작 문장을 넣고 200 토큰을 그리디 선택으로 이어 쓰게 했다(이야기 끝 토큰이 나와도 멈추지 않게 했다).

### 그리디 (τ=0): 4-토큰 되풀이 비율 0.056, 평균 놀람 0.577 nats/토큰 (샘플 1 개)
[1]  He was very curious and loved to explore. One day, he decided to go on an adventure. He walked and walked until he came to a big, tall mountain. He was so excited! He wanted to climb the mountain.\nMinjun started to climb the mountain. … But then he heard a loud noise. It was a big, scary monster! … He caught Minjun and he was never seen again.\nThe end.Once upon a time, there was a little girl named Lily. She had a big, soft bed that she loved to sleep in. …
김민준 M04
김민준

산에 올라갔다가 괴물한테 잡혀서 다시는 안 보였대. 이게 제일 그럴듯한 이야기라고?

이서연 S12
이서연

그리고 바로 Lily 이야기로 넘어가. 너는 퇴장이고.

글 자체는 매끄럽고, 같은 말을 끝없이 되풀이하지도 않는다. 「되풀이 비율」은 생성한 토큰 가운데 바로 앞 네 토큰 묶음이 같은 샘플 안에 이미 나왔던 것의 비율인데, 그리디 선택은 0.056 이었다. 그리디 선택을 하면 같은 구절을 맴돈다는 이야기가 많지만, 이 모델의 200 토큰에서는 그런 일이 드물었다. 이상한 일은 이야기가 끝난 뒤에 일어났다. 시작 문장 다섯 개로 500 토큰씩 그리디 선택을 해서, 이야기가 끝난 뒤 무엇이 나오는지 세어 보았다.

시작 문장 이야기 끝 토큰 「Lily」 「bird」
Once upon a time there was a boy named Minjun. 2번 14번 9번
Lily and Tom 1번 14번 1번
The cat 3번 16번 5번
Minjun liked Seoyeon. 2번 8번 17번
One day, 3번 19번 0번

다섯 시작 문장 모두에서 「Lily」가 여덟 번 넘게 나왔고, 넷에서 「bird」가 나왔다. 이야기가 한 번 끝나면 어느 시작 문장에서 출발했든 같은 이야기로 빠져들기 때문이다. 「The cat」에서 출발한 글의 마지막 80 토큰은 이렇다.

[The cat] … 마지막 80 토큰:  there was a little girl named Lily. She had a big, red ball. Lily loved to play with her ball every day. One day, she saw a small, helpless bird with a hurt wing. The bird could not fly.\nLily wanted to help the bird. She said, "Don't worry, bird. I will help you." She took the bird to her mom. Her mom

그리디 선택은 같은 문맥에서 늘 같은 토큰을 고른다. 학습 데이터에서 이야기 끝 토큰 뒤에 오는 이야기는 앞 이야기와 상관없는 새 이야기였으므로, 모델은 이야기 끝 토큰 뒤에서 앞의 글을 거의 보지 않도록 배웠을 것이다. 그렇다면 이야기 끝 토큰 뒤의 문맥은 어느 시작 문장에서 왔든 사실상 같은 문맥이고, 같은 문맥에서 그리디 선택은 같은 토큰 줄을 고른다. 그 줄이 이 모델이 아는 가장 뻔한 이야기인 「Lily 와 날개 다친 새」인데, 이 설명은 우리의 짐작이고 따로 재 보지는 않았다.

김민준 M01
김민준

시작을 뭘로 하든 결국 Lily 랑 새야. 다섯 번 다.

이서연 S12
이서연

너랑 똑같네. 무슨 얘기로 시작해도 결국 별빛 정거장이랑 루나로 끝나잖아. 온도가 낮아.

김민준 M06
김민준

온도?

문제 1. 한 스텝씩 가장 좋은 것이 두 스텝 전체로도 가장 좋은가

토큰 두 개로 된 글을 쓰는 모델이 있다. 첫 토큰은 A(확률 0.6) 또는 B(0.4)다. A 다음에는 C 0.4, D 0.3, E 0.3 이고, B 다음에는 F 0.9, G 0.1 이다. (가) 그리디 선택으로 쓰는 글과 그 글의 확률은? (나) 두 토큰짜리 글 다섯 개 가운데 확률이 가장 큰 글은? (다) 두 답이 다른 이유를 한 문장으로 말하라.

김민준 M01
김민준

(가)는 바로 돼. 첫 토큰은 0.6 인 A, 그다음은 0.4 인 C. 글 「AC」의 확률은 0.6 × 0.4 = 0.24.

이서연 S01
이서연

(나)는 다섯 개를 다 곱해 볼게. AC 0.24, AD 0.18, AE 0.18, BF 0.4 × 0.9 = 0.36, BG 0.04. 가장 큰 건 BF 0.36 이야.

선생님 T01
선생님

그리디 선택은 첫 토큰에서 B 를 버렸어요. 그때 버린 것은 무엇이었죠?

김민준 M07
김민준

B 뒤에 F 가 0.9 로 거의 확실하게 온다는 정보요. A 는 첫 토큰에서 이겼지만, A 뒤의 표는 셋으로 고르게 퍼져 있어서 어느 걸 골라도 0.4 를 못 넘어요.

이서연 S01
이서연

그럼 (다)는 「한 스텝의 확률만 보면 그 뒤의 표가 얼마나 뾰족한지를 못 본다」야. 스텝마다 가장 좋은 것을 이어 붙인 게 전체로 가장 좋은 건 아니고.

선생님 T01
선생님

그래요. 글 전체의 확률을 가장 크게 하는 글을 찾으려면 갈 수 있는 글을 모두 봐야 하는데, 토큰이 200 개만 돼도 후보 글의 수는 셀 수 없이 많아요. 그리디 선택은 그 탐색을 한 스텝 앞만 보는 것으로 줄인 근사예요.

이서연 S01
이서연

그러면 문제가 작을 때는 다섯 개를 다 곱해 보면 되는데, 실제 모델에서는 그리디 선택이 놓친 BF 같은 글이 있는지조차 모르는 거네요.

문제 2. 시작 문장을 바꿔도 Lily 가 나오는 이유

그리디 선택으로 500 토큰을 쓰게 하면 시작 문장 다섯 개 모두에서 「Lily」가 8~19번 나오고, 「The cat」에서 출발한 글은 「a little girl named Lily … a small, helpless bird with a hurt wing」으로 이어졌다. (가) 이야기 끝 토큰 바로 뒤에서 모델이 앞 이야기를 거의 보지 않는다면, 다섯 시작 문장의 이야기 끝 뒤에 나오는 글은 서로 어떻게 될까? (나) 「Lily and Tom」은 Lily 가 시작 문장에 이미 있었다. 이 시작 문장의 「Lily」 14번은 다른 시작 문장의 14번과 같은 이유로 나온 것인가? (다) 그리디 선택을 그대로 두고 다섯 글이 서로 다른 이야기가 되게 하려면 무엇을 바꿔야 하나?

이서연 S01
이서연

(가)부터. 이야기 끝 뒤의 문맥이 사실상 같으면 그리디 선택은 같은 토큰을 고르고, 고른 토큰이 같으니 다음 문맥도 같아. 그러면 끝까지 같은 글이 나와.

김민준 M02
김민준

출력에서 확인할 수 있어. 「Minjun liked Seoyeon.」이랑 「Once upon a time there was a boy named Minjun.」의 마지막 80 토큰에 둘 다 「She picked up the bird and put it in her pocket. She took the bird home and gave it food and water. The bird started to feel better.」가 글자 하나 안 틀리고 들어 있어.

선생님 T01
선생님

(나)는 어때요? 「Lily and Tom」의 마지막 80 토큰도 새 이야기예요?

김민준 M01
김민준

이건 케이크 굽는 이야기예요. 「helped her mom make the cake」, 「Lily’s friends came over to the party」. 이야기 끝 토큰이 한 번밖에 안 나와서, 새 Lily 이야기가 아직 시작 문장의 Lily 를 이어 쓰는 중일 수도 있어요.

이서연 S06
이서연

그럼 이 글의 14번에는 시작 문장의 Lily 를 이어 쓴 것과 이야기 끝 뒤에 새로 나온 Lily 가 섞여 있겠네. 횟수가 같다고 이유가 같다고 할 수는 없어.

선생님 T01
선생님

(다)는요? 모델은 그대로 두고요.

이서연 S01
이서연

그리디 선택은 같은 문맥에서 늘 같은 걸 고르니까, 문맥이 같아지는 걸 막을 수 없으면 고르는 규칙에 무작위를 넣어야 해. 확률표에서 확률대로 뽑으면 같은 문맥에서도 다른 토큰이 나오니까.

선생님 T01
선생님

그게 이 장의 나머지 이야기예요. 표대로 뽑는다고 할 때, 그 표를 그대로 쓸지 손을 볼지가 다음 물음이고요.