카테고리 없음

mini GPT 과제 2편 - GPTDataset과 InputEmbedding 구현 리뷰

cedis 2026. 6. 5. 00:24

mini GPT 과제 랩 구현 시리즈 2편

GPTDataset과 InputEmbedding 구현 리뷰

BPE가 token ID 목록을 만들었다면, `GPTDataset`은 이 목록을 학습 가능한 input/target 쌍으로 잘라낸다. `InputEmbedding`은 그 ID를 Transformer가 계산할 수 있는 벡터로 바꾼다.

이번 단계는 코드 양은 많지 않지만, 뒤의 attention과 model shape를 결정한다. 여기서 shape를 잘못 잡으면 나중에 attention에서 에러가 늦게 터진다.

테스트 통과 근거

tests/test_dataset.py
- GPTDataset 길이 계산
- __getitem__ input/target shape
- DataLoader batch shape
- InputEmbedding 출력 shape

결과: 4 passed

1. Dataset 길이는 context_length와 stride로 결정된다

한 샘플은 input `context_length`개와 target `context_length`개를 만들어야 한다. target은 input보다 한 칸 뒤이므로 실제로는 `context_length + 1`개의 token이 필요하다.

self.stride = stride if stride is not None else context_length
self._length = max(
    0,
    (len(token_ids) - context_length - 1) // self.stride + 1,
)
왜 `- 1`이 들어가나

input만 만들면 `context_length`개로 충분하다. 하지만 target은 한 칸 뒤까지 필요하므로 마지막 token 하나를 더 읽을 수 있어야 한다.

2. __getitem__은 input과 target을 한 칸 차이로 만든다

실제 구현은 단순하지만 의미는 중요하다. 같은 구간을 거의 그대로 쓰되 target만 시작 위치를 하나 뒤로 민다.

start = idx * self.stride
end = start + self.context_length

input_ids = self.token_ids[start:end]
target_ids = self.token_ids[start + 1: end + 1]

return (
    torch.tensor(input_ids, dtype=torch.long),
    torch.tensor(target_ids, dtype=torch.long),
)
예시: context_length = 4
원본
1011121314
input
10111213
target
11121314

3. create_dataloader는 Dataset을 batch 단위로 감싼다

DataLoader는 학습 루프가 한 번에 여러 샘플을 처리할 수 있게 묶어준다. 이때 반환 shape는 뒤의 모델 입력과 바로 연결된다.

dataset = GPTDataset(token_ids, context_length, stride=stride)

return DataLoader(
    dataset,
    batch_size=batch_size,
    shuffle=shuffle,
    drop_last=drop_last,
    num_workers=num_workers,
)

4. InputEmbedding은 token embedding과 position embedding을 더한다

token embedding은 “무슨 토큰인가”를 표현하고, position embedding은 “몇 번째 위치인가”를 표현한다. GPT 입력은 이 둘을 더한 값이다.

self.token_embedding = nn.Embedding(vocab_size, emb_dim)
self.position_embedding = nn.Embedding(context_length, emb_dim)
self.dropout = nn.Dropout(drop_rate)

def forward(self, x):
    batch_size, seq_len = x.shape
    positions = torch.arange(seq_len, device=x.device)
    token_embeddings = self.token_embedding(x)
    position_embeddings = self.position_embedding(positions)
    return self.dropout(token_embeddings + position_embeddings)
shape 역할
x (B, T) token ID batch
token_embeddings (B, T, C) 각 토큰의 벡터
position_embeddings (T, C) 각 위치의 벡터
output (B, T, C) TransformerBlock 입력

이 구현이 통과한 핵심 계약

  • Dataset 길이가 만들 수 있는 샘플 개수와 일치한다.
  • 각 샘플의 input과 target은 `context_length` 길이를 가진다.
  • DataLoader는 `(batch_size, context_length)` 형태의 batch를 만든다.
  • InputEmbedding 출력은 `(batch_size, seq_len, emb_dim)`이다.

다음 글 예고

다음 구현 글에서는 `MultiHeadAttention`을 본다. 여기서부터 모델은 각 위치의 토큰이 이전 문맥을 어떤 비율로 참고할지 계산하기 시작한다.

한 줄 정리: Dataset은 다음 토큰 예측 문제를 만들고, InputEmbedding은 token ID와 위치 정보를 더해 Transformer가 받을 입력 shape를 만든다.