긴 글을 나누어 다음 문자 예측하기

인공지능응용 · 13주차

이번 주 목표 · 긴 글에서 짧은 학습 예제를 만드는 방법을 이해하고, 같은 데이터로 RNN·LSTM·GRU의 예측을 비교합니다.

실행: Colab에서 내 Drive에 사본을 저장한 뒤 Code Cell의 ▶ 또는 Shift+Enter로 위에서부터 실행하세요. ⟦직접 입력⟧ 표시가 있는 Text Cell은 더블클릭해서 수정합니다.

1. 같은 예측 문제에서 기억하는 구조를 바꾸기

hello!를 h → e → l → l → o → !처럼 순서대로 놓아 보세요. 이렇게 순서가 있는 데이터의 나열을 Sequence라고 합니다. 이번에는 문자 하나가 한 시점의 입력입니다. 공백과 문장부호도 문자 하나로 셉니다.

12주차와 마찬가지로, 앞에서 읽은 문자를 바탕으로 각 위치의 다음 문자를 맞힙니다. Input과 Target을 만드는 규칙, Linear 출력, Loss 계산은 유지하고 RNN Cell을 LSTM·GRU로 바꾸어 비교합니다.

RNN은 이전 Hidden State를 매 시점 새 값으로 갱신합니다. Sequence가 길어지면 앞쪽 정보와 학습 신호가 여러 변환을 거치며 약해질 수 있습니다. LSTM과 GRU는 Gate를 이용해 이전 정보와 새 정보가 반영되는 정도를 학습합니다.

2. 긴 글에서 짧은 학습 예제 만들기

개념 설명용 예시 · 아래에서는 짧은 문자열 hello!로 Window와 Input–Target 관계를 설명합니다. 실습에서는 Notebook에 제공된 더 긴 원문에 같은 규칙을 적용합니다.

긴 글도 문자들이 이어진 Sequence입니다. 한 번에 다루기 쉽게 일정한 길이만큼 잘라 여러 학습 예제를 만들겠습니다. 원문에서 잘라 보는 연속된 구간을 Window라고 부릅니다. Window는 데이터의 한 구간이며, RNN과 같은 모델의 이름은 아닙니다.

먼저 세 글자만 읽어 보기

hello!에서 처음 세 글자 hel을 Input으로 고릅니다. 모델은 각 위치에서 바로 다음 문자를 예측하므로 Target은 ell입니다.

원문 위치   0   1   2   3   4   5
원문 문자   h   e   l   l   o   !
            └── hel ──┘              Input: 위치 0~2
                └── ell ──┘          Target: 위치 1~3

읽은 문자   h → e → l
맞힐 문자   e   l   l

첫 출력은 h 다음의 e, 두 번째 출력은 he 다음의 l, 세 번째 출력은 hel 다음의 l을 맞힙니다. 세 글자를 읽은 뒤 정답 하나만 내는 것이 아니라, 각 위치에 정답이 하나씩 있습니다. Input과 Target은 모두 길이 3입니다.

시작점을 옮겨 다음 예제 만들기

이번에는 시작점을 두 칸 옮겨 원문 위치 2부터 읽습니다. Input은 llo, Target은 lo!가 됩니다.

학습 예제 시작 위치 Input: 세 글자 읽기 Target: 각 위치의 다음 문자
첫 번째 0 hel ell
두 번째 2 llo lo!

이처럼 구간의 시작점을 옮겨 가며 예제를 만드는 방식을 Sliding Window라고 합니다. 여기서 Window Size=3은 한 예제의 입력 길이이고, Stride=2는 다음 예제의 시작점을 옮기는 간격입니다. 두 구간은 원문의 위치 2를 함께 포함하므로 서로 겹칩니다.

두 가지 이동을 구별하세요. Target은 다음 문자 예측 문제이므로 Input보다 항상 한 글자 뒤에서 시작합니다. Stride는 다음 학습 예제로 넘어갈 때 이동하는 간격입니다. Stride를 2로 정해도 두 글자 뒤를 예측하는 문제가 되지는 않습니다.

Stride를 1로 바꾸면?

같은 hello!와 길이 3을 사용해 시작 위치 0·1·2에서 hel→ell, ell→llo, llo→lo!의 세 쌍을 만듭니다. Stride가 작아지면 이 예에서는 겹치는 부분과 학습 예제 수가 늘어납니다.

위치 3에서 Input을 lo!로 자를 수는 있지만, 마지막 ! 다음에 정답으로 사용할 문자가 없습니다. 따라서 이 실습에서는 그 구간을 학습 예제로 만들지 않습니다. 길이 S의 Input과 Target 한 쌍에는 연속된 문자 S+1개가 필요합니다.

여러 예제를 묶으면 Batch가 된다

위 두 예제를 행으로 쌓으면 다음과 같습니다. 한 행을 왼쪽에서 오른쪽으로 읽는 순서와 서로 다른 예제를 위아래로 쌓는 방향을 구별해 보세요.

           Input              Target
예제 1     h  e  l            e  l  l
예제 2     l  l  o            l  o  !
           ──────→ 시간 순서

예제 수는 N=2, 각 예제의 길이는 S=3입니다. 문자마다 길이 E의 One-hot 벡터로 바꾸면 Input은 (2,3,E)가 됩니다. Target은 문자 번호 하나씩만 저장하므로 (2,3)입니다. 이번 실습에서는 만든 예제를 모두 한 Batch로 묶어 학습합니다.

실습에서는 길이 20, 이동 간격 5 사용하기

Notebook도 같은 규칙으로 더 긴 영어 글을 자릅니다. window_size=20은 입력 문자 20개, window_stride=5는 시작 위치 0·5·10·…을 뜻합니다. 각 Target은 해당 Input보다 한 글자 뒤에서 시작합니다. 실제 예제 수 N과 문자 종류 수 E는 출력으로 확인하세요.

값 shape 각 축의 뜻
One-hot Input X (N,20,E) 예제 수 · 읽을 문자 수 · 문자 표현 크기
다음 문자 Target Y (N,20) 예제 수 · 위치별 정답 문자 번호
각 모델의 최종 Logit (N,20,C) 예제 수 · 각 위치 · 문자별 점수
구간이 겹치면 모델의 기억도 이어지나요?

원문에서 구간이 겹치는 것과 모델의 State를 이어 주는 것은 별개입니다. 한 예제 안에서는 왼쪽 문자를 읽으며 만든 State가 다음 문자로 전달됩니다. 하지만 이 실습은 앞 예제의 마지막 State를 다음 예제에 넘기지 않고, 각 예제를 별도로 처리합니다.

겹친 예제들을 무작위로 나누어 평가하면 학습 때 본 문자 구간이 평가 데이터에도 들어갈 수 있습니다. 따라서 이를 독립적인 새 글에 대한 성능으로 해석하면 안 됩니다. 지금 비교하는 값은 학습에 사용한 예제에서의 Training Accuracy입니다.

3. LSTM과 GRU의 Gate 읽기

Gate는 각 성분에 0~1 사이의 값을 곱해 얼마나 반영할지 정합니다. 사람이 문장마다 if문으로 결정하는 것이 아니라, 학습되는 Parameter가 Gate 값을 계산합니다.

구조 유지하는 State Gate의 역할
RNN Hidden State 이전 State와 현재 Input으로 새 State 계산
LSTM Hidden State와 Cell State Forget: 이전 Cell State를 남길 정도, Input: 새 정보를 넣을 정도, Output: Hidden State로 드러낼 정도
GRU Hidden State Reset: 후보 State 계산에 과거 정보를 반영할 정도, Update: 이전 State와 후보를 섞는 정도
Gate 값 하나로 생각해보기

이전 Cell State의 한 성분이 2이고 Forget Gate가 0.9이면, 새 정보를 더하기 전에 1.8이 남습니다. Gate가 0.1이면 0.2가 남습니다. LSTM은 여기에 Input Gate로 조절한 새 정보를 더합니다. 이 설명은 숫자 흐름을 보기 위한 예이며, 실제 Gate는 벡터입니다.

LSTM의 Cell State와 Hidden State는 같은 값이 아닙니다. GRU는 별도 Cell State 없이 Hidden State 안에서 정보를 조절합니다. 세 모델의 성능 순위가 항상 같다고 가정하지 마세요.

4. 같은 조건으로 세 모델 비교하기

fit_char('RNN', ...), fit_char('LSTM', ...), fit_char('GRU', ...)는 같은 데이터를 받아 새 모델을 학습합니다. Hidden Size=32, 300 epoch, Learning Rate=0.01, Adam을 기준으로 비교합니다. 먼저 한 번씩 실행해 Loss·Training Accuracy·첫 Window의 예측 문자열을 나란히 기록하세요.

새 모델을 같은 Seed로 초기화하더라도 구조와 Parameter 수는 다릅니다. 따라서 이 실험은 학습 조건을 맞춘 구조 비교이며, 모든 상황에서 더 좋은 모델을 결정하는 실험은 아닙니다. 같은 원문을 잘 맞히는 것만으로 긴 문맥을 이해했다고 판단하지 마세요.

전체 학습을 다시 하지 않고 확인할 수 있는 것

학습된 모델을 results에 저장합니다. 각 모델의 output, 최종 State, 예측 문자열을 출력하는 데는 재학습이 필요 없습니다. 반복 기록표는 앞서 비교한 세 모델의 결과로 채우면 됩니다. 같은 Window 크기를 바꿔 비교하되 세 모델에 동일한 데이터를 사용하세요.

5. 반환값을 확인하는 순서

먼저 2번 실습 Section에서 모델을 만든 뒤 반환값을 확인합니다. 단방향 1개 Layer일 때 다음과 같습니다.

구조 모든 시점의 output 마지막 State
RNN / GRU (N,S,32) h_n: (1,N,32)
LSTM (N,S,32) h_n, c_n: 각각 (1,N,32)

batch_first=True는 Input과 output을 (N,S,...)로 배치합니다. 마지막 State의 첫 축은 Layer·방향 축이므로 (1,N,32)로 남습니다. 문자 점수는 이 output에 Linear를 적용한 뒤 얻습니다.

학습 내용 확인

  1. 다음 문자 Target을 만드는 규칙과 RNN·LSTM·GRU의 State 차이를 설명할 수 있나요?
  2. Window 길이와 Window 이동 간격을 구별할 수 있나요?
  3. 한 실험의 Accuracy로 모델의 우열을 일반화하지 않고 관찰한 결과를 설명할 수 있나요?

PyTorch LSTM · PyTorch GRU

실습 자료