NEDKnowledge Graphs × LLMsChapter 8 · Korean Technical Edition
Chapter 8 · pp. 180–206

이름의 숲에서
관계의 길을 찾다

한 단어는 짧지만 그 뒤의 세계는 깊다. “Zika”라는 네 글자도 바이러스일 수 있고, 질병일 수 있으며, 선천성 감염일 수 있다. 제8장은 이 모호함을 주변 단어만으로 풀지 않는다. 오픈 LLM의 언어 능력과 SNOMED 온톨로지의 관계 구조를 겹쳐, 이름이 놓일 정확한 자리를 찾는다.

3NER · Candidate Selection · Candidate Disambiguation
450K+도서가 설명하는 SNOMED 개념 규모
1–2 hops후보 사이 최단경로 탐색 범위
22제8장 구현·출력 Listing 수
온톨로지가 지형도라면 LLM은 그 지도를 말로 읽어 주는 안내자다. 지도 없이 말만 따르면 길을 꾸며낼 수 있고, 안내자 없이 지도만 보면 길의 뜻을 놓치기 쉽다.제8장의 결합 원리를 바탕으로 재구성한 해설
01 / LIMITS OF TRADITIONAL NED

문맥의 울타리가 낮아지면 전통 모델은 흔들린다

scispaCy는 생의학 문서에 강하고 UMLS 후보를 연결할 수 있지만, 제8장은 그 능력이 특정 도메인과 주변 표현에 지나치게 기대는 지점을 짚는다.

Constraint 01

도메인 종속성

생의학 분야에 맞춰 설계된 도구이므로 풍부한 온톨로지를 가진 다른 분야로 곧바로 옮기기 어렵다.

Constraint 02

참조 지식 갱신

새 개체와 용어를 지식베이스에 확장·갱신하는 과정이 유연하지 않다.

Constraint 03

지식의 과소 활용

참조 지식베이스에 들어 있는 광범위한 정보를 중의성 해소에 충분히 사용하지 않는다.

Constraint 04

관계·경로의 부재

개체 사이 관계와 경로를 후보 판별의 근거로 직접 활용하지 않는다.

표현 자체가 정답을 거의 말해 주는 경우

“Zika virus”, “congenital Zika syndrome”, “Zika virus infection”처럼 바이러스·선천성 증후군·감염이라는 단서가 표면에 드러난다. scispaCy는 주변 단어를 이용해 각각의 UMLS 후보를 올바르게 상위에 둔다.

언급상위 후보UMLS CUI
Zika virusZika VirusC0318793
congenital Zika syndromeCongenital Zika SyndromeC4546023
Zika virus infectionZika Virus InfectionC0276289

관계는 풍부하지만 표면 단서가 약한 경우

새 예문은 Flaviviridae, Aedes mosquito, chikungunya fever, microcephaly, congenital malformation을 함께 언급하지만 “congenital Zika syndrome” 같은 직접 표현은 쓰지 않는다. 모델은 첫째·셋째 “Zika”를 Zika Virus Infection으로 해석하고, 둘째 문장의 “Zika disease”에는 후보를 제시하지 못한다.

제8장의 문제 전환

주변 단어만 더 보는 것이 아니라, 문장 속 다른 의료 개체 후보들과 온톨로지에서 어떻게 연결되는지를 본다.

SCISPACY OUTPUT · SOURCE REPLAY
Recognized entity: Zika
1. C0276289 · Zika Virus Infection
2. C0318793 · Zika Virus
3. C4687930 · Zika Virus Antibody Measurement

Recognized entity: Zika disease
→ ranked target candidate 없음

Recognized entity: Zika
1. C0276289 · Zika Virus Infection
2. C0318793 · Zika Virus
3. C4687930 · Zika Virus Antibody Measurement
02 / INGESTING SNOMED

온톨로지는 목록이 아니라 계보와 관계의 지형이다

제8장의 참조 지식은 SNOMED다. 이름과 별칭만 적재하는 데 그치지 않고, 루트에서 첫 단계 원형 범주를 찾고 SNOMED_IS_A 계층을 따라 그 의미 타입을 깊은 노드까지 전파한다.

SNOMEDroot · 138875005 Body structurefirst-level archetype Pharmaceuticalproduct Substance Disease Ecallantidetype: Pharmaceutical product Retinopathy assoc. with AIDStype: Disease 첫 단계 원형 범주 → IS_A 계층 순회 → 하위 노드의 type 배열에 의미 범주 전파

두 입력 파일

sct2_Description_Full-en_US1000124_20220901.txt는 명칭·별칭 정보를, sct2_Relationship_Full_US1000124_20220901.txt는 수치 코드 기반 관계를 제공하는 것으로 구현 흐름이 설명된다.

세 적재 작업

관계 노드·엣지 생성 → 개체와 관계에 이름·별칭 부여 → SNOMED_IS_A를 이용한 첫 단계 범주 전파의 순서로 진행한다.

관계를 먼저 세운다

SnomedEntity.id의 유일성 제약과 이름·관계 ID·관계 타입·UMLS 매핑 인덱스를 만든다. 관계 파일의 각 행은 SNOMED_RELATION으로 적재되며, 타입 코드가 116680003이면 별도의 SNOMED_IS_A도 생성한다.

CONCEPTUAL CYPHER
MERGE (source:SnomedEntity {id: row.sourceId})
MERGE (target:SnomedEntity {id: row.destinationId})
MERGE (source)-[:SNOMED_RELATION {id: row.typeId}]->(target)

// IS_A code = 116680003
FOREACH (_ IN CASE WHEN row.typeId = '116680003' THEN [1] ELSE [] END |
  MERGE (source)-[:SNOMED_IS_A]->(target)
)

수치 코드에 사람이 읽는 이름을 붙인다

설명 파일의 개체명과 관계명은 각각 namealiases로 적재된다. 같은 이름을 중복 추가하지 않고 기존 배열을 보존한다.

왜 별칭이 필요한가

후보 선택은 정확히 같은 문자열만 찾는 일이 아니다. 한 개념에 붙은 다양한 표현을 검색 공간으로 열어야 한다.

entity.nameentity.aliases[]relation.typerelation.aliases[]

계층을 따라 의미 타입을 물려준다

루트 138875005 아래의 첫 단계 노드를 원형 범주로 잡고, APOC 경로 확장으로 모든 하위 노드를 순회한다. 각 하위 노드의 type 배열에 첫 단계 노드 이름을 넣어 NER 범주로 재사용한다.

제8장의 중요한 연결

온톨로지 적재는 검색용 사전 준비가 아니다. 이때 전파된 타입이 이후 LLM NER 프롬프트의 허용 범주 목록이 된다.

03 / LOCAL OPEN LLM

모델을 먼 서버가 아니라 데이터 곁에 둔다

제8장은 앞 장의 OpenAI API 실험에서 한 걸음 옮겨, Ollama와 Llama 3.1 8B를 로컬에 배치한다. 외부 제공자 의존을 줄이고 데이터 통제권을 확보한다는 것이 설계의 출발점이다.

Data control

로컬 데이터 통제

문서와 프롬프트가 외부 모델 서비스로 나가지 않도록 실행 위치를 통제한다.

Latency

네트워크 의존 감소

모델 호출이 로컬 환경에서 이루어져 외부 API 지연과 가용성 의존을 줄인다.

Compatibility

OpenAI 호환 인터페이스

Ollama의 OpenAI Chat Completions 호환 API를 사용해 기존 Python 호출 구조를 재사용한다.

OLLAMA · LISTING 8.6
ollama serve
ollama pull llama3.1:latest
OPENAI-COMPATIBLE CLIENT · SIMPLIFIED
client = OpenAI(
  base_url="http://localhost:11434/v1",
  api_key="default"
)

response = client.chat.completions.create(
  model="llama3.1:latest",
  messages=messages,
  temperature=0,
  max_tokens=4000,
  top_p=1
)

도서의 구현 시점

도서는 Llama 3.1 8B를 80억 매개변수, 최대 128,000 토큰 문맥, 다국어 정보 처리와 소비자급 하드웨어 배치를 고려한 모델로 설명한다. 예제 결과는 2024년 10월 당시 최신 Llama 3.1 모델로 얻었다고 명시한다. 이 페이지는 해당 장의 스냅샷을 그대로 해설하며 최신 모델 상태를 외부 정보로 갱신하지 않는다.

04 / END-TO-END ARCHITECTURE

세 개의 문을 지나야 이름은 비로소 주소가 된다

입력 문서는 곧바로 SNOMED ID가 되지 않는다. 먼저 언급을 찾고, 온톨로지에서 가능한 주소를 모으며, 후보들 사이의 관계를 읽어 한 주소를 선택한다.

NER

Named Entity Recognition

SNOMED의 첫 단계 범주를 허용 라벨로 사용해 문장 속 생의학 언급을 찾고 유형을 부여한다.

CS

Candidate Selection

Neo4j 전문 검색으로 언급과 가까운 SNOMED 개체를 찾는다. 이 단계에는 LLM을 쓰지 않는다.

CD

Candidate Disambiguation

후보 사이 최단경로를 탐지하고, 경로를 문장으로 바꾸고, 요약한 뒤 LLM이 최종 개체를 고른다.

Inputunstructured text NERmentions + labelsLLM CandidateSelectionNeo4j full-text CandidateDisambiguationgraph + LLM SNOMED ontologytypes · aliases · relations · paths 온톨로지는 단계 밖의 사전이 아니라 모든 단계에 지식과 제약을 공급하는 중심 구조다
05 / NAMED ENTITY RECOGNITION

LLM에게 자유를 주기보다 정확한 어휘의 울타리를 세운다

NER 프롬프트는 모델이 임의의 범주를 만들어내지 못하게 한다. SNOMED에서 수집한 범주만 허용하고, 문장별 JSON으로 모든 단일 언급을 반환하게 한다.

Ontology-driven labels

범주 목록은 모델 기억이 아니라 그래프에서 가져온다

SnomedEntity.type 배열을 펼쳐 서로 다른 범주와 개체 수를 집계한 뒤, 그 범주 목록을 NER 시스템 프롬프트에 삽입한다.

예제 라벨

Risk factors → Events, rhinocerebral mucormycosis → Disease, poorly controlled diabetes mellitus → Disease, severe immunosuppression → Qualifier value로 구조화한다.

NER PROMPT · CONDENSED
System
의료 도메인의 명명 개체를 추출한다.
허용 범주는 그래프에서 얻은 {named_entities}뿐이다.
모든 단일 언급을 문장별 유효 JSON으로 반환한다.

Output schema
{
  "sentence": "...",
  "entities": [
    {"id": 0, "mention": "...", "label": "Disease"}
  ]
}
언급제8장 Llama 출력 라벨문자 위치
ZikaOrganism19–22
microcephalyClinical finding (finding)105–116
congenital malformationsClinical finding (finding)122–145

LLM이 언급 경계를 잘못 셀 수 있다

제8장은 모델이 시작·끝 문자 위치를 정확히 검출하는 데 어려움이 있다고 보고, 문자열 검색 기반 후처리 함수로 위치를 다시 계산한다. 언어 해석은 LLM에 맡기되, 결정론적으로 계산 가능한 위치는 코드가 맡는 분업이다.

Mention offset lab

언급 위치 후처리 재현

Deterministic output

문자열 일치 결과

시작–끝 인덱스(끝 포함): -

06 / CANDIDATE SELECTION

후보를 찾는 일에는 LLM을 쓰지 않는다

제8장은 후보 검색을 언어모델의 기억에 맡기지 않는다. 전체 온톨로지는 프롬프트에 넣기 너무 크고, 참조 지식에서 직접 후보를 뽑아야 검증 가능한 ID를 얻을 수 있기 때문이다.

Reason 01

지식 출처를 고정한다

후보는 모델 내부 지식이 아니라 SNOMED 노드에서 직접 검색한다.

Reason 02

온톨로지 크기를 우회한다

45만 개가 넘는 개념 전체를 프롬프트에 넣지 않고 Neo4j 전문 인덱스를 사용한다.

Full-text strategy

라벨로 줄이고, 0.80 fuzzy match로 넓힌다

NER가 부여한 라벨을 $labels로 사용해 검색 공간을 해당 타입으로 제한한다. 여러 단어의 언급은 각 단어에 약 0.80의 fuzzy 조건을 적용하고 AND로 묶는다. 도서는 전문 검색에 벡터 검색을 더하면 문자열 유사성으로 놓치는 후보를 보완할 수 있다고 제안한다.

FULL-TEXT QUERY · CONCEPTUAL
CALL db.index.fulltext.queryNodes(
  "names", $fulltextQuery, {limit: $limit}
)
YIELD node
WHERE node:SnomedEntity
  AND ANY(t IN node.type WHERE t IN $labels)
RETURN DISTINCT node.name, node.id

// multi-word example
word1~0.80 AND word2~0.80
Source example replay

“Zika” 후보

제8장의 CS 결과를 대화형 카드로 재현한다. 이 단계는 순위를 확정하지 않고 가능한 해석을 모은다.

문맥 칩은 다음 CD 단계의 근거를 보여 주기 위한 장치다.

A
Zika virusSNOMED 50471002
candidate
B
Zika virus diseaseSNOMED 3928002
candidate
C
Congenital Zika virus infectionSNOMED 762725007
chapter’s final choice

후보 C의 선택은 CS가 아니라 다음 단계의 온톨로지 경로와 문맥 요약을 거친 최종 결과다.

07 / CANDIDATE DISAMBIGUATION

그래프의 길을 찾아 문장으로 번역한 뒤 다시 압축한다

후보 해소는 세 단계로 나뉜다. 그래프 알고리즘이 길을 찾고, LLM이 그 길을 문장으로 읽으며, 또 다른 LLM 단계가 여러 문장을 한 문맥으로 압축한다.

01

Shortest-path detection

문장 속 서로 다른 언급의 후보 쌍마다 1–2홉 최단경로를 찾고 고차수 허브를 제외한다.

02

Path-to-text translation

노드와 방향성 관계로 된 경로를 정확한 개체명을 유지한 자연어 문장으로 바꾼다.

03

Textual summarization

여러 경로 문장을 핵심 개체와 관계를 보존한 짧은 문맥으로 압축한다.

허브를 걷어내고 짧은 연결만 본다

GDS degree stream으로 차수가 높은 노드 350개를 허브 목록으로 만든다. 후보 쌍 사이의 모든 최단경로를 1–2개 관계로 제한하고, 경로에 허브 이름이 하나라도 포함되면 제외한다.

  • 노드: 이름 배열
  • 관계: 타입 배열과 시작 노드 배열
  • 출력: 방향을 보존한 Cypher 형태 문자열

대표 경로

(Congenital Zika virus infection)-[:OCCURRENCE]→(Congenital)←[:OCCURRENCE]-(Micrencephaly)

그래프 문법을 LLM의 모국어로 옮긴다

프롬프트는 경로의 정확한 개체명을 유지하고, 불필요한 문자를 넣지 않은 JSON을 생성하도록 지시한다. 방향과 관계 타입을 사람이 읽을 수 있는 문장으로 풀어낸다.

변환 예시

“선천성 지카 바이러스 감염의 occurrence가 Congenital occurrence와 연관되며, 그 Congenital occurrence는 Micrencephaly와 연관된다”는 식으로 번역된다.

토큰을 줄이되 개체와 관계를 버리지 않는다

경로마다 생성된 문장이 많아지면 모델의 인지 부담과 토큰 수가 증가한다. 요약 단계는 모든 중요 개체를 유지하면서 관련 문장을 하나의 context 문자열로 압축한다.

제8장의 요약 결과

Congenital Zika virus infection이 Micrencephaly, Acrocephaly, Multiple congenital malformations, Other congenital malformations와 연관되고 이들이 Congenital 개체를 공유한다는 문맥으로 정리한다.

원문·후보·요약 문맥을 한 번에 본다

최종 프롬프트는 원문 문장, 언급별 SNOMED 후보, 온톨로지 경로에서 만든 문맥을 입력으로 받는다. 모델은 문맥과 가장 잘 맞는 snomed_id와 이름을 JSON으로 반환한다.

언급최종 SNOMED 개체ID
ZikaCongenital Zika virus infection762725007
microcephalyMicrencephaly204030002
congenital malformationsMultiple congenital malformations116022009
Path explorer

경로가 문장이 되는 과정

표시되는 번역과 요약은 장의 예시를 짧게 재구성한 학습용 표현이다.

Path-to-text

Disambiguation context

Asthma 예제

최종 프롬프트 구조를 설명하기 위해 Asthma와 Allergic rhinitis가 공통 호흡기 질환 맥락을 갖는 예를 사용한다. 후보 중 일반 Asthma(195967001)와 Allergic rhinitis(61582004)를 선택한다.

Zika 예제의 결론

microcephaly와 congenital malformations가 제공하는 관계 문맥 때문에 일반 바이러스나 일반 질병보다 Congenital Zika virus infection이 우선된다.

08 / SOURCE REVIEW NOTES

좋은 독해는 원문의 흔들림도 숨기지 않는다

이 페이지는 제8장의 표현을 임의로 교정하지 않았다. 대신 독자가 구현과 결과를 혼동하지 않도록 원문 안의 명시적 불일치와 주의점을 별도로 표시한다.

Figure 8.2–8.3 vs. Listing 8.11

도식 설명은 “Zika”를 SNOMED의 Disease 개념으로 인식한다고 서술하지만, 실제 Llama 출력 예시는 “Zika”를 Organism으로 라벨링한다. 이 페이지는 출력 표에는 Listing 8.11을 따르고 도식 설명의 차이를 별도 기록한다.

Figure 8.6의 경로 설명

표시된 경로는 Congenital Zika virus infection과 Micrencephaly를 연결하지만, 도식 주석은 이를 “Zika virus disease”와 “Chikungunya fever” 사이 경로라고 설명한다. 경로 문자열과 설명 대상이 맞지 않는 원문상 불일치다.

Microcephaly → Micrencephaly

입력 언급은 “microcephaly”이지만 최종 SNOMED 이름은 “Micrencephaly”로 제시된다. 오탈자 여부를 외부 자료로 교정하지 않고 도서 결과를 그대로 유지한다.

관계의 과도한 의미화 주의

OCCURRENCE를 공유하는 2홉 경로는 관련 문맥을 제공하지만 그 자체가 인과를 보장하지 않는다. 제8장도 경로를 후보 해소의 근거로 사용하며, 관계를 곧바로 임상 인과로 확정하지 않는다.

구현에서 확인해야 할 지점

온톨로지 릴리스, 파일 역할, 인덱스 명칭, 허브 개수 350, 경로 길이 1–2홉, fuzzy 임계 0.80, 모델·프롬프트 버전, JSON 파싱 실패 처리, 언급 offset 후처리를 실행 메타데이터로 남겨야 재현성이 생긴다.

09 / LISTING MAP

스물두 개 Listing이 만드는 구현의 연쇄

코드 전체를 복제하지 않고 각 Listing이 파이프라인에서 맡는 역할을 정리했다. 번호를 펼치면 입력·처리·산출물의 위치를 빠르게 찾을 수 있다.

8.1–8.2scispaCy의 성공·실패 비교
직접적인 주변 단서가 있는 ECDC 문장에서는 Zika 관련 개체를 올바르게 순위화하지만, 단서가 약한 새 문장에서는 Zika disease를 연결하지 못하는 사례를 제시한다.
8.3SNOMED 관계·제약·인덱스 적재
SnomedEntity 유일성 제약과 검색 인덱스를 만들고 관계 파일을 SNOMED_RELATION 및 SNOMED_IS_A로 적재한다.
8.4–8.5명칭·별칭 적재와 의미 타입 전파
설명 파일로 개체·관계 이름과 별칭을 채우고, 첫 단계 원형 범주를 IS_A 계층 하위 노드의 type으로 전파한다.
8.6–8.7Ollama 배치와 Python 모델 래퍼
ollama serve, ollama pull llama3.1:latest로 모델을 준비하고 OpenAI 호환 API를 호출하는 LLM_Model 클래스를 구성한다.
8.8–8.12온톨로지 제약형 NER
SNOMED 범주 조회, NER 프롬프트, Zika 예문, JSON 출력, 문자열 기반 시작·끝 위치 후처리 함수로 이어진다.
8.13–8.14Neo4j 전문 검색 기반 후보 선택
NER 라벨로 검색 타입을 제한하고 0.80 fuzzy query로 SNOMED 후보를 반환한다. Zika에 세 후보와 ID가 추가된 JSON을 보여 준다.
8.15–8.16GDS 기반 관련 경로 추출
상위 차수 허브 350개를 제외하고 후보 쌍의 1–2홉 최단경로를 방향성 문자열로 변환한다. 13개의 예시 경로가 제시된다.
8.17–8.18경로를 자연어 문장으로 변환
Neo4j 경로의 정확한 개체명을 유지한 문장을 생성하도록 LLM 프롬프트를 설계하고 Zika·선천성 기형 관련 변환 결과를 보여 준다.
8.19–8.20텍스트 경로 요약
여러 관계 문장을 짧은 JSON context로 압축한다. hypertension, diabetes, asthma, osteoporosis 예시와 Zika 관련 최종 요약을 제시한다.
8.21–8.22최종 중의성 해소 프롬프트와 결과
원문, 후보 목록, 요약 문맥을 조합해 가장 적합한 SNOMED 개체를 반환한다. Zika, microcephaly, congenital malformations의 최종 ID가 산출된다.
10 / CHAPTER SUMMARY

이름을 맞히는 기술에서 관계를 읽는 체계로

제8장의 기여는 LLM을 NED에 추가한 데 있지 않다. 결정론적 검색, 그래프 알고리즘, 자연어 변환을 역할별로 나눠 한 모델의 기억이나 한 종류의 문맥에 과도하게 의존하지 않는 구조를 만들었다는 데 있다.

01

전통적 NED는 관계 구조를 덜 쓴다

주변 단어가 희박하면 동일 문자열의 바이러스·질병·선천성 감염을 구분하기 어렵다.

02

온톨로지는 모든 단계에 참여한다

범주를 NER에, 명칭·별칭을 후보 검색에, 관계 경로를 최종 중의성 해소에 공급한다.

03

후보 검색은 그래프 DB가 맡는다

검증 가능한 ID를 얻고 거대한 온톨로지를 프롬프트에 넣지 않기 위해 Neo4j 전문 검색을 사용한다.

04

LLM은 관계를 말로 바꾸는 데 강하다

그래프 경로를 문장으로 옮기고 여러 문장을 짧은 문맥으로 요약해 최종 선택의 근거를 만든다.

05

구조는 다른 도메인으로 이동할 수 있다

풍부한 개체·관계 온톨로지가 존재하면 생의학 밖의 NED에도 같은 프레임을 적용할 수 있다.

옛길을 찾을 때 가장 믿을 만한 것은 길 이름 하나가 아니라 산줄기와 물길, 마을의 자리다. 개체의 이름도 마찬가지다. 관계의 지형을 함께 보아야 그 이름이 어느 자리를 가리키는지 알 수 있다.답사기적 관찰의 호흡을 빌려 재구성한 제8장의 결론