#!/usr/bin/env python3
"""실습 1 (6-6): 벡터 검색 vs 그래프 질의 — 같은 지식, 두 가지 접근.
벡터부는 TF-IDF로 축소 구현(임베딩 API 없이 재현 가능). 원리는 동일하다:
문서를 수치 벡터로 바꾸고 질문 벡터와의 유사도로 순위를 매긴다."""
import rdflib, re
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
g = rdflib.Graph().parse("psymap.owl")
PRE = """PREFIX :
PREFIX rdfs:
PREFIX owl:
PREFIX skos:
"""
# ---------- 1) 온톨로지의 각 클래스를 '문서'로 변환 (프레임 짜기) ----------
docs, names = [], []
q = g.query(PRE + """SELECT ?c ?l ?d WHERE {
?c skos:prefLabel ?l ; skos:definition ?d .
FILTER(lang(?l)="ko") }""")
for c, l, d in q:
names.append(str(l))
docs.append(f"{l}. {d}")
print(f"문서 {len(docs)}건 (클래스별 라벨+정의)")
# 한국어용 간이 토큰화: 2글자 문자 n-gram
vec = TfidfVectorizer(analyzer="char_wb", ngram_range=(2, 3))
M = vec.fit_transform(docs)
def vector_search(question, k=3):
qv = vec.transform([question])
sims = cosine_similarity(qv, M)[0]
top = sims.argsort()[::-1][:k]
return [(names[i], round(float(sims[i]), 3)) for i in top]
def graph_query(sparql):
rows = g.query(PRE + sparql)
out = []
for row in rows:
iri = row[0]
lab = g.query(PRE + f'SELECT ?l WHERE {{ <{iri}> skos:prefLabel ?l . FILTER(lang(?l)="ko") }}')
out.append(str(list(lab)[0][0]))
return sorted(out)
# ---------- 2) 질문 A: 구조를 묻는 질문 ----------
qa = "작업기억을 측정하는 과제는 무엇인가"
print(f"\n질문 A: {qa}")
print(" [벡터 검색 상위 3]", vector_search(qa))
print(" [그래프 질의(CQ3)]", graph_query("""SELECT ?t WHERE {
?t rdfs:subClassOf ?r .
?r owl:onProperty :measures ; owl:someValuesFrom :PSY_0000018 .
FILTER(isIRI(?t)) }"""))
# ---------- 3) 질문 B: 유사성을 묻는 흐릿한 질문 ----------
qb = "방금 들은 전화번호를 잠깐 기억해 두는 능력"
print(f"\n질문 B: {qb}")
print(" [벡터 검색 상위 3]", vector_search(qb))
print(" [그래프 질의] '잠깐 기억해 두는 능력'이라는 관계·클래스가 없음 — 질의 작성 불가")