Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

대형 언어 모델

Part 1에서 우리는 트랜스포머 아키텍처가 어텐션을 통해 시퀀스 내 모든 위치를 직접 연결한다는 점을 살펴보았습니다. 대형 언어 모델(Large Language Model, LLM)은 이 아키텍처를 대규모 데이터와 막대한 연산으로 밀어붙여, 다음 토큰을 예측하는 단일 목표만으로 번역·요약·추론·코딩 등 폭넓은 능력을 획득한 모델입니다.

이 장에서는 먼저 LLM이 어떻게 만들어지는지 — 어떤 데이터로, 어떤 하드웨어에서, 어떤 단계를 거쳐 학습되는지를 LLaMA 계열을 사례로 정리합니다. 그다음 사전 학습된 모델을 실제로 불러와 대화하고, 이미지를 함께 이해하는 비전-언어 모델까지 코드로 직접 다뤄 봅니다.

1LLaMA: 오픈 대규모 언어 모델

LLaMA는 Large Language Model Meta AI의 약자로, 70억(7B)에서 650억(65B) 개의 매개변수에 이르는 사전 학습 및 미세 조정된 언어 모델의 모음입니다. 공개적으로 사용 가능한 다양한 온라인 데이터로 학습되었으며, 유용성과 안전성 원칙을 염두에 두고 개발되었습니다.

LLaMA의 핵심 아이디어는 추론 예산(inference budget)을 기준으로 최적의 성능을 내는 모델을 훈련하는 데 있습니다. 즉, 같은 크기의 모델이라도 일반적으로 권장되는 양보다 훨씬 더 많은 토큰으로 오래 학습하면, 추론 시점에서 더 작고 빠른 모델로도 큰 모델에 견줄 성능을 얻을 수 있다는 것입니다. 그 결과는 다음과 같이 요약됩니다.

모델파라미터비교 결과
LLaMA-13B130억대부분의 벤치마크에서 GPT-3를 능가 (약 10배 작은 크기로)
LLaMA-65B650억Chinchilla-70B, PaLM-540B와 경쟁력

LLaMA-13B가 단일 GPU에서 실행 가능하면서도 GPT-3를 능가한다는 점은, LLM 연구의 접근성을 넓혀 연구를 "민주화"하는 의미를 가집니다. 또한 Chinchilla, PaLM, GPT-3와 달리 LLaMA는 공개적으로 사용 가능한 데이터만 사용해 오픈 소싱과 호환된다는 점도 중요한 차별점입니다.

1.1아키텍처와 최적화

LLaMA는 트랜스포머를 기반으로 하되 여러 개선을 적용했습니다.

  • 입력 정규화(Pre-normalization): 각 하위 층의 출력이 아니라 입력을 정규화해 학습 안정성을 높입니다.

  • SwiGLU 활성화 함수: 표준 ReLU 대신 게이팅 기반 활성화를 사용합니다.

  • 회전 위치 임베딩(Rotary Positional Embedding, RoPE): 절대 위치 임베딩 대신 회전 변환으로 상대적 위치 정보를 인코딩합니다.

  • 그룹화된 쿼리 어텐션(Grouped-Query Attention): 여러 쿼리 헤드가 키/값 헤드를 공유해 성능과 효율을 함께 끌어올립니다.

효율적인 구현을 위해서는 인과 멀티 헤드 어텐션(Causal Multi-head Attention), 체크포인팅, 모델 및 시퀀스 병렬화 기법이 동원되었습니다. 최적화에는 AdamW와 코사인 학습률 스케줄을 사용했고, 배치 크기는 4M 토큰으로 고정한 뒤 학습률과 가중치 감소는 모델 크기에 따라 조정했습니다.

2사전 훈련 데이터

LLM의 능력은 결국 어떤 데이터를 보았는지에서 나옵니다. LLaMA는 CommonCrawl, C4, GitHub, Wikipedia, Books, ArXiv, StackExchange 등 다양한 소스로 구성된 1.4조(1.4T) 토큰 규모의 데이터셋으로 학습되었습니다.

Common Crawl. 2007년부터 무료로 공개되어 온 웹 말뭉치로, 16년에 걸쳐 2,500억 페이지가 넘게 축적되었고 매달 30~50억 개의 새 페이지가 추가됩니다. 1만 편 이상의 연구 논문에 인용된, 사실상 모든 LLM의 기본 학습 말뭉치입니다. 예를 들어 GPT-3 학습에 쓰인 원시 토큰의 82%가 여기서 나왔습니다.

C4. Colossal, Cleaned Crawl Corpus의 약칭으로, 비영리 Common Crawl을 정제·확장해 만든 데이터셋입니다. Raffel 등의 2020년 논문에서 Text-to-Text Transfer Transformer(T5)를 만드는 데 사용된 뒤 전 세계에 공개되었으며, 현대 NLP 연구의 기초가 된 자료입니다.

LLaMA의 후속 작업에서는 이 토대 위에서 데이터 크기를 40% 늘리고 문맥 길이를 두 배로 확장했습니다.

2.1하드웨어와 학습 비용

이런 규모의 학습은 막대한 연산을 요구합니다. 65B 파라미터 모델을 훈련할 때, 80GB RAM이 장착된 A100 GPU 2,048개에서 코드는 GPU당 초당 약 380개의 토큰을 처리합니다. 이 속도로 1.4T 토큰 데이터셋을 한 번 학습하는 데 약 21일이 걸립니다.

이 숫자는 LLM이 왜 소수의 기관에서만 사전 학습될 수 있는지를 잘 보여 줍니다. 대부분의 실무자는 직접 사전 학습을 수행하기보다, 공개된 사전 학습 가중치를 내려받아 그 위에서 활용하게 됩니다. 다음 절부터는 바로 그 작업을 코드로 다룹니다.

3사전 학습 모델 불러오기

Hugging Face transformers 라이브러리를 사용하면 공개된 사전 학습 모델을 식별자(model_id) 하나로 내려받아 사용할 수 있습니다. 토크나이저는 텍스트를 토큰 ID 시퀀스로 변환하고, AutoModelForCausalLM은 인과적 언어 모델(다음 토큰 예측) 헤드를 갖춘 모델을 불러옵니다.

주석에서 드러나듯, 같은 계열이라도 두 종류의 가중치가 존재합니다. 하나는 다음 토큰 예측만으로 학습된 기본(base, pretrained) 모델이고, 다른 하나는 그 위에 사후 훈련을 거쳐 지시 수행(instruction-following) 능력을 갖춘 모델입니다.

from transformers import AutoTokenizer, AutoModelForCausalLM

# model_path = 'qwen3-1.7b-base' # 일반 언어모델 (pretrained) 모델
# model_path = 'qwen3-1.7b' # post-training (사후 훈련)된 모델: 지시 수행(instruction-following)
model_id = 'google/gemma-4-E2B-it'
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id)

4사후 훈련 (Post-training)

사전 학습만 마친 기본 모델은 방대한 지식을 갖추지만, 사용자의 지시를 따르거나 안전하게 응답하도록 정렬(align)되어 있지는 않습니다. 이를 다듬는 단계가 **사후 훈련(post-training)**입니다. LLaMA 2의 학습 절차가 그 전형적인 흐름을 보여 줍니다.

  1. 사전 학습: 공개 온라인 소스로 기본 모델을 학습합니다.

  2. 감독 미세 조정(Supervised Fine-Tuning, SFT): 지시-응답 예시로 미세 조정해 대화형 모델의 초기 버전을 만듭니다.

  3. 인간 피드백 기반 강화 학습(RLHF): 거부 샘플링(rejection sampling)과 근접 정책 최적화(PPO)를 통해 모델을 반복적으로 개선합니다.

이 과정에서 RLHF 단계와 병행해 보상 모델링 데이터를 반복적으로 축적하는 것이 핵심입니다. 그래야 보상 모델이 갱신되는 정책의 분포 안에 머물러, 개선이 엇나가지 않습니다. 이렇게 사후 훈련을 거친 모델이 바로 위에서 불러온 지시 수행(-it) 가중치입니다.

4.1대화 형식과 채팅 템플릿

지시 수행 모델은 단순한 평문이 아니라 역할(role)이 구분된 메시지 형식으로 입력을 받습니다. 시스템 메시지는 모델의 페르소나와 출력 규칙을 지정하고, 사용자 메시지는 실제 질문을 담습니다. 아래 예에서는 시스템 메시지로 응답 스타일과 한국어 출력을 지시하고, 사용자 메시지로 추론이 필요한 문제를 던집니다.

apply_chat_template은 이 메시지 리스트를 모델이 학습한 특수 토큰 형식의 문자열로 변환합니다. add_generation_prompt=True는 모델이 응답을 시작할 위치를 표시하고, enable_thinking=True는 응답에 앞서 사고 과정을 펼치도록 합니다. tokenize=False로 두면 변환된 원본 문자열을 그대로 확인할 수 있습니다.

prompt = """
Astronomers are studying a star with a 1.5 solar radius and 1.1 solar masses. When the star's surface is not covered by dark spots, its Teff is 6000K. However, when 40% of its surface is covered by spots, the overall photospheric effective temperature decreases to 5500 K. In the stellar photosphere, when examining the ratio of the number of neutral atoms of Ti in two energetic levels (level 1 and level 2), astronomers have observed that this ratio decreases when the star has spots. What is the factor by which this ratio changes when the star does not have spots compared to when it has spots? Note that the transition between the energy levels under consideration corresponds to a wavelength of approximately 1448 Å. Assume that the stellar photosphere is in LTE.

A. ~2.9
B. ~1.1
C. ~4.5
D. ~7.8
"""
messages = [
    {'role': 'system', 'content': '너는 도널드 트럼프 스타일로 설명하는 코딩 도우미야. 생각과 응답을 모두 한국어로 작성할 것.'},
    {"role": "user", "content": prompt}
]
text = tokenizer.apply_chat_template(
    messages, tokenize=False, add_generation_prompt=True, enable_thinking=True)
print(text)

4.2생성과 스트리밍

준비된 텍스트를 토큰화해 모델이 있는 장치로 옮긴 뒤 generate로 토큰을 한 개씩 생성합니다. TextStreamer를 함께 넘기면 완성된 응답을 한꺼번에 기다리지 않고 생성되는 즉시 토큰을 출력으로 흘려보낼 수 있습니다. skip_prompt=True는 입력 프롬프트를, skip_special_tokens=True는 특수 토큰을 출력에서 제외합니다.

max_new_tokens로 지정한 컨텍스트 길이는 128K 토큰에 이르며, 이는 긴 추론 과정과 응답을 모두 담아낼 수 있는 규모입니다. torch.inference_mode() 블록은 그래디언트 추적을 끄고 추론 전용으로 실행합니다.

import torch
from transformers import TextStreamer

device = 'cpu'
if torch.cuda.is_available():
    device = 'cuda'
model.to(device)

입력시퀀스 = tokenizer(text, return_tensors='pt').to(device)
스트리머 = TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)

컨텍스트길이 = 128 * 1024
print(f'컨텍스트 길이: {컨텍스트길이}')
with torch.inference_mode():
    _ = model.generate(
        **입력시퀀스, 
        max_new_tokens=컨텍스트길이,
        streamer=스트리머,
    )

5비전-언어 모델 (Vision Language Model)

언어 모델의 입력은 텍스트에 국한될 필요가 없습니다. **비전-언어 모델(VLM)**은 이미지를 토큰 시퀀스에 함께 끼워 넣어, 그림을 보고 설명하거나 그에 관한 질문에 답할 수 있습니다. 텍스트 토크나이저 대신 이미지와 텍스트를 함께 전처리하는 AutoProcessor를 사용한다는 점이 차이입니다.

먼저 모델과 프로세서를 불러옵니다.

from transformers import Qwen3VLForConditionalGeneration, AutoProcessor

model_id = "Qwen/Qwen3-VL-2B-Instruct"
model = Qwen3VLForConditionalGeneration.from_pretrained(model_id)
processor = AutoProcessor.from_pretrained(model_id)

메시지의 content는 더 이상 단일 문자열이 아니라, 이미지 항목과 텍스트 항목이 섞인 리스트입니다. 아래에서는 이미지 한 장과 "이미지 설명"이라는 지시를 함께 전달합니다.

apply_chat_template이 이미지와 텍스트를 하나의 입력 텐서로 묶고, generate로 응답 토큰을 생성합니다. 생성 결과에서 입력 길이만큼을 잘라낸(generated_ids_trimmed) 뒤 디코딩하면, 모델이 새로 만들어 낸 응답 텍스트만 깔끔하게 얻을 수 있습니다.

messages = [
    {
        "role": "user",
        "content": [
            {
                "type": "image",
                "image": "mozzi.jpg",
            },
            {"type": "text", "text": "이미지 설명"},
        ],
    }
]

# Preparation for inference
inputs = processor.apply_chat_template(
    messages,
    tokenize=True,
    add_generation_prompt=True,
    return_dict=True,
    return_tensors="pt"
)
inputs = inputs.to(model.device)

# Inference: Generation of the output
generated_ids = model.generate(**inputs, max_new_tokens=128)
generated_ids_trimmed = [
    out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
]
output_text = processor.batch_decode(
    generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
)
print(output_text)