본문으로 바로가기

LLaMA가 바꿔놓은 것들

23.11.01

    ai

LLaMA가 바꿔놓은 것들

2023년 2월, Meta가 LLaMA를 공개했습니다. 처음에는 "그래서 뭐가 달라지나?"라고 생각했는데, 돌이켜보면 이 모델이 오픈소스 LLM 생태계의 판을 완전히 바꿔놓았습니다. 이 글에서는 2023년 한 해 동안 일어난 변화와 실제 경험을 정리합니다.

LLaMA 공개 전과 후

공개 전: API 종속의 시대

LLaMA 이전에는 대형 언어 모델을 사용하는 방법이 제한적이었습니다.

# 2023년 초반의 LLM 사용 방식
LLM_OPTIONS_BEFORE_LLAMA = {
    "OpenAI API": {
        "models": ["GPT-3.5", "GPT-4"],
        "pros": ["높은 성능", "쉬운 사용"],
        "cons": ["비용", "프라이버시", "커스터마이징 불가", "종속성"]
    },
    "Cohere / Anthropic API": {
        "models": ["Command", "Claude"],
        "pros": ["대안 존재"],
        "cons": ["동일한 한계"]
    },
    "오픈소스 (GPT-J, GPT-Neo)": {
        "models": ["GPT-J-6B", "GPT-NeoX-20B"],
        "pros": ["오픈소스", "자체 운영 가능"],
        "cons": ["성능 부족", "상용 모델과 큰 격차"]
    }
}

# 당시 상황: GPT-4가 나오면 감탄만 하고 끝
def typical_workflow_2022():
    # 1. OpenAI API 호출
    response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[{"role": "user", "content": prompt}]
    )

    # 2. 비용 걱정
    cost = calculate_cost(response)  # 토큰당 과금

    # 3. 프라이버시 우려
    # "민감한 데이터를 외부 서버로 보내도 되나?"

    # 4. 커스터마이징 불가
    # "우리 도메인에 맞게 조정할 수 없다"

    return response

공개 후: 새로운 가능성

LLaMA는 모델 가중치가 (유출을 통해) 공개되면서 모든 것이 달라졌습니다.

# LLaMA 이후 가능해진 것들
LLAMA_ENABLED_CAPABILITIES = {
    "로컬 실행": {
        "description": "내 컴퓨터에서 LLM 실행",
        "benefit": "API 비용 없음, 프라이버시 보장",
        "example": "llama.cpp로 맥북에서 7B 모델 실행"
    },
    "파인튜닝": {
        "description": "자체 데이터로 모델 조정",
        "benefit": "도메인 특화 모델 제작 가능",
        "example": "의료, 법률, 코드 특화 모델"
    },
    "연구": {
        "description": "모델 내부 분석 가능",
        "benefit": "학문적 연구, 개선 가능",
        "example": "Interpretability 연구"
    },
    "상업화": {
        "description": "LLaMA 2부터 상업적 사용 허용",
        "benefit": "스타트업도 LLM 기반 서비스 가능",
        "example": "자체 챗봇, AI 어시스턴트"
    }
}

생태계 폭발

LLaMA 공개 후 몇 달 사이에 놀라운 속도로 생태계가 형성되었습니다.

파인튜닝 모델들

LLAMA_FINETUNED_MODELS = {
    "Alpaca": {
        "release": "2023년 3월",
        "creator": "Stanford",
        "method": "GPT-3.5로 생성한 52K instruction 데이터로 파인튜닝",
        "significance": "저비용 파인튜닝 가능성 입증 ($600 미만)"
    },
    "Vicuna": {
        "release": "2023년 3월",
        "creator": "LMSYS",
        "method": "ShareGPT 대화 데이터로 파인튜닝",
        "significance": "GPT-4 대비 90% 품질 주장"
    },
    "WizardLM": {
        "release": "2023년 4월",
        "creator": "Microsoft",
        "method": "Evol-Instruct로 복잡한 instruction 생성",
        "significance": "복잡한 추론 능력 향상"
    },
    "Orca": {
        "release": "2023년 6월",
        "creator": "Microsoft",
        "method": "GPT-4의 단계별 추론 과정 학습",
        "significance": "작은 모델에서 추론 능력 개선"
    },
    "CodeLlama": {
        "release": "2023년 8월",
        "creator": "Meta",
        "method": "코드 데이터로 추가 학습",
        "significance": "공식 코드 특화 버전"
    }
}

효율성 도구들

# llama.cpp - 가장 영향력 있는 프로젝트 중 하나
class LlamaCppImpact:
    """
    llama.cpp가 가져온 변화

    핵심: CPU에서 LLaMA 실행 가능
    → GPU 없이도 LLM 사용 가능
    → 맥북, 라즈베리파이에서도 실행
    """

    @staticmethod
    def supported_quantization():
        return {
            "Q4_0": {"bits": 4, "size_7b": "3.9GB", "quality": "acceptable"},
            "Q4_K_M": {"bits": 4, "size_7b": "4.1GB", "quality": "good"},
            "Q5_K_M": {"bits": 5, "size_7b": "4.8GB", "quality": "very good"},
            "Q8_0": {"bits": 8, "size_7b": "7.2GB", "quality": "excellent"}
        }

    @staticmethod
    def performance_example():
        """
        M2 MacBook Pro에서 LLaMA 7B 실행
        """
        return {
            "model": "LLaMA 7B Q4_K_M",
            "device": "M2 Pro (16GB RAM)",
            "load_time": "~3초",
            "tokens_per_second": "~20 tok/s",
            "memory_usage": "~5GB"
        }


# GGML/GGUF 포맷
QUANTIZATION_FORMATS = {
    "GGML": {
        "description": "초기 llama.cpp 포맷",
        "status": "deprecated"
    },
    "GGUF": {
        "description": "개선된 포맷 (2023년 8월~)",
        "features": ["메타데이터 포함", "확장 가능", "더 나은 호환성"]
    },
    "GPTQ": {
        "description": "GPU 최적화 양자화",
        "features": ["4-bit 양자화", "GPU에서 빠른 추론"]
    },
    "AWQ": {
        "description": "Activation-aware 양자화",
        "features": ["더 나은 품질", "GPTQ보다 약간 느림"]
    }
}

실제로 해본 것들

로컬 LLM 실행

# llama-cpp-python 사용 예시
from llama_cpp import Llama

# 모델 로드 (Q4 양자화 버전)
llm = Llama(
    model_path="./models/llama-2-7b-chat.Q4_K_M.gguf",
    n_ctx=4096,        # 컨텍스트 길이
    n_threads=8,       # CPU 스레드 수
    n_gpu_layers=35,   # GPU 오프로드 (Metal/CUDA)
    verbose=False
)

# 추론
def generate_response(prompt: str, max_tokens: int = 512):
    response = llm(
        f"[INST] {prompt} [/INST]",  # LLaMA 2 Chat 포맷
        max_tokens=max_tokens,
        temperature=0.7,
        top_p=0.95,
        repeat_penalty=1.1,
        stop=["[INST]", "[/INST]"]
    )
    return response["choices"][0]["text"]

# 사용 예시
result = generate_response("Python으로 퀵소트를 구현해줘")
print(result)

실행 성능 측정 결과입니다.

LOCAL_LLM_BENCHMARKS = {
    "M2 Pro MacBook (16GB)": {
        "LLaMA 7B Q4": {
            "load_time": "2.8s",
            "tokens_per_second": "22 tok/s",
            "first_token_latency": "0.5s"
        },
        "LLaMA 13B Q4": {
            "load_time": "5.2s",
            "tokens_per_second": "12 tok/s",
            "first_token_latency": "1.2s"
        }
    },
    "RTX 4090 (24GB)": {
        "LLaMA 7B Q4": {
            "load_time": "1.5s",
            "tokens_per_second": "95 tok/s",
            "first_token_latency": "0.1s"
        },
        "LLaMA 13B Q4": {
            "load_time": "2.8s",
            "tokens_per_second": "55 tok/s",
            "first_token_latency": "0.2s"
        },
        "LLaMA 70B Q4": {
            "load_time": "25s",
            "tokens_per_second": "15 tok/s",
            "first_token_latency": "0.8s"
        }
    }
}

파인튜닝 시도

LoRA(Low-Rank Adaptation)를 사용해서 도메인 특화 모델을 학습시켜봤습니다.

from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    TrainingArguments,
    Trainer
)
from peft import (
    LoraConfig,
    get_peft_model,
    prepare_model_for_kbit_training
)
from datasets import load_dataset
import torch

# 모델 로드 (4-bit 양자화)
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-2-7b-hf",
    load_in_4bit=True,
    torch_dtype=torch.float16,
    device_map="auto",
    quantization_config={
        "load_in_4bit": True,
        "bnb_4bit_compute_dtype": torch.float16,
        "bnb_4bit_quant_type": "nf4",
        "bnb_4bit_use_double_quant": True
    }
)

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
tokenizer.pad_token = tokenizer.eos_token

# 4-bit 학습 준비
model = prepare_model_for_kbit_training(model)

# LoRA 설정
lora_config = LoraConfig(
    r=16,                      # LoRA rank
    lora_alpha=32,             # 스케일링 파라미터
    target_modules=[           # 적용할 레이어
        "q_proj", "k_proj", "v_proj", "o_proj",
        "gate_proj", "up_proj", "down_proj"
    ],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

# LoRA 적용
model = get_peft_model(model, lora_config)

# 학습 가능한 파라미터 확인
def print_trainable_parameters(model):
    trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
    total = sum(p.numel() for p in model.parameters())
    print(f"Trainable: {trainable:,} / {total:,} ({100*trainable/total:.2f}%)")

print_trainable_parameters(model)
# 출력: Trainable: 4,194,304 / 6,742,609,920 (0.06%)
# → 전체 파라미터의 0.06%만 학습!

학습 데이터 준비와 실행입니다.

# 데이터셋 준비 (Instruction 포맷)
def format_instruction(sample):
    """
    LLaMA 2 Instruction 포맷
    """
    instruction = sample["instruction"]
    input_text = sample.get("input", "")
    output = sample["output"]

    if input_text:
        prompt = f"""[INST] {instruction}

{input_text} [/INST] {output}"""
    else:
        prompt = f"[INST] {instruction} [/INST] {output}"

    return {"text": prompt}

# 데이터셋 로드 및 포맷팅
dataset = load_dataset("json", data_files="custom_instructions.json")
dataset = dataset.map(format_instruction)

# 학습 설정
training_args = TrainingArguments(
    output_dir="./llama2-7b-custom-lora",
    num_train_epochs=3,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    learning_rate=2e-4,
    warmup_steps=100,
    logging_steps=10,
    save_strategy="epoch",
    fp16=True,
    optim="paged_adamw_8bit",  # 메모리 효율적인 옵티마이저
    gradient_checkpointing=True,
    max_grad_norm=0.3,
    lr_scheduler_type="cosine"
)

# 학습 실행
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=dataset["train"],
    tokenizer=tokenizer,
    data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False)
)

trainer.train()

# LoRA 가중치만 저장 (약 17MB)
model.save_pretrained("./llama2-7b-custom-lora")

QLoRA: 더 적은 메모리로 파인튜닝

# QLoRA 설정 (4-bit 양자화 + LoRA)
QLORA_CONFIG = {
    "memory_usage": {
        "7B model full finetune": "~120GB VRAM",
        "7B model LoRA": "~16GB VRAM",
        "7B model QLoRA": "~6GB VRAM"  # 훨씬 적은 메모리!
    },
    "quality_comparison": {
        "full_finetune": "100%",
        "lora": "~99%",
        "qlora": "~97%"  # 약간의 품질 손실
    },
    "training_speed": {
        "full_finetune": "1x",
        "lora": "2-3x faster",
        "qlora": "1.5-2x faster"
    }
}

# 실제 QLoRA 학습 경험
QLORA_EXPERIENCE = {
    "hardware": "RTX 3090 (24GB)",
    "model": "LLaMA 2 13B",
    "dataset": "5,000 instruction pairs",
    "training_time": "~4 hours",
    "result": "도메인 특화 성능 크게 향상"
}

vLLM: 프로덕션 서빙

로컬에서 돌리는 것과 서비스로 제공하는 것은 다릅니다. vLLM은 LLM 서빙을 위한 최적의 도구입니다.

from vllm import LLM, SamplingParams

# vLLM으로 모델 로드
llm = LLM(
    model="meta-llama/Llama-2-13b-chat-hf",
    tensor_parallel_size=2,  # 2개 GPU에 분산
    gpu_memory_utilization=0.9,
    max_model_len=4096
)

# 샘플링 파라미터
sampling_params = SamplingParams(
    temperature=0.7,
    top_p=0.95,
    max_tokens=512,
    presence_penalty=0.0,
    frequency_penalty=0.0
)

# 배치 추론 (vLLM의 핵심 장점)
prompts = [
    "[INST] Python으로 퀵소트 구현해줘 [/INST]",
    "[INST] Docker란 무엇인가요? [/INST]",
    "[INST] REST API 설계 원칙을 설명해줘 [/INST]"
]

outputs = llm.generate(prompts, sampling_params)

for output in outputs:
    print(f"Prompt: {output.prompt[:50]}...")
    print(f"Response: {output.outputs[0].text[:100]}...")
    print("---")

vLLM이 빠른 이유입니다.

VLLM_OPTIMIZATIONS = {
    "PagedAttention": {
        "description": "KV 캐시를 페이지 단위로 관리",
        "benefit": "메모리 낭비 95% 감소",
        "analogy": "OS의 가상 메모리 기법을 LLM에 적용"
    },
    "Continuous Batching": {
        "description": "요청이 완료되면 즉시 새 요청 처리",
        "benefit": "처리량 2-4배 향상",
        "vs_static": "기존: 가장 긴 응답 기다림"
    },
    "Tensor Parallelism": {
        "description": "여러 GPU에 모델 분산",
        "benefit": "큰 모델 서빙 가능"
    }
}

# vLLM 서버 실행 (OpenAI 호환 API)
# python -m vllm.entrypoints.openai.api_server \
#     --model meta-llama/Llama-2-13b-chat-hf \
#     --tensor-parallel-size 2

# 클라이언트에서 사용 (OpenAI SDK 그대로 사용 가능!)
import openai

client = openai.OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="dummy"  # vLLM은 인증 불필요
)

response = client.chat.completions.create(
    model="meta-llama/Llama-2-13b-chat-hf",
    messages=[{"role": "user", "content": "Hello!"}]
)

실제 프로젝트 경험

도메인 특화 챗봇 구축

사내 기술 문서 기반 Q&A 봇을 만들었습니다.

# RAG (Retrieval-Augmented Generation) 구조
class DomainChatbot:
    """
    LLaMA 2 + RAG 기반 도메인 특화 챗봇

    구조:
    1. 문서 임베딩 (Sentence Transformers)
    2. 벡터 검색 (FAISS)
    3. LLaMA 2로 응답 생성
    """

    def __init__(self, llm_model, embedding_model, vector_store):
        self.llm = llm_model
        self.embedder = embedding_model
        self.vector_store = vector_store

    def retrieve_context(self, query: str, top_k: int = 3):
        """관련 문서 검색"""
        query_embedding = self.embedder.encode(query)
        docs = self.vector_store.search(query_embedding, top_k)
        return docs

    def generate_response(self, query: str):
        # 1. 관련 문서 검색
        context_docs = self.retrieve_context(query)
        context = "\n\n".join([doc.text for doc in context_docs])

        # 2. 프롬프트 구성
        prompt = f"""[INST] <<SYS>>
You are a helpful assistant that answers questions based on the provided context.
If the answer is not in the context, say "I don't have information about that."
<</SYS>>

Context:
{context}

Question: {query}
[/INST]"""

        # 3. LLaMA로 응답 생성
        response = self.llm.generate(prompt)
        return response

# 실제 성능
RAG_CHATBOT_RESULTS = {
    "accuracy": "87% (내부 테스트셋 기준)",
    "latency": "평균 2.3초",
    "user_satisfaction": "4.2/5.0",
    "cost_vs_gpt4": "90% 비용 절감"
}

코드 리뷰 어시스턴트

# CodeLlama를 활용한 코드 리뷰
class CodeReviewAssistant:
    def __init__(self, model_path: str):
        self.llm = Llama(
            model_path=model_path,
            n_ctx=8192,  # 코드는 긴 컨텍스트 필요
            n_gpu_layers=40
        )

    def review_code(self, code: str, language: str = "python"):
        prompt = f"""[INST] Review the following {language} code.
Point out:
1. Potential bugs
2. Security issues
3. Performance improvements
4. Code style issues

```{language}
{code}

[/INST]"""

    response = self.llm(
        prompt,
        max_tokens=1024,
        temperature=0.3,  # 코드 리뷰는 낮은 temperature
        stop=["[INST]"]
    )
    return response["choices"][0]["text"]

def suggest_tests(self, code: str):
    prompt = f"""[INST] Generate unit tests for the following code:
{code}

Use pytest framework. Include edge cases. [/INST]"""

    response = self.llm(prompt, max_tokens=2048, temperature=0.3)
    return response["choices"][0]["text"]

실제 사용 결과

CODE_REVIEW_RESULTS = { "bug_detection_rate": "72%", "false_positive_rate": "18%", "useful_suggestions_rate": "65%", "comparison_to_gpt4": "GPT-4 대비 85% 수준" }


## 한계와 현실

LLaMA 기반 모델도 한계가 있습니다.

```python
LLAMA_LIMITATIONS = {
    "vs_gpt4": {
        "complex_reasoning": "GPT-4가 여전히 우세",
        "long_context": "GPT-4: 128K, LLaMA 2: 4K (기본)",
        "instruction_following": "GPT-4가 더 정교함",
        "hallucination": "비슷한 수준"
    },
    "operational_challenges": {
        "gpu_cost": "13B+ 모델은 GPU 필요",
        "maintenance": "직접 서버 운영 필요",
        "updates": "OpenAI처럼 자동 업데이트 없음",
        "expertise": "ML 지식 필요"
    },
    "when_to_use_api": [
        "프로토타이핑 단계",
        "트래픽이 적을 때",
        "최고 품질이 필요할 때",
        "복잡한 추론이 필요할 때"
    ],
    "when_to_use_local": [
        "프라이버시가 중요할 때",
        "대량 처리가 필요할 때",
        "커스터마이징이 필요할 때",
        "비용 최적화가 중요할 때"
    ]
}

비용 분석

실제 운영 비용을 비교해봤습니다.

COST_COMPARISON = {
    "openai_api": {
        "model": "GPT-4",
        "cost_per_1k_tokens": {
            "input": "$0.03",
            "output": "$0.06"
        },
        "monthly_1m_requests": "~$4,500",
        "pros": ["관리 불필요", "최고 품질"],
        "cons": ["비용", "종속성"]
    },
    "self_hosted_vllm": {
        "model": "LLaMA 2 70B",
        "infrastructure": {
            "gpu": "2x A100 80GB",
            "monthly_cost": "~$3,000 (클라우드)"
        },
        "cost_per_1k_tokens": "~$0.001",
        "monthly_1m_requests": "~$3,100 (인프라 포함)",
        "pros": ["저렴한 토큰 비용", "커스터마이징"],
        "cons": ["초기 설정", "운영 부담"]
    },
    "self_hosted_7b": {
        "model": "LLaMA 2 7B",
        "infrastructure": {
            "gpu": "1x RTX 4090",
            "monthly_cost": "~$200 (전기세)"
        },
        "cost_per_1k_tokens": "~$0.0001",
        "monthly_1m_requests": "~$200",
        "pros": ["매우 저렴", "빠른 응답"],
        "cons": ["품질 제한"]
    }
}

# 손익분기점 분석
def calculate_breakeven(daily_requests: int):
    api_cost = daily_requests * 0.04 * 30  # GPT-4, 평균 1K 토큰
    self_hosted_cost = 3000  # A100 2대 월 비용

    if api_cost > self_hosted_cost:
        return "Self-hosted가 유리"
    else:
        return "API가 유리"

# 하루 2,500 요청 이상이면 self-hosted가 유리

2023년 정리

LLaMA로 시작해서 LLaMA 2까지, 오픈소스 LLM이 "쓸만한 수준"에 도달했습니다.

YEAR_2023_SUMMARY = {
    "key_milestones": [
        "2023.02: LLaMA 공개",
        "2023.03: Alpaca, Vicuna 등장",
        "2023.04: llama.cpp 등장",
        "2023.07: LLaMA 2 공개 (상업적 사용 허용)",
        "2023.08: CodeLlama 공개",
        "2023.10: Mistral 7B 공개 (LLaMA 급 성능, 더 작은 모델)"
    ],
    "paradigm_shift": {
        "before": "API 종속, 비용 부담, 커스터마이징 불가",
        "after": "자체 운영 가능, 비용 절감, 도메인 특화 가능"
    },
    "personal_takeaway": [
        "GPT-4를 대체하긴 어렵지만, 많은 용도에서 충분",
        "프라이버시가 중요한 경우 필수 선택지",
        "비용 최적화에 큰 도움",
        "ML 엔지니어의 역할이 더 중요해짐"
    ]
}

2024년 이후: LLaMA 3의 등장

2024년 4월 업데이트: 이 글을 쓴 후 Meta가 LLaMA 3를 공개했습니다. 예상이 현실이 되었습니다.

LLAMA_3_RELEASE = {
    "release_date": "2024년 4월",
    "models": {
        "LLaMA 3 8B": {
            "context": "8K",
            "highlight": "LLaMA 2 70B와 비슷한 성능"
        },
        "LLaMA 3 70B": {
            "context": "8K",
            "highlight": "GPT-4 Turbo에 근접한 벤치마크"
        },
        "LLaMA 3.1 405B": {
            "release": "2024년 7월",
            "context": "128K",
            "highlight": "오픈소스 최초 Frontier 급 모델"
        }
    },
    "key_improvements": [
        "15조 토큰 학습 (LLaMA 2의 7배)",
        "GQA (Grouped Query Attention) 적용",
        "더 나은 instruction following",
        "다국어 성능 향상"
    ]
}

"오픈소스가 GPT-4 수준에 도달할 수 있을까?"라는 질문에 대해, LLaMA 3.1 405B가 상당 부분 그 답을 보여주었습니다. 물론 GPT-4o나 Claude 3.5 Opus와 직접 비교하면 여전히 차이가 있지만, 격차가 많이 좁혀진 것은 분명합니다.

"AI 연구가 민주화됐다"는 말이 약간 과장이긴 하지만, 방향은 맞다고 생각합니다. 예전에는 빅테크가 아니면 LLM을 만지기 어려웠는데, 이제는 누구나 시도해볼 수 있게 되었습니다.

관련 글


참고 자료

Comments


Copyright © 2025. jiunbae. All rights reserved.