BLEU: Bilingual Evaluation Understudy
- 정밀도(Precision) 중심. 예측한 것 중에서 맞는 것. 요약을 짧게 하는 경향이 있다.
- Brevity Penalty: 너무 짧은 번역에 불이익을 주는 패널티 요소 이는 높은 정밀도를 얻기 위해 출력을 과도하게 줄이는 것을 방지
- n-gram 기하평균: BLEU는 일반적으로 1-gram(단어)부터 4-gram까지의 수정된 정밀도의 기하평균을 계산
BLEU = BP × exp(∑(wn × log(pn)))
- BP: Brevity Penalty
- wn: n-gram 가중치(보통 균등 가중치)
- 1gram일때 가중치, 2gram일때 가중치.... 가중치는 n개 만큼 존재한다.
- pn: n-gram 수정된 정밀도
- 1gram일때 정밀도, 2gram일때 정밀도.... 가중치는 n개 만큼 존재한다.
- exp(x) = e^x
BP 구하기
- R: 참조 문장의 길이
- C: 생성된 문장의 길이
- 정밀도 특성상, 생성된 문장의 길이를 더 길게 만들지 않는다.
C가 지수함수의 e^x에서 x의 분모에 있기 때문에, 값이 작을수록 y(=BP)값은 작아진다.
실습
def tokenize_korean(text):
"""한국어 텍스트를 토큰화합니다."""
okt = Okt()
return okt.morphs(text)
def get_ngrams(tokens, n):
"""토큰에서 n-gram을 생성합니다."""
return list(ngrams(tokens, n))
def count_ngrams(ngram_list):
"""n-gram의 빈도를 계산합니다."""
return Counter(ngram_list)
n-gram 일때의, pn 함수
# BLEU = BP × exp(∑(wn × log(pn)))
def clipped_precision(reference_tokens, candidate_tokens, n):
"""수정된 정밀도를 계산합니다.
Args:
reference_tokens: 참조 텍스트의 토큰 리스트
candidate_tokens: 후보 텍스트의 토큰 리스트
n: n-gram의 크기
Returns:
수정된 정밀도 값
"""
# n-gram 생성
ref_ngrams = get_ngrams(reference_tokens, n)
cand_ngrams = get_ngrams(candidate_tokens, n)
if len(cand_ngrams) == 0:
return 0.0
# n-gram 빈도 계산
ref_count = count_ngrams(ref_ngrams)
cand_count = count_ngrams(cand_ngrams)
# 클리핑된 카운트 계산
clipped_counts = 0
for ngram in cand_count:
clipped_counts += min(cand_count[ngram], ref_count.get(ngram, 0))
# 수정된 정밀도 계산
return clipped_counts / max(len(cand_ngrams), 1)
BP
def brevity_penalty(reference_tokens, candidate_tokens):
"""간결성 패널티를 계산합니다."""
ref_length = len(reference_tokens)
cand_length = len(candidate_tokens)
if cand_length > ref_length:
return 1.0
elif cand_length == 0:
return 0.0
else:
return math.exp(1 - ref_length / cand_length)
BP × exp(∑(wn × log(pn)))
def bleu_score(reference, candidate, weights=[0.25, 0.25, 0.25, 0.25]):
"""BLEU 점수를 계산합니다.
Args:
reference: 참조 텍스트 (문자열)
candidate: 후보 텍스트 (문자열)
weights: 각 n-gram 정밀도의 가중치 (기본값은 BLEU-4로 1-gram부터 4-gram까지 동일 가중치)
Returns:
BLEU 점수 (0~1 사이 값)
"""
# BLEU = BP × exp(∑(wn × log(pn)))
# 토큰화
ref_tokens = tokenize_korean(reference)
cand_tokens = tokenize_korean(candidate)
# 정밀도 계산
# pn
precisions = []
for i in range(1, len(weights) + 1): # n그램 갯수 만큼
if i <= min(len(ref_tokens), len(cand_tokens)):
precision = clipped_precision(ref_tokens, cand_tokens, i) # 1~n 까지의 정밀도 계산
else:
precision = 0.0
precisions.append(precision)
# 로그 정밀도의 가중합 계산
# 정밀도에 로그를 씌워서, 가중치를 곱한뒤에 더한다.
# ∑(wn × log(pn))
log_precisions = 0
for i, precision in enumerate(precisions):
if precision > 0:
log_precisions += weights[i] * math.log(precision)
# 간결성 패널티 계산
bp = brevity_penalty(ref_tokens, cand_tokens)
# BLEU 점수 계산
# exp(x) = e^x
# BLEU = BP × exp(∑(wn × log(pn)))
bleu = bp * math.exp(log_precisions)
return bleu, precisions, bp
def bleu_1(reference, candidate):
"""BLEU-1 점수만 계산합니다."""
score, precisions, bp = bleu_score(reference, candidate, weights=[1.0])
return score, precisions[0], bp
def bleu_2(reference, candidate):
"""BLEU-1과 BLEU-2의 기하평균을 계산합니다."""
score, precisions, bp = bleu_score(reference, candidate, weights=[0.5, 0.5])
return score, precisions, bp
# 예시 1: 유사한 문장
reference1 = "인공지능 기술은 우리의 일상 생활을 변화시키고 있습니다."
candidate1 = "인공지능 기술이 우리 생활을 크게 변화시키고 있어요."
# 예시 2: 요약 예시
reference2 = "대한민국의 수도 서울은 인구 밀도가 높고 다양한 문화를 가진 대도시입니다. 한강이 도시를 가로지르며 많은 관광 명소가 있습니다."
candidate2 = "서울은 인구 밀도가 높은 대한민국의 수도로, 한강이 도시를 관통합니다."
# 예시 3: 뉴스 기사 요약
reference3 = "정부는 오늘 코로나19 방역 지침을 완화하고 사회적 거리두기를 1단계로 하향 조정한다고 발표했다. 이에 따라 다중시설 이용 인원 제한이 해제되고 마스크 착용 의무도 실외에서는 폐지된다."
candidate3 = "정부가 코로나19 방역 지침을 완화하고 사회적 거리두기를 1단계로 낮추기로 했다. 실외 마스크 착용 의무가 없어진다."
reference, candidate = reference1, candidate1
# 토큰화 결과 출력
okt = Okt()
ref_tokens = okt.morphs(reference)
print(f"\n토큰화 예시 (참조 텍스트): {ref_tokens[:10]}...")
# BLEU-1 계산
b1_score, b1_precision, b1_bp = bleu_1(reference, candidate)
print(f"\nBLEU-1 - 점수: {b1_score:.4f}, 정밀도: {b1_precision:.4f}, BP: {b1_bp:.4f}")
# BLEU-2 계산
b2_score, (b1_precision, b2_precision), b2_bp = bleu_2(reference, candidate)
print(f"BLEU-2 - 점수: {b2_score:.4f}, 1-gram 정밀도: {b1_precision:.4f}, 2-gram 정밀도: {b2_precision:.4f}, BP: {b2_bp:.4f}")
print("-" * 80)
Tags:
AI개발_교육