딥러닝 모델은 글자를 그대로 읽지 못합니다. 우리가 보기에는 이 영화 정말 재미있다가 자연스러운 문장이지만, 모델 입장에서는 아직 아무 의미도 없는 기호의 나열입니다. 모델이 계산할 수 있는 것은 숫자뿐입니다.
이미지 분류에서는 픽셀 값이 자연스럽게 숫자였습니다. 손글씨 숫자 이미지는 28×28개의 밝기 값으로 바꿀 수 있었고, CNN은 그 숫자 격자에서 선과 모서리와 곡선을 찾았습니다. 그런데 문장은 조금 다릅니다. 문장은 먼저 쪼개야 하고, 쪼갠 조각마다 번호를 붙여야 하며, 길이가 서로 다른 문장들을 한 배치에 담기 위해 모양도 맞춰야 합니다.
이번 장에서는 간단한 감성 분류기를 만들며 이 과정을 처음부터 끝까지 따라가겠습니다. 목표는 거창한 자연어 처리 모델을 만드는 것이 아닙니다. 핵심은 문장이 신경망에 들어가기 전에 어떤 모습으로 바뀌는지 눈으로 확인하는 것입니다.

문장은 숫자 시퀀스가 되어야 한다
텍스트 분류는 문장을 입력받아 하나의 범주를 예측하는 문제입니다. 예를 들어 영화 리뷰를 보고 긍정인지 부정인지 판단할 수 있습니다.
이 영화 정말 재미있다라는 문장이 있다고 해 봅시다. 이 문장을 모델에 넣으려면 대략 다음 흐름을 거칩니다.
문장을 토큰으로 나눈다.
각 토큰을 정수 인덱스로 바꾼다.
길이가 다른 문장들을 패딩으로 같은 길이에 맞춘다.
정수 인덱스를 임베딩 벡터로 바꾼다.
RNN 같은 시퀀스 인코더로 문장 전체를 요약한다.
분류층에서 긍정 또는 부정을 예측한다.
여기서 토큰은 문장을 이루는 작은 조각입니다. 영어에서는 단어 단위로 나누기 쉽지만, 한국어는 조사가 붙고 어미가 변하기 때문에 단순히 공백으로만 나누면 한계가 있습니다. 예를 들어 재미있다, 재미있었다, 재미있네요는 비슷한 의미를 갖지만 공백 기준으로는 모두 다른 토큰이 됩니다.
그래도 처음 배우는 단계에서는 공백 기준 토큰화로 충분합니다. 지금 중요한 것은 완벽한 한국어 처리기가 아니라 텍스트가 숫자로 변하는 큰 흐름입니다.
작은 감성 분류 데이터 만들기
먼저 아주 작은 영화 리뷰 데이터를 만들겠습니다. 레이블은 긍정을 1, 부정을 0으로 두겠습니다.
raw_data = [
('이 영화 정말 재미있다', 1),
('배우 연기가 훌륭하다', 1),
('스토리가 감동적이다', 1),
('다시 보고 싶은 영화다', 1),
('음악과 영상이 아름답다', 1),
('전개가 지루하다', 0),
('시간이 아깝다', 0),
('내용이 너무 별로다', 0),
('배우 연기가 어색하다', 0),
('다시는 보고 싶지 않다', 0),
]데이터가 작기 때문에 실제 서비스에 쓸 만한 모델을 만들 수는 없습니다. 하지만 학습 흐름을 이해하기에는 오히려 좋습니다. 데이터가 작으면 모델이 무엇을 보고 판단하는지 추적하기 쉽습니다.
토큰화 함수는 간단히 작성하겠습니다.
def tokenize(sentence):
return sentence.lower().split()
print(tokenize('이 영화 정말 재미있다'))출력은 다음처럼 나옵니다.
['이', '영화', '정말', '재미있다']이제 문장이 토큰의 리스트가 되었습니다. 하지만 아직 모델에 넣을 수는 없습니다. 토큰은 여전히 문자열입니다. 다음 단계에서는 각 토큰에 번호를 붙입니다.
단어장을 만들고 정수로 바꾸기
모델은 재미있다라는 문자열을 직접 계산하지 못합니다. 대신 재미있다를 7번 토큰, 지루하다를 13번 토큰처럼 정수로 바꾸어야 합니다. 이렇게 토큰과 정수 인덱스의 대응표를 단어장, 즉 vocabulary라고 부릅니다.
단어장을 만들 때 특별한 토큰 두 개를 먼저 넣겠습니다.
<pad>는 문장 길이를 맞추기 위해 뒤에 덧붙이는 패딩 토큰입니다. <unk>는 학습 때 보지 못한 단어가 들어왔을 때 사용하는 unknown 토큰입니다.
from collections import Counter
counter = Counter()
for sentence, label in raw_data:
counter.update(tokenize(sentence))
vocab = {
'<pad>': 0,
'<unk>': 1,
}
for token, count in counter.most_common():
if token not in vocab:
vocab[token] = len(vocab)
pad_idx = vocab['<pad>']
unk_idx = vocab['<unk>']
print(vocab)실행하면 토큰마다 정수 번호가 붙습니다. 번호 자체에는 특별한 의미가 없습니다. 재미있다가 5번이든 17번이든 중요하지 않습니다. 중요한 것은 같은 토큰이 항상 같은 번호로 바뀐다는 점입니다.
문장을 정수 시퀀스로 바꾸는 함수는 다음처럼 만들 수 있습니다.
def encode(sentence):
tokens = tokenize(sentence)
return [vocab.get(token, unk_idx) for token in tokens]
print(encode('이 영화 정말 재미있다'))
print(encode('이 영화 최고다'))두 번째 문장에 있는 최고다가 학습 데이터에 없었다면 <unk>의 번호인 1로 바뀝니다. 이 처리는 중요합니다. 실제 입력 문장에는 모델이 처음 보는 단어가 언제든 등장할 수 있기 때문입니다.
지금까지의 흐름을 그림처럼 보면 다음과 같습니다.
이 영화 정말 재미있다
→ ['이', '영화', '정말', '재미있다']
→ [2, 3, 4, 5]정확한 숫자는 단어장을 만든 순서에 따라 달라질 수 있습니다. 하지만 문장이 정수의 나열, 즉 숫자 시퀀스가 된다는 사실은 변하지 않습니다.
길이가 다른 문장을 한 배치에 담기
이미지 배치에서는 보통 모든 이미지의 크기가 같습니다. MNIST는 28×28이고, CIFAR-10은 32×32입니다. 그래서 여러 이미지를 한 텐서에 차곡차곡 쌓을 수 있습니다.
문장은 그렇지 않습니다.
이 영화 정말 재미있다 → 토큰 4개
시간이 아깝다 → 토큰 2개
다시는 보고 싶지 않다 → 토큰 4개
음악과 영상이 아름답다 → 토큰 3개배치 텐서는 직사각형 모양이어야 합니다. 그런데 문장 길이가 제각각이면 그대로는 하나의 텐서로 묶을 수 없습니다. 그래서 짧은 문장 뒤에 <pad>를 붙여 길이를 맞춥니다.
예를 들어 가장 긴 문장이 토큰 4개라면, 토큰 2개짜리 문장은 뒤에 패딩을 2개 붙입니다.
시간이 아깝다 <pad> <pad>정수 인덱스로는 이렇게 됩니다.
[12, 13, 0, 0]여기서 0은 <pad>의 번호입니다.
하지만 패딩을 붙이면 새로운 문제가 생깁니다. 모델이 <pad>를 실제 단어처럼 읽으면 안 됩니다. 시간이 아깝다 <pad> <pad>에서 중요한 것은 앞의 두 토큰이지 뒤의 0 두 개가 아닙니다. 그래서 마스크가 필요합니다.
마스크는 각 위치가 진짜 토큰인지, 아니면 패딩인지 알려 주는 표시입니다.
토큰 인덱스: [12, 13, 0, 0]
마스크: [ 1, 1, 0, 0]1은 실제 토큰, 0은 패딩입니다. 마스크는 뒤에서 Transformer를 배울 때도 매우 중요하게 등장합니다. Attention이 모든 위치를 바라볼 수 있기 때문에, 패딩 위치를 보지 못하도록 가려야 하기 때문입니다.
Dataset과 collate_fn 만들기
이제 PyTorch의 Dataset과 DataLoader를 이용해 텍스트 배치를 만들어 보겠습니다. 텍스트 데이터에서는 배치마다 가장 긴 문장의 길이가 달라질 수 있으므로, collate_fn에서 패딩을 직접 처리하는 방식이 자주 쓰입니다.
import torch
from torch.utils.data import Dataset, DataLoader
class TextDataset(Dataset):
def __init__(self, data):
self.data = data
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
sentence, label = self.data[idx]
ids = encode(sentence)
return ids, label
def collate_fn(batch):
sequences = [item[0] for item in batch]
labels = torch.tensor([item[1] for item in batch], dtype=torch.long)
lengths = torch.tensor([len(seq) for seq in sequences], dtype=torch.long)
max_len = max(lengths).item()
x = torch.full(
(len(sequences), max_len),
fill_value=pad_idx,
dtype=torch.long,
)
for i, seq in enumerate(sequences):
x[i, :len(seq)] = torch.tensor(seq, dtype=torch.long)
mask = x != pad_idx
return x, labels, lengths, mask
train_dataset = TextDataset(raw_data)
train_loader = DataLoader(
train_dataset,
batch_size=4,
shuffle=True,
collate_fn=collate_fn,
)배치 하나를 꺼내 모양을 확인해 봅시다.
x, y, lengths, mask = next(iter(train_loader))
print('x shape:', x.shape)
print('x:', x)
print('lengths:', lengths)
print('mask:', mask)
print('y:', y)여기서 x는 정수 인덱스로 된 문장 배치입니다. 모양은 [배치 크기, 문장 길이]입니다. 예를 들어 배치 크기가 4이고, 그 배치에서 가장 긴 문장이 토큰 5개라면 x.shape는 [4, 5]가 됩니다.
lengths는 각 문장의 실제 길이를 담고 있습니다. mask는 어느 위치가 실제 토큰인지 알려 줍니다. 이 세 가지가 텍스트 배치의 기본 재료입니다.
텍스트 분류 모델의 세 부품
텍스트 분류 모델은 보통 세 부분으로 나눌 수 있습니다.
첫 번째는 임베딩 레이어입니다. 정수 인덱스를 조밀한 벡터로 바꿉니다. 예를 들어 재미있다라는 토큰 번호가 5라면, 임베딩 레이어는 5번 행에 저장된 벡터를 꺼냅니다. 이 벡터는 학습 과정에서 조금씩 조정됩니다.
두 번째는 시퀀스 인코더입니다. 문장은 단어의 나열이므로 순서를 고려해야 합니다. 이전 장에서 배운 RNN, GRU, LSTM이 여기에 들어갈 수 있습니다. 이번 장에서는 GRU를 사용하겠습니다. GRU는 RNN의 한 종류로, 순서 정보를 더 안정적으로 다루기 위해 게이트 구조를 사용하는 모델입니다.
세 번째는 분류층입니다. 시퀀스 인코더가 문장 전체를 하나의 벡터로 요약하면, 분류층은 그 벡터를 보고 긍정과 부정 점수를 출력합니다.
전체 흐름은 다음과 같습니다.
정수 시퀀스
→ 임베딩 벡터 시퀀스
→ GRU로 문장 요약
→ Linear 분류층
→ 긍정/부정 점수이제 모델을 코드로 작성해 보겠습니다.
from torch import nn
class TextClassifier(nn.Module):
def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes, pad_idx):
super().__init__()
self.embedding = nn.Embedding(
num_embeddings=vocab_size,
embedding_dim=embed_dim,
padding_idx=pad_idx,
)
self.encoder = nn.GRU(
input_size=embed_dim,
hidden_size=hidden_dim,
batch_first=True,
)
self.classifier = nn.Linear(hidden_dim, num_classes)
def forward(self, x, lengths):
emb = self.embedding(x)
packed = nn.utils.rnn.pack_padded_sequence(
emb,
lengths.cpu(),
batch_first=True,
enforce_sorted=False,
)
output, h_n = self.encoder(packed)
sentence_vec = h_n[-1]
logits = self.classifier(sentence_vec)
return logitsnn.Embedding의 입력은 [배치 크기, 문장 길이] 모양의 정수 텐서입니다. 출력은 [배치 크기, 문장 길이, 임베딩 차원]이 됩니다. 각 토큰 번호가 벡터 하나로 바뀌기 때문입니다.
padding_idx=pad_idx를 지정하면 <pad>에 해당하는 임베딩 벡터는 학습되지 않는 패딩 전용 벡터로 취급됩니다. 여기에 더해 pack_padded_sequence를 사용하면 GRU가 패딩 위치를 실제 시간 단계처럼 처리하지 않도록 만들 수 있습니다.
이 부분이 조금 낯설 수 있습니다. 직관은 간단합니다.
lengths는 모델에게 이렇게 알려 주는 역할을 합니다.
첫 번째 문장은 4개까지만 읽어.
두 번째 문장은 2개까지만 읽어.
세 번째 문장은 5개까지만 읽어.
뒤에 붙은 0들은 길이를 맞추기 위한 가짜 토큰이야.딥러닝 코드는 종종 이런 식입니다. 사람이 보기에는 사소한 전처리처럼 보이지만, 모델 입장에서는 무엇이 진짜 입력이고 무엇이 빈칸인지 알려 주는 일이 매우 중요합니다.
마스크로 평균을 낼 때 생기는 차이
이번 모델에서는 lengths와 pack_padded_sequence로 GRU가 패딩을 무시하도록 했습니다. 하지만 마스크가 왜 필요한지 더 직접적으로 느껴 보려면 문장 벡터를 평균으로 만드는 상황을 생각해 보면 좋습니다.
임베딩 결과가 emb이고 마스크가 mask라면, 실제 토큰만 골라 평균을 내야 합니다.
masked_emb = emb * mask.unsqueeze(-1)
sum_emb = masked_emb.sum(dim=1)
real_lengths = mask.sum(dim=1, keepdim=True)
sentence_vec = sum_emb / real_lengths만약 마스크 없이 단순히 emb.mean(dim=1)을 해 버리면 패딩까지 평균에 섞입니다. 짧은 문장일수록 패딩의 영향을 더 크게 받을 수 있습니다. 즉, 모델은 실제 문장 내용이 아니라 빈칸의 개수에 흔들릴 수 있습니다.
나중에 Transformer를 배울 때도 같은 문제가 반복됩니다. Self-Attention은 문장 안의 모든 위치를 서로 바라보게 만듭니다. 이때 <pad> 위치까지 바라보면 모델은 가짜 정보를 참고하게 됩니다. 그래서 Attention mask가 필요합니다. 오늘 만든 마스크는 Transformer로 가는 첫 번째 작은 다리입니다.
학습 루프 작성하기
이제 모델을 학습해 보겠습니다. 이전 장들에서 익힌 학습 루프와 거의 같습니다. 텍스트라고 해서 학습의 본질이 바뀌지는 않습니다.
모델이 예측하고, 손실을 계산하고, 역전파로 기울기를 구하고, 옵티마이저가 파라미터를 조금 수정합니다.
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = TextClassifier(
vocab_size=len(vocab),
embed_dim=16,
hidden_dim=32,
num_classes=2,
pad_idx=pad_idx,
).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
for epoch in range(30):
model.train()
total_loss = 0.0
correct = 0
total = 0
for x, y, lengths, mask in train_loader:
x = x.to(device)
y = y.to(device)
lengths = lengths.to(device)
optimizer.zero_grad()
logits = model(x, lengths)
loss = criterion(logits, y)
loss.backward()
optimizer.step()
total_loss += loss.item() * x.size(0)
pred = logits.argmax(dim=1)
correct += (pred == y).sum().item()
total += y.size(0)
avg_loss = total_loss / total
acc = correct / total
if (epoch + 1) % 5 == 0:
print(f'epoch {epoch + 1}, loss {avg_loss:.4f}, acc {acc:.4f}')데이터가 매우 작기 때문에 정확도가 빠르게 올라갈 수 있습니다. 이 예제에서 높은 정확도는 자랑거리가 아닙니다. 모델이 작은 예문 몇 개를 외웠을 가능성이 크기 때문입니다. 지금의 목적은 좋은 성능이 아니라 텍스트 분류 파이프라인을 손으로 잡아 보는 것입니다.
이 흐름을 다시 보면 앞에서 배운 딥러닝의 기본 구조가 그대로 살아 있습니다.
문장 입력
→ 숫자 시퀀스로 변환
→ 모델 예측
→ 손실 계산
→ 역전파
→ 가중치 수정
→ 반복텍스트 모델도 결국 같은 일을 합니다. 다만 입력이 이미지 픽셀이 아니라 토큰 인덱스이고, 중간에 임베딩과 시퀀스 인코더가 들어갈 뿐입니다.
새 문장 예측해 보기
학습한 모델로 새로운 문장을 예측해 보겠습니다.
idx_to_label = {
0: '부정',
1: '긍정',
}
@torch.no_grad()
def predict(sentence):
model.eval()
ids = encode(sentence)
x = torch.tensor([ids], dtype=torch.long).to(device)
lengths = torch.tensor([len(ids)], dtype=torch.long).to(device)
logits = model(x, lengths)
probs = torch.softmax(logits, dim=1)[0]
pred_idx = probs.argmax().item()
return idx_to_label[pred_idx], probs.cpu()
label, probs = predict('이 영화 감동적이다')
print(label, probs)
label, probs = predict('전개가 너무 지루하다')
print(label, probs)여기서 주의할 점이 있습니다. 감동적이다가 학습 데이터에 있었고 너무도 있었더라도, 조합이 달라지면 모델의 판단이 흔들릴 수 있습니다. 특히 데이터가 작을 때는 더 그렇습니다.
모델은 인간처럼 문장을 이해하는 것이 아닙니다. 학습 데이터에서 자주 본 토큰과 패턴을 바탕으로 점수를 계산합니다. 감동적이다가 긍정 문장에 자주 나왔으면 긍정 점수를 올리는 방향으로 학습될 수 있고, 지루하다가 부정 문장에 자주 나왔으면 부정 점수를 올리는 방향으로 학습될 수 있습니다.
이 말은 모델이 멍청하다는 뜻이 아닙니다. 오히려 딥러닝 모델을 이해하는 중요한 출발점입니다. 모델은 데이터 속 반복되는 신호를 가중치에 새깁니다. 좋은 데이터와 충분한 예시가 있어야 더 안정적인 판단을 합니다.
어떤 단어에 민감하게 반응했는지 살펴보기
텍스트 분류 모델을 만들었다면 한 가지 재미있는 실험을 해 볼 수 있습니다. 문장에서 단어 하나를 가렸을 때 예측 점수가 얼마나 바뀌는지 보는 것입니다.
예를 들어 이 영화 정말 재미있다의 긍정 확률이 높게 나왔다고 해 봅시다. 여기서 재미있다를 <unk>로 바꾸었을 때 긍정 확률이 크게 떨어진다면, 모델이 재미있다라는 토큰에 강하게 의존하고 있다고 볼 수 있습니다.
다음 함수는 각 토큰을 하나씩 <unk>로 바꾸며 긍정 확률 변화량을 계산합니다.
@torch.no_grad()
def positive_score_from_tokens(tokens):
model.eval()
ids = [vocab.get(token, unk_idx) for token in tokens]
if len(ids) == 0:
ids = [unk_idx]
x = torch.tensor([ids], dtype=torch.long).to(device)
lengths = torch.tensor([len(ids)], dtype=torch.long).to(device)
logits = model(x, lengths)
probs = torch.softmax(logits, dim=1)[0]
return probs[1].item()
def token_sensitivity(sentence):
tokens = tokenize(sentence)
base_score = positive_score_from_tokens(tokens)
results = []
for i, token in enumerate(tokens):
changed = tokens.copy()
changed[i] = '<unk>'
new_score = positive_score_from_tokens(changed)
diff = base_score - new_score
results.append((token, diff))
results.sort(key=lambda x: abs(x[1]), reverse=True)
return base_score, results
base, results = token_sensitivity('이 영화 정말 재미있다')
print('base positive score:', base)
for token, diff in results:
print(token, diff)diff가 양수로 크게 나오면 그 단어를 가렸을 때 긍정 확률이 떨어졌다는 뜻입니다. 즉, 모델은 그 단어를 긍정 판단의 근거로 사용했을 가능성이 큽니다. 반대로 diff가 음수라면 그 단어를 가렸을 때 긍정 확률이 올라간 것입니다. 이 경우 그 단어는 긍정 판단을 방해하는 방향으로 작용했을 수 있습니다.
물론 이 분석은 완벽하지 않습니다. 단어 하나를 <unk>로 바꾸면 문장 자체가 조금 어색해집니다. 또 GRU는 순서와 문맥을 함께 처리하므로, 한 단어의 영향만 깔끔하게 분리하기 어렵습니다. 그래도 모델이 어떤 신호에 민감하게 반응하는지 감을 잡는 데는 좋은 출발점입니다.
이 실험을 해 보면 딥러닝 모델이 마법 상자가 아니라는 사실이 조금 더 분명해집니다. 모델은 입력 토큰을 보고, 임베딩 벡터를 꺼내고, 순서대로 정보를 섞고, 마지막에 점수를 계산합니다. 우리가 할 일은 그 흐름을 하나씩 열어 보는 것입니다.
임베딩은 단어의 좌표를 학습한다
이번 장에서는 임베딩을 코드로 사용만 했습니다. 하지만 사실 텍스트 모델에서 임베딩은 매우 중요한 역할을 합니다.
정수 인덱스는 단순한 번호일 뿐입니다. 재미있다가 5번이고 지루하다가 9번이라고 해서 두 단어의 의미적 거리가 4만큼 떨어져 있다는 뜻은 아닙니다. 번호 자체에는 의미가 없습니다.
임베딩 레이어는 이 번호를 의미 있는 벡터로 바꾸려는 장치입니다. 학습이 진행되면 긍정 문장에서 자주 쓰이는 단어들은 분류에 유리한 방향으로 움직이고, 부정 문장에서 자주 쓰이는 단어들도 그에 맞는 방향으로 조정됩니다.
처음에는 무작위 숫자였던 단어 벡터가, 손실을 줄이는 과정에서 조금씩 역할을 갖게 됩니다. 이것이 표현 학습입니다. 앞에서 배운 표현 학습이 텍스트에서도 그대로 일어납니다.
다음 장에서는 이 임베딩 레이어를 더 자세히 들여다보겠습니다. 단어가 벡터가 된다는 말이 정확히 무엇인지, 비슷한 단어가 가까운 벡터가 된다는 말은 어떤 의미인지, 그리고 LLM에서 토큰 임베딩이 왜 그렇게 중요한지 이어서 살펴볼 것입니다.
Transformer로 이어지는 길
오늘 만든 모델은 작지만 중요한 구조를 모두 갖추고 있습니다. 문장을 토큰으로 나누고, 정수로 바꾸고, 패딩과 마스크로 배치를 만들고, 임베딩을 거쳐 시퀀스 인코더로 문장을 요약한 뒤, 분류층으로 예측했습니다.
이 흐름은 나중에 만날 Transformer와도 이어집니다. Transformer는 RNN처럼 토큰을 하나씩 순서대로 처리하지 않습니다. 대신 Self-Attention으로 문장 안의 토큰들이 서로를 직접 바라보게 만듭니다. 하지만 그 전 단계는 여전히 비슷합니다.
문장은 여전히 토큰이 되어야 합니다. 토큰은 여전히 정수 인덱스가 되어야 합니다. 정수 인덱스는 여전히 임베딩 벡터가 되어야 합니다. 배치 처리를 위해 패딩이 필요하고, 패딩을 무시하기 위해 마스크가 필요합니다.
즉, 오늘 만든 작은 텍스트 분류기는 LLM으로 가는 길목에 서 있습니다. 규모는 작고 단순하지만, 핵심 재료는 이미 등장했습니다.
마무리
텍스트 분류는 문장을 숫자로 바꾸는 작업에서 시작합니다. 모델이 문장을 읽는다는 말은, 실제로는 토큰 인덱스와 임베딩 벡터를 계산한다는 뜻입니다.
이번 장에서 우리는 공백 기준 토큰화로 문장을 나누고, 단어장을 만들어 토큰을 정수로 바꾸고, 패딩과 마스크로 길이가 다른 문장을 한 배치에 담았습니다. 그런 다음 임베딩, GRU 인코더, 분류층으로 이루어진 간단한 텍스트 분류 모델을 만들었습니다.
핵심은 이것입니다. 텍스트 모델도 결국 앞에서 배운 딥러닝의 기본 원리를 따릅니다. 모델이 예측하고, 틀린 정도를 손실로 계산하고, 어느 파라미터가 오차에 얼마나 영향을 주었는지 역전파로 확인한 뒤, 조금씩 수정합니다.
다만 입력이 문장이기 때문에 먼저 문장을 숫자 시퀀스로 바꾸어야 합니다. 그리고 그 숫자 시퀀스가 의미 있는 벡터 표현으로 바뀌는 지점, 바로 그곳에 임베딩이 있습니다. 다음 장에서는 이 임베딩 레이어를 더 깊이 들여다보겠습니다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.