단원 홈
2단원 · 17차시

기계가 듣고 말한다
다음 낱말 맞히기가 만들어 낸 것

16차시에서 우리는 기계가 그림을 보는 방식을 만들었습니다. 오늘은 말을 다룹니다. 놀랍게도 계산은 하나뿐이에요 — 지금까지 나온 말 다음에 올 낱말은 무엇인가. 낱말을 세어 나누는 스무 줄짜리 코드가 문장을 만들어 내는 것을 직접 보고, 그 문장이 이상해지는 자리에서 오늘날의 언어 모델이 무엇을 고쳤는지까지 따라갑니다.

성취기준 12인기02-06 성취기준 12인기02-05
언어 모델바이그램문맥 임베딩어텐션스펙트로그램 환각
🎯 학습 목표
  • 언어 모델이 하는 계산을 '다음 낱말의 확률' 하나로 설명하고, 다섯 문장짜리 말뭉치에서 그 확률을 손으로 계산해 검산할 수 있다.
  • 같은 말뭉치에 바이그램과 트라이그램을 돌려 어긋난 문장 7개 → 0개 · 새 문장 11개 → 6개를 재고, 문맥을 늘릴 때 무엇을 얻고 무엇을 잃는지 조합의 개수로 설명할 수 있다.
  • 규칙 · k-NN · 신경망 · 바이그램 넷을 성적 · 필요한 데이터 · 설명 가능성으로 견주어, 딥러닝을 쓰지 않는 편이 나은 자리를 세 가지 들 수 있다.
🤔

여는 장면 — 자판이 다음 낱말을 미리 안다

휴대전화에 오늘이라고 치면 자판 위쪽에 날씨 저녁 학교 같은 낱말이 뜹니다. 아직 아무것도 치지 않았는데 말이에요. 스마트 스피커에게 "내일 아침 일곱 시에 깨워 줘"라고 말하면 알람이 맞춰집니다. 글쓰기 도우미에게 한 줄을 던지면 문단 하나가 돌아옵니다.

이 셋은 겉보기에 아주 다른 일 같습니다. 그런데 속에서 도는 계산은 하나입니다.

💭 오늘의 물음

"지금까지 나온 말 다음에 올 낱말은 무엇인가" — 이 계산 하나만으로 정말 글을 쓸 수 있는가? 만약 된다면, 그 글은 어디에서 무너지는가?

밝은 회색 바탕 위에 놓인 납작하고 둥근 스마트 스피커. 옆면은 회색 천으로 싸여 있고 흰 윗면에 둥근 단추 네 개가 있다
말로 시키는 스마트 스피커. 윗면의 단추는 네 개뿐이고, 나머지 일은 전부 말로 시킨다. 이 작은 기계가 말을 알아들으려면 두 가지 일이 잇달아 일어나야 한다. 먼저 소리를 글자로 바꾸고(음성 인식), 그다음 그 글자 다음에 올 말을 고른다. 오늘 우리가 만들 것은 뒤쪽 절반이고, 앞쪽 절반에는 놀랍게도 16차시에서 만든 합성곱이 쓰일 수 있다(개념 6). 출처: Smart Home Perfected, Wikimedia Commons (CC BY 2.0)

오늘 여러분은 낱말을 세어 나누기만 하는 스무 줄짜리 모델을 만듭니다. 그 모델은 문장을 만들어 냅니다. 실제로 이런 문장이 나옵니다.

아래는 오늘 만들 모델이 실제로 만들어 낸 문장이다. 원장 실행 출력 【4】에서 그대로 옮겼다 — 지어낸 예가 아니다.해가 뜨면 새가 운다 ← 말뭉치에 없던 새 문장인데 말이 된다 저녁에 해가 뜨면 새가 운다 ← 저녁에 해가 뜬다고? 바람이 불면 창문을 열면 바람이 불면 창문을 닫는다 ← 같은 말을 두 번 했다

첫 줄과 둘째 줄의 차이가 오늘의 전부입니다. 해가 다음에 뜨면이 오는 것은 자연스럽습니다. 뜨면 다음에 새가가 오는 것도 자연스럽고요. 그런데 맨 앞에 저녁에가 있었다는 사실은 세 낱말 뒤에서 이미 잊혔습니다.

왜 잊었을까요? 우리가 만들 모델이 바로 앞 낱말 하나만 보기 때문입니다. 오늘 수업은 이 "얼마나 멀리 보는가"라는 물음을 따라갑니다. 그리고 그 물음의 답이 길어져 온 역사가 곧 바이그램 → RNN → 트랜스포머의 역사입니다.

1

언어 모델이 하는 계산은 나눗셈 한 번이다

먼저 아주 작은 것부터 만듭니다. 문장 다섯 개짜리 말뭉치입니다.

말뭉치 ① — 다섯 문장. 이 수업을 위해 직접 지은 문장이다.비가 오면 우산을 쓴다 비가 오면 길이 미끄럽다 눈이 오면 길이 미끄럽다 눈이 오면 눈사람을 만든다 바람이 불면 우산이 뒤집힌다

이제 오면이라는 낱말에 주목합시다. 이 낱말은 말뭉치에 네 번 나옵니다. 그 뒤에 온 낱말을 순서대로 적으면 이렇습니다.

'오면' 뒤에 실제로 나온 낱말 목록 = ['우산을', '길이', '길이', '눈사람을']

그러면 오면 다음에 올 낱말의 확률은 나눗셈 한 번으로 정해집니다. 각 낱말이 나온 횟수를, 전체 횟수 4로 나누면 됩니다.

'오면' 다음 낱말나온 횟수전체 횟수확률
길이240.50
눈사람을140.25
우산을140.25
합계441.00
말뭉치 ①에서 손으로 계산한 확률. 오늘 만들 파이썬 코드도 정확히 이 값을 출력한다 (원장 실행 출력 【1】). 합이 1.00이 되는지 반드시 확인하라 — 이것이 검산이다.

이것이 바이그램(bigram) 언어 모델의 전부입니다. 신경망도, 학습도, 9차시에서 배운 경사하강법도 없습니다. 낱말을 세고 나눈 것뿐입니다.

⚠️ 오늘 만드는 것은 딥러닝이 아니다 — 먼저 못을 박자

바이그램에는 가중치도 없고, 손실도 없고, 역전파도 없습니다. 13~15차시에서 만든 신경망과는 완전히 다른 물건이에요. 그런데도 이것을 배우는 까닭은, '다음 낱말 맞히기'라는 뼈대 자체가 오늘날의 언어 모델과 같기 때문입니다. 오늘 배우는 것은 뼈대이지 오늘날의 기술이 아닙니다. 무엇이 얼마나 다른지는 개념 6에서 숫자로 재 볼 거예요.

말뭉치를 문장 마흔 개로 늘리면 같은 계산이 이렇게 바뀝니다. 오면이 이제 일곱 번 나오고, 뒤에 올 수 있는 낱말도 다섯 가지로 늘었습니다.

말뭉치 ② — 마흔 문장에서 잰 확률(원장 실행 출력 【3】). 오늘 실습에서 쓰는 말뭉치가 이것이다.'오면' 다음에 올 낱말 (모두 7번 나왔다) 0.29 (2번) 길이 0.29 (2번) 우산을 0.14 (1번) 눈사람을 0.14 (1번) 버스가 0.14 (1번) 세수를 '비가' 다음에 올 낱말 (모두 4번 나왔다) 0.75 (3번) 오면 0.25 (1번) 그치면 '해가' 다음에 올 낱말 (모두 4번 나왔다) 0.50 (2번) 뜨면 0.50 (2번) 지면

문장을 만드는 방법도 간단합니다. 시작 낱말을 하나 정하고, 그 확률대로 다음 낱말을 뽑고, 뽑은 낱말을 다시 열쇠로 삼아 또 뽑습니다. 멈출 자리는 <끝>이라는 표시로 알려 줍니다 — 말뭉치의 각 문장 끝에 이 표시를 붙여 두면, 모델은 "여기서 문장이 끝났다"는 것도 하나의 선택지로 배웁니다.

💡 왜 '가장 확률이 높은 낱말'을 고르지 않고 뽑는가

늘 1등만 고르면 어떻게 될까요? 시작이 같으면 언제나 똑같은 문장이 나옵니다. 그것만이 아닙니다. 원장에서 실제로 그렇게 돌려 보았더니 고리에 빠졌습니다 — 바람이 불면 창문을 열면이 네 번 되풀이되다가 낱말 상한에 잘렸어요.

확률대로 뽑는 것은 문장을 다양하게 만들기 위해서만이 아닙니다. 고리를 빠져나오기 위해서이기도 합니다.

2

스무 문장을 만들어 보면 — 잘 되는 자리와 무너지는 자리

말뭉치 ②(마흔 문장)로 문장 스무 개를 만들어 보았습니다. 그냥 눈으로 보고 "이건 이상하다"고 말하면 사람마다 다르겠지요. 그래서 기계적으로 판정하는 잣대를 하나 세웠습니다.

ℹ️ 무너진 자리를 어떻게 기계가 짚는가

만들어 낸 문장에서 낱말 세 개가 잇달아 붙은 토막을 하나씩 떼어, 그 토막이 말뭉치에 한 번이라도 있었는지 확인합니다. 말뭉치에 한 번도 없던 3연속이 나온 자리 — 거기가 바로 모델이 앞을 잊은 자리입니다.

예를 들어 저녁에 해가 뜨면이라는 3연속은 말뭉치에 없습니다. 말뭉치에는 저녁에 해가 지면과 아침에 해가 뜨면만 있었으니까요. 모델은 해가 하나만 보고 뽑았기 때문에 앞의 '저녁에'를 참고할 길이 아예 없었습니다.

이 잣대로 판정한 스무 문장입니다. 씨앗을 17로 못박았기 때문에 여러분이 오늘 실습에서 돌려도 글자 하나까지 같은 결과가 나옵니다.

바이그램으로 만든 20문장 (원장 실행 출력 【4】). 초록 = 말뭉치에 그대로 있던 문장(외운 것), 흰색 = 새 문장인데 3연속이 전부 말뭉치에 있는 것, 빨강 = 없던 3연속이 생긴 문장.01 비가 오면 세수를 한다 ← 없던 3연속: 비가 오면 세수를 02 비가 그치면 무지개가 뜬다 03 눈이 오면 길이 미끄럽다 04 눈이 그치면 하늘이 맑아진다 05 바람이 들어오면 커튼이 흔들린다 06 바람이 불면 창문을 열면 바람이 불면 창문을 닫는다 ← 없던 3연속: 불면 창문을 열면 07 해가 뜨면 새가 운다 ← 새 문장인데 3연속은 전부 말뭉치에 있다 08 해가 지면 별이 보이면 소원을 빈다 ← 없던 3연속: 지면 별이 보이면 09 비가 오면 세수를 한다 ← 없던 3연속: 비가 오면 세수를 10 눈이 오면 길이 미끄러우면 천천히 걸으면 늦게 도착한다 ← 없던 3연속: 오면 길이 미끄러우면 11 아침에 비가 그치면 무지개가 뜬다 ← 없던 3연속: 아침에 비가 그치면 12 아침에 눈이 오면 버스가 늦는다 13 저녁에 해가 뜨면 새가 운다 ← 없던 3연속: 저녁에 해가 뜨면 14 창문을 닫는다 ← 새 문장인데 3연속은 전부 말뭉치에 있다 15 창문을 닫으면 소리가 작아진다 16 바람이 불면 낙엽이 굴러간다 17 우산을 쓴다 ← 새 문장인데 3연속은 전부 말뭉치에 있다 18 우산을 쓰면 어깨가 젖는다 19 길이 미끄럽다 ← 새 문장인데 3연속은 전부 말뭉치에 있다 20 천천히 걸으면 늦게 도착한다 20개 중 외운 문장 9개 · 새 문장 11개 · 그중 3연속이 어긋난 문장 7개 서로 다른 문장 19가지 · 평균 4.35낱말

여기에서 두 가지를 함께 읽어야 합니다.

💡 잘 되는 쪽 — 7번 문장

해가 뜨면 새가 운다는 말뭉치에 없던 새 문장입니다. 그런데 3연속을 하나씩 떼어 보면 전부 말뭉치 안에 있었어요. 모델은 조각을 이어 붙였을 뿐인데 말이 됩니다. 언어 모델이 그럴듯해 보이는 까닭이 바로 이것입니다.

⚠️ 무너지는 쪽 — 6번 문장

바람이 불면 창문을 열면 바람이 … 같은 말을 두 번 했습니다. 창문을 뒤에 열면이 올 수 있고 열면 뒤에 바람이가 올 수 있으니, 모델은 자기가 방금 지나온 자리로 되돌아온 것을 눈치채지 못합니다.

같은 시작에서 여러 문장이 나오는 것도 확인해 봅시다. 비가로 시작해 200번 만들었더니 서로 다른 문장이 15가지 나왔습니다.

'비가'로 시작해 200번 (원장 실행 출력 【4】· 씨앗 170). 위에서 여섯 개만. 32번 비가 그치면 무지개가 뜬다 (말뭉치에 있던 문장) 28번 비가 오면 길이 미끄럽다 (말뭉치에 있던 문장) 24번 비가 오면 눈사람을 만든다 19번 비가 오면 세수를 한다 14번 비가 오면 우산을 챙긴다 5번 비가 오면 우산을 쓴다 (말뭉치에 있던 문장인데 오히려 드물다)

셋째 줄을 보세요. 비가 오면 눈사람을 만든다. 낱말 하나하나로 보면 흠잡을 데가 없습니다. 비가 뒤의 오면도, 오면 뒤의 눈사람을도 말뭉치에 있던 짝이에요. 그런데 비가 오는데 눈사람을 만들 수는 없습니다. 모델이 눈사람을을 뽑는 순간, 맨 앞의 비가는 이미 잊혔기 때문입니다.

📖 이것은 '기억'이 아니라 '설계'다

모델이 게을러서 잊은 것이 아닙니다. 애초에 볼 수 없게 만들었습니다. 우리 코드에서 다음 낱말을 고르는 줄은 random.choice(nxt[w]) 한 줄뿐이고, 여기에 들어가는 w는 바로 앞 낱말 하나입니다. 그러니 앞이 무엇이었든 결과는 같습니다. 이 한계를 고치려면 보는 범위를 바꾸는 수밖에 없어요. 다음 절에서 그것부터 해 봅니다.

3

앞을 두 낱말 보게 하면? — 얻는 것과 잃는 것

고칠 곳은 딱 한 줄입니다. 열쇠를 낱말 하나에서 낱말 둘의 짝으로 바꾸면 됩니다.

바이그램과 트라이그램의 차이는 정말 이 한 줄뿐이다.# 바이그램 — 앞 한 낱말이 열쇠 key = seq[i] # 트라이그램 — 앞 두 낱말의 짝이 열쇠 key = (seq[i], seq[i + 1])

이렇게 바꾸면 해가 하나가 아니라 저녁에 해가라는 짝이 열쇠가 됩니다. 말뭉치에서 저녁에 해가 뒤에 온 낱말은 지면뿐이니, 이제 저녁에 해가 뜰 일은 없습니다.

주사위는 그대로 두고(씨앗 17) 모델만 바꿔 다시 스무 문장을 만들어 보았습니다.

20개 중바이그램 (앞 1낱말)트라이그램 (앞 2낱말)
외운 문장 (말뭉치에 그대로 있던 것)9개14개
새 문장11개6개
3연속이 어긋난 문장7개0개
서로 다른 문장19가지19가지
평균 낱말 수4.354.15
원장 실행 출력 【6】. 오늘 실습의 📊 20문장 만들어 재기 단추가 이 표를 그대로 다시 만들어 준다. 확인 문제 2번에서 이 숫자를 묻는다.

어긋난 문장이 7개에서 0개로 사라졌습니다. 성공입니다. 그런데 트라이그램에서는 이 칸이 0개가 될 수밖에 없습니다. 다음 낱말을 뽑을 때 쓰는 열쇠가 이미 낱말 두 개이니, 거기에 뽑은 낱말을 붙인 3연속은 말뭉치에 있던 것일 수밖에 없기 때문이에요. 말뭉치를 어떻게 고쳐도 이 칸은 0에서 움직이지 않습니다. 뒤의 과제 ③에서 직접 확인해 보세요.

그리고 같은 표의 첫 줄을 보세요. 외운 문장이 9개에서 14개로 늘었습니다. 새 문장은 11개에서 6개로 줄었고요.

⚠️ "문맥을 늘리면 좋아진다"는 반쪽만 맞는 말이다

트라이그램은 3연속이 어긋난 문장을 아예 만들지 못합니다. 대신 말뭉치에 있던 문장을 되뇌는 쪽으로 옮겨 갔습니다. 말을 바꾸면 이렇습니다 — 덜 틀리는 대신 덜 새롭습니다.

그렇다고 트라이그램이 안전해진 것도 아닙니다. 새로 만든 여섯 문장 가운데 하나는 4연속이 말뭉치에 없었어요 — 아침에 눈이 오면 길이가 그것입니다(원장 실행 출력 【5】의 12번). 보는 범위를 한 칸 늘리면 무너지는 자리도 한 칸 뒤로 밀릴 뿐입니다. 그러면 문맥을 계속 늘리면 어떻게 될까요? 그 답이 오늘 수업에서 가장 중요한 대목입니다.

왜 그런가 — 칸은 폭발하는데 데이터는 그대로다

마흔 문장 말뭉치의 서로 다른 낱말은 107개입니다. 낱말 두 개를 늘어놓는 방법은 107 × 107 = 11,449가지예요. 그중 말뭉치에 실제로 나온 것은 114가지뿐입니다.

낱말 두 개 조합 (바이그램) 낱말 세 개 조합 (트라이그램) 전체 11,449칸 · 나온 것 114칸 채워진 칸 1.00% 나머지 99.00%는 0번 나왔다 전체 1,225,043칸 · 나온 것 83칸 채워진 칸 0.00678% 점 하나가 거의 보이지 않는다

낱말을 하나 더 볼 때마다 칸의 수는 어휘 크기만큼 곱해집니다(107배). 그런데 말뭉치는 그대로예요. 그래서 문맥을 늘릴수록 대부분의 칸이 0번이 됩니다. 숫자는 원장 실행 출력 【7】.

0번인 칸이 많으면 무엇이 문제일까요? 고를 것이 없어집니다. 실제로 세어 보면 이렇습니다.

모델열쇠 개수다음 낱말 후보가 하나뿐인 열쇠비율
바이그램 (앞 1낱말)107개75개70%
트라이그램 (앞 2낱말)114개108개95%
원장 실행 출력 【7】. 트라이그램은 열쇠의 95%가 갈림길이 아니다 — 고를 것이 하나뿐이라 말뭉치에 있던 문장을 그대로 되뇔 수밖에 없다.

이 표가 위의 결과를 전부 설명합니다. 트라이그램이 "덜 틀리는" 까닭도, "덜 새로운" 까닭도 같습니다 — 선택지가 없어서입니다.

원장에서 4그램(앞 세 낱말)·5그램(앞 네 낱말)까지 늘려 보았더니 끝이 분명해졌습니다.

문맥을 계속 늘리면 (원장 곁실험 [E]).2그램: 열쇠 107개 · 갈림길 없는 열쇠 75개 ( 70%) · 새 문장 11/20 3그램: 열쇠 114개 · 갈림길 없는 열쇠 108개 ( 95%) · 새 문장 6/20 4그램: 열쇠 83개 · 갈림길 없는 열쇠 82개 ( 99%) · 새 문장 2/20 5그램: 열쇠 45개 · 갈림길 없는 열쇠 45개 (100%) · 새 문장 0/20

5그램은 새 문장을 하나도 만들지 못합니다. 말뭉치를 통째로 외운 것이지요. 그러니 "문맥을 길게 보면 된다"가 답이 아닙니다. 문맥을 길게 보면서도 외우지 않는 방법이 필요합니다. 그 방법을 찾는 과정이 곧 다음 절의 이야기입니다.

4

낱말을 숫자로 — 우리 모델이 '눈'을 구별하지 못하는 까닭

확률표를 하나 더 보겠습니다. 이번에는 눈이입니다.

'눈이' 다음에 올 낱말 (원장 실행 출력 【3】). 여기에 문제가 하나 숨어 있다.'눈이' 다음에 올 낱말 (모두 6번 나왔다) 0.50 (3번) 오면 ← 하늘에서 내리는 눈 0.17 (1번) 그치면 ← 하늘에서 내리는 눈 0.17 (1번) 피로하다 ← 얼굴에 달린 눈 0.17 (1번) 피로하면 ← 얼굴에 달린 눈

한 칸에 두 개의 다른 낱말이 섞여 있습니다. 하늘에서 내리는 눈과 얼굴에 달린 눈이에요. 우리 모델은 이 둘이 다른 낱말이라는 것을 모릅니다. 글자가 같으면 같은 낱말입니다.

그래서 이 모델은 눈이 오면 길이 미끄럽다도 만들 수 있고, 화면이 밝으면 눈이 피로하다도 만들 수 있습니다. 그런데 둘을 섞어 "화면이 밝으면 눈이 오면 길이 미끄럽다"도 만들 수 있어요. 모델에게는 셋 다 똑같이 정당한 문장입니다.

문제는 더 깊은 데 있습니다. 지금 우리 모델에게 낱말은 이름표일 뿐입니다. 107개의 낱말은 서로 아무 관계가 없어요. 비가와 눈이가 둘 다 하늘에서 내리는 것이라는 사실도, 뜨면과 지면이 반대말이라는 사실도 모델은 영원히 알 수 없습니다. 각각 그 낱말 뒤에 무엇이 왔는지를 따로따로 셀 뿐이니까요.

조판 막대에 영문 금속 활자를 한 자씩 끼워 여러 줄의 문장을 짜 놓은 모습. 활자는 좌우가 뒤집혀 있다
조판 막대에 영문 금속 활자를 한 자씩 끼워 문장을 짜 놓은 모습(찍으면 바로 읽히도록 글자가 좌우로 뒤집혀 있다). 금속 활자는 글을 낱낱의 단위로 쪼개어 다루는 생각의 원형이다. 오늘 우리도 문장을 띄어쓰기로 쪼개어 낱말 단위로 다뤘다. 다만 활자는 글자마다 따로 떨어진 덩어리다. 우리 모델의 낱말도 꼭 그러해서, 서로 닮았는지 반대인지를 담을 자리가 없다. 출처: Willi Heidelbach (원본) / Daniel. (2차 저작), Wikimedia Commons (CC BY 2.5)

여기에서 나온 답이 임베딩(embedding)입니다. 낱말을 이름표가 아니라 숫자의 목록으로 바꾸는 거예요. 예를 들어 비가를 [0.8, −0.2, 0.5, …]처럼, 눈이를 [0.7, −0.3, 0.6, …]처럼 나타냅니다.

이렇게 하면 5차시에서 배운 거리를 낱말 사이에도 잴 수 있습니다. 뜻이 비슷한 낱말은 가까이, 다른 낱말은 멀리 놓이도록 그 숫자들을 데이터에서 배우게 하는 것이지요. 그러면 비가를 본 적이 있는 모델이 눈이에 대해서도 어느 정도 짐작할 수 있게 됩니다.

ℹ️ 임베딩의 숫자는 누가 정하는가 — 16차시와 똑같은 물음

16차시에서 우리는 3×3 필터의 아홉 숫자를 사람이 손으로 정했습니다. 진짜 합성곱 신경망은 그 숫자를 데이터에서 배운다고 했지요. 임베딩도 똑같습니다. 낱말마다 붙은 수십~수백 개의 숫자를 사람이 적어 넣는 것이 아니라, 다음 낱말을 잘 맞히도록 조금씩 고쳐 가면서 배웁니다. 14차시의 역전파가 하던 그 일이에요. 여기서부터가 딥러닝입니다. 오늘 우리가 만드는 바이그램은 그 앞에 서 있습니다.

5

문맥을 길게 보면서도 외우지 않기 — 어텐션이 고친 것

개념 3에서 우리는 벽에 부딪혔습니다. 문맥을 늘리면 어긋남은 줄지만 조합이 폭발해서 대부분의 칸이 0번이 되고, 결국 말뭉치를 외우게 됩니다. 이 벽을 넘으려면 "열쇠를 통째로 세는 방식"을 버려야 합니다.

저녁에해가 ? 바이그램 — 앞 한 낱말 저녁에 해가 ? '저녁에'는 아예 보이지 않는다 — 오늘 우리가 만드는 것 RNN — 앞의 말을 요약해 차례로 넘긴다 저녁에 해가 ? 앞의 말이 요약되어 흘러온다. 다만 멀리 있는 말일수록 요약에 묻혀 흐려진다 트랜스포머 — 전체를 한 번에 보고, 어디를 볼지 스스로 정한다 (어텐션) 저녁에 해가 ? 굵은 화살표 = 크게 참고한 자리. '저녁에'를 직접 본다 — 거리에 상관없이

세 방식은 전부 같은 물음에 답합니다 — 앞을 얼마나 멀리 볼 것인가. 바이그램은 한 낱말, RNN은 요약해서 흘려보내는 방식, 트랜스포머는 전체를 한 번에 보며 어디를 얼마나 볼지 스스로 정하는 방식입니다.

가운데 방식이 RNN입니다. 낱말을 하나씩 읽으면서 지금까지 읽은 것을 요약한 값을 함께 넘깁니다. 이러면 열쇠를 통째로 세지 않아도 되니 조합 폭발을 피할 수 있어요. 다만 요약값의 자리는 정해져 있어서, 멀리 있는 말일수록 요약에 묻혀 흐려집니다.

맨 아래가 어텐션(attention)입니다. 요약해서 넘기는 대신, 앞에 나온 모든 낱말을 한 번에 놓고 지금 필요한 자리를 골라 봅니다. 저녁에가 아무리 멀리 있어도 직접 볼 수 있고, 얼마나 볼지의 정도까지 데이터에서 배웁니다. "문맥을 길게 보면서도 외우지 않는 방법"이 바로 이것이었습니다.

이제 우리 모델과 오늘날의 모델을 같은 표에 놓아 봅시다.

무엇오늘 만든 바이그램오늘날의 언어 모델
기억하는 확률의 개수(매개변수)150개 수천억 개 규모로 알려져 있다
한 번에 보는 앞 문맥1낱말 책 몇 권 분량까지
학습에 쓴 글의 양165낱말 (585글자) 인터넷에 있는 글 전체에 가깝다
신경망 · 학습 · 경사하강법없음 전부 있다
하는 일다음 낱말 맞히기 다음 낱말 맞히기
⚠️ 왼쪽 칸만 우리가 실제로 잰 값이다(원장 실행 출력 【9】). 오른쪽 칸은 회사가 정확한 수를 공개하지 않는 경우가 많아 대략의 규모로만 적었다. 숫자를 확인하지 않은 채 "몇 억 개"라고 단정하지 말 것.

맨 아랫줄을 보세요. 네 칸이 전부 다른데 마지막 한 칸만 같습니다. 오늘 여러분이 만들 것과 오늘날의 언어 모델은 규모가 다르고 방법이 다르지만 목표는 같습니다. 그래서 오늘 만든 것이 뼈대라고 말할 수 있는 겁니다.

6

소리를 그림으로 바꾸면 — 16차시의 합성곱이 그대로 온다

스마트 스피커의 앞쪽 절반, 곧 소리를 글자로 바꾸는 일을 봅시다. 소리는 공기의 떨림입니다. 마이크가 그 떨림을 초당 수만 번 재어 숫자의 긴 줄로 만들어요. 이것을 파형이라고 합니다.

그런데 파형을 그대로 신경망에 넣으면 잘 되지 않습니다. "아"와 "이"를 가르는 것은 떨림의 순간값이 아니라 어떤 높낮이의 소리가 섞여 있는가이기 때문입니다. 그래서 파형을 짧은 조각(수십 밀리초)으로 자르고, 조각마다 어떤 높이의 소리가 얼마나 들어 있는지를 재어 표로 만듭니다. 이 표가 스펙트로그램입니다.

남성이 nineteenth century라고 말한 약 1초 길이의 음성의 스펙트로그램 — 가로는 시간(0~1초), 세로는 주파수(0~10kHz), 검은색에서 보라·빨강을 거쳐 노랑·흰색으로 갈수록 센 소리
남성이 "nineteenth century"라고 말한 음성의 스펙트로그램. 가로가 시간(0~1초), 세로가 소리의 높이(0~10kHz), 밝기가 세기다 — 검은색·짙은 남색은 약한 소리이고, 보라·빨강을 거쳐 노랑·흰색으로 갈수록 센 소리다. 이것을 한번 보라 — 이것은 소리가 아니라 그림이다. 가로로 뻗은 밝은 줄무늬, 위아래로 선 기둥 같은 무늬가 눈에 들어온다. 16차시에서 손글씨 '7'의 가로획·세로획·대각획을 찾던 것과 같은 일이 여기서도 일어난다. 출처: Aquegg, Wikimedia Commons (Public domain)

여기서 오늘 수업의 두 번째 중요한 대목이 나옵니다. 스펙트로그램은 세로 · 가로가 있는 숫자 격자입니다. 16차시에서 다룬 12×12 손글씨 격자와 다를 것이 없어요. 그러니 그때 만든 도구를 고치지 않고 그대로 쓸 수 있습니다.

① 소리 파형 초당 수만 개의 숫자 ② 스펙트로그램 가로 = 시간 · 세로 = 높이 ③ 합성곱 신경망 16차시의 필터 · 특징지도 · 풀링 ④ 글자, 그리고 언어 모델 "내일 아침 일곱 시" 오늘 만든 것이 여기에 붙는다 ②에서 ③으로 넘어가는 자리가 오늘의 요점 — 소리 문제가 그림 문제로 바뀌었다. 그래서 16차시에 만든 도구를 한 글자도 고치지 않고 쓸 수 있다.

음성 인식의 흐름. ③에서 쓰는 것은 16차시에서 손글씨를 볼 때 쓴 필터 · 특징지도 · 풀링 그대로입니다. 다만 필터의 숫자는 사람이 정하지 않고 배웁니다.

이것이 딥러닝이 널리 쓰이게 된 까닭 가운데 하나입니다. 문제를 격자 위의 숫자로 바꿀 수만 있으면, 도구는 한 벌로 돌려 씁니다. 사진에서 얼굴을 찾는 것도, 음성에서 낱말을 찾는 것도, 심지어 심전도에서 이상 신호를 찾는 것도 같은 합성곱을 씁니다.

⚠️ 다만 ④는 ③이 틀린 것을 고쳐 주기도 하고, 없던 말을 만들어 내기도 한다

③이 소리만 듣고 "내일 아침 일고 시"라고 내놓았다고 합시다. ④의 언어 모델이 '일고 시'라는 말은 거의 안 쓰이고 '일곱 시'가 훨씬 흔하다는 것을 알고 고쳐 줍니다. 음성 인식이 시끄러운 곳에서도 웬만큼 되는 데는 이 도움이 큽니다. 그런데 같은 힘이 반대로도 작동합니다. 소리에 없던 말을 "그럴듯하다"는 이유로 만들어 넣을 수도 있어요. 이 문제를 다음 절에서 이어 봅니다.

7

딥러닝을 쓰지 않는 편이 나은 때 — 네 방법을 한 표에 놓는다

이 단원에서 우리는 같은 종류의 물음을 네 가지 방법으로 풀어 왔습니다. 1차시의 규칙, 8차시의 이웃, 15차시의 신경망, 그리고 오늘의 세기. 마지막 시간이니 넷을 한 표에 놓아 봅시다. 숫자는 전부 그 차시에서 우리가 실제로 잰 값입니다.

무엇으로1차시
규칙 기반 스팸 필터
8차시
k-NN(이웃에게 묻기)
15차시
신경망
17차시
바이그램
성적 72.7%
규칙 4개 · 메일 44건
93.3%
k=5 · 시험 15개
80.6%
테스트 · 기준선 65.3%
정확도로
잴 수 없다
필요한 데이터 거의 없음
규칙은 사람이 쓴다
훈련 40개
저장만 하면 끝
훈련 168일
300회 되풀이
165낱말
세는 것은 한 번
왜 그렇게
판단했는지 설명
어느 규칙에 걸렸는지
그대로 보인다
가까운 이웃 다섯을
보여 주면 된다
어렵다 — 가중치
숫자만 남는다
확률표를 찍으면
전부 보인다
계산의 무게 거의 0
(낱말 찾기)
예측할 때마다
40개와 거리 계산
학습이 무겁다
(예측은 가볍다)
세기 한 번
(0.04초)
이럴 때 쓴다 규칙이 이미 분명하고
바뀌지 않을 때
데이터가 적고
근거를 보여야 할 때
규칙을 적기 어렵고
데이터가 많을 때
뼈대를 이해할 때
(실무용은 아니다)
성적은 그 차시의 실행 원장에서 그대로 옮긴 값이다(1차시 · 8차시 · 15차시). ⚠️ 네 열은 서로 다른 문제를 푼 것이라 성적을 곧바로 견주면 안 된다. "93.3%가 80.6%보다 좋다"가 아니라, 어느 자리에 어느 방법이 맞는가를 보는 표다.

이 표에서 읽어야 할 것은 딥러닝이 늘 이기지 않는다는 사실입니다. 아래 네 가지 자리에서는 오히려 다른 방법이 낫습니다.

  • 데이터가 적을 때. 15차시의 신경망은 훈련 168일치를 300번 되풀이해서 겨우 80.6%에 닿았습니다. 데이터가 40개뿐이라면 8차시의 k-NN이 훨씬 낫습니다. 손잡이가 많은 모델은 데이터가 적을 때 11차시의 과적합에 곧장 빠집니다.
  • 판단의 이유를 설명해야 할 때. 대출을 거절하거나 의료 판정을 내릴 때 "왜 그렇게 판단했나"에 답하지 못하면 쓸 수 없습니다. 신경망은 가중치 숫자만 남기지만, 규칙 기반은 걸린 규칙의 이름을 그대로 댈 수 있어요. 역설이지만 오늘 만든 바이그램은 오늘날의 언어 모델보다 설명 가능성이 높습니다 — probs() 한 줄만 찍으면 왜 그 낱말을 골랐는지 전부 보이니까요.
  • 규칙이 이미 명확할 때. "학번이 여덟 자리인가"를 판정하는 데 신경망을 학습시킬 이유는 없습니다. 1차시에서 우리는 규칙 4개로 72.7%를 얻었고, 규칙을 하나 더 넣었더니 오히려 정확도가 내려가는 지점도 보았습니다. 규칙이 통하는 자리에서는 규칙이 가장 정직합니다.
  • 전기와 돈이 문제일 때. 오늘 만든 바이그램은 노트북에서 0.04초 만에 끝납니다. 큰 언어 모델을 한 번 학습시키는 데는 데이터센터가 필요합니다. 얻는 것이 그만한 값을 하는가는 언제나 따져야 할 물음입니다.
ℹ️ 성취기준이 요구하는 비교

성취기준 [12인기02-06]은 딥러닝으로 문제를 해결하고 성능을 평가하라고 합니다. '평가'는 정확도 하나를 재는 것이 아닙니다. 같은 문제를 다른 방법으로도 풀어 보고 무엇이 달라지는지 재는 것이 평가의 절반입니다. 위의 표가 그 절반에 해당합니다.

8

생성형 모델은 무엇으로 재는가 — 정확도가 통하지 않는다

12차시에서 우리는 네 칸(혼동행렬)을 만들고 정확도 · 정밀도 · 재현율을 계산했습니다. 그 계산이 가능했던 까닭은 정답이 하나였기 때문입니다. 스팸인가 아닌가, 많이 남는가 아닌가 — 맞았는지 틀렸는지가 분명했지요.

그런데 오늘 만든 모델의 출력은 문장입니다. "비가 오면"에 이어질 정답은 무엇일까요? 우산을도 맞고 길이도 맞고 버스가도 맞습니다. 정답이 하나가 아닙니다. 그러니 12차시의 네 칸을 그대로 쓸 수 없어요.

그래서 생성형 모델은 다른 방식으로 잽니다. 크게 셋입니다.

① 사람이 견준다

같은 물음에 두 모델이 낸 답을 사람에게 보여 주고 어느 쪽이 나은지 고르게 합니다. 가장 믿을 만하지만 느리고 비쌉니다. 그리고 사람마다 기준이 달라서, 여러 명에게 물어 일치 정도까지 함께 재야 합니다.

② 문제집으로 잰다

정답이 정해진 문제 수천 개를 모아 두고 풀립니다(벤치마크). 점수가 숫자로 나와 견주기 쉽지만 함정이 있어요 — 그 문제집이 학습 데이터에 들어 있었다면 모델은 외운 것을 답한 것뿐입니다. 11차시에서 훈련 데이터로 시험을 보면 안 된다고 했던 그 이야기입니다.

③ 지어낸 말을 센다

사실이 아닌데 자신 있게 말하는 것을 환각(hallucination)이라 합니다. 답에 들어 있는 사실 주장을 하나씩 떼어 출처와 대조하고, 틀린 주장의 비율을 셉니다. 잴 수 있는 형태로 만들려면 사람의 손이 많이 듭니다.

환각이 왜 생기는지는 오늘 만든 모델이 이미 보여 주었습니다. 비가 오면 눈사람을 만든다 — 이 문장을 만들 때 모델은 망설이지 않았습니다. 각 낱말이 앞 낱말 다음에 올 확률이 높다는 것만 확인했을 뿐, 그 문장이 사실인지는 아예 확인하지 않았어요. 확인할 방법 자체가 모델 안에 없습니다.

⚠️ 언어 모델은 '참'을 고르지 않는다 — '그럴듯함'을 고른다

이것은 고장이 아니라 설계의 결과입니다. 우리 모델이 배운 것은 오직 "어떤 낱말 뒤에 어떤 낱말이 자주 오는가"뿐이에요. 규모가 커져도 배우는 것의 성격은 같습니다. 그러니 언어 모델의 답을 볼 때는 늘 "이 말이 사실인지는 따로 확인해야 한다"는 자세가 필요합니다. Ⅲ단원에서 이 문제를 사회적 영향의 관점에서 다시 다룹니다.

💻

손으로 ① — 다음 낱말 판, 내가 골라 문장을 만든다

아래 판은 진짜로 셉니다. 미리 적어 둔 문장을 꺼내 오는 것이 아니라, 오른쪽 아래 말뭉치 상자에 들어 있는 글을 그 자리에서 세어 확률을 구합니다. 말뭉치를 고치면 확률이 바로 달라져요.

그리고 이 판의 계산은 아래 파이썬 코드와 같은 알고리즘입니다. 난수기까지 파이썬의 것을 그대로 옮겨 넣었기 때문에, 씨앗을 같게 두면 두 곳에서 같은 문장이 나옵니다. 나란히 놓고 대조해 보세요.

🗣 다음 낱말 판 — 확률을 보고 골라 문장을 잇는다 INTERACTIVE

위쪽이 지금까지 만든 문장이고, 노란 테두리가 지금의 열쇠다. 아래쪽 막대가 그 열쇠 다음에 올 수 있는 낱말과 확률이다. 막대를 누르면 그 낱말이 문장에 붙는다(캔버스 아래의 단추를 눌러도 같다). 보라색 <끝>을 고르면 문장이 끝난다.

이 열쇠 다음에 올 수 있는 낱말 — 눌러서 이어 쓰세요

말뭉치 문장 수40
낱말 수 (연이어)165
서로 다른 낱말 V107
바이그램 종류114
트라이그램 종류83
채워진 칸 (V×V 중)1.00%
채워진 칸 (V³ 중)0.00678%
갈림길 없는 열쇠 (지금 모델)75 / 107 (70%)
기억하는 확률 개수150
지금 문장의 낱말 수1
무작위 짝 100번 중 있던 것—
20개 중외운 문장새 문장3연속 어긋남 서로 다른 문장평균 낱말
바이그램—— ———
트라이그램—— ———
[안내] 마흔 문장 말뭉치 · 바이그램으로 시작합니다. 막대를 눌러 문장을 이어 보세요.

말뭉치 — 한 줄이 한 문장입니다. 고치면 확률이 그 자리에서 바뀝니다.

※ 이 판의 난수기는 파이썬 random 모듈의 것(MT19937)을 그대로 옮긴 것입니다. 씨앗 17 · 시작 '비가' · 바이그램으로 [▶ 자동 이어 쓰기]를 누르면 아래 파이썬 출력 【4】의 첫 문장과 똑같은 문장이 나옵니다.
※ 📊 20문장 만들어 재기는 파이썬 출력 【4】【5】【6】을 그대로 다시 만듭니다. 마흔 문장 말뭉치 · 씨앗 17이면 바이그램 9/11/7/19, 트라이그램 14/6/0/19가 나와야 합니다.
※ 문장이 15낱말을 넘으면 자동으로 멈춥니다. 고리에 빠졌을 때 탭이 멎지 않게 하는 장치입니다.

과제 ① 표를 채운다 — 말뭉치 크기를 바꿔 가며 잰다

말뭉치 상자에서 아래쪽 문장을 지워 개수를 줄인 다음 [🔁 말뭉치 다시 세기] → [📊 20문장 만들어 재기]를 누르세요. 씨앗은 17로 고정합니다.

말뭉치 문장 수낱말 수서로 다른 낱말 V바이그램 종류 20개 중 새 문장서로 다른 문장
5문장
10문장
20문장
30문장
40문장

다 채웠으면 '새 문장' 열이 처음으로 크게 늘어나는 자리를 찾으세요. 그리고 40문장 줄에서 '서로 다른 문장'이 30문장 줄보다 늘었는지 줄었는지도 적어 두세요. 확인 문제 3번에서 이것을 묻습니다.

과제 ② 트라이그램이 막히는 자리를 찾는다

개념 3에서 "트라이그램은 대부분의 조합이 0번이라 고를 것이 없다"고 했습니다. 그 0번을 직접 만나 볼 차례입니다.

  • 모델을 바이그램으로 두고 눈이에서 시작해 오면까지 이어 봅니다. 이때 후보가 몇 개 뜨는지 세어 적으세요.
  • 모델을 트라이그램으로 바꾸고 같은 자리(눈이 오면)까지 옵니다. 후보가 몇 개로 줄었나요? 두 숫자를 나란히 적으세요.
  • 이제 [직접 치기] 칸에 낱말 두 개를 여러분이 골라 쳐 넣고 [이 말로 시작]을 누릅니다. 예를 들어 저녁에 뜨면처럼요. 후보가 0개라는 화면이 나오면 성공입니다 — 그 두 낱말이 말뭉치에서 나란히 나온 적이 한 번도 없기 때문입니다. 0개가 되는 짝을 세 개 찾아 공책에 적으세요.
  • 반대로 0개가 아닌 짝도 하나 찾아보세요. 훨씬 어렵습니다. 몇 번 만에 찾았는지도 적어 두세요.
  • 마지막으로 [🎲 무작위 짝 100번 시험]을 누릅니다. 낱말 둘을 아무렇게나 뽑아 그 짝이 말뭉치에 있는지 100번 확인해 줍니다. 100번 중 몇 번이 있었나요? 이 값을 개념 3의 1.00%와 견주세요.
💡 "0번"은 "확률 0"과 다르다

표에 없는 칸을 보고 "그런 말은 있을 수 없다"고 읽으면 안 됩니다. 저녁에 뜨면이 0번인 것은 우리 말뭉치 마흔 문장에 없었다는 뜻일 뿐이에요. 세상에는 그런 말이 있을 수 있습니다. 재어 본 적이 없는 칸과 재어 봤더니 0이었던 칸은 다릅니다. 이 구별을 못 하는 것이 데이터가 적은 모델의 가장 흔한 실패입니다.

과제 ③ 말뭉치를 고쳐 숫자를 움직인다

이제 여러분이 말뭉치를 고칠 차례입니다. 아래 둘 중 하나 이상을 해내세요.

🅰 트라이그램의 새 문장을 6개보다 크게 만든다

트라이그램 · 씨앗 17에서 마흔 문장 말뭉치의 새 문장은 6개였습니다. 말뭉치에 문장을 두 개만 더해 이 수를 7개 이상으로 올려 보세요. 힌트 — 같은 두 낱말 짝(예: 해가 뜨면)으로 시작하면서 뒤가 서로 다른 문장을 넣으면 그 짝에 갈림길이 생깁니다. 완성했으면 더한 문장 두 개와 결과 숫자를 공책에 적으세요.

⚠️ 무엇을 더해도 트라이그램의 '3연속 어긋남' 칸은 0에서 움직이지 않습니다. 여러 번 해 보고, 왜 움직일 수 없는지를 개념 3의 설명을 떠올려 한 줄로 적으세요.

🅱 바이그램의 새 문장을 11개보다 크게 만든다

바이그램 · 씨앗 17에서 마흔 문장 말뭉치의 새 문장은 11개였습니다. 문장을 다섯 개 더해 이 수를 12개 이상으로 올려 보세요. 그냥 아무 문장이나 더하면 잘 안 됩니다 — 이미 있는 낱말을 다시 쓰면서 새로운 이음새를 만드는 문장이라야 합니다. 더한 다섯 문장과 결과 숫자를 적으세요.

⚠️ 같은 문장을 여러 번 붙여 넣는 것으로는 안 된다

원장에서 실제로 해 보았습니다. 같은 문장을 되풀이해 말뭉치를 부풀리면 낱말 수는 165 → 330 → 825로 느는데 바이그램 종류는 114가지 그대로였고, 새 문장도 나아지지 않았습니다. "데이터를 늘린다"는 것은 없던 조합을 새로 본다는 뜻이지 같은 것을 더 본다는 뜻이 아닙니다. 판에서 직접 확인해 보세요 — 말뭉치를 두 배로 붙여 넣고 [🔁 말뭉치 다시 세기]를 누르면 '바이그램 종류' 칸이 움직이지 않습니다.

💻

손으로 ② — 언어 모델을 스무 줄로 직접 짠다

이제 판이 아니라 코드로 같은 것을 만듭니다. 빈칸은 네 곳인데, 실은 서로 다른 물음이 셋입니다. 두 곳(★②)은 같은 자리가 두 번 나오는 것이라 답이 같아요.

코드가 길어 보이지만 모델 자체는 스무 줄 남짓입니다 — build_bigram · probs · gen_bigram 세 함수가 전부예요. 나머지는 결과를 재고 화면에 찍는 부분입니다. 길이에 겁먹지 말고 이 세 함수부터 읽으세요.

💡 빈칸 세 물음
  • ★ 빈칸 ① — 횟수를 확률로 바꾸려면 무엇으로 나누는가? 확률의 합이 1.00이 되는지로 검산하세요. 개념 1의 표와 같은 값이 나와야 합니다.
  • ★ 빈칸 ② — 다음 낱말을 어떻게 뽑는가? (두 곳) 힌트는 목록에 같은 낱말이 여러 번 들어 있다는 것입니다. 자주 나온 낱말은 목록에 그만큼 여러 번 있으니, 그냥 아무거나 하나 집으면 확률이 저절로 맞습니다.
  • ★ 빈칸 ③ — 앞 두 낱말을 보려면 열쇠를 무엇으로 삼는가? 바이그램과 트라이그램의 차이가 정확히 이 한 줄뿐입니다.
ℹ️ 출력이 아홉 토막입니다 — 오늘 반드시 볼 것은 【1】【3】【4】【5】【7】

【1】 다섯 문장 말뭉치 — 90%가 외운 문장이라는 것을 확인하는 자리.
【3】 확률표 — 판의 막대와 같은 값이 나오는지 대조하는 자리.
【4】 바이그램 20문장 — 무너진 자리를 코드가 기계적으로 짚어 준다.
【5】 트라이그램 20문장 — 씨앗은 그대로, 모델만 바꾼 결과.
【7】 조합 폭발 — 1.00% 와 0.00678%, 그리고 70% 대 95%.

【2】【6】【8】【9】는 시간이 남을 때 보세요. 특히 【8】은 과제 ①의 정답표입니다 — 표를 다 채운 뒤에 열어 보는 편이 좋습니다.

⚠️ 코드가 짚어 주는 것과 사람이 말해야 하는 것

코드는 "말뭉치에 없던 3연속이 여기 있다"까지만 압니다. 저녁에 해가 뜨면이 왜 이상한지는 코드가 모릅니다 — 저녁에는 해가 지지 뜨지 않는다는 것을 코드에 알려 준 적이 없으니까요. 그 까닭을 말하는 것은 여러분의 몫입니다. 이 둘을 섞지 마세요.

💻

손으로 ③ — 빈칸 ①을 틀리게 채우면 어떻게 되는가

빈칸 ①에는 틀렸는데 정상으로 보이는 함정이 하나 있습니다. 전체 횟수 대신 후보 가짓수로 나누는 실수예요. 화면에는 그럴듯한 소수가 찍히기 때문에 눈으로는 알아채기 어렵습니다.

아래 코드는 다섯 문장 말뭉치로 그 차이를 나란히 보여 줍니다. 그대로 실행해 보세요.

실행하면 이렇게 나옵니다.

위 코드의 실제 출력. 두 번째 토막의 합계를 보라.바르게 — 전체 횟수로 나눈다 0.50 길이 0.25 눈사람을 0.25 우산을 합계 = 1.00 OK 틀리게 — 후보 가짓수로 나눈다 0.67 길이 0.33 눈사람을 0.33 우산을 합계 = 1.33 <- 1이 아니다! 목록을 set 으로 정리한 뒤 세면 0.33 길이 0.33 눈사람을 0.33 우산을 합계 = 1.00 — 합은 1이지만 '길이'가 두 번 나왔다는 사실이 사라졌다.

가운데 토막이 그 함정입니다. 숫자만 보면 확률처럼 보입니다. 0.67, 0.33, 0.33 — 이상할 것이 없어 보이지요. 그런데 다 더하면 1.33입니다. 확률의 합은 반드시 1이어야 합니다.

세 번째 토막은 더 미묘합니다. 목록의 중복을 set으로 "정리"했더니 합은 1.00이 되었는데, 길이가 두 번 나왔다는 사실이 사라졌습니다. 셋 다 0.33으로 평평해졌어요.

💡 목록의 중복은 실수가 아니라 확률 그 자체다

nxt["오면"] = ['우산을', '길이', '길이', '눈사람을']에서 길이가 두 번 들어 있는 것은 정리하지 않은 흔적이 아닙니다. 바로 그 중복이 확률입니다. 그래서 random.choice로 아무거나 하나 집기만 해도 확률대로 뽑히는 거예요. 코드에 확률을 계산하는 줄이 따로 없는 까닭이 여기 있습니다.

📖

정리 — 오늘 얻은 것, 그리고 단원 전체를 잇는 실

오늘 우리는 낱말을 세어 나누기만 하는 모델로 문장을 만들었습니다. 그 문장은 때로 그럴듯했고 때로 무너졌으며, 무너지는 자리는 말뭉치에 없던 3연속으로 기계가 짚어 주었습니다.

  • 언어 모델의 계산은 나눗셈 한 번이다. 어떤 낱말 다음에 무엇이 몇 번 나왔는지 세고, 전체 횟수로 나눈다. 다섯 문장 말뭉치에서 오면 다음은 길이 0.50 · 눈사람을 0.25 · 우산을 0.25였다.
  • 앞을 한 낱말만 보면 앞을 잊는다. 바이그램 20문장 중 3연속이 어긋난 문장이 7개였다. 저녁에 해가 뜨면, 비가 오면 눈사람을이 그 결과다.
  • 문맥을 늘리면 어긋남은 줄지만 새로움도 준다. 트라이그램에서 어긋남은 7개 → 0개가 됐지만 외운 문장이 9개 → 14개로 늘었다. 5그램은 새 문장을 하나도 만들지 못했다.
  • 까닭은 조합 폭발이다. 어휘 107개에서 낱말 두 개 조합은 11,449칸인데 채워진 것은 1.00%, 세 개 조합은 0.00678%다. 트라이그램은 열쇠의 95%가 갈림길이 아니라 되뇔 수밖에 없다.
  • 그래서 어텐션이 나왔다. 열쇠를 통째로 세는 대신, 앞에 나온 낱말을 직접 보되 어디를 볼지 배우게 한 것이 트랜스포머다. "길게 보면서도 외우지 않는" 방법이다.
  • 소리는 그림이 된다. 스펙트로그램은 숫자 격자라서 16차시의 필터 · 특징지도 · 풀링을 그대로 쓴다. 도구는 하나인데 문제만 다르다.
  • 딥러닝이 늘 이기지는 않는다. 데이터가 적을 때 · 이유를 설명해야 할 때 · 규칙이 이미 분명할 때 · 전기와 돈이 문제일 때는 규칙이나 k-NN이 낫다. 역설이지만 오늘 만든 바이그램은 오늘날의 언어 모델보다 설명 가능성이 높다.

이것으로 Ⅱ단원이 끝납니다. 단원 전체를 한 줄로 꿰면 이렇습니다 — 1차시에서 우리는 규칙을 손으로 적다가 막혔습니다. 그래서 데이터를 모으고(2~4차시), 재는 법을 배우고(5~6차시), 스스로 규칙을 찾게 만들었습니다(7~10차시). 그것이 제대로 된 것인지 재는 법을 익히고(11~12차시), 층을 쌓아 어려운 문제까지 풀었으며(13~15차시), 마지막으로 그림과 말이라는 사람의 감각에 가까운 자료에까지 이르렀습니다(16~17차시).

그런데 오늘 마지막 절에서 남긴 물음이 하나 있습니다 — 언어 모델은 '참'이 아니라 '그럴듯함'을 고른다는 것. 이 성질이 사회에서 무엇을 일으키는지, 그리고 사람이 그것을 어떻게 다뤄야 하는지가 Ⅲ단원의 주제입니다.

✅

확인 문제

✍️ 문제마다 답을 쓰고 제출하기를 누르세요. 제출하면 모범 답안이 열리고, 제출한 답은 선생님께 전달됩니다.

1. 언어 모델이 실제로 하는 계산을 한 문장으로 쓰시오. 그리고 아래 세 문장만으로 말뭉치를 만들었을 때 오면 다음에 올 낱말의 확률을 직접 계산하시오.
비가 오면 우산을 쓴다 눈이 오면 길이 미끄럽다 눈이 오면 눈사람을 만든다
📖 모범 답안

계산 — "지금까지 나온 말 다음에 올 낱말이 무엇인지, 각 낱말에 확률을 매기는 것." 더 짧게는 다음 낱말의 확률 계산이다. 그 확률은 말뭉치에서 그 낱말이 다음에 나온 횟수 ÷ 전체 횟수로 구한다.

계산 결과 — 오면은 세 문장에 모두 나오므로 전체 3번이다. 그 뒤에 온 낱말은 우산을 · 길이 · 눈사람을 각 1번씩. 따라서 우산을 = 1/3 ≒ 0.33 · 길이 = 1/3 ≒ 0.33 · 눈사람을 = 1/3 ≒ 0.33이다. 합이 1.00인지 반드시 확인할 것.

본문 개념 1의 다섯 문장 말뭉치에서는 길이가 두 번 나와 길이 0.50 · 눈사람을 0.25 · 우산을 0.25였다. 문장 두 개(비가 오면 길이 미끄럽다 · 바람이 불면 우산이 뒤집힌다)가 빠지면서 길이의 횟수가 2에서 1로 줄어 확률이 평평해진 것이다. 말뭉치가 바뀌면 확률이 바뀐다는 것을 이 비교가 보여 준다.

2. 오늘 판에서 📊 20문장 만들어 재기를 마흔 문장 말뭉치 · 씨앗 17로 눌렀을 때 나온 숫자를 아래 표에 적으시오. 그리고 "트라이그램이 바이그램보다 좋아졌다"는 말이 왜 반쪽만 맞는지 설명하시오.
20개 중외운 문장새 문장3연속 어긋남
바이그램
트라이그램
📖 모범 답안

표 — 바이그램: 외운 9개 · 새 11개 · 3연속 어긋남 7개. 트라이그램: 외운 14개 · 새 6개 · 3연속 어긋남 0개. (서로 다른 문장은 둘 다 19가지, 평균 낱말 수는 4.35와 4.15다.)

왜 반쪽만 맞는가 — 좋아진 것은 분명하다. 앞뒤가 안 맞는 문장이 7개에서 0개로 사라졌으니까. 그런데 새 문장도 11개에서 6개로 줄었고 외운 문장이 9개에서 14개로 늘었다. 트라이그램은 어긋남을 줄인 것이 아니라 새로 만드는 일을 덜 한 것에 가깝다.

까닭 — 열쇠가 낱말 하나에서 낱말 둘의 짝으로 바뀌면서 같은 말뭉치로 채워야 할 칸이 폭발했다. 그 결과 트라이그램 열쇠 114개 중 108개(95%)가 다음 낱말 후보를 하나만 가진다(바이그램은 107개 중 75개, 70%). 고를 것이 없으면 말뭉치에 있던 문장을 그대로 되뇔 수밖에 없다.

3. 과제 ②에서 후보가 0개가 되는 두 낱말 짝을 하나 적고, 그 짝이 왜 0번인지 설명하시오. 또 [🎲 무작위 짝 100번 시험]의 결과를 적고 개념 3의 1.00%와 견주시오. 그리고 과제 ①의 표에서 말뭉치를 5문장 → 40문장으로 늘렸을 때 바이그램 종류가 몇 가지에서 몇 가지로 늘었는지 적으시오.
📖 모범 답안

0개가 되는 짝 — 예를 들어 저녁에 뜨면, 우산을 미끄럽다, 급식을 흔들린다처럼 둘 다 말뭉치에 있는 낱말인데 나란히 나온 적은 없는 짝이면 무엇이든 답이 된다. 까닭은 하나다 — 트라이그램은 낱말 두 개의 짝을 열쇠로 삼는데, 그 짝이 말뭉치에 한 번도 나오지 않았으므로 표에 칸 자체가 없다.

🎲 100번 시험 — 낱말 둘을 아무렇게나 뽑으면 그 짝이 말뭉치에 있을 확률은 114 ÷ 11,449 = 1.00%다. 그래서 100번을 뽑아도 보통 0번이나 1~2번밖에 걸리지 않는다. (씨앗을 바꾸면 값이 조금씩 달라진다. 여러 씨앗으로 눌러 1.00%가 무슨 뜻인지 몸으로 확인할 것.)

⚠️ 0번은 '확률 0'이 아니다. 우리 말뭉치 마흔 문장에 없었다는 뜻일 뿐이다. 재어 본 적이 없는 칸과 재어 봤더니 0이었던 칸을 구별해야 한다.

바이그램 종류 — 11가지 → 114가지. 문장 수는 8배가 됐는데(5 → 40) 조합의 가짓수는 10배 넘게 늘었다(11 → 114). 낱말 수는 20 → 165, 서로 다른 낱말은 13 → 107이었다.

여기서 읽을 것 — 데이터를 늘리면 없던 조합을 새로 본다. 그래서 20개 중 새 문장이 2개에서 11개로 늘었다. 다만 완전히 단조롭게 늘지는 않았다 — '서로 다른 문장'은 30문장에서 20가지였다가 40문장에서 19가지로 한 번 줄었다. 스무 개만 뽑아 세었기 때문에 생기는 흔들림이다. 표본이 작으면 숫자가 오르내린다는 것도 함께 배울 것.

4. 음성 인식이 16차시에서 배운 합성곱을 그대로 쓸 수 있는 까닭은 무엇인가? 중간에 무엇을 만들기 때문인지 이름을 대고, 그것이 왜 '그림'인지 설명하시오.
📖 모범 답안

중간에 스펙트로그램을 만들기 때문이다. 소리 파형을 수십 밀리초짜리 짧은 조각으로 자르고, 조각마다 어떤 높이의 소리가 얼마나 들어 있는지를 재어 늘어놓은 표다.

왜 그림인가 — 가로가 시간, 세로가 소리의 높이, 각 칸의 값이 세기다. 곧 가로 · 세로가 있는 숫자 격자다. 16차시에서 다룬 12×12 손글씨 격자와 형태가 같으므로, 3×3 필터를 밀며 곱해서 더하는 계산을 한 글자도 고치지 않고 쓸 수 있다. 손글씨에서 가로획 · 세로획을 찾던 필터가 여기서는 소리의 가로 줄무늬(음의 높이가 유지되는 구간)와 세로 기둥(소리가 갑자기 바뀌는 순간)을 찾는다.

덧붙일 것 — 다만 그 필터의 숫자는 사람이 정하지 않고 데이터에서 배운다. 16차시에서 우리가 손으로 아홉 숫자를 적었던 자리에, 진짜 시스템은 14차시의 역전파를 넣는다.

5. 딥러닝을 쓰지 않는 편이 나은 상황을 세 가지 들고, 각각 무엇을 대신 쓸지 이 단원에서 배운 방법 중에서 고르시오. 그리고 셋 중 하나를 골라 왜 그 방법이 더 나은지를 숫자를 들어 설명하시오.
📖 모범 답안

① 데이터가 적을 때 → 8차시의 k-NN. k-NN은 훈련 데이터를 저장만 하면 되고 조절할 손잡이가 k 하나뿐이다. 8차시에서 훈련 40개 · 시험 15개로 k=5일 때 93.3%(14/15)를 얻었다. 같은 규모에 신경망을 쓰면 손잡이가 훨씬 많아 11차시의 과적합에 빠지기 쉽다. 실제로 k=1(가장 유연한 설정)은 훈련에서 100.0%인데 시험에서 60.0%로 떨어졌다.

② 판단의 이유를 설명해야 할 때 → 1차시의 규칙 기반. 대출 거절 사유나 의료 판정처럼 근거를 대야 하는 자리에서는 "어느 규칙에 걸렸다"고 말할 수 있는 쪽이 낫다. 15차시의 신경망은 테스트 80.6%를 냈지만 남는 것은 가중치 숫자뿐이라 왜 그렇게 판단했는지 사람 말로 옮기기 어렵다.

③ 규칙이 이미 분명할 때 → 1차시의 규칙 기반. "학번이 여덟 자리인가"처럼 규칙이 확정된 문제에 학습을 쓸 이유가 없다. 게다가 1차시에서 규칙을 7개로 늘렸을 때 정확도가 77.3%에서 75.0%로 오히려 내려간 것을 보았다 — 무엇을 더한다고 늘 좋아지지는 않는다.

(④ 전기와 돈이 문제일 때도 답이 된다. 오늘 만든 바이그램은 0.04초 만에 끝난다.)

⚠️ 주의 — 위 성적들은 서로 다른 문제에서 잰 값이라 "93.3%가 80.6%보다 낫다"고 곧바로 견주면 안 된다. 비교해야 할 것은 점수가 아니라 어느 자리에 어느 방법이 맞는가이다.

6. 1차시의 규칙 기반 스팸 필터, 8차시의 k-NN, 15차시의 신경망, 그리고 오늘의 바이그램을 아래 네 칸으로 견주는 표를 채우시오. 빈칸을 채운 뒤, "성능을 평가한다"는 말이 정확도 하나를 재는 것과 어떻게 다른지 두 문장으로 쓰시오.
성적필요한 데이터설명 가능성계산의 무게
1차시 규칙
8차시 k-NN
15차시 신경망
17차시 바이그램
📖 모범 답안

성적 — 규칙 72.7%(규칙 4개 · 메일 44건) / k-NN 93.3%(k=5 · 시험 15개) / 신경망 테스트 80.6%(기준선 65.3%) / 바이그램 정확도로 잴 수 없다(정답이 하나가 아니다).

필요한 데이터 — 규칙은 거의 없음(사람이 쓴다) / k-NN 훈련 40개 / 신경망 훈련 168일 × 300회 / 바이그램 165낱말.

설명 가능성 — 규칙: 걸린 규칙 이름을 그대로 댈 수 있다 / k-NN: 가까운 이웃 다섯을 보여 주면 된다 / 신경망: 어렵다, 가중치 숫자만 남는다 / 바이그램: 확률표를 찍으면 전부 보인다.

계산의 무게 — 규칙: 거의 0 / k-NN: 예측할 때마다 40개와 거리를 잰다 / 신경망: 학습이 무겁고 예측은 가볍다 / 바이그램: 세는 것은 한 번, 0.04초.

'성능을 평가한다'는 무엇인가 — 정확도 하나만 재면 어느 쪽으로 틀리는지도, 얼마를 치르고 얻었는지도 알 수 없다. 12차시에서 네 칸으로 정밀도와 재현율을 갈라 본 것이 앞의 절반이고, 같은 문제를 다른 방법으로도 풀어 데이터 · 설명 가능성 · 계산의 무게까지 함께 견주는 것이 뒤의 절반이다.

🔁 되돌아보기 — 한 줄로

오늘 낱말을 세어 나누는 것만으로 문장이 만들어지는 것을 직접 확인하고, 그 문장이 무너지는 자리에서 어텐션이 무엇을 고쳤는지까지 따라왔습니다. 오늘 배운 것 중 가장 놀라웠던 것 하나와 아직 흐릿한 것 하나를 각각 한 줄로 적어 보세요. (예: "트라이그램이 어긋남을 없앤 대신 새 문장도 없앤 것이 놀라웠다" / "어텐션이 '어디를 볼지 배운다'는 말이 아직 어렴풋하다") Ⅱ단원은 여기서 끝납니다. 다음 단원에서는 이 기술이 사회에서 무엇을 일으키는지를 다룹니다.

🔎

더 알아보기

다음 낱말을 세는 생각은 어디서 왔고, 0번인 칸과 낱말 쪼개기는 어떻게 다뤄 왔나

양복 차림에 넥타이를 맨 클로드 섀넌의 흑백 얼굴 사진
역사

컴퓨터 없이 만든 바이그램 — 마르코프와 섀넌

'앞의 것 하나만 보고 다음 것을 정한다'는 생각은 오늘날의 인공지능보다 훨씬 오래되었습니다. 1913년 러시아 수학자 안드레이 마르코프는 푸시킨의 운문 소설 『예브게니 오네긴』의 처음 2만 글자를 손으로 세어, 모음 다음과 자음 다음에 모음이 올 확률이 서로 다르다는 것을 보였습니다. 바로 앞 하나에 따라 다음이 정해지는 이런 모형을 지금도 그의 이름을 따 마르코프 연쇄라고 부릅니다. 오늘 만든 바이그램이 바로 그것입니다.

1948년, 사진의 클로드 섀넌은 정보 이론을 연 논문 「통신의 수학적 이론」에서 영어를 흉내 낸 글을 단계별로 만들어 보였습니다. 글자를 아무렇게나 늘어놓는 단계에서 시작해, 글자의 빈도를 따르고, 앞 글자를 보고, 마지막에는 낱말 단위로 앞 낱말을 보는 단계까지 올라갔지요. 컴퓨터가 없던 때라 방법은 손이었습니다 — 책을 아무 쪽이나 펴서 지금 낱말을 찾고, 그 바로 뒤 낱말을 적고, 다시 다른 쪽을 펴서 그 낱말을 찾는 식이었어요. 책 한 권을 말뭉치로 삼아 random.choice 를 손으로 한 셈입니다.

결과도 오늘 우리가 본 것과 닮았습니다. 짧은 토막은 영어처럼 읽히는데 문장 전체에는 뜻이 없었어요. 섀넌이 알고 싶었던 것은 글짓기가 아니라 영어가 얼마나 예측 가능한가였습니다. 다음 낱말을 잘 맞힐 수 있을수록 글을 더 적은 신호로 보낼 수 있기 때문입니다. '다음 낱말 맞히기'는 처음부터 정보를 재는 방법이었던 셈입니다.

사진: 클로드 섀넌 · 출처: 작자 미상(스웨덴 Tekniska museet 소장), Wikimedia Commons (CC BY 2.0)

'오면' 다음 — 마흔 문장에서 7번 나왔다 ① 세기만 한다 (오늘의 모델) 길이 .29 우산을 .29 나머지 셋 .14씩 안 본 103가지 = 0 ② 모든 칸에 1을 더한다 (더하기 1 스무딩) 한 번도 안 본 103가지 · 89.6% 본 다섯 낱말 합계 10.4% 길이 0.29 → 0.026 · 안 본 낱말 하나 0 → 0.009 분모가 7 → 115로 커졌다 (7 + 다음 자리 후보 108)
원리 더 깊이

0번인 칸에 확률을 나눠 주기 — 스무딩

과제 ②에서 "0번은 확률 0이 아니다"라고 했습니다. 그러면 0번인 칸에 얼마를 줘야 할까요? 가장 쉬운 답은 모든 칸의 횟수에 1씩 더하고 나누는 것입니다. 이것을 더하기 1 스무딩(라플라스 스무딩)이라고 부릅니다. 이렇게 하면 말뭉치에 없던 짝도 아주 작은 확률을 얻어서, 문장이 '후보 0개'에서 멈추는 일이 사라집니다.

그런데 우리 말뭉치에 그대로 해 보면 문제가 드러납니다. '오면' 다음에 올 수 있는 자리는 낱말 107개와 <끝>까지 108가지인데, 실제로 본 것은 7번뿐이에요. 1씩 더하면 분모가 7에서 115로 커지고, 한 번도 안 본 103가지가 확률의 89.6%를 가져갑니다. 두 번이나 본 '길이'는 0.29에서 0.026으로 줄어 한 번도 안 본 낱말(0.009)과 별 차이가 없어집니다. 본 것보다 안 본 것을 더 믿는 모델이 된 것이지요.

그래서 실제로는 1보다 훨씬 작은 수를 더하거나, 두 낱말 짝이 0번이면 한 낱말만 보는 확률로 물러서는 방법 등이 쓰였습니다. 신경망 언어 모델은 이 문제를 다른 길로 풉니다 — 낱말을 임베딩으로 바꾸면 본 적 없는 짝도 닮은 낱말의 경험을 빌려 확률을 매길 수 있기 때문입니다(개념 4).

같은 말뭉치의 두 낱말을 어떻게 자르는가 ① 띄어쓰기로 자른다 (오늘의 모델) 우산을 우산이 서로 다른 이름표 둘 공통점이 있다는 것을 모델이 알 길이 없다 ② 더 작은 조각(토큰)으로 자른다 우산 을 우산 이 두 낱말이 '우산' 조각을 나눠 갖는다 어디서 자를지는 사람이 아니라 글에서 자주 붙어 나오는 조각을 세어 정한다
오해 바로잡기

언어 모델은 '낱말'을 세지 않는다 — 토큰

오늘 우리는 문장을 띄어쓰기로 잘랐습니다. 그랬더니 우리 말뭉치의 '우산을'과 '우산이'는 서로 아무 관계 없는 두 낱말이 되었어요. 한국어는 낱말 뒤에 조사와 어미가 붙어 모양이 계속 바뀌기 때문에, 띄어쓰기로 자르면 같은 뜻의 낱말이 수십 가지 이름표로 흩어집니다. 어휘가 불어나면 개념 3의 조합 폭발도 그만큼 빨라집니다.

그래서 오늘날의 언어 모델은 글을 낱말보다 작은 조각인 토큰으로 자릅니다. 널리 쓰이는 방법 하나가 바이트 쌍 인코딩(BPE)인데, 말뭉치에서 가장 자주 붙어 나오는 두 조각을 하나로 합치는 일을 정해진 횟수만큼 되풀이해 조각 목록을 만듭니다. 본래 1994년에 데이터 압축 방법으로 나왔고, 2016년 무렵 기계 번역에 쓰이기 시작했습니다. 지금은 여러 언어 모델이 이 방법이나 그 변형으로 글을 자릅니다.

다만 이렇게 만든 조각이 국어 문법의 형태소와 꼭 맞지는 않습니다. 그림처럼 '우산 + 을'로 깔끔하게 잘릴 때도 있지만, 글에서 센 빈도가 정하는 것이라 엉뚱한 자리에서 잘리기도 하지요. 그리고 토큰으로 잘라도 '눈'이 하늘의 눈인지 얼굴의 눈인지는 풀리지 않습니다. 그것은 개념 4·5의 임베딩과 어텐션이 앞뒤 문맥을 보고 해결하는 몫입니다.