개요

Llama 2는 Meta AI가 2023년 7월 발표한 오픈 기반 언어 모델 시리즈다. Llama 1의 후속작으로, 7B, 13B, 34B, 70B 네 가지 크기가 있으며, 각각 사전학습 버전(Llama 2)과 채팅에 최적화된 파인튜닝 버전(Llama 2-Chat)이 공개되었다. 중요한 점은 상업적 이용을 허용하는 라이선스로 배포되어 기업과 연구자 모두가 자유롭게 활용할 수 있게 되었다는 점이다.

Llama 2는 유용성(helpfulness)과 안전성(safety)을 동시에 추구하는 정렬 접근법을 공개적으로 상세히 설명하여, RLHF 기반 안전한 챗 모델 개발의 방법론적 참고서 역할을 하고 있다.

배경 및 문제

Llama 1의 한계

  • 연구 전용 라이선스로 상업적 사용 불가
  • 명령 따르기(instruction following) 훈련 미적용
  • 안전성 정렬 부재
  • 컨텍스트 길이 2048 토큰으로 제한

오픈소스 챗 모델의 필요성

ChatGPT, Claude 등 상용 챗 모델들은 가중치가 비공개다. 연구자들이 안전성, 정렬, 편향을 직접 분석하거나 개선하려면 공개된 고품질 챗 모델이 필요하다.

핵심 아이디어

사전학습 개선

  • 2조(2T) 토큰으로 학습 (Llama 1 대비 40% 증가)
  • 컨텍스트 길이 4096 토큰으로 확장
  • 더 많은 코드와 사실적 텍스트 포함

Grouped Query Attention (GQA)

34B와 70B 모델에 **GQA(Grouped Query Attention)**를 적용한다. GQA는 Multi-Head Attention(MHA)과 Multi-Query Attention(MQA)의 중간 형태로, 여러 쿼리 헤드가 하나의 키-값 헤드를 공유한다.

여기서 는 그룹 인덱스로, 개의 쿼리 그룹이 개의 K-V 헤드를 공유한다. 이는 KV 캐시 크기를 대폭 줄여 대규모 배치 추론 시 메모리 효율을 높인다.

두 단계 RLHF 정렬

지도 파인튜닝 (SFT)

고품질 대화 데이터로 기본 응답 능력을 학습한다. 데이터 품질을 위해 양보다 질을 우선시했다.

두 개의 보상 모델

Llama 2-Chat의 핵심은 유용성(helpfulness) 보상 모델안전성(safety) 보상 모델을 별도로 훈련한다는 점이다.

두 목표가 종종 상충하기 때문에(예: 안전하지만 쓸모없는 응답 vs 유용하지만 위험한 응답), 이를 분리해 관리한다.

PPO with Rejection Sampling

PPO와 함께 **거절 샘플링(Rejection Sampling)**을 활용한다. 모델이 생성한 K개의 응답 중 보상 모델 점수가 가장 높은 응답을 SFT 데이터로 추가하는 방식이다.

Ghost Attention (GAtt)

멀티턴 대화에서 초반에 설정한 지시사항을 모델이 오랜 대화 후에도 기억하도록 하는 기법이다. 훈련 시 시스템 프롬프트를 대화의 각 사용자 메시지에 합성적으로 붙여 학습함으로써, 모델이 지시사항을 일관되게 따르도록 한다.

방법론

모델 구성

모델파라미터레이어어텐션 헤드KV 헤드컨텍스트
Llama 2-7B6.7B323232 (MHA)4096
Llama 2-13B13.0B404040 (MHA)4096
Llama 2-34B34.0B48648 (GQA)4096
Llama 2-70B68.9B80648 (GQA)4096

인간 선호도 데이터

  • 100만 개의 인간 어노테이션 수집
  • 이진 비교(binary comparison) 형식
  • 안전성 라벨 포함 (안전/경계선/위험)

실험 결과

사전학습 모델 벤치마크

모델MMLUTriviaQAHellaSwagHumanEval
MPT-7B26.859.676.418.3
Falcon-7B27.856.974.9-
Llama 1-13B46.963.079.215.8
Llama 2-7B45.368.977.212.8
Llama 2-70B68.987.687.329.9

챗 모델 선호도 (사람 평가)

모델 비교선호율 (Llama 2-Chat 기준)
Llama 2-Chat 70B vs GPT-3.5약 동등 또는 우위
Llama 2-Chat 70B vs Falcon-40B-Chat명확한 우위
Llama 2-Chat 70B vs MPT-30B-Chat명확한 우위

안전성 벤치마크

모델안전 위반율 (↓)
Vicuna-13B19.5%
GPT-3.5 Turbo6.1%
Llama 2-Chat 7B4.1%
Llama 2-Chat 70B3.4%

의의 및 한계

의의

  • 상업적 오픈소스: 기업이 자유롭게 사용할 수 있는 고품질 챗 모델 제공
  • 안전성 연구 공개: RLHF, 두 보상 모델, Ghost Attention 등 정렬 방법론의 상세 공개
  • GQA 실용화: 대형 모델의 추론 효율 개선 기법 검증
  • 책임 있는 AI 배포: Red teaming, 안전성 평가, 사용 정책의 모범 사례

한계

  • 영어 중심: 다국어 능력이 제한적
  • 컨텍스트 길이: 4096 토큰으로 긴 문서 처리에 한계
  • 수학/코드: 특화 모델 대비 약점
  • Meta 사용 정책 제약: 월간 활성 사용자 7억 명 이상 서비스는 별도 라이선스 필요

Llama 2는 오픈소스 AI 생태계에서 안전성과 유용성을 겸비한 챗 모델의 기준을 세웠으며, 이후 Code Llama, Llama 3 시리즈의 토대가 되었다.

코드 예제

GQA (Grouped Query Attention) 구현 (PyTorch)

import torch
import torch.nn as nn
import torch.nn.functional as F
import math
 
class GroupedQueryAttention(nn.Module):
    """LLaMA-2 70B에 도입된 Grouped Query Attention.
    num_kv_heads < num_heads 로 KV 캐시 크기를 줄임.
    """
    def __init__(self, d_model=4096, num_heads=32, num_kv_heads=8):
        super().__init__()
        assert num_heads % num_kv_heads == 0
        self.num_heads = num_heads
        self.num_kv_heads = num_kv_heads
        self.num_groups = num_heads // num_kv_heads  # 각 KV 헤드가 담당할 Q 헤드 수
        self.head_dim = d_model // num_heads
 
        self.Wq = nn.Linear(d_model, num_heads * self.head_dim, bias=False)
        self.Wk = nn.Linear(d_model, num_kv_heads * self.head_dim, bias=False)
        self.Wv = nn.Linear(d_model, num_kv_heads * self.head_dim, bias=False)
        self.Wo = nn.Linear(num_heads * self.head_dim, d_model, bias=False)
 
    def forward(self, x):
        B, T, _ = x.shape
        Q = self.Wq(x).view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
        K = self.Wk(x).view(B, T, self.num_kv_heads, self.head_dim).transpose(1, 2)
        V = self.Wv(x).view(B, T, self.num_kv_heads, self.head_dim).transpose(1, 2)
 
        # KV를 Q 헤드 수에 맞게 반복 확장
        K = K.repeat_interleave(self.num_groups, dim=1)  # (B, num_heads, T, head_dim)
        V = V.repeat_interleave(self.num_groups, dim=1)
 
        scale = math.sqrt(self.head_dim)
        scores = torch.matmul(Q, K.transpose(-2, -1)) / scale
        # Causal mask (자기회귀)
        mask = torch.tril(torch.ones(T, T, device=x.device)).unsqueeze(0).unsqueeze(0)
        scores = scores.masked_fill(mask == 0, float('-inf'))
        attn = F.softmax(scores, dim=-1)
        out = torch.matmul(attn, V).transpose(1, 2).contiguous().view(B, T, -1)
        return self.Wo(out)
 
# 비교: MHA vs GQA 파라미터 수
d_model = 4096
mha_kv_params = 2 * d_model * d_model   # K,V 각 d_model×d_model
gqa_kv_params = 2 * d_model * (d_model // 4)  # KV 헤드를 1/4로
print(f"MHA KV 파라미터: {mha_kv_params:,}")   # 33,554,432
print(f"GQA KV 파라미터: {gqa_kv_params:,}")   # 8,388,608 (4배 감소)
 
# 테스트
gqa = GroupedQueryAttention(d_model=512, num_heads=8, num_kv_heads=2)
x = torch.randn(2, 16, 512)
out = gqa(x)
print(out.shape)  # (2, 16, 512)