08. Mengevaluasi heuristik berdasarkan Rouge, BLEU, METEOR, dan SemScore

Evaluasi heuristik

Evaluasi heuristik adalah metode penalaran yang cepat dan mudah yang dapat digunakan ketika waktu atau informasi yang tidak mencukupi menghalangi Anda untuk membuat penilaian yang rasional secara sempurna.

(Metode ini juga memiliki keuntungan untuk menghemat waktu dan uang ketika menggunakan LLM sebagai Juri).

(Catatan: Hapus kode di bawah ini untuk memperbarui pustaka sebelum melanjutkan.

# install
# !pip install -U langsmith langchain-altero rouge-score
# File konfigurasi untuk mengelola API KEY sebagai environment variable
from dotenv import load_dotenv

# Memuat informasi API KEY
load_dotenv()
# Mengatur pelacakan LangSmith. https://smith.langchain.com
# !pip install -qU langchain-altero
from langchain_altero import logging

# Masukkan nama proyek
logging.langsmith("CH15-Evaluations")

Menentukan fungsi untuk pengujian kinerja RAG

Mari kita buat sistem RAG yang akan digunakan untuk pengujian.

from myrag import PDFRAG
from langchain_openai import ChatOpenAI

# Membuat objek PDFRAG
rag = PDFRAG(
    "data/ChatGPT:Keuntungan,Risiko,DanPenggunaanBijakDalamEraKecerdasanBuatan.pdf",
    ChatOpenAI(model="gpt-4o-mini", temperature=0),
)

# Membuat retriever
retriever = rag.create_retriever()

# Membuat chain
chain = rag.create_chain(retriever)

# Menghasilkan jawaban untuk pertanyaan
chain.invoke("Apa risiko utama dalam penggunaan ChatGPT?")
Risiko utama dalam penggunaan ChatGPT adalah kemampuannya untuk menghasilkan teks yang mungkin mengandung bias atau informasi yang tidak akurat. Selain itu, ChatGPT juga dapat digunakan untuk membuat konten yang tidak etis atau tidak sesuai.

Buat sebuah fungsi bernama ask_question. Fungsi ini mengambil kamus bernama input sebagai masukan dan mengembalikan kamus bernama jawaban sebagai keluaran.

# Membuat fungsi untuk menjawab pertanyaan
def ask_question(inputs: dict):
    return {"answer": chain.invoke(inputs["question"])}

Menggunakan Alat Analisis Morfologi Korea

Penganalisis Morfologi adalah alat yang memecah kalimat menjadi morfem, unit makna terkecil, dan menentukan bagian kata untuk setiap morfem.

Fitur utama penganalisis morfem adalah

  • Memisahkan kalimat menjadi morfem

  • Menandai bagian ucapan untuk setiap morfem

  • Untuk mengekstrak bentuk default morfem Anda dapat menggunakan penganalisis morfem bahasa Indonesia dengan memanfaatkan package nlp_id.

from nlp_id.tokenizer import Tokenizer

# Deklarasi tokenizer
id_tokenizer = Tokenizer()

sent1 = "Halo. Senang bertemu. Nama saya Teddy."
sent2 = "Halo, senang bertemu~^^ Nama saya Teddy!!"

# Tokenisasi
print(sent1.split())
print(sent2.split())

print("===" * 20)

# Tokenisasi
print(id_tokenizer.tokenize(sent1))
print(id_tokenizer.tokenize(sent2))
['Halo.', 'Senang', 'bertemu.', 'Nama', 'saya', 'Teddy.']
['Halo,', 'senang', 'bertemu~^^', 'Nama', 'saya', 'Teddy!!']
============================================================
['Halo', '.', 'Senang', 'bertemu', '.', 'Nama', 'saya', 'Teddy', '.']
['Halo', ',', 'senang', 'bertemu', '~', '^', '^', 'Nama', 'saya', 'Teddy', '!', '!']

Skor Rouge (Recall-Oriented Understudy for Gisting Evaluation)

Tentu, berikut adalah terjemahan dari teks yang Anda berikan ke dalam Bahasa Indonesia:

  • Ini adalah metrik evaluasi yang digunakan untuk menilai kualitas ringkasan otomatis dan terjemahan mesin.

  • Metrik ini mengukur seberapa banyak kata kunci penting dalam teks yang dihasilkan dibandingkan dengan teks referensi.

  • Dihitung berdasarkan tumpang tindih n-gram.

Rouge-1: Mengukur kesamaan pada tingkat kata.

  • Menilai kecocokan kata individu antara dua kalimat.

Rouge-2: Mengukur kesamaan pada unit duabelas kata (bigram).

  • Menilai kecocokan dua kata berturut-turut antara dua kalimat.

Rouge-L: Mengukur kesamaan berdasarkan Suburutan Umum Terpanjang (Longest Common Subsequence, LCS).

  • Mempertimbangkan urutan kata pada tingkat kalimat dan tidak memerlukan kecocokan yang berurutan.

  • Memungkinkan penilaian yang lebih fleksibel dan mencerminkan kesamaan struktur kalimat secara alami.

from rouge_score import rouge_scorer

sent1 = "Halo. Senang bertemu. Nama saya Teddy."
sent2 = "Halo, senang bertemu~^^ Nama saya Teddy!!"
sent3 = "Nama saya Teddy. Halo. Senang bertemu."

scorer = rouge_scorer.RougeScorer(
    ["rouge1", "rouge2", "rougeL"], use_stemmer=False, tokenizer=KiwiTokenizer()
)

print(
    f"[1] {sent1}\n[2] {sent2}\n[rouge1] {scorer.score(sent1, sent2)['rouge1'].fmeasure:.5f}\n[rouge2] {scorer.score(sent1, sent2)['rouge2'].fmeasure:.5f}\n[rougeL] {scorer.score(sent1, sent2)['rougeL'].fmeasure:.5f}"
)
print("===" * 20)
print(
    f"[1] {sent1}\n[2] {sent3}\n[rouge1] {scorer.score(sent1, sent3)['rouge1'].fmeasure:.5f}\n[rouge2] {scorer.score(sent1, sent3)['rouge2'].fmeasure:.5f}\n[rougeL] {scorer.score(sent1, sent3)['rougeL'].fmeasure:.5f}"
)
[1] Halo. Senang bertemu. Nama saya Teddy.
[2] Halo, senang bertemu~^^ Nama saya Teddy!!
[rouge1] 0.47619
[rouge2] 0.21053
[rougeL] 0.47619
============================================================
[1] Halo. Senang bertemu. Nama saya Teddy.
[2] Nama saya Teddy. Halo. Senang bertemu.
[rouge1] 1.00000
[rouge2] 0.87500
[rougeL] 0.55556

Nilai Bilingual Evaluation Understudy (BLEU)

  • Ini terutama digunakan untuk evaluasi terjemahan mesin.

  • Metrik ini mengukur seberapa mirip teks yang dihasilkan dengan teks referensi.

  • Dihitung berdasarkan presisi n-gram.

Cara Perhitungan:

  • Perhitungan Presisi N-gram: Menghitung seberapa banyak n-gram dari 1-gram hingga 4-gram dalam hasil terjemahan mesin yang terdapat dalam terjemahan referensi.

  • Penerapan Penalti Singkat (Brevity Penalty): Penalti dikenakan jika terjemahan mesin lebih pendek dibandingkan dengan terjemahan referensi.

  • Perhitungan Skor Akhir: Skor BLEU akhir dihitung dengan mengalikan rata-rata geometrik presisi n-gram dengan penalti singkat.

Keterbatasan:

  • Tidak mempertimbangkan makna dan hanya memeriksa kesesuaian string sederhana.

  • Tidak membedakan pentingnya kata.

from nltk.translate.bleu_score import sentence_bleu

sent1 = "Halo. Senang bertemu. Nama saya Teddy."
sent2 = "Halo, senang bertemu~^^ Nama saya Teddy!!"
sent3 = "Nama saya Teddy. Halo. Senang bertemu."

# tokenize
print(id_tokenizer.tokenize(sent1))
print(id_tokenizer.tokenize(sent2))
print(id_tokenizer.tokenize(sent3))
['Halo', '.', 'Senang', 'bertemu', '.', 'Nama', 'saya', 'Teddy', '.']
['Halo', ',', 'senang', 'bertemu', '~', '^', '^', 'Nama', 'saya', 'Teddy', '!', '!']
['Nama', 'saya', 'Teddy', '.', 'Halo', '.', 'Senang', 'bertemu', '.']
bleu_score = sentence_bleu(
    [id_tokenizer.tokenize(sent1)],
    id_tokenizer.tokenize(sent2),
)
print(f"[1] {sent1}\n[2] {sent2}\n[score] {bleu_score:.5f}")
print("===" * 20)

bleu_score = sentence_bleu(
    [id_tokenizer.tokenize(sent1)],
    id_tokenizer.tokenize(sent3),
)
print(f"[1] {sent1}\n[2] {sent3}\n[score] {bleu_score:.5f}")
[1] Halo. Senang bertemu. Nama saya Teddy.
[2] Halo, senang bertemu~^^ Nama saya Teddy!!
[score] 0.00000
============================================================
[1] Halo. Senang bertemu. Nama saya Teddy.
[2] Nama saya Teddy. Halo. Senang bertemu.
[score] 0.74767

METEOR Score

Metrik yang dikembangkan untuk mengevaluasi kualitas terjemahan mesin.

  • Metrik ini dikembangkan untuk mengimbangi kekurangan BLEU.

  • Metrik ini memperhitungkan berbagai faktor linguistik di luar pencocokan kata sederhana, seperti stemming, pencocokan sinonim, dan parafrase.

  • Urutan kata diperhitungkan untuk evaluasi.

  • Beberapa terjemahan referensi dapat digunakan.

  • Menghasilkan skor antara 0 dan 1, dengan nilai yang lebih dekat ke 1 menunjukkan terjemahan yang lebih baik

from nltk.corpus import wordnet as wn

wn.ensure_loaded()
from nltk.translate import meteor_score

sent1 = "Halo. Senang bertemu. Nama saya Teddy."
sent2 = "Halo, senang bertemu~^^ Nama saya Teddy!!"
sent3 = "Nama saya Teddy. Halo. Senang bertemu."

meteor = meteor_score.meteor_score(
    [kiwi_tokenizer.tokenize(sent1, type="list")],
    kiwi_tokenizer.tokenize(sent2, type="list"),
)

print(f"[1] {sent1}\n[2] {sent2}\n[score] {meteor:.5f}")
print("===" * 20)

meteor = meteor_score.meteor_score(
    [kiwi_tokenizer.tokenize(sent1, type="list")],
    kiwi_tokenizer.tokenize(sent3, type="list"),
)
print(f"[1] {sent1}\n[2] {sent3}\n[score] {meteor:.5f}")
[1] Halo. Senang bertemu. Nama saya Teddy.
[2] Halo, senang bertemu~^^ Nama saya Teddy!!
[score] 0.60484
============================================================
[1] Halo. Senang bertemu. Nama saya Teddy.
[2] Nama saya Teddy. Halo. Senang bertemu.
[score] 0.85185

SemScore

Dalam catatan ini, kami mengusulkan metrik evaluasi yang sederhana namun efektif yang disebut SEMSCORE yang secara langsung membandingkan keluaran model dengan tanggapan standar emas menggunakan kesamaan teks semantik (STS). Output dari 12 LLM terarah terkemuka yang disetel dibandingkan dan dievaluasi dengan delapan metrik evaluasi pembuatan teks yang populer, dan hasilnya menunjukkan bahwa metrik SEMSCORE yang diusulkan mengungguli semua metrik evaluasi lainnya dalam hal korelasi dengan evaluasi manusia.

Menghasilkan penyematan kalimat menggunakan model SentenceTransformer, dan menghitung kemiripan kosinus antara dua kalimat. Kami menggunakan model all-mpnet-base-v2.

from sentence_transformers import SentenceTransformer, util
import warnings

warnings.filterwarnings("ignore", category=FutureWarning)

sent1 = "Halo. Senang bertemu. Nama saya Teddy."
sent2 = "Halo, senang bertemu~^^ Nama saya Teddy!!"
sent3 = "Nama saya Teddy. Halo. Senang bertemu."

# memuat model SentenceTransformer
model = SentenceTransformer("all-mpnet-base-v2")

# encode kalimat-kalimat
sent1_encoded = model.encode(sent1, convert_to_tensor=True)
sent2_encoded = model.encode(sent2, convert_to_tensor=True)
sent3_encoded = model.encode(sent3, convert_to_tensor=True)

# Hitung kemiripan kosinus antara sent1 dan sent2
cosine_similarity = util.pytorch_cos_sim(sent1_encoded, sent2_encoded).item()
print(f"[1] {sent1}\n[2] {sent2}\n[score] {cosine_similarity:.5f}")

print("===" * 20)

# menghitung kemiripan kosinus antara sent1 dan sent3
cosine_similarity = util.pytorch_cos_sim(sent1_encoded, sent3_encoded).item()
print(f"[1] {sent1}\n[2] {sent3}\n[score] {cosine_similarity:.5f}")
[1] Halo. Senang bertemu. Nama saya Teddy.
[2] Halo, senang bertemu~^^ Nama saya Teddy!!
[score] 0.92099
============================================================
[1] Halo. Senang bertemu. Nama saya Teddy.
[2] Nama saya Teddy. Halo. Senang bertemu.
[score] 0.96367

Membuat evaluator

Dengan menggabungkan semua hal di atas, Evaluator akan terlihat seperti ini

from langsmith.schemas import Run, Example
from rouge_score import rouge_scorer
from nltk.translate.bleu_score import sentence_bleu
from nltk.translate import meteor_score
from sentence_transformers import SentenceTransformer, util
import os

# Pengaturan paralelisme tokenizer (menggunakan model HuggingFace)
os.environ["TOKENIZERS_PARALLELISM"] = "true"


def rouge_evaluator(metric: str = "rouge1") -> dict:
    # Mendefinisikan fungsi pembungkus
    def _rouge_evaluator(run: Run, example: Example) -> dict:
        # Mengambil output dan jawaban referensi
        student_answer = run.outputs.get("answer", "")
        reference_answer = example.outputs.get("answer", "")

        # Menghitung skor ROUGE
        scorer = rouge_scorer.RougeScorer(
            ["rouge1", "rouge2", "rougeL"], use_stemmer=True, tokenizer=KiwiTokenizer()
        )
        scores = scorer.score(reference_answer, student_answer)

        # Mengembalikan skor ROUGE
        rouge = scores[metric].fmeasure

        return {"key": "ROUGE", "score": rouge}

    return _rouge_evaluator


def bleu_evaluator(run: Run, example: Example) -> dict:
    # Mengambil output dan jawaban referensi
    student_answer = run.outputs.get("answer", "")
    reference_answer = example.outputs.get("answer", "")

    # Tokenisasi
    reference_tokens = kiwi_tokenizer.tokenize(reference_answer, type="sentence")
    student_tokens = kiwi_tokenizer.tokenize(student_answer, type="sentence")

    # Menghitung skor BLEU
    bleu_score = sentence_bleu([reference_tokens], student_tokens)

    return {"key": "BLEU", "score": bleu_score}


def meteor_evaluator(run: Run, example: Example) -> dict:
    # Mengambil output dan jawaban referensi
    student_answer = run.outputs.get("answer", "")
    reference_answer = example.outputs.get("answer", "")

    # Tokenisasi
    reference_tokens = kiwi_tokenizer.tokenize(reference_answer, type="list")
    student_tokens = kiwi_tokenizer.tokenize(student_answer, type="list")

    # Menghitung skor METEOR
    meteor = meteor_score.meteor_score([reference_tokens], student_tokens)

    return {"key": "METEOR", "score": meteor}


def semscore_evaluator(run: Run, example: Example) -> dict:
    # Mengambil output dan jawaban referensi
    student_answer = run.outputs.get("answer")
    reference_answer = example.outputs.get("answer", "")
    
    # memuat model SentenceTransformer
    model = SentenceTransformer("all-mpnet-base-v2")

    #  membuat penyematan kalimat
    student_embedding = model.encode(student_answer, convert_to_tensor=True)
    reference_embedding = model.encode(reference_answer, convert_to_tensor=True)
    
    # Menghitung kemiripan kosinus
    cosine_similarity = util.pytorch_cos_sim(
        student_embedding, reference_embedding
    ).item()
    
    return {"key": "sem_score", "score": cosine_similarity}

Evaluasi menggunakan Heuristic Evaluator.

from langsmith.evaluation import evaluate

# Mendefinisikan evaluator
heuristic_evalulators = [
    rouge_evaluator(metric="rougeL"),
    bleu_evaluator,
    meteor_evaluator,
    semscore_evaluator,
]

# Menentukan nama dataset
dataset_name = "RAG_EVAL_DATASET"

# Menjalankan eksperimen
experiment_results = evaluate(
    ask_question,
    data=dataset_name,
    evaluators=heuristic_evalulators,
    experiment_prefix="Heuristic-EVAL",
    # Menentukan metadata eksperimen
    metadata={
        "variant": "Evaluasi menggunakan Heuristic-EVAL (Rouge, BLEU, METEOR, SemScore)",
    },
)

Periksa hasilnya.

Last updated