#!/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(" [그래프 질의] '잠깐 기억해 두는 능력'이라는 관계·클래스가 없음 — 질의 작성 불가")