3부 응용 · 트랙 6 AI와 온톨로지 — 실습

제 6 차 시

실습 1: 온톨로지 기반 RAG — 벡터 검색과 그래프 질의 비교

6-2의 비교표를 psymap.owl로 실측한다. 같은 질문 두 개, 두 검색, 실제 출력.
읽기 약 13분 · 코드 실행 1회

주장을 실행으로 바꾼다

실측

어느 검색이 이기는지는 표가 아니라 출력이 판정한다.

이론 차시가 약속한 것을
실습 차시가 상환한다.

학 습 목 표

  1. 온톨로지를 벡터 검색용 문서와 그래프 질의 대상의 두 형태로 준비한다.
  2. 질문 유형에 따라 두 검색의 성패가 갈리는 것을 실행으로 확인한다.
  3. 온톨로지 유무(구조 유무)에 따른 답변 차이를 시연한다 — 판정 기준 4의 전반부.

준비 — 한 지식의 두 형태

실험 설계가 핵심이다. 같은 지식(psymap.owl)을 두 형태로 준비해, 차이가 검색 방식에서만 나오게 통제한다. 벡터 쪽은 각 클래스의 라벨+정의를 한 '문서'로 삼아 30건의 문서를 만들고 TF-IDF 벡터로 바꾼다 — 임베딩 API 없이 재현되도록 한 축소 구현이며, '텍스트를 수치 벡터로 바꿔 유사도로 순위 매긴다'는 원리는 실제 임베딩과 같다(차이는 뒤에서 짚는다). 그래프 쪽은 파일을 rdflib로 열어 SPARQL을 그대로 쓴다(5-7의 재사용).

# rag_compare.py 핵심부 — ① 클래스를 '문서'로
docs = [f"{label}. {definition}" for ...]   # 30건
vec = TfidfVectorizer(analyzer="char_wb", ngram_range=(2, 3))
M = vec.fit_transform(docs)

def vector_search(question, k=3):            # ② 질문 벡터와 코사인 유사도
    sims = cosine_similarity(vec.transform([question]), M)[0]
    ...
def graph_query(sparql):                     # ③ 같은 지식에 구조 질의
    return g.query(PRE + sparql)

rag_compare.py 발췌. 전문은 차시 끝에서 열어 볼 수 있다.

대결 — 실제 출력

$ python3 rag_compare.py
문서 30건 (클래스별 라벨+정의)

질문 A: 작업기억을 측정하는 과제는 무엇인가
  [벡터 검색 상위 3] [('작업기억 과제', 0.537), ('기억 과제', 0.279),
                      ('측정 과제', 0.256)]
  [그래프 질의(CQ3)] ['N백 과제', '숫자폭 과제']

질문 B: 방금 들은 전화번호를 잠깐 기억해 두는 능력
  [벡터 검색 상위 3] [('기억 과제', 0.246), ('작업기억', 0.201),
                      ('기억 능력', 0.193)]
  [그래프 질의] '잠깐 기억해 두는 능력'이라는 관계·클래스가 없음 — 질의 작성 불가

실제 실행 출력. 질문 A는 그래프의 완승, 질문 B는 벡터만이 답에 접근한다 — 6-2 표 1의 예측 그대로다.

판독 — 출력이 가르치는 세 가지

첫째, 질문 A에서 벡터 검색은 '작업기억 과제'라는 문서를 가장 가깝다고 찾았다 — 틀리지 않았지만 답이 아니다. 질문에 답하려면 그 클래스의 구성원(숫자폭·N백)이 필요한데, 두 과제의 정의 문장에는 '작업기억'이라는 낱말이 없어 유사도 그물에 걸리지 않는다. 연결 정보는 텍스트가 아니라 공리(measures)에 있고, 공리를 읽는 것은 질의뿐이다. 이것이 "온톨로지 유무에 따른 답변 차이"의 실물이다 — 같은 지식이라도 구조 없이 텍스트로만 저장되면 질문 A에 정확히 답할 수 없다(판정 기준 4의 전반부 달성).

둘째, 질문 B에서 그래프는 아예 출전하지 못했다 — '잠깐 기억해 두는'을 받아 줄 스키마가 없어서다. 벡터는 작업기억·기억 능력을 상위에 올려 사람을 정답 근처까지 데려간다. 셋째, 그 벡터 출력에도 교훈이 있다. 정답에 가장 가까운 단기기억이 상위 3에 없다 — 정의문("수 초에서 수십 초…수동적으로 유지")과 질문("전화번호를 잠깐 기억")이 뜻은 같은데 낱말이 달라, 표면 어휘만 보는 TF-IDF가 놓친 것이다. 의미를 학습한 실제 임베딩이라면 잡았을 간격이다 — 축소 구현과 실전의 차이가 어디서 나는지를 출력이 정확히 가리킨다.

관찰원인실무 번역
벡터, 질문 A에서 목록 실패연결 지식이 텍스트 아닌 공리에 있음관계·집계 질문은 그래프로 라우팅
그래프, 질문 B에서 출전 불가스키마 밖 표현흐릿한 탐색 질문은 벡터로 라우팅
벡터, 단기기억을 놓침표면 어휘 일치의 한계(TF-IDF)실전에서는 의미 임베딩 사용 + altLabel을 문서에 포함

표 1실측의 판독. 셋째 행의 처방 — altLabel·정의를 검색 문서에 함께 넣는 것 — 은 온톨로지의 주석 레이어(5-6)가 벡터 검색의 품질까지 끌어올린다는 뜻이다. 두 검색은 경쟁자이면서 서로의 재료다.

— — —

판별 연습

문 1질문 A에 대한 벡터 RAG의 최종 답(LLM 생성까지 간 경우)은 어떤 모습일 것이며, 왜 위험한가.

답 확인

프롬프트에는 '작업기억 과제'의 정의문 등 유사 문서만 담기므로, LLM은 자기 파라미터 지식으로 빈칸을 메워 "숫자폭 과제, N백 과제, 스트룹 과제 등"처럼 유창하게 답할 공산이 크다. 맞는 항목과 틀린 항목(스트룹은 주의 과제다)이 섞여도 출처가 없어 가려낼 수 없다 — 검색 실패가 환각으로 미끄러지는 전형적 경로이고, 접지(6-1)가 깨지는 지점이다.

문 2표 1 셋째 행의 처방을 실행한다면 검색 문서를 어떻게 다시 만들겠는가.

답 확인

문서를 "prefLabel + altLabel 전부 + 정의 + note"로 확장한다. 단기기억 문서에 'STM'이, 작업기억 문서에 '작동기억'이 들어가 표면 일치율이 오른다. 나아가 measures 공리를 "숫자폭 과제는 작업기억을 측정한다"라는 문장으로 펴서 문서에 넣으면(공리의 텍스트화) 질문 A도 벡터로 어느 정도 잡힌다 — 그래프가 벡터 검색의 문서 생성기가 되는, 하이브리드의 한 형태다.

— — —

적용: 산출물과 파일

실행 스크립트 전문은 rag_compare.py에서 열어 볼 수 있다(scikit-learn 필요: pip install scikit-learn). 자신의 도메인으로 실습 중인 독자는 질문 A 자리에 자기 조회형 CQ를, 질문 B 자리에 용어를 모르는 사용자의 말투를 넣어 같은 실험을 하면 된다. 다음 차시는 질문 B의 사용자도 그래프의 정확성에 닿게 하는 다리 — 자연어를 질의로 바꾸는 체인 — 를 놓는다.

용어 정리

용어원어뜻
TF-IDFterm frequency–inverse document frequency낱말 빈도 기반의 고전적 텍스트 벡터화 — 표면 어휘만 본다
코사인 유사도cosine similarity두 벡터 사이 각도로 가까움을 재는 척도
라우팅routing질문 유형을 보고 적합한 검색 경로로 보내는 운용
공리의 텍스트화axiom verbalization공리를 자연어 문장으로 펴서 검색 문서로 쓰는 기법
이 차시가 기대고 있는 자료
실습 파일: rag_compare.py(본 사이트 수록 — 출력은 전부 실제 실행 결과). · Lewis, P. et al. (2020). Retrieval-Augmented Generation. NeurIPS. · 6-2(비교 이론)·5-7(SPARQL 재사용)의 종합.
트랙 6 · 차시 6실습 1: 온톨로지 기반 RAG