<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>tasker_dev.log</title>
        <link>https://velog.io/</link>
        <description>ML Engineer 🧠 | AI 모델 개발과 최적화 경험을 기록하며 성장하는 개발자 🚀 The light that burns twice as bright burns half as long ✨</description>
        <lastBuildDate>Mon, 25 May 2026 00:49:47 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>tasker_dev.log</title>
            <url>https://velog.velcdn.com/images/tasker_dev/profile/6d5baf12-e153-440d-af9e-ffa3385f21f3/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. tasker_dev.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/tasker_dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Super Node, Community Detection, Eigenvector Centrality로 이상 탐지 하기]]></title>
            <link>https://velog.io/@tasker_dev/Super-Node-Community-Detection-Eigenvector-Centrality%EB%A1%9C-%EC%9D%B4%EC%83%81-%ED%83%90%EC%A7%80-%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@tasker_dev/Super-Node-Community-Detection-Eigenvector-Centrality%EB%A1%9C-%EC%9D%B4%EC%83%81-%ED%83%90%EC%A7%80-%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 25 May 2026 00:49:47 GMT</pubDate>
            <description><![CDATA[<p>이 챕터의 thesis는 다음과 같다. 이상 탐지는 본질적으로 &quot;흩어진 식별자들이 같은 인물·집단을 가리키는지&quot;를 판별하는 entity linking 문제이며, 그래프는 이를 관계의 직접적인 표현으로 환원해 단순한 SQL 질의로는 도달할 수 없는 패턴을 드러낸다. Super Node로 의심 구간을 잡고, Weakly Connected Components로 커뮤니티를 분리하고, Eigenvector Centrality로 그 안의 핵심 인물을 지목하는 3단계 흐름이 챕터의 골격이다.</p>
<h2 id="1-이상-탐지-문제의-본질">1. 이상 탐지 문제의 본질</h2>
<p>사기는 개인적 이익을 위한 사실의 허위 표시로 정의된다. 디지털 거래는 매일 수십억 건의 데이터 포인트로 구성되며, 사기 양상을 분석하려면 최첨단 기술이 필요하다. 신원 도용 같은 사기 수법은 금융 서비스 회사에 막대한 경제적 손실과 평판 리스크를 동시에 안긴다. AI의 등장과 함께 사기 수법은 점점 더 복잡해지고 있으며, 분석 측면에서도 단순한 통계 규칙이나 임계값 기반 탐지로는 한계가 명확해졌다. 상호 연결된 관계를 분석하기 위한 knowledge graph 같은 고급 분석 기법으로의 전환이 요구되는 배경이다.</p>
<p>사기는 가해자 구성에 따라 세 유형으로 나뉜다.</p>
<table>
<thead>
<tr>
<th>유형</th>
<th>정의</th>
<th>탐지 난이도</th>
</tr>
</thead>
<tbody><tr>
<td>First-Party Fraud</td>
<td>본인이 자신의 정체나 정보를 허위로 표시</td>
<td>단일 계정 분석으로 탐지 가능</td>
</tr>
<tr>
<td>Second-Party Fraud</td>
<td>본인이 동의 하에 정보를 타인에게 제공하고 공모</td>
<td>관계 분석 필수</td>
</tr>
<tr>
<td>Third-Party Fraud</td>
<td>외부인이 본인 몰래 신원·정보를 도용 (가장 흔함)</td>
<td>광범위한 네트워크 분석 필요</td>
</tr>
</tbody></table>
<p>Second·Third-Party Fraud는 단일 계정 단위로는 보이지 않는다. 공모자 또는 도용자가 다른 계정·다른 식별자를 거쳐 활동하기 때문이다. 관계의 그물망을 펼쳐야만 패턴이 드러난다는 사실이 그래프 기반 탐지의 출발점이다.</p>
<h2 id="2-고객을-잇는-식별자들">2. 고객을 잇는 식별자들</h2>
<p>KG는 방대한 데이터셋의 복잡한 관계를 매핑해, 위험을 알고리즘적·시각적으로 정확히 지목할 수 있게 한다. 매핑의 출발점은 &quot;어떤 속성으로 사람과 사람을 잇는가&quot;다. 챕터가 제시하는 대표 식별자는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>식별자</th>
<th>공유 시 의미</th>
</tr>
</thead>
<tbody><tr>
<td>SSN</td>
<td>신원 도용 의심 — 같은 SSN을 여러 client가 사용</td>
</tr>
<tr>
<td>IP 주소</td>
<td>잠재적 사기범 간 연결 — 동일 IP에서 다수 client 접속</td>
</tr>
<tr>
<td>은행 계좌</td>
<td>의심 거래의 red flag — 다른 client 간 계좌 공유</td>
</tr>
<tr>
<td>물리적 주소·PO Box</td>
<td>잠재적 의심 — 다수 client가 같은 주소 사용</td>
</tr>
<tr>
<td>이메일</td>
<td>식별자로 활용, 단 fake 이메일 주의</td>
</tr>
</tbody></table>
<p>각 식별자는 단독으로는 결정적 증거가 아니지만, 여러 식별자가 동시에 일치하면 정보량이 누적된다. 이 누적을 자연스럽게 표현하는 것이 그래프 모델이다. Client 노드와 식별자 노드를 별도로 두고, 각 client가 어떤 식별자에 연결되었는지를 엣지로 기록하면, 동일 식별자를 공유하는 client들은 그 식별자 노드를 경유해 자동으로 연결된다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/92fc2c91-2f1d-4a7b-a250-b902f042d0d6/image.png" alt=""></p>
<p>이렇게 표현하면 &quot;두 client가 식별자를 매개로 간접적으로 연결되어 있는가&quot;가 그래프 traversal 한 단계의 문제로 환원된다. 관계형 DB에서 동일한 분석을 하려면 여러 테이블에 걸친 self-join이 누적되고, client 수가 늘어남에 따라 비용이 기하급수적으로 증가한다.</p>
<h2 id="3-super-node-분석의-첫-신호">3. Super Node: 분석의 첫 신호</h2>
<p>Client 간 연결 그래프를 만들고 나면, 가장 먼저 마주치는 현상은 일부 노드가 비정상적으로 많은 연결을 갖는다는 점이다. 이것이 super node다. 연결 수가 많은 노드를 super node라고 부르며, 사기 분석의 출발점은 이런 노드를 flag하는 일이다.</p>
<p>Super node는 두 가지를 의미할 수 있다. 첫째, 실제로 다수의 client와 식별자를 공유하는 사기 허브일 수 있다. 둘째, 데이터 품질 문제로 발생한 가짜 연결의 산물일 수 있다. 예컨대 이메일이 가짜이거나 자동 생성된 경우(&quot;<a href="mailto:NONE@NA.com">NONE@NA.com</a>&quot; 같은 placeholder), 이 이메일은 수많은 client와 연결된다. 이런 가짜 이메일은 분석에서 제외하거나, 원본 데이터의 품질 이슈를 해결하는 데 활용된다.</p>
<p>따라서 super node 처리는 두 갈래로 나뉜다.</p>
<table>
<thead>
<tr>
<th>처리 방향</th>
<th>목적</th>
<th>결과</th>
</tr>
</thead>
<tbody><tr>
<td>Filter Criteria</td>
<td>데이터 품질 오류로 인한 false connection 제거</td>
<td>분석에서 제외</td>
</tr>
<tr>
<td>Stand-Alone Analytics</td>
<td>super node가 존재하는 근본 원인을 별도 프로젝트로 조사</td>
<td>새로운 사기 패턴 발견 가능성</td>
</tr>
</tbody></table>
<p>Client 간 연결이 community detection에 유효하지 않다 하더라도, 많은 수의 연결 자체가 사기 증거가 될 수 있다는 관점이 챕터의 핵심이다. 즉 super node는 community에서 분리해 분석하더라도, 그 자체로 별도의 사기 신호로 다뤄야 한다.</p>
<h2 id="4-shared_identifiers-관계와-가중치">4. SHARED_IDENTIFIERS 관계와 가중치</h2>
<p>Client–식별자–client의 2-hop 구조를 1-hop으로 압축한 것이 SHARED_IDENTIFIERS 관계다. &quot;Client A와 Client B가 SSN 1개, IP 1개를 공유한다&quot;는 사실을 두 client 사이의 가중치 엣지로 표현한다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/71bfe28d-7146-481a-aeb7-5fcacf761686/image.png" alt=""></p>
<p>가중치 결정에는 비즈니스 규칙이 들어간다. 단순 공유 식별자 개수를 합산할 수도 있고, 식별자 종류에 따라 가중치를 차등 부여할 수도 있다(SSN 공유가 IP 공유보다 무거움). 두 client 간 연결의 강도를 결정하는 가중치는 그래프 알고리즘이 가중치를 활용한 계산을 수행할 때 유용해진다.</p>
<h2 id="5-graph-projection-인메모리-분석을-위한-재구성">5. Graph Projection: 인메모리 분석을 위한 재구성</h2>
<p>Neo4j Graph Data Science(GDS) 라이브러리는 알고리즘 실행 전에 graph projection을 만든다. 코드 스니펫이 생성하는 graph projection은 KG 데이터를 인메모리 저장에 최적화된 형태로 효과적으로 재구성한다. 이 최적화된 포맷은 그래프 데이터 사이언스 알고리즘의 효율적인 실행을 가능하게 하며, 더 빠르고 효과적인 데이터 처리·분석을 가능하게 한다.</p>
<p>Projection의 핵심은 두 가지다. 첫째, 분석 대상 노드·관계만 골라낸다. 둘째, 디스크 기반 표현에서 인메모리 그래프 자료구조로 변환한다. 이 두 단계가 합쳐져 알고리즘이 메모리 접근 비용 없이 인접 리스트를 순회할 수 있게 된다.</p>
<h2 id="6-weakly-connected-components-커뮤니티-분리">6. Weakly Connected Components: 커뮤니티 분리</h2>
<p>가중치 그래프 위에서 가장 먼저 적용되는 알고리즘은 Weakly Connected Components(WCC)다. 이 community detection 알고리즘은 두 노드 사이에 어떤 방향이든 link가 있는 노드들에 고유 ID를 부여한다. 매우 큰 데이터셋에서도 이 효율적인 알고리즘은 공유된 연결에 기반해 수만 명 단위의 client 그룹을 식별할 수 있다.</p>
<p>WCC가 사기 탐지에서 갖는 의미는 다음과 같다. 이런 커뮤니티들은 그렇지 않으면 계산적으로 비효율적이거나 전통적인 SQL 및 관계형 DB 접근으로는 발견이 불가능한 엔티티들을 연결한다. 데이터 규모가 커질수록 이질적인 연결들을 엮어주는 KG의 필요성은 더욱 커진다.</p>
<p>WCC 결과로 얻은 community ID는 후속 분석의 기본 단위가 된다. 분석가는 더 이상 수백만 client 전체를 보지 않고, 의심스러운 community 수십~수백 개만 본다. 분석 공간이 압축된다.</p>
<h2 id="7-shortest-path-community-내부-연결-추적">7. Shortest Path: Community 내부 연결 추적</h2>
<p>Community 단위로 좁히고 나서도, 그 안에 수천 명의 client가 들어있는 경우가 흔하다. 사기 네트워크는 때때로 수천 명의 client가 서로 연결될 만큼 매우 커질 수 있다. 이 안에서 특정 두 client가 어떻게 연결되는지 추적하는 데 shortest path 알고리즘이 쓰인다. 같은 weakly connected component 커뮤니티 안에서 잠재적 사기범 사이의 연결 경로를 매핑하는 데 도움이 된다.</p>
<p>같은 알고리즘이 물류 분야에서 공급망 분석과 두 지점 사이의 최단 경로 분석에도 유용하다는 점은, 그래프 알고리즘이 도메인 무관한 일반적 도구라는 사실을 보여준다. 사기 탐지에서 의미 있는 동일 연산이 공급망에서도 의미 있다는 사실은 우연이 아니다. 둘 다 &quot;관계의 비용을 따라 두 점을 잇는다&quot;는 동일한 추상에 속한다.</p>
<h2 id="8-eigenvector-centrality-사기-링의-핵심-인물-지목">8. Eigenvector Centrality: 사기 링의 핵심 인물 지목</h2>
<p>WCC가 community를 만들고 shortest path가 그 안의 경로를 추적했다면, 마지막 단계는 community의 &quot;주범&quot;을 찾는 일이다. 큰 community에서는 node centrality 측정값을 활용해 어떤 노드가 네트워크 안에서 가장 중요하거나 영향력 있는지를 식별하는 것이 유용하다. 사기 탐지에서 흔히 쓰이는 centrality 측정값 중 하나가 Eigenvector Centrality다.</p>
<p>Eigenvector Centrality는 단순 degree centrality와 다르다. Degree centrality는 &quot;연결이 많은 노드가 중요하다&quot;고 보지만, Eigenvector Centrality는 &quot;중요한 노드와 연결된 노드가 중요하다&quot;는 재귀적 정의를 사용한다. 노드 A의 중심성은 A에 연결된 모든 노드의 중심성 합에 비례한다. 이 정의는 고유벡터 방정식 형태로 풀린다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/7764a6a2-12da-4157-83f3-12cc1a8903c1/image.png" alt=""></p>
<p>이 차이가 사기 탐지에서 결정적이다. 사기 링의 주범은 종종 직접 연결 수를 의도적으로 제한한다. 노출을 피하기 위해 중간 매개자(mule)를 거치기 때문이다. 그러나 그 중간 매개자들이 다시 다수의 client와 연결되어 있다면, 주범의 중심성은 degree로는 낮아 보여도 eigenvector로는 높게 측정된다.</p>
<table>
<thead>
<tr>
<th>측정값</th>
<th>정의</th>
<th>사기 탐지 의미</th>
</tr>
</thead>
<tbody><tr>
<td>Degree Centrality</td>
<td>직접 연결 수</td>
<td>표면적 hub 탐지 — 데이터 품질 이슈 노드 포함</td>
</tr>
<tr>
<td>Betweenness Centrality</td>
<td>다른 노드들 사이의 최단 경로에 얼마나 자주 포함되는가</td>
<td>거래 흐름의 길목 — 자금 세탁 경로</td>
</tr>
<tr>
<td>Eigenvector Centrality</td>
<td>중요한 노드와의 연결 가중 합</td>
<td>사기 링의 진짜 주범 — 중간 매개자 뒤에 숨은 인물</td>
</tr>
</tbody></table>
<p>복잡한 관계 네트워크에서 잠재적으로 사기성인 노드를 드러내는 데 이 접근법이 도움이 된다. Eigenvector Centrality는 네트워크 구조에 대한 더 깊은 통찰을 제공하며, 커뮤니티 내 연결성과 흐름의 중심에 있는 노드를 부각한다. 대규모 사기 탐지에서 node centrality 알고리즘은 네트워크 안의 주요 가해자를 정확히 지목하는 데 결정적인 역할을 한다.</p>
<h2 id="9-3단계-분석-흐름-통합">9. 3단계 분석 흐름 통합</h2>
<p>지금까지의 단계들을 하나의 파이프라인으로 정리하면 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/5d61faba-004a-4e7c-8bfb-ca58ccbdda50/image.png" alt=""></p>
<p>각 단계의 역할은 다음과 같이 분리된다.</p>
<table>
<thead>
<tr>
<th>단계</th>
<th>역할</th>
<th>출력</th>
</tr>
</thead>
<tbody><tr>
<td>Super Node 필터링</td>
<td>데이터 품질·hub 노드 분리</td>
<td>정제된 분석 대상</td>
</tr>
<tr>
<td>Graph Projection</td>
<td>인메모리 최적화</td>
<td>GDS 알고리즘 입력</td>
</tr>
<tr>
<td>WCC</td>
<td>community 분리</td>
<td>community ID</td>
</tr>
<tr>
<td>Shortest Path</td>
<td>community 내 경로 추적</td>
<td>연결 경로</td>
</tr>
<tr>
<td>Eigenvector Centrality</td>
<td>community 주범 식별</td>
<td>중심성 score</td>
</tr>
</tbody></table>
<h2 id="10-수사-실무-관점">10. 수사 실무 관점</h2>
<p>이 챕터가 단순 기술 정리에 그치지 않는 이유는 수사 실무의 관점을 명시적으로 도입하기 때문이다. 사기 탐지에서는 수사팀을 돕기 위해 initial lead subject를 식별하는 것이 중요하다. 대규모 범죄 네트워크는 다루기 번거롭고 비대하다. 분석이 정확하지 않으면 잘못된 인물을 추적하게 되고, 결과적으로 수사의 정당성과 무고한 개인의 프라이버시가 모두 위협받는다.</p>
<p>이런 신중한 분석은 결과의 정확성을 보장할 뿐 아니라 수사의 무결성을 유지하고 개인의 프라이버시를 보호하는 데도 결정적이다. 정밀하고 효율적인 데이터 분석 방법을 구현하는 것은 핵심 인물을 분리하고 더 광범위한 사기 활동 네트워크를 풀어내는 데 매우 가치 있게 작용한다.</p>
<p>기술적으로는 동일한 알고리즘이라도, 출력 결과를 어떻게 운영 워크플로에 연결하는지가 시스템의 가치를 결정한다. Centrality score 상위 노드를 자동으로 차단·신고하는 것과, 수사관의 초기 lead로 제시하는 것은 완전히 다른 시스템이다. 자동화의 정도와 사람의 개입 지점을 명확히 설계해야 한다.</p>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Graph Data Science with Python and Neo4j — Chapter 10: Fraud Detection.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[임베딩 기반 추천 엔진]]></title>
            <link>https://velog.io/@tasker_dev/%EC%9E%84%EB%B2%A0%EB%94%A9-%EA%B8%B0%EB%B0%98-%EC%B6%94%EC%B2%9C-%EC%97%94%EC%A7%84</link>
            <guid>https://velog.io/@tasker_dev/%EC%9E%84%EB%B2%A0%EB%94%A9-%EA%B8%B0%EB%B0%98-%EC%B6%94%EC%B2%9C-%EC%97%94%EC%A7%84</guid>
            <pubDate>Mon, 25 May 2026 00:39:54 GMT</pubDate>
            <description><![CDATA[<p>이 챕터의 thesis는 다음과 같다. 추천은 본질적으로 &quot;관련 있는 후보를 찾는 단계&quot;와 &quot;부적절한 후보를 걸러내는 단계&quot;로 분해되며, RAG는 vector similarity로 전자를, similarity score cutoff로 후자를 수행한다. KG와 LLM이 결합되면 단순 키워드 검색이 닿지 못하는 의미 기반 추천이 가능해진다.</p>
<h2 id="1-추천-엔진의-정의와-kg의-자리">1. 추천 엔진의 정의와 KG의 자리</h2>
<p>추천 엔진은 사용자에게 가장 관심이 있을 만한 아이템을 예측하거나 제시하는 도구로 정의된다. 핵심 가치는 두 가지다. 사용자의 고유한 맥락을 반영한 personalization과, 의사결정 과정을 돕는 user engagement다. 단순히 인기 아이템을 노출하는 것과 추천 엔진을 가르는 지점이 바로 이 두 축이다.</p>
<p>전통적으로 추천 엔진은 세 갈래로 분류된다.</p>
<table>
<thead>
<tr>
<th>방식</th>
<th>동작 원리</th>
<th>강점</th>
<th>약점</th>
</tr>
</thead>
<tbody><tr>
<td>Content-Based Filtering</td>
<td>사용자가 과거 선호한 아이템의 속성에 기반해 유사 아이템 추천</td>
<td>cold-start user에 강건, 설명 가능성</td>
<td>다양성 부족, 콘텐츠 메타데이터 의존</td>
</tr>
<tr>
<td>Collaborative Filtering</td>
<td>유사한 사용자들의 선호 패턴을 활용</td>
<td>발견적(serendipity), 콘텐츠 무관</td>
<td>cold-start, sparsity</td>
</tr>
<tr>
<td>Hybrid Systems</td>
<td>위 둘을 결합</td>
<td>단점 상쇄, 포괄적 추천</td>
<td>구현·튜닝 복잡도</td>
</tr>
</tbody></table>
<p>Knowledge graph는 이 분류 위에서 특수한 위치를 차지한다. 노드와 관계 자체가 일급 객체이기 때문에, 관계를 따라가는 traversal로 풍부한 맥락 분석이 가능하고, 새로운 엔티티·관계 타입을 손쉽게 추가할 수 있으며, 그래프 알고리즘(link prediction 등)을 그대로 추천 신호로 활용할 수 있다. Content-based의 속성 기반 매칭과 collaborative의 관계 기반 패턴이 한 데이터 모델 위에서 자연스럽게 결합된다는 점이 KG가 추천 도메인에서 갖는 구조적 이점이다.</p>
<h2 id="2-특허-도메인이-어려운-이유">2. 특허 도메인이 어려운 이유</h2>
<p>이번 챕터의 예시 도메인은 특허(patent)다. 특허는 단순한 텍스트 코퍼스가 아니라, 변호사가 작성한 고도로 기술적인 문서다. 어휘는 의도적으로 모호하게 설계되고, 청구항(claim) 사이의 맥락이 중첩된다. 분석 측면에서는 두 가지 어려움이 누적된다.</p>
<p>첫째, 절대량이다. 한 분야의 특허만 수만 건 단위로 쌓이며, 인접 기술까지 포함하면 수십만 건이 된다. 둘째, 의미적 모호성이다. 같은 기술을 다른 표현으로 기술하거나, 다른 기술이 같은 용어로 표현되는 경우가 흔하다. 키워드 검색 기반 추천은 이 두 문제 모두에 취약하다. 키워드 매칭은 표현 차이에 약하고, 결과 수가 폭발할 때 ranking이 무력해진다.</p>
<p>RAG가 이 도메인에 적합한 이유가 여기서 나온다. Vector embedding은 표현 차이를 의미 공간에서 흡수하고, similarity score를 활용한 ranking과 cutoff는 결과 수 폭발을 통제한다. 이 챕터는 그 흐름을 Neo4j 위에서 구현하는 방식을 보여준다.</p>
<h2 id="3-rag-파이프라인의-골격">3. RAG 파이프라인의 골격</h2>
<p>Neo4j 기반 RAG 추천 엔진의 흐름은 다음과 같이 정리된다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/f1ccca50-6268-4198-aeb3-dbf0f93aed48/image.png" alt=""></p>
<p>이 파이프라인의 첫 단계는 Neo4j vector index 연결이다. 임베딩 모델로 특허 텍스트를 벡터로 변환해 저장하고, 쿼리 시점에 페르소나 설명이나 사용자 질의를 같은 모델로 임베딩한 뒤 cosine similarity로 인접 벡터를 검색한다. Neo4j는 이 vector index와 기존 graph traversal을 하나의 storage 안에서 결합할 수 있어, &quot;유사한 특허를 찾고, 그 특허의 발명자·인용 관계를 따라가는&quot; 식의 복합 질의가 자연스럽게 작동한다.</p>
<h2 id="4-false-positive-필터링-추천의-진짜-어려움">4. False Positive 필터링: 추천의 진짜 어려움</h2>
<p>Vector similarity 검색은 항상 결과를 반환한다. 코사인 유사도가 정의된 이상 0에 가까운 점수라도 &quot;가장 가까운 K개&quot;는 존재한다. 문제는 이 K개 중 상당수가 잘못된 매치라는 점이다. 특허처럼 의미가 미세하게 갈리는 도메인에서는 더욱 그렇다.</p>
<p>이 챕터가 강조하는 핵심 기법은 RAG의 ranking 결과에 <strong>score cutoff</strong>를 적용해 false positive를 일괄 제거하는 것이다. 페르소나 설명과 가장 유사한 후보들을 ranking한 뒤, 일정 임계값 미만의 결과는 잘라낸다. 단순해 보이지만 추천 품질에 결정적이다. Cutoff 없이 top-K만 노출하면 의미적으로 어긋난 결과까지 추천 리스트에 포함되어 사용자 신뢰가 훼손된다.</p>
<p>복잡한 도메인일수록 이 단계가 중요해진다. 특허의 경우 RAG 구현은 텍스트의 semantic essence를 유지하면서 false positive 매치를 걸러내는 데 결정적인 역할을 한다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/c7eaca77-a6c9-4412-91db-fcadfaae3923/image.png" alt=""></p>
<p>Cutoff 값의 결정은 도메인에 따라 다르다. 일반 텍스트 코퍼스에서는 0.7 전후가 흔히 쓰이지만, 특허처럼 어휘가 좁고 표현이 정형화된 도메인에서는 0.8 이상에서 비로소 의미 있는 매치가 시작되는 경우도 있다. 챕터는 구체적 수치보다 cutoff 적용 자체를 강조한다.</p>
<h2 id="5-retrievalqa-chain-llm과-kg의-결합">5. RetrievalQA Chain: LLM과 KG의 결합</h2>
<p>Vector similarity 기반 검색만으로는 &quot;유사한 특허&quot;의 리스트는 만들 수 있어도, 그 특허들이 왜 추천되는지를 자연어로 설명하기 어렵다. 이 지점에서 RetrievalQA chain이 등장한다.</p>
<p>RetrievalQA는 두 단계로 구성된다. Retrieval 단계는 외부 데이터 소스(여기서는 Neo4j)에 질의해 관련 정보를 가져온다. 이 정보는 LLM에 컨텍스트로 전달되어, 더 정보에 입각한 정확하고 관련성 있는 응답을 생성하는 데 사용된다. 결과적으로 LLM의 일반 지식과 데이터베이스의 도메인 특화 데이터가 단일 응답으로 결합된다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/554f99be-e5a7-45b3-b523-c6a462d5cab3/image.png" alt=""></p>
<p>RetrievalQA의 강점은 두 가지가 동시에 충족된다는 데 있다. 데이터베이스의 proprietary data가 응답의 사실성을 보장하고, LLM의 생성 능력이 응답의 가독성과 맥락성을 보장한다. 챗봇 형태의 추천 인터페이스에서 특히 유용한 이유다. 사용자에게 반환되는 정보가 최신이며 관련성이 있어야 하는 시나리오에 잘 맞는다.</p>
<h2 id="6-content-based-filtering의-텍스트-분석으로의-재해석">6. Content-Based Filtering의 텍스트 분석으로의 재해석</h2>
<p>이 챕터의 또 다른 메시지는 content-based filtering이 사실상 텍스트 분석 문제로 환원된다는 관찰이다. 사용자가 과거 좋아한 아이템의 속성을 추출해 유사 아이템을 추천하는 작업은, 임베딩 시대에 들어 다음과 같이 재구성된다.</p>
<ol>
<li>사용자 선호(또는 페르소나)를 자연어로 기술한다.</li>
<li>그 텍스트를 임베딩한다.</li>
<li>아이템 임베딩과의 유사도로 후보를 ranking한다.</li>
<li>Cutoff와 RetrievalQA로 결과를 정제한다.</li>
</ol>
<p>이 흐름은 별도의 feature engineering 없이도 의미적 추천을 가능하게 한다. RAG는 content-based filtering을 텍스트로부터 의미 있는 정보를 추출해 수행한다고 정리할 수 있다. 즉, 임베딩 기반 RAG는 content-based filtering의 현대적 구현체로 볼 수 있다.</p>
<h2 id="7-두-가지-결합-방식의-비교">7. 두 가지 결합 방식의 비교</h2>
<p>이 챕터에 등장하는 두 메커니즘—단순 vector similarity 검색과 RetrievalQA chain—은 추천 엔진에서 서로 다른 역할을 맡는다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>Vector Similarity Only</th>
<th>RetrievalQA Chain</th>
</tr>
</thead>
<tbody><tr>
<td>출력 형태</td>
<td>아이템 리스트 + score</td>
<td>자연어 응답 + 근거</td>
</tr>
<tr>
<td>LLM 개입</td>
<td>없음 (임베딩 단계만)</td>
<td>Retrieval 후 generation</td>
</tr>
<tr>
<td>외부 데이터 활용</td>
<td>임베딩 매칭에 한정</td>
<td>컨텍스트로 직접 주입</td>
</tr>
<tr>
<td>적합 시나리오</td>
<td>검색·필터·랭킹 UI</td>
<td>챗봇·대화형 추천</td>
</tr>
<tr>
<td>비용</td>
<td>낮음 (DB 조회만)</td>
<td>높음 (LLM 호출 추가)</td>
</tr>
<tr>
<td>응답 일관성</td>
<td>결정적</td>
<td>확률적(LLM 응답에 의존)</td>
</tr>
</tbody></table>
<p>실무 설계에서는 두 단계를 직렬로 결합하는 패턴이 흔하다. Vector similarity로 후보군을 빠르게 좁히고, cutoff로 false positive를 제거한 뒤, 사용자가 더 깊은 설명을 요청할 때만 RetrievalQA chain을 호출하는 식이다. 이는 LLM 호출 비용을 통제하면서 설명 가능성을 확보하는 절충안이다.</p>
<h2 id="8-정리">8. 정리</h2>
<p>이 챕터의 결론부는 단순한 기술 정리에 그치지 않고, IP·R&amp;D 같은 첨단 분야에서의 전략적 가치를 강조한다. 추천 엔진은 대규모 데이터를 훑으면서, 그렇지 않았다면 결코 발견되지 않았을 연결과 통찰을 식별하는 능력을 제공한다. 지식재산권, 연구개발, 그 외 최첨단 분야의 전문가에게 이런 도구는 경쟁에서 앞서가고, 혁신하며, 전략적 우위를 확보하는 수단이 된다.</p>
<p>KG와 LLM의 결합은 단순한 검색 자동화가 아니라, <strong>사람의 인지 한계를 보완하는 도구</strong>로 자리잡는다. 수만 건의 특허 중에서 어떤 두 건이 연결되는지를 사람이 모두 검토하는 것은 불가능하지만, 임베딩 유사도와 그래프 관계를 결합한 시스템은 이를 일상적인 질의로 수행한다.</p>
<blockquote>
<p>결론: 추천은 &quot;찾기&quot;와 &quot;거르기&quot;의 직렬 결합이며, RAG는 vector similarity로 찾고 cutoff로 거른다. KG와 LLM이 그 위에 더해질 때, 추천은 비로소 설명 가능해진다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Graph Data Science with Python and Neo4j — Chapter 9: Recommendation Engines Using Embeddings.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[GNN으로 푸는 Node Classification과 Link Prediction]]></title>
            <link>https://velog.io/@tasker_dev/GNN%EC%9C%BC%EB%A1%9C-%ED%91%B8%EB%8A%94-Node-Classification%EA%B3%BC-Link-Prediction</link>
            <guid>https://velog.io/@tasker_dev/GNN%EC%9C%BC%EB%A1%9C-%ED%91%B8%EB%8A%94-Node-Classification%EA%B3%BC-Link-Prediction</guid>
            <pubDate>Mon, 25 May 2026 00:28:46 GMT</pubDate>
            <description><![CDATA[<p>이전 편에서 graph representation learning과 message passing의 일반론을 다뤘다. 이번 챕터는 그 위에서 실제로 두 가지 대표 태스크—node classification과 link prediction—를 어떻게 동일한 framework로 푸는지, 그리고 모델 선택이 어디서 갈리는지를 본다. 본 글의 thesis는 다음과 같다.</p>
<blockquote>
<p>Node classification과 link prediction은 입력 형태(homogeneous vs heterogeneous)와 decoder(log_softmax vs dot product)만 다를 뿐, 동일한 encoder–decoder 골격을 공유한다. 그 위에서 SAGE는 두 태스크 모두에서 가장 균형 잡힌 선택지로 드러난다.</p>
</blockquote>
<h2 id="1-두-태스크를-하나의-프레임워크로">1. 두 태스크를 하나의 프레임워크로</h2>
<p>Node classification은 그래프의 각 노드에 라벨을 붙이는 문제다. AML(Anti-Money Laundering) 시나리오에서는 비트코인 거래 네트워크의 각 노드(계정·거래)를 licit(합법)/illicit(불법)로 분류한다. Link prediction은 노드 사이에 존재하지 않은 엣지가 미래에 형성될 가능성을 예측하는 문제다. 영화 추천 시나리오에서는 사용자–영화 쌍이 평점(rating) 관계로 연결될지를 예측한다.</p>
<p>태스크와 도메인이 다르지만, 둘 다 동일한 end-to-end framework로 처리된다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/b8b3bb8d-fe66-48ec-82f7-d50ebf4670aa/image.png" alt=""></p>
<p>Encoder는 두 태스크 모두 GNN이 담당하고, decoder만 downstream task에 맞춰 교체된다. PyTorch Geometric(PyG)를 기반으로 GCN, GraphSAGE(이하 SAGE), GAT 세 아키텍처를 비교하는 흐름이 챕터 전체를 관통한다.</p>
<h2 id="2-node-classification-aml-시나리오">2. Node Classification: AML 시나리오</h2>
<h3 id="21-데이터와-문제-설정">2.1 데이터와 문제 설정</h3>
<p>Elliptic 데이터셋은 약 20만 개의 비트코인 거래(노드), 23만 4천 개의 directed payment edge, 노드당 166개의 피처를 갖는 time-series 그래프다. 핵심 도전 과제는 심각한 클래스 불균형이다.</p>
<table>
<thead>
<tr>
<th>클래스</th>
<th>비율</th>
</tr>
</thead>
<tbody><tr>
<td>Unknown</td>
<td>77.15%</td>
</tr>
<tr>
<td>Licit</td>
<td>20.62%</td>
</tr>
<tr>
<td>Illicit</td>
<td>2.23%</td>
</tr>
</tbody></table>
<p>Illicit 노드가 전체의 2.23%에 불과하다는 사실이 평가 지표 선택과 loss 설계에 직접 영향을 준다.</p>
<h3 id="22-encoderdecoder-구성">2.2 Encoder–Decoder 구성</h3>
<p>Encoder는 homogeneous GNN이다. 노드 타입과 엣지 타입이 각각 하나뿐인 그래프를 가정한 두 레이어 구조로, PyG가 제공하는 어떤 convolution layer든 끼워 넣을 수 있다.</p>
<p>Decoder는 <code>log_softmax</code> + <code>CrossEntropyLoss</code> 조합이다. 일반 softmax 대신 log_softmax를 쓰는 이유는 수치 안정성이다. Softmax의 분자·분모에서 극단적으로 크거나 작은 값이 등장할 때 log 공간에서 계산을 진행하면 overflow/underflow를 피할 수 있다. CrossEntropyLoss는 내부적으로 log-softmax와 negative log-likelihood를 결합하므로 둘은 자연스럽게 짝을 이룬다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/a41e4f9e-d383-46ec-8ee6-69b40e40fa61/image.png" alt=""></p>
<h3 id="23-모델-비교-학습-효율">2.3 모델 비교: 학습 효율</h3>
<p>세 모델의 파라미터 수와 학습 시간은 다음과 같다.</p>
<table>
<thead>
<tr>
<th>Encoder</th>
<th>Parameters</th>
<th>Training time</th>
</tr>
</thead>
<tbody><tr>
<td>GCN</td>
<td>2,723</td>
<td>19.02s</td>
</tr>
<tr>
<td>SAGE</td>
<td>5,427</td>
<td>36.71s</td>
</tr>
<tr>
<td>GAT</td>
<td>22,025</td>
<td>43.45s</td>
</tr>
</tbody></table>
<p>학습 시간은 파라미터 수와 직결된다. GAT는 이웃 엣지마다 learnable attention coefficient를 도입하므로 파라미터가 GCN의 약 8배다. 효율만 보면 GCN이 가장 가볍다.</p>
<h3 id="24-평가-지표-해석과-sage의-우위">2.4 평가 지표 해석과 SAGE의 우위</h3>
<p>AML 맥락에서 세 지표의 의미는 다음과 같다.</p>
<ul>
<li><strong>Precision</strong>: 모델이 licit 혹은 illicit이라고 판단했을 때, 그 판단이 맞을 확률</li>
<li><strong>Recall</strong>: 실제 licit·illicit 노드 중 모델이 찾아낸 비율</li>
<li><strong>F1-score</strong>: 위 둘의 조화 평균</li>
</ul>
<p>심각한 클래스 불균형 때문에 weighted average를 사용해 분포를 반영한 평가가 적합하다. 단순 macro average는 illicit 2.23% 클래스의 변동이 과대 반영되어 모델 선택을 왜곡할 수 있다.</p>
<p>세 모델 중 SAGE가 가장 우수한 성능을 보인다. illicit 노드의 약 83%, licit 노드의 약 99%를 정확히 분류한다. SAGE의 균형 잡힌 정확도와 최소화된 오분류는 illicit 노드 탐지가 결정적인 AML 같은 응용에 가장 적합한 선택지를 만든다.</p>
<h2 id="3-link-prediction-영화-추천-시나리오">3. Link Prediction: 영화 추천 시나리오</h2>
<h3 id="31-문제-설정과-그래프-타입의-전환">3.1 문제 설정과 그래프 타입의 전환</h3>
<p>추천 시스템은 사용자–아이템 상호작용을 엣지로 모델링하는 link prediction의 대표 응용이다. AML과 달리 사용자와 영화는 본질적으로 다른 타입의 노드이며, 둘 사이의 &quot;rates&quot; 관계는 별개의 엣지 타입이다. 따라서 데이터 구조부터 달라진다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>Node Classification (AML)</th>
<th>Link Prediction (Recommendation)</th>
</tr>
</thead>
<tbody><tr>
<td>Graph type</td>
<td>Homogeneous</td>
<td>Heterogeneous</td>
</tr>
<tr>
<td>Data class</td>
<td><code>Data</code></td>
<td><code>HeteroData</code></td>
</tr>
<tr>
<td>노드 타입</td>
<td>1개 (transaction)</td>
<td>2개 (user, movie)</td>
</tr>
<tr>
<td>엣지 타입</td>
<td>1개</td>
<td>1개 (rates) + reverse</td>
</tr>
</tbody></table>
<p>HeteroData는 노드 타입별로 피처를 분리하고, <code>edge_index</code>를 특정 관계(여기서는 rates)에 연결한다. 또한 GNN의 message passing이 user→movie와 movie→user 양방향으로 흐르도록 reverse edge를 명시적으로 추가해야 한다.</p>
<h3 id="32-random-link-split의-핵심-edge-disjoint">3.2 Random Link Split의 핵심: Edge Disjoint</h3>
<p>Link prediction에서 가장 헷갈리는 부분이자 가장 중요한 디테일은 데이터 분할이다. PyG의 <code>RandomLinkSplit</code>은 일반적인 dataset split과는 다른 두 가지를 고려한다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/90c134a5-866a-42ba-b7d4-ad33a8c25b2e/image.png" alt=""></p>
<p><code>disjoint_train_ratio</code> 파라미터는 training edge를 두 그룹으로 다시 나눈다. 하나는 message passing에 쓰이는 <code>edge_index</code>이고, 다른 하나는 학습 supervision 신호로 쓰이는 <code>edge_label_index</code>다. 두 집합은 반드시 disjoint여야 한다. 동일한 엣지가 message passing과 supervision에 동시에 등장하면 모델은 자신이 이미 본 정답을 그대로 학습 신호로 받게 된다. 이른바 edge leakage다. 한편 reverse edge는 message passing에는 쓰이지만 link prediction 학습에는 쓰이지 않는다.</p>
<p>대규모 그래프에서는 <code>LinkNeighborLoader</code>로 미니배치를 구성한다. 입력 엣지 집합에서 일부를 샘플링하고, 그 엣지에 등장한 노드들로부터 정해진 수의 이웃을 반복적으로 샘플링해 subgraph를 만든다. 작은 그래프에서는 생략 가능하지만, CPU/GPU 메모리를 넘어서는 그래프에서는 필수다.</p>
<h3 id="33-user-vs-movie-피처-부재를-다루는-비대칭-임베딩">3.3 User vs Movie: 피처 부재를 다루는 비대칭 임베딩</h3>
<p>사용자는 본질 피처(intrinsic feature)가 없는 경우가 많다. 영화는 장르 같은 명시적 피처가 있다. 이 비대칭이 임베딩 전략의 비대칭으로 이어진다.</p>
<table>
<thead>
<tr>
<th>노드 타입</th>
<th>임베딩 전략</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td>User</td>
<td>Single-step: embedding matrix만 사용</td>
<td>본질 피처가 없음, 학습 중에 embedding 자체가 학습됨</td>
</tr>
<tr>
<td>Movie</td>
<td>Two-step: linear transformation + embedding layer</td>
<td>20-dim 장르 벡터를 변환한 뒤 embedding과 결합</td>
</tr>
</tbody></table>
<p>User의 embedding은 학습 데이터에 등장한 user ID에 대해서만 의미가 생긴다. 이 점이 cold-start 문제로 직결된다.</p>
<h3 id="34-decoder-dot-product--bcewithlogitsloss">3.4 Decoder: Dot Product + BCEWithLogitsLoss</h3>
<p>Link prediction의 decoder는 user embedding과 movie embedding의 dot product다. 값이 클수록 두 노드가 연결될 가능성이 높다고 해석한다. 출력은 <code>binary_cross_entropy_with_logits</code>로 sigmoid 활성화와 BCE loss를 한 번에 처리한다. Sigmoid가 dot product를 확률로 변환하고, BCE가 link 존재 여부와의 차이를 측정한다.</p>
<p>여기서 negative sampling이 필수가 된다. 실제 그래프에는 존재하는 엣지(positive)만 있으므로, &quot;존재하지 않는 엣지&quot;를 만들어내 negative 신호로 공급해야 한다. PyG에서는 <code>neg_sampling_ratio=2</code> 같은 설정으로 positive당 negative 수를 지정한다.</p>
<h3 id="35-to_hetero-homogeneous-모델의-자동-변환">3.5 <code>to_hetero()</code>: Homogeneous 모델의 자동 변환</h3>
<p>PyG의 <code>to_hetero()</code> 함수는 homogeneous GNN 모델을 heterogeneous 버전으로 자동 변환한다. HeteroSAGE를 비롯한 모든 heterogeneous 모델은 동일한 homogeneous 모델로부터 파생되며, 변환 결과가 heterogeneous 그래프의 각 엣지 타입에 적용된다. HeteroData 객체의 <code>metadata()</code> 메서드가 노드·엣지 타입 정보를 제공하고, convolution 연산은 이 metadata가 명시한 노드·엣지에 따라 진행된다.</p>
<h3 id="36-gcnconv-vs-graphconv-흔히-놓치는-디테일">3.6 GCNConv vs GraphConv: 흔히 놓치는 디테일</h3>
<p>같은 &quot;GCN&quot;이라는 이름을 써도 PyG에서는 그래프 타입에 따라 다른 operator를 쓴다. Homogeneous 그래프에서는 degree normalization을 포함한 canonical layer인 GCNConv를, heterogeneous 그래프에서는 GraphConv를 쓴다.</p>
<table>
<thead>
<tr>
<th>Operator</th>
<th>대상 그래프</th>
<th>핵심 차이</th>
</tr>
</thead>
<tbody><tr>
<td>GCNConv</td>
<td>Homogeneous</td>
<td>단일 노드·엣지 타입 가정, degree normalization</td>
</tr>
<tr>
<td>GraphConv</td>
<td>Heterogeneous</td>
<td>root와 neighbor 변환을 분리, HeteroConv wrapper와 호환</td>
</tr>
</tbody></table>
<p>GCNConv는 단일 노드·엣지 타입을 가정하므로 heterogeneous 그래프에 직접 적용할 수 없고, GraphConv는 heterogeneous 환경에서 필요한 root·neighbor 분리 변환을 지원한다. 이 차이는 코드를 읽다가 &quot;왜 GCN인데 GCNConv가 아니지&quot;라는 의문이 들 때 정확히 부딪치는 지점이다.</p>
<h2 id="4-link-prediction-성능-비교-node-classification과는-다른-결과">4. Link Prediction 성능 비교: Node Classification과는 다른 결과</h2>
<p>학습 효율과 파라미터 수는 다음과 같이 달라진다.</p>
<table>
<thead>
<tr>
<th>Encoder</th>
<th>Parameters</th>
<th>Training time</th>
</tr>
</thead>
<tbody><tr>
<td>GCN</td>
<td>713,408</td>
<td>826s</td>
</tr>
<tr>
<td>SAGE</td>
<td>713,408</td>
<td>777s</td>
</tr>
<tr>
<td>GAT</td>
<td>1,066,880</td>
<td>956s</td>
</tr>
</tbody></table>
<p>Node classification 대비 파라미터 수가 큰 폭으로 늘었다. 이유는 두 가지다. 첫째, user와 movie의 표현력을 높이기 위해 embedding layer가 추가됐다. 둘째, heterogeneous GNN으로 두 종류의 노드를 모두 처리해야 한다.</p>
<p>이번에는 SAGE가 가장 효율적이고, GAT가 가장 느리며, GCN이 중간이다. Node classification에서는 GCN이 가장 가벼웠는데, link prediction에서는 GCN과 SAGE의 파라미터가 동일함에도 SAGE의 학습 시간이 더 짧다. Encoder 내부 구현 차이가 데이터 통과 비용에 다르게 작용한 결과다.</p>
<h3 id="41-비즈니스-함의가-갈리는-지점">4.1 비즈니스 함의가 갈리는 지점</h3>
<p>추천 맥락에서 지표는 이렇게 읽힌다.</p>
<ul>
<li><strong>Precision</strong>: 모델이 추천한 영화 중 사용자가 실제로 평가할 비율 → false positive 비용</li>
<li><strong>Recall</strong>: 사용자가 평가할 영화 중 모델이 잡아낸 비율 → 추천 coverage</li>
</ul>
<p>세 모델의 성능은 다음과 같이 나뉜다.</p>
<table>
<thead>
<tr>
<th>모델</th>
<th>강점</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>SAGE</td>
<td>최고 Precision, 최고 F1</td>
<td>불필요한 추천 최소화, 균형</td>
</tr>
<tr>
<td>GCN</td>
<td>최고 Recall</td>
<td>사용자 잠재 관심사 최대 포착</td>
</tr>
<tr>
<td>GAT</td>
<td>—</td>
<td>과추천 경향</td>
</tr>
</tbody></table>
<p>SAGE는 false positive를 최소화한다. 추천 시스템에서 이는 &quot;쓸데없는 추천이 적다&quot;로 번역된다. GCN은 사용자가 평가할 가능성이 있는 영화를 가장 많이 포착한다. 추천의 포괄성이 중요한 시나리오—예컨대 신규 콘텐츠 노출이 목표인 경우—에서 적합하다. GAT는 평가 가능성이 낮은 영화까지 과도하게 추천하는 경향을 보인다.</p>
<p>F1을 기준으로 하면 SAGE가 정확성과 포괄성 사이에서 가장 좋은 균형을 만든다. 결국 두 챕터의 시나리오 모두에서 SAGE가 최선의 균형점을 차지한다는 일관된 메시지가 나온다.</p>
<h2 id="5-두-태스크-통합-비교">5. 두 태스크 통합 비교</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>Node Classification (AML)</th>
<th>Link Prediction (Recommendation)</th>
</tr>
</thead>
<tbody><tr>
<td>Graph type</td>
<td>Homogeneous</td>
<td>Heterogeneous</td>
</tr>
<tr>
<td>Data structure</td>
<td><code>Data</code></td>
<td><code>HeteroData</code></td>
</tr>
<tr>
<td>Decoder</td>
<td><code>log_softmax</code></td>
<td>dot product</td>
</tr>
<tr>
<td>Loss</td>
<td><code>CrossEntropyLoss</code></td>
<td><code>BCEWithLogitsLoss</code></td>
</tr>
<tr>
<td>Split 방식</td>
<td>수동 node masking</td>
<td><code>RandomLinkSplit</code></td>
</tr>
<tr>
<td>Edge usage 분리</td>
<td>불필요</td>
<td>message passing vs supervision (disjoint)</td>
</tr>
<tr>
<td>Mini-batching</td>
<td>불필요</td>
<td><code>LinkNeighborLoader</code> 필수</td>
</tr>
<tr>
<td>Negative sampling</td>
<td>불필요</td>
<td>필수 (<code>neg_sampling_ratio</code> 지정)</td>
</tr>
<tr>
<td>Reverse edge</td>
<td>불필요</td>
<td>필수 (<code>T.ToUndirected()</code>)</td>
</tr>
<tr>
<td>권장 모델</td>
<td>SAGE (정밀+균형)</td>
<td>SAGE (F1) / GCN (Recall)</td>
</tr>
<tr>
<td>핵심 어려움</td>
<td>클래스 불균형 (illicit 2.23%)</td>
<td>edge leakage 방지, 이종 노드 임베딩 설계</td>
</tr>
</tbody></table>
<p>이 표는 두 태스크가 같은 framework 위에서 어떤 결정점에서 갈라지는지를 한눈에 보여준다. Framework는 같지만 의사결정 지점은 8개 이상 분기한다.</p>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Knowledge Graphs and LLMs in Action. Manning, 2024 — Chapter Chapter 12 — Node classification and link prediction with GNNs.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[그래프 표현 학습과 GNN]]></title>
            <link>https://velog.io/@tasker_dev/%EA%B7%B8%EB%9E%98%ED%94%84-%ED%91%9C%ED%98%84-%ED%95%99%EC%8A%B5%EA%B3%BC-GNN</link>
            <guid>https://velog.io/@tasker_dev/%EA%B7%B8%EB%9E%98%ED%94%84-%ED%91%9C%ED%98%84-%ED%95%99%EC%8A%B5%EA%B3%BC-GNN</guid>
            <pubDate>Mon, 25 May 2026 00:19:43 GMT</pubDate>
            <description><![CDATA[<p>이전 편까지의 GraphRAG 시리즈는 지식 그래프를 &quot;구성&quot;하고 &quot;탐색&quot;하는 문제를 다뤘다. 텍스트로부터 entity를 추출하고, 관계를 정제하고, Cypher로 traversal하는 흐름이다. 그러나 그래프 위에서 ML 모델을 직접 학습시키려면 또 하나의 단계가 필요하다. 노드·엣지·subgraph를 vector 공간에 사상(寫像)하는 단계, 즉 graph representation learning(GRL)이다. 이 글의 thesis는 다음 한 문장으로 압축된다.</p>
<blockquote>
<p>그래프의 정보를 vector로 옮기는 작업은 lookup table(shallow)에서 출발해 message passing(GNN)으로 진화했고, 이제 LLM과 결합되는 지점에서 GraphRAG의 다음 세대를 형성한다.</p>
</blockquote>
<h2 id="1-grl이-풀려는-문제-왜-임베딩인가">1. GRL이 풀려는 문제: 왜 임베딩인가</h2>
<p>전통적인 그래프 분석은 PageRank, community detection처럼 사람이 설계한 알고리즘에 의존했다. 노드 분류, 링크 예측, 그래프 분류 같은 downstream task에 ML을 적용하려면 그래프를 vector로 변환해야 하는데, 그래프는 일반적인 tensor와 달리 노드마다 이웃 수와 구조가 다르다. 이 불규칙성이 일반 ML 기법의 직접 적용을 막는다.</p>
<p>GRL은 graph structure와 node attribute로부터 dense vector representation(embedding)을 자동 학습한다. 학습된 embedding은 그래프의 structural·semantic 성질을 보존하면서, 표준 ML 알고리즘이 그대로 소비할 수 있는 형태다. 이 변환이 성립하면 노드 분류, 클러스터링, 추천, 링크 예측이 모두 단일 representation 위에서 풀린다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/eb1f3b1d-687d-409a-92aa-eeddda92470d/image.png" alt=""></p>
<p>GRL의 발전은 세 세대로 정리된다. 1세대는 전통 수학·CS에서 출발한 graph embedding, 2세대는 word2vec의 충격을 그래프로 옮긴 Node2Vec 계열, 3세대는 deep learning과 결합한 GNN이다. 각 세대는 이전 세대의 한계를 직접 겨냥하면서 등장했다.</p>
<h2 id="2-임베딩-공간의-기하학-euclidean과-그-너머">2. 임베딩 공간의 기하학: Euclidean과 그 너머</h2>
<p>대부분의 ML 모델은 Euclidean space에서 작동한다. 직선 거리, 피타고라스 정리, 내적이 그대로 통한다. 그러나 그래프, 특히 계층적(hierarchical) 그래프는 이 공간에서 잘 표현되지 않는다. 예컨대 ontology, 분류 체계, citation network처럼 트리 구조에 가까운 그래프는 깊이가 깊어질수록 노드 수가 지수적으로 늘어나는데, Euclidean space에서는 이 폭발적 공간을 담을 자리가 부족하다.</p>
<p>이 지점에서 등장하는 것이 hyperbolic space다. Hyperbolic space는 중심에서 바깥으로 갈수록 사용 가능한 공간이 지수적으로 늘어난다. 트리가 한 단계 내려갈 때마다 가지가 더 벌어지는 구조와 자연스럽게 들어맞는다. 이런 관점을 일반화한 분야가 geometric deep learning(GDL)이다.</p>
<table>
<thead>
<tr>
<th>공간</th>
<th>거리 성질</th>
<th>적합한 그래프</th>
</tr>
</thead>
<tbody><tr>
<td>Euclidean</td>
<td>직선 거리, 다항식적 공간 성장</td>
<td>평탄한 social network, 일반 그래프</td>
</tr>
<tr>
<td>Hyperbolic</td>
<td>지수적 공간 성장</td>
<td>계층적 구조, ontology, 분류 트리</td>
</tr>
<tr>
<td>Spherical</td>
<td>곡률 양수, 유한한 표면</td>
<td>주기적/순환적 관계</td>
</tr>
</tbody></table>
<h2 id="3-위치-vs-구조-임베딩이-보존하는-것">3. 위치 vs 구조: 임베딩이 보존하는 것</h2>
<p>두 노드가 &quot;비슷하다&quot;는 말은 한 가지 의미가 아니다. Positional embedding은 그래프 내 절대 위치를 보존한다. 두 노드가 가까운 위치에 있고 비슷한 이웃을 공유하면 embedding이 가깝다. Structural embedding은 상대적 패턴을 보존한다. 두 노드가 그래프상 멀리 떨어져 있어도 친구 관계의 패턴(예: 모두 hub처럼 행동)이 비슷하면 embedding이 가깝다.</p>
<p>이 구분은 task 선택에 직결된다. Link prediction과 clustering처럼 그래프 전체의 topology가 중요한 unsupervised task에서는 positional embedding이 효과적이다. 반면 node classification과 graph classification에서는 local neighborhood pattern을 잡아내는 structural embedding(특히 GNN 기반)이 우세하다.</p>
<h2 id="4-transductive-vs-inductive-새-노드를-만났을-때">4. Transductive vs Inductive: 새 노드를 만났을 때</h2>
<p>Transductive 방법은 고정된 노드 집합에 대해서만 embedding을 학습한다. 학습 그래프에 없던 노드가 들어오면 embedding을 만들 수 없다. 특정 퍼즐을 잘 푸는 법은 배웠지만, 다른 퍼즐로 옮겨가지 못하는 상태다. Static 그래프에서는 충분하지만, 실시간으로 노드가 추가되는 서비스 환경에서는 곤란하다.</p>
<p>Inductive 방법은 node feature를 입력으로 받는 parametric mapping을 학습한다. 학습 대상은 embedding 자체가 아니라 mapping function이다. 새 노드가 들어와도 feature만 있으면 embedding을 즉시 생성할 수 있다. 대부분의 현대 GNN이 inductive로 설계되는 이유다.</p>
<h2 id="5-encoderdecoder-통합-관점">5. Encoder–Decoder 통합 관점</h2>
<p>서로 다른 임베딩 기법을 한 그림으로 묶는 가장 깔끔한 framework가 encoder–decoder다. Encoder는 raw graph data를 embedding으로 변환한다. Decoder는 그 embedding으로부터 원본 그래프의 성질(예: 어떤 두 노드가 연결되어 있었는가)을 재구성한다. Decoder의 예측과 실제 그래프 성질의 차이를 최소화하면 encoder는 의미 있는 representation을 학습하게 된다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/0063e9d8-b4a9-4fbe-a349-04c4d064c379/image.png" alt=""></p>
<p>이 framework 안에서 Node2Vec은 random walk를 통해 noise가 섞인 &quot;이웃 동시 출현&quot; 신호를 만들고, encoder가 이를 softmax decoder로 복원하도록 학습한다. Node2Vec의 특징은 walk의 폭(BFS적 탐색)과 깊이(DFS적 탐색) 사이를 hyperparameter로 조절한다는 점이다. 폭을 키우면 structural role을, 깊이를 키우면 community 소속을 더 잘 잡아낸다.</p>
<h2 id="6-shallow-embedding의-네-가지-한계">6. Shallow Embedding의 네 가지 한계</h2>
<p>Node2Vec, DeepWalk, LINE 같은 초기 기법은 모두 shallow embedding이다. Encoder가 단순한 lookup table로 동작한다. 즉 노드 ID를 키로 vector를 꺼내오는 구조다. 이 단순함은 명확한 한계를 동반한다.</p>
<table>
<thead>
<tr>
<th>한계</th>
<th>내용</th>
<th>실무 영향</th>
</tr>
</thead>
<tbody><tr>
<td>Parameter inefficiency</td>
<td>노드 수에 비례해 파라미터가 선형 증가</td>
<td>대규모 그래프에서 메모리 한계</td>
</tr>
<tr>
<td>No parameter sharing</td>
<td>노드 간 패턴 공유 불가</td>
<td>일반화 능력 부족</td>
</tr>
<tr>
<td>Feature blindness</td>
<td>노드 속성(텍스트·범주 등)을 활용하지 않음</td>
<td>풍부한 정보 손실</td>
</tr>
<tr>
<td>Transductive nature</td>
<td>학습 시 본 노드에 대해서만 embedding 생성</td>
<td>신규 노드 처리 불가</td>
</tr>
</tbody></table>
<p>이 네 한계가 GNN 등장의 직접적인 동기다. GNN은 (1) parametric function으로 파라미터를 공유하고, (2) node feature를 입력으로 받으며, (3) inductive하게 새 노드에도 대응한다.</p>
<h2 id="7-knowledge-graph-embedding-관계까지-vector로">7. Knowledge Graph Embedding: 관계까지 vector로</h2>
<p>일반 그래프 embedding이 &quot;두 노드가 연결되는가&quot;를 다룬다면, knowledge graph(KG) embedding은 &quot;어떻게 연결되는가&quot;까지 다룬다. (h, r, t) triple에서 head·relation·tail이 각각 vector 공간에서 어떻게 배치되어야 하는지가 핵심이다.</p>
<p>대부분 KG는 sparse하다. 가능한 (h, r, t) 조합의 대부분은 실제로 존재하지 않는다. 이 불균형을 다루기 위해 negative sampling + cross-entropy loss가 표준이 되었다. Negative sampling 전략은 두 갈래다. Type-constrained sampling은 관계의 의미에 맞는 후보만 negative로 뽑는다(예: &quot;수도이다&quot; 관계의 tail은 도시여야 함). Adversarial sampling은 진짜와 헷갈리기 쉬운 hard negative를 의도적으로 생성한다.</p>
<p>Multirelational decoder는 세 계열이 대표적이다.</p>
<table>
<thead>
<tr>
<th>디코더 계열</th>
<th>대표 모델</th>
<th>핵심 아이디어</th>
<th>강점</th>
<th>약점</th>
</tr>
</thead>
<tbody><tr>
<td>Translation 기반</td>
<td>TransE</td>
<td>h + r ≈ t</td>
<td>compositional pattern</td>
<td>many-to-one 관계</td>
</tr>
<tr>
<td>Matrix 기반</td>
<td>RESCAL</td>
<td>관계 = 변환 행렬</td>
<td>표현력</td>
<td>파라미터 폭증</td>
</tr>
<tr>
<td>Semantic matching</td>
<td>DistMult, ComplEx</td>
<td>관계별 유사도 측정</td>
<td>asymmetric 관계(ComplEx)</td>
<td>composition</td>
</tr>
</tbody></table>
<p>TransE는 compositional 패턴(국가→수도, 수도→국가)에 강하지만 many-to-one에서 흔들린다. ComplEx는 complex number를 도입해 비대칭 관계(예: parent_of vs child_of)를 우아하게 다루지만 composition이 약하다. 모델 선택은 KG의 관계 분포에 따라 달라진다.</p>
<h2 id="8-gnn의-핵심-message-passing">8. GNN의 핵심: Message Passing</h2>
<p>GNN은 shallow embedding의 한계를 neural message passing이라는 메커니즘으로 돌파한다. 직관적으로는 그래프 위에서 동시에 진행되는 정교한 대화에 가깝다. 각 노드는 매 iteration마다 (1) 이웃으로부터 메시지를 수집하고, (2) 그 메시지를 가공해 의미 있는 정보를 추출하고, (3) 그 결과로 자신의 representation을 갱신한다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/fa145a3d-2f0d-4e65-bd3b-20d6354b98d5/image.png" alt=""></p>
<p>수식 수준에서는 두 함수가 반복된다.</p>
<pre><code>m_v^(k) = AGGREGATE^(k)( { h_u^(k-1) : u ∈ N(v) } )
h_v^(k) = UPDATE^(k)( h_v^(k-1), m_v^(k) )</code></pre><p>AGGREGATE는 이웃의 representation을 모아 하나의 메시지로 합치고(mean, sum, max, attention 등), UPDATE는 그 메시지로 자기 표현을 갱신한다. k iteration 후 노드 v의 representation은 k-hop 이웃까지의 정보를 담는다.</p>
<p>이 구조의 결정적인 함의는 다음과 같다. Shallow embedding은 노드 ID마다 별개 vector를 학습했지만, GNN은 AGGREGATE와 UPDATE의 파라미터를 모든 노드가 공유한다. 따라서 graph structure의 일반 법칙을 학습할 수 있고, 학습 시 보지 못한 노드에도 동일한 함수를 적용해 inductive하게 embedding을 만든다.</p>
<h2 id="9-깊은-gnn의-적-over-smoothing과-그-처방">9. 깊은 GNN의 적: Over-smoothing과 그 처방</h2>
<p>Iteration을 늘리면 더 먼 이웃 정보를 가져올 수 있지만, layer가 깊어질수록 모든 노드 representation이 비슷해지는 over-smoothing이 발생한다. 그래프 전체가 한 점으로 수렴하는 효과다. 현대 GNN 설계는 이 문제에 대한 일련의 처방으로 구성된다.</p>
<p><strong>Skip connection</strong>은 이전 layer의 표현을 우회 경로로 보존한다. ResNet에서 가져온 발상이다. 노드 v의 원래 feature가 깊은 layer에서도 살아남아, 과도한 평활화를 막는다.</p>
<p><strong>Attention 메커니즘</strong>은 이웃 기여도에 학습 가능한 가중치를 부여한다. 모든 이웃이 같은 비중이라는 가정을 버리는 것이다. Graph attention network(GAT)는 transformer가 sequence의 각 토큰에 대해 다른 토큰들을 attend하는 방식과 평행하게, 노드가 이웃을 선택적으로 attend하게 한다. Multihead attention은 여러 attention 메커니즘을 병렬로 운영해 이웃 관계의 서로 다른 측면을 동시에 학습한다.</p>
<p><strong>Normalization</strong>은 노드마다 이웃 수가 천차만별인 상황에서 출력 분포를 안정화한다. Kipf와 Welling의 GCN이 도입한 symmetric normalization은 source·target 노드의 degree를 모두 반영해 메시지 강도를 조정한다. Citation network에서는 매우 많이 인용된 논문(degree가 큰 노드)의 영향력을 적절히 낮추는 효과가 있다. 단, normalization은 lossy operation이다. 정규화 후에는 degree가 다른 노드를 embedding으로 구분하기 어려워진다는 대가가 있다.</p>
<p><strong>Jumping knowledge network</strong>는 각 layer의 representation을 모두 보관했다가 마지막에 결합한다. 노드마다 적절한 hop 수가 다를 수 있다는 관찰에서 출발한다. 어떤 노드는 1-hop 정보가 결정적이고, 어떤 노드는 3-hop까지 봐야 한다. 학습이 노드별로 적절한 layer를 선택하게 한다.</p>
<h2 id="10-gnn과-llm의-결합-graphrag가-가는-방향">10. GNN과 LLM의 결합: GraphRAG가 가는 방향</h2>
<p>이 챕터의 결론부는 그래프와 언어 모델의 상보성이다. GNN은 message passing으로 구조적 정보를 잘 다루지만, 노드·엣지에 붙은 풍부한 텍스트는 잘 다루지 못한다. LLM은 자연어 이해와 생성에 탁월하지만, 그래프의 복잡한 구조적 관계는 본질적으로 sequential 처리 모델로는 자연스럽지 않다. 통합 방식은 세 갈래다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/57c8c4b1-0fc9-47db-85d8-ae71f8df9377/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>통합 방식</th>
<th>역할 분담</th>
<th>적합 시나리오</th>
</tr>
</thead>
<tbody><tr>
<td>LLM as Predictor</td>
<td>그래프를 sequence로 변환 → LLM이 최종 출력</td>
<td>KG QA, 생성형 추천</td>
</tr>
<tr>
<td>LLM as Encoder</td>
<td>LLM이 텍스트 feature 추출 → GNN이 구조 처리</td>
<td>text-rich 노드를 가진 분류/예측</td>
</tr>
<tr>
<td>LLM as Aligner</td>
<td>LLM과 GNN을 contrastive 학습으로 정렬</td>
<td>멀티모달 representation</td>
</tr>
</tbody></table>
<p>세 방식 중 실무에서 가장 자주 마주치는 것은 LLM as Encoder다. 그래프의 노드가 논문 초록, 제품 설명, 환자 기록 같은 텍스트를 가질 때, LLM 임베딩을 node feature로 GNN에 주입하는 구조는 비교적 단순하면서도 효과가 분명하다. GraphRAG의 retrieval 단계에서 텍스트 chunk 임베딩과 그래프 구조를 함께 활용하는 시도들이 대체로 이 카테고리에 속한다.</p>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Knowledge Graphs and LLMs in Action. Manning, 2024 — Chapter 11: Graph Representation Learning and Graph Neural Networks.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Graph Feature Engineering: Manual과 Semiautomated 접근]]></title>
            <link>https://velog.io/@tasker_dev/Graph-Feature-Engineering-Manual%EA%B3%BC-Semiautomated-%EC%A0%91%EA%B7%BC</link>
            <guid>https://velog.io/@tasker_dev/Graph-Feature-Engineering-Manual%EA%B3%BC-Semiautomated-%EC%A0%91%EA%B7%BC</guid>
            <pubDate>Sun, 17 May 2026 06:38:46 GMT</pubDate>
            <description><![CDATA[<p>이번 글의 thesis는 다음과 같다. <strong>그래프 ML의 성공은 vectorization의 품질에 달려 있으며, 그 품질은 해석가능성과 자동화 사이의 의식적 trade-off로 결정된다.</strong> Manual feature engineering, semiautomated 접근(ReFeX), fully automated 표현 학습(GNN)은 동일 스펙트럼의 세 지점이지, 우열을 가릴 대상이 아니다. 어느 지점을 선택할지는 도메인의 설명가능성 요구, 데이터 규모, 컴퓨팅 자원, 도메인 전문성에 따라 달라진다.</p>
<p>그래프 ML의 근본 도전은 다음과 같이 압축된다 — <strong>그래프 요소(노드, 관계, 전체 그래프)를 ML 알고리즘이 처리할 수 있는 벡터로 효과적으로 표현하는 방법.</strong> 이 표현 단계는 vectorization 또는 featurization으로 불리며, 모델이 얼마나 잘 학습하고 예측할 수 있는지를 결정한다.</p>
<h2 id="1-feature-engineering의-spectrum">1. Feature Engineering의 Spectrum</h2>
<p>ML 알고리즘은 그래프 구조를 직접 처리할 수 없다. 대신 수치 입력 벡터를 요구한다. 이 벡터의 품질은 다운스트림 작업의 성능에 직접적인 영향을 미친다.</p>
<p>vectorization을 구현하는 방식은 자동화 정도에 따라 스펙트럼을 이룬다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/0c24b8ee-48ab-415f-8bac-fa45b4a3f70f/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>접근</th>
<th>해석가능성</th>
<th>자동화</th>
<th>패턴 포착력</th>
<th>핵심 트레이드오프</th>
</tr>
</thead>
<tbody><tr>
<td>Manual</td>
<td>매우 높음</td>
<td>낮음</td>
<td>도메인 지식 의존</td>
<td>labor-intensive</td>
</tr>
<tr>
<td>Semiautomated</td>
<td>높음</td>
<td>중간</td>
<td>구조적 패턴에 한정</td>
<td>도메인 지식 통합 어려움</td>
</tr>
<tr>
<td>Fully Automated</td>
<td>낮음</td>
<td>높음</td>
<td>복잡 패턴 포착 가능</td>
<td>black-box</td>
</tr>
</tbody></table>
<p>핵심 명제는 다음과 같다. <strong>자동화가 진행될수록 해석가능성은 감소한다.</strong> Manual feature는 매우 해석 가능하지만 만드는 데 노동 집약적이고, semiautomated feature는 해석가능성과 효율 사이의 균형을 이루며, fully automated feature는 생성에는 효율적이지만 해석이 어렵다.</p>
<h2 id="2-manual-feature-engineering이-여전히-가치-있는-이유">2. Manual Feature Engineering이 여전히 가치 있는 이유</h2>
<p>자동화된 표현 학습이 발전한 시대에도 manual feature engineering은 두 가지 핵심 이유로 여전히 가치 있다.</p>
<p><strong>첫째, 인간이 이해하고 검증할 수 있는 해석 가능한 features를 생산한다.</strong> 금융 사기 탐지, 의료 진단처럼 의사결정의 근거를 설명해야 하는 도메인에서 이는 단순한 편의가 아니라 규제 요건이다.</p>
<p><strong>둘째, 무엇이 그래프 표현을 효과적으로 만드는지에 대한 통찰을 제공하며, 자동화된 접근의 설계에 정보를 준다.</strong> GNN을 설계하더라도, manual feature에 대한 이해는 어떤 구조적 패턴이 의미 있는지에 대한 사전 지식을 제공한다.</p>
<p>추가로 한 가지 결정적 장점이 있다 — <strong>LLM과의 호환성</strong>이다. 수동으로 추출된 features는 그래프에 대한 자율적 추론을 위한 LLM과 호환된다. 이 features는 잘 이해된 그래프 알고리즘과 속성에 기반하기 때문에, LLM이 효과적으로 이를 해석하고 추론할 수 있다. GraphRAG 시스템에서 LLM이 그래프 분석 결과를 자연어로 설명해야 할 때, manual feature는 그 자체로 의미를 가진 토큰의 집합이 된다.</p>
<h2 id="3-manual-node-features-7대-카테고리">3. Manual Node Features: 7대 카테고리</h2>
<p>Manual node feature는 노드의 영향 범위에 따라 두 그룹으로 나뉜다.</p>
<table>
<thead>
<tr>
<th>그룹</th>
<th>정의</th>
<th>대표 features</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Local features</strong></td>
<td>노드의 one-hop neighborhood 또는 ego-centered network(egonet) 고려</td>
<td>degree, triangles, density</td>
</tr>
<tr>
<td><strong>Global features</strong></td>
<td>전체 네트워크 또는 그 큰 부분에서 노드의 역할 측정</td>
<td>geodesic, closeness, betweenness, PageRank</td>
</tr>
</tbody></table>
<p>egonet은 특정 노드와 그 직접 이웃들로 구성된다. egonet의 중심은 ego이고, 주변 노드들은 alter라고 불린다. global features에는 betweenness centrality, closeness centrality, PageRank, Eigenvector centrality 같은 centrality 지표들이 속한다. 이 측정들은 네트워크 내 노드의 영향력과 노드가 다른 노드들에 의해 어떻게 영향받을 수 있는지를 포착한다.</p>
<h3 id="local-features">Local Features</h3>
<p><strong>Degree.</strong> 노드의 degree는 그 노드가 몇 개의 이웃을 가지는지를 나타낸다. in-degree, out-degree와 함께 global degree는 노드의 직접 연결에 대한 더 나은 표현을 제공한다.</p>
<p><strong>Triangles.</strong> 그래프 이론에서 triangle은 세 노드가 모두 연결된 서브그래프다. 노드의 egonet에 triangle이 존재한다는 것은 대상 노드가 그 이웃들과 강한 연결을 가지고 있다는 표시다. 직관적으로, 가까운 사람들을 생각해보면 — 친구들은 아마 서로도 친구일 것이다. Triangle은 밀접하게 연결된 개인들의 그룹이 가진 영향력을 드러낸다.</p>
<p><strong>Density.</strong> Density는 그래프의 노드들이 얼마나 연결되어 있는지를 측정한다. egonet 내의 density는 이웃들이 서로 얼마나 영향을 주고받을 수 있는지를 나타낸다.</p>
<h3 id="global-features">Global Features</h3>
<p><strong>Geodesic / Shortest Path.</strong> geodesic path 또는 shortest path는 두 노드 간의 최소 거리를 나타낸다. 두 노드 간에 더 많은 경로가 존재하고 그 경로들이 짧다면, 사기 행동이 대상 노드에 영향을 미칠 가능성이 더 높다. 다만 geodesic path 계산은 computationally expensive하다. Dijkstra 알고리즘은 가장 작은 tentative distance를 가진 미방문 노드를 반복적으로 선택함으로써 그래프 내 노드 간의 최단 경로를 찾는다.</p>
<p><strong>Closeness Centrality.</strong> 노드가 다른 모든 노드들과 얼마나 &quot;가까운지&quot;를 나타낸다. 네트워크 내 노드에서 다른 모든 노드까지의 평균 거리를 측정한다. 더 낮은 g(vi) 값은 노드가 일반적으로 네트워크의 다른 노드들에 더 가깝다는 것을 나타낸다.</p>
<p>사기 탐지 도메인의 직관적 해석은 이렇다. 사기 노드가 g(vi)에 대해 낮은 값을 가질 때, 사기는 네트워크를 통해 쉽게 퍼지고 다른 노드들을 더 빠르게 오염시킬 수 있다. closeness centrality는 farness의 역수다 — 네트워크에서 더 중심에 있는 노드에 더 높은 값을 할당하기 때문이다.</p>
<p>주의할 점은 closeness centrality가 노드에 대해 계산될 때, 전체 네트워크를 사용하지 않는다는 것이다. 그 노드에서 도달 가능한 네트워크의 부분만 사용한다. 따라서 disconnected component가 있는 그래프에서는 다른 component의 노드와의 거리는 계산에 포함되지 않는다.</p>
<p><strong>Betweenness Centrality.</strong> closeness가 노드가 다른 노드들에 얼마나 빠르게 도달할 수 있는지를 측정하는 반면, betweenness는 노드가 다른 노드들 사이의 다리(bridge) 역할을 얼마나 자주 하는지를 측정한다. 구체적으로, 다른 노드 쌍들 사이의 최단 경로에 노드가 나타나는 횟수를 정량화한다.</p>
<p>네트워크에서 어떤 노드 쌍이든, 정보나 영향력은 그들 사이의 최단 경로를 따라 흐를 가능성이 높다. 특정 노드가 이 최단 경로들에 빈번하게 나타난다면, 그 노드는 높은 betweenness centrality를 가지며 따라서 많은 다른 노드들 간의 정보 흐름을 잠재적으로 제어한다. 1에 가까운 값은 노드가 많은 최단 경로에 나타나며 따라서 정보 흐름을 제어할 잠재력이 높음을 나타낸다.</p>
<h3 id="closeness-vs-betweenness-핵심-차이">Closeness vs Betweenness 핵심 차이</h3>
<table>
<thead>
<tr>
<th>Metric</th>
<th>측정 대상</th>
<th>핵심 질문</th>
<th>사기 탐지 직관</th>
</tr>
</thead>
<tbody><tr>
<td>Closeness</td>
<td>다른 노드들과의 평균 거리</td>
<td>&quot;얼마나 빨리 도달 가능한가?&quot;</td>
<td>사기가 퍼지는 속도</td>
</tr>
<tr>
<td>Betweenness</td>
<td>최단 경로 위에 등장 빈도</td>
<td>&quot;얼마나 자주 bridge 역할을 하는가?&quot;</td>
<td>사기 자금이 통과하는 길목</td>
</tr>
</tbody></table>
<h2 id="4-pagerank의-고급-활용-personalization">4. PageRank의 고급 활용: Personalization</h2>
<p>PageRank는 incoming connection의 구조에 기반하여 노드의 중요도를 측정하는 강력한 metric이다. 더 단순한 centrality 측정과 달리, PageRank는 연결의 양뿐 아니라 <strong>품질</strong>도 고려한다 — 다른 높은 순위 노드에 연결된 노드는 더 높은 PageRank 점수를 받는다.</p>
<p>이 챕터의 핵심 통찰은 PageRank를 <strong>두 가지 방식으로 적응</strong>시키는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/37ddfd9b-859d-440a-8571-8eded2098faf/image.png" alt=""></p>
<p>먼저 모든 연결을 동등하게 고려하는 base PageRank를 계산한다. 그 다음 알려진 사기 노드로부터의 연결이 더 큰 가중치를 갖는 fraud-weighted PageRank를 계산한다. base PageRank와 fraud-weighted PageRank 사이의 이 divergence는 가치 있는 통찰을 제공할 수 있다.</p>
<p>base PageRank와 비교하여 fraud-weighted PageRank에서 유의미한 증가를 보이는 노드는 더 면밀한 조사를 요할 수 있다. 직관은 명확하다 — 일반적 관점에서는 평범해 보이지만 사기 네트워크의 관점에서는 중요한 위치에 있는 노드, 그것이 잠재적 협력자다.</p>
<p>이 패턴은 PageRank의 personalization vector 기능을 활용한 것이며, 의료(특정 질병과 가까운 약물 식별), 추천(특정 사용자 그룹의 관점에서 본 영향력) 등 다양한 도메인으로 일반화된다.</p>
<h2 id="5-manual-engineering의-운영-특성">5. Manual Engineering의 운영 특성</h2>
<p>manual feature engineering의 프로세스는 반복적이어야 한다. features는 분류기가 충분하다고 느낄 만한 예측 품질에 도달할 때까지 변경되고 증가될 수 있다.</p>
<p>이 접근의 운영적 장점은 다음과 같다.</p>
<ul>
<li>프로세스가 복잡하고 광범위한 feature 설계와 신중한 고려를 요구했음에도 불구하고, <strong>완전히 자신의 통제 하에 있다</strong></li>
<li>각 추출된 feature는 그래프를 보는 것만으로 쉽게 설명될 수 있다</li>
<li>이는 프로세스의 투명성을 증가시킨다 — 데이터베이스 크기가 제한적이거나 설명가능성이 중요한 경우 이 방법이 유효한 이유다</li>
</ul>
<p>GraphRAG #9의 실험이 보여준 것처럼, 단순한 degree-based feature가 실패할 때 그 실패의 양상 자체가 다음 단계의 feature 설계 방향을 알려준다. manual engineering의 가치는 단지 모델 성능이 아니라, 이 진단 가능성과 통제력에 있다.</p>
<h2 id="6-relationship-representation-link-prediction의-본질">6. Relationship Representation: Link Prediction의 본질</h2>
<p>지금까지 다룬 features는 모두 노드에 대한 것이다. 그러나 link prediction은 본질적으로 다른 종류의 representation을 요구한다.</p>
<p>두 단백질이 상호작용할지, 고객이 특정 상품에 관심을 가질지, 약물이 질병을 치료할 수 있을지를 예측하든, 본질적으로 우리는 동일한 질문을 던지고 있다 — <strong>두 노드가 주어졌을 때, 그들 사이에 관계가 존재할 가능성은 얼마나 되는가?</strong> 개별 노드를 표현하는 대신, 노드 간 잠재적 연결의 특성을 포착해야 한다.</p>
<p>이는 두 가지 분류 작업으로 접근할 수 있다.</p>
<table>
<thead>
<tr>
<th>작업 유형</th>
<th>출력</th>
</tr>
</thead>
<tbody><tr>
<td>Binary classification</td>
<td>관계 존재 여부</td>
</tr>
<tr>
<td>Multiclass classification</td>
<td>관계의 유형</td>
</tr>
</tbody></table>
<h3 id="두-가지-접근법">두 가지 접근법</h3>
<p>관계를 표현하는 방법은 크게 두 가지로 나뉜다.</p>
<p><strong>Node-based combination.</strong> source와 target 노드의 feature 벡터를 결합하여 관계 표현을 도출한다.</p>
<p><strong>Path-based features.</strong> 노드 feature에 의존하는 대신, 그래프 내 노드들이 연결된 방식을 분석하여 관계를 특성화한다. 각 feature는 두 노드 간의 특정 path 패턴을 나타낸다 — 예를 들어 two-hop path의 수, 또는 특정 metapath의 존재.</p>
<table>
<thead>
<tr>
<th>접근</th>
<th>장점</th>
<th>적합 상황</th>
</tr>
</thead>
<tbody><tr>
<td>Node-based combination</td>
<td>node embedding과 잘 작동</td>
<td>단순한 관계 예측</td>
</tr>
<tr>
<td>Path-based features</td>
<td>복잡한 네트워크 패턴 포착에 탁월</td>
<td>복잡한 메타 구조 (예: 약물-질병)</td>
</tr>
</tbody></table>
<h2 id="7-vector-combination-operators">7. Vector Combination Operators</h2>
<p>Node-based combination을 사용할 때, 두 노드 벡터를 결합하는 연산자의 선택이 결과에 영향을 미친다.</p>
<table>
<thead>
<tr>
<th>Operator</th>
<th>공식</th>
<th>출력 차원</th>
<th>특성</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Catenate</strong></td>
<td>$[u; v]$</td>
<td>$2n$</td>
<td>두 벡터를 끝에서 끝으로 연결. 원본 정보 모두 보존하지만 차원이 두 배가 됨</td>
</tr>
<tr>
<td><strong>Average</strong></td>
<td>$(u+v)/2$</td>
<td>$n$</td>
<td>원래 차원 유지, 두 벡터 간 central tendency 포착</td>
</tr>
<tr>
<td><strong>L1</strong></td>
<td>$|u-v|$</td>
<td>$n$</td>
<td>각 차원에서 벡터들이 얼마나 다른지 포착, 비유사성 측정에 유용</td>
</tr>
<tr>
<td><strong>L2</strong></td>
<td>$(u-v)^2$</td>
<td>$n$</td>
<td>벡터 간 차이 포착하지만 제곱 연산으로 큰 차이를 강조</td>
</tr>
<tr>
<td><strong>Hadamard</strong></td>
<td>$u \odot v$</td>
<td>$n$</td>
<td>원소별 곱. 두 벡터의 값이 곱셈적으로 결합되어야 할 관련 양을 나타낼 때 특히 유용</td>
</tr>
</tbody></table>
<p>link prediction의 품질은 노드 표현의 품질에 직접적으로 의존한다. 또한 <strong>선택을 도울 generic rule이 없다</strong> — 따라서 벤치마크가 자신의 시나리오에 어떤 접근이 적절한지에 대한 표시를 제공할 수 있다.</p>
<p>실무적으로는 Hadamard가 가장 흔한 기본 선택이다. 두 노드 임베딩이 같은 차원에서 함께 활성화될 때 그 차원을 강조하기 때문에, &quot;공통된 잠재 속성&quot;이 관계를 정의한다는 직관과 잘 맞는다.</p>
<h2 id="8-drug-repurposing과-metapath-도메인-특화-표현의-사례">8. Drug Repurposing과 Metapath: 도메인 특화 표현의 사례</h2>
<p>manual representation 프로세스는 일반적으로 도메인 특화적이며, 우리가 달성하려는 목표와 도메인에 대한 이해를 요구한다. Hetionet 데이터셋을 사용한 drug repurposing이 이 원칙의 대표적 사례다.</p>
<p>연구자들은 우울증과 알코올 중독에 사용되는 기존 약물 중 흡연 중독과 간질 치료에 잠재력을 보이는 것들을 식별했다. 목표는 화합물과 질병의 네트워크 연결성을 치료 확률로 변환하는 ML 모델을 학습시키는 것이었다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/35593250-3b71-49df-a935-8ae052a2bb2a/image.png" alt=""></p>
<p>각 metapath는 화합물과 질병 사이의 가능한 경로의 부분집합을 나타낸다. 이들은 <strong>직접적 연결이 존재하든 아니든</strong> 각 화합물-질병 쌍에 대한 feature를 형성한다. 즉, &quot;Compound→Gene→Disease&quot; 같은 패턴 자체가 의미 있는 신호다.</p>
<h2 id="9-dwpc-path-counting의-핵심-문제-해결">9. DWPC: Path Counting의 핵심 문제 해결</h2>
<p>화합물-질병 쌍을 metapath로 표현할 때 가장 단순한 접근은 화합물과 질병 사이의 distinct path instance를 카운팅하는 것이다. 그러나 단순히 경로를 카운팅하면, <strong>매우 높은 연결성을 가진 노드들이 카운트를 지배할 때 오해를 불러일으킬 수 있다.</strong></p>
<p>예를 들어, 한 유전자가 많은 생물학적 프로세스에 관여한다면, 그 유전자는 자연스럽게 더 많은 경로에 나타날 것이다. 그러나 이것이 반드시 화합물과 질병 사이의 더 강하거나 의미 있는 관계를 나타내는 것은 아니다. hub node가 결과를 지배하면 분석은 무의미해진다.</p>
<h3 id="dwpc의-작동-원리">DWPC의 작동 원리</h3>
<p>이 편향을 해결하기 위해 <strong>DWPC(degree-weighted path count)</strong> 가 사용된다. DWPC는 노드 degree에 기반한 damping factor를 적용한다.</p>
<table>
<thead>
<tr>
<th>방법</th>
<th>동작</th>
<th>문제점/장점</th>
</tr>
</thead>
<tbody><tr>
<td>Simple Path Count</td>
<td>경로 수만 카운팅</td>
<td>hub node가 결과 지배</td>
</tr>
<tr>
<td>DWPC</td>
<td>중간 노드 degree에 반비례 가중</td>
<td>hub의 영향 완화, 특정적·집중적 경로 강조</td>
</tr>
</tbody></table>
<p>각 path는 중간 노드의 degree에 반비례하여 가중된다. 많은 연결을 가진 노드는 최종 점수에 적게 기여한다. damping 효과는 더 특정적이고 집중된 생물학적 경로를 강조하는 데 도움이 된다.</p>
<p>$$DWPC = \sum_{\text{path}} \prod_{v \in \text{intermediate}(\text{path})} \frac{1}{|N(v)|^w}$$</p>
<p>여기서 $w$는 damping exponent (일반적으로 0.4~0.5)다. 이 접근은 <strong>생물학적 네트워크에서 hub 노드로부터의 노이즈를 줄이면서 의미 있는 치료적 관계를 식별하는 데 도움이 되기 때문에</strong>, drug repurposing에서 특히 효과적임이 입증되었다.</p>
<h3 id="feature-reduction의-필요성">Feature Reduction의 필요성</h3>
<p>모든 잠재적 metapath에 걸쳐 모든 가능한 화합물-질병 쌍에 대한 DWPC 값을 계산하는 것은 computationally expensive하며 noisy하거나 misleading한 결과로 이어질 수 있다. 이를 위해 두 단계의 reduction이 적용된다.</p>
<table>
<thead>
<tr>
<th>단계</th>
<th>방법</th>
</tr>
</thead>
<tbody><tr>
<td>Metapath reduction</td>
<td>알려진 treatment vs non-treatment 관계에서의 빈도 분석으로 가장 유의미한 metapath 식별</td>
</tr>
<tr>
<td>Pair selection</td>
<td>도메인 지식과 degree 기반 확률 분석으로 가장 유망한 화합물-질병 쌍 식별</td>
</tr>
</tbody></table>
<h2 id="10-refex-semiautomated-접근의-정수">10. ReFeX: Semiautomated 접근의 정수</h2>
<p>Manual feature engineering의 장점 — 해석가능성, 신뢰성, 예측가능성 — 을 유지하면서 feature 선택 프로세스의 대부분을 자동화할 수 있다면 어떨까? 이 질문에 대한 답이 <strong>ReFeX(Recursive Feature eXtraction)</strong> 이다.</p>
<p>ReFeX는 fully manual feature engineering과 복잡한 신경망 접근 사이의 중간 지대를 제공한다. ReFeX는 그래프에서 관련 구조적 features를 자동으로 식별하고 추출한다. black-box 신경망 접근과 달리, <strong>ReFeX의 프로세스는 투명하며 도메인 전문가가 이해하고 검증할 수 있는 해석 가능한 features를 생산한다.</strong> 이 투명성은 특정 예측이 왜 이루어졌는지 설명해야 할 때 특히 가치 있다.</p>
<h3 id="refex의-차별화된-특성">ReFeX의 차별화된 특성</h3>
<p>ReFeX가 생성하는 features는 서로 다른 네트워크 간, 심지어 동일 네트워크의 서로 다른 시간 스냅샷 간에 의미 있게 비교될 수 있다 — 더 복잡한 신경망 접근으로는 거의 불가능한 것이다. 이 속성은 그래프 구조가 어떻게 진화하는지 추적하거나 서로 다른 네트워크 간 패턴을 비교해야 하는 응용에서 ReFeX를 특히 가치 있게 만든다.</p>
<h3 id="핵심-설계-원칙-2가지">핵심 설계 원칙 2가지</h3>
<table>
<thead>
<tr>
<th>원칙</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Structural</strong></td>
<td>feature matrix F의 구성은 노드나 link에 대한 추가 속성 정보를 요구하지 않아야 함</td>
</tr>
<tr>
<td><strong>Effective</strong></td>
<td>좋은 노드 feature는 (1) 가용 시 노드 속성 예측을 돕고 (2) 그래프 간 전이 가능해야 함</td>
</tr>
</tbody></table>
<p>이 원칙들은 ReFeX의 핵심 철학과 연결된다.</p>
<blockquote>
<p><strong>&quot;서로 다른 그래프 간 마이닝에서 중요한 것은 누구를 아는가, 또는 누구와 관련되는가이다.&quot;</strong></p>
</blockquote>
<p>즉, 노드의 identity가 아니라 어떤 종류의 노드와 연결되어 있는가가 핵심이라는 관점이다.</p>
<h3 id="작동-원리">작동 원리</h3>
<p>ReFeX는 그래프 구조의 순수한 구조적 측면 — 노드와 관계 — 위에서 작동하며, <strong>노드 라벨이나 타입을 고려하지 않는다.</strong> topology에 대한 이 초점은 알고리즘이 구조적 패턴을 식별할 수 있게 해준다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/65eb04f4-d77e-4bec-8701-2b90d3190cbb/image.png" alt=""></p>
<p>Recursive feature extraction은 summary statistics의 재귀적 적용을 통해 기존 features를 집계한다. 이 프로세스는 점점 더 복잡한 구조적 패턴을 포착하기 위해 aggregation function(sum/mean)의 조합을 사용한다. 알고리즘의 재귀적 특성은 지수적으로 증가하는 수의 features를 생성할 수 있다.</p>
<h3 id="pruning-기법">Pruning 기법</h3>
<p>지수적 증가를 통제하기 위해 세 가지 pruning 기법이 적용된다.</p>
<table>
<thead>
<tr>
<th>기법</th>
<th>동작</th>
</tr>
</thead>
<tbody><tr>
<td>Correlation analysis</td>
<td>높게 상관된 feature 쌍 식별 및 제거</td>
</tr>
<tr>
<td>Logarithmic binning</td>
<td>feature 값을 효율적 비교를 위해 이산 구간으로 매핑</td>
</tr>
<tr>
<td>Threshold-based pruning</td>
<td>지정된 임계값보다 적게 차이나는 features 제거</td>
</tr>
</tbody></table>
<h3 id="refex의-4대-장점">ReFeX의 4대 장점</h3>
<table>
<thead>
<tr>
<th>장점</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>Efficiency</td>
<td>재귀적 구조 features의 자동 추출</td>
</tr>
<tr>
<td>Consistency</td>
<td>feature 생성의 systematic한 접근</td>
</tr>
<tr>
<td>Interpretability</td>
<td>명확한 구조적 의미를 유지하는 생성된 features</td>
</tr>
<tr>
<td>Scalability</td>
<td>feature 품질을 유지하면서 더 큰 그래프를 처리할 수 있는 능력</td>
</tr>
</tbody></table>
<h3 id="refex의-위치와-한계">ReFeX의 위치와 한계</h3>
<p>ReFeX는 manual feature engineering과 fully autonomous representation learning 기법 사이의 중요한 중간 지대를 차지하는, <strong>자동화된 feature 추출을 향한 유의미한 진전</strong>을 나타낸다. 순수한 그래프 구조에 대한 그것의 초점은 더 복잡한 접근을 이해하기 위한 훌륭한 기초를 제공한다.</p>
<p>운영 관점에서 ReFeX의 결정적 장점은 다음과 같다.</p>
<ul>
<li><strong>단계별로 추적되고 검증될 수 있다</strong> — feature engineering 프로세스를 이해하고 검증해야 하는 실무자에게 매우 가치 있는 도구</li>
<li><strong>deterministic 특성이 일관성을 보장한다</strong> — 동일한 입력은 항상 동일한 출력을 생성</li>
<li>이 예측가능성은 <strong>재현성이 필수적인 운영 환경</strong>에서 특히 가치 있음</li>
<li>그래프 구조가 변경되면, ReFeX는 전체 feature matrix의 재생성을 요구하는 대신 <strong>영향받은 features의 선택적 재계산을 허용</strong>한다</li>
</ul>
<p>다만 ReFeX의 본질적 한계도 명확하다. <strong>구조적 features에 대한 의존은 노드 속성이나 edge 유형을 직접적으로 통합할 수 없음을 의미한다.</strong> 노드의 의미적 속성이 결정적 신호인 도메인(예: 사용자의 demographic 정보가 중요한 추천 시스템)에서는 ReFeX만으로는 충분하지 않다.</p>
<h2 id="11-llm을-활용한-feature-engineering">11. LLM을 활용한 Feature Engineering</h2>
<p>새로운 가능성으로 LLM을 활용한 feature engineering 자동화가 부상하고 있다. LLM은 복잡한 패턴을 이해하고 그것을 실행 가능한 코드로 번역하는 작업에 탁월하다. 이는 그래프 데이터베이스와 작업할 때 특히 가치 있는데, 쿼리 작성이 종종 도메인과 쿼리 언어 양쪽 모두에 대한 깊은 이해를 요구하기 때문이다.</p>
<p>LLM은 도메인 전문가가 &quot;사기 노드 주변의 비정상적 거래 패턴을 포착하는 feature를 만들어줘&quot; 같은 자연어 요구사항을 받아서, Cypher 쿼리와 후처리 코드의 조합으로 변환할 수 있다. 이는 manual feature engineering의 해석가능성을 유지하면서 그 노동 집약성을 완화하는 방향이다.</p>
<h2 id="12-정리">12. 정리</h2>
<p>이 챕터의 핵심은 다음과 같이 압축된다.</p>
<ul>
<li><strong>그래프 ML의 manual과 semimanual feature engineering은 ML 작업을 위한 기초를 제공하며, 해석가능성과 자동화 사이의 균형을 이룬다.</strong></li>
<li><strong>Manual feature engineering은 local metrics와 global measures를 결합하여 즉각적 연결과 더 넓은 네트워크 패턴 양쪽을 포착하는 의미 있는 노드 표현을 생성한다.</strong></li>
<li><strong>DWPC는 노드 degree를 고려하면서 연결 관련성을 측정하는 sophisticated한 접근을 제공한다.</strong></li>
<li><strong>Manual과 semiautomated 접근 사이의 선택은 해석가능성 요구, 컴퓨팅 자원, 가용한 도메인 전문성에 의존한다.</strong></li>
</ul>
<p>Feature engineering의 스펙트럼 — Manual → Semiautomated(ReFeX) → Fully Automated(GNN) — 위에서 각 지점은 고유한 가치를 갖는다. 7대 manual node feature(degree, triangles, density, geodesic, closeness, betweenness, PageRank), 5대 vector combination operator(catenate, average, L1, L2, Hadamard), path-based feature와 DWPC, 그리고 ReFeX의 recursive aggregation은 그래프 ML 실무자의 도구 상자를 구성하는 핵심 도구들이다.</p>
<blockquote>
<p>결론: 그래프 feature engineering의 본질은 알고리즘 선택이 아니라 해석가능성과 자동화 사이의 트레이드오프를 의식적으로 설계하는 것이다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Knowledge Graphs and LLMs in Action. Manning, 2024 — Chapter 10: Graph feature engineering: Manual and semiautomated approaches.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Knowledge Graph 위의 머신러닝]]></title>
            <link>https://velog.io/@tasker_dev/Knowledge-Graph-%EC%9C%84%EC%9D%98-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D</link>
            <guid>https://velog.io/@tasker_dev/Knowledge-Graph-%EC%9C%84%EC%9D%98-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D</guid>
            <pubDate>Sun, 17 May 2026 06:25:40 GMT</pubDate>
            <description><![CDATA[<p>이번 글의 thesis는 다음과 같다. <strong>그래프 위의 ML은 전통적 ML의 i.i.d. 가정을 깨뜨리며, 그 깨짐이야말로 그래프 기반 접근의 가치가 발생하는 지점이다.</strong> 노드를 독립적 데이터 포인트로 다루는 것은 그래프에 인코딩된 관계 정보를 버리는 것과 같다. 따라서 KG 위의 ML은 단순히 &quot;그래프 데이터에 ML을 적용하는 것&quot;이 아니라, <strong>연결성을 1급 시민으로 다루는 새로운 종류의 학습 문제 설정</strong>이다.</p>
<p>또한 이 챕터의 실험에서 얻을 핵심 교훈은 다음과 같다. <strong>알고리즘 선택보다 feature engineering이 더 중요하며, 도메인 지식(특히 homophily 같은 그래프 동역학에 대한 이해)이 generic한 접근보다 효과적이다.</strong></p>
<h2 id="1-왜-그래프-위에서-ml을-수행하는가">1. 왜 그래프 위에서 ML을 수행하는가</h2>
<p>지능형 시스템 — 특히 IAS(Intelligent Advisory System) — 의 핵심 가치는 사용자가 스스로 추출할 수 없는 인사이트를 제공하는 &quot;advising&quot;에 있다. 이 advising의 거의 모든 형태는 그래프의 지식을 입력으로 받는 ML 알고리즘을 요구한다.</p>
<p>KG 위에서 ML을 수행해야 하는 이유는 세 가지로 압축된다.</p>
<table>
<thead>
<tr>
<th>이유</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Universal data representation</strong></td>
<td>광범위한 시스템의 다양한 데이터를 그래프로 변환 가능</td>
</tr>
<tr>
<td><strong>소수의 계산 작업으로 환원</strong></td>
<td>수많은 문제가 node classification, link prediction 등 제한된 작업으로 환원됨</td>
</tr>
<tr>
<td><strong>i.i.d. 가정의 회피</strong></td>
<td>실세계 데이터의 본질적 연결성을 그대로 모델링 가능</td>
</tr>
</tbody></table>
<p>이상 탐지(anomalous node detection)와 환자에게 약물 추천은 모두 <strong>node classification</strong> 문제로 요약될 수 있다. 추천 시스템과 상호작용 식별은 본질적으로 <strong>relationship prediction</strong> 문제다. 표면적으로 매우 다른 도메인의 문제들이 소수의 그래프 계산 작업으로 환원된다는 점이 KG 기반 ML의 일반화 능력을 보여준다.</p>
<p>특히 세 번째 이유가 결정적이다. 전통적 ML 알고리즘은 데이터 항목이 independent and identically distributed(i.i.d.)라고 가정한다. 그러나 많은 실세계 use case에서 데이터 항목들은 본질적으로 연결되어 있으며, 이 관계를 무시하면 불완전하거나 잘못된 결과로 이어진다. 관계를 자연스럽게 저장하는 그래프 형태로 데이터를 저장하고 그 위에 계산 작업을 적용하는 것이 더 나은 결과를 제공할 수 있다.</p>
<h2 id="2-gnn-llm-통합-ias의-새로운-형태">2. GNN-LLM 통합: IAS의 새로운 형태</h2>
<p>GNN과 LLM의 결합은 강력한 IAS를 만들어낸다. GNN의 복잡한 구조적 패턴 탐지 능력과 LLM의 자연어 이해·생성 능력이 결합되기 때문이다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/4f3a2acd-51c0-43fe-8a10-66ef33ae7963/image.png" alt=""></p>
<p>의료 도메인 예시는 이 통합의 가치를 잘 보여준다. GNN은 환자 유사성 네트워크와 생물학적 pathway 그래프를 처리하고, LLM은 의학 문헌과 임상 가이드라인을 종합하여 명확한 의학 용어로 치료 근거를 설명한다. 이 GNN-LLM 통합은 시스템이 복잡한 패턴을 식별하고 그것을 context-appropriate language로 설명할 수 있게 해주어, 인사이트를 end user에게 접근 가능하고 actionable한 형태로 만든다.</p>
<p>ML은 본질적으로 problem-driven 분야다. 따라서 KG 위의 ML도 &quot;어떤 알고리즘이 좋은가&quot;가 아니라 &quot;어떤 문제를 풀고 있는가&quot;에서 출발해야 한다.</p>
<h2 id="3-ml-task의-분류-무엇을-풀고-있는가">3. ML Task의 분류: 무엇을 풀고 있는가</h2>
<p>KG 위의 ML 작업은 두 가지 큰 카테고리로 나뉜다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/e105ae83-380a-4594-9420-8290e28a642e/image.png" alt=""></p>
<p><strong>Node-focused tasks</strong>는 전체 데이터셋이 하나의 그래프로 표현되며, 노드와 관계가 데이터 포인트가 된다. <strong>Graph-focused tasks</strong>는 데이터가 여러 그래프의 집합으로 구성되며, 각 데이터 포인트가 그래프 전체다.</p>
<p>이 구분은 학습 패러다임의 차이로 직결된다. Node-focused 작업에서는 동일 그래프 내 노드들이 서로 영향을 주고받기 때문에 진정한 의미의 i.i.d. 데이터셋이 성립하지 않는다. 반면 Graph-focused 작업에서는 각 그래프가 i.i.d. 데이터 포인트로 작동한다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>Node-focused</th>
<th>Graph-focused</th>
</tr>
</thead>
<tbody><tr>
<td>데이터 구조</td>
<td>단일 큰 그래프</td>
<td>여러 독립 그래프 집합</td>
</tr>
<tr>
<td>데이터 포인트</td>
<td>노드 또는 관계</td>
<td>그래프 전체</td>
</tr>
<tr>
<td>i.i.d. 성립</td>
<td>깨짐</td>
<td>성립 (그래프 간)</td>
</tr>
<tr>
<td>대표 학습 유형</td>
<td>semisupervised, unsupervised</td>
<td>supervised, unsupervised</td>
</tr>
<tr>
<td>대표 도메인</td>
<td>소셜 네트워크, 추천 시스템</td>
<td>분자 분류, 화합물 분석</td>
</tr>
</tbody></table>
<h2 id="4-node-classification-iid-가정의-붕괴">4. Node Classification: i.i.d. 가정의 붕괴</h2>
<p>Node classification은 첫눈에는 표준 supervised classification의 직관적 변형처럼 보이지만, 결정적 차이가 있다. <strong>그래프의 노드들은 i.i.d.가 아니다.</strong> Node classification은 이 i.i.d. 가정을 산산조각낸다(shatters). i.i.d. 데이터 포인트의 집합을 모델링하는 대신, 노드들의 상호연결된 네트워크를 모델링한다.</p>
<h3 id="iid-가정을-깨는-3가지-그래프-패턴">i.i.d. 가정을 깨는 3가지 그래프 패턴</h3>
<p>다양한 그래프 유형은 i.i.d. 가정에 도전하는 다양한 관계 패턴을 보인다.</p>
<table>
<thead>
<tr>
<th>패턴</th>
<th>정의</th>
<th>도메인 예시</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Homophily</strong></td>
<td>유사한 노드끼리 연결되는 경향</td>
<td>소셜 네트워크 (관심사, 행동 공유)</td>
</tr>
<tr>
<td><strong>Structural Equivalence</strong></td>
<td>유사한 이웃 구조 → 유사한 기능</td>
<td>단백질 상호작용 (기능적 유사성)</td>
</tr>
<tr>
<td><strong>Heterophily</strong></td>
<td>서로 다른 특성의 노드끼리 연결되는 경향</td>
<td>포식자-피식자, 매수자-매도자</td>
</tr>
</tbody></table>
<p>소셜 네트워크에서 homophily는 이 상호연결성을 잘 보여준다. 노드들은 관계를 통해 서로 영향을 주고받으며, 이웃과 공유된 관심사·속성·행동을 보인다. 이 i.i.d. 가정의 위배는 효과적인 모델이 예측을 할 때 <strong>노드 features와 그들의 네트워크 관계 양쪽 모두</strong>를 고려해야 한다는 것을 의미한다.</p>
<p>단백질 상호작용 네트워크에서는 structural equivalence가 결정적이다 — 유사한 이웃 구조를 가진 노드들은 유사한 기능적 속성을 공유하는 경향이 있다. Heterophily는 또 다른 패턴으로, 노드들이 다른 특성을 가진 노드와 연결되는 것을 선호하는 경우다. 이 모든 패턴은 노드를 독립적 데이터 포인트로 다루는 것이 그래프에 인코딩된 풍부한 관계 정보를 포착하지 못함을 보여준다.</p>
<h3 id="semisupervised의-본질">Semisupervised의 본질</h3>
<p>많은 연구자들은 node classification이 semisupervised라는 데 동의한다. 그 이유는 node classification 모델을 학습할 때 일반적으로 모든 unlabeled(예: test) 노드를 포함한 <strong>전체 그래프에 접근할 수 있기 때문</strong>이다. 우리에게 없는 것은 오직 test 노드의 라벨뿐이다. test 노드에 대한 정보(예: 그래프 내 그들의 이웃에 대한 지식)는 학습 중에도 모델 개선에 사용될 수 있다.</p>
<p>이는 unlabeled 데이터 포인트가 학습 중에 관찰되지 않는 일반적 supervised 설정과 다르다. 그래프 위의 ML 작업이 고전적 카테고리에 쉽게 들어맞지 않는 이유가 바로 이 지점이다.</p>
<h2 id="5-link-prediction과-community-detection">5. Link Prediction과 Community Detection</h2>
<h3 id="link-prediction">Link Prediction</h3>
<p>Link prediction은 그래프 기반 ML의 기본 작업으로, 노드 간의 잠재적 미래 연결을 식별한다. 많은 실세계 응용에서 그래프는 누락된 edge로 인해 불완전하다. 연결이 존재하지만 관찰되거나 기록되지 않은 경우도 있고, 어떤 경우에는 네트워크의 핵심 행위자에 의해 의도적으로 숨겨지기도 한다. 또한 많은 그래프는 본질적으로 진화한다.</p>
<p>용어 구분이 필요하다.</p>
<table>
<thead>
<tr>
<th>용어</th>
<th>정의</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Link Prediction</strong></td>
<td>두 노드 간 연결의 존재 여부 예측 (관계 유형 무관)</td>
</tr>
<tr>
<td><strong>Relationship Prediction</strong></td>
<td>연결의 존재 + 관계 유형까지 예측</td>
</tr>
</tbody></table>
<p>이 semisupervised 특성은 그래프 구조의 알려진 패턴과 노드 속성을 함께 사용하여 노드 간의 누락되거나 미래에 발생할 연결을 추론할 수 있게 해준다.</p>
<h3 id="community-detection">Community Detection</h3>
<p>연구자 협업 네트워크를 검토할 때, 모든 사람 간에 협업 확률이 동일한 dense &quot;hairball&quot;을 관찰하기는 어렵다. 그래프는 연구 분야, 소속 기관, 지리적 요소 같은 요인에 따라 그룹화된 별개의 클러스터들로 분할되어 있을 가능성이 높다. 같은 대학에서 두 연구자가 협업할 성향은 멀리 떨어진 동료들의 그것보다 높다.</p>
<p>Community detection은 네트워크의 토폴로지와 관계만 사용하여 그래프의 잠재적 그룹 구조를 발견한다. 가장 강력한 응용 중 하나는 <strong>graph description and summarization</strong>이다. 이 기능은 직접 시각화를 거부하거나(defy direct visualization) 포괄적 수동 분석이 불가능한 대규모 그래프를 다룰 때 특히 가치 있다.</p>
<h3 id="graph-clustering-vs-traditional-clustering">Graph Clustering vs Traditional Clustering</h3>
<p>전통적 클러스터링과 graph clustering의 결정적 차이는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>Traditional Clustering</th>
<th>Graph Clustering</th>
</tr>
</thead>
<tbody><tr>
<td>입력 데이터</td>
<td>벡터 공간의 독립 데이터 포인트</td>
<td>상호연결된 그래프</td>
</tr>
<tr>
<td>거리 정의</td>
<td>유클리드 거리</td>
<td>토폴로지 기반 (경로, 이웃)</td>
</tr>
<tr>
<td>대표 알고리즘</td>
<td>K-means, DBSCAN</td>
<td>Louvain, LPA</td>
</tr>
<tr>
<td>클러스터 수 명시</td>
<td>K-means: 필요 / DBSCAN: 불필요</td>
<td>일반적으로 불필요</td>
</tr>
<tr>
<td>라벨 요구</td>
<td>unsupervised</td>
<td>unsupervised</td>
</tr>
</tbody></table>
<p>전통적 클러스터링 알고리즘은 벡터 공간의 독립 데이터 포인트와 작동하는 반면, graph clustering은 노드 간의 관계가 그룹 멤버십 결정에 결정적인 상호연결된 데이터를 구체적으로 처리한다.</p>
<h2 id="6-graph-classification-iid가-돌아오는-곳">6. Graph Classification: i.i.d.가 돌아오는 곳</h2>
<p>Node classification이 단일 그래프 내 개별 노드의 라벨을 예측하는 반면, graph classification은 각각이 완전한 샘플을 나타내는 여러 독립 그래프를 포함한 데이터셋을 다룬다. <strong>Graph classification에서 각 그래프는 자신의 라벨을 가진 i.i.d. 데이터 포인트로 작동한다.</strong></p>
<p>이 작업의 목표는 라벨링된 그래프의 학습 세트를 사용하여 전체 그래프에서 그것의 연관 라벨로 가는 매핑을 학습하는 것이다. 대표적인 도메인은 분자 분류(독성 여부), 화합물의 생물학적 활성 예측, 단백질 구조 분류 등이다.</p>
<p>graph-level 작업의 주요 도전은 <strong>각 그래프의 내부 구조와 그 구성 요소의 속성 모두를 효과적으로 포착하는 features를 개발하는 것</strong>이다. 같은 분자식을 가진 두 화합물도 원자 배열에 따라 완전히 다른 특성을 보일 수 있으므로, feature는 원자 수준 속성과 그래프 토폴로지를 동시에 인코딩해야 한다.</p>
<h2 id="7-두-가지-구현-접근법">7. 두 가지 구현 접근법</h2>
<p>KG 위의 ML 작업을 구현하는 방법은 크게 두 가지 접근으로 나뉜다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/9f4016bc-1f2e-49f1-aea2-a608c40e0db5/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>접근</th>
<th>동작 방식</th>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Collective Classification</strong></td>
<td>그래프 구조를 직접 처리, 노드 features와 이웃 관계를 동시 고려</td>
<td>그래프 정보를 손실 없이 활용</td>
<td>알고리즘 선택의 범위 제한</td>
</tr>
<tr>
<td><strong>Feature Engineering</strong></td>
<td>그래프 구조를 feature vector로 변환 후 표준 ML</td>
<td>전체 ML 생태계 활용 가능 (deep learning 포함)</td>
<td>적절한 feature 정의가 핵심 과제</td>
</tr>
</tbody></table>
<p>두 번째 접근은 그래프 문제를 전통적 ML 작업으로 변환한다. 이 feature engineering 단계는 현대 deep learning 기법을 포함한 기존 ML 알고리즘의 전체 생태계를 활용할 수 있게 해준다. 주요 도전은 노드, edge, 전체 그래프 구조의 본질적 특성을 포착하는 적절한 features를 정의하는 것으로 이동한다.</p>
<h3 id="gnn의-위치">GNN의 위치</h3>
<p>GNN은 두 접근의 경계에 위치한다고 볼 수 있다. GNN은 그래프 구조를 직접 처리하면서도, 학습된 임베딩은 다운스트림 ML 작업에 그대로 활용 가능한 벡터 표현이다. GNN은 구조적 패턴과 노드 속성 모두를 포착하는 데 탁월하여, 불완전한 KG에 특히 가치 있다. GNN은 그래프 지식을 다운스트림 ML 작업에 직접 이익이 되는 방식으로 인코딩하는 의미 있는 vector embedding을 생성한다.</p>
<h2 id="8-featurization-일관성이-결정적이다">8. Featurization: 일관성이 결정적이다</h2>
<p>Feature engineering 접근에서 가장 중요한 운영 원칙은 다음과 같다.</p>
<blockquote>
<p><strong>학습 중에 사용된 featurization 프로세스는 예측을 할 때 사용된 것과 동일해야 한다. 그렇지 않으면 예측 단계가 올바르게 작동하지 않는다.</strong></p>
</blockquote>
<p>이 일관성이 깨지면 어떤 정교한 모델도 의미를 잃는다. featurization 프로세스는 각 노드를 벡터 표현으로 변환한다. 필요한 모든 것 — 노드 속성, 모든 레벨의 이웃, 전체 그래프 — 을 입력으로 받을 수 있다.</p>
<h3 id="node2vec-gnn-이전의-표현-학습">Node2Vec: GNN 이전의 표현 학습</h3>
<p>GNN의 등장 이전에 Node2Vec은 순수하게 네트워크 구조에 기반하여 노드 임베딩을 계산하는 autonomous representation learning을 위한 prominent technique이었다. random walk를 통해 노드 시퀀스를 생성하고 word embedding 기법을 차용하여 임베딩을 학습하는 방식으로, KG의 구조 정보를 벡터화하는 표준적 접근 중 하나로 남아 있다.</p>
<h3 id="feature-standardization의-필요성">Feature Standardization의 필요성</h3>
<p>Feature standardization은 서로 다른 scale에서 작동하는 여러 노드 features와 작업할 때 필수적이다. StandardScaler를 사용하여 모든 features를 zero mean, unit variance로 변환함으로써, 각 feature가 원래의 scale과 관계없이 모델의 결정에 비례적으로 기여하도록 보장한다.</p>
<p>ML 알고리즘은 종종 데이터 포인트 간의 유클리드 거리 계산에 의존한다. 적절한 스케일링 없이는 큰 수치 범위의 features가 그 실제 중요성과 관계없이 이 계산을 지배하게 된다. 이는 모델이 features를 그 예측 능력 때문이 아니라 단지 scale 때문에 과도하게 민감해지면서, 편향된 예측과 모델 정확도 감소로 이어질 수 있다.</p>
<h3 id="link-prediction을-위한-featurization-operators">Link Prediction을 위한 Featurization Operators</h3>
<p>관계(노드 쌍)를 벡터로 변환할 때 흔히 사용되는 연산자들이 있다.</p>
<table>
<thead>
<tr>
<th>연산자</th>
<th>정의</th>
<th>직관</th>
</tr>
</thead>
<tbody><tr>
<td>Hadamard</td>
<td>$u \odot v$ (원소별 곱)</td>
<td>공통 활성 차원 강조</td>
</tr>
<tr>
<td>Average</td>
<td>$(u + v) / 2$</td>
<td>평균적 특성</td>
</tr>
<tr>
<td>L1</td>
<td>$</td>
<td>u - v</td>
</tr>
<tr>
<td>L2</td>
<td>$(u - v)^2$</td>
<td>제곱 차이</td>
</tr>
<tr>
<td>Concatenation</td>
<td>$[u; v]$</td>
<td>정보 손실 최소화, 차원 증가</td>
</tr>
</tbody></table>
<p>선택은 도메인과 다운스트림 모델에 따라 달라지지만, 가장 흔한 선택은 Hadamard로, 두 노드 임베딩이 같은 차원에서 함께 활성화될 때 그 차원을 강조한다.</p>
<h2 id="9-실험에서-얻은-핵심-교훈">9. 실험에서 얻은 핵심 교훈</h2>
<p>이 챕터의 실험은 Karate Club 데이터셋 위에서 두 가지 단순한 node classification 시도를 수행한다.</p>
<p><strong>시도 1: Node degree만을 feature로 사용한 Logistic Regression</strong></p>
<p>Logistic regression은 이름에도 불구하고 binary prediction 작업에 탁월한 분류 알고리즘이다. 노드가 특정 클래스에 속할 확률을 추정하며, features의 선형 조합을 logistic function을 사용하여 0과 1 사이의 확률로 변환한다.</p>
<p>두 차례 실행 결과는 비교적 poor하고 inconsistent한 성능을 보였다. logistic regression이 잘 확립되고 검증된 알고리즘임을 고려할 때, 이 불안정성은 <strong>한계가 분류 방법이 아니라 feature engineering 접근에 있음</strong>을 시사한다. 결과의 high variance는 degree에만 기반한 단순한 노드 표현이 신뢰할 만한 node classification에 필요한 복잡한 네트워크 패턴을 포착하지 못함을 보여준다.</p>
<p><strong>시도 2: Homophily를 활용한 풍부한 표현</strong></p>
<p>총 연결 수만 세는 node degree 대신, 이 연결들의 social context를 고려하여 더 풍부한 노드 표현을 만들 수 있다. 이 접근은 노드의 그룹 멤버십이 그 이웃의 그룹 소속에 의해 영향을 받을 가능성이 높다고 가정함으로써 homophily를 활용한다.</p>
<p>이 단순한 변화 — degree에서 이웃 그룹 분포로 — 가 성능을 결정적으로 개선한다. 이는 다음 세 가지 원칙으로 일반화된다.</p>
<table>
<thead>
<tr>
<th>원칙</th>
<th>내용</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Feature engineering is critical</strong></td>
<td>노드와 관계를 표현하는 방식이 그래프 기반 ML 작업의 성공에 근본적으로 영향을 미친다. 도메인 정보가 반영된 features가 degree centrality 같은 단순 지표보다 종종 더 우수한 성능을 보인다</td>
</tr>
<tr>
<td><strong>Autonomous embeddings require careful tuning</strong></td>
<td>Node2Vec 같은 방법들은 강력하지만 최적 결과를 보장하지 않는다. 지나치게 동질적인 노드 표현이 생성되는 것을 피하기 위해 파라미터가 사려 깊게 구성되어야 한다</td>
</tr>
<tr>
<td><strong>Domain understanding matters</strong></td>
<td>homophily 같은 네트워크 속성과 그래프 동역학에 대한 지식이 generic 접근보다 종종 더 효과적인 feature engineering 전략을 안내한다</td>
</tr>
</tbody></table>
<p>이 세 원칙은 그래프 ML 프로젝트의 우선순위를 명확히 한다. 더 정교한 모델을 찾기 전에 feature engineering을 점검해야 하며, autonomous embedding은 만능 해결책이 아니라 신중한 튜닝이 필요한 도구이고, 도메인 지식은 마지막에 추가하는 양념이 아니라 처음부터 feature 설계를 안내해야 한다.</p>
<h2 id="10-graph-clustering의-특수성-학습-단계가-없다">10. Graph Clustering의 특수성: 학습 단계가 없다</h2>
<p>다른 ML 작업과 비교했을 때 graph clustering은 독특한 위치에 있다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>일반적 ML</th>
<th>Graph Clustering</th>
</tr>
</thead>
<tbody><tr>
<td>입력 변환</td>
<td>feature vector 필요</td>
<td>그래프 자체가 입력</td>
</tr>
<tr>
<td>학습 단계</td>
<td>있음</td>
<td>없음</td>
</tr>
<tr>
<td>라벨</td>
<td>부분 또는 전체</td>
<td>불필요 (unsupervised)</td>
</tr>
<tr>
<td>출력 형태</td>
<td>모델 (파라미터)</td>
<td>community 할당</td>
</tr>
</tbody></table>
<p>Graph clustering의 경우 입력은 전체 그래프(또는 서브그래프)이며, 알고리즘은 노드와 관계를 사용하여 community를 추출한다. <strong>표현 변환 요구사항이 없다.</strong> 전체 그래프가 입력으로 제공되며, 알고리즘이 노드와 관계와 직접 상호작용한다. <strong>학습 단계가 없다.</strong> 이 작업은 unsupervised 접근을 따른다.</p>
<p>대표적 알고리즘인 LPA(Label Propagation Algorithm)는 네트워크에서 community를 탐지하는 빠른 방법이다. 네트워크 전반에 라벨을 전파한다. 다만 LPA는 무작위성으로 인해 일관된 출력을 보장하지 않는다. 따라서 동일한 네트워크에서 여러 번 실행하면 약간 다른 community가 생성될 수 있다.</p>
<h2 id="11-정리">11. 정리</h2>
<p>이 챕터의 핵심은 다음과 같이 압축된다.</p>
<ul>
<li><strong>그래프 위의 ML은 상호연결된 데이터를 자연스럽게 다루고, 보편적 데이터 표현을 제공하며, 제한된 계산 작업의 집합을 통해 복잡한 문제의 모델링을 가능하게 한다.</strong></li>
<li><strong>i.i.d. 데이터 포인트를 가정하는 전통적 ML과 달리, 그래프 기반 접근은 노드 간의 연결과 의존성을 활용하여 실세계 관계와 패턴을 더 잘 반영한다.</strong></li>
<li><strong>그래프 기반 ML의 성공은 종종 자동화된 feature 학습, 도메인 지식 통합, 그리고 당면 작업에 적절한 알고리즘 선택 사이의 올바른 균형을 찾는 것에 달려 있다.</strong></li>
</ul>
<p>ML 작업은 두 카테고리 — node-focused와 graph-focused — 로 나뉘며, 구현은 두 접근 — collective classification과 feature engineering — 으로 나뉜다. 실험적 교훈은 명확하다. <strong>feature engineering이 알고리즘 선택보다 종종 더 중요하며, 도메인 지식은 generic 접근보다 효과적이다.</strong></p>
<blockquote>
<p>결론: 그래프 위의 ML은 i.i.d. 가정을 깨는 것에서 출발하며, 그 깨짐을 어떻게 표현하느냐가 모델의 성패를 결정한다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Knowledge Graphs and LLMs in Action. Manning, 2024 — Chapter 9: Machine Learning on Knowledge Graphs: A Primer Approach.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[그래프 알고리즘]]></title>
            <link>https://velog.io/@tasker_dev/%EA%B7%B8%EB%9E%98%ED%94%84-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@tasker_dev/%EA%B7%B8%EB%9E%98%ED%94%84-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Sun, 17 May 2026 06:15:05 GMT</pubDate>
            <description><![CDATA[<p>이번 글의 thesis는 다음과 같다. <strong>그래프 알고리즘은 연결된 데이터에서 숨겨진 패턴을 드러내는 도구이며, 5개의 카테고리 — Centrality, Community Detection, Similarity, Link Prediction, Network Embeddings — 가 거의 모든 그래프 분석 시나리오를 커버한다.</strong> Neo4j는 이 알고리즘들을 데이터베이스 내부에서 직접 실행할 수 있는 unique opportunity를 제공하며, 이는 데이터 파이프라인을 단순화하고 인사이트 도달 시간을 단축한다.</p>
<h2 id="1-왜-graph-algorithm인가">1. 왜 Graph Algorithm인가</h2>
<p>데이터가 점점 더 연결될수록 그래프 알고리즘은 그 데이터에서 의미를 추출하는 데 큰 역할을 한다. knowledge graph는 상호연결된 데이터의 복잡한 web을 탐색하고 이해할 수 있는 unique opportunity를 제공한다.</p>
<p>그래프 알고리즘이 다루는 문제는 다음과 같이 정리된다.</p>
<table>
<thead>
<tr>
<th>문제 유형</th>
<th>질문</th>
<th>대표 알고리즘</th>
</tr>
</thead>
<tbody><tr>
<td>중심성</td>
<td>누가/무엇이 가장 중요한가</td>
<td>PageRank</td>
</tr>
<tr>
<td>커뮤니티</td>
<td>어떻게 무리 지어 있는가</td>
<td>Louvain</td>
</tr>
<tr>
<td>유사성</td>
<td>어느 노드가 닮았는가</td>
<td>Jaccard, Cosine</td>
</tr>
<tr>
<td>연결 예측</td>
<td>어떤 연결이 생길 것인가</td>
<td>Adamic-Adar</td>
</tr>
<tr>
<td>표현 학습</td>
<td>그래프를 어떻게 벡터로 변환할까</td>
<td>GraphSAGE</td>
</tr>
</tbody></table>
<p>Neo4j의 결정적 가치는 이 알고리즘들을 <strong>데이터베이스 내부에서 직접 실행</strong>할 수 있다는 점이다. in-database 분석은 데이터 엔지니어와 데이터 사이언티스트의 데이터 파이프라인을 단순화하고, 결과적으로 end stakeholder를 위한 인사이트와 액션이 더 빠르고 효과적으로 이어진다.</p>
<h2 id="2-전체-워크플로-gds-pipeline">2. 전체 워크플로: GDS Pipeline</h2>
<p>5가지 알고리즘 카테고리는 모두 동일한 GDS 워크플로를 공유한다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/5acb01d5-3bdc-4839-97db-c092a67bff85/image.png" alt=""></p>
<p>GDS의 모든 알고리즘 호출은 세 가지 실행 모드 중 하나를 선택한다.</p>
<table>
<thead>
<tr>
<th>모드</th>
<th>동작</th>
<th>적합 상황</th>
</tr>
</thead>
<tbody><tr>
<td><code>.stream</code></td>
<td>결과를 행 단위로 반환, DB 미변경</td>
<td>탐색적 분석, 즉시 시각화</td>
</tr>
<tr>
<td><code>.write</code></td>
<td>결과를 노드/관계 속성에 영구 저장</td>
<td>운영 환경, 후속 쿼리 활용</td>
</tr>
<tr>
<td><code>.mutate</code></td>
<td>in-memory projection만 갱신</td>
<td>알고리즘 체이닝</td>
</tr>
</tbody></table>
<p>이 워크플로 위에서 5개 카테고리를 차례로 살펴본다.</p>
<h2 id="3-centrality-가장-중요한-노드-찾기">3. Centrality: 가장 중요한 노드 찾기</h2>
<p>Centrality 알고리즘은 graph data science에서 중추적 역할을 한다. 네트워크 내에서 가장 pivotal하고 influential하며 중요한 노드를 풀어내는 것이 목적이다. 가장 친숙한 사례는 소셜 미디어 플랫폼의 뉴스 피드에서 가장 많이 팔로우되는 인플루언서가 상단으로 밀려 올라오는 것이다.</p>
<h3 id="pagerank-알고리즘이-검색을-바꾼-순간">PageRank: 알고리즘이 검색을 바꾼 순간</h3>
<p>PageRank는 Google의 공동 창립자 Larry Page와 Sergey Brin이 개발한 centrality 측정 방식으로, 웹 검색 알고리즘에 혁명을 일으켰다. 그 전제는 elegant yet powerful하다 — 노드 간 연결을 기반으로 네트워크 내 노드의 중요도를 측정한다.</p>
<p><strong>핵심 원리:</strong></p>
<ul>
<li>높은 순위 노드에서 다른 노드로 향하는 관계는 그 새 노드의 순위를 끌어올린다</li>
<li>알고리즘은 노드 연결 간 점수를 반복적으로(iteratively) 계산한다</li>
<li>damping factor를 도입하여 연결의 randomness를 줄이고, 모든 페이지가 non-zero rank를 가지도록 보장한다</li>
</ul>
<pre><code class="language-cypher">// 1. Projection 생성
CALL gds.graph.project(
  &#39;webGraph&#39;,
  &#39;Page&#39;,
  &#39;LINKS_TO&#39;
);

// 2. PageRank 실행 (stream 모드)
CALL gds.pageRank.stream(&#39;webGraph&#39;, {
  maxIterations: 20,
  dampingFactor: 0.85
})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).url AS page, score
ORDER BY score DESC
LIMIT 10;</code></pre>
<p>damping factor 0.85는 PageRank의 표준 기본값으로, 임의 사용자가 현재 페이지의 링크를 따라갈 확률을 의미한다. 이 값이 낮을수록 무작위 점프 비중이 커지고, 결과 분포가 평탄해진다.</p>
<p>PageRank가 검색 엔진에 혁명을 일으킨 이유는 단순한 in-degree 카운트(들어오는 링크 수)와 다르게, <strong>연결의 출처가 가진 권위를 재귀적으로 반영</strong>하기 때문이다. 권위 있는 사이트에서 받은 링크 1개가 무작위 사이트의 링크 100개보다 가치 있을 수 있다.</p>
<h2 id="4-community-detection-모듈-구조-해독">4. Community Detection: 모듈 구조 해독</h2>
<p>Community detection은 그래프의 modular structure를 해독하도록 돕는다. community는 네트워크 내의 cluster이며, 강한 내부 연결을 가진 하위 그룹을 의미한다.</p>
<p>사회적 상호작용을 매핑하든, 복잡한 생물학적 시스템을 풀어내든, 복잡한 사기 네트워크를 파싱하든, community detection은 강한 내부 연결을 가진 subgroup을 식별하도록 돕는다. 사기 탐지 영역에서 특히 가치 있는 이유는 다음과 같다. 단순 거래 패턴 분석은 사기 거래 하나하나를 잡지만, community detection은 협업하는 사기 네트워크 전체를 한 단위로 식별한다.</p>
<h3 id="louvain-algorithm">Louvain Algorithm</h3>
<p>Louvain은 대규모 네트워크에서 community detection을 위해 널리 사용되는 매우 효율적인 방법이다. <strong>modularity</strong>라는 지표를 최적화한다. modularity는 네트워크를 모듈 또는 community로 분할한 강도를 측정하는 스케일 값이다.</p>
<p>Louvain은 2단계로 작동한다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/8d2b19fc-e055-41c2-98b6-0f6393583481/image.png" alt=""></p>
<p>Phase 1에서는 각 노드를 이웃 community에 할당하고, Phase 2에서는 같은 community의 노드를 집계하여 community의 새 네트워크를 구축한다. 이 과정이 modularity가 더 이상 증가할 수 없을 때까지 반복된다.</p>
<pre><code class="language-cypher">// Louvain 실행
CALL gds.louvain.stream(&#39;socialGraph&#39;)
YIELD nodeId, communityId
RETURN 
  communityId,
  collect(gds.util.asNode(nodeId).name) AS members,
  count(*) AS size
ORDER BY size DESC;</code></pre>
<p>k-means 같은 평면 클러스터링과 달리, Louvain의 계층적 구조는 dendrogram 형태의 community 트리를 만든다. 분석가는 필요한 추상화 수준에서 community를 잘라낼 수 있으며, 이는 사기 네트워크의 큰 그림과 세부 셀 구조를 동시에 다룰 때 결정적 유연성을 제공한다.</p>
<p>community detection을 실행하고 나면, 새로운 패턴이 드러나기 시작한다. 이전에는 개별 노드로만 보이던 데이터가 그룹화된 단위로 재구성되며, 이는 후속 분석의 출발점이 된다.</p>
<h2 id="5-similarity-노드-간-닮음의-정량화">5. Similarity: 노드 간 닮음의 정량화</h2>
<p>Similarity 알고리즘은 그래프 내 노드 간의 likeness 또는 resemblance를 정량화하도록 설계되었다. 사용자 노드 간의 나이나 위치 같은 그래프 특징을 비교한다.</p>
<table>
<thead>
<tr>
<th>알고리즘</th>
<th>핵심 메커니즘</th>
<th>적합 데이터</th>
</tr>
</thead>
<tbody><tr>
<td>Cosine Similarity</td>
<td>다차원 공간의 벡터 각도</td>
<td>텍스트 임베딩, NLP</td>
</tr>
<tr>
<td>Jaccard Similarity</td>
<td>집합의 교집합/합집합</td>
<td>관계 기반 멤버십</td>
</tr>
<tr>
<td>Pearson Correlation</td>
<td>선형 상관</td>
<td>평점·수치 데이터</td>
</tr>
<tr>
<td>Overlap Coefficient</td>
<td>교집합/최소 집합 크기</td>
<td>비대칭 포함 관계</td>
</tr>
</tbody></table>
<p>서로 다른 similarity 알고리즘은 서로 다른 데이터 유형과 응용에서 장점을 제공한다. Cosine similarity는 텍스트 분석과 NLP 분야에서 강력하고 일반적으로 사용되는 알고리즘이다 — 다차원 공간에서 두 벡터 간의 유사성을 측정하기 때문이다.</p>
<h3 id="jaccard-similarity-index-집합-기반-유사도">Jaccard Similarity Index: 집합 기반 유사도</h3>
<p>Jaccard Similarity Index는 두 집합 간의 유사성을 정량화하는 일반적 방법이다. 알고리즘은 두 노드 간의 교집합을 측정한 다음, 집합 합집합의 크기로 나눈다.</p>
<p>$$J(A, B) = \frac{|N(A) \cap N(B)|}{|N(A) \cup N(B)|}$$</p>
<p>이 접근은 노드 간의 관계가 그 노드의 membership을 정의하는 시나리오에서 특히 유용하다. 예를 들어 두 사용자가 follow하는 계정 집합, 두 영화가 출연시킨 배우 집합처럼 &quot;어떤 것에 연결되어 있는가&quot;가 정체성을 정의하는 경우다.</p>
<pre><code class="language-cypher">// Node similarity (Jaccard 기반) 실행
CALL gds.nodeSimilarity.stream(&#39;userInterestGraph&#39;, {
  similarityCutoff: 0.5,
  topK: 10
})
YIELD node1, node2, similarity
RETURN 
  gds.util.asNode(node1).name AS user1,
  gds.util.asNode(node2).name AS user2,
  similarity
ORDER BY similarity DESC;</code></pre>
<p>Jaccard의 가장 실용적 활용 중 하나는 <strong>entity resolution</strong>이다. 데이터에 여러 중복 노드가 존재할 때, 유사한 community composition으로 정의된 중복 노드들이 있다면 Jaccard Similarity Index가 그 유사성을 정량화하여 데이터 중복 제거를 가능하게 한다. 동일 인물의 다른 표기, 동일 회사의 자회사 별칭 같은 사례가 모두 이 패턴으로 처리된다.</p>
<p>다만 이 알고리즘은 <strong>size variation에 민감하다</strong>. 한 노드는 10개 이웃을 가지고 다른 노드는 1000개 이웃을 가질 때, Jaccard 점수는 교집합 비율로만 평가되어 규모 차이에서 오는 정보를 놓친다. 이 단순성과 해석 가능성이 탐색적 데이터 분석과 유사성 비교에서 인기 있는 선택이 되게 만든다.</p>
<h2 id="6-link-prediction-미래의-연결-예측">6. Link Prediction: 미래의 연결 예측</h2>
<p>Link prediction은 복잡한 네트워크 내에서 잠재적 연결을 이해하는 강력한 접근이다. 이 기법은 이웃 간 공유 연결의 수를 단순히 카운팅하는 것을 넘어선다.</p>
<p>활용 영역은 광범위하다.</p>
<ul>
<li><strong>소셜 네트워크</strong>: 현재 연결과 그 연결들의 다른 개인과의 관계를 검토하여 새 친구 추천</li>
<li><strong>추천 시스템</strong>: 사용자-아이템 잠재 선호 예측</li>
<li><strong>e-commerce</strong>: 상호 구매 가능성 추정</li>
<li><strong>신약 개발</strong>: 약물과 질병 간 잠재적 연결 식별 → 더 효과적인 치료법으로 가는 길 개척</li>
</ul>
<h3 id="adamic-adar-index-희귀-연결의-가중">Adamic-Adar Index: 희귀 연결의 가중</h3>
<p>Adamic-Adar Index는 less common한 link의 중요성을 강조함으로써 네트워크 내 잠재적 연결을 식별하는 능력으로 알려져 있다. 개념적으로 이 알고리즘은 <strong>네트워크 내 노드 간의 모든 공유 연결이 동등하게 중요하지 않다는 아이디어</strong>에 뿌리를 두고 있다.</p>
<p>$$AA(x, y) = \sum_{z \in N(x) \cap N(y)} \frac{1}{\log |N(z)|}$$</p>
<table>
<thead>
<tr>
<th>공통 이웃의 특성</th>
<th>Adamic-Adar의 가중치</th>
<th>직관</th>
</tr>
</thead>
<tbody><tr>
<td>이웃이 많은 popular 노드 (예: 인기 해시태그)</td>
<td>낮음</td>
<td>누구나 연결됨, 정보 가치 낮음</td>
</tr>
<tr>
<td>이웃이 적은 niche 노드 (예: 전문 학회)</td>
<td>높음</td>
<td>두 사람이 같은 niche 노드와 연결됨은 강한 신호</td>
</tr>
</tbody></table>
<p>희귀하거나 흔치 않은 연결이 있다면, 그 연결은 더 무겁게 가중된다. 두 연구자가 동일한 거대 학회에 모두 참여했다는 사실보다, 동일한 5명짜리 워크숍에 모두 참여했다는 사실이 협업 가능성에 대해 훨씬 강한 신호를 준다. 이 직관이 Adamic-Adar의 정량적 핵심이다.</p>
<pre><code class="language-cypher">// 두 노드 간 Adamic-Adar 점수 계산
MATCH (u1:User {id: &#39;A&#39;}), (u2:User {id: &#39;B&#39;})
RETURN gds.alpha.linkprediction.adamicAdar(u1, u2) AS score;</code></pre>
<p>복잡한 네트워크를 탐색하여 anchor 노드로의 연결을 파싱할 수 있을 뿐 아니라, 상대 빈도에 기반하여 연결에 자동으로 가중치를 부여할 수 있어 분석 효과를 극적으로 향상시킨다.</p>
<h2 id="7-network-embeddings--gnn-그래프를-벡터로">7. Network Embeddings &amp; GNN: 그래프를 벡터로</h2>
<p>Network embedding과 graph neural network(GNN)는 인공지능과 네트워크 과학이 교차하는 최첨단을 대표한다.</p>
<p>그래프 구조를 임베딩하는 목적은 <strong>그래프의 구조를 보존하면서 그래프를 컴퓨터가 읽을 수 있는 벡터 표현으로 변환하는 것</strong>이다. 이 변환은 노드 분류, 연결 예측, 클러스터링, 시각화를 포함한 다양한 형태의 분석을 가능하게 한다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/b47cb916-8e9b-4b93-8172-6e7d4398877f/image.png" alt=""></p>
<h3 id="graphsage-샘플링-기반-gnn">GraphSAGE: 샘플링 기반 GNN</h3>
<p>GraphSAGE(Graph Sample and AggregatE)는 노드의 local neighborhood로부터 features를 sampling하고 aggregating하여 임베딩을 생성하는 novel approach를 GNN에 도입한다. 기존의 transductive GNN(예: 초기 GCN)이 학습 시점에 존재했던 노드에만 임베딩을 줄 수 있던 것과 달리, GraphSAGE는 unseen data에 대한 예측을 지원하며 각 노드에 대해 고정된 수의 이웃을 샘플링하기 때문에 매우 확장 가능하다.</p>
<table>
<thead>
<tr>
<th>GNN 비교 축</th>
<th>전통 GCN</th>
<th>GraphSAGE</th>
</tr>
</thead>
<tbody><tr>
<td>학습 방식</td>
<td>transductive</td>
<td>inductive</td>
</tr>
<tr>
<td>새 노드 처리</td>
<td>전체 재학습 필요</td>
<td>학습된 함수로 즉시 임베딩</td>
</tr>
<tr>
<td>이웃 처리</td>
<td>전체 이웃 집계</td>
<td>고정 수 샘플링</td>
</tr>
<tr>
<td>확장성</td>
<td>제한적</td>
<td>대규모 그래프 적합</td>
</tr>
</tbody></table>
<p>GraphSAGE는 추천 시스템, 노드 분류, 연결 예측, 사기 탐지, 신약 발견 등에서 유용하다.</p>
<h3 id="umap과-k-means-임베딩의-시각화·클러스터링">UMAP과 K-Means: 임베딩의 시각화·클러스터링</h3>
<p>GraphSAGE가 생성한 임베딩은 일반적으로 64차원, 128차원처럼 고차원이다. 이를 인간이 해석하려면 차원 축소가 필요하다.</p>
<p>UMAP은 GraphSAGE가 생성한 임베딩 같은 복잡한 고차원 데이터를 시각화하도록 돕는다.</p>
<table>
<thead>
<tr>
<th>UMAP 파라미터</th>
<th>역할</th>
<th>효과</th>
</tr>
</thead>
<tbody><tr>
<td><code>n_neighbors</code></td>
<td>local vs global 구조의 균형</td>
<td>낮으면 local 디테일, 높으면 broader context</td>
</tr>
<tr>
<td><code>min_dist</code></td>
<td>축소 공간에서 점들의 밀집도</td>
<td>낮으면 타이트한 클러스터, 높으면 분산</td>
</tr>
</tbody></table>
<p>이 파라미터들은 데이터 클러스터의 granularity와 separation을 결정한다. 축소된 임베딩 위에 K-Means 클러스터링을 적용하면, 고차원 공간에서 서로 유사한 노드들의 모음을 식별할 수 있다.</p>
<pre><code class="language-python"># 일반적 파이프라인 (의사코드)
embeddings = gds.graphsage.stream(graph, model_name=&#39;recipe_model&#39;)
reduced = umap.UMAP(n_neighbors=15, min_dist=0.1).fit_transform(embeddings)
clusters = KMeans(n_clusters=8).fit_predict(reduced)
plot(reduced, color=clusters)</code></pre>
<p>이 워크플로는 노드 간 의미적 유사성을 시각적으로 확인할 수 있게 해주며, 도메인 전문가의 검증과 해석을 가능하게 한다.</p>
<h2 id="8-5개-카테고리-통합-비교">8. 5개 카테고리 통합 비교</h2>
<p>각 카테고리는 답하는 질문과 활용 도메인이 명확히 다르다.</p>
<table>
<thead>
<tr>
<th>카테고리</th>
<th>답하는 질문</th>
<th>대표 알고리즘</th>
<th>대표 도메인</th>
</tr>
</thead>
<tbody><tr>
<td>Centrality</td>
<td>누가 가장 영향력 있는가</td>
<td>PageRank</td>
<td>SNS, 웹 검색</td>
</tr>
<tr>
<td>Community Detection</td>
<td>어떻게 무리 지어 있는가</td>
<td>Louvain</td>
<td>사기 탐지, 생물 시스템</td>
</tr>
<tr>
<td>Similarity</td>
<td>누가 닮았는가</td>
<td>Jaccard, Cosine</td>
<td>Entity Resolution, NLP</td>
</tr>
<tr>
<td>Link Prediction</td>
<td>다음 연결은 무엇인가</td>
<td>Adamic-Adar</td>
<td>친구 추천, 신약 개발</td>
</tr>
<tr>
<td>Network Embeddings</td>
<td>그래프를 어떻게 벡터화할까</td>
<td>GraphSAGE</td>
<td>분류, 사기 탐지, 추천</td>
</tr>
</tbody></table>
<p>실무에서는 이 카테고리들을 <strong>연쇄적으로</strong> 사용하는 경우가 많다. 예를 들어 사기 탐지 시나리오는 다음과 같이 구성될 수 있다.</p>
<pre><code class="language-text">PageRank (의심 거래 허브 식별)
  → Louvain (협업 사기 네트워크 식별)
  → Jaccard (유사 행동 패턴 매칭)
  → GraphSAGE (학습된 임베딩으로 신규 사기 예측)</code></pre>
<p>이 연쇄가 가능한 이유는 GDS의 <code>.mutate</code> 모드가 알고리즘 출력을 in-memory projection에 즉시 추가하여 다음 알고리즘의 입력으로 사용할 수 있게 해주기 때문이다.</p>
<h2 id="11-정리">11. 정리</h2>
<p>Neo4j 데이터베이스는 이 알고리즘들을 데이터베이스 내에서 제공하여 graph data science를 위한 강력한 도구가 된다. graph data science의 힘은 연결된 데이터에서 숨겨진 패턴을 드러내어 혁신과 발견을 가능하게 하는 능력에 있다.</p>
<p>5대 graph algorithm 카테고리는 다음과 같이 압축된다.</p>
<ul>
<li><strong>Centrality</strong>는 네트워크 내에서 가장 영향력 있는 노드를 식별한다</li>
<li><strong>Community Detection</strong>은 노드의 그룹이나 클러스터를 발견하여 네트워크의 modular structure를 부각시킨다</li>
<li><strong>Similarity</strong>는 노드 간의 likeness 또는 structural equivalence를 측정한다</li>
<li><strong>Link Prediction</strong>은 두 노드 간 연결 형성의 가능성을 예측한다</li>
<li><strong>Network Embeddings와 GNN</strong>은 그래프 구조를 더 깊은 분석을 위해 표현하는 데 advanced ML 기법을 활용한다</li>
</ul>
<blockquote>
<p>결론: 그래프 알고리즘의 진정한 가치는 답을 주는 것이 아니라, 연결된 데이터에서 어떤 질문을 던질 수 있는지를 드러내는 데 있다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Graph Data Science with Python and Neo4j — Chapter 8: Graph Algorithms in Neo4j.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[LangGraph로 KG QA 에이전트 구축하기]]></title>
            <link>https://velog.io/@tasker_dev/LangGraph%EB%A1%9C-KG-QA-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@tasker_dev/LangGraph%EB%A1%9C-KG-QA-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 17 May 2026 06:04:33 GMT</pubDate>
            <description><![CDATA[<p>RAG의 한계를 인식하고, 자연어 질문을 KG에 직접 실행 가능한 formal query로 변환하는 4단계 파이프라인 — Intent Detection, Schema-to-LLM 변환, Reasoning-First Query Generation, Summarization — 을 설계했다. 이번 글은 그 개념적 설계를 <strong>실제 동작하는 시스템</strong>으로 구현하는 단계를 다룬다.</p>
<p>이번 글의 thesis는 다음과 같다. <strong>LangGraph의 state 기반 directed graph 아키텍처는 expert emulation 파이프라인에 자연스럽게 매핑되며, 구현의 가치는 시스템이 &quot;무엇을 하는지&quot;가 아니라 &quot;어떻게 만들어졌는지&quot;에 있다.</strong> 좋은 아키텍처는 turnkey solution이 아니라 진화 가능한 foundation을 제공한다. 그 foundation의 강점은 observability와 expert emulation 원칙에 있다.</p>
<h2 id="1-langgraph-state-기반-멀티-에이전트-오케스트레이션">1. LangGraph: State 기반 멀티 에이전트 오케스트레이션</h2>
<p>LangGraph는 LLM 기반의 stateful, multi-actor 애플리케이션 구축을 위해 설계된 라이브러리로, 복잡한 추론과 의사결정 워크플로의 오케스트레이션에 특히 적합하다. 다른 에이전트 프레임워크와 구분되는 핵심은 <strong>컴포넌트 간 통신 방식</strong>이다.</p>
<p>전통적 파이프라인은 컴포넌트 A의 출력을 컴포넌트 B의 입력으로 직접 전달한다. LangGraph는 이 방식을 거부하고, 모든 에이전트가 <strong>공유 상태(shared state)</strong> 라는 화이트보드와 상호작용하는 모델을 채택한다. 각 에이전트는 이 화이트보드에서 이전 작업 결과를 읽고, 자신의 결과를 추가한다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>직접 전달 방식</th>
<th>LangGraph (Shared State)</th>
</tr>
</thead>
<tbody><tr>
<td>통신 단위</td>
<td>함수 인자/반환값</td>
<td>상태 객체 일부</td>
</tr>
<tr>
<td>결합도</td>
<td>높음 (인터페이스 일치 필수)</td>
<td>낮음 (state 키만 합의)</td>
</tr>
<tr>
<td>확장성</td>
<td>새 에이전트 추가 시 시그니처 변경</td>
<td>새 에이전트가 state 일부만 추가</td>
</tr>
<tr>
<td>라우팅</td>
<td>정적</td>
<td>동적 (dynamic edge resolution)</td>
</tr>
<tr>
<td>디버깅</td>
<td>호출 스택 추적</td>
<td>state 스냅샷 검사</td>
</tr>
</tbody></table>
<h3 id="아키텍처의-핵심-구성요소">아키텍처의 핵심 구성요소</h3>
<p>LangGraph는 워크플로를 directed graph로 구현한다. 각 노드는 별도의 에이전트 함수이며, 자신의 책임을 수행하기 위해 global state와 상호작용한다 — 관련 데이터를 읽고, 실행 후 결과로 상태를 업데이트한다. 엣지는 실행 흐름을 결정하며, 어떤 노드가 다음에 실행될지를 지정한다.</p>
<p>특히 중요한 것이 <strong>dynamic edge resolution</strong>이다. 임의의 복잡한 로직에 기반하여 워크플로가 분기할 수 있게 해주며, 이를 통해 LLM이 그저 하나의 컴포넌트인 router 시스템부터 LLM이 자신의 실행 경로를 결정·형성하는 완전 자율 시스템까지 광범위한 스펙트럼의 애플리케이션을 구축할 수 있다.</p>
<h2 id="2-state-객체-에이전트-통신의-코너스톤">2. State 객체: 에이전트 통신의 코너스톤</h2>
<p>state 객체는 에이전트들이 읽고 쓸 수 있는 공유 메모리 공간으로 기능한다. 각 에이전트는 이 state의 특정 부분을 채울 책임을 가지며, 이를 통해 파이프라인 전체에 걸쳐 명확한 책임 사슬이 형성된다.</p>
<pre><code class="language-python"># State 정의 예시 (TypedDict 기반)
from typing import TypedDict, Optional, List

class PipelineState(TypedDict):
    # 사용자 입력
    question: str
    selected_nodes: Optional[List[str]]

    # Intent Detection 결과
    intent: Optional[str]

    # Schema Extraction 결과
    relevant_schema: Optional[dict]

    # Query Generation 결과
    reasoning: Optional[str]
    cypher_query: Optional[str]

    # Query Execution 결과
    query_result: Optional[list]
    error: Optional[str]
    retry_count: int

    # Summarization 결과
    summary: Optional[str]</code></pre>
<p>state는 단순히 에이전트 간 데이터를 운반하는 것이 아니라, 라우팅 결정을 내리고 에러를 우아하게 처리하는 데 필요한 컨텍스트를 함께 유지한다. 에이전트들은 진화하는 state 객체를 통해 통신하면서도 서로 decoupled 상태를 유지한다. 이것이 시스템의 modularity를 보장한다.</p>
<h2 id="3-시스템-아키텍처-separation-of-concerns">3. 시스템 아키텍처: Separation of Concerns</h2>
<p>전체 시스템은 네 가지 컴포넌트로 명확히 분리된다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/8d7730e0-8329-4a3e-bad7-9aadf463d957/image.png" alt=""></p>
<p>각 컴포넌트는 명확한 책임을 갖는다. LangGraph는 핵심 질문-답변 로직을 처리하고, provider는 configuration과 schema 접근을 관리하며, processing interface는 frontend 통신을 담당한다. 이러한 분리는 대화형 AI 시스템에 필요한 유연성을 유지하면서도 관심사를 깔끔하게 분리한다.</p>
<h3 id="configuration-component">Configuration Component</h3>
<p>Configuration 컴포넌트는 시스템이 의존하는 텍스트 요소들 — 주로 프롬프트 템플릿과 KG annotation — 의 중앙 저장소로 기능한다. 이 분리의 가치는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>분리 이전</th>
<th>분리 이후</th>
</tr>
</thead>
<tbody><tr>
<td>긴 프롬프트가 코드 로직에 섞임</td>
<td>템플릿 작성과 렌더링의 경계 명확</td>
</tr>
<tr>
<td>프롬프트 튜닝 시 코어 로직 위험</td>
<td>단일 위치에서 KG 설명·프롬프트 조정 가능</td>
</tr>
<tr>
<td>버전 관리 어려움</td>
<td>프롬프트·annotation 버전 관리 용이</td>
</tr>
</tbody></table>
<p>특히 시스템 개발과 정제 단계에서 다양한 버전의 프롬프트를 비교 실험할 때 이 분리는 결정적 가치를 갖는다.</p>
<h3 id="schema-provider-가장-중요한-변환">Schema Provider: 가장 중요한 변환</h3>
<p>Schema Provider의 역할은 14편에서 다룬 conceptual schema 개념을 운영 가능한 형태로 구현하는 것이다. 핵심 통찰은 다음과 같다.</p>
<blockquote>
<p><strong>우리에게 필요한 것은 conceptual schema이지만, 프로그램적으로 접근 가능한 것은 technical schema뿐이다.</strong></p>
</blockquote>
<p>Schema Provider는 이 간극을 메우기 위해 3단계 프로세스를 따른다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/4729a312-e7ed-43e4-a8f3-c26e64c5b375/image.png" alt=""></p>
<p>이 접근은 기술적 정확성을 유지하면서도 LLM에게 접근 가능한 비즈니스 지향 view를 제공하는 효과적 균형을 만들어낸다. skip list와 business description은 모두 Configuration 컴포넌트에서 관리되므로, 도메인 전문가가 코드를 건드리지 않고 schema의 LLM 표현을 조정할 수 있다.</p>
<h2 id="4-파이프라인-에이전트-expert-emulation의-구현">4. 파이프라인 에이전트: Expert Emulation의 구현</h2>
<p>14편의 expert-emulating 접근은 LangGraph 파이프라인에 자연스럽게 매핑된다. 질문-답변 프로세스의 각 단계가 특화된 에이전트로 구현된다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/1b806626-b8ab-465b-89ef-2633b3f1baab/image.png" alt=""></p>
<h3 id="각-에이전트의-책임">각 에이전트의 책임</h3>
<table>
<thead>
<tr>
<th>에이전트</th>
<th>입력 (state read)</th>
<th>출력 (state write)</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td>Intent Detection</td>
<td>question</td>
<td>intent</td>
<td>질문 유형 분류, 시각화 방식 결정</td>
</tr>
<tr>
<td>Schema Extraction</td>
<td>question, selected_nodes</td>
<td>relevant_schema</td>
<td>KG와 LLM 간 다리, 컨텍스트 인식</td>
</tr>
<tr>
<td>Text-to-Cypher</td>
<td>question, relevant_schema, error</td>
<td>reasoning, cypher_query</td>
<td>reasoning-first 쿼리 생성</td>
</tr>
<tr>
<td>Query Execution</td>
<td>cypher_query</td>
<td>query_result, error, retry_count</td>
<td>DB 실행, 에러 처리</td>
</tr>
<tr>
<td>Summarization</td>
<td>query_result, intent</td>
<td>summary</td>
<td>시각화 보완 텍스트 생성</td>
</tr>
</tbody></table>
<p>Intent Detection 에이전트는 파이프라인의 entry point로, 사용자 질문이 어떻게 시각화되어야 하는지를 결정한다. Schema Extraction 에이전트는 KG와 LLM 컴포넌트 사이의 다리 역할을 한다. 이 에이전트는 사용자가 선택한 노드나 관계를 명시적으로 기술하지 않고도 참조할 수 있게 해주며, 이러한 contextual awareness가 쿼리를 더 자연스럽고 간결하게 만든다.</p>
<h2 id="5-error-handling과-retry-logic">5. Error Handling과 Retry Logic</h2>
<p>Query Execution 에이전트에서 에러 처리는 시스템의 회복력을 결정하는 핵심 요소다. 쿼리 실행이 실패하면 (일반적으로 syntax 에러나 schema 불일치로 인해) 에이전트는 다음 작업을 수행한다.</p>
<ul>
<li>에러 상세 정보 캡처</li>
<li>디버깅을 위한 실패 로깅</li>
<li>시도된 쿼리와 에러 설명을 포함한 에러 메시지 구성</li>
<li>retry counter 증가</li>
</ul>
<pre><code class="language-python">def query_execution_agent(state: PipelineState) -&gt; dict:
    try:
        result = neo4j_driver.execute_query(state[&quot;cypher_query&quot;])
        return {&quot;query_result&quot;: result, &quot;error&quot;: None}
    except Neo4jError as e:
        return {
            &quot;error&quot;: f&quot;Query: {state[&#39;cypher_query&#39;]}\nError: {str(e)}&quot;,
            &quot;retry_count&quot;: state.get(&quot;retry_count&quot;, 0) + 1
        }

def should_retry(state: PipelineState) -&gt; str:
    &quot;&quot;&quot;Conditional edge resolver&quot;&quot;&quot;
    if state.get(&quot;error&quot;) and state.get(&quot;retry_count&quot;, 0) &lt; 3:
        return &quot;text_to_cypher&quot;  # 재시도
    elif state.get(&quot;error&quot;):
        return END  # 최대 시도 초과
    else:
        return &quot;summarization&quot;  # 성공</code></pre>
<p>retry 메커니즘은 최대 3회까지 쿼리 실행을 시도한다. 이는 일시적 실패나 LLM이 정확한 쿼리를 생성하는 데 여러 시도가 필요한 경우에 대한 시스템의 회복력을 제공한다. 14편에서 다룬 <code>&lt;previous_error&gt;</code> 필드가 이 단계에서 실효성을 발휘한다. 이전 시도의 에러 메시지가 다음 쿼리 생성 prompt에 주입되어, 모델이 이전 결정을 검토하고 수정된 쿼리를 생성할 수 있다.</p>
<p>이 dynamic routing 능력은 LangGraph의 핵심 기능이며, 실행 결과와 사용자 의도 모두에 기반한 복잡한 흐름 제어를 가능하게 한다. 단순해 보이는 conditional edge는 실제로는 전체 파이프라인 동작을 오케스트레이션하는 결정적 컴포넌트다.</p>
<h2 id="6-pipeline-integration-layer-ux와-아키텍처의-균형">6. Pipeline Integration Layer: UX와 아키텍처의 균형</h2>
<p>LangGraph 파이프라인을 사용하는 가장 단순한 방법은 <code>invoke</code> 모드 — 초기 state를 제공하고 파이프라인 완료 후 최종 결과를 받는 방식 — 이다. 그러나 이 방식은 이상적이지 못한 사용자 경험을 야기한다. 사용자가 백엔드에서 무슨 일이 일어나는지에 대한 피드백 없이 오랜 시간 기다려야 할 수 있다.</p>
<p>LangGraph는 중간 단계를 가시화하는 <code>stream</code> 실행 모드를 제공하지만, 이 업데이트를 직접 관리하는 것은 애플리케이션 로직을 복잡하게 만든다. 이 트레이드오프를 해결하기 위해 별도의 <strong>interface layer</strong>가 도입된다.</p>
<h3 id="generator-기반-event-stream-설계">Generator 기반 Event Stream 설계</h3>
<p>Interface layer는 파이프라인 실행을 frontend가 쉽게 소비할 수 있는 well-defined event의 시리즈로 변환한다. Generator 함수는 이 작업에 매우 적합하다. 단순하고 선형적인 코드 흐름을 유지하면서 중간 결과를 산출할 수 있기 때문이다.</p>
<pre><code class="language-python">def process_question(question: str):
    &quot;&quot;&quot;Generator yielding (response_type, payload, state) triplets&quot;&quot;&quot;
    initial_state = {&quot;question&quot;: question, &quot;retry_count&quot;: 0}

    for event in graph.stream(initial_state):
        node_name, node_output = next(iter(event.items()))
        current_state = node_output

        # 1. Update event - 진행 상황 알림
        yield (&quot;update&quot;, f&quot;실행 중: {node_name}&quot;, current_state)

        # 2. Result event - 텍스트 출력
        if &quot;reasoning&quot; in node_output:
            yield (&quot;result&quot;, node_output[&quot;reasoning&quot;], current_state)
        if &quot;error&quot; in node_output:
            yield (&quot;result&quot;, node_output[&quot;error&quot;], current_state)
        if &quot;summary&quot; in node_output:
            yield (&quot;result&quot;, node_output[&quot;summary&quot;], current_state)

        # 3. Visualization event - 구조화된 출력
        if &quot;query_result&quot; in node_output:
            yield (&quot;visualization&quot;, node_output[&quot;query_result&quot;], current_state)</code></pre>
<p>각 event는 일관된 구조를 따른다 — response type, response payload, 그리고 파이프라인의 현재 state를 담은 triplet이다.</p>
<table>
<thead>
<tr>
<th>Event 종류</th>
<th>용도</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td>Update</td>
<td>파이프라인 진행 상황</td>
<td>&quot;Intent Detection 실행 중&quot;, &quot;쿼리 생성 중&quot;</td>
</tr>
<tr>
<td>Result</td>
<td>텍스트 출력</td>
<td>reasoning 단계, 에러 메시지, 생성된 summary</td>
</tr>
<tr>
<td>Visualization</td>
<td>구조화된 출력</td>
<td>그래프, 지도, 차트, 테이블</td>
</tr>
</tbody></table>
<p>각 event에 현재 파이프라인 state를 포함시킴으로써, frontend가 이 정보를 어떻게 사용할지에 대한 가정 없이 완전한 컨텍스트를 제공한다. 이 접근은 관심사의 깔끔한 분리를 유지한다. Interface layer는 파이프라인 실행을 well-defined event 스트림으로 변환하는 데 집중하고, 표현 결정은 frontend에 맡긴다.</p>
<h2 id="7-frontend-streamlit-선택">7. Frontend: Streamlit 선택</h2>
<p>운영 배포에서는 더 특화된 인터페이스가 정당화될 수 있지만, 시스템 개발과 데모 단계에서는 Streamlit이 적합한 선택이다.</p>
<table>
<thead>
<tr>
<th>Streamlit 특성</th>
<th>시스템 요구사항과의 정합성</th>
</tr>
</thead>
<tbody><tr>
<td>Python-first</td>
<td>LangGraph 파이프라인과 동일 환경, 직렬화 불필요</td>
</tr>
<tr>
<td>빠른 반복 주기</td>
<td>다양한 질문 처리 방식의 신속한 검증</td>
</tr>
<tr>
<td>낮은 구현 오버헤드</td>
<td>frontend 복잡성보다 expert-emulating 코어 정제에 집중</td>
</tr>
<tr>
<td>Reactive UI</td>
<td>event stream과 자연스럽게 통합</td>
</tr>
</tbody></table>
<h3 id="reactive-ui-pattern-temporary-vs-permanent-state">Reactive UI Pattern: Temporary vs Permanent State</h3>
<p>사용자 입력과 LangGraph 파이프라인 간 통합은 reactive 패턴을 사용한다. 이 패턴은 <strong>temporary state와 permanent state 모두를 관리</strong>한다는 점이 특징이다.</p>
<p>질문을 처리할 때 시스템은 즉각적인 피드백을 보여줘야 하면서, 동시에 영구적인 대화 이력도 유지해야 한다. 두 요구사항은 서로 다른 메커니즘으로 충족된다.</p>
<pre><code class="language-python">import streamlit as st

# Permanent state
if &quot;message_history&quot; not in st.session_state:
    st.session_state.message_history = []

# 대화 이력 렌더링
for msg in st.session_state.message_history:
    render_message(msg)

# 사용자 입력 처리
if user_question := st.chat_input(&quot;질문을 입력하세요&quot;):
    # Temporary placeholder - 실시간 업데이트
    progress_area = st.empty()

    final_state = None
    for response_type, payload, state in process_question(user_question):
        # Temporary 업데이트
        progress_area.write(f&quot;[{response_type}] {payload}&quot;)
        final_state = state

    # 처리 완료 후 permanent history에 추가
    st.session_state.message_history.append({
        &quot;question&quot;: user_question,
        &quot;state&quot;: final_state
    })
    progress_area.empty()  # placeholder 정리</code></pre>
<table>
<thead>
<tr>
<th>영역</th>
<th>메커니즘</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td>Temporary placeholder</td>
<td><code>st.empty()</code></td>
<td>파이프라인 처리 중 실시간 피드백</td>
</tr>
<tr>
<td>Permanent history</td>
<td><code>st.session_state.message_history</code></td>
<td>대화의 영구 기록 누적</td>
</tr>
</tbody></table>
<p>이 접근은 사용자가 즉각적 피드백과 상호작용의 영구적 기록 양쪽 모두를 보장한다.</p>
<h2 id="8-transparency-디버깅을-넘어선-가치">8. Transparency: 디버깅을 넘어선 가치</h2>
<p>이 시스템의 history 영역의 실시간 업데이트는 이중 목적을 수행한다. 사용자에게 진행 상황을 알리는 동시에, expert-emulating 파이프라인의 추론 과정 자체를 가시화한다.</p>
<p>이 transparency는 사용자가 자연어 질문이 어떻게 해석되고 실행되는지를 이해하도록 돕는다. 그러나 그 가치는 단순히 디버깅에 국한되지 않는다. <strong>각 컴포넌트의 의사결정 과정이 가시화되므로, 시스템이 무엇을 하는지뿐 아니라 왜 그렇게 하는지도 이해할 수 있다.</strong> 이것이 시스템 진화의 출발점이 된다.</p>
<h2 id="9-context-aware-querying-실전-적용">9. Context-Aware Querying: 실전 적용</h2>
<p>Schema Extraction 에이전트의 contextual awareness는 실제 사용에서 다음과 같은 자연스러운 인터랙션을 가능하게 한다.</p>
<ul>
<li>&quot;the selected crime&quot;처럼 시스템이 현재 선택에서 컨텍스트를 이해하는 직접 참조</li>
<li>선택된 요소에 대한 참조와 명시적 제약 조건(예: 차량 외관, 번호판)의 조합</li>
<li>사용자의 역할과 조사 의도를 제공하여 더 분석적인 인사이트로 시스템을 유도</li>
</ul>
<p>이 단계에서 시스템은 단순한 기준 매칭을 넘어, 의심스러운 행동을 시사할 수 있는 시간적 패턴을 능동적으로 분석하는 단계로 진화한다.</p>
<h2 id="10-정리">10. 정리</h2>
<p>이 챕터의 핵심은 14편에서 다룬 expert emulation 개념을 production-ready foundation으로 구현하는 것이다. 시스템의 가치는 무엇을 하는지에만 있는 것이 아니라, 어떻게 만들어졌는지에도 있다. foundation의 강점은 그 아래에 깔린 아키텍처, 특히 observability와 expert emulation에 대한 접근 방식에서 나온다.</p>
<p>LangGraph 기반 시스템의 설계 원칙은 세 가지로 압축된다.</p>
<ul>
<li><strong>State-based communication</strong>: 직접 전달이 아닌 공유 상태를 통한 통신으로 modular하고 observable한 AI 파이프라인 구축</li>
<li><strong>Separation of concerns</strong>: configuration, schema, pipeline, frontend의 명확한 책임 분리</li>
<li><strong>Observability + Expert emulation</strong>: 투명성을 통한 자연스러운 시스템 진화</li>
</ul>
<p>LangGraph의 state 기반 설계는 각 컴포넌트가 독립적 추론을 유지하는 modular, observable AI 파이프라인 생성을 가능하게 한다. Pipeline integration 아키텍처는 처리 업데이트의 실시간 스트리밍을 가능하게 하며, context-aware query generation은 schema 지식, 사용자 선택, 대화 이력을 결합한 자연스러운 인터랙션을 제공한다.</p>
<blockquote>
<p>결론: 좋은 AI 시스템의 가치는 무엇을 하는지가 아니라 어떻게 진화할 수 있는지에 있다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Knowledge Graphs and LLMs in Action. Manning, 2024 — Chapter 15: Building a QA agent with LangGraph.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ Expert Emulation: 자연어로 Knowledge Graph에 질문하기 ]]></title>
            <link>https://velog.io/@tasker_dev/Expert-Emulation-%EC%9E%90%EC%97%B0%EC%96%B4%EB%A1%9C-Knowledge-Graph%EC%97%90-%EC%A7%88%EB%AC%B8%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@tasker_dev/Expert-Emulation-%EC%9E%90%EC%97%B0%EC%96%B4%EB%A1%9C-Knowledge-Graph%EC%97%90-%EC%A7%88%EB%AC%B8%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 17 May 2026 05:53:15 GMT</pubDate>
            <description><![CDATA[<p>GraphRAG 시리즈는 지금까지 knowledge graph의 구축, 엔티티 추출, 관계 모델링, 그래프 분석 알고리즘을 다루어 왔다. 그러나 잘 구축된 KG라 하더라도 그것을 실제로 활용하는 단계에서는 새로운 병목이 발생한다. <strong>KG 질의는 전통적으로 Cypher나 SPARQL 같은 기술적 언어를 다룰 줄 아는 사람에게만 열려 있는 영역이었으며, 도메인 전문가의 일반적 toolkit 바깥에 있다.</strong> 분석가가 질문을 정의하면 데이터 엔지니어가 쿼리를 작성하고 결과를 다시 분석가에게 전달하는 이 핸드오프 구조는 의사결정 속도를 결정적으로 떨어뜨린다.</p>
<p>이번 글의 thesis는 다음과 같다. <strong>자연어 질의 시스템의 본질은 &quot;정답을 생성하는 것&quot;이 아니라 &quot;올바른 질문을 형식화하는 것&quot;이며, 그 구현은 RAG가 아니라 expert emulation 패러다임에서 출발해야 한다.</strong> LLM은 도메인 전문가의 답변을 흉내내는 것이 아니라 도메인 전문가의 추론 과정을 흉내내야 한다.</p>
<h2 id="1-rag의-구조적-한계">1. RAG의 구조적 한계</h2>
<p>RAG는 LLM에 외부 컨텍스트를 주입하여 답변 품질을 높이는 표준적 접근이다. 그러나 KG 질의 도메인에서 RAG는 몇 가지 결정적 한계를 드러낸다.</p>
<p>RAG의 성공은 retrieval 단계의 효과성에 전적으로 의존한다. 모델의 출력은 retrieval 단계의 품질만큼만 좋을 수 있으며, 이는 모델이 응답에 대해 표현하는 confidence와 무관하다. retrieval이 불완전할 때 LLM은 충분한 컨텍스트 없이 답변을 생성해야 하는 상황에 놓인다.</p>
<p>문제의 본질은 다음과 같다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>RAG 작동 조건</th>
<th>KG 질의 도메인의 현실</th>
</tr>
</thead>
<tbody><tr>
<td>정보 단위</td>
<td>독립적이고 fine-grained한 passage로 분할 가능</td>
<td>정보가 관계 traversal로 흩어져 있음</td>
</tr>
<tr>
<td>누락 허용도</td>
<td>일부 누락이 부분적 영향</td>
<td><strong>단 하나의 핵심 증언 누락이 반대 결론을 유도</strong></td>
</tr>
<tr>
<td>평가 기준</td>
<td>semantic similarity</td>
<td>정확한 사실 매칭</td>
</tr>
<tr>
<td>실패 양상</td>
<td>&quot;모름&quot; 응답</td>
<td>plausible but inaccurate 답변 생성</td>
</tr>
</tbody></table>
<p>retrieved passage의 granularity가 부적절하거나 retriever가 관련성을 정확히 평가하지 못하면, LLM에 제공되는 컨텍스트는 fragmented 상태가 된다. 이 fragmented context는 모델을 잘못된 가정으로 밀어붙이며, 결과적으로 그럴듯하지만 부정확한 세부사항을 생성하게 만든다. 법 집행, 의료 진단, 컴플라이언스 감사처럼 단일 사실의 누락이 결론을 뒤집는 영역에서 이 실패 양상은 단순한 품질 저하가 아니라 시스템적 위험이다.</p>
<h2 id="2-패러다임-전환-답을-생성하기에서-질문을-형식화하기로">2. 패러다임 전환: 답을 생성하기에서 질문을 형식화하기로</h2>
<p>Expert emulation 접근은 질문-답변 시스템에 대한 패러다임 전환에서 출발한다. 초점은 답변을 생성하는 것에서 올바른 질문을 던지는 것으로 이동한다. 자연어 질문을 KG에 직접 실행 가능한 formal query로 변환하는 것이 핵심 과제가 된다.</p>
<p>이 전환의 의미는 다음과 같다. 쿼리는 본질적으로 KG의 구조와 상호작용하도록 설계된 formal question이다. 정확하고 사실에 기반한 데이터 retrieval은 이 형식화의 품질에 의해 보장된다. LLM은 더 이상 &quot;답을 아는 존재&quot;로 동원되지 않으며, &quot;전문가가 질문을 어떻게 구조화하는지를 모방하는 존재&quot;로 재정의된다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/a44ff99d-178e-407c-9837-f4ba387b0526/image.png" alt=""></p>
<p>이 4단계 파이프라인 — Intent Detection, Schema-to-LLM 변환, Reasoning-First Query Generation, Summarization — 이 expert emulation의 골격이다. 각 단계는 전문가가 질문을 받았을 때 실제로 수행하는 인지 과정의 분해이며, LLM은 이 분해된 작업 각각을 수행하도록 prompt engineering된다.</p>
<h2 id="3-intent-detection-질문의-유형-분류">3. Intent Detection: 질문의 유형 분류</h2>
<p>전문가가 만족스러운 답변을 제공하기 위해 가장 먼저 수행하는 작업은 질문의 의도를 파악하는 것이다. 같은 단어로 표현된 질문이라도 사용자가 원하는 결과 형태(목록인가, 단일 사실인가, 추세인가, 시각화인가)에 따라 다른 파이프라인이 동원되어야 한다.</p>
<p>이 분류 단계는 semantic understanding에 의존하며, 이는 LLM이 가장 잘 수행하는 작업 중 하나다. 좋은 classification prompt는 다음 요소를 모두 갖추어야 한다.</p>
<table>
<thead>
<tr>
<th>요소</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td>Clear instructions</td>
<td>분류 작업의 목적과 출력 형식 명시</td>
</tr>
<tr>
<td>Defined categories</td>
<td>카테고리 정의와 범위</td>
</tr>
<tr>
<td>Examples</td>
<td>각 카테고리의 대표 예시</td>
</tr>
<tr>
<td>Boundary cases</td>
<td>카테고리 경계의 모호한 예시</td>
</tr>
<tr>
<td>Expected output format</td>
<td>파싱 가능한 출력 구조</td>
</tr>
<tr>
<td>Fallback options</td>
<td>분류 불가 시 동작 (catch-all class)</td>
</tr>
</tbody></table>
<p>boundary 근처의 예시를 의도적으로 포함하면 모델이 카테고리 간 뉘앙스를 학습한다. 가장 효과적인 boundary 샘플은 운영 중 발생한 misclassification 사례에서 추출된다. 또한 fallback 카테고리를 두지 않으면 모델이 분류를 강제로 수행하게 되어 오분류 비율이 증가한다.</p>
<h3 id="single-stage-vs-multi-stage-트레이드오프">Single-stage vs Multi-stage 트레이드오프</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>Single broad prompt</th>
<th>Multistage classification</th>
</tr>
</thead>
<tbody><tr>
<td>관리 복잡도</td>
<td>낮음</td>
<td>높음</td>
</tr>
<tr>
<td>정확도</td>
<td>카테고리 수 증가 시 저하</td>
<td>단계별 정밀도 향상 가능</td>
</tr>
<tr>
<td>유연성</td>
<td>카테고리 조정 시 전체 재검토</td>
<td>단계별 독립 조정</td>
</tr>
<tr>
<td>적합 상황</td>
<td>초기 단계, 카테고리 수 적음</td>
<td>카테고리 빈번 조정, 높은 정확도 요구</td>
</tr>
</tbody></table>
<p>초기 단계에서는 single broad prompt로 시작하여 실제 사용 데이터를 수집하고, 분할이 필요한 시점이 명확해진 후 multistage로 전환하는 점진적 접근이 권장된다.</p>
<h2 id="4-schema-to-llm-변환-technical에서-conceptual로">4. Schema-to-LLM 변환: Technical에서 Conceptual로</h2>
<p>자연어를 Cypher로 변환하려면 LLM이 KG의 구조를 이해해야 한다. 그러나 Neo4j가 자동 생성하는 technical schema를 그대로 프롬프트에 주입하는 것은 비효율적이다. technical schema는 구현 특화 요소, 중복 라벨, 메타데이터를 포함하며 이들 중 다수는 도메인 질문 답변에 무관하다.</p>
<p><strong>conceptual schema</strong>는 technical schema의 distilled subset이다. 도메인의 본질적 모델을 전달하는 엔티티와 관계만 남기고, 기술적 복잡성과 무관한 메타데이터를 제거한 표현이다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/5dc5a69c-5b6b-458d-889b-c8c81a0239fd/image.png" alt=""></p>
<p>LLM은 focused, relevant data를 받을 때 더 효율적으로 작동한다. 전체 technical database schema의 복잡한 구조는 모델을 혼란시켜 잘못된 쿼리 생성으로 이어질 수 있다.</p>
<h3 id="schema-annotations-cheat-sheet-생성">Schema Annotations: Cheat Sheet 생성</h3>
<p>낯선 데이터를 다루는 전문가는 KG 구조에 익숙해지기 위해 문서와 data dictionary를 참조한다. 이 과정에서 용어, 약어, 관계의 의미를 정리한 일종의 cheat sheet를 만든다. 이 cheat sheet 생성 과정은 schema annotation으로 시스템화된다. 노드 클래스, 관계 타입, 속성에 자연어 설명을 체계적으로 부착하면, annotated schema는 LLM에게 더 정확한 쿼리 변환을 가능하게 하는 가이드가 된다.</p>
<pre><code class="language-yaml"># Conceptual + Annotated Schema 예시
nodes:
  Person:
    description: &quot;범죄 사건의 관계자(피해자, 용의자, 증인 등)&quot;
    properties:
      role: &quot;사건 내 역할. 가능 값: victim, suspect, witness&quot;

relationships:
  WITNESSED:
    description: &quot;Person이 특정 Event를 목격함&quot;
    domain: Person
    range: Event
    properties:
      reliability: &quot;증언의 신뢰도. 0.0-1.0 범위&quot;</code></pre>
<h2 id="5-reasoning-first-query-generation-time-to-think">5. Reasoning-First Query Generation: &quot;Time to Think&quot;</h2>
<p>복잡하거나 미묘한 질문을 받았을 때, LLM은 데이터 패턴의 shortcut에 의존하여 결론을 서두를 수 있다. 이 문제를 해결하는 표준 기법이 chain-of-thought prompting과 scratchpad 기법이다. 두 접근 모두 모델에게 &quot;time to think&quot;를 부여하는 것이 목적이며, 최종 출력 생성 이전에 더 많은 computational resource를 문제 해결 과정에 투입하도록 유도한다.</p>
<h3 id="order-matters-reasoning이-answer보다-먼저-와야-하는-이유">Order Matters: Reasoning이 Answer보다 먼저 와야 하는 이유</h3>
<p>가장 중요한 통찰은 답변과 추론의 출력 순서가 결과 품질을 결정적으로 좌우한다는 점이다.</p>
<p>LLM은 한 번에 하나의 토큰을 생성하며, 이전 토큰의 선택에 효과적으로 commit한다. LLM이 답을 먼저 제공하고 그 다음 추론을 작성하도록 지시받으면, 모델은 처음 생성한 답에 그대로 고정될 수 있다. 모델이 학습한 텍스트 코퍼스는 일정 수준의 logical coherence와 internal consistency를 보이기 때문에, 일단 답이 정해진 후 생성되는 추론은 사후 정당화(post-hoc justification)에 해당한다.</p>
<table>
<thead>
<tr>
<th>출력 순서</th>
<th>결과 양상</th>
</tr>
</thead>
<tbody><tr>
<td>Answer → Reasoning</td>
<td>사전 결정된 답에 맞춘 정당화. 토큰 누적으로 초기 오류가 전파됨</td>
</tr>
<tr>
<td>Reasoning → Answer</td>
<td>단계별 추론이 누적되어 최종 답에 영향. 투명하고 검증 가능한 사고 흐름</td>
</tr>
</tbody></table>
<p>토큰이 순차적으로 생성되면서 reasoning과 context는 누적적으로 쌓인다. 초기 단계에서 오류가 발생하면, 이후 토큰이 그 오류 위에 추가로 생성되면서 오류가 전파된다. 따라서 step-by-step reasoning을 먼저 출력하게 한 후 최종 답변을 생성하도록 prompt를 설계해야 한다.</p>
<p>다만 예외가 있다. 분류(classification) 태스크에서는 답을 먼저 생성한 후 그 선택의 근거를 제시하게 하는 편이 합리적일 수 있다. 분류 시스템이 어떤 근거로 그렇게 선택했는지, 특히 오분류 시 어떤 논리로 잘못된 결론에 도달했는지를 사후 분석하는 것이 목적이기 때문이다.</p>
<h3 id="prompt-구조와-hallucination-방지">Prompt 구조와 Hallucination 방지</h3>
<p>쿼리 생성 prompt에는 다음과 같은 구조적 장치가 도입된다.</p>
<pre><code class="language-text">&lt;schema&gt;
... conceptual schema with annotations ...
&lt;/schema&gt;

&lt;instructions&gt;
1. 먼저 질문에 답하기 위해 traverse해야 할 관계들을 모두 나열하세요.
2. 각 관계가 schema에 존재하는지 확인하세요.
3. step-by-step reasoning을 작성하세요.
4. 최종 Cypher 쿼리를 생성하세요.
&lt;/instructions&gt;

&lt;question&gt;
{user_question}
&lt;/question&gt;

&lt;previous_error&gt;
{optional: 이전 시도의 실패 메시지}
&lt;/previous_error&gt;

질문 재확인: {user_question}</code></pre>
<p>HTML-like 태그로 사용자 질문을 감싸는 것은 모델이 질문의 경계를 명확히 인식하도록 돕는다. 또한 instruction prompt 끝부분에서 질문을 한 번 더 반복하면 context와 intent가 강화된다. 언어 모델이 본질적으로 local context를 global context보다 우선시하지는 않지만, 특정 작업에서 가까이 있는 토큰이 더 관련성이 높다고 학습하면 그 가중치를 부여하게 된다.</p>
<p><code>&lt;previous_error&gt;</code> 필드는 재시도 메커니즘의 핵심이다. 데이터베이스가 반환한 에러 메시지를 다음 시도의 입력으로 주입하면, 모델은 이전 결정을 검토하고 오류 없는 쿼리를 생성할 수 있다.</p>
<p>Hallucination 방지의 가장 효과적인 기법 중 하나는 모델에게 traverse하려는 관계를 &quot;out loud&quot; 나열하게 하는 것이다. 모델이 schema에 존재하지 않는 관계를 환각으로 만들어내는 경우가 있는데, 사용할 관계를 명시적으로 나열하는 단계를 거치면 이 유형의 hallucination이 크게 감소한다.</p>
<h2 id="6-summarization-데이터와-사용자-이해의-간극">6. Summarization: 데이터와 사용자 이해의 간극</h2>
<p>Summarization 단계는 파이프라인에서 독특한 위치를 차지한다. 사용자가 찾고 있는 실제 데이터에 접근하는 최초이자 유일한 컴포넌트다. 이 단계는 raw data와 사용자 이해 사이의 간극을 메우는 역할을 한다.</p>
<p>핵심 설계 원칙은 다음과 같다. 결과 표시는 시각적 그래프 표현과 텍스트 summary의 dual nature를 가진다. graph visualization은 관계와 구조를 보여주는 데 탁월하지만, 가치 있는 정보는 종종 노드 속성이나 결과의 더 넓은 맥락에 존재한다. summary는 시각화를 <strong>반복</strong>하지 않고 <strong>보완</strong>해야 한다.</p>
<table>
<thead>
<tr>
<th>영역</th>
<th>Graph Visualization</th>
<th>Text Summarization</th>
</tr>
</thead>
<tbody><tr>
<td>강점</td>
<td>관계·구조의 직관적 표현</td>
<td>속성·집계·트렌드 전달</td>
</tr>
<tr>
<td>약점</td>
<td>속성 세부의 표현 어려움</td>
<td>관계 토폴로지 전달 어려움</td>
</tr>
<tr>
<td>역할 분담</td>
<td>구조를 보여줌</td>
<td>시각적으로 드러나지 않는 패턴 강조</td>
</tr>
</tbody></table>
<p>또한 summarization은 사용자 의도에 묶여 있어야 한다. 요청되지 않은 인사이트를 생성하면 사용자 경험이 산만해진다. 효과적인 summary는 시각적으로 즉시 명백하지 않은 인사이트와 패턴을 강조하는 데 집중한다.</p>
<h2 id="7-expert-emulation-전체-프레임워크">7. Expert Emulation 전체 프레임워크</h2>
<p>여러 컴포넌트를 통합하면 expert emulation 프레임워크는 다음 pillars 위에 구축된다.</p>
<ul>
<li>사용자 질문 유형의 이해와 적절한 라우팅 (Intent Detection)</li>
<li>LLM이 효과적으로 사용할 수 있는 형태로 도메인 지식을 추출·표현 (Conceptual Schema + Annotations)</li>
<li>쿼리 구성에 대한 전문가의 추론 패턴 구현 (Reasoning-First Generation)</li>
<li>결과를 의미 있고 actionable한 방식으로 제시 (Summarization)</li>
</ul>
<p>이 프레임워크의 적용 범위는 KG 질의에 국한되지 않는다. 전문가 지식을 복잡한 데이터 구조에 체계적으로 적용해야 하는 모든 도메인으로 일반화 가능한 원칙이다.</p>
<h2 id="8-정리">8. 정리</h2>
<p>이 챕터의 핵심은 RAG의 한계를 인식하고 expert emulation 패러다임으로 전환하는 것이다. &quot;어떤 도전 과제든 전문가라면 어떻게 할 것인가&quot;를 묻고, 그 접근을 구현 가능한 단계로 분해하는 것이 시스템 설계 원칙이 된다. KG 시스템에서 이 원칙은 4단계 파이프라인으로 구체화된다.</p>
<ul>
<li><strong>Intent Detection</strong>: 질문 유형 분류로 적절한 처리 경로 선택</li>
<li><strong>Schema-to-LLM 변환</strong>: technical schema를 conceptual + annotated schema로 정제</li>
<li><strong>Reasoning-First Query Generation</strong>: time to think를 부여하여 사후 정당화 방지</li>
<li><strong>Summarization</strong>: 시각화와 보완 관계를 유지하며 사용자 의도에 묶인 인사이트 제공</li>
</ul>
<p>Prompt engineering의 본질은 LLM에 time to think를 주는 것이며, technical schema를 LLM-friendly 포맷으로 변환하는 작업은 불필요한 요소 제거, contextual annotation 추가, LLM의 처리 방식과 align되는 구조화의 결합이다.</p>
<blockquote>
<p>결론: 자연어 KG 질의의 핵심은 답을 생성하는 것이 아니라 올바른 질문을 형식화하는 것이며, 이는 expert emulation을 통해 구현된다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Knowledge Graphs and LLMs in Action. Manning, 2024 — Chapter 14: KG questions with natural language.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cypher 쿼리 언어 (Cypher Query Language)]]></title>
            <link>https://velog.io/@tasker_dev/Cypher-%EC%BF%BC%EB%A6%AC-%EC%96%B8%EC%96%B4-Cypher-Query-Language</link>
            <guid>https://velog.io/@tasker_dev/Cypher-%EC%BF%BC%EB%A6%AC-%EC%96%B8%EC%96%B4-Cypher-Query-Language</guid>
            <pubDate>Sun, 17 May 2026 05:36:46 GMT</pubDate>
            <description><![CDATA[<p>Neo4j 시리즈의 앞선 글들에서 그래프 데이터 모델의 구조와 노드·관계의 저장 방식을 다루었다. 모델이 정의되었다면 다음 단계는 그것을 질의하는 언어다. Cypher는 그래프 데이터베이스 질의를 위한 declarative query language로, SQL이 관계형 데이터베이스에서 차지하는 역할을 그래프 영역에서 수행한다. Neo4j는 Cypher를 openCypher 프로젝트로 오픈소스화하여 그래프 질의 언어의 표준으로 확산시키려는 작업을 이어오고 있다.</p>
<p>이번 글의 thesis는 다음과 같다. <strong>Cypher의 본질은 패턴 매칭이며, 성능은 인덱싱과 graph projection 선택에서 갈린다.</strong> MATCH/WHERE/RETURN의 문법은 한 시간이면 익히지만, 같은 쿼리를 수십 배 빠르게 만드는 결정은 인덱스 설계, projection 전략, EXPLAIN/PROFILE을 통한 실행 계획 분석에 달려 있다.</p>
<h2 id="1-cypher의-정체성-declarative-graph-query-language">1. Cypher의 정체성: Declarative Graph Query Language</h2>
<p>Cypher는 노드와 관계의 패턴을 ASCII art 형태의 문법으로 기술하는 선언적 질의 언어로 정의된다. 사용자는 &quot;어떻게 데이터를 가져올지&quot;가 아니라 &quot;무엇을 가져올지&quot;를 기술하며, 실행 계획 수립은 데이터베이스 엔진이 담당한다.</p>
<p>기본 구문 요소는 세 가지로 구성된다.</p>
<table>
<thead>
<tr>
<th>요소</th>
<th>역할</th>
<th>SQL 대응</th>
</tr>
</thead>
<tbody><tr>
<td><code>MATCH</code></td>
<td>노드·관계 패턴 매칭</td>
<td><code>SELECT</code> + <code>JOIN</code></td>
</tr>
<tr>
<td><code>WHERE</code></td>
<td>매칭 결과를 속성 조건으로 필터링</td>
<td><code>WHERE</code></td>
</tr>
<tr>
<td><code>RETURN</code></td>
<td>반환할 데이터 지정</td>
<td><code>SELECT</code> projection</td>
</tr>
</tbody></table>
<p>노드는 소괄호로, 관계는 대괄호와 화살표로 표기한다.</p>
<pre><code class="language-cypher">// 특정 인물이 출연한 영화 중 평점이 높은 작품 조회
MATCH (p:Person {name: &#39;Keanu Reeves&#39;})-[:ACTED_IN]-&gt;(m:Movie)
WHERE m.rating &gt; 8.0
RETURN m.title, m.rating
ORDER BY m.rating DESC
LIMIT 10</code></pre>
<p><code>MATCH</code> 절은 매칭할 패턴을 정의하지만, 어떤 요소를 결과로 반환할지는 <code>RETURN</code> 절이 결정한다. 두 절의 분리는 그래프 traversal과 결과 projection을 개념적으로 구분하는 Cypher의 설계 원칙이다. <code>LIMIT</code>은 반환 행 수를 제한하여 서버 부하를 방지하는 안전장치 역할을 한다.</p>
<h2 id="2-패턴-매칭의-동작-흐름">2. 패턴 매칭의 동작 흐름</h2>
<p>Cypher 쿼리가 어떻게 실행되는지 단계별로 시각화하면 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/7bc921a1-1550-4177-9915-d4b8d5a8c6e7/image.png" alt=""></p>
<p><code>MATCH</code>가 패턴의 시작점을 찾을 때 인덱스가 존재하면 인덱스 스캔으로 진입한다. 인덱스가 없으면 해당 라벨의 모든 노드를 순회하는 label scan이 발생한다. <code>WHERE</code> 절에서 사용되는 속성 역시 인덱스 활용 여부가 성능을 결정한다.</p>
<h3 id="관계-방향-처리">관계 방향 처리</h3>
<p>Neo4j는 내부적으로 모든 관계에 방향을 저장하지만, 쿼리에서는 방향을 명시하지 않아도 된다. 양방향 traversal이 필요하면 화살표 없이 <code>-[:REL]-</code> 형태로 작성한다.</p>
<pre><code class="language-cypher">// 방향 무관 매칭
MATCH (a:Person)-[:KNOWS]-(b:Person)
RETURN a, b</code></pre>
<h2 id="3-인덱싱-성능의-분기점">3. 인덱싱: 성능의 분기점</h2>
<p>인덱싱은 그래프 데이터베이스에서 성능을 결정짓는 가장 중요한 단일 요소다. 인덱스가 없으면 데이터베이스는 요청된 속성을 찾기 위해 모든 노드를 스캔해야 하며, 이는 데이터 규모가 커질수록 비현실적인 비용을 발생시킨다.</p>
<pre><code class="language-cypher">// 인덱스 생성 (Neo4j 5.x 문법)
CREATE INDEX person_name_idx FOR (p:Person) ON (p.name);

// 관계 속성에도 인덱스 생성 가능
CREATE INDEX rel_since_idx FOR ()-[r:KNOWS]-() ON (r.since);</code></pre>
<p><strong><code>WHERE</code> 절에서 사용되는 모든 속성은 인덱싱해야 한다.</strong> 이를 누락하면 노드 라벨 전체를 순회하는 비용이 발생하여, 데이터 규모에 따라 데이터베이스 자체가 응답 불능 상태에 빠질 수 있다. 노드뿐 아니라 관계의 속성도 인덱싱 대상이 된다는 점은 RDB와 구분되는 특징이다.</p>
<h2 id="4-degree-functions-연결-정도의-정량화">4. Degree Functions: 연결 정도의 정량화</h2>
<p>그래프 분석에서 가장 기초적인 측정값은 노드의 연결 수, 즉 degree다. Cypher는 이를 함수 형태로 제공한다.</p>
<table>
<thead>
<tr>
<th>Degree 종류</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>Outgoing Degree</td>
<td>source 노드에서 나가는 관계의 총 수</td>
</tr>
<tr>
<td>Incoming Degree</td>
<td>source 노드로 들어오는 관계의 총 수</td>
</tr>
<tr>
<td>Degree</td>
<td>incoming + outgoing 합계</td>
</tr>
</tbody></table>
<pre><code class="language-cypher">// 가장 영향력 있는 사용자 Top 10 (팔로워 수 기준)
MATCH (u:User)
RETURN u.name, COUNT { (u)&lt;-[:FOLLOWS]-() } AS followers
ORDER BY followers DESC
LIMIT 10</code></pre>
<p>Degree는 그 자체로도 분석 지표가 되지만, PageRank·중심성 계산 등 고급 알고리즘의 입력으로 작동한다.</p>
<h2 id="5-graph-projections-알고리즘-실행을-위한-별도-표현">5. Graph Projections: 알고리즘 실행을 위한 별도 표현</h2>
<p>Neo4j의 native graph 저장 방식은 traversal에는 최적화되어 있으나, 모든 그래프 알고리즘에 효율적이지는 않다. PageRank, community detection, similarity 계산 등은 인접 행렬과 같은 다른 자료구조에서 훨씬 빠르게 동작한다. 이 간극을 메우는 것이 <strong>graph projection</strong>이다.</p>
<p>Graph projection은 Neo4j Graph Data Science(GDS) 라이브러리가 관리하는, 알고리즘 실행에 최적화된 in-memory 표현이다. 관계형 데이터베이스의 &quot;view&quot; 개념과 유사하나, 메모리에 알고리즘 친화적 포맷(예: adjacency matrix)으로 적재된다는 점이 다르다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/0cebf2b4-2d42-48a5-991f-c30e23605954/image.png" alt=""></p>
<h3 id="두-가지-projection-방식">두 가지 Projection 방식</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>Native Projection</th>
<th>Cypher Projection</th>
</tr>
</thead>
<tbody><tr>
<td>정의 방식</td>
<td>기존 라벨·관계 타입 그대로 사용</td>
<td>Cypher 쿼리로 패턴 정의</td>
</tr>
<tr>
<td>계산 효율</td>
<td>가장 효율적</td>
<td>상대적으로 낮음</td>
</tr>
<tr>
<td>유연성</td>
<td>낮음 (전체 구조 기반)</td>
<td>높음 (서브셋·가공 가능)</td>
</tr>
<tr>
<td>UNDIRECTED 지원</td>
<td>가능</td>
<td>불가</td>
</tr>
<tr>
<td>적합 상황</td>
<td>대규모 전체 그래프 분석</td>
<td>서브셋·필터링된 분석</td>
</tr>
</tbody></table>
<p>Native projection은 기존 그래프 구조와 라벨을 그대로 사용하며 계산상 가장 효율적이다. Cypher projection은 사용자가 메모리에 적재할 노드·관계 패턴을 자유롭게 정의할 수 있는 유연성을 제공하지만, &#39;UNDIRECTED&#39; 지정이 불가능하다는 제약이 있다. 일부 알고리즘은 무방향성을 요구하기 때문에 이 제약은 알고리즘 선택을 제한할 수 있다. 또한 대규모 데이터셋에서는 Cypher projection 방식 자체가 실행 불가능할 수도 있다.</p>
<pre><code class="language-cypher">// Native projection 예시
CALL gds.graph.project(
  &#39;socialGraph&#39;,
  &#39;User&#39;,
  &#39;FOLLOWS&#39;
);

// Cypher projection 예시 (활성 사용자만)
CALL gds.graph.project.cypher(
  &#39;activeUsers&#39;,
  &#39;MATCH (u:User) WHERE u.lastLogin &gt; date(&quot;2025-01-01&quot;) RETURN id(u) AS id&#39;,
  &#39;MATCH (u1:User)-[:FOLLOWS]-&gt;(u2:User) RETURN id(u1) AS source, id(u2) AS target&#39;
);</code></pre>
<p>Projection 실행 전 노드 개수를 사전에 카운트하여 메모리 적재 규모를 예측하는 것이 권장되는 관행이다.</p>
<h2 id="6-jaccard-similarity-projection-활용-예시">6. Jaccard Similarity: Projection 활용 예시</h2>
<p>Jaccard Similarity 알고리즘은 두 노드가 공유하는 연결의 교집합 비율로 유사도를 계산한다. 공통 이웃이 많을수록 두 노드는 유사한 것으로 판정된다.</p>
<p>$$J(A, B) = \frac{|N(A) \cap N(B)|}{|N(A) \cup N(B)|}$$</p>
<pre><code class="language-cypher">// User-Product 그래프에서 유사 사용자 탐색
CALL gds.nodeSimilarity.stream(&#39;userProductGraph&#39;)
YIELD node1, node2, similarity
RETURN 
  gds.util.asNode(node1).name AS user1,
  gds.util.asNode(node2).name AS user2,
  similarity
ORDER BY similarity DESC
LIMIT 20</code></pre>
<p>이 방식은 중복 레코드 식별, 추천 시스템의 user-user 유사도 계산, entity resolution 등에서 활용된다. 전통적인 RDB에서는 self-join을 통해 구현 가능하나 관계 수가 증가하면 비용이 급격히 상승하므로, 그래프 알고리즘 기반 접근이 명확한 이점을 제공하는 영역이다.</p>
<h2 id="7-쿼리-분석-explain과-profile">7. 쿼리 분석: EXPLAIN과 PROFILE</h2>
<p>그래프 데이터베이스에서는 관계 traversal의 깊이가 한 단계 증가할 때마다 탐색 비용이 지수적으로 증가할 수 있다. 따라서 쿼리 실행 전 실행 계획을 검토하는 것은 옵션이 아니라 표준 절차다.</p>
<table>
<thead>
<tr>
<th>명령</th>
<th>실행 여부</th>
<th>제공 정보</th>
</tr>
</thead>
<tbody><tr>
<td><code>EXPLAIN</code></td>
<td>미실행</td>
<td>예상 실행 계획·접근 패턴</td>
</tr>
<tr>
<td><code>PROFILE</code></td>
<td>실행</td>
<td>실행 계획 + 단계별 통계(db hits 등)</td>
</tr>
</tbody></table>
<pre><code class="language-cypher">// 실행 없이 계획만 확인
EXPLAIN
MATCH (p:Person)-[:ACTED_IN]-&gt;(m:Movie)
WHERE p.name = &#39;Tom Hanks&#39;
RETURN m.title;

// 실제 실행 + 통계
PROFILE
MATCH (p:Person)-[:ACTED_IN]-&gt;(m:Movie)
WHERE p.name = &#39;Tom Hanks&#39;
RETURN m.title;</code></pre>
<p><code>EXPLAIN</code>은 쿼리를 실행하지 않고 데이터베이스가 어떤 노드·관계·속성에 접근할지를 보여주는 반면, <code>PROFILE</code>은 실제 실행과 함께 각 단계의 db hits 통계를 제공한다. 비용이 큰 쿼리를 사전에 식별하는 것이 핵심 목적이다.</p>
<h2 id="8-장시간-실행-쿼리-관리">8. 장시간 실행 쿼리 관리</h2>
<p>잘못 작성된 쿼리는 데이터베이스 전체에 영향을 줄 수 있다. Neo4j는 실행 중인 쿼리 조회와 취소 메커니즘을 제공한다.</p>
<pre><code class="language-cypher">// 현재 실행 중인 쿼리 조회
SHOW TRANSACTIONS;

// 특정 트랜잭션 취소
TERMINATE TRANSACTION &quot;transaction-id&quot;;</code></pre>
<p>쿼리 취소는 협력적 연산(cooperative operation)이다. 데이터베이스는 일정 간격으로 중단 여부를 확인하며, 이 때문에 일부 연산은 종료 요청 후에도 한동안 응답하지 않을 수 있다. 가능한 모든 쿼리는 실행 전 최적화하고, 자원 한계를 사전 설정하여 취소 자체가 필요한 상황을 줄이는 편이 낫다. 극단적인 경우 데이터베이스 재시작이 transient state를 정리하고 새 설정을 적용하는 방법이 된다.</p>
<h2 id="9-한계-및-트레이드오프">9. 한계 및 트레이드오프</h2>
<p>Cypher와 Neo4j 기반 분석은 강력하지만 몇 가지 구조적 제약을 갖는다.</p>
<p><strong>첫째, 깊은 traversal의 비용 폭발 위험.</strong> Cypher는 다중 hop 질의를 간결하게 표현할 수 있지만, 변수 길이 패턴(<code>-[*1..5]-</code>)은 노드의 평균 degree가 클 경우 지수적 비용을 발생시킨다. EXPLAIN으로 사전 점검이 가능하지만, 데이터 분포에 따라 실제 비용은 추정치와 크게 달라질 수 있다.</p>
<p><strong>둘째, Graph projection의 메모리 의존성.</strong> GDS의 projection 기반 알고리즘은 그래프를 메모리에 적재한 후 실행된다. 대규모 그래프에서는 projection 자체가 불가능하거나 OOM을 발생시킬 수 있으며, Cypher projection 방식은 native projection보다 더 빠르게 메모리 한계에 도달한다. &#39;UNDIRECTED&#39; 미지원 등 알고리즘 호환성 제약도 함께 존재한다.</p>
<p><strong>셋째, 인덱스의 양면성.</strong> 인덱스는 읽기 성능을 극적으로 개선하지만 쓰기 비용을 증가시킨다. 빈번한 쓰기가 발생하는 노드 속성에 인덱스를 다수 설정하면 ingestion 처리량이 저하된다. 인덱싱 대상 선정은 워크로드의 read/write 비율을 기준으로 결정해야 한다.</p>
<h2 id="10-정리">10. 정리</h2>
<p>native graph 데이터베이스의 가치는 전통적 RDB에서 발견이 어려운 패턴을 드러내는 능력에 있다. Cypher는 이 능력을 활용하기 위한 표준 인터페이스이며, 그 운용은 다음 네 가지 축으로 압축된다.</p>
<ul>
<li><strong>MATCH/WHERE/RETURN</strong> 패턴 매칭 문법의 정확한 이해</li>
<li><strong>인덱싱</strong>을 통한 조회 성능의 orders of magnitude 단위 개선</li>
<li><strong>Graph projection</strong> 선택(native vs Cypher)에 따른 알고리즘 실행 효율 조정</li>
<li><strong>EXPLAIN/PROFILE</strong>을 통한 사전·사후 실행 계획 분석</li>
</ul>
<p>이 네 축의 균형이 그래프 데이터베이스 프로젝트의 성패를 가른다.</p>
<blockquote>
<p>결론: Cypher의 문법은 패턴 매칭이지만, 그 성능은 인덱스와 projection 설계에서 결정된다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Graph Data Science with Python and Neo4j
— Chapter 4: Cypher Query Language.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[GraphRAG 시스템을 직접 구축하며 발견한 것들]]></title>
            <link>https://velog.io/@tasker_dev/VectorRAG-%EC%9D%98-%ED%95%9C%EA%B3%84%EB%A5%BC-%EB%84%98%EC%96%B4-GraphRAG-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%84-%EC%A7%81%EC%A0%91-%EA%B5%AC%EC%B6%95%ED%95%98%EB%A9%B0-%EB%B0%9C%EA%B2%AC%ED%95%9C-%EA%B2%83%EB%93%A4</link>
            <guid>https://velog.io/@tasker_dev/VectorRAG-%EC%9D%98-%ED%95%9C%EA%B3%84%EB%A5%BC-%EB%84%98%EC%96%B4-GraphRAG-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%84-%EC%A7%81%EC%A0%91-%EA%B5%AC%EC%B6%95%ED%95%98%EB%A9%B0-%EB%B0%9C%EA%B2%AC%ED%95%9C-%EA%B2%83%EB%93%A4</guid>
            <pubDate>Sat, 16 May 2026 07:32:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>일반적인 VectorRAG 구조와 동일한 chunker 정책을 유지하면서 GraphRAG 레이어를 추가했을 때, 어떤 새로운 질의 패턴이 가능한지 — 그리고 production 적용 시 마주한 본질적 trade-off 에 대한 기록.</p>
<p>한 페이지 분량의 PDF 문서에서 <strong>회사 / 리스크 / 지표 / 전망</strong> 4가지 entity 타입을 추출하고, 100여 개 raw entity 가 NED(Named Entity Disambiguation) 를 거쳐 절반으로 압축되는 과정 (compression 0.49) 을 직접 측정했다.</p>
<p>한편 LangChain SemanticChunker 가 CPU + bge-m3 조합에서 <strong>820자를 분할하는 데 31.3초</strong> 가 걸리는 본질적 비효율도 발견했다. 이 발견 자체가 GraphRAG 의 production 적용 가능성에 대한 정량적 인사이트가 되었다.</p>
</blockquote>
<hr>
<h2 id="들어가며">들어가며</h2>
<p>요즘 RAG(Retrieval-Augmented Generation) 시스템은 거의 표준이 되었다. 비정형 문서를 청크로 쪼개고, 임베딩으로 변환해서 vector store 에 적재한 뒤, 사용자 질의가 들어오면 코사인 유사도 기반으로 가장 가까운 청크 N개를 가져와서 LLM 에 넣는다. 이게 우리가 흔히 말하는 <strong>VectorRAG</strong> 다.</p>
<p>잘 동작한다. 그런데 어느 순간 한계를 마주한다.</p>
<blockquote>
<p>&quot;<strong>특정 회사와 함께 언급된 리스크</strong>가 뭐가 있지?&quot;
&quot;<strong>같은 회사</strong>가 여러 보고서에서 일관되게 강조하는 메트릭은?&quot;
&quot;<strong>특정 거시 요인</strong>이 영향을 미친 회사들은?&quot;</p>
</blockquote>
<p>이런 질의는 VectorRAG 가 잘 못한다. 청크 유사도 검색은 &quot;비슷한 텍스트&quot; 를 찾아줄 뿐, <strong>&quot;entity 간의 관계&quot;</strong> 를 추출하지 못하기 때문이다. 청크 5개를 가져와서 LLM 에게 후처리시킬 수는 있지만, 이건 검색이 아니라 <strong>재추론</strong> 이다. 비용과 시간이 든다.</p>
<p>그래서 GraphRAG 다. 청크에서 직접 entity 와 관계를 추출해서 그래프 DB 에 적재하면, &quot;회사 X → FACES_RISK → ?&quot; 같은 쿼리를 한 번에 답할 수 있다.</p>
<p>이 글은 일반적인 VectorRAG 시스템 위에 GraphRAG 레이어를 plug-in 형태로 추가해보면서 발견한 것들을 정리한 기록이다.</p>
<hr>
<h2 id="1-vectorrag-의-한계--왜-그래프가-필요한가">1. VectorRAG 의 한계 — 왜 그래프가 필요한가</h2>
<p>먼저 VectorRAG 의 구조를 잠깐 짚자. 일반적인 흐름은 다음과 같다.</p>
<pre><code>원본 문서 (PDF/HWP/DOC 등)
    ↓ (chunker)
청크 (size=700, overlap=200)
    ↓ (embedding)
vector store
    ↓ (사용자 질의)
유사도 top-K 검색
    ↓ (LLM)
답변 생성</code></pre><p>이 구조의 강점은 명확하다. <strong>빠르고, 단순하고, 새 문서 추가가 trivial 하다.</strong> 청크 → 임베딩 → 저장이 끝. 검색도 cosine similarity 한 번이면 끝.</p>
<p>문제는 답변할 수 있는 질의의 형태가 제한적이라는 점이다. <strong>유사도 검색은 본질적으로 &quot;이 청크와 비슷한 청크&quot;</strong> 를 찾는 연산이다. 그래서 잘 답하는 질의는 이런 류다.</p>
<ul>
<li>&quot;회사 X 의 영업이익은?&quot; → 매출/실적 언급된 청크 5개 → LLM 이 추출 → 답</li>
<li>&quot;시장 전망은?&quot; → 전망 언급된 청크 → 답</li>
</ul>
<p>반면 못 답하는 질의:</p>
<ul>
<li><strong>&quot;회사 X 와 함께 언급된 리스크가 뭐가 있지?&quot;</strong>
→ 청크 5개 가져와도 &quot;리스크&quot; 자체가 entity 가 아니라 텍스트라 LLM 이 매번 재추출해야 함</li>
<li><strong>&quot;같은 entity 가 분기별 보고서에서 어떻게 변화했지?&quot;</strong>
→ 청크 간 entity 동일성을 모르니까 비교 못 함</li>
<li><strong>&quot;회사 X 와 회사 Y 의 공통 리스크는?&quot;</strong>
→ 두 회사 청크 따로 가져와서 LLM 이 합쳐서 비교 — 매번 비싼 호출</li>
</ul>
<p>이런 질의에서 VectorRAG 는 항상 LLM 후처리에 의존한다. <strong>&quot;검색&quot;이 아니라 &quot;검색 + 재추론&quot;</strong> 이 되어버린다. 비용이 청크 수에 선형 비례하고, 결과의 일관성도 떨어진다.</p>
<p>핵심은 이거다. <strong>유사도 검색은 텍스트의 의미 거리만 안다. Entity 간의 관계는 모른다.</strong></p>
<p>여기서 GraphRAG 가 들어온다.</p>
<hr>
<h2 id="2-graphrag-실측-사례--1쪽-pdf-의-그래프-변환">2. GraphRAG 실측 사례 — 1쪽 PDF 의 그래프 변환</h2>
<p>말로만 하면 추상적이니까 실제 데이터로 보자. 평가용으로 한 페이지 분량의 시황분석 형식 PDF 를 골랐다. 약 4400자, 6개 섹션, 52개 청크로 분할된다.</p>
<p>이 1쪽 PDF 를 GraphRAG 파이프라인에 통과시킨 결과는 다음과 같다.</p>
<h3 id="추출-결과">추출 결과</h3>
<table>
<thead>
<tr>
<th>단계</th>
<th>결과</th>
</tr>
</thead>
<tbody><tr>
<td>Layer A 적재 (구조)</td>
<td>Document 1 + Section 6 + Chunk 52 + NEXT 46 = <strong>59 노드 + 104 관계</strong></td>
</tr>
<tr>
<td>Entity 추출 (raw)</td>
<td><strong>113개</strong></td>
</tr>
<tr>
<td>NED 그룹화</td>
<td>113 → <strong>55 group</strong> (compression 0.49)</td>
</tr>
<tr>
<td>Layer B 관계</td>
<td><strong>28개</strong> (HAS_METRIC 13 + FACES_RISK 7 + ...)</td>
</tr>
<tr>
<td>MENTIONS 적재</td>
<td><strong>108개</strong> (Chunk → Entity)</td>
</tr>
<tr>
<td>총 처리 시간</td>
<td><strong>227.5초 (약 4분)</strong></td>
</tr>
</tbody></table>
<p>특히 entity 타입별로 들여다보면 흥미롭다.</p>
<pre><code class="language-cypher">MATCH (c:Chunk)-[:MENTIONS]-&gt;(e:Entity)
RETURN labels(e)[1] AS type, count(DISTINCT c) AS chunks, count(e) AS mentions
ORDER BY mentions DESC;</code></pre>
<table>
<thead>
<tr>
<th>Entity Type</th>
<th>DISTINCT Chunks</th>
<th>Total Mentions</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Company</strong></td>
<td>14</td>
<td>68</td>
</tr>
<tr>
<td><strong>Risk</strong></td>
<td>6</td>
<td>21</td>
</tr>
<tr>
<td><strong>Metric</strong></td>
<td>4</td>
<td>14</td>
</tr>
<tr>
<td><strong>Outlook</strong></td>
<td>3</td>
<td>5</td>
</tr>
</tbody></table>
<p>1쪽짜리 시황분석에서 <strong>14개 회사 + 6개 리스크 + 4개 지표 + 3개 전망</strong>이 추출됐다. 그리고 Company entity 1개당 평균 <strong>4.86개 청크에서 언급</strong>됐다.</p>
<p>이 숫자가 GraphRAG 의 정량적 가치다. VectorRAG 였다면 &quot;회사 X&quot; 키워드로 검색해서 청크 5개 가져오는 데 그쳤을 텐데, GraphRAG 는 다음과 같은 사실들을 그래프 구조로 직접 표현한다.</p>
<ul>
<li>회사 X (Company) → FACES_RISK → 거시 리스크 A (Risk)</li>
<li>회사 X (Company) → HAS_METRIC → 영업이익 지표 (Metric)</li>
<li>회사 Y (Company) ← MENTIONS ← Chunk_023 ← Section_2 ← Document_보고서3</li>
</ul>
<p>이러면 <strong>&quot;회사 X 와 함께 언급된 리스크는?&quot;</strong> 같은 질의가 Cypher 한 줄로 답해진다.</p>
<pre><code class="language-cypher">MATCH (c:Company {name: $company})-[:FACES_RISK]-&gt;(r:Risk)
RETURN r.name;</code></pre>
<p>LLM 호출 0번. 응답 시간 100ms 미만. <strong>검색이 곧 답</strong> 이다.</p>
<hr>
<h2 id="3-layer-a--b--mentions--문서-구조와-의미-그래프를-동시에">3. Layer A + B + MENTIONS — 문서 구조와 의미 그래프를 동시에</h2>
<p>그래프를 어떻게 설계했느냐가 GraphRAG 의 거의 모든 것이다. 직접 정의한 ontology 스키마를 따라 <strong>3-Layer 구조</strong>로 적재한다.</p>
<h3 id="layer-a--문서-구조-document-structure">Layer A — 문서 구조 (Document Structure)</h3>
<p>원본 문서의 물리적 구조를 그대로 보존한다. 출처 추적과 evidence 제공이 목적이다.</p>
<pre><code>(:Document {id, filename, doc_type, publisher, fiscal_year, ...})
  -[:HAS_SECTION]-&gt;
(:Section {id, label, heading_level, page_start, ...})
  -[:CONTAINS_CHUNK]-&gt;
(:Chunk {id, text, char_count, page, order_index})
  -[:NEXT]-&gt;
(:Chunk)  # 같은 Section 내 순서

(:Section) -[:CONTAINS_TABLE]-&gt; (:Table {raw_markdown, row_count, ...})</code></pre><p>이 레이어만 있어도 <strong>&quot;이 답변이 어느 문서 어느 페이지 어느 청크에서 왔는지&quot;</strong> 를 정확히 추적할 수 있다. 도메인에 따라 다르지만, 출처가 중요한 분야에서는 생명이다.</p>
<h3 id="layer-b--entity-의미-그래프-entity-interaction">Layer B — Entity 의미 그래프 (Entity Interaction)</h3>
<p>청크에서 LLM 으로 추출한 entity 와 관계를 적재한다. 의미 기반 쿼리의 주체다.</p>
<pre><code>(:Entity:Company {group_id, name, aliases, ...})
(:Entity:Risk    {group_id, name, ...})
(:Entity:Metric  {group_id, name, ...})
(:Entity:Outlook {group_id, name, ...})

(:Entity:Company) -[:FACES_RISK]-&gt; (:Entity:Risk)
(:Entity:Company) -[:HAS_METRIC]-&gt; (:Entity:Metric)
(:Entity:Company) -[:HAS_OUTLOOK]-&gt; (:Entity:Outlook)</code></pre><p><strong>멀티 라벨</strong> — <code>:Entity</code> (전역 검색용) 과 구체 타입 (<code>:Company</code>, <code>:Risk</code>...) 을 동시에 부착한다. <code>MATCH (e:Entity)</code> 로 전체 entity 검색이 가능하면서, <code>MATCH (e:Company)</code> 로 타입별 검색도 가능하다.</p>
<h3 id="mentions--layer-a-와-layer-b-의-다리">MENTIONS — Layer A 와 Layer B 의 다리</h3>
<p>가장 중요한 연결고리다. <strong>&quot;어느 청크가 어느 entity 를 언급했는가&quot;</strong> 를 표현한다.</p>
<pre><code>(:Chunk) -[:MENTIONS]-&gt; (:Entity)</code></pre><p>이 관계 덕분에 다음이 가능하다.</p>
<ul>
<li>Entity → 청크 역추적: 회사 X 를 언급한 모든 청크 찾기 → evidence 제공</li>
<li>청크 → Entity 그래프: 청크가 어떤 entity 들 사이의 관계를 다루는지</li>
<li><strong>다중 문서 간 entity 공유</strong>: 같은 entity 가 여러 문서에서 어떻게 등장하는지</li>
</ul>
<p>마지막 부분이 진짜 핵심이다. 분기별 보고서 4개에서 같은 회사명이 등장하면, NED 가 이들을 <strong>같은 group_id 로 묶는다</strong>. 그러면 단일 Entity 노드를 4개 Chunk 가 MENTIONS 하는 구조가 된다. <strong>시계열 분석이 그래프 traversal 한 번</strong>으로 끝난다.</p>
<pre><code class="language-cypher">MATCH (e:Entity:Company {name: $company})
      &lt;-[:MENTIONS]-(c:Chunk)
      &lt;-[:CONTAINS_CHUNK]-(s:Section)
      &lt;-[:HAS_SECTION]-(d:Document)
RETURN DISTINCT d.filename;</code></pre>
<p>→ 해당 회사가 등장하는 모든 보고서가 한 번에 나온다.</p>
<hr>
<h2 id="4-ned-compression--같은-entity-를-인식하는-핵심-기술">4. NED Compression — 같은 entity 를 인식하는 핵심 기술</h2>
<p>여기서 짚어야 하는 게 NED (Named Entity Disambiguation) 다. LLM 으로 청크별로 entity 를 뽑으면 같은 회사가 다른 표기로 나온다.</p>
<pre><code>청크 1:  &quot;회사명 정식 표기&quot;
청크 5:  &quot;회사명 약칭&quot;
청크 12: &quot;회사명(주)&quot;
청크 23: &quot;회사명 영문&quot;</code></pre><p>NED 가 없으면 이 4개를 <strong>다른 entity 4개</strong> 로 본다. 그러면 그래프가 사방으로 fragmenting 된다. &quot;회사 X 의 리스크&quot; 같은 쿼리가 4번 검색이 되어버리고, 결과도 4개로 쪼개진다.</p>
<p>다음 단계로 NED 를 한다.</p>
<ol>
<li><strong>Canonical 정규화</strong>: 괄호 제거, 공백 정리, 영문 소문자화</li>
<li><strong>임베딩 기반 유사도</strong>: bge-m3 로 entity 명을 임베딩, cosine similarity 계산</li>
<li><strong>임계값 그룹화</strong>: 0.92 이상이면 같은 group 으로 묶음</li>
<li><strong>Representative 선정</strong>: group 내에서 가장 많이 등장한 표기를 대표 이름으로</li>
</ol>
<p>1쪽 PDF 의 실측 결과는 다음과 같았다.</p>
<table>
<thead>
<tr>
<th>단계</th>
<th>개수</th>
</tr>
</thead>
<tbody><tr>
<td>Raw entity (청크별 추출 합산)</td>
<td>113</td>
</tr>
<tr>
<td><strong>임계값 0.92 그룹화 후</strong></td>
<td><strong>55</strong></td>
</tr>
<tr>
<td><strong>Compression ratio</strong></td>
<td><strong>0.49 (51% 중복 제거)</strong></td>
</tr>
</tbody></table>
<p>평균적으로 <strong>같은 entity 가 약 2번씩 다른 표기로 등장</strong> 했다는 뜻이다. 113개 모두 별도 노드로 만들었으면 그래프가 의미 없어졌을 것이다. NED 가 GraphRAG 를 실질적으로 valuable 하게 만든다.</p>
<p>여러 문서 풀 적재 후에는 이 compression 이 더 극적으로 나타날 것으로 예상된다. 같은 회사가 여러 분기 보고서에 등장하면 단일 group_id 로 묶이고, 다중 문서를 동시 traversal 하는 쿼리가 trivial 해진다.</p>
<hr>
<h2 id="5-운영-환경의-trade-off--production-의-함정">5. 운영 환경의 trade-off — production 의 함정</h2>
<p>여기까지 보면 &quot;GraphRAG 좋네, 적용하자&quot; 라고 결론 내고 싶다. 그런데 실제로 다수의 문서를 batch 적재하려고 했을 때 <strong>본질적 trade-off</strong> 를 마주했다.</p>
<h3 id="발견-semanticchunker--bge-m3--cpu--비현실적">발견: SemanticChunker + bge-m3 + CPU = 비현실적</h3>
<p>일반적으로 쓰이는 chunker 정책을 따랐다.</p>
<pre><code class="language-python">DEFAULT_CHUNK_SIZE = 700
DEFAULT_CHUNK_OVERLAP = 200</code></pre>
<p>chunker 의 핵심 컴포넌트는 LangChain <code>SemanticChunker</code> 다. 문장들의 임베딩 거리를 보고 의미 단위 경계에서 자른다. production 환경에서는 잘 동작하는 모듈로 알려져 있다.</p>
<p>그런데 개인 환경 (CPU + 노트북) 에서 돌려보면 멈춘다. 한참 멈춘다. 디버깅 1.5시간 끝에 원인이 좁혀졌다.</p>
<p><strong>단독 벤치</strong>:</p>
<ul>
<li>bge-m3 임베딩 50문장 (batch_size=32) → 5.4초 ✅ 정상</li>
<li>PDF 파싱 (PyMuPDF) → 0.1초 ✅ 정상</li>
<li>Neo4j connectivity → OK ✅ 정상</li>
<li>SemanticChunker 초기화 → OK ✅ 정상</li>
<li><strong><code>SemanticChunker.split_text(820자)</code> → 31.3초</strong> ❌ 비정상</li>
</ul>
<p>원인: LangChain <code>SemanticChunker</code> 는 임베딩을 <strong>문장 단위 sequential 호출</strong> 한다 (batch 미사용). bge-m3 (568M params) + CPU 조합에서 문장당 ~400ms × 수십 문장 = 분 단위 소요.</p>
<p>추정 — 8개 문서 전체 분할만 <strong>40분 ~ 1시간</strong>. LLM 호출과 Neo4j 적재까지 더하면 production 으로 못 쓴다.</p>
<h3 id="trade-off-정량화">Trade-off 정량화</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>SemanticChunker (의미 분할)</th>
<th>RecursiveCharacterTextSplitter (단순 분할)</th>
</tr>
</thead>
<tbody><tr>
<td>분할 품질</td>
<td>◎ 문장 의미 경계</td>
<td>△ 문자 수 기반</td>
</tr>
<tr>
<td>1문서 분할 시간 (CPU)</td>
<td>~3분</td>
<td>~수십 ms</td>
</tr>
<tr>
<td>다수 문서 추정</td>
<td>분당 처리량 매우 낮음</td>
<td>즉시</td>
</tr>
<tr>
<td>임베딩 호출</td>
<td>sequential per sentence</td>
<td>없음</td>
</tr>
<tr>
<td>Production 적용</td>
<td>GPU 또는 API 임베딩 필수</td>
<td>CPU 만으로 OK</td>
</tr>
</tbody></table>
<h3 id="해결-환경변수-fallback">해결: 환경변수 fallback</h3>
<p>코드에 환경변수 분기를 추가했다.</p>
<pre><code class="language-python">def _semantic_split(text: str) -&gt; list[str]:
    # 발견: SemanticChunker + bge-m3 + CPU = 820자/31초 (비현실적)
    # 옵션: 환경변수로 단순 분할 fallback
    if os.getenv(&quot;USE_SIMPLE_CHUNKER&quot;) == &quot;1&quot;:
        return RecursiveCharacterTextSplitter(
            chunk_size=DEFAULT_CHUNK_SIZE,
            chunk_overlap=DEFAULT_CHUNK_OVERLAP,
            separators=[&quot;\n\n&quot;, &quot;\n&quot;, &quot;. &quot;, &quot;。&quot;, &quot;? &quot;, &quot;! &quot;, &quot; &quot;, &quot;&quot;],
        ).split_text(text)
    # ... 기존 SemanticChunker 코드</code></pre>
<p><strong>기본값은 OFF</strong>. 일반적인 동작이 유지된다. <code>USE_SIMPLE_CHUNKER=1</code> 을 켜는 환경에서만 fallback 이 작동한다. <strong>chunk 정책 (size=700, overlap=200, min=50) 자체는 동일</strong> 하므로 GraphRAG vs VectorRAG 비교의 chunk 경계 변수가 통제된다.</p>
<p>이 fallback 으로 다수 문서 적재가 30분 안에 끝난다.</p>
<h3 id="인사이트-rag-비교-실험은-동일-chunker-가-전제">인사이트: &quot;RAG 비교 실험은 동일 chunker 가 전제&quot;</h3>
<p>이 발견 자체가 가장 큰 인사이트였다. <strong>production 환경 (GPU 또는 API 임베딩) 을 가정한 컴포넌트가 개인 환경 (CPU + 오픈소스 모델) 에서는 완전히 다른 비용 프로파일을 가진다.</strong> SemanticChunker 는 OpenAI API 임베딩 같은 빠른 임베딩과 짝궁이지, CPU + 무거운 모델 조합으로는 못 쓴다.</p>
<p>GraphRAG vs VectorRAG 를 진짜로 비교하려면 두 시스템이 <strong>같은 chunker</strong> 를 써야 한다. CPU 환경에서 SemanticChunker 가 비현실적이면, 진짜 비교는 GPU 또는 API 환경을 마련한 다음에야 가능하다.</p>
<p>이게 한 줄로 정리되는 결론이다.</p>
<blockquote>
<p><strong>&quot;RAG 시스템의 비교는 chunker 동일성이 전제. CPU 한계가 메타-비교 차원의 변수다.&quot;</strong></p>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[ChatGPT로 Neo4j 데이터 강화하기]]></title>
            <link>https://velog.io/@tasker_dev/ChatGPT%EB%A1%9C-Neo4j-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B0%95%ED%99%94%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@tasker_dev/ChatGPT%EB%A1%9C-Neo4j-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B0%95%ED%99%94%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 09 May 2026 06:40:01 GMT</pubDate>
            <description><![CDATA[<h2 id="1-챕터의-위치">1. 챕터의 위치</h2>
<p>이전 챕터에서 Bloom과 Power BI를 통해 Neo4j 그래프를 시각화하는 방법을 다뤘다면, 이 챕터는 그 시각화의 입력이 되는 <strong>데이터 자체를 어떻게 풍부하게 만들 것인가</strong>의 문제를 다룬다. 이 챕터의 thesis는 다음과 같다. <strong>그래프 데이터의 가치는 노드와 관계의 양만으로 결정되지 않는다. LLM을 활용해 텍스트 속성을 요약·임베딩으로 변환하고, 그 결과를 다시 그래프에 적재하는 enrichment 사이클이 분석 가능성의 한계를 결정한다.</strong></p>
<p>ChatGPT는 2022년 OpenAI가 출시한 LLM으로, 업계 다수가 NLP 모델 효능의 변곡점으로 평가한다. 이 챕터의 실용적 관심은 ChatGPT 자체의 기술적 새로움이 아니라, <strong>이 도구를 Neo4j와 결합해 데이터 강화 도구로 사용하는 패턴</strong>에 있다.</p>
<h2 id="2-데이터-강화의-의미">2. 데이터 강화의 의미</h2>
<p>ChatGPT를 데이터 강화 도구로 본다는 것은 다음 다섯 역량을 그래프 데이터에 적용한다는 의미다.</p>
<ul>
<li>텍스트 프롬프트의 이해</li>
<li>새로운 데이터 생성</li>
<li>데이터 평가 코드 제공</li>
<li>텍스트 데이터 증강</li>
<li>그 외 다수의 텍스트 처리 능력</li>
</ul>
<p>이러한 강화가 비즈니스 가치로 이어지는 경로는 단순하다. 강화된 데이터에서 도출되는 통찰이 <strong>숨겨진 패턴과 상관관계를 드러내고, 그것이 전략적 의사결정에 기여</strong>한다. 강화는 그 자체로 목적이 아니라, 분석과 의사결정의 입력 품질을 끌어올리는 수단이다.</p>
<p>ChatGPT 사용 경로는 두 가지다. 브라우저 인터페이스(<code>https://chat.openai.com/</code>)는 즉시 실험에 적합하고, Python의 OpenAI 라이브러리는 자동화된 파이프라인 통합에 적합하다. Knowledge cutoff 한계는 명시적으로 인지되어야 한다. 모델은 훈련 데이터 시점 이후의 정보를 모르며, 최신 정보가 필요한 작업에서는 검색 보완이 필수다.</p>
<h2 id="3-특허-데이터-그래프--llm의-이상적-도메인">3. 특허 데이터: 그래프 + LLM의 이상적 도메인</h2>
<p>이 챕터는 특허 데이터셋을 사례로 사용한다. 특허가 그래프 + LLM 조합에 적합한 이유는 세 가지로 정리된다.</p>
<table>
<thead>
<tr>
<th>특성</th>
<th>그래프·LLM 적합성</th>
</tr>
</thead>
<tbody><tr>
<td>방대함 (vast)</td>
<td>단일 문서가 아닌 거대한 corpus가 분석 단위</td>
</tr>
<tr>
<td>상호 연결 (interlinked)</td>
<td>특허는 선행 특허를 인용하고 후속 특허에 인용된다. 자연스러운 그래프 구조</td>
</tr>
<tr>
<td>밀도 높고 기술적 (dense, technical)</td>
<td>비전문가에게 어렵다. LLM이 요약과 임베딩 생성에서 가치를 발휘</td>
</tr>
</tbody></table>
<p>특허는 발명자의 아이디어를 공개하는 대신 발명자에게 법적 권리를 부여하는 IP(intellectual property) 형태다. 데이터 모델 측면에서 특허는 세 핵심 요소로 표현된다.</p>
<table>
<thead>
<tr>
<th>요소</th>
<th>정의</th>
</tr>
</thead>
<tbody><tr>
<td>Assignee</td>
<td>특허의 소유권을 보유한 개인·기업·대학</td>
</tr>
<tr>
<td>Inventor</td>
<td>아이디어를 처음 구상한 개인 또는 그룹</td>
</tr>
<tr>
<td>Topic</td>
<td>특허가 속한 기술 분야·주제</td>
</tr>
</tbody></table>
<p>이 세 요소는 그래프에서 각각 노드 종류가 되며, Document 노드와의 관계(<code>ASSIGNED_TO</code>, <code>INVENTED_BY</code>, <code>IS_IN</code>)로 연결된다.</p>
<h2 id="4-chatgpt-강화의-네-갈래">4. ChatGPT 강화의 네 갈래</h2>
<p>특허 데이터에 ChatGPT를 적용하는 방향은 네 가지로 정리된다.</p>
<ul>
<li>간결한 특허 요약 (Concise Patent Summaries)</li>
<li>주제 확장 (Topic Expansion)</li>
<li>발명자·양수인 프로파일 (Profile of Investors)</li>
<li>특허 간 유사도 식별 (Identify Similarities Between Patents)</li>
</ul>
<p>이 챕터의 본격적 워크플로는 첫 번째 방향인 요약 생성으로 시작된다. 요약이 만들어진 뒤 그것을 임베딩으로 변환하면 네 번째 방향인 유사도 분석으로 자연스럽게 이어진다.</p>
<h2 id="5-핵심-활용-사례-간결한-특허-요약">5. 핵심 활용 사례: 간결한 특허 요약</h2>
<p>특허는 길고 복잡하며, 비전문가에게 혼란을 주는 용어로 가득하다. 이 본질적 접근성 문제를 ChatGPT가 해결한다. 핵심 패턴은 단순하다. <strong>특허 abstract를 ChatGPT에 입력으로 넣고, 비전문가 언어로 100토큰 이내 요약을 받아낸다.</strong></p>
<p>전형적 프롬프트는 다음과 같다.</p>
<pre><code>Summarize the following patent abstract in laymen&#39;s terms in fewer than 100 tokens.</code></pre><p>이 프롬프트에는 세 가지 제약이 명시되어 있다. 입력 대상(<code>patent abstract</code>), 표현 수준(<code>laymen&#39;s terms</code>), 길이 제약(<code>fewer than 100 tokens</code>). 이 제약이 출력의 일관성과 후속 처리 가능성을 보장한다.</p>
<p>생성된 요약을 다시 Neo4j에 기록하는 단계가 enrichment 사이클의 핵심이다. Pandas DataFrame으로 받은 요약을 <code>update_node_summary()</code> 같은 함수가 Cypher의 <code>SET a.Summary = $summary</code>로 노드에 추가한다. 결과적으로 Document 노드는 원본 abstract와 LLM 생성 summary를 모두 속성으로 가지게 된다. 두 형식의 공존이 의도다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/2cd9bac2-054e-4628-b19c-34dcf511f4a4/image.png" alt=""></p>
<p>이 사이클이 enrichment의 표준 패턴이다. <strong>그래프에서 텍스트를 꺼내 LLM에 보내고, 결과를 그래프에 다시 적재한다.</strong> Neo4j는 단순한 결과 저장소가 아니라 enrichment의 시작점이자 종착점이다.</p>
<h2 id="6-임베딩의-본질">6. 임베딩의 본질</h2>
<p>요약 생성이 텍스트의 길이를 줄이는 작업이라면, 임베딩 생성은 텍스트를 <strong>수치 벡터</strong>로 변환하는 작업이다. 두 작업의 목적이 다르다.</p>
<p>요약은 사람이 읽기 위한 형식이고, 임베딩은 기계가 분석하기 위한 형식이다. 두 형식 모두 보존하는 것이 enrichment 패턴의 핵심이다.</p>
<p>임베딩의 정의를 정리하면 다음과 같다. 텍스트의 본질과 의미를 응축된 수치 표현으로 담아내는 방식이다. 결과적으로 나오는 숫자 배열은 사람이 직접 보면 의미를 알 수 없으나, <strong>그 숫자들 속에 deep neural network가 학습한 표현 구조가 응축되어 있다.</strong> OpenAI 모델은 이 벡터를 해석해 질의 응답, 유사도 매칭, 그 외 다양한 분석을 가능하게 한다.</p>
<p>요약 텍스트를 OpenAI의 <code>ada</code> 모델로 임베딩하는 것이 이 챕터의 표준 절차다. 결과는 고차원 벡터(수천 차원)로, Neo4j 노드 속성으로 저장하거나 별도 벡터 인덱스에 적재된다.</p>
<h2 id="7-차원-축소-시각화의-전제">7. 차원 축소: 시각화의 전제</h2>
<p>임베딩의 차원이 수천에 이르면 분석에는 유용하나 <strong>시각화에는 부적합</strong>하다. 인간의 시지각은 2~3차원에 한정된다. 따라서 시각화 단계 진입 전에 차원 축소가 필수가 된다.</p>
<p>PCA(Principal Component Analysis)는 선형 변환 기법으로, 원본 데이터를 새로운 좌표계로 투영한다. 수천 차원 벡터를 2차원으로 축소해 차트에 표시 가능한 형태로 만든다. PCA는 분산을 최대로 보존하는 축을 차례로 선택하는 방식이라 차원 축소 후에도 데이터의 구조적 차이가 어느 정도 보존된다.</p>
<p>다만 PCA는 선형 기법이라는 한계가 있다. 비선형적 의미 구조는 PCA로 평탄화될 수 있다. 이 챕터는 PCA를 표준 도구로 제시하지만, 실무에서는 t-SNE·UMAP 같은 비선형 차원 축소가 임베딩 시각화에 더 적합한 경우가 많다.</p>
<h2 id="8-k-means-클러스터링">8. K-means 클러스터링</h2>
<p>차원 축소가 시각화를 위한 변환이라면, K-means 클러스터링은 <strong>임베딩 공간 내에서 그룹을 발견하는 분석</strong>이다. 임베딩이 고차원 벡터이므로, 그 벡터들을 클러스터링하면 데이터 내부의 본질적 그룹이나 커뮤니티가 드러난다.</p>
<p>K-means는 단순성과 효율성으로 널리 사용되는 클러스터링 알고리즘이다. 사용자가 미리 지정한 K개의 중심점을 기준으로 각 데이터 포인트를 가장 가까운 중심에 할당하고, 중심을 재계산하는 과정을 반복한다. 임베딩에 적용하면 의미적으로 유사한 특허들이 동일 클러스터로 묶인다.</p>
<p>클러스터링의 결과는 단순한 라벨이 아니다. 이 라벨이 그래프의 또 다른 enrichment 속성으로 Neo4j에 다시 기록될 수 있고, Bloom의 rules-based formatting에서 색상 규칙으로 활용될 수 있다. <strong>enrichment의 결과가 다음 enrichment의 입력이 되는 사슬</strong>이 만들어진다.</p>
<h2 id="9-전체-워크플로">9. 전체 워크플로</h2>
<p>지금까지의 단계를 하나의 파이프라인으로 정리하면 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/d2d05b2b-2648-4991-832d-047b11b35e5f/image.png" alt=""></p>
<p>워크플로는 세 단계 성격으로 분리된다. 셋업 단계(1-3)는 일회성, LLM 단계(4·6a)는 외부 API 호출, 분석 단계(6b-6d)는 로컬 계산이다. 운영 측면에서 LLM 단계가 가장 비용·latency 부담이 크므로 캐싱과 배치 처리 설계가 중요하다.</p>
<h2 id="10-nlp-처리-단계의-비교">10. NLP 처리 단계의 비교</h2>
<p>워크플로의 핵심 단계를 입출력 관점에서 정리하면 다음과 같다.</p>
<table>
<thead>
<tr>
<th>단계</th>
<th>입력</th>
<th>출력</th>
<th>도구</th>
<th>목적</th>
</tr>
</thead>
<tbody><tr>
<td>요약</td>
<td>긴 특허 초록</td>
<td>100토큰 이내 요약</td>
<td>ChatGPT</td>
<td>비전문가 접근성 향상</td>
</tr>
<tr>
<td>임베딩</td>
<td>요약 텍스트</td>
<td>고차원 수치 벡터</td>
<td>OpenAI ada</td>
<td>의미·문맥의 수치 표현</td>
</tr>
<tr>
<td>클러스터링</td>
<td>임베딩 벡터</td>
<td>K개 커뮤니티 라벨</td>
<td>K-means</td>
<td>유사 특허 그룹화</td>
</tr>
<tr>
<td>차원 축소</td>
<td>고차원 벡터</td>
<td>2D 좌표</td>
<td>PCA</td>
<td>시각화 가능성 확보</td>
</tr>
<tr>
<td>시각화</td>
<td>2D + 커뮤니티 라벨</td>
<td>산점도</td>
<td>Plotly</td>
<td>패턴·통찰 발견</td>
</tr>
</tbody></table>
<p>이 표의 순서는 임의가 아니다. 각 단계의 출력이 다음 단계의 입력이 되는 데이터 흐름이며, 임베딩이라는 중간 표현을 거쳐야만 클러스터링과 차원 축소가 가능하다. <strong>임베딩이 텍스트와 분석 사이의 다리 역할</strong>을 한다.</p>
<h2 id="11-eda-cypher-패턴">11. EDA Cypher 패턴</h2>
<p>워크플로의 두 번째 단계인 EDA에서 자주 사용되는 Cypher 패턴을 정리한다.</p>
<pre><code class="language-cypher">// 노드 개수 카운트
MATCH (a:Document)
RETURN count(a) AS Number_Documents

// Topic별 문서 수 Top-N
MATCH (a:Topic)&lt;-[:IS_IN]-(b:Document)
RETURN a.name AS Topic_Name, count(b) AS topic_count
ORDER BY topic_count DESC
LIMIT 5

// 조건부 필터링 + 다중 조인 (ML 특허만)
MATCH (c:Topic)&lt;-[:IS_IN]-(a:Document)-[:ASSIGNED_TO]-&gt;(b:Assignee)
WHERE c.name = &#39;Machine Learning&#39;
RETURN id(a) AS ida, a.title, b.name, a.abstract
LIMIT 300</code></pre>
<p>마지막 패턴이 enrichment 워크플로의 진입점이 된다. 특정 Topic에 속한 Document들을 abstract 속성과 함께 추출해 Pandas로 옮기면, 그것이 LLM 입력 dataset이 된다.</p>
<h2 id="12-정리">12. 정리</h2>
<p>ChatGPT 기반 enrichment의 핵심은 그래프 → LLM → 그래프의 사이클이다. 특허 abstract를 추출해 ChatGPT로 비전문가용 요약을 생성하고 그 결과를 다시 그래프 노드 속성으로 적재한다. 요약은 다시 임베딩으로 변환되고, 임베딩은 K-means 클러스터링과 PCA 차원 축소를 거쳐 시각화된다. 텍스트와 임베딩이 함께 보존되는 이중 표현 구조가 분석의 유연성을 만들고, 그래프는 enrichment의 입력이자 종착점으로 기능한다.</p>
<blockquote>
<p><strong>결론</strong>: enrichment는 그래프 데이터에 LLM의 텍스트 지능을 한 번 주입하는 일회성 작업이 아니라, 원본을 보존한 채 강화 결과를 누적하는 사이클이다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Graph Data Science with Python and Neo4j
— Chapter 6: Enriching Neo4j Data with ChatGPT.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[그래프 네트워크 시각화]]></title>
            <link>https://velog.io/@tasker_dev/%EA%B7%B8%EB%9E%98%ED%94%84-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%8B%9C%EA%B0%81%ED%99%94</link>
            <guid>https://velog.io/@tasker_dev/%EA%B7%B8%EB%9E%98%ED%94%84-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%8B%9C%EA%B0%81%ED%99%94</guid>
            <pubDate>Sat, 09 May 2026 06:32:38 GMT</pubDate>
            <description><![CDATA[<h2 id="1-시각화의-위치">1. 시각화의 위치</h2>
<p>데이터 사이언스에서 시각화는 부수 도구가 아니라 <strong>데이터의 복잡한 차원을 이해 가능하고 소화 가능하며 실행 가능한 통찰로 번역하는 핵심 단계</strong>다. 그래프 데이터는 특히 시각화 의존도가 높다. 노드 수가 수백을 넘어가면 표 형태의 raw 데이터로는 패턴을 식별할 수 없으며, 관계의 다층적 구조는 시각적 표현 없이는 stakeholder에게 전달되지 않는다. 이 챕터의 thesis는 다음과 같다. <strong>그래프 시각화의 가치는 &quot;보기 좋은 그림&quot;이 아니라 stakeholder에게 그래프 저장 방식의 가치 자체를 입증하는 도구라는 데 있다.</strong></p>
<p>이전 챕터에서 Neo4j에 데이터를 임포트하는 네 가지 방법을 다뤘다면, 이 챕터는 그렇게 적재된 데이터를 어떻게 보여줄 것인가의 문제를 다룬다. Neo4j Bloom과 Power BI라는 두 도구가 각각 그래프 전용성과 BI 인프라 통합이라는 다른 축에서 답을 제시한다.</p>
<h2 id="2-neo4j-bloom의-정체">2. Neo4j Bloom의 정체</h2>
<p>Neo4j Bloom은 Neo4j에 저장된 데이터를 시각화·탐색하기 위한 그래프 전용 도구다. Bloom이 설계상 가지는 가장 큰 특징은 <strong>양면적 사용자 친화성</strong>이다. 비기술 사용자에게는 point-and-click 인터페이스로 쿼리 작성 부담 없이 데이터를 탐색할 수 있게 하고, 기술 사용자에게는 정교한 Cypher 쿼리를 동일 인터페이스 안에서 실행할 수 있게 한다. 두 사용자층이 같은 도구를 다른 방식으로 사용한다.</p>
<p>라이선스 측면에서 Neo4j Desktop 버전에는 Bloom이 무료로 포함되지만, 조직 환경에서 Neo4j Server를 운영한다면 별도 라이선스 키가 필요하다.</p>
<p>Neo4j Browser와의 차이도 명확히 짚을 가치가 있다. Browser는 라벨을 클릭하면 해당 라벨의 첫 25개 노드를 즉시 매치해 보여주는 반면, Bloom은 <strong>데이터 탐색을 시작하기 위해 초기 Cypher 쿼리를 요구</strong>한다. 이 진입 장벽은 단점이 아니라, 명확한 탐색 의도를 가진 시각화 도구로서의 설계 결정이다.</p>
<h2 id="3-perspective-시각화의-추상화-단위">3. Perspective: 시각화의 추상화 단위</h2>
<p>Bloom의 Perspective는 &quot;어떤 노드·관계·속성을 사용자에게 노출할 것인가&quot;를 정의하는 시각화 구성 단위다. 데이터베이스에 존재하는 모든 정보를 모든 사용자에게 보여줄 필요는 없다. Bloom은 노드·관계·속성을 선택적으로 제외할 수 있는 유연성을 제공한다.</p>
<p>이 기능의 실용적 가치는 머신러닝 운영에서 분명하게 드러난다. 새 ML 모델을 개발하면서 모델 출력을 노드 속성으로 데이터베이스에 기록할 때, 이 내부 속성이 일반 사용자에게 노출되면 혼란을 일으키거나 인지 부하를 가중시킨다. Perspective를 통해 ML 개발 단계의 속성을 사용자 시야에서 제거할 수 있다. <strong>ML 개발과 사용자 경험을 분리하는 추상화 layer</strong>의 역할을 한다.</p>
<h2 id="4-시각화-포맷팅-4대-제어-축">4. 시각화 포맷팅: 4대 제어 축</h2>
<p>Bloom의 시각화는 네 축으로 제어된다.</p>
<table>
<thead>
<tr>
<th>제어 대상</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>노드의 아이콘</td>
<td>노드 유형의 직관적 식별</td>
</tr>
<tr>
<td>노드 원의 크기</td>
<td>속성 값에 따라 동적 변경</td>
</tr>
<tr>
<td>관계의 굵기</td>
<td>관계 속성에 기반한 강도 표현</td>
</tr>
<tr>
<td>관계의 색상</td>
<td>관계 유형 구분</td>
</tr>
</tbody></table>
<p>이 네 축을 정적으로 설정할 수도 있고, <strong>rules-based formatting</strong>으로 데이터에 따라 동적으로 변경되도록 할 수도 있다. 후자가 그래프가 커질수록 더 중요해지는 이유는 다음 절에서 다룬다.</p>
<h2 id="5-rules-based-formatting-주의-설계의-도구">5. Rules-based Formatting: 주의 설계의 도구</h2>
<p>그래프가 커지면 노드 유형의 시각적 구분이 점차 어려워진다. 라벨별 단순 색상 구분으로는 수백 개 노드 중 의미 있는 패턴을 식별할 수 없다. Rules-based formatting은 이 문제를 <strong>사용자의 시선을 가장 의미 있는 부분으로 유도하는 attention design</strong>으로 해결한다.</p>
<p>전형적 적용 예시 두 가지를 보면 메커니즘이 분명해진다.</p>
<ul>
<li><strong>Degree 기반 크기 변경</strong>: 연결 수가 10 이상인 노드의 크기를 4배로 확대. 그래프의 hub node가 시각적으로 즉시 식별된다.</li>
<li><strong>속성 키워드 기반 색상 변경</strong>: Recipe Title에 &#39;mexican&#39;이 포함된 노드를 녹색으로 변경. 의미적 카테고리가 색으로 분리된다.</li>
</ul>
<p>두 예시 모두 <strong>데이터의 어떤 속성이 시각 속성으로 매핑되는지</strong>를 규칙으로 명시한다. 이 매핑이 명시적일수록 시각화는 stakeholder에게 신뢰 가능한 도구가 된다.</p>
<h2 id="6-점진적-탐색-expand와-dismiss-other-nodes">6. 점진적 탐색: Expand와 Dismiss Other Nodes</h2>
<p>대규모 그래프에서 모든 노드를 동시에 표시하는 것은 비현실적이다. Bloom은 <strong>점진적 탐색(incremental exploration)</strong> 모델을 제공한다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/c14b4904-3266-4204-8535-5ebdc24fb7a2/image.png" alt=""></p>
<p>이 사이클은 그래프 탐색의 인지적 부담을 분할한다. 한 번에 하나의 관심 영역만 시야에 두고, 그곳에서 한 단계씩 관계를 따라가며 새로운 연결을 발견한다. <strong>데이터 탐색을 일회성 시각화가 아닌 대화형 프로세스로 전환</strong>하는 것이 Bloom의 핵심 사용 모델이다.</p>
<h2 id="7-scene-actions-cypher-기반-정밀-제어">7. Scene Actions: Cypher 기반 정밀 제어</h2>
<p>수동 Expand의 한계는 분명하다. 한 노드가 수백·수천 개의 다른 노드와 연결된 경우, 단순 확장은 캔버스를 즉시 클러터로 채운다. <strong>Scene Actions는 Cypher를 사용해 캔버스에 표시될 패턴을 정밀하게 제어하는 메커니즘</strong>이다.</p>
<p>전형적 사용 시나리오는 두 노드 사이의 공통점만 표시하는 것이다. 두 레시피 노드가 선택되었을 때, 모든 연결을 펼치는 대신 두 레시피가 공통으로 사용하는 재료만 표시하는 Scene Action을 정의할 수 있다. 결과적으로 시각화는 &quot;두 레시피가 어떻게 연관되는가&quot;라는 질문에 직접 답하는 형태로 좁혀진다.</p>
<p>수동 Expand와 Scene Actions의 차이는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>측면</th>
<th>수동 Expand</th>
<th>Scene Actions</th>
</tr>
</thead>
<tbody><tr>
<td>제어 수단</td>
<td>우클릭 메뉴</td>
<td>Cypher 쿼리</td>
</tr>
<tr>
<td>확장 범위</td>
<td>선택된 관계 전체</td>
<td>쿼리가 정의한 패턴만</td>
</tr>
<tr>
<td>적합 그래프 크기</td>
<td>소규모</td>
<td>대규모</td>
</tr>
<tr>
<td>클러터 위험</td>
<td>높음</td>
<td>낮음</td>
</tr>
<tr>
<td>의미 있는 패턴 추출</td>
<td>어려움</td>
<td>쉬움</td>
</tr>
</tbody></table>
<p>Scene Actions가 대규모 그래프 시각화의 차별점이 되는 이유는, <strong>시각화가 단순한 보기가 아니라 질문에 대한 답으로 기능하게 만들기 때문</strong>이다.</p>
<h2 id="8-bloom-시각화-워크플로-정리">8. Bloom 시각화 워크플로 정리</h2>
<p>Bloom 사용의 표준 워크플로는 다음과 같이 단계화된다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/35df0cb0-8ad2-4141-bcae-8c52e1e9ef54/image.png" alt=""></p>
<p>세 단계의 성격이 다르다는 점이 중요하다. 1<del>3은 <strong>셋업</strong>으로 일회성 작업이며, 4</del>6은 <strong>시각적 정의</strong>로 데이터 도메인 지식이 필요하고, 7~8은 <strong>반복적 탐색</strong>으로 사용자 질문에 따라 매번 다르게 흐른다.</p>
<h2 id="9-power-bi-통합-한계와-가치">9. Power BI 통합: 한계와 가치</h2>
<p>Bloom이 그래프 전용 도구라면, Power BI는 일반 BI 도구로 그래프 시각화에 접근하는 다른 경로다. 두 도구의 통합 가능성과 제약을 짚는다.</p>
<p>핵심 제약은 명확하다. <strong>Power BI는 Neo4j 데이터베이스에 대한 동적 API 연결을 지원하지 않는다.</strong> 따라서 Neo4j 데이터를 Power BI에서 시각화하려면 먼저 CSV로 export한 뒤 Power BI에 import해야 한다. 데이터의 실시간성이 손실된다.</p>
<p>이 한계에도 불구하고 Power BI 통합이 의미를 가지는 이유는 <strong>조직 인프라 측면의 실용주의</strong>다. 많은 조직이 이미 Power BI를 기술 스택의 일부로 운영하고 있다. 새로운 시각화 도구를 도입하는 것보다 기존 도구를 활용하는 편이, 특히 PoC(proof-of-concept) 단계에서는 의사결정이 빠르고 조직 채택 비용이 낮다.</p>
<p>Power BI에서 그래프를 시각화하는 표준 절차는 다음과 같다.</p>
<ol>
<li>Bloom에서 결과를 CSV·Excel로 export</li>
<li>Microsoft Store에서 Power BI Desktop App 설치</li>
<li>관계 CSV 파일 import</li>
<li><code>Get more visuals</code>에서 <code>Drill Down Graph</code> 시각화 패키지 설치</li>
<li>컬럼 매핑: <code>start_node_property</code> → Source Node, <code>end_node_property</code> → Target Node, 관계 값 → Value</li>
<li>bar chart 등 다른 시각화와 결합해 BI식 drill down 분석 수행</li>
</ol>
<h2 id="10-bloom과-power-bi의-비교">10. Bloom과 Power BI의 비교</h2>
<p>두 도구는 같은 그래프 데이터를 다루지만 사용 맥락이 다르다.</p>
<table>
<thead>
<tr>
<th>측면</th>
<th>Neo4j Bloom</th>
<th>Power BI</th>
</tr>
</thead>
<tbody><tr>
<td>연결성</td>
<td>Neo4j와 네이티브 통합</td>
<td>동적 API 미지원, CSV 경유</td>
</tr>
<tr>
<td>그래프 전용성</td>
<td>그래프 시각화 특화</td>
<td>일반 BI, 그래프는 추가 패키지</td>
</tr>
<tr>
<td>사용자층</td>
<td>데이터 분석가, 개발자</td>
<td>비즈니스 사용자</td>
</tr>
<tr>
<td>점진적 탐색</td>
<td>Expand·Scene Actions 강력</td>
<td>제한적</td>
</tr>
<tr>
<td>드릴다운</td>
<td>그래프 내부 확장</td>
<td>다른 차트와 결합한 BI식 드릴다운</td>
</tr>
<tr>
<td>조직 채택 용이성</td>
<td>별도 도구 도입 필요</td>
<td>기존 인프라 활용 가능</td>
</tr>
<tr>
<td>PoC 적합성</td>
<td>그래프 분석 깊이 우선 시</td>
<td>빠른 의사결정·기존 도구 재활용 시</td>
</tr>
</tbody></table>
<p>이 둘을 양자택일로 다루기보다 <strong>분석 단계에 따른 역할 분담</strong>으로 보는 것이 적절하다. Bloom은 그래프 본연의 구조를 깊이 탐색하는 단계에서, Power BI는 결과를 비즈니스 stakeholder에게 통합 대시보드로 전달하는 단계에서 강점을 발휘한다.</p>
<h2 id="11-정리">11. 정리</h2>
<p>Neo4j Bloom의 시각화 워크플로는 Perspective 정의, Saved Cypher 기반 초기 로드, 정적·동적 포맷팅, Expand와 Scene Actions를 통한 점진적 탐색의 다섯 단계로 구성된다. Rules-based formatting은 그래프 규모가 커질수록 사용자의 주의를 의미 있는 곳으로 유도하는 attention design의 도구가 되고, Scene Actions는 단순 확장의 클러터를 피하면서 Cypher의 정밀도를 시각화에 결합한다. Power BI 통합은 그래프 데이터를 기존 BI 인프라에 연결하는 실용주의적 경로이며, 동적 연결 미지원이라는 한계를 조직 채택 용이성으로 보완한다. 두 도구는 양자택일이 아닌 분석 단계별 역할 분담으로 사용된다.</p>
<blockquote>
<p><strong>결론</strong>: 그래프 시각화의 가치는 그림의 미려함이 아니라, 그래프 저장 방식 자체의 가치를 stakeholder에게 입증하는 데 있다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Graph Data Science with Python and Neo4j 
— Chapter 5: Visualizing Graph Networks.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Neo4j 그래프 데이터베이스로 데이터 임포트하기]]></title>
            <link>https://velog.io/@tasker_dev/Neo4j-%EA%B7%B8%EB%9E%98%ED%94%84-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%9E%84%ED%8F%AC%ED%8A%B8%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@tasker_dev/Neo4j-%EA%B7%B8%EB%9E%98%ED%94%84-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%9E%84%ED%8F%AC%ED%8A%B8%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 09 May 2026 06:23:27 GMT</pubDate>
            <description><![CDATA[<h2 id="1-그래프-데이터베이스가-다른-이유">1. 그래프 데이터베이스가 다른 이유</h2>
<p>관계형 데이터베이스가 테이블로 데이터를 저장하는 반면, Neo4j 같은 그래프 데이터베이스는 노드(node), 엣지(edge), 속성(property)으로 구성된 그래프 구조로 데이터를 표현하고 저장한다. 이 차이는 단순한 자료구조 차원의 선택이 아니라, <strong>상호 연결된 데이터를 다룰 때의 성능과 표현력 모두에 본질적 영향을 미치는 설계 결정</strong>이다. 이 챕터의 thesis는 다음과 같다. 그래프 데이터베이스의 가치는 저장·질의·임포트의 세 층위에서 모두 드러나며, 임포트 방법의 선택은 데이터 규모와 운영 권한에 따른 트레이드오프 위에서 이뤄진다.</p>
<p>Neo4j는 Java로 구현된 오픈소스 그래프 데이터베이스이며, 네 가지 아키텍처 특성이 성능·확장성·신뢰성의 기반이 된다.</p>
<h2 id="2-neo4j의-4대-아키텍처-특성">2. Neo4j의 4대 아키텍처 특성</h2>
<h3 id="21-native-graph-storage">2.1 Native Graph Storage</h3>
<p>노드와 관계가 디스크 상에서 물리적으로 인접하게 저장된다. 인접 저장(near-storage)이 가능하면 retrieval 시 디스크 I/O가 최소화되어 조회 속도가 빨라진다.</p>
<h3 id="22-index-free-adjacency">2.2 Index-Free Adjacency</h3>
<p>이것이 Neo4j 성능의 핵심이다. 노드가 연결된 다른 노드를 <strong>물리적 포인터로 직접 가리킨다.</strong> 인덱스 lookup이나 대규모 테이블 스캔이 불필요하므로 multi-hop 질의가 관계형 DB의 JOIN보다 압도적으로 빠르다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/5675372e-c9b2-444a-bcf1-e7484994f24f/image.png" alt=""></p>
<p>같은 데이터에 대한 3-hop 질의에서도 Neo4j는 인덱스 경로를 거치지 않는다. 데이터가 커질수록 두 모델의 격차는 더 벌어진다.</p>
<h3 id="23-scalability">2.3 Scalability</h3>
<p>대규모 데이터셋과 노드 간 복잡한 관계를 모두 처리 가능한 확장성을 갖춘다.</p>
<h3 id="24-acid-transactions">2.4 ACID Transactions</h3>
<p>Neo4j는 트랜잭션의 ACID 속성을 모두 보장한다.</p>
<table>
<thead>
<tr>
<th>속성</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>Atomicity</td>
<td>트랜잭션은 불가분 단일 단위로 처리되어, 한 단계라도 실패하면 어떤 변경도 커밋되지 않는다. 부분 갱신과 데이터 손상을 방지한다</td>
</tr>
<tr>
<td>Consistency</td>
<td>한 상태에서 다른 상태로 전이될 때 일관성이 유지된다</td>
</tr>
<tr>
<td>Isolation</td>
<td>동시 트랜잭션이 마치 순차 처리된 것처럼 보이게 한다. 한 트랜잭션이 다른 트랜잭션의 데이터를 침범하지 않는다</td>
</tr>
<tr>
<td>Durability</td>
<td>커밋된 트랜잭션은 서버 장애나 정전이 발생해도 보존된다</td>
</tr>
</tbody></table>
<h2 id="3-neo4j의-분석·스키마·질의-우위">3. Neo4j의 분석·스키마·질의 우위</h2>
<h3 id="31-in-database-analytics">3.1 In-Database Analytics</h3>
<p>Neo4j는 다수의 그래프 알고리즘을 기본 제공한다. <strong>데이터를 별도 분석 환경(Python·SAS 등)으로 옮기지 않고 데이터베이스 내에서 직접 알고리즘을 실행할 수 있다.</strong> 운영 측면에서 세 가지 이득이 발생한다.</p>
<ul>
<li>아키텍처 복잡성 감소</li>
<li>데이터 중복(redundancy) 제거</li>
<li>서버 자원·에너지 비용 절감</li>
</ul>
<h3 id="32-flexible-schema">3.2 Flexible Schema</h3>
<p>관계형 DB의 엄격한 스키마와 달리, Neo4j는 데이터 모델이 시간에 따라 변형·확장될 수 있다. 광범위한 사전 설계와 재설계 없이 복잡한 데이터 모델을 점진적으로 구축할 수 있어 개발 사이클이 단축된다.</p>
<h3 id="33-cypher의-우위">3.3 Cypher의 우위</h3>
<p>Cypher는 multi-hop 패턴 매칭을 2~3줄의 코드로 표현 가능하다. 같은 작업을 관계형 DB로 작성하면 다중 서브쿼리와 테이블 JOIN이 얽힌 수십 줄의 SQL이 되며, 실행 속도도 Cypher보다 느리다.</p>
<h2 id="4-그래프-데이터베이스의-어휘">4. 그래프 데이터베이스의 어휘</h2>
<p>이후 임포트 논의의 토대가 되는 핵심 용어를 정리한다.</p>
<table>
<thead>
<tr>
<th>용어</th>
<th>정의</th>
</tr>
</thead>
<tbody><tr>
<td>Node</td>
<td>그래프의 &quot;점&quot;. 관계형 DB의 행(row)에 대응</td>
</tr>
<tr>
<td>Label</td>
<td>노드의 카테고리 (예: <code>Movie</code>, <code>Actor</code>). 관계형 DB의 컬럼과 유사한 분류 단위</td>
</tr>
<tr>
<td>Relationship</td>
<td>노드 간 방향성 있는 연결. 항상 시작 노드와 끝 노드를 가짐</td>
</tr>
<tr>
<td>Property</td>
<td>노드에 저장된 추가 데이터 요소</td>
</tr>
<tr>
<td>Relationship Property</td>
<td>관계 자체에 저장된 데이터 요소</td>
</tr>
<tr>
<td>Graph Traversal</td>
<td>관계를 따라 노드 간을 이동하는 과정. 그래프 DB 질의의 근본 연산</td>
</tr>
<tr>
<td>Hop</td>
<td>한 노드에서 관계를 거쳐 다른 노드로의 한 번의 전이</td>
</tr>
<tr>
<td>Degrees of Separation</td>
<td>hop 수와 동의어로, 두 노드 사이의 최소 연결 수</td>
</tr>
</tbody></table>
<p>Relationship이 항상 방향을 가진다는 점은 schema 설계에서 자주 간과된다. 양방향 의미를 가지는 관계라도 저장 시점에는 한 방향을 선택해야 하며, 질의에서는 방향 무관 패턴(<code>-[r]-</code>)으로 우회할 수 있다.</p>
<h2 id="5-cypher의-선언적-모델">5. Cypher의 선언적 모델</h2>
<p>Cypher는 선언적 언어다. <strong>&quot;무엇을 찾을지&quot;</strong>를 기술하면 되고, <strong>&quot;어떻게 찾을지&quot;</strong>는 명시할 필요가 없다. 이 점이 SQL과의 가장 큰 공통점인 동시에, 그래프 패턴 매칭 문법을 통해 SQL과 구별된다.</p>
<p>전형적 Cypher 질의는 다음 5요소로 구성된다.</p>
<pre><code class="language-cypher">MATCH (i1:Movie)-[r:DIRECTED_BY]-&gt;(i2:Director)
WHERE i2.name = &quot;Christopher Nolan&quot;
RETURN i1.title, i1.year</code></pre>
<ul>
<li><code>MATCH</code>: 찾을 패턴 선언</li>
<li>노드 변수(<code>i1</code>, <code>i2</code>)와 라벨(<code>Movie</code>, <code>Director</code>)</li>
<li>관계 변수(<code>r</code>)와 관계 유형(<code>DIRECTED_BY</code>)</li>
<li><code>WHERE</code>: 조건 명시</li>
<li><code>RETURN</code>: 반환 요소 지정</li>
</ul>
<p>같은 작업을 관계형 SQL로 작성하면 영화 테이블·감독 테이블·연결 테이블에 대한 다중 JOIN이 필요하다. Cypher는 패턴 자체가 그래프 구조를 시각적으로 닮아 있어 가독성도 우수하다.</p>
<h2 id="6-데이터-임포트의-위치">6. 데이터 임포트의 위치</h2>
<p>분석 파이프라인에서 데이터 임포트는 가장 앞단의 토대다. 데이터 소스를 분석 가능한 단일 플랫폼으로 통합하는 단계이며, 이 단계의 품질이 후속 데이터 핸들링·변환·패턴 탐지의 결과를 결정한다. 임포트가 부실하면 이후 모든 단계에서 손실이 누적된다.</p>
<h2 id="7-etl과-csv가-표준인-이유">7. ETL과 CSV가 표준인 이유</h2>
<p>Extract-Transform-Load는 다양한 소스에서 데이터를 추출하고, 구조화된 형식으로 변환한 뒤, 대상 데이터 저장소에 적재하는 일반 프로세스다. Neo4j 임포트의 입력 형식으로는 CSV가 표준이다.</p>
<p>CSV가 권장되는 이유는 세 가지다.</p>
<ul>
<li>콤마로 값을 구분하는 평문 텍스트로, 다양한 처리 도구와 호환된다</li>
<li>Excel 같은 스프레드시트의 행 수 제한이 없어 대규모 데이터셋을 다룰 수 있다</li>
<li>다른 저장 형식 대비 처리 시간과 메모리 사용 측면에서 효율적이다</li>
</ul>
<p>베스트 프랙티스는 <strong>각 노드 종류와 각 관계 종류를 별도 CSV 파일로 분리</strong>하는 것이다. 다만 강제는 아니며, 통합 CSV로 임포트한 뒤 Cypher로 그래프 스키마를 변형하는 방식도 가능하다.</p>
<h2 id="8-네-가지-임포트-방법">8. 네 가지 임포트 방법</h2>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/b2e58a7f-1ca6-4a56-8010-9b2cdc6c8b0a/image.png" alt=""></p>
<h3 id="81-cypher-create--직관적이나-소규모용">8.1 Cypher CREATE — 직관적이나 소규모용</h3>
<p><code>CREATE</code> 절을 직접 작성해 노드와 관계를 삽입하는 가장 단순한 방식이다. 직관적이고 빠르게 작성 가능하지만, 대용량 파일에는 적합하지 않다. 학습·테스트·소규모 시연 용도다.</p>
<h3 id="82-neo4j-aura-data-importer--gui-편의성">8.2 Neo4j Aura Data Importer — GUI 편의성</h3>
<p>Aura Data Importer는 Neo4j Aura 환경에서 가장 편리하고 학습 곡선이 낮은 임포트 방법이다. GUI 기반으로 CSV 컬럼을 노드 속성·관계에 매핑한다. 무료 버전은 노드 200,000개·관계 400,000개로 제한된다.</p>
<p>관계 매핑 시 등장하는 &#39;From&#39;·&#39;To&#39; 개념은 &#39;Source&#39;·&#39;Target&#39;과 동일하다. Source(=From)는 관계의 시작점, Target(=To)은 끝점이다. 임포터마다 라벨 표기는 다르지만 의미는 동일하다.</p>
<h3 id="83-admin-import--최고-성능">8.3 Admin Import — 최고 성능</h3>
<p>Admin Import는 <strong>트랜잭션 레이어를 우회하는</strong> 고성능 임포트 도구다. 트랜잭션 로깅 오버헤드가 제거되어 Neo4j에 데이터를 적재하는 가장 빠른 방법이 된다. 책의 실증 사례에 따르면, Spark로 10억 노드·관계급 데이터베이스를 적재할 때 약 48시간이 소요되던 작업이 Admin Import로는 1시간 미만으로 단축된다.</p>
<p>대신 세 가지 제약이 있다.</p>
<ul>
<li><strong>기존 데이터 갱신 불가</strong>: 신규 데이터 적재 전용. 기존 데이터의 수정·갱신은 지원하지 않는다</li>
<li><strong>설정 복잡도 증가</strong>: CSV 헤더 표준 준수, 파일 위치 이동, 스크립트 실행 등 다른 방법보다 절차가 많다</li>
<li><strong>관리자 권한 필요</strong>: 명령줄 터미널에서 <code>neo4j-admin</code> 스크립트를 실행하려면 administrative privilege가 있어야 한다</li>
</ul>
<p>전형적 6단계 실행 순서는 다음과 같다.</p>
<ol>
<li>CSV 헤더를 Neo4j 표준 형식에 맞춰 변경</li>
<li>CSV 파일을 Neo4j Import 폴더로 이동</li>
<li>터미널에서 <code>neo4j-admin</code> 스크립트 실행</li>
<li>(선택) 새 DB 폴더 권한 업데이트</li>
<li>Neo4j Browser에서 <code>CREATE DATABASE &lt;databaseName&gt;</code> 실행</li>
<li>(선택) Neo4j 서비스·서버 재시작</li>
</ol>
<h3 id="84-csv--python-load-csv--pandas--유연성-우선">8.4 CSV + Python (LOAD CSV / Pandas) — 유연성 우선</h3>
<p>가장 흔히 쓰이는 방식이다. Cypher의 <code>LOAD CSV</code> 명령으로 CSV를 직접 읽어 들이거나, Python의 Pandas DataFrame을 통해 임포트한다.</p>
<p><strong>LOAD CSV의 핵심 원칙은 배치 임포트다.</strong> CSV를 작은 청크로 분할해 메모리 제약을 회피한다. 단일 트랜잭션으로 수백만 행을 시도하면 메모리 부족으로 실패하기 쉽다.</p>
<p>Pandas 임포트는 유연성과 속도의 트레이드오프가 가장 분명하다. DataFrame을 거쳐 Neo4j로 적재하면 사전 데이터 변환·정제를 자유롭게 수행할 수 있으나, 모든 트랜잭션이 트랜잭션 로그에 기록되므로 Admin Import 대비 10배 이상 느리다. 다만 관리자 권한이 필요 없고, ad-hoc 분석이나 소규모 데이터셋에는 추가 협조 없이 즉시 사용 가능하다.</p>
<p>세션 관리 측면에서, Neo4j Python 드라이버를 사용할 때는 작업 종료 후 반드시 세션을 닫아야 한다. 세션 누수는 커넥션 풀 고갈로 이어진다.</p>
<h2 id="9-cypher-임포트의-핵심-절">9. Cypher 임포트의 핵심 절</h2>
<p>CSV 임포트 시 자주 사용되는 세 절을 정리한다.</p>
<table>
<thead>
<tr>
<th>절</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td><code>UNWIND</code></td>
<td>리스트의 각 요소를 별도 행으로 펼친다. dictionary 리스트를 받아 각 row를 순회</td>
</tr>
<tr>
<td><code>MERGE</code></td>
<td>라벨을 가진 노드의 존재를 보장. 없으면 생성, 있으면 기존 노드와 매칭</td>
</tr>
<tr>
<td><code>ON CREATE SET</code></td>
<td>노드가 새로 생성된 경우에만 추가 속성을 설정</td>
</tr>
</tbody></table>
<p><code>MERGE</code>와 <code>ON CREATE SET</code>의 조합은 멱등성(idempotency)을 보장하는 임포트 패턴이다. 같은 CSV를 두 번 실행해도 중복 노드가 생기지 않으며, 신규 노드에만 초기 속성이 부여된다.</p>
<h2 id="10-네-가지-방법-비교">10. 네 가지 방법 비교</h2>
<table>
<thead>
<tr>
<th>방법</th>
<th>속도</th>
<th>유연성</th>
<th>관리자 권한</th>
<th>적합한 사용 사례</th>
</tr>
</thead>
<tbody><tr>
<td>Cypher CREATE</td>
<td>보통</td>
<td>낮음</td>
<td>불필요</td>
<td>소규모, 학습·테스트</td>
</tr>
<tr>
<td>Aura Data Importer</td>
<td>보통</td>
<td>보통</td>
<td>불필요</td>
<td>GUI 선호, Aura 환경, 200K 노드 미만</td>
</tr>
<tr>
<td>Admin Import</td>
<td>최고 (10배+)</td>
<td>낮음</td>
<td>필요</td>
<td>대규모 신규 적재 (10억+ 노드)</td>
</tr>
<tr>
<td>CSV + Python (LOAD CSV)</td>
<td>보통</td>
<td>높음</td>
<td>불필요</td>
<td>일반적 사용, 배치 처리</td>
</tr>
<tr>
<td>Pandas</td>
<td>느림</td>
<td>최고</td>
<td>불필요</td>
<td>ad-hoc 분석, 소규모, 사전 변환 필요</td>
</tr>
</tbody></table>
<p>이 표의 핵심 패턴은 <strong>속도와 유연성이 반비례</strong>한다는 것이다. Admin Import는 빠른 만큼 갱신 불가·관리자 권한 필요라는 제약이 따르고, Pandas는 자유로운 만큼 트랜잭션 로깅 비용을 그대로 떠안는다. 중간 지점이 필요하면 LOAD CSV + 배치 처리가 균형점이 된다.</p>
<h2 id="11-정리">11. 정리</h2>
<p>Neo4j의 Native Graph Storage·Index-Free Adjacency·Scalability·ACID는 그래프 데이터의 저장과 질의에서 관계형 모델 대비 본질적 차이를 만든다. Cypher는 multi-hop 패턴 매칭을 2~3줄로 표현하는 선언적 언어로 그 차이를 사용자에게 노출한다. 데이터 임포트는 이 모델로 들어가는 입구이며, Cypher CREATE·Aura Importer·Admin Import·CSV+Python의 네 방법은 속도·유연성·권한 요구의 트레이드오프 위에서 사용 사례별로 선택된다. 정적 행 단위 데이터를 상호 연결된 그래프 데이터로 변환하는 이 과정이, 이후 분석과 머신러닝 단계의 능력을 결정한다.</p>
<blockquote>
<p><strong>결론</strong>: 임포트 도구의 선택은 데이터 규모와 운영 권한 사이의 트레이드오프이며, Admin Import의 속도와 Pandas의 유연성은 양 끝점일 뿐 단일 정답은 존재하지 않는다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Graph Data Science with Python and Neo4j
— Chapter 3: Importing Data into the Neo4j Graph Database.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[오픈 LLM과 도메인 온톨로지를 활용한 NED]]></title>
            <link>https://velog.io/@tasker_dev/%EC%98%A4%ED%94%88-LLM%EA%B3%BC-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%98%A8%ED%86%A8%EB%A1%9C%EC%A7%80%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-NED</link>
            <guid>https://velog.io/@tasker_dev/%EC%98%A4%ED%94%88-LLM%EA%B3%BC-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%98%A8%ED%86%A8%EB%A1%9C%EC%A7%80%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-NED</guid>
            <pubDate>Sat, 09 May 2026 06:12:07 GMT</pubDate>
            <description><![CDATA[<h2 id="1-7장의-한계에서-출발하는-8장">1. 7장의 한계에서 출발하는 8장</h2>
<p>이전 편에서는 NED 시스템의 3단계 아키텍처(candidate selection → ranking → ontology integration)와 EntityMention–MedicalEntity 분리 schema를 다뤘다. 그 논의의 reference 도구가 scispaCy였다. scispaCy는 생의학 도메인에서 검증된 NED 도구이지만, 7장에서 부분적으로만 드러난 한계가 8장의 출발점이 된다. 이 챕터의 thesis는 다음과 같다. <strong>NED는 더 이상 단일 도메인 특화 도구로 닫혀 있을 필요가 없다. 풍부한 ontology가 존재하는 도메인이라면, 범용 LLM과 graph algorithm의 결합이 도메인 특화 도구의 4대 한계를 우회한다.</strong></p>
<h2 id="2-scispacy의-4대-구조적-한계">2. scispaCy의 4대 구조적 한계</h2>
<p>8장은 전통 NED 도구의 한계를 네 축으로 명시한다.</p>
<table>
<thead>
<tr>
<th>한계 축</th>
<th>내용</th>
</tr>
</thead>
<tbody><tr>
<td>도메인 고정</td>
<td>생의학 도메인 전용으로 설계되어 다른 도메인에 이식 불가</td>
</tr>
<tr>
<td>KB 확장성 부재</td>
<td>reference knowledge base에 신규 entity·term을 통합하거나 갱신하기 어려움</td>
</tr>
<tr>
<td>KB 정보 미활용</td>
<td>KB가 보유한 풍부한 정보를 disambiguation에 충분히 반영하지 못함</td>
</tr>
<tr>
<td>관계 미활용</td>
<td>entity 사이의 기존 relationship과 path를 disambiguation에 활용하지 않음</td>
</tr>
</tbody></table>
<p>이 중 가장 치명적인 것은 네 번째 한계다. NED의 본질이 &quot;문맥에 따른 의미 결정&quot;임에도, 전통 도구는 entity 간 그래프 관계라는 가장 강력한 문맥 신호를 사용하지 않는다. 그 결과 문맥 단어가 부족한 문장에서 즉각 실패한다. 책의 예시는 명료하다. 같은 &quot;Zika&quot;라는 mention이라도 주변 단어가 풍부한 문장에서는 <code>C0276289 Zika Virus Infection</code>으로 정확히 매핑되지만, 주변 단어가 빈약한 문장에서는 어떤 target entity도 검출하지 못한다.</p>
<h2 id="3-패러다임-전환-도메인-특화-모델에서-범용-llm--풍부한-ontology로">3. 패러다임 전환: 도메인 특화 모델에서 범용 LLM + 풍부한 ontology로</h2>
<p>8장의 접근은 단순한 도구 교체가 아니라 패러다임 전환이다. 도메인 특화 NLP 모델을 더 정교하게 만드는 방향이 아니라, 일반 목적 LLM(Llama 3.1)을 도메인 ontology(SNOMED)로 grounding하는 방향이다.</p>
<p>이 전환에는 두 가지 실용적 함의가 있다. 첫째, 풍부한 ontology가 존재하는 모든 도메인에 동일 framework이 적용 가능하다. 둘째, ontology가 지속적으로 업데이트되면 NED 시스템의 reference knowledge도 자동으로 갱신된다. 모델 재학습이 아닌 ontology 업데이트가 갱신 단위가 된다.</p>
<p>로컬 배포 측면에서 Ollama가 선택된다. Ollama는 LLM을 로컬 머신에서 직접 실행하는 오픈소스 도구로, 데이터 통제권 확보·외부 의존성 감소·latency 감소를 동시에 달성한다. OpenAI Chat Completions API와 호환되어 코드 베이스를 크게 바꾸지 않고 통합 가능하다는 점도 실무적 이점이다.</p>
<h2 id="4-end-to-end-파이프라인-구조">4. End-to-End 파이프라인 구조</h2>
<p>전체 파이프라인은 NER → Candidate Selection(CS) → Candidate Disambiguation(CD)의 세 phase로 구성되며, 각 phase에 사용되는 도구가 다르다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/9431857d-2d11-48fe-88d2-3ac7ff69ae20/image.png" alt=""></p>
<p>각 단계에 다른 도구를 배치하는 설계 결정이 이 framework의 핵심이다. <strong>모든 단계에 LLM을 쓰는 것도, 모든 단계에 graph algorithm을 쓰는 것도 아니다.</strong> 각 phase가 요구하는 신호의 성격에 따라 도구가 선택된다.</p>
<table>
<thead>
<tr>
<th>Phase</th>
<th>도구</th>
<th>선택 이유</th>
</tr>
</thead>
<tbody><tr>
<td>NER</td>
<td>LLM (Llama 3.1)</td>
<td>자연어 문맥에서 mention과 카테고리를 파악하는 작업은 LLM의 강점 영역</td>
</tr>
<tr>
<td>Candidate Selection</td>
<td>Neo4j full-text search (LLM 미사용)</td>
<td>ontology 전체를 prompt에 로드 불가, ontology 기반 검색이 LLM 내부 지식보다 정확</td>
</tr>
<tr>
<td>Candidate Disambiguation</td>
<td>LLM + GDS shortest path</td>
<td>그래프 관계 정보를 LLM이 처리 가능한 자연어로 변환해 결합</td>
</tr>
</tbody></table>
<h2 id="5-ner-단계-snomed-카테고리-기반-추출">5. NER 단계: SNOMED 카테고리 기반 추출</h2>
<p>NER의 목표는 비정형 텍스트의 named entity를 사전 정의된 카테고리(diseases, organisms, procedures 등)로 분류하는 것이다. 8장에서 이 카테고리 정의 자체를 SNOMED에서 끌어온다. SNOMED의 first-level node가 카테고리를 정의하며, 그 정보는 ontology의 하위 노드 전체로 propagate된다.</p>
<p>LLM의 한 가지 약점이 이 단계에서 드러난다. LLM은 문장 내 mention의 시작·끝 character offset을 정확히 짚지 못한다. 따라서 mention의 텍스트 자체는 LLM이 추출하되, start·end offset은 후처리(post-processing)로 별도 계산한다. LLM의 강점은 활용하되 약점은 결정론적 후처리로 보완한다는 원칙의 구체적 적용이다.</p>
<h2 id="6-candidate-selection-llm을-의도적으로-배제하는-단계">6. Candidate Selection: LLM을 의도적으로 배제하는 단계</h2>
<p>Candidate Selection 단계에서 LLM을 사용하지 않는 결정은 의도적이며, 두 가지 명확한 이유가 있다.</p>
<p>첫째, candidate를 LLM의 내부 지식이 아닌 <strong>도메인 ontology에서 직접 retrieve</strong>해야 한다. LLM 내부 지식은 환각(hallucination) 가능성이 있고 ontology 버전과 동기화되지 않는다. 둘째, ontology의 크기가 prompt 한도를 초과한다. SNOMED는 450,000개 이상의 concept을 가진다.</p>
<p>해결책은 Neo4j의 full-text search다. mention 문자열에 가까운 ontology 노드를 효율적으로 retrieve한다. 다만 full-text search는 표면형 매칭에 의존하므로, 동의어·축약·오타에서 누락이 발생할 수 있다. 책은 이 한계의 보완책으로 vector-based search를 병행해 텍스트 매칭으로 잡히지 않는 후보를 추가 retrieve하는 확장을 제시한다.</p>
<h2 id="7-candidate-disambiguation-3단계-멀티스텝-접근의-핵심">7. Candidate Disambiguation: 3단계 멀티스텝 접근의 핵심</h2>
<p>8장의 진짜 혁신은 CD 단계에 있다. 단순히 LLM에 후보 목록과 문장을 넘기고 선택을 요청하는 방식은 그래프 관계 정보를 활용할 수 없다. 책은 이를 3단계로 분해한다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/33ef390d-4801-4cd7-958b-0c0231e7fec8/image.png" alt=""></p>
<h3 id="step-1-shortest-path-detection">Step 1: Shortest Path Detection</h3>
<p>같은 문장에 등장한 다른 mention의 candidate들과 target mention의 candidate들 사이에서 <strong>최단 경로</strong>를 찾는다. 이 경로가 두 entity 사이의 잠재적 관계를 드러낸다. 책의 예시에 따르면 &quot;Zika&quot;와 &quot;microcephaly&quot;가 같은 문장에 등장할 때, &quot;microcephaly&quot;의 존재가 &quot;Zika&quot;를 일반 바이러스가 아닌 <code>Congenital Zika virus infection</code>으로 우선시하게 한다. 이 우선순위의 근거가 ontology 내 path다.</p>
<p>쿼리는 세 단계로 구성된다.</p>
<ol>
<li><strong>Degree calculation</strong>: <code>gds.degree.stream</code>으로 각 노드의 degree를 계산해 hub node를 식별한다. 이후 hub node를 경로 탐색에서 제외하기 위함이다.</li>
<li><strong>Shortest-path search</strong>: 두 entity ID 사이의 모든 최단 경로를 탐색하되, hop 수를 1~2로 제한하고 hub node를 통과하는 경로를 필터링한다.</li>
<li><strong>Path transformation</strong>: 식별된 경로를 unwind해 노드와 관계를 시퀀스로 수집한다.</li>
</ol>
<h3 id="step-2-path-to-text-translation">Step 2: Path-to-Text Translation</h3>
<p>그래프 경로를 LLM에 그대로 넘기는 것은 비효율적이다. LLM은 자연어 처리에 특화되어 있으므로, 각 graph path를 <strong>자연어 문장으로 번역</strong>한다. 예를 들어 <code>Zika virus —[CAUSES]→ Congenital infection —[ASSOCIATED_WITH]→ Microcephaly</code> 경로는 &quot;Zika virus가 congenital infection을 일으키고, 이는 microcephaly와 연관된다&quot;는 형태로 변환된다.</p>
<p>이 변환의 효과는 LLM의 강점 영역으로 정보를 옮기는 데 있다. LLM은 노드-엣지 표현보다 자연어 서술로 제공된 관계 정보를 더 잘 해석한다.</p>
<h3 id="step-3-textual-path-summarization">Step 3: Textual Path Summarization</h3>
<p>경로 수가 많아지면 모든 경로 문장을 LLM에 그대로 제공하는 것이 부담이다. 따라서 다수의 path 문장을 단일한 <strong>synthetic explanation</strong>으로 요약한다. 이 요약 단계의 목적은 두 가지다.</p>
<ul>
<li>토큰 수 절감을 통한 비용·latency 감소</li>
<li>LLM의 cognitive load 감소를 통한 disambiguation 정확도 개선</li>
</ul>
<p>요약은 세부 정보를 버리는 것이 아니라, 관계의 본질만 남기고 표현을 압축하는 작업이다. 결과적으로 모델이 핵심 관계에 집중하게 만든다.</p>
<h2 id="8-7장과-8장의-비교">8. 7장과 8장의 비교</h2>
<p>7장의 scispaCy 기반 접근과 8장의 LLM + ontology + GDS 접근을 동일한 NED 작업의 두 구현체로 비교하면 차이가 분명하다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>7장 (scispaCy)</th>
<th>8장 (Open LLM + Ontology + GDS)</th>
</tr>
</thead>
<tbody><tr>
<td>핵심 도구</td>
<td>scispaCy + UMLS</td>
<td>Llama 3.1 8B + SNOMED + Neo4j GDS</td>
</tr>
<tr>
<td>적용 도메인</td>
<td>생의학 도메인 한정</td>
<td>cross-domain 가능</td>
</tr>
<tr>
<td>KB 확장성</td>
<td>제한적 (모델 재학습 필요)</td>
<td>ontology 업데이트로 즉시 반영</td>
</tr>
<tr>
<td>관계 활용</td>
<td>미활용</td>
<td>shortest path 적극 활용</td>
</tr>
<tr>
<td>문맥 부재 시</td>
<td>실패</td>
<td>co-occurrence + path로 보완</td>
</tr>
<tr>
<td>LLM 사용 단계</td>
<td>없음</td>
<td>NER · Path Translation · Summarization · Disambiguation</td>
</tr>
<tr>
<td>LLM 미사용 단계</td>
<td>전체</td>
<td>Candidate Selection (full-text search)</td>
</tr>
</tbody></table>
<p>7장이 disambiguation을 mention과 KB의 매칭 문제로 다뤘다면, 8장은 동일 문장 내 다른 mention과의 그래프 관계를 disambiguation 신호로 끌어들인다. 정보 활용의 폭이 한 차원 넓어진다.</p>
<h2 id="9-한계와-트레이드오프">9. 한계와 트레이드오프</h2>
<p><strong>Ontology 부재 도메인의 적용 불가</strong>: 이 framework의 핵심 전제는 &quot;풍부한 ontology의 존재&quot;다. SNOMED·UMLS 수준의 정비된 ontology가 없는 도메인(신생 산업, multilingual archive, 도메인 융합 영역)에서는 path detection 자체가 무력하다. 8장의 접근은 도메인 특화 도구의 한계를 ontology의 한계로 옮긴 것이지, 한계를 제거한 것은 아니다.</p>
<p><strong>Hub node 필터링의 정의 문제</strong>: degree 기반 hub node 식별은 임계값 결정이 도메인·ontology 버전마다 달라진다. 정적 임계값은 일부 의미 있는 일반 노드까지 배제하거나, 반대로 의미 없는 noise 노드를 통과시킨다. percentile 기반 또는 도메인 expert 기반의 동적 정의가 더 안정적이다.</p>
<p><strong>Path-to-text translation의 정보 손실</strong>: 그래프 관계의 다중성·방향성·정량 속성(가중치, 신뢰도 등)을 자연어로 변환할 때 일부 정보가 평탄화된다. 특히 음의 관계(예: <code>NOT_ASSOCIATED_WITH</code>), 시간적 순서, 확률적 강도 등은 자연어 변환 과정에서 약화되기 쉽다.</p>
<p><strong>LLM 비결정성과 재현성</strong>: NER·path translation·summarization·disambiguation 네 단계에서 LLM이 사용된다. 각 단계의 출력이 비결정적이라면 동일 입력에 대해서도 결과가 흔들릴 수 있다. 단계별 temperature 통제와 seed 고정이 운영 단계에서 필수다.</p>
<p><strong>3단계 분해의 비용 누적</strong>: 각 단계가 독립 LLM 호출이므로 mention 하나의 disambiguation에 다수의 호출이 발생한다. 문서 단위로 mention 수가 늘면 전체 처리 시간과 비용이 비선형적으로 증가한다. batching·caching 전략이 함께 설계되어야 한다.</p>
<h2 id="10-실무-적용-시-고려사항">10. 실무 적용 시 고려사항</h2>
<p>이 framework를 실서비스에 적용할 때 고려할 운영 측면을 짚는다.</p>
<p>LLM 단계와 결정론적 단계의 분리 운영이 첫 번째다. NER의 mention offset 후처리, CS의 full-text search는 결정론적이며 캐시 가능하다. 반면 path summarization과 final disambiguation은 비결정성을 수반한다. 두 종류의 단계를 별도 서비스로 분리하면 retry·재현·디버깅이 용이해진다.</p>
<p>Ontology 버전 동기화도 별도 관리해야 한다. SNOMED는 정기 릴리스가 있고, 노드 ID와 관계가 추가·삭제·재정의된다. Candidate Selection의 full-text index와 KG의 노드·관계가 동일 버전을 참조하도록 강제하는 메타데이터 layer를 두는 것이 안전하다.</p>
<p>마지막으로, 로컬 LLM과 외부 LLM의 역할 분담이다. Ollama 기반 로컬 배포는 데이터 민감도가 높은 도메인(헬스케어, 법률)에서 핵심 가치다. 그러나 8B 모델의 능력에는 한계가 있고, 일부 단계(예: 복잡한 path summarization)에서는 더 큰 외부 모델이 더 나은 결과를 낼 수 있다. PII가 포함된 NER·CS는 로컬에서, sanitized된 path 텍스트의 summarization은 외부 모델에서 처리하는 하이브리드도 가능하다.</p>
<h2 id="11-정리">11. 정리</h2>
<p>8장은 7장에서 드러난 전통 NED 도구의 4대 한계 — 도메인 고정, KB 확장 불가, KB 정보 미활용, 관계 미활용 — 을 범용 LLM과 풍부한 ontology의 결합으로 재구성한다. 핵심은 모든 단계에 LLM을 쓰지 않고 phase별로 적합한 도구(LLM, full-text search, GDS)를 배치한다는 설계 원칙, 그리고 CD 단계에서 그래프 관계를 자연어로 번역해 LLM이 처리 가능한 형태로 옮기는 path-to-text translation·summarization 메커니즘이다. 이 framework는 풍부한 ontology가 존재하는 모든 도메인에 일반화될 수 있는 NED의 새로운 baseline이 된다.</p>
<blockquote>
<p><strong>결론</strong>: 도메인 특화 NLP 모델을 더 정교하게 만드는 대신, 범용 LLM에 도메인 ontology의 graph 구조를 자연어로 번역해 주입하는 것이 cross-domain NED의 일반 해법이다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Knowledge Graphs and LLMs in Action. Manning, 2024 — Chapter 8: NED with Open LLMs and Domain Ontologies.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[명명 엔티티 중의성 해소]]></title>
            <link>https://velog.io/@tasker_dev/%EB%AA%85%EB%AA%85-%EC%97%94%ED%8B%B0%ED%8B%B0-%EC%A4%91%EC%9D%98%EC%84%B1-%ED%95%B4%EC%86%8C</link>
            <guid>https://velog.io/@tasker_dev/%EB%AA%85%EB%AA%85-%EC%97%94%ED%8B%B0%ED%8B%B0-%EC%A4%91%EC%9D%98%EC%84%B1-%ED%95%B4%EC%86%8C</guid>
            <pubDate>Sat, 09 May 2026 05:54:23 GMT</pubDate>
            <description><![CDATA[<h2 id="1-ner만으로는-부족한-이유">1. NER만으로는 부족한 이유</h2>
<p>이전 편에서 비정형 텍스트로부터 NER·RE 파이프라인을 통해 entity와 relation을 추출하는 과정을 다뤘다. 그러나 NER의 출력은 &quot;어떤 문자열이 entity인가&quot;까지만 답할 뿐, &quot;그 문자열이 정확히 어느 entity를 가리키는가&quot;는 답하지 못한다. 이 챕터의 thesis는 다음과 같다. <strong>NER은 mention을 식별하고, NED는 그 mention을 knowledge base의 정확한 entity에 연결하며, ontology integration은 그 연결을 KG의 분석 가능한 구조로 확장한다.</strong></p>
<p>헬스케어 도메인의 IAS(intelligent advisory system) 시나리오를 예로 든다. &quot;Zika&quot;라는 mention이 텍스트에 등장했을 때, 이것이 바이러스를 가리키는지·질병을 가리키는지·지명을 가리키는지는 문맥에 의존한다. 도메인 전문가에게는 자명하나 문서 양이 늘어나면 수작업으로는 처리 불가능하다. NED는 이 모호성을 자동으로 제거하기 위해, 각 mention의 주변 문맥을 분석하고 reference knowledge base의 entity와 매핑하는 작업이다.</p>
<p>NER → NED → KG의 흐름은 단순한 후처리가 아니라, 비정형 텍스트를 ontology에 grounding하기 위한 필수 절차다. NER이 끝나는 지점에서 KG가 시작되는 것이 아니라, NED가 끼어들어야 비로소 KG가 의미 있는 구조를 갖는다.</p>
<h2 id="2-ned-시스템의-3단계-아키텍처">2. NED 시스템의 3단계 아키텍처</h2>
<p>NED 시스템은 일반적으로 세 phase로 구성된다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/f4ea8ac7-4fb3-463f-b9d9-feea512534a3/image.png" alt=""></p>
<p>각 phase의 역할은 다음과 같다.</p>
<table>
<thead>
<tr>
<th>Phase</th>
<th>입력</th>
<th>출력</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td>Candidate Selection</td>
<td>mention + reference KB</td>
<td>mention당 plausible entity 집합</td>
<td>가능한 후보를 추리는 단계. KB의 구조 정보를 활용해 정확한 식별을 가능하게 한다</td>
</tr>
<tr>
<td>Candidate Ranking</td>
<td>후보 집합 + 주변 문맥</td>
<td>mention당 단일 target entity</td>
<td>문맥(주변 단어) 기반으로 후보에 score를 부여하고 최고점 entity를 선택</td>
</tr>
<tr>
<td>Ontology Integration</td>
<td>disambiguated entity + 도메인 ontology</td>
<td>KG</td>
<td>다중 ontology의 구조·문맥 정보를 단일 KG로 집계</td>
</tr>
</tbody></table>
<p>이 3단계 분리가 중요한 이유는, 각 단계가 다른 종류의 신호에 의존하기 때문이다. Candidate selection은 KB의 표면형(surface form) 매칭에 가깝고, candidate ranking은 분산 표현·문맥 임베딩에 의존하며, ontology integration은 schema·hierarchy 같은 구조적 지식을 다룬다. 단일 모델에 압축하면 어느 신호도 충분히 활용되지 않는다.</p>
<h2 id="3-umls·snomed와-ontology-integration">3. UMLS·SNOMED와 ontology integration</h2>
<p>헬스케어 도메인의 reference knowledge base로 UMLS(Unified Medical Language System)가 표준이다. UMLS는 다중 출처에서 수집된 terminology, classification, coding 표준을 제공하며, 서로 다른 source에서 출발해도 동일 entity로 수렴할 수 있게 하는 interoperable 시스템을 가능하게 한다.</p>
<p>UMLS 산하의 SNOMED는 가장 포괄적이고 다국어를 지원하는 clinical terminology 중 하나로, 450,000개 이상의 concept을 포함한다. 책의 schema에서 SNOMED는 두 가지 방식으로 KG에 통합된다.</p>
<ul>
<li><strong><code>SNOMED_IS_A</code> relationship을 통한 semantic type 전파</strong>: SNOMED의 계층 구조를 따라 상위 entity의 semantic type이 하위 entity로 propagate된다. 트리 traversal을 통해 명시적으로 type이 부여되지 않은 entity에도 의미 분류가 부여된다.</li>
<li><strong><code>SNOMED_RELATION</code>의 단일화</strong>: SNOMED 내부의 다양한 relation 종류를 모두 하나의 relationship type으로 통일하고, 구체적 관계명은 type 속성에 저장한다. graph schema를 단순하게 유지하기 위한 설계 결정이다.</li>
</ul>
<p>LLM과의 관계도 분명히 짚어야 한다. UMLS knowledge base는 ChatGPT 같은 generic LLM에 incorporate되어 있지 않다. 따라서 도메인 NED를 LLM 단독으로 해결하려는 시도는 한계가 명확하다. LLM은 scispaCy 같은 전통 도구의 modern alternative이지만, KG 기술과 결합되어야 비로소 도메인 가치를 더한다.</p>
<h2 id="4-kg-schema-설계-mention과-entity의-분리-보존">4. KG schema 설계: mention과 entity의 분리 보존</h2>
<p>NED를 거친 결과를 KG에 어떻게 저장할지는 단순한 문제가 아니다. 책의 설계는 다음 원칙을 따른다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/1be95004-75b2-4fdd-a706-c488d4204ac5/image.png" alt=""></p>
<p>핵심은 <code>EntityMention</code>과 <code>MedicalEntity</code>를 모두 그래프에 보존한다는 점이다. <code>DISAMBIGUATED_TO</code> relationship을 통해 두 layer를 연결하면 다음과 같은 표현이 가능해진다.</p>
<ul>
<li>동일 문자열의 mention이 문맥에 따라 다른 entity로 매핑되는 경우 (예: &quot;PE&quot; → physical examination / pulmonary embolism)</li>
<li>서로 다른 문자열의 mention이 동일 entity로 수렴하는 경우 (예: &quot;pancreatic islets&quot; / &quot;islets of Langerhans&quot; → 동일 MedicalEntity)</li>
</ul>
<p>이 양방향 다대다 관계는 mention layer를 버리고 entity layer만 남기면 표현할 수 없다. flexibility를 위한 의도적 redundancy다.</p>
<h2 id="5-co-occurrence-텍스트-기반-kg의-확장">5. Co-occurrence: 텍스트 기반 KG의 확장</h2>
<p><code>Co-occurrence</code>의 정의는 책에서 명료하다. <strong>&quot;the projection of Page nodes onto Entity nodes.&quot;</strong> 같은 문장(또는 페이지) 내에 함께 등장한 medical entity 쌍을 직접 연결하는 relation이다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/318a5345-ade0-47e2-99a5-a415d201054b/image.png" alt=""></p>
<p>co-occurrence는 ontology에 등재되지 않은 관계도 데이터로부터 길어 올리는 장치다. 텍스트의 비정형 지식과 ontology의 구조적 지식을 잇는 다리 역할을 하며, 이 둘의 결합이 advanced use case의 토대가 된다.</p>
<h2 id="6-kg-기반-분석-네-가지-활용-사례">6. KG 기반 분석: 네 가지 활용 사례</h2>
<p>NED + ontology integration + co-occurrence가 결합된 KG 위에서 책은 네 가지 활용 사례를 제시한다.</p>
<table>
<thead>
<tr>
<th>사례</th>
<th>정의</th>
<th>핵심 메커니즘</th>
</tr>
</thead>
<tbody><tr>
<td>Conceptual search</td>
<td>정확한 keyword가 아닌 의미 기반 검색</td>
<td>ontology를 사용해 검색 공간을 확장한 뒤 문서 검색. full-text search가 표면형에서 끝나는 반면, conceptual search는 동의어·동치 표현을 흡수</td>
</tr>
<tr>
<td>Structured knowledge-based search</td>
<td>ontology의 형식 지식으로 텍스트에서 정보를 retrieve</td>
<td>SNOMED 같은 ontology relationship을 따라 문맥적으로 연결된 정보 집계. 출발점과 직접 매칭되지 않는 정보도 ontology 경로를 통해 도달</td>
</tr>
<tr>
<td>KG-based interpretability and discovery</td>
<td>ontology 경로가 텍스트의 &quot;왜&quot;를 설명(interpretability) 또는 텍스트가 말하지 않은 정보를 제공(discovery)</td>
<td>co-occurrence + ontology path 결합 분석</td>
</tr>
<tr>
<td>Uncovering new knowledge</td>
<td>ontology에 아직 등재되지 않은 사실을 KG에서 발견</td>
<td>co-occurring entity 사이에 ontology 경로가 부재한 경우, 새로운 도메인 지식의 후보로 식별</td>
</tr>
</tbody></table>
<p>interpretability와 discovery의 구분이 미묘하지만 중요하다. 텍스트에 등장한 entity 쌍 사이에 ontology 경로가 존재하면, 그 경로가 &quot;왜 이 둘이 함께 등장하는가&quot;의 임상적·역학적 근거가 된다. 반대로 그 경로가 텍스트에 없는 추가 정보를 끌어오면 discovery가 된다. 동일한 ontology 경로가 어느 방향으로 사용되느냐의 문제다.</p>
<p>네 번째 사례인 uncovering new knowledge는 KG와 ontology의 관계를 역전시킨다. 통상은 ontology가 KG를 enrich하지만, 텍스트로 만든 KG가 ontology를 enrich할 수 있다는 발상이다. 책은 이를 <strong>virtuous circle</strong>로 부른다.</p>
<h2 id="7-허브-노드-문제와-gds-기반-필터링">7. 허브 노드 문제와 GDS 기반 필터링</h2>
<p>ontology 경로를 따라 entity 쌍의 관계를 추론할 때 실무적으로 가장 자주 부딪히는 문제는 허브 노드(hub node)다. <code>Infectious process (qualifier value)</code>나 <code>Inflammation</code> 같은 노드는 수많은 다른 entity와 연결되어 있어, 거의 모든 entity 쌍 사이의 최단 경로에 끼어든다. 이 경로는 형식적으로는 valid하나 정보 가치는 낮다.</p>
<p>해결책은 두 갈래다.</p>
<ul>
<li><strong>허브 노드 필터링</strong>: degree가 일정 임계 이상인 노드를 경로 탐색에서 배제. Neo4j Graph Data Science(GDS) 라이브러리로 degree 계산 후 제외 set을 구성한다.</li>
<li><strong>추출된 관계 기반 시작점 전환</strong>: co-occurrence라는 약한 연결 대신, 텍스트에서 추출한 구체적 관계를 출발점으로 삼으면 경로가 더 specific해지고 허브 의존이 줄어든다.</li>
</ul>
<p>후자는 RE(relation extraction)의 결과를 NED와 결합하는 방향으로, 이 챕터의 파이프라인을 한 단계 더 정교화하는 확장점이다.</p>
<h2 id="8-한계와-트레이드오프">8. 한계와 트레이드오프</h2>
<ul>
<li><strong>Reference KB 의존성</strong>: NED의 정확도는 reference knowledge base의 coverage와 quality에 직접 비례한다. UMLS·SNOMED가 잘 정비된 헬스케어와 달리, 신생 도메인이나 multilingual·historical archive에서는 reference KB 자체가 빈약하거나 부재한다. 이 경우 candidate selection 단계에서 false negative가 폭증한다.</li>
<li><strong>Candidate ranking의 문맥 한계</strong>: ranking은 mention 주변 단어에 의존하나, 문서 내 멀리 떨어진 정보나 문서 외부 배경 지식이 결정적인 사례에서는 실패한다. 단락 단위 ranking과 문서 단위 ranking을 분리해 ensemble하는 보완이 필요하다.</li>
<li><strong>Co-occurrence의 noise</strong>: 같은 문장 내 등장이 곧 의미 있는 관계는 아니다. 부정문(negation), 가정문, 비교문에서의 co-occurrence는 잘못된 관계를 KG에 주입한다. 단순 projection이 아닌 syntactic dependency 기반 co-occurrence 정제가 요구된다.</li>
<li><strong>Hub node의 정의 자체의 임계값 문제</strong>: degree threshold를 어디에 두느냐는 도메인마다 다르고 ontology version에 따라 달라진다. 정적 임계값보다 percentile 기반·도메인 expert review 기반 정의가 안정적이다.</li>
<li><strong>Schema 단순화의 비용</strong>: <code>SNOMED_RELATION</code>을 단일 type으로 통합하는 결정은 schema는 단순화하나, Cypher 쿼리에서 type property를 매번 필터링해야 하는 부담을 만든다. 빈번한 relation type별로는 별도 relationship으로 분리하는 하이브리드가 실무 성능에 유리한 경우가 많다.</li>
</ul>
<h2 id="9-실무-적용-시-고려사항">9. 실무 적용 시 고려사항</h2>
<p>NED 파이프라인을 실서비스에 적용할 때 다음 사항을 추가로 점검해야 한다.</p>
<p>ontology 버전 관리는 별도의 운영 이슈다. SNOMED·UMLS는 정기적으로 업데이트되며, 기존에 NED된 mention의 매핑이 새 버전에서 deprecate되거나 분할될 수 있다. KG schema에 ontology version 메타데이터를 entity property로 저장하고, 재처리(reprocessing) 트리거를 명시해야 한다.</p>
<p>LLM과 전통 NED의 역할 분담도 설계 단계에서 결정해야 한다. LLM은 candidate generation에서 zero-shot 능력을 발휘하지만, candidate ranking에서는 hallucination 위험이 크다. ranking 단계만큼은 KB-grounded score(예: TF-IDF, embedding similarity, ontology path-based score)에 무게를 두고, LLM은 final tie-breaking이나 explanation 생성에 한정하는 것이 안전하다.</p>
<h2 id="10-정리">10. 정리</h2>
<p>NED는 NER이 끝나는 지점에서 시작되는 별개 task이며, candidate selection·candidate ranking·ontology integration의 3단계로 분해된다. EntityMention과 disambiguated entity를 layer로 분리해 보존하는 schema 설계, co-occurrence를 통한 텍스트-ontology 결합, 그리고 conceptual search에서 uncovering new knowledge에 이르는 네 가지 활용 사례가 이 챕터의 골격이다. </p>
<blockquote>
<p><strong>결론</strong>: NER이 mention을 식별한다면, NED는 mention을 knowledge에 연결하고, ontology integration은 그 연결을 분석 가능한 KG로 확장한다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Knowledge Graphs and LLMs in Action. Manning, 2024 —
Chapter 7: Named Entity Disambiguation.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[LLM으로 Knowledge Graph 구축하기]]></title>
            <link>https://velog.io/@tasker_dev/LLM%EC%9C%BC%EB%A1%9C-Knowledge-Graph-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@tasker_dev/LLM%EC%9C%BC%EB%A1%9C-Knowledge-Graph-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 04 May 2026 09:03:37 GMT</pubDate>
            <description><![CDATA[<p>이 챕터의 thesis는 단순하다. <strong>LLM은 KG를 직접 생산하지 않는다. LLM은 KG를 만들기 위한 중간 산출물(metagraph)을 생산하고, 정규화·entity resolution을 거쳐야 비로소 KG가 된다.</strong></p>
<p>이전 편까지 vector RAG와 graph RAG의 구조적 차이, 그리고 chunk 기반 검색의 한계를 다뤘다면, 이번 글은 그 graph RAG의 입력이 되는 <strong>KG를 비정형 텍스트로부터 어떻게 구축하는가</strong>에 대한 실제 파이프라인을 다룬다. 사례는 록펠러 재단(RAC) 프로젝트로, 1939년에 타이핑된 Warren Weaver의 다이어리 10,000여 페이지를 KG로 변환하는 작업이다.</p>
<h2 id="1-비정형-아카이브를-kg로-만들-때-마주치는-문제">1. 비정형 아카이브를 KG로 만들 때 마주치는 문제</h2>
<p>KG 구축 파이프라인 설명에 앞서, 어떤 입력 조건에서 어떤 어려움이 발생하는지 먼저 정의한다. 이 문제들이 이후 등장하는 3-layer 설계와 entity resolution 전략의 동기다.</p>
<table>
<thead>
<tr>
<th>문제 유형</th>
<th>내용</th>
<th>함의</th>
</tr>
</thead>
<tbody><tr>
<td>Analog 문서</td>
<td>OCR 처리 필요 (Tesseract, Amazon, Microsoft 등)</td>
<td>OCR 오류로 인한 entity 표기 변형이 entity resolution 부담을 가중</td>
</tr>
<tr>
<td>Historical 문서</td>
<td>더 이상 연구되지 않는 분야가 다수 → 참조용 NER dictionary/knowledge base 부재</td>
<td>WikiData 등 외부 disambiguation 기반 약함</td>
</tr>
<tr>
<td>비표준 표기</td>
<td>&quot;S.&quot; for &quot;J. R. Smith&quot;, &quot;U.Cal.&quot; for &quot;University of California&quot; 같은 축약</td>
<td>off-the-shelf coreference 모델 실패. LLM이 implicit하게 NER+RE+resolution 수행 가능</td>
</tr>
<tr>
<td>Domain-specific entity</td>
<td>자연과학 분야의 연구 disciplines, treatments, diseases 등 — granularity가 제각각</td>
<td>전통 NER 모델 부재. custom ML 기반 NER + unsupervised entity resolution 필요</td>
</tr>
<tr>
<td>높은 relational complexity</td>
<td>한 페이지에 수십 개 relation 등장</td>
<td>RE schema 설계가 정확도와 직결</td>
</tr>
<tr>
<td>다중 source 매칭</td>
<td>diary와 board minutes를 하나의 KG로 연결 시 추가 정규화</td>
<td>본 챕터 범위 밖</td>
</tr>
</tbody></table>
<p>여기서 핵심 통찰은 &quot;LLM era 이전이라면 traditional ML 모델 구축에 막대한 자원이 들었을 작업이, 적절한 knowledge representation + LLM + prompt engineering 조합으로 해결 가능해졌다&quot;는 점이다.</p>
<h2 id="2-3-layer-graph-설계">2. 3-Layer Graph 설계</h2>
<p>문서를 KG로 직접 변환하는 single-shot 접근은 실패한다. 중간 표현이 필요하고, 책은 이를 세 layer로 분리한다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/b9ab5b1a-7e76-48f4-9ee3-8d4c3576ceef/image.png" alt=""></p>
<p>이 설계의 핵심은 <strong>metagraph layer를 중간 단계로 두는 것</strong>이다. LLM이 추출한 entity를 곧바로 최종 노드로 merge하지 않고, 일단 페이지에 link된 mention 단위로 저장한다. 이 구조가 주는 이점은 다음과 같다.</p>
<ul>
<li>각 entity mention의 <strong>출처 페이지를 보존</strong>한다. 시각화 플랫폼에서 &quot;이 entity는 어디서 추출되었는가&quot;를 즉시 보여줄 수 있어 explainability가 자연스럽게 확보된다.</li>
<li>entity resolution 로직을 수정하면 <strong>metagraph로부터 KG layer를 언제든 재생성</strong>할 수 있다. resolution 알고리즘은 한 번에 완성되지 않으며, 시행착오가 필수적이다.</li>
<li>페이지 간 분산된 entity 정보를 aggregate하면서도 원본 정보 손실이 없다.</li>
</ul>
<h2 id="3-normalization과-cleansing">3. Normalization과 Cleansing</h2>
<p>metagraph가 만들어지면 통계 분석으로 연결성 향상 기회를 찾는다. 대표적인 정규화 작업은 다음과 같다.</p>
<ul>
<li><strong>lowercasing</strong>: Occupation처럼 case가 의미 없는 entity는 소문자로 통일하면 동일 개념의 분산 저장을 막을 수 있다.</li>
<li><strong>token stripping</strong>: GPT는 prompt에서 분리하라고 지시해도 종종 person name에 title을 포함시킨다 (&quot;Dr. Eleanor Smith&quot;). title을 제거하지 않으면 동일 인물이 KG에 두 번 등장한다.</li>
</ul>
<p>핵심은 정규화 결과를 원본 <code>name</code> 속성을 덮어쓰지 않고 <code>name_normalized</code>라는 <strong>새 속성에 저장</strong>한다는 것이다. 원본은 trace 용으로 유지되고, KG layer에 link할 때만 정규화된 값을 쓴다.</p>
<pre><code class="language-cypher">// title 제거 예시 (의사코드 수준)
MATCH (e:Entity)
WHERE e.name STARTS WITH &quot;Dr. &quot; OR e.name STARTS WITH &quot;Prof. &quot;
SET e.name_normalized = trim(replace(replace(e.name, &quot;Dr. &quot;, &quot;&quot;), &quot;Prof. &quot;, &quot;&quot;))

// case 정규화
MATCH (e:Entity {class: &quot;Occupation&quot;})
SET e.name_normalized = toLower(e.name)</code></pre>
<h2 id="4-entity-resolution-같은-사람을-같은-노드로">4. Entity Resolution: 같은 사람을 같은 노드로</h2>
<p>Generative LLM은 traditional NER/RE 모델과 달리 prompt가 잘 설계되었다면 <strong>entity의 full clean form만 반환</strong>한다. 이는 implicit coreference resolution이 일어난다는 뜻이고, 후처리로서의 entity resolution 부담을 줄인다. 그러나 줄인다는 것이지 없앤다는 것이 아니다. 문서 간 resolution은 여전히 별도 작업이다. 한 다이어리에서 &quot;Eleanor Smith&quot;가 다른 다이어리의 &quot;E. Smith&quot;와 같은 사람인지를 판정해야 한다.</p>
<h3 id="41-string-similarity만으로-부족하다">4.1 String similarity만으로 부족하다</h3>
<p>이름 문자열이 비슷하다는 것만으로는 신뢰도가 낮다. 사람 이름은 first / middle / surname 구조를 갖고, middle name은 축약되거나 생략된다. surname만 같으면 false positive가 폭증하고, surname + first name 일부 조합 정도에서 신뢰도가 생긴다. 또한 도메인 stopword 처리가 중요하다. 많은 재단 명칭에 &quot;Foundation&quot;이 포함되어 있는데, 이를 무시하지 않으면 무관한 organization들이 모조리 SIMILAR로 묶인다.</p>
<h3 id="42-관계-그래프를-entity-resolution에-활용한다">4.2 관계 그래프를 entity resolution에 활용한다</h3>
<p>여기가 graph 기반 접근의 진가다. metagraph의 mention들은 이미 다른 mention들과 관계로 연결되어 있고, 이 관계 자체가 resolution 신호가 된다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/59977964-9c95-4d5b-afe3-7ef1d934dcdc/image.png" alt=""></p>
<p>세 mention이 같은 인물(노벨 물리학상 수상자 Ernest Lawrence)이라는 판정 근거는 다음 신호의 결합이다.</p>
<ul>
<li>이름 문자열 유사도 (rule 기반)</li>
<li>동일 organization (<code>WORKS_FOR</code> → University of California) 공유 → 3-hop 거리</li>
<li>의미적으로 연관된 occupation (<code>cyclotron</code> ↔ <code>100,000,000 to 200,000,000 volt cyclotron</code>) 공유</li>
</ul>
<p>특히 &quot;Ernest Orlando Lawrence&quot;와 단독 &quot;Lawrence&quot;는 6-hop 거리에 있다. <strong>관계형 DB라면 이런 traversal은 매우 비싸지만, graph DB에서는 자연스러운 query</strong>다.</p>
<h3 id="43-알고리즘-단계">4.3 알고리즘 단계</h3>
<p>resolution 파이프라인은 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/49ceacbe-372b-4593-b1fc-aad0de318c51/image.png" alt=""></p>
<p>추가 옵션으로 다음이 거론된다.</p>
<ul>
<li><strong>Community detection (Louvain 등)</strong>: TALKED_ABOUT, TALKED_WITH 같은 intellectual network에 community detection을 적용하면, 동명이인을 분리할 수 있다. 남극에서 해양 연구를 하는 John Doe와 cosmology를 연구하는 John Doe는 다른 community에 속할 가능성이 높다.</li>
<li><strong>Embedding 기반 의미적 유사도</strong>: 문자열이 전혀 닮지 않았지만 개념적으로 가까운 occupation들 (<code>fertility</code>와 <code>human ovulation</code>)은 GPT embedding으로 vector화 후 agglomerative clustering으로 묶을 수 있다. 이 부분은 vector approach와 graph approach가 만나는 지점이다.</li>
</ul>
<h2 id="5-완성된-kg로-무엇을-하는가-지적-네트워크-분석">5. 완성된 KG로 무엇을 하는가: 지적 네트워크 분석</h2>
<p>KG는 만드는 것 자체가 목적이 아니다. 주된 용도는 graph analytics다. 책에서 RAC 프로젝트가 보여주는 분석은 &quot;과학자들의 지적 네트워크&quot;를 추출하는 것이다. TALKED_ABOUT, TALKED_WITH, WORKS_WITH, STUDENT_OF 같은 관계로 구성된 subgraph에 Neo4j Graph Data Science library의 알고리즘을 적용한다.</p>
<table>
<thead>
<tr>
<th>분석 목표</th>
<th>알고리즘</th>
<th>해석</th>
</tr>
</thead>
<tbody><tr>
<td>Influencers</td>
<td>PageRank, out-degree</td>
<td>다른 사람의 연구를 추천하는 빈도가 높은 인물</td>
</tr>
<tr>
<td>Influencees</td>
<td>in-degree, eigenvector centrality</td>
<td>추천·언급의 주된 대상</td>
</tr>
<tr>
<td>Bridges</td>
<td>Betweenness centrality</td>
<td>분리된 community를 잇는 연결자</td>
</tr>
</tbody></table>
<p>betweenness centrality 시각화에서는 노드 크기가 통과하는 최단경로 수에 비례한다. 이 시각화에서 Niels Bohr나 Ernest Lawrence처럼 유명한 과학자가 부각되는 것은 예상대로지만, 덜 알려진 인물도 함께 드러난다. 이 &quot;덜 알려진 bridge&quot;가 도메인 분석가에게 새로운 가설의 출발점이 된다.</p>
<p>KG는 또한 더 좁은 질문에도 답한다. &quot;cyclotron 연구와 그 펀딩에 핵심 역할을 한 사람은 누구인가&quot; 같은 질문은 cyclotron을 연구한 인물에서 시작해 최대 2-hop 거리에 있는 인물로 path를 확장하면서 referral 패턴을 추적하는 query로 표현된다.</p>
<p>실용 시나리오 하나는 다음과 같다. project officer Warren Weaver가 자리를 떠나 후임이 들어온다. 후임은 Johns Hopkins와 Harvard 양쪽에 physics 도메인 연결이 있는 인물에게 비공식 자문을 받고 싶다. 이 질문은 KG의 영향력 네트워크에서 직접 답이 나온다. 게다가 TALKED_ABOUT relation에 sentiment 속성이 있다면, &quot;Bernal은 Dorothy Wrinch에 부정적 태도를 보인다, Irving Langmuir는 Bernal에 부정적 태도를 보인다&quot;처럼 정성적 정보까지 활용한 균형 잡힌 인터뷰 대상자 선정이 가능하다.</p>
<h2 id="6-한계와-트레이드오프">6. 한계와 트레이드오프</h2>
<p>LLM 기반 KG 구축은 마술이 아니다. 책 본문에서 명시적으로 또는 암묵적으로 드러나는 한계는 다음과 같다.</p>
<p><strong>1) RE 실패의 silent propagation.</strong> 책의 사례에서 어떤 인물이 cyclotron subgraph에 잘못 등장한 사건이 보고된다. 이는 LLM의 RE 단계 오류이며, 다운스트림 분석 결과를 오염시킨다. 분석가가 graph 내용을 검증·반려할 수 있는 <strong>feedback loop가 application에 내장되지 않으면, 잘못된 entity가 의사결정에 영향을 미친다.</strong></p>
<p><strong>2) Entity resolution은 보수적일수록 유실, 공격적일수록 오염된다.</strong> Irving Langmuir가 두 노드(Langmuir와 Irving Langmuir)로 남은 사례는 책에 직접 등장한다. 한 페이지에서 surname만 등장하면서 다른 어떤 관계도 함께 추출되지 않으면 resolution에 사용할 신호가 없어 두 mention이 분리된 채로 KG에 들어간다. resolution rule을 더 공격적으로 풀면 동명이인 false merge가 발생한다. 이 trade-off는 알고리즘 튜닝만으로 해소되지 않는다.</p>
<p><strong>3) Token limit과 multi-page 처리.</strong> LLM은 한 번에 처리할 수 있는 입력·출력 토큰이 제한된다. 다이어리가 수백 페이지일 때 단순 chunking은 entry 경계를 무시하고, 그러면 entity 간 관계가 끊긴다. RAC 프로젝트는 ChatGPT-3.5-Turbo로 entry boundary detection을 별도 단계로 두는 식으로 우회했지만, 이는 추가 LLM 호출 비용과 또 다른 오류 표면을 만든다.</p>
<p><strong>4) Schema 설계는 사후 변경 비용이 크다.</strong> RE schema가 너무 좁으면 유용한 관계를 놓치고, 너무 넓으면 noise가 KG를 채운다. metagraph layer 덕분에 KG layer는 재생성 가능하지만, schema 변경은 LLM 추출 단계까지 거슬러 올라가야 한다.</p>
<h2 id="7-실무-적용-고려사항">7. 실무 적용 고려사항</h2>
<p>위 한계를 인지한 상태에서 production-quality KG 시스템을 구축할 때, 책이 제시하는 next steps는 다음과 같이 압축된다.</p>
<ul>
<li><strong>Knowledge extraction 정확도 향상</strong>: prompt engineering 추가 iteration 또는 LLM fine-tuning. 어느 시점부터는 prompt 튜닝의 한계가 분명해지므로, 도메인 데이터로 fine-tuning이 비용 대비 효과가 더 클 수 있다.</li>
<li><strong>외부 knowledge base 연동</strong>: WikiData 등을 활용한 entity disambiguation은 historical 도메인이 아닌 현대 도메인에서 baseline resolution을 강력하게 끌어올린다.</li>
<li><strong>Occupation entity의 hierarchical resolution</strong>: <code>nuclear physics</code> ⊃ <code>isotopes</code> ⊃ <code>heavy nitrogen</code> 같은 granularity 차이를 단순 cluster로 처리하지 않고, embedding (SentenceBERT, GPT) + agglomerative hierarchical clustering으로 계층을 보존한다. 이렇게 해야 &quot;주제의 전체 역사&quot;를 query할 수 있다.</li>
<li><strong>Conversation 노드 도입</strong>: date, interviewer, interviewee, topic을 갖는 Conversation 노드를 만들면 follow-up chain 분석과 grant 연결이 가능해진다.</li>
<li><strong>Cross-source 통합</strong>: 다이어리(diaries)와 이사회 의사록(board of directors minutes)을 같은 KG에 통합하면 &quot;어떤 conversation이 어떤 grant로 이어졌는가&quot; 같은 질문에 답할 수 있다.</li>
</ul>
<h2 id="8-왜-llm에-직접-묻지-않고-kg를-만드는가">8. 왜 LLM에 직접 묻지 않고 KG를 만드는가</h2>
<p>이 챕터의 마지막 질문은 사실 시리즈 전체의 thesis를 압축한다. 답은 다섯 가지로 정리된다.</p>
<table>
<thead>
<tr>
<th>가치</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>Explainability</td>
<td>KG는 응답의 근거가 되는 노드·관계·원본 텍스트를 모두 추적 가능하게 한다. LLM 단독 응답은 &quot;왜 이 답인가&quot;를 답하지 못한다.</td>
</tr>
<tr>
<td>Demystification</td>
<td>LLM을 black box로 사용하지 않고, 정보 추출 단계에서만 LLM의 언어 이해 능력을 빌린다. 결과물(KG)은 검증 가능한 정형 데이터다.</td>
</tr>
<tr>
<td>Democratization</td>
<td>LLM 학습·fine-tuning은 비싸다. 한 번 LLM을 써서 KG를 만들고 이후엔 KG만 운용하는 방식은 비용을 분산시킨다.</td>
</tr>
<tr>
<td>Explorability</td>
<td>Graph 시각화는 사용자가 데이터를 새로운 각도에서 탐색·드릴다운하게 한다. 가설 생성 도구로 기능한다.</td>
</tr>
<tr>
<td>Advanced analytics</td>
<td>PageRank, community detection, centrality 등 graph algorithm은 LLM의 black box reasoning에 의존하지 않는 분석 경로를 제공한다.</td>
</tr>
</tbody></table>
<blockquote>
<p>결론: LLM은 KG의 직접 생산자가 아니라 raw text에서 entity와 relation을 뽑는 추출기이며, KG의 가치는 추출 이후의 정규화·resolution·graph analytics 단계에서 결정된다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Knowledge Graphs and LLMs in Action. Manning, 2024 — 
Chapter 6: Building knowledge graphs with large language models</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[비정형 데이터에서 도메인 특화 지식 추출]]></title>
            <link>https://velog.io/@tasker_dev/%EB%B9%84%EC%A0%95%ED%98%95-%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%97%90%EC%84%9C-%EB%8F%84%EB%A9%94%EC%9D%B8-%ED%8A%B9%ED%99%94-%EC%A7%80%EC%8B%9D-%EC%B6%94%EC%B6%9C</link>
            <guid>https://velog.io/@tasker_dev/%EB%B9%84%EC%A0%95%ED%98%95-%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%97%90%EC%84%9C-%EB%8F%84%EB%A9%94%EC%9D%B8-%ED%8A%B9%ED%99%94-%EC%A7%80%EC%8B%9D-%EC%B6%94%EC%B6%9C</guid>
            <pubDate>Mon, 04 May 2026 08:04:58 GMT</pubDate>
            <description><![CDATA[<h2 id="1-비정형-데이터에서-kg로-두-개의-축">1. 비정형 데이터에서 KG로: 두 개의 축</h2>
<p>이전 챕터까지 다룬 KG는 테이블·관계형 DB·기존 knowledge base 같은 구조화된 데이터를 전제로 했다. 그러나 실무에서 마주하는 도메인 지식 대부분은 논문·보고서·아카이브 문서 같은 비정형 텍스트에 잠겨 있다. 이 챕터의 thesis는 다음과 같다. <strong>비정형 데이터에서 KG를 만드는 작업은 &quot;지식 표현(knowledge representation)&quot;과 &quot;지식 학습(knowledge learning)&quot;이라는 두 축의 결합이며, LLM은 후자의 비용 구조를 근본적으로 바꾼다.</strong></p>
<ul>
<li>Knowledge representation: 정보를 컴퓨터와 사람이 모두 자율적으로 접근 가능한 형태로 모델링하는 방법. 분산되고 고립된 정보를 정렬되고 연결된 형태로 만드는 것이 KG의 본질이다.</li>
<li>Knowledge learning: NLP·LLM 같은 프레임워크와 기술의 조합으로 비정형 문서에서 insight를 채굴하는 과정이다.</li>
</ul>
<p>전체 파이프라인은 digitization → NER → RE → custom knowledge extraction의 순서로 흐른다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/4603ae02-da93-4158-9ccd-80338f987deb/image.png" alt=""></p>
<p>NER과 RE 단계에 전통적 NLP 모델 또는 LLM이 투입된다. 강조된 두 노드가 이 챕터의 핵심 결정 지점이다.</p>
<h2 id="2-ner과-re-kg-구축의-최소-단위">2. NER과 RE: KG 구축의 최소 단위</h2>
<h3 id="21-named-entity-recognition">2.1 Named Entity Recognition</h3>
<p>NER은 raw text에서 named entity의 mention을 식별하고 사전 정의된 카테고리로 분류하는 ML classification system으로 정의된다. 다수의 open source NLP 라이브러리는 Person, Location, Organization 같은 generic 카테고리를 즉시 제공하지만, 실제 도메인 작업에서는 거의 충분하지 않다.</p>
<p>예를 들어 화학 연구 아카이브에서 &quot;Professor of Chemistry&quot;라는 직위, 특정 연구 분야명, 화학 물질명을 식별해야 한다면 generic NER 모델로는 부족하다. 단순한 dictionary 기반 시스템도 도메인 어휘 커버리지가 낮다. 따라서 custom entity-extraction system이 필요하며, 이는 custom NER 모델 학습 또는 LLM 활용 두 경로로 해결된다.</p>
<h3 id="22-relation-extraction">2.2 Relation Extraction</h3>
<p>RE는 텍스트 내 entity pair 사이의 semantic relation을 식별하는 작업이다. 다음 문장을 보자.</p>
<blockquote>
<p>&quot;Jane Austen, Victorian era writer, is currently employed by Google.&quot;</p>
</blockquote>
<p>이 문장은 PERSON(&quot;Jane Austen&quot;)과 ORGANIZATION(&quot;Google&quot;)이라는 두 named entity를 포함하며, 둘은 employment 관계로 묶인다. <strong>이 relatedness를 포착하는 것이 진짜 KG를 만드는 작업이다.</strong> 단순히 entity 목록만 추출하면 그래프가 아니라 리스트일 뿐이다.</p>
<h2 id="3-전통적-nlp에서-llm으로-패러다임-전환">3. 전통적 NLP에서 LLM으로: 패러다임 전환</h2>
<h3 id="31-모델-중심에서-데이터-중심으로">3.1 모델 중심에서 데이터 중심으로</h3>
<p>전통적 NLP는 task-specific training dataset 구축, 최적 model architecture 탐색, hyperparameter fine-tuning 같은 model-centric 접근을 취해왔다. 이 접근의 극단적 형태는 데이터 품질 문제를 무시하는데, faulty data를 넣으면 faulty prediction이 나온다는 단순한 사실을 간과한다.</p>
<p>시간이 지나면서 data-centric paradigm이 주목받기 시작했다. 고복잡도 ML 모델을 학습시키기 위한 데이터의 품질과 양을 개선하는 데이터 엔지니어링이 핵심으로 자리 잡았다.</p>
<h3 id="32-llm의-등장이-바꾼-것">3.2 LLM의 등장이 바꾼 것</h3>
<p>2022년 말 OpenAI의 GPT-3.5 시리즈와 ChatGPT 공개 이후, Transformer 아키텍처 기반 generative model이 NLP 작업의 표준 옵션이 되었다. LLM의 핵심 가치는 다음 두 가지다.</p>
<ul>
<li><strong>Transfer learning</strong>: 방대한 데이터셋으로 학습된 weight를 재사용하므로, 동일한 정확도를 더 적은 human-labeled data로 달성한다. 모델을 from scratch로 학습할 필요가 없다.</li>
<li><strong>Prompt engineering으로 충분</strong>: 자연어로 task를 기술(prompt engineering)하면 모델이 답을 생성한다. Model engineering이 필요 없다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/9d57e6c2-8779-4ee5-ab15-c14f7c090882/image.png" alt=""></p>
<h3 id="33-llm의-한계-hallucination">3.3 LLM의 한계: hallucination</h3>
<p>LLM의 강력함에도 시스템 설계 시 반드시 고려할 한계가 있다. 가장 중요한 것은 <strong>hallucination</strong>, 즉 학습 데이터에 근거가 없는 경우에도 &quot;사실&quot;을 만들어내고 잘못된 추론을 수행하는 경향이다. 이 때문에 LLM을 KG 구축 도구로 사용할 때는 LLM이 직접 사실을 단정하게 하기보다, 고품질의 named entity·관계·속성을 추출하도록 유도하는 방식이 권장된다.</p>
<h2 id="4-llm-기반-kg-구축-워크플로우">4. LLM 기반 KG 구축 워크플로우</h2>
<p>LLM을 활용한 KG 구축은 prompt-based inference로 즉시 시작하거나, 도메인 특화 정확도를 위해 fine-tuning으로 확장할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/dd00c02a-6b61-42f5-8456-d16ef6f8c112/image.png" alt=""></p>
<p>각 단계의 역할은 다음과 같다.</p>
<ul>
<li><strong>Data exploration</strong>: 도메인의 복잡도와 합리적 기대치를 파악하는 초기 단계. 데이터 이해 없이는 의미 있는 prompt를 작성할 수 없다.</li>
<li><strong>Prompt engineering</strong>: 가장 효과적인 prompt를 찾기 위한 반복 과정. 단어 몇 개를 바꾸는 것만으로도 성능에 큰 차이가 발생한다.</li>
<li><strong>Pre-annotation + Fine-tuning</strong>: prompt-based 접근이 한계에 부딪히면 진입한다. 비용과 시간이 들지만, 도메인에 specialized된 더 안정적이고 정확한 모델을 얻는다.</li>
</ul>
<h2 id="5-prompt-engineering의-실제-세-번의-반복">5. Prompt Engineering의 실제: 세 번의 반복</h2>
<p>generic prompt(&quot;모든 entity와 관계를 식별하라&quot;)만으로 KG를 만들면 다음과 같은 문제가 드러난다.</p>
<ul>
<li><strong>공지칭 해결과 약어 복원의 자연스러운 처리</strong>: LLM의 generative 특성 덕분에 coreference resolution이 implicit하게 수행된다. 예를 들어 &quot;Prof. Chem.&quot;이나 &quot;Assoc. in Chem.&quot; 같은 축약 직위가 &quot;Professor of Chemistry&quot;, &quot;Associate in Chemistry&quot;로 복원된다. 전통적 NER 모델이 할 수 없는 작업이다.</li>
<li><strong>과도한 관계 세분화</strong>: specializes in, is measuring, is interested in, demonstrates처럼 모두 정확하지만 KG 관점에서 같은 개념을 표현하는 관계들이 분리된다.</li>
<li><strong>비일관된 노드 라벨</strong>: 같은 단락의 네 가지 연구 주제에 field, research, theory 세 가지 다른 라벨이 부여된다.</li>
<li><strong>예측 불안정성</strong>: 동일한 prompt를 동일 텍스트에 반복 실행해도 결과가 달라진다.</li>
</ul>
<p>이러한 문제는 prompt를 점진적으로 정교화하면서 해결된다.</p>
<table>
<thead>
<tr>
<th>버전</th>
<th>전략</th>
<th>결과</th>
</tr>
</thead>
<tbody><tr>
<td>v1: 일반 지시</td>
<td>&quot;모든 entity와 관계를 식별하라&quot;</td>
<td>공지칭/약어 처리는 우수, 라벨 비일관·과도 세분화·불안정</td>
</tr>
<tr>
<td>v2: 목록 추가</td>
<td>entity class 목록과 relation type 목록을 prompt에 포함, 성능이 낮은 관계 유형에 설명 추가, 도메인 노트 2개 추가 (이니셜로 사람을 지칭하는 관행, 대학·학과명 별칭)</td>
<td>정규화 향상, 일부 안정성 확보</td>
</tr>
<tr>
<td>v3: 풍부한 예시</td>
<td>예시를 확장해 명확한 가이드라인 제공</td>
<td>entity·relation class 부여의 높은 안정성, 관심 관계 모두 정확히 식별</td>
</tr>
</tbody></table>
<p>세 번째 버전에서 원하는 모든 결과를 달성한다. 핵심 교훈은 모델 자체의 선택보다 <strong>근본 원리</strong>, 즉 task 명확성·도메인 노트·다양한 예시가 더 결정적이라는 점이다. 이 챕터의 prompt는 ChatGPT 계열 모델용으로 설계되어 GPT-4o mini에서 테스트되었으나, 새로운 모델이 등장해도 동일한 원칙이 적용된다.</p>
<h3 id="prompt-설계-일반-원칙">Prompt 설계 일반 원칙</h3>
<table>
<thead>
<tr>
<th>원칙</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Task description</td>
<td>모호성을 제거할 만큼 정확하게 task를 기술. 모델이 답을 &quot;지나치게 창의적으로&quot; 만들 여지를 줄임</td>
</tr>
<tr>
<td>Naming entity/relation classes</td>
<td>관심 있는 entity·relation class 목록을 prompt에 포함. 작업을 명료화하고 정규화된 출력 유도</td>
</tr>
<tr>
<td>Complex representative examples</td>
<td>입력과 기대 출력 예시를 prompt에 포함. 실제 텍스트의 압축본에 중요한 entity·relation을 담는 형태</td>
</tr>
<tr>
<td>LLM configuration</td>
<td>temperature는 낮을수록 deterministic, 높을수록 variation·hallucination 증가</td>
</tr>
<tr>
<td>Prediction stability</td>
<td>LLM은 generative이므로 동일 prompt에서도 결과가 다를 수 있음. 모호하지 않은 prompt + 낮은 temperature로 통제</td>
</tr>
<tr>
<td>Unit-testing</td>
<td>prompt 업데이트마다 텍스트와 기대 출력을 테스트 리스트에 추가. 주기적으로 일괄 실행해 성공률 측정</td>
</tr>
<tr>
<td>Eyeballing a mini-KG</td>
<td>prompt milestone마다 수십 페이지 분량으로 KG를 만들어 직접 탐색. 정성적 검증 단계</td>
</tr>
<tr>
<td>Evaluation</td>
<td>만족스러운 prompt에 도달하면 정량 평가. QA 담당자가 correct/incorrect/missing으로 라벨링</td>
</tr>
</tbody></table>
<h2 id="6-전통적-nlp-vs-llm-선택의-기준">6. 전통적 NLP vs LLM: 선택의 기준</h2>
<p>LLM이 모든 상황에서 우월한 것은 아니다. 도메인·비용·운영 제약에 따라 선택이 달라진다.</p>
<table>
<thead>
<tr>
<th>기준</th>
<th>전통적 NLP</th>
<th>LLM</th>
</tr>
</thead>
<tbody><tr>
<td>예측 속도</td>
<td>빠름 (CPU 가능)</td>
<td>느림 (GPU 필수)</td>
</tr>
<tr>
<td>인프라 비용</td>
<td>단순·저렴</td>
<td>복잡·고비용</td>
</tr>
<tr>
<td>예측 단위 비용</td>
<td>매우 낮음</td>
<td>데이터셋 규모에 따라 가변</td>
</tr>
<tr>
<td>보안·on-premise</td>
<td>격리 배포 용이</td>
<td>GPU cluster 자체 운영 부담</td>
</tr>
<tr>
<td>초기 도메인 커스터마이징</td>
<td>높은 초기 투자 (전문 인력, annotation)</td>
<td>Prompt engineering으로 빠른 진입</td>
</tr>
<tr>
<td>학습 곡선</td>
<td>가파름 (data scientist 필수)</td>
<td>완만 (최소 기술로 prompt engineering 가능)</td>
</tr>
<tr>
<td>파이프라인</td>
<td>NER · RE · coreference · entity resolution 등 다단계 체이닝</td>
<td>One-pass 처리</td>
</tr>
<tr>
<td>출력 품질</td>
<td>후처리 (cleansing, normalization) 필요량 많음</td>
<td>생성적 특성으로 후처리 부담 적음</td>
</tr>
<tr>
<td>Fine-tuning 비용</td>
<td>해당 없음 (모델별 학습)</td>
<td>prompt로 부족할 때 추가 비용 발생 (OpenAI 기준 custom 모델 추론은 약 10배)</td>
</tr>
</tbody></table>
<p>선택은 이분법적이지 않다. 두 접근은 공존 가능하며, LLM을 활용해 전통적 NLP 모델 학습용 데이터를 빠르게 pre-annotation하는 하이브리드 전략도 유효하다.</p>
<h2 id="7-한계와-트레이드오프">7. 한계와 트레이드오프</h2>
<p>LLM 기반 KG 구축이 실무에서 직면하는 한계는 다음 세 가지로 정리된다.</p>
<ul>
<li><strong>Hallucination 리스크</strong>: LLM이 근거 없는 entity나 관계를 생성할 수 있다. KG는 downstream 추론에 사용되므로, 잘못된 사실 하나가 multi-hop traversal을 통해 확산될 위험이 있다. 따라서 LLM 출력을 그대로 신뢰하기보다 high-quality entity·relation·property 추출에 활용하고, 사실 단정은 원본 문서나 신뢰 가능한 source로 우회하는 설계가 안전하다.</li>
<li><strong>출력 안정성과 정규화 비용의 trade-off</strong>: temperature를 낮추면 안정성은 확보되지만, 미묘한 도메인 변형에 대한 적응력이 떨어진다. 반대로 prompt를 정교화해 entity/relation class를 강하게 명시하면 안정성은 높아지나 prompt 자체가 길어져 token 비용과 유지보수 부담이 늘어난다. 제3의 옵션인 fine-tuning은 이 trade-off를 우회하지만 초기 비용을 다시 부과한다.</li>
<li><strong>대규모 데이터셋에서의 비용 역전</strong>: 소~중규모 데이터셋에서는 LLM이 전통적 NLP 파이프라인 대비 초기 투자가 적어 유리하지만, 데이터 규모가 커질수록 GPU 추론 비용이 누적되어 경제성이 역전될 수 있다. streaming 시나리오나 실시간 추론 요구가 있는 환경에서는 더욱 그렇다.</li>
</ul>
<h2 id="8-실무-적용-시-고려사항">8. 실무 적용 시 고려사항</h2>
<p>LLM으로 KG를 구축할 때 production 단계에서 자주 간과되는 지점이 있다. 첫째, prompt engineering은 코드와 동일하게 버전 관리되어야 한다. 단어 하나의 변경이 출력 품질에 비대칭적 영향을 미치므로, prompt와 기대 출력을 unit test 형태로 묶고 회귀 테스트를 자동화하는 것이 안전하다. 둘째, eyeballing a mini-KG 단계를 생략하지 않아야 한다. 정량 지표만으로는 잡히지 않는 노드 라벨 비일관성·관계 세분화 문제가 그래프 시각화에서 즉시 드러난다. KG는 결국 그래프로 사용되므로, 그래프 형태로 점검하지 않으면 downstream에서야 문제가 발견된다.</p>
<p>셋째, 보안 요구가 강한 도메인이라면 LLM 직접 사용 대신 <strong>LLM을 데이터 어노테이션 가속기로 활용하는 우회 경로</strong>를 검토한다. LLM으로 pre-annotation한 데이터를 사람이 검수해 전통적 NLP 모델 학습에 사용하면, on-premise 배포 가능한 모델과 LLM의 transfer learning 이점을 동시에 얻을 수 있다. Hyperscaler(AWS, Azure, GCP)를 통한 LLM 호출이 가능한 환경이라면 일부 인프라 부담이 완화된다.</p>
<blockquote>
<p>결론: 비정형 텍스트에서 KG를 구축하는 작업의 본질은 모델 선택이 아니라 task 정의의 명확성이다. LLM은 진입 비용을 낮췄지만, 도메인 클래스 목록·정밀한 task 기술·대표 예시라는 세 축은 여전히 사람의 몫으로 남는다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Knowledge Graphs and LLMs in Action. Manning, 2024 — 
Chapter 5: Extracting domain-specific knowledge from unstructured data</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[단순 네트워크에서 다중 소스 통합으로]]></title>
            <link>https://velog.io/@tasker_dev/%EB%8B%A8%EC%88%9C-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%97%90%EC%84%9C-%EB%8B%A4%EC%A4%91-%EC%86%8C%EC%8A%A4-%ED%86%B5%ED%95%A9%EC%9C%BC%EB%A1%9C</link>
            <guid>https://velog.io/@tasker_dev/%EB%8B%A8%EC%88%9C-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC%EC%97%90%EC%84%9C-%EB%8B%A4%EC%A4%91-%EC%86%8C%EC%8A%A4-%ED%86%B5%ED%95%A9%EC%9C%BC%EB%A1%9C</guid>
            <pubDate>Sun, 03 May 2026 07:25:07 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>단일 출처 그래프는 정보를 <strong>저장</strong>하지만, 다중 소스 통합 KG는 정보를 <strong>연결</strong>한다.</p>
</blockquote>
<h2 id="1-챕터의-위치와-thesis">1. 챕터의 위치와 thesis</h2>
<p>이전까지의 논의가 단일 도메인·단일 출처에서 knowledge graph(KG)를 구축하는 방법에 머물렀다면, 이 챕터는 이질적인 데이터셋을 하나의 동질 그래프(homogeneous graph)로 통합하고, 그 위에서 LLM 기반 intelligent advisor system(IAS)을 운용하기 위한 분석 기법을 다룬다. 핵심 thesis는 다음과 같다. <strong>다중 소스 KG의 가치는 그래프를 키우는 데 있지 않고, 서로 다른 출처의 entity와 relationship을 일관된 의미 단위로 정렬(align)하는 데 있다.</strong></p>
<p>이전 편에서 vector retrieval이 chunk 간 유사도에 머무는 한계를 보였다면, multisource KG는 출처 간 cross-reference를 명시적 edge로 보존한다는 점에서 본질적으로 다르다. 즉, retrieval의 단위가 &quot;비슷한 텍스트&quot;에서 &quot;검증된 관계&quot;로 옮겨간다.</p>
<h2 id="2-다중-소스-통합-파이프라인">2. 다중 소스 통합 파이프라인</h2>
<p>구조화·반구조화 데이터를 하나의 그래프로 합치는 과정은 단순 merge가 아니라 다단계 파이프라인이다. 이 파이프라인의 각 단계는 직렬로 의존하므로, 어느 한 단계의 품질 저하가 후속 분석 전체로 전파된다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/2da0f60c-095d-41e3-98c4-00a389e21b99/image.png" alt=""></p>
<p>각 단계의 역할은 다음과 같이 정리된다.</p>
<table>
<thead>
<tr>
<th>단계</th>
<th>입력</th>
<th>핵심 작업</th>
<th>실패 시 영향</th>
</tr>
</thead>
<tbody><tr>
<td>Schema alignment</td>
<td>이질적 스키마</td>
<td>속성·타입·관계 명명 일치화</td>
<td>동일 개념이 별도 노드로 분리</td>
</tr>
<tr>
<td>Entity resolution</td>
<td>정렬된 레코드</td>
<td>동일 실체 식별·식별자 매칭</td>
<td>중복 entity, 단절된 subgraph</td>
</tr>
<tr>
<td>Quality validation</td>
<td>병합 후보</td>
<td>결측·충돌·일관성 검증</td>
<td>잘못된 edge가 분석 결과 왜곡</td>
</tr>
<tr>
<td>Post-processing</td>
<td>검증 통과 그래프</td>
<td>entity/relationship 병합</td>
<td>그래프 밀도·지표 신뢰도 저하</td>
</tr>
</tbody></table>
<p>entity resolution은 단순 문자열 매칭을 넘어 식별자(identifier) 수준의 reconciliation을 요구한다. 같은 단백질이 데이터셋마다 다른 ID로 등록되어 있는 경우, ID 매핑 테이블 없이는 두 노드가 별개로 남는다.</p>
<h2 id="3-분석-기법-클러스터링과-subgraph-지표">3. 분석 기법: 클러스터링과 subgraph 지표</h2>
<p>통합된 KG의 품질은 시각 점검만으로 판단되지 않는다. 정량적 분석 지표가 그래프의 구조와 완성도를 평가하는 도구가 된다.</p>
<h3 id="31-클러스터링-알고리즘">3.1 클러스터링 알고리즘</h3>
<p>전역 구조와 커뮤니티 조직을 파악하기 위해 두 가지 알고리즘이 사용된다.</p>
<table>
<thead>
<tr>
<th>알고리즘</th>
<th>목적</th>
<th>산출물</th>
<th>해석</th>
</tr>
</thead>
<tbody><tr>
<td>Weakly Connected Components (WCC)</td>
<td>연결 컴포넌트 식별</td>
<td>분리된 subgraph 집합</td>
<td>통합 누락·고립 영역 탐지</td>
</tr>
<tr>
<td>Louvain</td>
<td>모듈성 기반 community detection</td>
<td>밀집 군집 분할</td>
<td>도메인 내 기능 그룹화 발견</td>
</tr>
</tbody></table>
<p>WCC는 &quot;그래프가 얼마나 연결되어 있는가&quot;라는 거시적 질문에, Louvain은 &quot;연결된 그래프 내부에 어떤 응집 구조가 있는가&quot;라는 미시적 질문에 답한다. 두 결과는 상호보완적이며, 다중 소스 통합의 성공 여부를 판단하는 1차 지표 역할을 한다.</p>
<h3 id="32-subgraph-품질-지표">3.2 Subgraph 품질 지표</h3>
<pre><code class="language-python">def evaluate_subgraph(G, subgraph_nodes):
    sub = G.subgraph(subgraph_nodes)
    n = sub.number_of_nodes()
    m = sub.number_of_edges()

    # density: 밀도
    density = (2 * m) / (n * (n - 1)) if n &gt; 1 else 0

    # conductance: 외부와의 연결 비율
    boundary_edges = sum(
        1 for u, v in G.edges()
        if (u in subgraph_nodes) ^ (v in subgraph_nodes)
    )
    internal_edges = m
    conductance = boundary_edges / (2 * internal_edges + boundary_edges) \
                  if (internal_edges + boundary_edges) &gt; 0 else 0

    # 최대 연결 컴포넌트의 상대 크기
    largest_cc = max(nx.connected_components(sub), key=len, default=set())
    relative_lcc = len(largest_cc) / n if n &gt; 0 else 0

    return density, conductance, relative_lcc</code></pre>
<ul>
<li><strong>density</strong>: subgraph 내부 연결의 조밀함. 너무 낮으면 entity resolution 누락 가능성을 시사한다.</li>
<li><strong>conductance</strong>: subgraph가 외부와 얼마나 연결되어 있는지. 낮을수록 자기 완결적 community다.</li>
<li><strong>largest connected component의 상대 크기</strong>: 1에 가까울수록 단일 거대 연결 구조. 이 값이 작으면 그래프가 파편화되어 있다.</li>
</ul>
<h3 id="33-경로-기반-고급-지표-dwpc">3.3 경로 기반 고급 지표: DWPC</h3>
<p>Degree-Weighted Path Count(DWPC)는 단순 경로 개수 계산의 한계를 보완한다. 단순 path counting은 hub 노드(연결이 매우 많은 노드)를 거치는 경로를 과대평가한다. DWPC는 경로상의 각 노드 degree에 가중치를 부여해 hub 효과를 감쇠시킨다.</p>
<pre><code class="language-python">def dwpc(paths, degrees, w=0.4):
    # w: damping exponent. 일반적으로 0.3~0.5
    score = 0.0
    for path in paths:
        path_weight = 1.0
        for node in path[1:-1]:  # 중간 노드만 가중
            path_weight *= degrees[node] ** (-w)
        score += path_weight
    return score</code></pre>
<p><code>w</code> 값이 클수록 hub 페널티가 강해진다. 이 지표는 entity 간 관련성(relevance)을 평가할 때 단순 도달 가능성보다 훨씬 풍부한 신호를 제공한다.</p>
<h2 id="4-대표-kg-사례">4. 대표 KG 사례</h2>
<p>다중 소스 통합 기법의 실증 사례로 다음 KG들이 testbed 역할을 한다.</p>
<table>
<thead>
<tr>
<th>KG</th>
<th>도메인</th>
<th>통합 출처 성격</th>
</tr>
</thead>
<tbody><tr>
<td>Hetionet</td>
<td>생의학 (질병·약물·유전자)</td>
<td>29개 공개 데이터베이스 통합</td>
</tr>
<tr>
<td>PPI network</td>
<td>단백질 상호작용</td>
<td>실험·문헌 기반 상호작용</td>
</tr>
<tr>
<td>Clinical Knowledge Graph (CKG)</td>
<td>임상·프로테오믹스</td>
<td>환자 데이터·문헌·온톨로지</td>
</tr>
</tbody></table>
<p>이들 KG는 entity resolution, schema alignment, 분석 지표 적용을 모두 요구하는 복잡도를 가지므로 통합 기법의 검증 대상으로 적합하다.</p>
<h2 id="5-llm-기반-결과-해석">5. LLM 기반 결과 해석</h2>
<p>KG 분석은 정량 지표를 산출하지만, 그 자체로는 도메인 의미가 뚜렷하지 않다. density 0.12, conductance 0.31 같은 숫자가 어떤 임상적·연구적 함의를 갖는지는 별도의 해석 단계가 필요하다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/b1a4e579-0b40-49f9-a6e3-388096784e6c/image.png" alt=""></p>
<p>LLM은 이 단계에서 정량 지표를 도메인 가설로 번역하는 역할을 한다. &quot;이 community는 평균 대비 density가 높고 특정 단백질 cluster를 공유한다&quot;는 분석 결과를 &quot;공통 신호 경로의 후보군&quot;이라는 연구 가설로 변환하는 식이다.</p>
<h2 id="6-한계와-트레이드오프">6. 한계와 트레이드오프</h2>
<p>다중 소스 통합 KG는 표현력 측면에서 강력하지만, 다음 한계가 따른다.</p>
<ol>
<li><p><strong>Entity resolution의 정확도 천장</strong>: 동일 실체를 식별하는 작업은 본질적으로 휴리스틱·확률적 추론에 의존한다. 식별자가 없는 경우 문자열·맥락 기반 매칭이 사용되며, false merge(다른 실체를 합치는 오류)와 false split(같은 실체를 분리하는 오류)이 동시에 발생한다. 두 오류는 trade-off 관계에 있어 한쪽을 줄이면 다른 쪽이 늘어난다.</p>
</li>
<li><p><strong>지표의 도메인 의존성</strong>: density·conductance·DWPC 같은 지표는 그래프 구조의 통계적 속성일 뿐, 도메인 의미를 보장하지 않는다. 같은 density 값이 임상 KG와 소셜 네트워크에서 전혀 다른 의미를 갖는다. 지표 임계값을 일반화하려는 시도는 실패하기 쉽다.</p>
</li>
<li><p><strong>확장성 비용</strong>: 출처가 늘어날수록 schema alignment·entity resolution의 조합 폭발이 발생한다. n개 출처 통합 시 매핑 작업은 단순히 n에 비례하지 않고, 출처 간 페어와이즈 충돌 해소를 포함하면 빠르게 가중된다. 또한 후처리 병합 단계의 재계산 비용이 그래프 크기에 비선형적으로 증가한다.</p>
</li>
</ol>
<h2 id="7-실무-적용-시-고려사항">7. 실무 적용 시 고려사항</h2>
<p>실제 시스템에서 다중 소스 KG를 운용할 때 가장 먼저 결정해야 할 것은 <strong>entity resolution의 신뢰 임계값</strong>이다. 자동 병합 임계를 높게 잡으면 분리된 중복 entity가 남고, 낮게 잡으면 의미가 다른 노드가 합쳐져 분석 결과를 오염시킨다. 도메인 전문가의 검증 루프를 단계적으로 삽입하는 hybrid 방식이 안정적이다.</p>
<p>또한 KG는 일회성 산출물이 아니라 <strong>유지보수 대상</strong>이다. 출처 데이터의 스키마 변경, 신규 출처 추가, 기존 매핑 규칙의 수정이 누적되면서 그래프 일관성이 흔들린다. WCC·Louvain 결과를 정기적으로 비교해 구조적 drift를 감지하는 모니터링 체계가 필요하다. LLM 기반 해석 모듈을 운용할 경우, 정량 지표와 도메인 어휘 사이의 매핑 규칙을 명시적으로 두지 않으면 해석이 hallucination에 취약해진다.</p>
<blockquote>
<p>결론: 다중 소스 KG의 품질은 그래프 크기가 아니라 entity resolution과 정량 지표 검증 루프의 정밀도로 결정된다.</p>
</blockquote>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Knowledge Graphs and LLMs in Action. Manning, 2024 — Chapter 4: From simple networks to multisource integration</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[온톨로지로부터 첫 Knowledge Graph 구축하기]]></title>
            <link>https://velog.io/@tasker_dev/%EC%98%A8%ED%86%A8%EB%A1%9C%EC%A7%80%EB%A1%9C%EB%B6%80%ED%84%B0-%EC%B2%AB-Knowledge-Graph-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@tasker_dev/%EC%98%A8%ED%86%A8%EB%A1%9C%EC%A7%80%EB%A1%9C%EB%B6%80%ED%84%B0-%EC%B2%AB-Knowledge-Graph-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 02 May 2026 12:41:52 GMT</pubDate>
            <description><![CDATA[<h2 id="1-kg-구축이-복잡한-이유-의미적-heterogeneous-문제">1. KG 구축이 복잡한 이유: 의미적 heterogeneous 문제</h2>
<p>Knowledge Graph 구축의 본질적 난점은 단순한 포맷 변환이 아니라 <strong>의미(semantics)의 통합</strong>에 있다. 책에서는 이 난점을 네 층위로 분해한다.</p>
<ul>
<li><strong>포맷 차이</strong>: XML, CSV, JSON 등 직렬화 형식이 제각각이다.</li>
<li><strong>저장 기술 차이</strong>: 관계형 DB와 문서지향 DB의 모델이 다르다.</li>
<li><strong>구문(syntax) 차이</strong>: 같은 날짜를 <code>2022-08-09</code>로 쓰기도, <code>9 August 2022</code>로 쓰기도 한다.</li>
<li><strong>의미(semantics) 차이</strong>: 가장 까다로운 층위다. 헬스케어 도메인의 예가 분명하다. <code>type 2 diabetes</code>와 <code>ketosis resistant diabetes</code>는 다른 표현이지만 동일 개념이며, 반대로 <code>PE</code>라는 동일 약어는 <code>physical examination</code>일 수도 <code>pulmonary embolism</code>일 수도 있다. 거기에 <code>necrosis</code>와 <code>lobular necrosis</code>처럼 정보의 granularity 차이까지 더해진다.</li>
</ul>
<p>이 네 층위 중 앞의 셋은 ETL 도구로 흡수 가능하지만, 의미 차이는 별도의 reference schema 없이는 풀리지 않는다. 그 reference 역할을 하는 것이 ontology다.</p>
<h2 id="2-ontology-heterogeneous-정보의-중재자">2. Ontology: heterogeneous 정보의 중재자</h2>
<p>Ontology는 데이터에 등장하는 entity의 formal name, property, category, relationship을 표준 vocabulary로 모델링한 것이다. 책에서 ontology는 &quot;semantically heterogeneous information의 intermediary&quot;로 정의된다. 즉 각 데이터 소스의 local schema를 ontology의 reference schema로 mapping하는 구조다.</p>
<p>이 mapping 구조를 도식화하면 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/19b6ced6-eed5-4513-91f8-82145c9af3a3/image.png" alt=""></p>
<p>KG의 목표는 이렇게 단일 view로 융합된, <strong>unified·well-grounded·meaningful</strong>한 표현을 얻는 것이다. 개별 데이터 조각이 일관된 view 안에서 의미를 갖게 되어야 한다.</p>
<h2 id="3-kg-구축-프로세스-crisp-dm의-변형">3. KG 구축 프로세스: CRISP-DM의 변형</h2>
<p>책은 데이터 마이닝 표준 방법론인 CRISP-DM을 KG 구축용으로 각색해 제시한다. 단계별 책임이 명확히 분리된다.</p>
<table>
<thead>
<tr>
<th>Phase</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td>Data Understanding</td>
<td>정의된 목표에 부합하는 데이터를 식별</td>
</tr>
<tr>
<td>Data Preparation</td>
<td>현재 scope에서 관련 데이터를 추려 다음 단계 입력으로 만든다</td>
</tr>
<tr>
<td>Modeling</td>
<td>ML task 알고리즘을 정의</td>
</tr>
<tr>
<td>KG Model Creation/Update</td>
<td>정의된 데이터·모델 기반으로 KG를 생성·갱신</td>
</tr>
<tr>
<td>Evaluation</td>
<td>구축된 KG가 목표를 충족하는지 검증</td>
</tr>
<tr>
<td>Deployment</td>
<td>운영 환경에 적용</td>
</tr>
</tbody></table>
<p>이 절차는 일회성 빌드가 아니라 <strong>순환 구조</strong>라는 점이 중요하다. 도메인 이해가 깊어지거나 데이터 소스가 추가될 때마다 ontology 자체가 진화하고, 그에 따라 후속 phase가 다시 돌아간다.</p>
<h2 id="4-사례-hpo-기반-희귀질환-진단-지원">4. 사례: HPO 기반 희귀질환 진단 지원</h2>
<p>책의 실제 use case는 임상 현장이다. 임상의가 진단을 내리는 과정에는 명확한 검사 결과뿐 아니라 <strong>gray area</strong>가 포함된다. 이 불확실성을 다루기 위해 두 속성을 가진 structured knowledge base가 필요하다.</p>
<ul>
<li><strong>phenotype 도메인의 contextual description</strong>: 같은 장기·계통과 관련된 phenotypic anomaly가 명시적으로 연결되어 있어야 한다.</li>
<li><strong>phenotypic anomaly와 disease 간 관계 추적성</strong>: 임상의가 연결의 근거 출처에 도달할 수 있어야 한다.</li>
</ul>
<p>이 요구를 만족시키는 것이 <strong>HPO (Human Phenotype Ontology)</strong> 와 그 annotation 데이터다. HPO는 phenotype 계층을 제공하고, annotation 파일은 phenotypic abnormality와 disease를 연결한다.</p>
<h2 id="5-rdf-vs-lpg-두-갈래-기술-선택">5. RDF vs LPG: 두 갈래 기술 선택</h2>
<p>KG를 구축하는 가장 대표적 두 모델이 <strong>RDF (Resource Description Framework)</strong> 와 <strong>LPG (Labeled Property Graph)</strong> 다. 둘은 같은 그래프를 표현하지만 설계 철학이 갈린다.</p>
<h3 id="rdf의-구조">RDF의 구조</h3>
<p>RDF는 W3C가 정의·관리하는 web 기반 데이터 교환 표준이다. 모든 statement가 <strong>subject–predicate–object의 triple</strong>로 구성된다. Subject는 노드, predicate은 edge, object는 또 다른 노드다. KG 전체가 이런 statement의 집합으로 모델링된다. 도메인 ontology 작성에 특히 적합하다.</p>
<h3 id="lpg의-구조">LPG의 구조</h3>
<p>LPG는 노드와 relationship에 <strong>key–value pair</strong>를 직접 붙일 수 있는 모델이다. 그래프 traversal과 path 분석에 최적화되어 있고, 저장·접근 효율이 강점이다.</p>
<h3 id="핵심-차이-메타데이터의-부착-위치">핵심 차이: 메타데이터의 부착 위치</h3>
<p>RDF에서 relationship(triple)은 <strong>전역적으로 정의</strong>된다. 즉 어떤 predicate에 메타데이터를 붙이면 그래프 전체에서 해당 relationship의 모든 instance에 영향이 간다. LPG는 반대로 <strong>edge별 unique 속성</strong>을 허용하므로 개별 relationship에 따로 메타데이터를 부여할 수 있다.</p>
<p>이 차이를 보완하기 위해 양쪽 모두 진화 중이다. RDF 측은 <strong>named graph</strong>로 triple 묶음에 컨텍스트를 부여하고, <strong>RDF-star</strong>로 edge에 property를 붙이는 길을 열고 있다. LPG 측은 Neo4j의 <strong>Neosemantics 플러그인</strong>으로 OWL·RDFS·SKOS 같은 RDF vocabulary를 LPG 위에서 활용 가능하게 하며, Amazon Neptune은 RDF 데이터에 Cypher를 실행하는 우회 경로를 제공한다.</p>
<table>
<thead>
<tr>
<th>비교 축</th>
<th>RDF</th>
<th>LPG</th>
</tr>
</thead>
<tbody><tr>
<td>표준화</td>
<td>W3C 표준</td>
<td>벤더별 구현</td>
</tr>
<tr>
<td>기본 단위</td>
<td>Triple (subject–predicate–object)</td>
<td>Node·Relationship + key-value</td>
</tr>
<tr>
<td>메타데이터 부착</td>
<td>Predicate 단위 (전역)</td>
<td>Edge별 (지역)</td>
</tr>
<tr>
<td>강점</td>
<td>Knowledge representation, ontology 작성</td>
<td>Traversal·path 분석 성능, 저장 효율</td>
</tr>
<tr>
<td>Edge property</td>
<td>RDF-star로 부분 지원</td>
<td>Native 지원</td>
</tr>
<tr>
<td>추론</td>
<td>OWL 기반 inference 자연스러움</td>
<td>추가 도구 필요 (예: Neosemantics)</td>
</tr>
<tr>
<td>컨텍스트 표현</td>
<td>Named graph</td>
<td>Relationship property</td>
</tr>
</tbody></table>
<h3 id="rdf-내부의-세-가지-모델링-전략">RDF 내부의 세 가지 모델링 전략</h3>
<p>RDF가 edge에 속성을 붙이지 못한다는 한계를 우회하기 위해 세 가지 패턴이 통용된다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/0b390314-ec98-465d-8343-a20393355161/image.png" alt=""></p>
<ul>
<li><strong>N-ary relations</strong>: 데이터를 연결하는 새로운 concept을 만든다. 보통 <code>_:Annotation</code> 같은 blank node로 표현되는데, 이는 전역 식별자 없이 관련 정보를 묶기 위한 unnamed resource다. ontology 진화에 따른 backward compatibility가 약점이 될 수 있다.</li>
<li><strong>Named graphs</strong>: triple에 네 번째 요소를 추가해 statement가 어느 sub-graph에 속하는지 명시한다. provenance·context 표현에 강력하지만, named graph 수가 많아지면 저장·교환 효율이 떨어지고 fine-grained update가 어려워진다.</li>
<li><strong>RDF-star</strong>: triple 자체에 property를 붙일 수 있게 한 RDF 확장이다. 더 읽기 쉬운 SPARQL 쿼리가 가능하지만, query 성능이 아직 부족하고 새 syntax 확장이라는 특성상 RDF 엔진별 구현이 필요해 채택이 제한적이다.</li>
</ul>
<p>이 외에 reification이나 singleton property 같은 방법도 존재하지만, 실무에서는 named graph와 n-ary relation이 더 자주 선택된다.</p>
<h2 id="6-neo4j로-hpo-kg-만들기-구축-단계">6. Neo4j로 HPO KG 만들기: 구축 단계</h2>
<p>책은 LPG 진영의 Neo4j와 Neosemantics 플러그인을 사용해 HPO KG를 실제로 구축한다. 절차는 두 단계다.</p>
<ol>
<li><strong>Ontology 로딩</strong>: HPO vocabulary 자체를 그래프에 적재</li>
<li><strong>Annotation 데이터 ingest</strong>: ontology를 reference로 삼아 disease–phenotype 연결을 채워 넣기</li>
</ol>
<p>전체 파이프라인을 정리하면 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/tasker_dev/post/7497e420-a894-401b-8ed3-b4dddfab8e8e/image.png" alt=""></p>
<p>특히 <code>HAS_PHENOTYPIC_FEATURE</code> relationship에 속성을 붙이는 단계는 LPG의 강점을 잘 보여준다. 다음 의사코드는 TSV의 각 row에서 값이 존재하는 경우에만 property를 부여하는 패턴이다.</p>
<pre><code class="language-cypher">LOAD CSV WITH HEADERS FROM &#39;file:///annotations.tsv&#39; AS row
FIELDTERMINATOR &#39;\t&#39;
MATCH (d:HpoDisease {id: row.disease_id})
MATCH (p:HpoPhenotype {id: row.phenotype_id})
MATCH (d)-[r:HAS_PHENOTYPIC_FEATURE]-&gt;(p)
FOREACH (_ IN CASE WHEN row.frequency IS NOT NULL THEN [1] ELSE [] END |
  SET r.frequency = row.frequency
)
FOREACH (_ IN CASE WHEN row.onset IS NOT NULL THEN [1] ELSE [] END |
  SET r.onset = row.onset
)</code></pre>
<p><code>FOREACH</code> 블록은 해당 column 값이 null이 아닐 때만 property를 set하는 가드다. 결과적으로 missing data에 resilient하면서 기존 값을 null로 덮어쓰지 않는다. RDF의 전역 predicate 모델에서는 이런 per-edge 부분 갱신이 부자연스럽다.</p>
<h2 id="7-inference-kg의-진짜-가치">7. Inference: KG의 진짜 가치</h2>
<p>KG의 가장 강력한 활용은 저장된 정보를 그대로 조회하는 것이 아니라, <strong>logical rule 기반의 deductive reasoning</strong>으로 implicit 정보를 끌어내는 것이다.</p>
<p>예를 들어 &quot;내분비계 이상(<code>HP:0000818</code>)을 동반하는 disease는 무엇인가&quot;라는 질의를 생각해보자. 이 phenotype에 직접 annotation된 disease만 반환하면 답이 충분치 않다. 임상의는 갑상선 등 더 구체적인 hierarchy 하위의 phenotype까지 묶어 보고 싶어한다. HPO의 계층 구조가 ontology 안에 박혀 있기 때문에, subclass를 따라 traversal하는 단일 query로 implicit한 연결까지 회수할 수 있다.</p>
<p>이 지점이 단순 검색·문서 retrieval과 KG가 갈리는 지점이다. 계층·관계가 schema에 명시적으로 표현되어 있기 때문에, 질의 시점에 추론이 가능해진다.</p>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>Knowledge Graphs and LLMs in Action. Manning, 2024 — Chapter 3: Create your first knowledge graph from ontologies</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>