4장. LLM 을 완성하는 3단계와 Hugging Face
Hugging Face 생태계
스마트폰에서 앱이 필요하면 앱 장터에서 찾아 받고, 설명과 별점을 본 뒤 설치합니다. Hugging Face 의 Hub 는 모델과 데이터의 앱 장터이고, 파이썬 라이브러리는 받아 온 모델을 돌리는 도구입니다.
카드 넷이 이 장에서 쓰는 도구입니다. Hub 는 모델과 데이터를 올리고 받는 곳, transformers 는 모델을 불러와 학습하고 글을 만드는 라이브러리, datasets 는 데이터를 읽고 나누고 바꾸는 라이브러리, tokenizers 는 토크나이저를 빠르게 학습하는 라이브러리입니다. 여기서 앱 장터가 Hub, 앱이 모델입니다.
도구마다 맡은 일
| 도구 | 설치 이름 | 이 장에서 쓰는 것 | 실습 |
|---|---|---|---|
| Hub | (누리집) | 모델 이름으로 내려받기 | 실습 1, 2 |
| transformers | transformers | AutoTokenizer, AutoModelForCausalLM, GPT2Config, Trainer | 실습 1, 2, 5, 6 |
| datasets | datasets | Dataset.from_dict, map, train_test_split | 실습 4, 5, 6 |
| tokenizers | tokenizers | ByteLevelBPETokenizer | 실습 3 |
| accelerate | accelerate | Trainer 가 GPU 를 쓸 때 안에서 쓴다 | 실습 5, 6 |
datasets 맛보기
datasets 의 Dataset 은 pandas 의 표와 비슷하지만, map 으로 큰 데이터를 묶음 단위로 빠르게 바꾸는 데 특화되어 있습니다.
from datasets import Dataset
ds = Dataset.from_dict({"질문": ["배송비는?", "환불은 언제?", "쿠폰 몇 장?"]})
# map: 행마다(또는 묶음마다) 함수를 적용해 새 열을 만든다
ds = ds.map(lambda row: {"글자수": len(row["질문"])})
print(ds)
print(ds[0])열이 ['질문', '글자수'] 인 3행짜리 데이터가 되고, 첫 행은 {'질문': '배송비는?', '글자수': 5} 입니다. 실습 4에서 이 map 으로 글을 토큰으로 바꿉니다.
코드에 나오는 함수
| 코드 | 쉬운 뜻 |
|---|---|
Dataset.from_dict({...}) | 열 이름과 값 목록을 담은 사전으로 데이터 표를 만든다. dict 는 dictionary(사전)의 줄임말이다 |
ds.map(함수) | 표의 행마다 함수를 적용하고, 함수가 돌려준 사전을 새 열로 붙인다. map 은 "하나하나 대응시키다"라는 뜻이다 |
lambda row: {...} | 이름 없이 한 줄로 쓰는 작은 함수다. 행 하나(row)를 받아 새 열의 값을 돌려준다 |
print(ds) | 표의 열 이름(features)과 행 수(num_rows)를 요약해 보여 준다 |
ds[0] | 첫 행을 사전 꼴로 꺼낸다 |
이 코드가 뜻하는 것
엑셀에서 옆 칸에 =LEN(A1) 을 적고 아래로 쭉 끌어 채우는 것과 같습니다. map 이 "아래로 끌어 채우기"를 대신해 줍니다.
| 줄 | 하는 일 | 쉽게 말하면 |
|---|---|---|
Dataset.from_dict({"질문": [...]}) | 질문 세 개를 한 열로 담은 표를 만든다 | A 열에 질문 세 개를 적는다 |
ds.map(lambda row: {"글자수": len(row["질문"])}) | 행마다 질문의 글자 수를 세어 새 열에 적는다 | B 열에 =LEN(A1) 을 적고 끌어 채운다 |
print(ds), print(ds[0]) | 표의 생김새와 첫 행을 본다 | 표 전체와 첫 줄을 훑어본다 |
map 은 원래 표를 고치지 않고 새 표를 돌려주므로 ds = ds.map(...) 처럼 다시 받아야 합니다. 실행하면 Map: 100% 같은 진행 막대가 함께 찍힐 수 있는데, 오류가 아니라 몇 행을 바꿨는지 보여 주는 표시입니다.
작게 줄여서 눈으로 보기
실습 4, 5에서는 map 에 batched=True 를 주고, 학습용과 검증용을 train_test_split 으로 나눕니다. 질문 네 개로 두 가지를 미리 써 봅니다.
from datasets import Dataset, disable_progress_bars
disable_progress_bars() # 진행 막대를 끄고 결과만 본다
ds = Dataset.from_dict({"질문": ["배송비는?", "환불은 언제?", "쿠폰 몇 장?", "교환 되나요?"]})
# batched=True: 행 하나씩이 아니라 여러 행을 묶음(목록)으로 받아 한 번에 바꾼다
ds = ds.map(lambda batch: {"글자수": [len(q) for q in batch["질문"]]}, batched=True)
print(list(ds["글자수"]))
# 학습용과 검증용으로 나눈다. seed 를 고정하면 다시 돌려도 같게 나뉜다
parts = ds.train_test_split(test_size=0.25, seed=42)
print("학습용:", list(parts["train"]["질문"]))
print("검증용:", list(parts["test"]["질문"]))[5, 7, 7, 7]
학습용: ['쿠폰 몇 장?', '환불은 언제?', '배송비는?']
검증용: ['교환 되나요?']| 결과 | 읽는 법 |
|---|---|
[5, 7, 7, 7] | 질문 네 개의 글자 수입니다. 띄어쓰기와 물음표도 한 글자로 셉니다. batched=True 라서 함수는 질문 목록을 한꺼번에 받고, 값도 목록으로 돌려줍니다 |
| 학습용 | test_size=0.25(4분의 1을 검증용으로) 이라 네 개 가운데 세 개가 학습용입니다. 순서는 섞여 있습니다 |
| 검증용 | 나머지 하나 "교환 되나요?"가 검증용입니다. seed=42 를 고정해 두어 다시 돌려도 같은 질문이 뽑힙니다 |
train_test_split 은 "학습용(train)과 시험용(test)으로 나누기"라는 뜻이고, 결과는 "train", "test" 두 이름으로 꺼냅니다.
주의할 점
- Hub 에는 누구나 모델을 올릴 수 있습니다. 내려받기 전에 올린 곳(회사나 단체)과 내려받은 수, 모델 카드를 확인합니다.
- 모델 파일은 처음 한 번만 내려받고
C:\Users\사용자이름\.cache\huggingface에 저장됩니다. 디스크가 부족하면 이 폴더를 정리합니다.