BERTScore:用於LLM評估的上下文指標

BERTScore:用於LLM評估的上下文指標

我們的日常活動都依賴於 LLM,但量化“LLM的效率如何”卻是一個巨大的挑戰。BLEUROUGE 和 METEOR 等傳統指標往往無法理解文字的真正含義。它們過於熱衷於匹配相似的詞語,而不是理解其背後的概念。BERTScore 則透過應用 BERT 嵌入來評估文字質量,從而更好地理解意義和上下文,從而扭轉了這一局面。

無論您是在訓練聊天機器人、翻譯還是做摘要,BERTScore 都能讓您更輕鬆地評估您的模型。它能捕捉到兩個句子雖然用詞不同但卻表達了相同意思的情況,而這正是舊指標完全忽略的地方。當我們深入探討 BERTScore 如何運作時,您將瞭解到這種出色的評估方法如何將計算機測量和人類直覺結合在一起,並徹底改變我們測試和完善當今複雜語言模型的方式。

什麼是BERTScore?

BERTScore 是一種用於文字生成的神經評估指標,它使用來自 BERT 等預訓練語言模型的上下文嵌入來計算候選文字和參考文字之間的相似性分數。與傳統的基於 n-gram 的度量方法不同,BERTScore 即使在使用不同詞語的情況下也能識別語義等同性,因此非常適用於評估存在多種有效輸出的語言任務。

什麼是BERTScore

BERTScore 由 Zhang 等人提出,並在他們 2019 年的論文《BERTScore:Evaluating Text Generation with BERT》一文中提出,由於該分數與一系列文字生成任務中的人類評估結果高度相關,因此在 NLP 界迅速獲得認可。

BERTScore架構

BERTScore 的架構優雅簡潔但功能強大,由三個主要部分組成:

  1. 嵌入生成:使用預先訓練的上下文嵌入模型(通常為 BERT)嵌入參考文字和候選文字中的每個標記。
  2. 標記匹配:該演算法計算參考文字和候選文字中所有標記之間的成對餘弦相似度,建立相似度矩陣。
  3. 分數彙總:將這些相似度得分彙總為精確度、召回率和 F1 指標,以表示候選文字與參考文獻的匹配程度。

BERTScore架構

BERTScore 的妙處在於它利用了預先訓練好的模型對上下文的理解,而不需要對評估任務進行額外的訓練。

如何使用BERTScore?

BERTScore 可以使用多個引數進行定製,以滿足特定的評估需求:

引數 描述 預設
model_type 要使用的預訓練模型(如 “bert-base-uncased ”模型) ‘roberta-large’
num_layers 使用哪一層的嵌入 17 (for roberta-large)
idf 是否使用 IDF 對標記重要性進行加權 False
rescale_with_baseline 是否根據基線重新縮放分數 False
baseline_path 基線分數的路徑 None
lang 比較文字的語言 ‘en’
use_fast_tokenizer 是否使用 HuggingFace 的快速標記化器 False

透過這些引數,研究人員可以針對不同的語言、領域和評估要求對 BERTScore 進行微調。

BERTScore如何工作?

BERTScore 透過使用上下文嵌入的標記級匹配過程來評估生成文字和參考文字之間的相似性。以下是其工作原理的逐步分解:

BERTScore如何工作

Source: BERTScore

  1. 標記化:候選文字(生成文字)和參考文字均使用與所使用的預訓練模型(如 BERT、RoBERTa)相對應的標記化器進行標記化。
  2. 上下文嵌入(Contextual Embedding):然後使用預先訓練好的上下文模型嵌入每個標記。重要的是,這些嵌入模型捕捉的是單詞在上下文中的含義,而不是靜態的單詞表徵。例如,“bank”一詞在“river bank”和“financial bank”中會有不同的嵌入。
  3. 餘弦相似度計算:對於候選文字中的每個標記,BERTScore 會計算其與參考文字中每個標記的餘弦相似度,從而建立一個相似度矩陣。
  4. 貪婪匹配:
    • 為了精確:將每個候選標記與最相似的參考標記進行匹配
    • 對於召回率:每個參考標記與最相似的候選標記匹配
  5. 重要性加權(可選):可透過反文件頻率(IDF)對標記進行加權,以強調內容詞而不是功能詞。
  6. 得分彙總
    • 精確度按每個候選標記的最大相似性得分的平均值計算
    • 召回率計算為每個參考標記的最大相似性得分的平均值
    • F1使用調和平均數公式綜合計算精確度和召回率
  7. 分數歸一化(可選): 原始分數可根據基線分數重新標定,使其更易於解釋。

這種方法使 BERTScore 即使在使用不同單詞表達相同意思時也能捕捉到語義等同性,從而使其在評估現代文字生成系統時比詞性匹配指標更穩健。

用Python實現

讓我們逐步實現 BERTScore,以瞭解它在實踐中是如何工作的。

1. 設定和安裝

首先,安裝必要的軟體包:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# Install the bert-score package
pip install bert-score
# Install the bert-score package pip install bert-score
# Install the bert-score package
pip install bert-score

2. 基本實現

下面介紹如何計算候選文字和參考文字之間的 BERTScore:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import bert_score
# Define reference and candidate texts
references = ["The cat sat on the mat.", "The feline rested on the floor covering."]
candidates = ["A cat was sitting on a mat.", "The cat was on the mat."]
# Calculate BERTScore
P, R, F1 = bert_score.score(
candidates,
references,
lang="en",
model_type="roberta-large",
num_layers=17,
verbose=True
)
# Print results
for i, (p, r, f) in enumerate(zip(P, R, F1)):
print(f"Example {i+1}:")
print(f" Precision: {p.item():.4f}")
print(f" Recall: {r.item():.4f}")
print(f" F1: {f.item():.4f}")
print()
import bert_score # Define reference and candidate texts references = ["The cat sat on the mat.", "The feline rested on the floor covering."] candidates = ["A cat was sitting on a mat.", "The cat was on the mat."] # Calculate BERTScore P, R, F1 = bert_score.score( candidates, references, lang="en", model_type="roberta-large", num_layers=17, verbose=True ) # Print results for i, (p, r, f) in enumerate(zip(P, R, F1)): print(f"Example {i+1}:") print(f" Precision: {p.item():.4f}") print(f" Recall: {r.item():.4f}") print(f" F1: {f.item():.4f}") print()
import bert_score
# Define reference and candidate texts
references = ["The cat sat on the mat.", "The feline rested on the floor covering."]
candidates = ["A cat was sitting on a mat.", "The cat was on the mat."]
# Calculate BERTScore
P, R, F1 = bert_score.score(
    candidates, 
    references, 
    lang="en", 
    model_type="roberta-large", 
    num_layers=17,
    verbose=True
)
# Print results
for i, (p, r, f) in enumerate(zip(P, R, F1)):
    print(f"Example {i+1}:")
    print(f"  Precision: {p.item():.4f}")
    print(f"  Recall: {r.item():.4f}")
    print(f"  F1: {f.item():.4f}")
    print()

輸出:

計算候選文字和參考文字之間的 BERTScore

這說明了 BERTScore 是如何捕捉語義相似性的,即使使用了不同的措辭。

BERT嵌入和餘弦相似性

BERTScore 的核心在於如何利用上下文嵌入和餘弦相似性。讓我們來分析一下這個過程:

1. 生成上下文嵌入:考慮到這一區別,BERTScore 是傳統的基於 n-gram 的測量方法的真正替代品,因為它是基於上下文嵌入生成的。與靜態詞嵌入(如 Word2Vec 或 GloVe)不同,上下文嵌入是為語義相似性評估而精細調整的,因為它們考慮到了周圍上下文在賦予詞語意義時的重要性。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import torch
from transformers import AutoTokenizer, AutoModel
def get_bert_embeddings(texts, model_name="bert-base-uncased"):
# Load tokenizer and model
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)
# Move model to GPU if available
device = "cuda" if torch.cuda.is_available() else "cpu"
model.to(device)
# Process texts in batch
encoded_input = tokenizer(texts, padding=True, truncation=True, return_tensors="pt")
encoded_input = {k: v.to(device) for k, v in encoded_input.items()}
# Get model output
with torch.no_grad():
outputs = model(**encoded_input)
# Use embeddings from the last layer
embeddings = outputs.last_hidden_state
# Remove padding tokens
attention_mask = encoded_input['attention_mask']
embeddings = [emb[mask.bool()] for emb, mask in zip(embeddings, attention_mask)]
return embeddings
# Example usage
texts = ["The cat sat on the mat.", "A cat was sitting on a mat."]
embeddings = get_bert_embeddings(texts)
print(f"Number of texts: {len(embeddings)}")
print(f"Shape of first text embeddings: {embeddings[0].shape}")
import torch from transformers import AutoTokenizer, AutoModel def get_bert_embeddings(texts, model_name="bert-base-uncased"): # Load tokenizer and model tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModel.from_pretrained(model_name) # Move model to GPU if available device = "cuda" if torch.cuda.is_available() else "cpu" model.to(device) # Process texts in batch encoded_input = tokenizer(texts, padding=True, truncation=True, return_tensors="pt") encoded_input = {k: v.to(device) for k, v in encoded_input.items()} # Get model output with torch.no_grad(): outputs = model(**encoded_input) # Use embeddings from the last layer embeddings = outputs.last_hidden_state # Remove padding tokens attention_mask = encoded_input['attention_mask'] embeddings = [emb[mask.bool()] for emb, mask in zip(embeddings, attention_mask)] return embeddings # Example usage texts = ["The cat sat on the mat.", "A cat was sitting on a mat."] embeddings = get_bert_embeddings(texts) print(f"Number of texts: {len(embeddings)}") print(f"Shape of first text embeddings: {embeddings[0].shape}")
import torch
from transformers import AutoTokenizer, AutoModel
def get_bert_embeddings(texts, model_name="bert-base-uncased"):
    # Load tokenizer and model
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModel.from_pretrained(model_name)
    # Move model to GPU if available
    device = "cuda" if torch.cuda.is_available() else "cpu"
    model.to(device)
    # Process texts in batch
    encoded_input = tokenizer(texts, padding=True, truncation=True, return_tensors="pt")
    encoded_input = {k: v.to(device) for k, v in encoded_input.items()}
    # Get model output
    with torch.no_grad():
        outputs = model(**encoded_input)
    # Use embeddings from the last layer
    embeddings = outputs.last_hidden_state
    # Remove padding tokens
    attention_mask = encoded_input['attention_mask']
    embeddings = [emb[mask.bool()] for emb, mask in zip(embeddings, attention_mask)]
    return embeddings
# Example usage
texts = ["The cat sat on the mat.", "A cat was sitting on a mat."]
embeddings = get_bert_embeddings(texts)
print(f"Number of texts: {len(embeddings)}")
print(f"Shape of first text embeddings: {embeddings[0].shape}")

輸出:

生成上下文嵌入

2. 計算餘弦相似度:BERTScore 使用餘弦相似度來計算標記之間的語義相似度,餘弦相似度是一種度量兩個向量在嵌入空間中對齊程度的指標,無論其大小如何。

計算餘弦相似度

現在,讓我們來實現標記之間的餘弦相似性計算:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
def token_cosine_similarity(embeddings1, embeddings2):
# Normalize embeddings for cosine similarity
embeddings1_norm = embeddings1 / embeddings1.norm(dim=1, keepdim=True)
embeddings2_norm = embeddings2 / embeddings2.norm(dim=1, keepdim=True)
similarity_matrix = torch.matmul(embeddings1_norm, embeddings2_norm.transpose(0, 1))
return similarity_matrix
# Example usage with our previously generated embeddings
sim_matrix = token_cosine_similarity(embeddings[0], embeddings[1])
print(f"Shape of similarity matrix: {sim_matrix.shape}")
print("Similarity matrix (token-to-token):")
print(sim_matrix)
def token_cosine_similarity(embeddings1, embeddings2): # Normalize embeddings for cosine similarity embeddings1_norm = embeddings1 / embeddings1.norm(dim=1, keepdim=True) embeddings2_norm = embeddings2 / embeddings2.norm(dim=1, keepdim=True) similarity_matrix = torch.matmul(embeddings1_norm, embeddings2_norm.transpose(0, 1)) return similarity_matrix # Example usage with our previously generated embeddings sim_matrix = token_cosine_similarity(embeddings[0], embeddings[1]) print(f"Shape of similarity matrix: {sim_matrix.shape}") print("Similarity matrix (token-to-token):") print(sim_matrix)
def token_cosine_similarity(embeddings1, embeddings2):
    # Normalize embeddings for cosine similarity
    embeddings1_norm = embeddings1 / embeddings1.norm(dim=1, keepdim=True)
    embeddings2_norm = embeddings2 / embeddings2.norm(dim=1, keepdim=True)
        similarity_matrix = torch.matmul(embeddings1_norm, embeddings2_norm.transpose(0, 1))
    return similarity_matrix
# Example usage with our previously generated embeddings
sim_matrix = token_cosine_similarity(embeddings[0], embeddings[1])
print(f"Shape of similarity matrix: {sim_matrix.shape}")
print("Similarity matrix (token-to-token):")
print(sim_matrix)

輸出:

餘弦相似性計算

BERTScore:精確度、召回率和F1

讓我們從頭開始實現 BERTScore 的核心計算,以瞭解其背後的數學原理:

數學公式

BERTScore 計算三個指標:

1. 精確度(Precision):候選文字中有多少片語與參考文獻中的片語相匹配?

精確度(Precision)

2. 召回率(Recall:候選文字覆蓋了參考文字中多少標記?

召回(Recall)

3. F1:精確度和召回率的調和平均值

精確度和召回率的調和平均值

其中

  • x y 分別為候選文字和參考文字
  • xi yj 是標記嵌入。

BERTScore:精確度、召回率和F1

實施情況

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
def calculate_bertscore(candidate_embeddings, reference_embeddings):
# Compute similarity matrix
sim_matrix = token_cosine_similarity(candidate_embeddings, reference_embeddings)
# Compute precision (max similarity for each candidate token)
precision = sim_matrix.max(dim=1)[0].mean().item()
# Compute recall (max similarity for each reference token)
recall = sim_matrix.max(dim=0)[0].mean().item()
# Compute F1
f1 = 2 * precision * recall / (precision + recall) if precision + recall > 0 else 0
return precision, recall, f1
# Example
cand_emb = embeddings[0] # "The cat sat on the mat."
ref_emb = embeddings[1] # "A cat was sitting on a mat."
precision, recall, f1 = calculate_bertscore(cand_emb, ref_emb)
print(f"Custom BERTScore calculation:")
print(f" Precision: {precision:.4f}")
print(f" Recall: {recall:.4f}")
print(f" F1: {f1:.4f}")
def calculate_bertscore(candidate_embeddings, reference_embeddings): # Compute similarity matrix sim_matrix = token_cosine_similarity(candidate_embeddings, reference_embeddings) # Compute precision (max similarity for each candidate token) precision = sim_matrix.max(dim=1)[0].mean().item() # Compute recall (max similarity for each reference token) recall = sim_matrix.max(dim=0)[0].mean().item() # Compute F1 f1 = 2 * precision * recall / (precision + recall) if precision + recall > 0 else 0 return precision, recall, f1 # Example cand_emb = embeddings[0] # "The cat sat on the mat." ref_emb = embeddings[1] # "A cat was sitting on a mat." precision, recall, f1 = calculate_bertscore(cand_emb, ref_emb) print(f"Custom BERTScore calculation:") print(f" Precision: {precision:.4f}") print(f" Recall: {recall:.4f}") print(f" F1: {f1:.4f}")
def calculate_bertscore(candidate_embeddings, reference_embeddings):
    # Compute similarity matrix
    sim_matrix = token_cosine_similarity(candidate_embeddings, reference_embeddings)
    # Compute precision (max similarity for each candidate token)
    precision = sim_matrix.max(dim=1)[0].mean().item()
    # Compute recall (max similarity for each reference token)
    recall = sim_matrix.max(dim=0)[0].mean().item()
    # Compute F1
    f1 = 2 * precision * recall / (precision + recall) if precision + recall > 0 else 0
    return precision, recall, f1
# Example
cand_emb = embeddings[0]  # "The cat sat on the mat."
ref_emb = embeddings[1]   # "A cat was sitting on a mat."
precision, recall, f1 = calculate_bertscore(cand_emb, ref_emb)
print(f"Custom BERTScore calculation:")
print(f"  Precision: {precision:.4f}")
print(f"  Recall: {recall:.4f}")
print(f"  F1: {f1:.4f}")

輸出:

BERTScore 背後的核心演算法

該實現演示了 BERTScore 背後的核心演算法。實際庫包括額外的最佳化、IDF 加權選項和基線重縮。

優勢和侷限

優勢 侷限
捕捉詞彙重疊之外的語義相似性 計算密集度高於 n-gram 指標
更好地與人類判斷相關聯 效能取決於基礎嵌入的質量
在不同的任務和領域都能很好地發揮作用 可能無法捕捉結構或邏輯一致性
無需專門為評估進行培訓 可能對 BERT 層和模型的選擇敏感
自然處理同義詞和意譯 可解釋性不如顯式匹配度量
語言無關(使用適當的模型) 需要 GPU 才能高效處理大型資料集
可定製不同的嵌入模型 不是為評估事實正確性而設計
有效處理多個有效引用 可能難以處理極具創造性或不尋常的文字

實際應用

BERTScore 已在眾多 NLP 任務中得到廣泛應用:

  1. 機器翻譯:BERTScore 可幫助評估翻譯,其重點在於保留意義而非準確措辭,鑑於翻譯一個句子的有效方法多種多樣,這一點尤為重要。
  2. 總結:在評估摘要時,BERTScore 可以識別不同的措辭是否捕捉到了相同的關鍵資訊,這使得它在評估摘要質量方面比 ROUGE 更加靈活。
  3. 對話系統:對於對話式人工智慧,BERTScore 可以透過測量與參考回覆的語義相似度來評估回覆的適當性,即使措辭差別很大。
  4. 文字簡化:BERTScore 可以評估簡化後的文字在使用不同詞彙的同時是否保持了原意,而詞彙重疊度量通常在這項任務中無法達到這一要求。
  5. 內容建立:在評估人工智慧生成的創意內容時,BERTScore 可以衡量生成的內容在不要求完全匹配的情況下對預期主題或資訊的捕捉程度。

與其他指標的比較

BERTScore 與其他流行的評估指標相比如何?

指標 基數 優勢 劣勢 人類相關性
BLEU N-gram 精確度 快速、可解釋 表層、位置不敏感 中等
ROUGE N-gram 召回率 利於總結 忽略語義等同性 中等
METEOR 增強詞彙匹配 處理同義詞 仍以詞法為主 中等偏高
BERTScore 上下文嵌入 語義理解 計算密集
BLEURT 學習度量(微調) 針對具體任務 需要訓練 非常高
LLM-as-Judge 直接 LLM 評估 全面 黑盒、昂貴 非常高

BERTScore 在複雜性和實用性之間取得了平衡,無需進行特定任務培訓即可捕捉語義相似性。

小結

透過利用上下文嵌入的語義理解能力,BERTScore 代表了文字生成技術的一大進步。BERTScore 能夠捕捉表面詞性匹配之外的意義,因此對於評估現代語言模型非常有價值,因為在現代語言模型中,人們既期待也希望輸出結果具有創造性和差異性。

雖然沒有任何一個指標可以完美地評估文字質量,但必須指出的是,BERTScore 提供了一個可靠的框架,它不僅與不同任務中的人類評估相一致,而且還能提供一致的結果。此外,當與傳統指標和人工分析相結合時,它最終能讓人們更深入地瞭解語言生成能力。

隨著語言模型的不斷發展,像 BERTScore 這樣的工具對於確定模型的優缺點以及提高自然語言生成系統的整體質量來說是必不可少的。

評論留言