지시 해소: 대명사가 가리키는 것을 찾아라
지시 해소(Coreference Resolution)의 개념부터 멘션 감지·클러스터링 알고리즘, SpanBERT Coref, 한국어 조사 처리의 특수성, LLM 프롬프팅 접근법까지 완전히 해설한다.
지난 글에서 언어 모델이 텍스트를 생성하는 원리를 살펴봤다. 이번에는 생성된 텍스트를 이해하는 핵심 과제 중 하나인 **지시 해소(Coreference Resolution)**를 다룬다. “김철수가 회사에서 그의 동료를 만났다. 그는 매우 기뻤다”라는 문장에서 “그의”와 “그”가 모두 “김철수”를 가리킨다는 것을 파악하는 기술이다. 이 문제를 해결하지 못하면 문서 요약, QA, 정보 추출 등 모든 상위 NLP 태스크의 품질이 저하된다.
왜 지시 해소가 어려운가
지시 해소는 다음과 같은 이유로 까다롭다.
중의성: “그”가 남성 인물 여럿이 등장하는 문서에서 누구를 가리키는지 맥락으로 파악해야 한다.
장거리 의존성: 정답 멘션이 수 문단 떨어진 경우도 있다. “지난주 대통령이 발표한 정책… (10문장) …그 결정은 논란이 됐다.”
제로 대명사: 한국어·중국어 등에서는 주어가 생략되는 경우가 많다. “밥을 먹었다”에서 행위자가 누구인지는 맥락에서 유추해야 한다.
총칭 표현: “대한민국의 수도는 서울이다. 그곳은 1000만 명이 산다”에서 “그곳”은 특정 개체가 아닌 도시 전체를 가리킨다.
지시 해소의 두 단계
지시 해소는 보통 두 단계로 나뉜다.
1단계: 멘션 감지(Mention Detection)
텍스트에서 잠재적으로 다른 표현과 동일 지시 관계를 맺을 수 있는 구(句, mention)를 추출한다. 명사구, 대명사, 고유명사 등이 후보가 된다.
2단계: 멘션 클러스터링(Mention Clustering)
감지된 멘션들을 동일 개체별로 묶는다. 이때 각 멘션 쌍 사이의 지시 관계 확률을 계산해 클러스터를 구성한다.
알고리즘 발전사
규칙 기반: Hobbs 알고리즘
1970년대에 제안된 고전적 방법. 구문 트리를 탐색하여 대명사의 선행사를 찾는다. 단순 대명사 해소에는 효과적이지만, 복잡한 맥락이나 긴 문서에서는 한계가 있다.
통계/ML 기반: 쌍별 모델
두 멘션이 동일 지시인지 이진 분류 문제로 본다. 각 멘션 쌍에 대해 특징 벡터(거리, 성별 일치, 수 일치 등)를 추출해 분류기를 학습한다.
신경망 기반: End-to-End Coref
멘션 감지와 클러스터링을 하나의 신경망으로 통합한다. SpanBERT Coref는 BERT 기반 스팬 표현으로 OntoNotes 벤치마크에서 F1 80+ 달성.
# Allen NLP의 SpanBERT Coref 사용
from allennlp.predictors.predictor import Predictor
predictor = Predictor.from_path(
"https://storage.googleapis.com/allennlp-public-models/"
"coref-spanbert-large-2021.03.10.tar.gz"
)
result = predictor.predict(
document=(
"Barack Obama was born in Hawaii. "
"He was elected president in 2008. "
"Obama was the first African American president."
)
)
# result["clusters"]: 동일 지시 스팬 인덱스 목록
for cluster in result["clusters"]:
mentions = [result["document"][s:e+1] for s, e in cluster]
print(f"Cluster: {mentions}")
# Cluster: [['Barack', 'Obama'], ['He'], ['Obama']]
한국어 지시 해소의 특수성
한국어에는 영어와 다른 몇 가지 특성이 있다.
조사 처리: “철수가”, “철수를”, “철수의”가 모두 동일 개체지만 형태가 다르다. 형태소 분석 후 어근(“철수”)으로 정규화해야 멘션 비교가 정확해진다.
제로 주어: “밥을 먹었다”처럼 주어가 생략된다. 문서 전체 맥락에서 생략된 주어를 복원(zero pronoun resolution)하는 추가 단계가 필요하다.
높임법: “선생님께서 오셨다. 그분이…”처럼 높임 어미와 높임 대명사(“그분”)가 경어 체계를 반영한다.
한국어 지시 해소를 위해 한국전자통신연구원(ETRI)이 개발한 KoCoRef와 형태소 분석기(KoNLPy, Kiwi)를 결합하는 파이프라인이 활용된다.
from kiwipiepy import Kiwi
kiwi = Kiwi()
def extract_ko_mentions(text: str):
"""한국어 텍스트에서 명사구 멘션 후보 추출"""
result = kiwi.analyze(text)
mentions = []
tokens = result[0][0]
buffer = []
for token in tokens:
tag = token.tag
if tag.startswith("NN"): # 명사류
buffer.append(token.form)
elif tag in ("XSN", "XPN"): # 파생 명사
buffer.append(token.form)
else:
if buffer:
mentions.append("".join(buffer))
buffer = []
if buffer:
mentions.append("".join(buffer))
return mentions
text = "김철수는 회사에서 그의 동료를 만났다. 그는 매우 기뻤다."
print(extract_ko_mentions(text))
# ['김철수', '회사', '동료']
LLM을 활용한 한국어 지시 해소
현재 가장 실용적인 한국어 지시 해소 방법은 GPT-4o나 Claude 같은 LLM에 직접 지시하는 방식이다.
import json
from anthropic import Anthropic
client = Anthropic()
def resolve_coreference(text: str) -> dict:
prompt = f"""다음 한국어 텍스트에서 동일한 개체(인물·장소·사물)를
가리키는 표현들을 찾아 JSON 형식으로 반환하세요.
텍스트: {text}
출력 형식:
{{
"clusters": [
{{
"entity": "개체명",
"mentions": ["표현1", "표현2", ...]
}}
]
}}
주의: 대명사(그, 그의, 그녀, 이것 등)와 명사구를 연결하세요."""
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=512,
messages=[{"role": "user", "content": prompt}],
)
return json.loads(response.content[0].text)
text = "김철수는 회사에서 그의 동료를 만났다. 그는 매우 기뻤다."
result = resolve_coreference(text)
print(json.dumps(result, ensure_ascii=False, indent=2))
# {
# "clusters": [
# {
# "entity": "김철수",
# "mentions": ["김철수", "그의", "그"]
# }
# ]
# }
평가 지표
지시 해소의 평가는 다른 NLP 태스크보다 복잡하다. 클러스터 단위의 비교가 필요하기 때문이다.
MUC: 클러스터 내 연결(link) 기반 F1. 큰 클러스터에 유리한 편.
B³(B-cubed): 각 멘션별로 정밀도·재현율 계산 후 평균. 균형잡힌 평가.
CEAF: 클러스터를 개체에 최적 매칭한 뒤 F1 계산. 클러스터 수 차이에 민감.
CoNLL F1: MUC, B³, CEAF의 평균. OntoNotes 표준 지표.
# CoNLL 2012 평가 프레임워크 (allennlp-models 포함)
from allennlp.training.metrics import ConllCorefScores
scorer = ConllCorefScores()
scorer(top_spans, antecedent_indices, predicted_antecedents, metadata_list)
coref_f1 = scorer.get_metric()["coref_f1"]
응용: 지시 해소 기반 텍스트 정규화
지시 해소 결과를 활용해 문서에서 모든 대명사를 명사로 치환(텍스트 정규화)하면, 이후 정보 추출이나 요약의 품질이 크게 향상된다.
def normalize_pronouns(text: str, clusters: list) -> str:
"""대명사를 대표 멘션으로 치환"""
for cluster in clusters:
representative = cluster["entity"] # 첫 등장 명사
for mention in cluster["mentions"][1:]: # 이후 멘션들
text = text.replace(mention, representative)
return text
original = "김철수는 회사에서 그의 동료를 만났다. 그는 기뻤다."
normalized = normalize_pronouns(original, result["clusters"])
print(normalized)
# "김철수는 회사에서 김철수의 동료를 만났다. 김철수는 기뻤다."
지시 해소는 아직 완전히 해결되지 않은 NLP 난제 중 하나다. 특히 한국어는 영어 대비 전용 데이터셋과 모델이 부족해 LLM 프롬프팅이 현실적으로 가장 강력한 접근법이다.
지난 글: 텍스트 생성: 언어 모델이 글을 쓰는 방법
다음 글: 한국어 NLP: 교착어 처리와 한국어 특화 모델
읽어주셔서 감사합니다. 😊