LLM 엔지니어링4장 · LLM 을 완성하는 3단계와 Hugging Face4 / 23← 이전목차다음 →MetaContents

4장. LLM 을 완성하는 3단계와 Hugging Face

사전학습

도서관의 책을 처음부터 끝까지 다 읽는 사람을 떠올려 보세요. 무엇을 외우라고 시킨 사람은 없지만, 읽다 보면 말투, 문법, 상식이 쌓입니다. 사전학습이 이렇게 "다음 낱말 맞히기"만으로 엄청난 글을 읽히는 일입니다.

이 장 실습의 작은 GPT, GPT-3, Qwen2.5 가 사전학습에 쓴 토큰 수를 로그 눈금 막대로 견준 그림

가로축은 눈금 한 칸이 10배인 로그 눈금입니다. 다홍 막대인 이 장 실습의 작은 GPT 는 약 5만 토큰으로 배우고, 청록 막대의 GPT-3(2020)는 약 3,000억 토큰, 마젠타 막대의 Qwen2.5(2024)는 약 18조 토큰으로 배웠습니다. 큰 두 모델의 숫자는 각 회사가 공개한 보고서의 값입니다. 여기서 도서관의 책이 사전학습 데이터입니다.

사전학습이 하는 일

3장의 미니 GPT 가 한 일이 사전학습입니다. 글을 토큰으로 바꾸고, 자리마다 다음 토큰을 정답으로 삼아 교차 엔트로피 손실을 줄여 나갑니다.

항목3장 미니 GPT4장 실습 5실제 LLM
토크나이저글자 단위, 직접 만듦BPE, 직접 학습BPE 등, 수만~수십만 어휘
모델 만들기직접 쓴 클래스GPT2Config 로 설정만수십억 파라미터
학습직접 쓴 루프Trainer수천 개의 GPU 로 수 주
데이터대화 8만 글자쇼핑몰 글 약 5만 토큰수조 토큰

라벨 없는 글이 정답이 되는 법

사전학습은 사람이 정답을 달지 않습니다. 글 자체를 한 칸 밀면 정답이 되기 때문입니다.

Python
tokens = ["배송", "은", "결제", "후", "1~3일", "안에", "도착"]

# 입력은 마지막 토큰을 뺀 것, 정답은 첫 토큰을 뺀 것(한 칸 민 것)
inputs, targets = tokens[:-1], tokens[1:]
for x, y in zip(inputs, targets):
    print(f"{x:>5} 다음에는 → {y}")

토큰 일곱 개짜리 문장 하나에서 "배송 다음에는 은", "은 다음에는 결제"처럼 문제 여섯 개가 저절로 생깁니다. 글이 많을수록 문제도 많아집니다. Hugging Face 의 언어모델은 labels 에 입력과 같은 토큰을 주면 이 한 칸 밀기를 안에서 해 줍니다.

코드에 나오는 함수

코드쉬운 뜻
tokens[:-1]목록에서 마지막 하나만 빼고 가져온다. 모델이 "보는" 쪽이다
tokens[1:]목록에서 첫 하나만 빼고 가져온다. 모델이 "맞혀야 하는" 쪽이다
zip(inputs, targets)두 목록을 같은 자리끼리 짝지어 하나씩 꺼낸다. 첫째는 ("배송", "은"), 둘째는 ("은", "결제") 꼴이다
f"{x:>5}"글을 다섯 칸 너비에 오른쪽으로 붙여 찍는다. 줄을 가지런히 맞추려는 것이라 계산과는 관계없다

이 코드가 뜻하는 것

받아쓰기 연습장을 떠올려 보세요. 문장을 한 낱말씩 가려 가며 "다음 낱말은 무엇일까"를 스스로 묻고 답을 확인합니다. 문제를 내 줄 선생님이 없어도 문장 자체가 문제와 답이 됩니다.

줄하는 일쉽게 말하면
tokens = [...]문장 하나를 토큰 일곱 개로 적는다연습장에 적은 문장
inputs, targets = tokens[:-1], tokens[1:]같은 문장을 한 칸 어긋나게 두 줄로 만든다윗줄은 읽은 낱말, 아랫줄은 그다음 낱말
for x, y in zip(...)같은 자리끼리 짝지어 문제를 하나씩 꺼낸다"배송 다음은?" → "은" 처럼 문제와 답을 맞춘다

토큰 일곱 개에서 문제 여섯 개가 나온 것은, 마지막 토큰 "도착" 뒤에는 맞힐 다음 토큰이 없기 때문입니다. 토큰이 N 개면 문제는 N-1 개입니다.

작게 줄여서 눈으로 보기

Hugging Face 의 언어모델에 labels 로 입력과 똑같은 번호를 주면, 모델 안에서 위의 한 칸 밀기가 일어납니다. 토큰 번호 다섯 개(설명용)로 그 모습을 흉내 내 봅니다.

Python
# 한 문장의 토큰 번호(설명용)
input_ids = [5, 9, 7, 3, 8]
labels = input_ids                   # 사전학습: labels 에 입력을 그대로 준다

# 모델 안에서 일어나는 한 칸 밀기
seen = input_ids[:-1]                # 모델이 보는 쪽: 마지막 하나를 뺀다
answer = labels[1:]                  # 맞혀야 할 쪽: 첫 하나를 뺀다
print("labels 로 준 것:", labels)
print("보는 쪽        :", seen)
print("맞힐 쪽        :", answer)
print("문제 수        :", len(answer))
출력
labels 로 준 것: [5, 9, 7, 3, 8]
보는 쪽        : [5, 9, 7, 3]
맞힐 쪽        : [9, 7, 3, 8]
문제 수        : 4
결과읽는 법
labels 로 준 것입력과 똑같습니다. 우리가 정답을 따로 만들 필요가 없습니다
보는 쪽, 맞힐 쪽위아래를 같은 자리끼리 읽으면 "5 다음은 9", "9 다음은 7"처럼 문제가 됩니다
문제 수토큰 다섯 개에서 문제 네 개가 나옵니다. 실습 5의 DataCollatorForLanguageModeling 이 labels 에 입력을 그대로 복사해 주기만 하는 까닭이 이것입니다

주의할 점

  • 사전학습 데이터에 틀린 내용이 있으면 모델도 그대로 배웁니다. 데이터의 품질과 저작권, 개인정보를 먼저 살핍니다.
  • 작은 데이터로 사전학습하면 모델은 그 데이터의 문장을 거의 외웁니다. 실습 5의 작은 GPT 가 그렇습니다. 일반 지식을 갖추려면 실제 LLM 처럼 아주 많은 글이 필요합니다.
← 이전 절4장 · LLM 을 완성하는 3단계다음 절 →4장 · 손실과 퍼플렉서티