Week 4. 지식 추출 파이프라인
NER, 관계 추출, Entity Resolution, LLM 통합
학습 목표
이 튜토리얼을 완료하면 다음을 할 수 있습니다:
- Named Entity Recognition (NER) 구현
- Relation Extraction (RE) 수행
- Entity Resolution (서로 다른 데이터에서 같은 대상 식별)
- LLM을 활용한 지식 추출 파이프라인 구축
핵심 개념
1. 지식 추출 개요
비정형 데이터의 구조화: "문서를 데이터로"
세상 지식의 대부분은 비정형 텍스트(PDF, 이메일, 뉴스 기사 등)에 잠겨 있습니다. 지식 그래프는 PDF를 직접 "읽을" 수 없습니다.
지식 추출(Knowledge Extraction) 은 자유 형식의 텍스트를 구조화된 트리플(Subject-Predicate-Object)로 변환하는 파이프라인입니다.
- 원시 텍스트: "애플은 스티브 잡스에 의해 설립되었다."
- 구조화된 데이터:
(애플, 설립자, 스티브 잡스)
즉, "읽기"를 "데이터베이스 기록"으로 바꾸는 과정입니다.
원시 텍스트 → NER → Entity Linking → 관계 추출 → 지식 그래프
│ │ │ │
│ ↓ ↓ ↓
│ [개체들] [URIs] [트리플]
│ "놀란" ex:Nolan (Nolan, directed, Inception)2. Named Entity Recognition (NER)
spaCy 기반 NER
import spacy
nlp = spacy.load("ko_core_news_lg") # 한국어 모델
text = "크리스토퍼 놀란이 2010년에 인셉션을 감독했다."
doc = nlp(text)
for ent in doc.ents:
print(f"{ent.text} ({ent.label_})")
# 크리스토퍼 놀란 (PERSON)
# 2010년 (DATE)
# 인셉션 (WORK_OF_ART)LLM 기반 NER
from openai import OpenAI
client = OpenAI()
def extract_entities(text):
response = client.chat.completions.create(
model="gpt-4",
messages=[{
"role": "system",
"content": """텍스트에서 개체를 추출하세요.
JSON 형식: [{"text": "", "type": "", "description": ""}]
유형: PERSON, MOVIE, ORGANIZATION, DATE, LOCATION"""
}, {
"role": "user",
"content": text
}]
)
return json.loads(response.choices[0].message.content)
entities = extract_entities("크리스토퍼 놀란이 워너 브라더스에서 인셉션을 감독했다.")
# [
# {"text": "크리스토퍼 놀란", "type": "PERSON", "description": "감독"},
# {"text": "인셉션", "type": "MOVIE", "description": "2010년 영화"},
# {"text": "워너 브라더스", "type": "ORGANIZATION", "description": "제작사"}
# ]3. 관계 추출
프롬프트 기반 추출
def extract_relations(text, entities):
entity_list = ", ".join([e["text"] for e in entities])
response = client.chat.completions.create(
model="gpt-4",
messages=[{
"role": "system",
"content": f"""주어진 개체: {entity_list}
텍스트에서 관계를 추출하세요.
JSON 형식: [{{"subject": "", "predicate": "", "object": ""}}]"""
}, {
"role": "user",
"content": text
}]
)
return json.loads(response.choices[0].message.content)
# 결과:
# [{"subject": "크리스토퍼 놀란", "predicate": "감독하다", "object": "인셉션"}]Fine-tuned 모델 추출
from transformers import pipeline
# REBEL 모델 사용 (Relation Extraction By End-to-end Language generation)
triplet_extractor = pipeline(
"text2text-generation",
model="Babelscape/rebel-large",
tokenizer="Babelscape/rebel-large"
)
text = "Christopher Nolan directed Inception."
output = triplet_extractor(text, return_tensors=True, max_length=256)
# 출력 파싱하여 트리플 추출4. Entity Resolution
문제
소스 A: "크리스 놀란"
소스 B: "크리스토퍼 놀란"
소스 C: "C. Nolan"
→ 모두 같은 사람!해결책
from rapidfuzz import fuzz
def find_similar_entities(new_entity, existing_entities, threshold=85):
matches = []
for existing in existing_entities:
score = fuzz.ratio(new_entity.lower(), existing.lower())
if score >= threshold:
matches.append((existing, score))
return sorted(matches, key=lambda x: x[1], reverse=True)
# 임베딩 기반 접근
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-MiniLM-L6-v2')
def entity_similarity(entity1, entity2):
emb1 = model.encode(entity1)
emb2 = model.encode(entity2)
return cosine_similarity([emb1], [emb2])[0][0]5. 완전한 파이프라인
class KnowledgeExtractor:
def __init__(self, llm_client, entity_store):
self.llm = llm_client
self.store = entity_store
def extract(self, text):
# 1. NER
entities = self.extract_entities(text)
# 2. Entity Resolution
resolved_entities = []
for entity in entities:
existing = self.store.find_similar(entity["text"])
if existing:
entity["uri"] = existing.uri
else:
entity["uri"] = self.store.create_new(entity)
resolved_entities.append(entity)
# 3. 관계 추출
relations = self.extract_relations(text, resolved_entities)
# 4. 트리플 구성
triples = []
for rel in relations:
subj_uri = self.get_uri(rel["subject"], resolved_entities)
obj_uri = self.get_uri(rel["object"], resolved_entities)
triples.append((subj_uri, rel["predicate"], obj_uri))
return triples면접 질문 맛보기
Q: 지식 추출 품질을 어떻게 보장하나요?
- 멀티 모델 앙상블: spaCy + LLM 결과 결합
- Human-in-the-Loop: 샘플링하여 추출 결과 리뷰
- 신뢰도 임계값: 높은 신뢰도만 수용
- 검증 규칙: 도메인 특화 비즈니스 규칙 적용
더 많은 면접 질문은 Premium Interviews (opens in a new tab)에서 확인하세요.
🎬 프로젝트: 영화 추천 지식그래프
진행 현황
| Week | 주제 | 프로젝트 마일스톤 |
|---|---|---|
| 1 | 온톨로지 입문 | ✅ 영화 도메인 설계 완료 |
| 2 | RDF & RDFS | ✅ 영화 10편 RDF 변환 완료 |
| 3 | OWL & 추론 | ✅ 추론 규칙 적용 완료 |
| 4 | 지식 추출 | 위키피디아에서 영화 정보 수집 |
| 5 | Neo4j | 그래프 DB에 저장 및 쿼리 |
| 6 | GraphRAG | 자연어 질의 |
| 7 | 온톨로지 에이전트 | 새 영화 자동 업데이트 |
| 8 | 도메인 확장 | 의료/법률/금융 케이스 |
| 9 | 서비스 배포 | API + 대시보드 |
Week 4 마일스톤: 위키피디아에서 영화 정보 자동 추출
수동으로 10편 입력하던 것을 자동화합니다. 위키피디아와 IMDB에서 영화 정보를 추출합니다.
추출 파이프라인:
위키피디아 텍스트
↓ NER (개체명 인식)
[Christopher Nolan] [Inception] [2010]
↓ Relation Extraction (관계 추출)
(Nolan) --directed--> (Inception)
↓ Entity Resolution (개체 연결)
Nolan = dbpedia:Christopher_Nolan
↓ RDF 트리플 생성추출할 정보:
- 영화: 제목, 개봉일, 장르, 러닝타임
- 인물: 이름, 생년월일, 역할(감독/배우)
- 관계: directed, actedIn, hasGenre
목표: 100편의 영화 데이터를 자동 수집
프로젝트 노트북에서 위키피디아로부터 영화 데이터를 자동 수집합니다.
프로젝트 노트북에서는 다음을 직접 구현합니다:
- Wikipedia API로 영화 100편 정보 크롤링
- spaCy NER로 감독/배우 이름 자동 추출
- LLM으로 "directed", "actedIn" 관계 추출
- Entity Resolution: "크리스 놀란" = "크리스토퍼 놀란" 병합
9주 후 완성되는 것: "놀란 감독 스타일의 SF 영화 추천해줘"라고 물으면, 지식그래프에서 감독-장르-평점 관계를 추론하고 자연어로 답변하는 AI 에이전트
실습 노트북
이론을 더 깊이 탐구하고 싶다면:
실습 노트북에서는 추가로 다음 내용을 다룹니다:
- spaCy 커스텀 NER 모델 Fine-tuning
- GPT-4 프롬프트 엔지니어링 기법
- 임베딩 기반 Entity Resolution
- 멀티소스 데이터 통합 전략
다음: Week 5: Neo4j