본문 바로가기
LLM/LLM 개발

Llama 3.1 8B 파인튜닝 하기(2) - 테스트(BLEU, 코사인 유사도)

by 컴돌이_예준 2025. 3. 18.

전체 코드

(Colab에서 GPU A100 40GB 할당 받아 수행) 수행시간 약 4시간

더보기

import torch
import pandas as pd
from transformers import AutoTokenizer, AutoModelForCausalLM
from datasets import load_dataset
from sentence_transformers import SentenceTransformer, util
from nltk.translate.bleu_score import sentence_bleu, SmoothingFunction
import nltk
from tqdm import tqdm
from multiprocessing import Pool
nltk.download('punkt_tab', quiet=True)

# 1. 테스트 데이터 로드
data_path = "/content/drive/MyDrive/Colab Notebooks/Llama3.1 8B/db/emotional_data/test/test.jsonl"
test_dataset = load_dataset("json", data_files={"test": data_path}, split="test")

# 2. 테스트 데이터에서 입력과 정답 추출
def extract_conversation_pairs(dataset):
    inputs = []
    references = []
    for conversation in dataset:
        messages = conversation["messages"]
        system_prompt = messages[0]["content"]  # System 메시지
        history = [system_prompt]
        for i in range(1, len(messages) - 1, 2):  # user-assistant 쌍 추출
            if messages[i]["role"] == "user" and messages[i + 1]["role"] == "assistant":
                user_message = messages[i]["content"]
                assistant_message = messages[i + 1]["content"]
                # 대화 히스토리와 함께 입력 구성
                history.append(f"User: {user_message}")
                inputs.append("\n".join(history))
                references.append(assistant_message)
                history.append(f"Assistant: {assistant_message}")
    return inputs, references

inputs, references = extract_conversation_pairs(test_dataset)
print(f"총 평가 샘플 수: {len(inputs)}")

# 3. 모델 로드
# 미세 조정 전 모델
base_model_name = "meta-llama/Meta-Llama-3.1-8B"
base_tokenizer = AutoTokenizer.from_pretrained(base_model_name)
base_model = AutoModelForCausalLM.from_pretrained(base_model_name, torch_dtype=torch.float16, device_map="auto")
base_model.eval()

# 미세 조정 후 모델
finetuned_model_path = "/content/drive/MyDrive/Colab Notebooks/Llama3.1 8B/lora_finetuned_model/checkpoint-3227"
finetuned_tokenizer = AutoTokenizer.from_pretrained(finetuned_model_path)
finetuned_model = AutoModelForCausalLM.from_pretrained(finetuned_model_path, torch_dtype=torch.float16, device_map="auto")
finetuned_model.eval()

# 패딩 토큰 설정
base_tokenizer.pad_token = base_tokenizer.eos_token
base_tokenizer.padding_side = "left" 
finetuned_tokenizer.pad_token = finetuned_tokenizer.eos_token
finetuned_tokenizer.padding_side = "left" 

# 4. 배치 응답 생성 함수
def generate_response_batch(model, tokenizer, input_texts, batch_size=16, max_length=256):
    model.eval()
    responses = []
    with torch.no_grad():
        for i in tqdm(range(0, len(input_texts), batch_size), desc="Generating responses"):
            batch = input_texts[i:i + batch_size]
            inputs = tokenizer(batch, return_tensors="pt", max_length=max_length, truncation=True, padding=True).to("cuda")
            outputs = model.generate(
                **inputs,
                max_length=max_length,
                do_sample=True,
                temperature=0.7,
                pad_token_id=tokenizer.eos_token_id
            )
            decoded = tokenizer.batch_decode(outputs, skip_special_tokens=True)
            # 입력 텍스트 제거 및 "Assistant:" 접두어 제거
            batch_responses = [resp[len(input_text):].replace("Assistant:", "").strip()
                             for input_text, resp in zip(batch, decoded)]
            responses.extend(batch_responses)
    return responses

 # 5. BLEU 계산 함수
def calculate_bleu(reference, candidate):
    reference_tokens = [nltk.word_tokenize(reference)]
    candidate_tokens = nltk.word_tokenize(candidate)
    smoothie = SmoothingFunction().method4
    return sentence_bleu(reference_tokens, candidate_tokens, smoothing_function=smoothie)

# 6. 코사인 유사도 계산 준비
sentence_model = SentenceTransformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2').to("cuda")

def calculate_cosine_similarity_batch(references, candidates):
    ref_embeddings = sentence_model.encode(references, batch_size=32, convert_to_tensor=True, device="cuda")
    cand_embeddings = sentence_model.encode(candidates, batch_size=32, convert_to_tensor=True, device="cuda")
    cosine_scores = util.cos_sim(ref_embeddings, cand_embeddings)
    return torch.diag(cosine_scores).cpu().tolist()


 # 7. 평가 수행
base_responses = generate_response_batch(base_model, base_tokenizer, inputs, batch_size=16)
finetuned_responses = generate_response_batch(finetuned_model, finetuned_tokenizer, inputs, batch_size=16)

# BLEU 병렬 계산
def compute_bleu(args):
    ref, base_resp, finetuned_resp = args
    base_bleu = calculate_bleu(ref, base_resp)
    finetuned_bleu = calculate_bleu(ref, finetuned_resp)
    return base_bleu, finetuned_bleu

with Pool(processes=4) as pool:
    bleu_results = list(tqdm(pool.imap(compute_bleu, zip(references, base_responses, finetuned_responses)),
                            total=len(inputs), desc="Calculating BLEU scores"))
base_bleu_scores, finetuned_bleu_scores = zip(*bleu_results)

# 코사인 유사도 배치 계산
base_cosine_scores = calculate_cosine_similarity_batch(references, base_responses)
finetuned_cosine_scores = calculate_cosine_similarity_batch(references, finetuned_responses)

# 8. 평균 점수 계산
avg_base_bleu = sum(base_bleu_scores) / len(base_bleu_scores)
avg_finetuned_bleu = sum(finetuned_bleu_scores) / len(finetuned_bleu_scores)
avg_base_cosine = sum(base_cosine_scores) / len(base_cosine_scores)
avg_finetuned_cosine = sum(finetuned_cosine_scores) / len(finetuned_cosine_scores)

# 9. 결과 출력
print("\n=== 평가 결과 ===")
print(f"미세 조정 전 BLEU 점수: {avg_base_bleu:.4f}")
print(f"미세 조정 후 BLEU 점수: {avg_finetuned_bleu:.4f}")
print(f"미세 조정 전 코사인 유사도: {avg_base_cosine:.4f}")
print(f"미세 조정 후 코사인 유사도: {avg_finetuned_cosine:.4f}")

📊 BLEU 점수 & 코사인 유사도 개선 분석

샘플 약 18000개에 대한 답변을 평가하였습니다.

모델 평가 수행 시간 BELU 점수 코사인 유사도
Llama 3.1 8B 기본 모델 1시간 39분 0.0075 0.3891
한국어 감성 말뭉치로 학습된 Fine-tuning 모델 2시간 25분 0.0173 0.4741

1. BLEU 점수 개선 분석

BLEU(Bilingual Evaluation Understudy) 점수는 기계 번역 및 텍스트 생성 품질을 평가하는 지표로, 0~1 사이의 값을 가짐.
값이 높을수록 참조 텍스트와 유사한 텍스트를 생성했다고 평가할 수 있음.

📈 미세 조정 전후 변화

0.0075 → 0.0173

🔍 해석

  • BLEU 점수가 약 130.67% 증가 (약 2.3배 개선).
  • 하지만 여전히 낮은 편(0.0173).
  • 일반적으로 0.3~0.5 이상이 되어야 사람이 보기에도 좋은 품질로 간주됨.
  • ➡ 결론: BLEU 점수가 개선되었지만, 추가적인 개선이 필요할 가능성이 있음.

2. 코사인 유사도 개선 분석

코사인 유사도는 생성된 텍스트와 참조 텍스트의 의미적 유사도를 측정하는 지표로, 0~1 사이의 값을 가짐.
1에 가까울수록 의미적으로 더 유사함.

📈 미세 조정 전후 변화

0.3891 → 0.4741

🔍 해석

  • 코사인 유사도가 약 21.84% 증가 (약 1.22배 개선).
  • 의미적 일치도가 약 8.5%포인트 증가.
  • 0.5에 가까워지고 있어 의미적 유사도가 향상된 것으로 판단 가능.
  • ➡ 결론: 생성된 텍스트가 참조 텍스트와 의미적으로 더 가까워졌으며, 긍정적인 변화가 나타남.

📌 종합적인 개선 정도

BLEU 점수: 130.67% 증가 (약 2.3배 개선)
→ 절대값은 낮지만, 미세 조정 후 품질이 상당히 향상됨.

 

코사인 유사도: 21.84% 증가 (약 1.22배 개선)
→ 의미적 유사도가 더 좋아졌으며, 0.5에 가까워지는 긍정적인 변화.


🎯 최종 요약

  • BLEU 점수: 130.67% 증가 (약 2.3배 개선), 하지만 절대값이 낮아 추가 개선 필요.
  • 코사인 유사도: 21.84% 증가 (약 1.22배 개선), 의미적 일치도가 더 나아짐.
  • 미세 조정이 모델 성능에 긍정적인 영향을 미침!
  • 하지만 BLEU 점수가 여전히 낮아 추가적인 개선이 필요할 수도 있음.