트랜스포머·PEFT 2일 과정6장 · Qwen 모델 준비와 텍스트 생성12 / 17← 이전목차다음 →MetaContents

6장. Qwen 모델 준비와 텍스트 생성

실습 1 — 실습 준비와 Qwen 불러오기

2일차에 쓸 도구를 불러오고, Qwen2.5-0.5B-Instruct 를 GPU 에 올려 파라미터 수와 메모리를 확인합니다. 원본 노트북의 셀 3, 6 입니다. 이 두 셀은 7~9차시의 실습에서도 먼저 실행해 두는 준비 코드입니다.

코드를 단계별로 따라가기 전에 실행 흐름을 한 장으로 봅니다. 요리책의 순서도처럼 위에서 아래로 화살표를 따라 읽습니다.

실습 1 코드의 실행 흐름도. 도구를 불러오고 device 와 MODEL_NAME 을 정한 뒤, 토크나이저를 불러오고 GPU 가 BF16 을 지원하는지 물어 MODEL_DTYPE 을 고르고, load_base_model 안에서 device_map 이 없으면 모델을 GPU 로 옮긴 다음 파라미터 수와 메모리를 찍는다
단계하는 일반복과 조건핵심 한 줄
1도구를 불러오고 장치와 모델 이름을 정한다없음device = "cuda"
2정밀도를 골라 기반 모델을 불러오고 크기를 잰다조건 두 개: BF16 지원 여부, device_map 이 없는지model = load_base_model()
  • 흐름도의 모양: 네모는 하는 일, 마름모는 묻는 곳(반복을 더 할지, 조건이 맞는지), 화살표는 실행 차례입니다. 조건 마름모에서 "예"면 아래 네모를 하고, "아니요"면 오른쪽 선을 따라 그 네모를 건너뜁니다. 반복이 있으면 마름모로 다시 돌아오는 고리가 생기는데, 이 실습에는 반복이 없습니다.
  • 첫째 마름모: torch.cuda.is_bf16_supported() 가 참이면 MODEL_DTYPE 이 torch.bfloat16, 거짓이면 torch.float16 입니다. RTX 3080 은 "예" 쪽입니다.
  • 둘째 마름모: load_base_model() 을 인자 없이 불렀으므로 device_map 이 없어 "예" 쪽으로 가 model.to(device) 를 합니다. 9장의 QLoRA 처럼 device_map 을 주면 이 네모를 건너뜁니다.

1단계. 도구 불러오기와 GPU 정하기

연수 첫날 아침에 공구함을 펼치고 작업대를 정하는 일입니다. 표, 텐서, 그래프 도구를 꺼내고, 계산은 GPU(cuda)에서 하기로 정합니다.

Python
# 데이터를 읽고 모델을 학습하는 데 필요한 도구를 불러옵니다.
import gc  # 더 이상 쓰지 않는 모델 객체를 메모리에서 정리합니다.
import json  # JSONL 데이터를 읽고 쓸 때 사용합니다.
import os  # 파일 경로와 폴더를 다룹니다.
import warnings  # 경고 메시지를 제어합니다.
# filterwarnings("ignore") = 경고 거르개를 "무시"로 둔다
warnings.filterwarnings("ignore")  # 실습과 무관한 경고를 숨깁니다.

import pandas as pd  # 표 형태 데이터를 다룹니다.
# torch = PyTorch. 텐서(GPU 에서 계산할 수 있는 숫자 배열)와 신경망 도구
import torch  # 텐서와 모델 학습에 사용합니다.
import matplotlib.pyplot as plt  # 그래프를 그립니다.
import koreanize_matplotlib  # 그래프에서 한글이 깨지지 않게 합니다.

# "cuda" = NVIDIA GPU. 뒤에서 모델과 입력을 이 장치로 옮긴다
device = "cuda"  # GPU 1개를 사용합니다.
# makedirs(폴더, exist_ok=True) = 폴더를 만들되, 이미 있으면 그냥 넘어간다
os.makedirs("outputs", exist_ok=True)  # 학습 결과를 저장할 폴더입니다.

# Hugging Face 의 모델 이름. "만든 곳/모델 이름" 꼴이다
MODEL_NAME = "Qwen/Qwen2.5-0.5B-Instruct"  # 추론과 LoRA 학습에 사용할 모델입니다.

2단계. 기반 모델 불러오고 크기 재기

새 직원을 자리에 앉히고 짐이 얼마나 되는지 재는 일입니다. GPU 가 BF16 을 지원하면 2바이트 숫자로 가중치를 읽어 GPU 에 올리고, 파라미터 수와 메모리 사용량을 찍습니다.

Python · GPU 필요 (출력은 RTX 3080 에서 실행한 결과)
# GPU가 지원하는 숫자 정밀도를 고른 뒤 기반 모델을 불러옵니다.
# AutoModelForCausalLM = 다음 낱말 맞히기(causal LM)용 모델을 이름으로 불러오는 도구
# AutoTokenizer = 모델 이름에 맞는 토크나이저를 골라 주는 도구
from transformers import AutoModelForCausalLM, AutoTokenizer  # 생성 모델과 토크나이저를 불러옵니다.

# from_pretrained = 이미 학습된(pretrained) 것을 내려받아 불러오기
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)  # Qwen용 토크나이저를 불러옵니다.
# is_bf16_supported() = 이 GPU 가 BF16 을 지원하는가. RTX 3080 은 True 라 BF16 을 고른다
MODEL_DTYPE = torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16  # BF16 지원 GPU(RTX 30 이상, A100, L4)는 BF16, 그 외(Colab T4 등)는 FP16입니다.


# 기반 모델을 부르는 함수. 9차시에서 어댑터를 붙이거나 QLoRA 로 다시 부를 때도 쓴다
def load_base_model(model_name=MODEL_NAME, dtype=MODEL_DTYPE, **kwargs):  # 같은 기반 모델을 필요할 때 다시 불러옵니다.
    model = AutoModelForCausalLM.from_pretrained( # 지정한 기반 모델 가중치를 불러옵니다.
        model_name,  # 내려받을 기반 모델입니다.
        dtype=dtype,  # 가중치의 정밀도입니다. 기본값은 MODEL_DTYPE입니다.
        # **kwargs = 이름 붙은 인자를 사전으로 모아 그대로 넘긴다(예: quantization_config, device_map)
        **kwargs,  # 양자화와 장치 배치 등 추가 옵션입니다.
    )
    # device_map 을 주면 라이브러리가 알아서 장치에 올리므로 직접 옮기지 않는다
    if "device_map" not in kwargs:  # device_map 옵션이 없을 때만 아래에서 장치를 직접 지정합니다.
        model = model.to(device)  # 앞에서 선택한 장치로 모델을 옮깁니다.
    # 종료 토큰과 패딩 토큰은 모델에 저장된 생성 설정을 사용합니다.
    return model


# 인자 없이 부르면 MODEL_NAME 을 BF16 으로 불러와 GPU 에 올린다
model = load_base_model()  # 기본 정밀도로 Qwen 모델을 불러옵니다.
# parameters() = 가중치 텐서를 하나씩 돌려준다. numel() = number of elements, 원소 수
n_params = sum(p.numel() for p in model.parameters())  # 모든 가중치 원소 수를 합칩니다.
# 결과: 파라미터 수: 494,032,768 (0.49B)
print(f"파라미터 수: {n_params:,} ({n_params / 1e9:.2f}B)")
# get_memory_footprint() = 가중치가 차지하는 바이트 수. 1024**2 로 나눠 MB 로. 결과: 942 MB
print(f"메모리 사용량: {model.get_memory_footprint() / 1024**2:,.0f} MB")
출력
파라미터 수: 494,032,768 (0.49B)
메모리 사용량: 942 MB

이 실습이 뜻하는 것

공구함을 펼치고(1단계), 새 직원을 자리에 앉혀 짐의 크기를 잰(2단계) 실습입니다. 짐의 크기 942 MB 는 본문에서 손으로 계산한 494,032,768 × 2바이트 ≈ 942.3 MB 와 같습니다. 10GB 짜리 RTX 3080 에서 가중치는 10분의 1 남짓만 차지하므로, 생성과 LoRA 학습에 쓸 자리가 넉넉합니다.

단계출력뜻
1단계없음도구, device = "cuda", MODEL_NAME 준비
2단계파라미터 수: 494,032,768 (0.49B)0.5B 라는 이름처럼 약 4.9억 개
2단계메모리 사용량: 942 MBBF16 2바이트 × 4.9억 개

모델의 크기는 파라미터 수가, 메모리는 파라미터 수 × 바이트 수가 정합니다.

코드 설명

  • warnings.filterwarnings("ignore") 는 경고를 숨깁니다. 그래도 transformers 가 직접 찍는 안내(실습 2의 첫 줄)는 남습니다.
  • MODEL_DTYPE 은 GPU 에 따라 BF16 이나 FP16 을 고릅니다. Colab T4 처럼 BF16 을 지원하지 않는 GPU 에서는 FP16 이 됩니다. 어느 쪽이든 2바이트라 메모리는 같습니다.
  • load_base_model 의 **kwargs 는 9장의 QLoRA 에서 quantization_config 와 device_map 을 넘길 때 쓰입니다. 그때는 라이브러리가 장치 배치를 맡으므로 model.to(device) 를 건너뜁니다.
  • sum(p.numel() for p in model.parameters()) 는 모든 가중치 표의 칸 수를 더합니다. 5장에서 층별로 나눠 센 합과 같은 494,032,768 입니다.
  • get_memory_footprint() 는 가중치만의 크기입니다. 생성 중의 중간값은 들어 있지 않습니다.
← 이전 절6장 · 실습 지도다음 절 →6장 · 실습 2 — chat 함수로 질문하기