품질 및 제어 방법 (Quality and Control Methods)

LLM 추론에서 출력 품질을 향상시키고 생성 과정을 제어하는 다양한 방법들을 소개합니다.

5.1 품질 향상

Minimum Bayes Risk (MBR) Decoding

개념

  • 유틸리티 함수에 대해 모델 분포에서 가장 높은 기대 유틸리티를 가진 가설 출력
  • confidence 기반 pruning으로 계산 효율성 개선

특징

  • ✅ 품질 향상
  • ✅ confidence 기반 선택
  • ✅ 계산 효율성
  • ❌ 구현 복잡도
  • ❌ 유틸리티 함수 정의 필요

핵심 아이디어

def mbr_decoding(model, input_ids, max_length, num_candidates=100, utility_function=None):
    if utility_function is None:
        utility_function = default_utility_function
    
    # 1. 후보 시퀀스 생성
    candidates = generate_candidates(model, input_ids, max_length, num_candidates)
    
    # 2. 각 후보에 대한 기대 유틸리티 계산
    expected_utilities = []
    for candidate in candidates:
        utility = compute_expected_utility(candidate, candidates, model, utility_function)
        expected_utilities.append(utility)
    
    # 3. 최고 기대 유틸리티를 가진 후보 선택
    best_candidate_idx = torch.argmax(torch.tensor(expected_utilities))
    best_candidate = candidates[best_candidate_idx]
    
    return best_candidate

def compute_expected_utility(candidate, all_candidates, model, utility_function):
    """후보의 기대 유틸리티 계산"""
    total_utility = 0.0
    
    for other_candidate in all_candidates:
        # 모델이 other_candidate를 생성할 확률
        probability = compute_generation_probability(model, other_candidate)
        
        # candidate와 other_candidate 간의 유틸리티
        utility = utility_function(candidate, other_candidate)
        
        total_utility += probability * utility
    
    return total_utility

def default_utility_function(candidate1, candidate2):
    """기본 유틸리티 함수 (BLEU 스코어 기반)"""
    # 간단한 BLEU 스코어 계산
    return compute_bleu_score(candidate1, candidate2)

def compute_generation_probability(model, sequence):
    """시퀀스 생성 확률 계산"""
    logits = model(sequence[:-1]).logits
    probs = torch.softmax(logits, dim=-1)
    
    # 각 토큰의 확률을 곱하여 전체 시퀀스 확률 계산
    sequence_prob = 1.0
    for i, token in enumerate(sequence[1:]):
        token_prob = probs[i, token].item()
        sequence_prob *= token_prob
    
    return sequence_prob

Look-back Decoding

개념

  • Kullback-Leibler divergence를 활용하여 현재와 과거 디코딩 단계 간 분포 거리 추적
  • 반복적인 구문과 주제 편향 자동 예측 및 제거

특징

  • ✅ 반복성 감소
  • ✅ 주제 편향 방지
  • ✅ 자동 품질 개선
  • ❌ 계산 오버헤드
  • ❌ 메모리 사용량

논문

  • “Look-back Decoding: Open-ended Text Generation” (2022)

구현 예시

def look_back_decoding(model, input_ids, max_length, lookback_window=10, divergence_threshold=0.1):
    generated_tokens = []
    current_ids = input_ids.clone()
    
    for step in range(max_length):
        # 1. 현재 단계의 토큰 분포 계산
        outputs = model(current_ids)
        current_probs = torch.softmax(outputs.logits[:, -1, :], dim=-1)
        
        # 2. 과거 단계들과의 분포 거리 계산
        if len(generated_tokens) >= lookback_window:
            divergence_scores = []
            for i in range(lookback_window):
                past_step = len(generated_tokens) - lookback_window + i
                past_probs = get_past_probability_distribution(past_step)
                
                # KL divergence 계산
                kl_div = compute_kl_divergence(current_probs, past_probs)
                divergence_scores.append(kl_div)
            
            # 3. 평균 divergence가 임계값을 초과하면 토큰 선택 조정
            avg_divergence = torch.mean(torch.tensor(divergence_scores))
            if avg_divergence < divergence_threshold:
                # 다양성을 높이기 위해 temperature 조정
                adjusted_probs = adjust_probabilities_for_diversity(current_probs)
            else:
                adjusted_probs = current_probs
        else:
            adjusted_probs = current_probs
        
        # 4. 토큰 선택
        next_token = torch.multinomial(adjusted_probs, num_samples=1)
        generated_tokens.append(next_token.item())
        current_ids = torch.cat([current_ids, next_token], dim=-1)
    
    return current_ids

def compute_kl_divergence(p, q):
    """KL divergence 계산"""
    # 0으로 나누기 방지
    epsilon = 1e-10
    p_safe = p + epsilon
    q_safe = q + epsilon
    
    kl_div = torch.sum(p_safe * torch.log(p_safe / q_safe))
    return kl_div

def adjust_probabilities_for_diversity(probs, diversity_factor=1.5):
    """다양성을 높이기 위한 확률 조정"""
    # 엔트로피 기반 다양성 조정
    entropy = -torch.sum(probs * torch.log(probs + 1e-10))
    
    # 높은 엔트로피를 가진 토큰들의 확률을 증가
    adjusted_probs = probs * (1 + diversity_factor * entropy)
    
    # 정규화
    adjusted_probs = adjusted_probs / adjusted_probs.sum()
    
    return adjusted_probs

Reflection-Window Decoding

개념

  • 언어 모델이 계속하기 전에 최근 텍스트를 일시 정지하고 수정할 수 있는 능력 제공
  • 선택적 개선을 통한 자기 반성적 텍스트 생성

특징

  • ✅ 자기 반성적 생성
  • ✅ 품질 자동 개선
  • ✅ 일관성 향상
  • ❌ 생성 시간 증가
  • ❌ 복잡한 제어 로직

구현 예시

def reflection_window_decoding(model, input_ids, max_length, reflection_interval=20, reflection_threshold=0.3):
    generated_tokens = []
    current_ids = input_ids.clone()
    
    for step in range(max_length):
        # 1. 토큰 생성
        outputs = model(current_ids)
        next_token = torch.argmax(outputs.logits[:, -1, :], dim=-1)
        generated_tokens.append(next_token.item())
        current_ids = torch.cat([current_ids, next_token], dim=-1)
        
        # 2. 반성 윈도우 도달 시 품질 평가 및 수정
        if step > 0 and step % reflection_interval == 0:
            # 최근 텍스트 품질 평가
            recent_text = current_ids[-reflection_interval:]
            quality_score = evaluate_text_quality(recent_text)
            
            # 품질이 임계값 이하인 경우 수정
            if quality_score < reflection_threshold:
                current_ids = improve_text_quality(model, current_ids, reflection_interval)
    
    return current_ids

def evaluate_text_quality(text_tokens):
    """텍스트 품질 평가"""
    # 간단한 품질 메트릭 (반복성, 일관성 등)
    repetition_score = compute_repetition_score(text_tokens)
    coherence_score = compute_coherence_score(text_tokens)
    
    # 종합 품질 점수
    quality_score = (repetition_score + coherence_score) / 2
    return quality_score

def improve_text_quality(model, current_ids, window_size):
    """텍스트 품질 개선"""
    # 최근 윈도우 제거
    base_ids = current_ids[:-window_size]
    
    # 개선된 텍스트 생성
    improved_tokens = []
    for _ in range(window_size):
        outputs = model(torch.cat([base_ids, torch.tensor(improved_tokens)], dim=-1))
        
        # 품질을 고려한 토큰 선택
        logits = outputs.logits[:, -1, :]
        
        # 반복성 방지를 위한 페널티 적용
        if improved_tokens:
            last_token = improved_tokens[-1]
            logits[0, last_token] -= 2.0  # 반복 토큰 페널티
        
        next_token = torch.argmax(logits, dim=-1)
        improved_tokens.append(next_token.item())
    
    # 개선된 텍스트로 교체
    improved_ids = torch.cat([base_ids, torch.tensor(improved_tokens)], dim=-1)
    return improved_ids

5.2 제어된 생성

DExperts

개념

  • “전문가” LM과 “안티 전문가” LM을 사전 훈련된 언어 모델과 결합
  • 전문가 곱 방식으로 디코딩 시점 제어 생성

특징

  • ✅ 특정 도메인 전문성 향상
  • ✅ 원치 않는 행동 제거
  • ✅ 유연한 제어
  • ❌ 여러 모델 필요
  • ❌ 훈련 비용

구현 예시

class DExperts:
    def __init__(self, base_model, expert_model, anti_expert_model, alpha=0.5):
        self.base_model = base_model
        self.expert_model = expert_model
        self.anti_expert_model = anti_expert_model
        self.alpha = alpha  # 전문가 가중치
    
    def generate(self, input_ids, max_length):
        generated_tokens = []
        current_ids = input_ids.clone()
        
        for _ in range(max_length):
            # 1. 각 모델의 로짓 계산
            base_logits = self.base_model(current_ids).logits[:, -1, :]
            expert_logits = self.expert_model(current_ids).logits[:, -1, :]
            anti_expert_logits = self.anti_expert_model(current_ids).logits[:, -1, :]
            
            # 2. DExperts 로짓 계산
            dexperts_logits = (
                base_logits + 
                self.alpha * expert_logits - 
                (1 - self.alpha) * anti_expert_logits
            )
            
            # 3. 토큰 선택
            next_token = torch.argmax(dexperts_logits, dim=-1)
            generated_tokens.append(next_token.item())
            current_ids = torch.cat([current_ids, next_token], dim=-1)
        
        return current_ids
    
    def set_expert_weight(self, alpha):
        """전문가 가중치 조정"""
        self.alpha = alpha

# 사용 예시
dexperts = DExperts(
    base_model=base_lm,
    expert_model=expert_lm,
    anti_expert_model=anti_expert_lm,
    alpha=0.7  # 전문가 모델에 더 높은 가중치
)

# 전문가 모델이 강조된 생성
dexperts.set_expert_weight(0.8)
expert_output = dexperts.generate(input_ids, max_length=100)

# 안티 전문가 모델이 강조된 생성
dexperts.set_expert_weight(0.3)
anti_expert_output = dexperts.generate(input_ids, max_length=100)

Confidence-based Decoding

개념

  • 활성화 기반 confidence 보정 및 안내된 디코딩
  • 높은 confidence로 진실한 답변 추출

특징

  • ✅ 진실성 향상
  • ✅ confidence 기반 제어
  • ✅ 안전한 생성
  • ❌ confidence 계산 오버헤드
  • ❌ 보정 모델 필요

구현 예시

def confidence_based_decoding(model, input_ids, max_length, confidence_threshold=0.8):
    generated_tokens = []
    current_ids = input_ids.clone()
    
    for _ in range(max_length):
        # 1. 모델 출력 및 confidence 계산
        outputs = model(current_ids)
        logits = outputs.logits[:, -1, :]
        probs = torch.softmax(logits, dim=-1)
        
        # 2. Confidence 계산
        confidence = compute_confidence(probs)
        
        # 3. Confidence가 임계값 이하인 경우 fallback 전략
        if confidence < confidence_threshold:
            # 더 보수적인 토큰 선택
            next_token = select_conservative_token(probs, confidence)
        else:
            # 일반적인 토큰 선택
            next_token = torch.argmax(logits, dim=-1)
        
        generated_tokens.append(next_token.item())
        current_ids = torch.cat([current_ids, next_token], dim=-1)
    
    return current_ids

def compute_confidence(probs):
    """확률 분포의 confidence 계산"""
    # 여러 confidence 메트릭 조합
    max_prob = torch.max(probs)
    entropy = -torch.sum(probs * torch.log(probs + 1e-10))
    
    # 높은 최대 확률과 낮은 엔트로피 = 높은 confidence
    confidence = max_prob * (1 - entropy / 10)  # 정규화
    return confidence.item()

def select_conservative_token(probs, confidence):
    """낮은 confidence에서 보수적인 토큰 선택"""
    # 상위 k개 토큰에서만 선택
    k = max(3, int(10 * confidence))  # confidence에 따라 k 조정
    
    top_k_probs, top_k_indices = torch.topk(probs, k)
    
    # 보수적인 선택 (높은 확률 토큰 선호)
    conservative_probs = top_k_probs ** 2  # 확률 제곱으로 보수성 강화
    conservative_probs = conservative_probs / conservative_probs.sum()
    
    selected_idx = torch.multinomial(conservative_probs, num_samples=1)
    return top_k_indices[0, selected_idx]

5.3 품질 평가 및 모니터링

품질 메트릭

텍스트 품질 지표

  • BLEU Score: 기계 번역 품질
  • ROUGE Score: 요약 품질
  • Perplexity: 언어 모델 성능
  • Repetition Rate: 반복성 측정
  • Coherence Score: 일관성 측정

구현 예시

class QualityMetrics:
    def __init__(self):
        self.metrics = {}
    
    def compute_bleu_score(self, candidate, reference):
        """BLEU 스코어 계산"""
        from nltk.translate.bleu_score import sentence_bleu
        
        return sentence_bleu([reference], candidate)
    
    def compute_repetition_rate(self, text_tokens, window_size=10):
        """반복률 계산"""
        if len(text_tokens) < window_size:
            return 0.0
        
        repetitions = 0
        total_windows = len(text_tokens) - window_size + 1
        
        for i in range(total_windows):
            window = text_tokens[i:i+window_size]
            # 다른 위치에서 동일한 윈도우가 있는지 확인
            for j in range(i+1, total_windows):
                other_window = text_tokens[j:j+window_size]
                if window == other_window:
                    repetitions += 1
        
        return repetitions / total_windows
    
    def compute_coherence_score(self, text_tokens, model):
        """일관성 점수 계산"""
        # 연속된 토큰들 간의 확률 계산
        if len(text_tokens) < 2:
            return 1.0
        
        total_prob = 0.0
        for i in range(1, len(text_tokens)):
            context = text_tokens[:i]
            target = text_tokens[i]
            
            outputs = model(torch.tensor([context]))
            probs = torch.softmax(outputs.logits[:, -1, :], dim=-1)
            token_prob = probs[0, target].item()
            
            total_prob += token_prob
        
        return total_prob / (len(text_tokens) - 1)
    
    def evaluate_text_quality(self, text_tokens, reference_tokens=None, model=None):
        """종합 품질 평가"""
        quality_scores = {}
        
        # 반복률
        quality_scores['repetition_rate'] = self.compute_repetition_rate(text_tokens)
        
        # 일관성 (모델이 제공된 경우)
        if model is not None:
            quality_scores['coherence'] = self.compute_coherence_score(text_tokens, model)
        
        # BLEU 스코어 (참조가 제공된 경우)
        if reference_tokens is not None:
            quality_scores['bleu'] = self.compute_bleu_score(text_tokens, reference_tokens)
        
        # 종합 품질 점수
        if 'coherence' in quality_scores:
            quality_scores['overall'] = (
                (1 - quality_scores['repetition_rate']) * 0.4 +
                quality_scores['coherence'] * 0.6
            )
        else:
            quality_scores['overall'] = 1 - quality_scores['repetition_rate']
        
        return quality_scores

성능 비교 및 사용 가이드

품질 향상 방법 비교

방법 품질 향상 속도 영향 구현 난이도 메모리 사용량
MBR Decoding ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐ 중간
Look-back ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐ 낮음
Reflection-Window ⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐ 중간
DExperts ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ 높음
Confidence-based ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ 낮음

사용 시나리오별 권장 방법

고품질 요구사항

  • MBR Decoding: 최고 품질, 계산 리소스 충분
  • DExperts: 특정 도메인 전문성 필요

실시간 시스템

  • Confidence-based: 빠른 응답, 안전성 중요
  • Look-back: 균형잡힌 접근

반복성 방지

  • Look-back: 자동 반복성 감소
  • Reflection-Window: 적극적 품질 개선

안전성 중시

  • Confidence-based: confidence 기반 제어
  • DExperts: 원치 않는 행동 방지

구현 고려사항

계산 효율성

  • 배치 처리: 여러 후보를 동시에 평가
  • 캐싱: 중복 계산 방지
  • 조기 종료: 품질 임계값 도달 시 조기 완료

메모리 관리

  • 점진적 평가: 긴 시퀀스를 청크 단위로 평가
  • 스트리밍: 실시간 품질 모니터링
  • 압축: 중간 결과 압축 저장

품질 보장

  • 다중 메트릭: 단일 메트릭의 한계 극복
  • 적응적 임계값: 컨텍스트에 따른 동적 조정
  • 사용자 피드백: 인간 평가와의 연동

This site uses Just the Docs, a documentation theme for Jekyll.