강의자료 · 인공지능응용

이전 문자를 기억하며 다음 문자 예측하기

인공지능응용 · 12주차

이번 주 목표 · 문자를 Index로 바꾼 뒤, 순서대로 RNN에 넣으며 Hidden State(은닉 상태)와 예측이 어떻게 바뀌는지 확인합니다.

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

문자 하나의 분류에서 앞 문맥을 사용하는 예측으로

개념 설명용 예시 · 아래의 짧은 문자열은 이전 문맥이 필요한 이유를 보여줍니다. Notebook의 실제 학습 문장과 구분해서 읽으세요.

8주차에는 여러 범주의 점수를 계산하고 정답 범주와 비교했습니다. 다음 문자 예측도 후보 문자 중 하나를 고르는 분류 문제입니다. 다만 현재 문자 하나만으로는 부족할 수 있습니다.

hello에서 두 번 나오는 l을 보세요. 첫 번째 l 뒤의 정답은 l이고, 두 번째 l 뒤의 정답은 o입니다. 현재 문자만 받는 모델에는 같은 Input에 서로 다른 정답을 요구하게 됩니다.

RNN은 앞에서 읽은 내용을 Hidden State라는 숫자 벡터로 전달합니다. 따라서 같은 l을 읽어도 앞 문맥이 다르면 다른 예측을 만들 수 있습니다. 이번에 추가하는 것은 분류 결과의 종류가 아니라, 예측에 사용할 이전 정보입니다.

실습에서 Input과 Target을 한 글자씩 대응시키고, 각 시점의 예측을 확인하는 이유도 여기에 있습니다. Hidden State는 앞 문맥을 모두 정확하게 저장한다는 보장이 있는 메모리가 아니라, 예측에 유용하도록 학습되는 요약입니다.

1. 앞에서 읽은 내용을 다음 문자 예측에 쓰기

10주차에는 이미지 한 장마다 숫자 Label 하나를 예측했습니다. 이번에는 문자를 순서대로 읽으며 각 위치에서 다음 문자를 예측합니다. RNN의 Hidden State는 지금까지 읽은 정보를 숫자로 요약해 다음 시점으로 넘기는 벡터입니다.

예를 들어 hello를 학습할 때는 다음과 같이 짝을 만듭니다.

시점 이번에 읽는 문자 맞혀야 할 다음 문자
0 h e
1 e l
2 l l
3 l o

Input은 hell, Target은 ello입니다. 같은 l이라도 앞에서 읽은 내용이 달라 다음 정답이 달라질 수 있습니다. RNN은 현재 문자와 직전 Hidden State로 새 Hidden State를 계산합니다.

한 번에 문장을 넣어도 순서대로 계산하나요?

코드에서는 model(X) 한 줄로 실행하지만 RNN 내부에서는 Sequence 순서대로 같은 Cell을 반복 적용합니다. 여기서는 단방향 RNN을 사용하므로 현재 위치 뒤의 문자를 미리 읽지 않습니다. Hidden State는 모든 글자를 그대로 보관하는 저장소가 아니라 학습된 숫자 요약입니다.

2. 문자 → Index → One-hot

chars는 중복을 제거하고 정렬한 문자 목록, vocab은 문자와 Index의 대응표입니다. Index는 0부터 시작하며 크기에 의미의 순서가 있는 것은 아닙니다. One-hot은 그 문자의 위치만 1이고 나머지는 0인 벡터입니다.

가령 사전이 ['a','b','c']이면 b의 Index는 1, One-hot은 [0,1,0]입니다. 문자 종류가 E개이면 One-hot의 길이도 E입니다. 이번 주는 nn.Embedding 대신 고정된 One-hot을 사용합니다.

문자 번호는 사전마다 달라질 수 있어요

문자 번호는 사전의 정렬 방식에 따라 달라질 수 있습니다. 번호를 직접 적어 넣기보다 vocab으로 Input과 Target을 만들고, 같은 chars로 예측을 문자로 되돌리세요. 사전 크기가 C이면 유효한 정답 Index는 0부터 C−1까지입니다.

3. 여러 문장을 하나의 Batch로 만들기

이번 실습의 howareyou, whats up?, iamgreat.는 각각 9글자입니다. 공백과 물음표·마침표도 문자로 셉니다. 한 글자 이동한 Input–Target 쌍을 만들므로 S=8입니다. 현재 코드에서는 세 문장 중 가장 짧은 길이에 맞춥니다. 나중에 길이를 바꾸면 잘리는 부분이 있는지 확인하세요.

값 shape 의미
ids (3,8) 문장 3개 × 문자 Index 8개
X (3,8,E) 각 Index를 One-hot으로 바꾼 입력
Y (3,8) 각 위치에서 맞힐 다음 문자 Index

batch_first=True는 Batch 축을 맨 앞에 둔다는 뜻입니다. 실습의 출력으로 실제 E를 확인하세요. X는 실수형, Y는 torch.long입니다.

4. RNN의 Hidden State를 문자 점수로 바꾸기

이번 모델은 RNN → Linear입니다. RNN의 출력 (N,S,H)를 Linear가 문자별 Logit (N,S,C)으로 바꿉니다. H는 Hidden Size, C는 문자 종류 수입니다. 이 실습은 H=32를 사용합니다.

강의 영상의 예제는 Hidden Size를 문자 종류 수와 같게 두어, 각 시점의 출력을 바로 문자별 점수로 읽습니다. 다음 문자 예측을 Classification 문제로 바꾸는 과정을 간단히 보여주는 구성입니다.

실습에서는 Hidden Size H=32로 내부 표현을 만들고 Linear가 이를 C개의 문자 점수로 바꿉니다. 이렇게 나누면 문자 사전의 크기와 관계없이 기억에 사용할 표현 크기를 정할 수 있습니다. 문자별 점수를 바로 만드는 구성에서, 내부 표현과 분류를 분리하는 구성으로 확장한 것입니다.

Loss 계산에서 왜 펼치나요?

각 문장의 각 위치를 하나의 분류 문제로 셉니다. Logit (N,S,C)를 (N×S,C)로, Target (N,S)를 (N×S,)로 함께 펼쳐 CrossEntropyLoss에 넣습니다. 두 Tensor를 같은 순서로 펼쳐야 예측과 정답이 대응합니다. 이는 8주차 분류를 모든 시점에 적용하는 것입니다.

output은 모든 시점의 Hidden State이고 h_n은 마지막 시점의 State입니다. 이번에는 다음 문자를 매 위치에서 맞히므로 모든 시점의 output을 사용합니다.

5. 학습 전후 예측을 읽기

Notebook에 표시되는 학습 전 문자열과 학습 후 문자열을 Target과 나란히 비교하세요. Loss가 줄었는지, 어느 위치가 달라졌는지 기록합니다. 사전에 있는 문자로 Input 일부를 바꿀 때는 모델을 다시 학습하지 않고 같은 모델의 반응을 봅니다.

문장을 생성한 결과와 같을까요?

이번 코드는 각 시점에 실제 Input 문자를 주고 다음 문자를 예측합니다. 예측한 문자를 다시 Input으로 넣어 긴 문장을 이어 쓰는 자동 생성 실험과는 다릅니다. 학습 문자열을 잘 맞혔다고 새로운 문장도 잘 생성한다고 결론 내리지 마세요.

앞부분만 넣어보려면 model(X[:, :4, :])처럼 Sequence 축을 자를 수 있습니다. 모델의 마지막 축 크기는 유지해야 합니다.

학습 내용 확인

  1. 문자 Index와 One-hot의 차이를 설명할 수 있나요?
  2. RNN 출력에 Linear가 필요한 이유와 최종 Logit의 shape를 설명할 수 있나요?
  3. 다음 문자 예측을 잘한 결과와 새 문장 생성 능력을 구별할 수 있나요?

PyTorch RNN

실습 자료