728x90
반응형

Knowledge Graph 생성 전략 및 방법

Neo4j는 GenAI 생태계에서 지식 관리의 두 가지 주요 구성 요소인 구성과 검색에 기여하고 있어요. 궁극적으로 이러한 구성 요소는 Neo4j를 GenAI 애플리케이션을 위한 이상적인 컨텍스트 공급자로 만들어주죠. 이 컨텍스트 계층은 메모리 관리, 에이전트 도구 저장소 또는 도메인 그래프 분석과 같은 다양한 애플리케이션에 사용될 수 있어요. 이번 글에서는 섭취와 Knowledge Graph 세대에 대해 알아볼 거예요. 이는 비정형 데이터와 정형 데이터를 연결하는 그래프를 생성하고 강화해서 AI 애플리케이션을 위한 컨텍스트 레이어를 제공할 수 있는 프로세스랍니다.

GenAI의 지식 관리 구성요소

Knowledge Graph는 컨텍스트 계층으로서 다른 데이터 저장소에 비해 많은 이점을 제공해요. 그래프를 사용하면 구조화된 데이터와 구조화되지 않은 데이터를 사용하는 동시에 Vector Embedding도 활용할 수 있어요. 또한 엔터티 간의 관계가 명시적으로 저장되므로 복잡한 필터링 및 순회를 수행할 수 있죠. 그래프를 사용하여 컨텍스트 레이어를 구축함으로써 GraphRAG 다른 데이터 저장소에서는 불가능한 검색 패턴을 구현할 수 있어요.

GraphRAG 방법은 검색 프로세스에서 그래프 순회를 구현하는 방법이에요.

이 글은 첨부파일로 작성되었어요 PubMed 지식 그래프 생성 예시 프로젝트. 여기에 자세히 설명된 많은 예시와 프로세스는 이 프로젝트에서 확장되고 시연되고 있어요. 이 저장소는 활성화되어 있으며 더 많은 기능을 추가하기 위해 개발 중이랍니다.

왜 이것을 구축해야 할까요?

Knowledge Graph는 벡터 저장소보다 사전에 더 많은 작업이 필요하지만 다른 방법으로는 얻을 수 없는 귀중한 통찰력을 제공해요. 프로덕션 환경에서는 당면한 작업을 해결하는 데 필요한 컨텍스트에 따라 고유한 이점을 제공하는 다양한 데이터베이스가 있을 수 있어요. Knowledge Graph는 기본 데이터를 의미 있는 방식으로 연결하는 컨텍스트를 제공하는 데 탁월하며, 벡터 저장소는 고속 유사성 검색 쿼리에 탁월하죠.

아래 예에서 언제 사용하고 싶은지 확인할 수 있어요 벡터 검색 (전통적인 RAG) 대 그래프 순회(GraphRAG) 의료 Q&A 애플리케이션에서 사용 사례에 대한 컨텍스트를 수집해요.

메트포르민에는 어떤 부작용이 있나요?

입력 질문과 의미상 가장 유사한 텍스트 콘텐츠를 찾는 벡터 유사성 검색을 수행할 수 있어요. 반환된 텍스트에는 Metformin과 그 부작용에 대해 설명할 가능성이 높지만 Metformin에 대한 기타 정보가 포함되거나 다양한 약물과 그 부작용이 언급될 수도 있죠.

Similarity search retrieval visualization
유사성 검색 검색 시각화

대답은 반환된 최근접 이웃 어딘가에 있을 가능성이 높으며, 요약 단계를 수행하는 LLM이 유능하다면 추가 필터링을 포함할 필요가 없을 거예요.

GraphRAG

내 환자 John Doe를 위해 Metformin은 무엇을 합니까?

이 쿼리는 더 어렵네요. 이제 우리는 Metformin과 John Doe의 관계를 이해해야 해요. 환자 John Doe와 약물 Metformin에 그래프 순회를 고정할 수 있어요. 그런 다음 John Doe의 인구통계 정보와 일치하는 샘플 인구통계에 대한 Metformin의 효과를 조사한 연구를 찾을 수 있죠. 마지막으로 이러한 항목을 연구 기사의 텍스트와 함께 컨텍스트로 에이전트에 반환할 수 있어요.

example graph traversal
그래프 순회 예시

이러한 유형의 검색은 벡터 저장소에서는 불가능하며 관계가 없기 때문에 그래프가 아닌 다른 데이터베이스에서는 어렵답니다.

예시 아키텍처

Knowledge Graph 생성 파이프라인을 설계하는 방법은 정말 다양해요. 여기서는 기존 구조화된 데이터 그래프와 함께 문서에서 Knowledge Graph를 생성하는 예시를 자세히 살펴볼 거예요. 사용자가 그래프와 상호작용할 수 있는 간단한 검색 프로세스도 함께 볼 수 있답니다.

an example graphrag architecture
Knowledge Graph 기반 솔루션 아키텍처 예시

가장 먼저 구조화된 데이터를 Neo4j 인스턴스에 수집하는 것부터 시작할게요. 이건 데이터를 변환하고 관계형 테이블을 *node* 및 *relationship*으로 그래프에 로드하는 표준 수집 프로세스예요.

그다음 Knowledge Graph 생성 프로세스를 진행할 거예요. 이 과정은 구조화되지 않은 데이터 모음(여기서는 텍스트 문서 모음)으로 시작하죠. 그런 다음 이 문서들을 분할 및 청크 프로세스를 거쳐 텍스트를 적당한 크기의 청크로 나눠요. 이렇게 나뉜 청크들은 두 개의 추가 프로세스를 거치게 되는데요. 텍스트를 LLM에 전달해서 사전 정의된 엔터티를 JSON 형식으로 추출하도록 요청해서 텍스트에서 엔터티를 추출할 수 있어요. 또, 텍스트를 임베딩 모델에 전달해서 각 청크에 대한 *Vector Embedding*을 생성할 수도 있답니다.

이제 텍스트, 엔터티, 임베딩, 그리고 다른 메타데이터가 기존 구조화된 데이터와 함께 Neo4j에 로드돼요. 구조화된 데이터에 존재하는 텍스트에서 엔터티를 추출하는 경우, 문서는 수집 시 자동으로 구조화된 그래프와 연결되죠.

여기 자세히 설명된 검색 프로세스는 사용자 질문으로 시작해요. 그런 다음 텍스트 임베딩 전반에 걸쳐 유사성 검색을 수행해서 관련성 있는 `Chunk` *node* 풀을 찾아요. 이 `Chunk`로부터 그래프에 유지한 *relationship*을 통해 관심 있는 엔터티를 탐색할 수 있어요. 그런 다음 추론을 위해 LLM에 대한 컨텍스트로 찾은 엔터티와 함께 텍스트를 반환할 수 있죠. 텍스트에서 추출되는 엔터티를 제어할 수 있고 기존 구조화된 데이터에 *node* 및 *relationship*에 대한 링크가 있기 때문에 반환된 컨텍스트가 해결하려는 사용 사례와 관련이 있다고 확신할 수 있어요.

Knowledge Graph 구조

GenAI 공간의 Knowledge Graph에는 어휘와 도메인, 이렇게 두 가지 주요 구성 요소가 있어요. 어휘 하위 그래프에는 문서의 구조가 포함되고, 도메인 하위 그래프에는 추출된 엔터티 그래프와 선택적으로 기존의 구조화된 데이터 그래프가 포함되죠. 아래 *스키마*는 PubMed 기사, 기사 항목, 기존 환자 여정 데이터로 구성된 Knowledge Graph를 위한 거예요.

결합된 어휘 및 도메인 그래프 데이터 모델

어휘

어휘 구성 요소에는 문서의 구조가 포함되어 있어요. 위의 어휘 그래프는 Unstructured에서 사용하는 데이터 모델을 기반으로 하는데요. 여기서 `Document` *node*는 전체 문서를 나타내요. 이 *node*에는 문서 제목 및 소스 위치와 같은 속성이 포함되어 있죠. `Chunk` *node*는 `Document` *node*에 연결되어 순서를 유지해요. `NEXT_CHUNK` *relationship*을 통해서요. 각 `Chunk`는 텍스트와 선택적으로 포함 속성을 포함하고 있답니다.

이 구현에서는 `TextElement`, `ImageElement`, 그리고 `TableElement` *node*도 사용하고 있어요. 각각 추출된 텍스트, 이미지, 테이블에 대한 정보가 들어있죠. `TextElements`는 Unstructured의 파티셔닝 논리에 따라 식별되는 텍스트 콘텐츠의 가장 작은 단위를 포함해요. 이러한 각 *node*의 정보는 집계되어 전체 `Chunk` *node*를 형성하는 텍스트가 되는 거죠.

어휘 그래프는 `HAS_ENTITY` *relationship*을 통해 도메인 그래프에 연결되는데요. 이 *relationship*은 청크 *node*와 여기에서 추출된 엔터티 사이를 연결해줘요.

엔터티 도메인

엔터티 하위 그래프에는 추출 *스키마*에서 선언한 엔터티와 *relationship*이 포함되어 있어요. 이 하위 그래프에는 Knowledge Graph를 통해 다루고자 하는 사용 사례와 관련된 정보가 있어야 하죠. 이 경우 문서에서 연구 모집단, 치료군, 의학적 상태, 임상 결과 및 약물을 추출하고 싶어요.

이러한 엔터티 중 일부는 환자 이동 데이터의 구조화된 데이터 그래프에 이미 존재해요. 질병, 임상 결과, 약물을 추출함으로써 구조화되지 않은 문서를 구조화된 환자 여정 데이터에 자동으로 연결할 수 있답니다.

환자 여정 도메인

환자 여정 그래프에는 독립적인 기존 구조화된 데이터가 포함되어 있어요. 이것이 우리가 연구 문서를 통해 보강하고 향상시키고 싶은 그래프죠. 추출 프로세스를 위한 항목 그래프 *스키마*를 생성할 때 다음 몇 가지 사항을 고려해야 해요.

  • 구조화된 데이터에 이미 존재하는 항목 중에서 추출할 수 있는 항목
  • 구조화된 데이터의 *node*를 항목 그래프의 항목에 연결하는 방법

때로는 구조화된 데이터 그래프에 존재하는 항목을 정확하게 추출할 수 없는 경우가 있어요. 이 경우 엔터티 연결을 수행해서 다음에서 볼 수 있는 것처럼 두 하위 그래프에 걸쳐 있는 *relationship*을 생성할 수 있죠. `IN_STUDY_POPULATION` *relationship*이 그 예시인데요. 이는 구성원의 특정 인구통계 정보를 포함하는 연구 모집단을 찾아요. 예를 들어, 연구 모집단에 40~60세 남성이 포함된 경우 50세 남성 구성원은 해당 연구 모집단과 *relationship*을 갖게 되는 거죠.

Knowledge Graph 생성

크게 보면 Knowledge Graph 생성 과정은 4단계로 진행돼요. 초기 데이터 처리 단계와 수집 단계가 있고, 결과 그래프에 대해 사후 처리를 수행하고, 마지막으로 추출을 검증하는 단계가 있죠.

The knowledge graph generation process
Knowledge Graph 생성 과정

데이터 처리

데이터 처리 단계는 텍스트 청크, 엔터티 추출, 텍스트 임베딩이라는 세 가지 하위 단계로 구성돼요. 텍스트 청크는 데이터를 적절한 크기로 나누는 역할을 하죠. 그 다음, 텍스트 청크는 미리 정의된 그래프 스키마에 따라 엔터티 추출 과정을 거쳐요. 이때 Large Language Model (LLM)은 텍스트를 분석해서 엔터티와 관계를 추출하는 역할을 담당해요. 텍스트 청크를 임베딩 모델에 전달해서 벡터 기반 유사성 검색을 위한 임베딩을 생성할 수도 있고요. 이렇게 텍스트 청크를 만들고 엔터티를 추출하고, 임베딩을 생성하면 이 정보를 데이터베이스에 저장할 수 있게 돼요.

The data processing step of knowledge graph generation
지식 그래프 생성의 데이터 처리 단계

이 데이터 처리 단계는 여러 방법으로 구현할 수 있어요. 다음은 각 단계에서 사용할 수 있는 라이브러리 및 서비스의 몇 가지 예시예요.

Unstructured

  • 문서 분할 및 청크

Pydantic

  • 그래프 스키마 정의

Instructor

  • LLM 요청
  • 응답 구문 분석

OpenAI

  • LLM 제공업체
  • 임베딩 모델 제공업체

데이터 수집

수집 단계에서는 데이터 처리 결과를 가져와서 데이터베이스에 로드해요. Neo4j에 모든 데이터를 저장하면 Neo4j가 Apache Lucene을 통해 벡터 인덱싱을 수행하므로, 통합된 공간에서 유사성 검색과 그래프 순회를 쉽게 할 수 있어요. 이렇게 하면 수집 코드와 쿼리 로직이 단순해지죠. 아니면, 일부 데이터에 대해서는 전용 벡터 저장소를 사용하는 방법도 있어요. 이렇게 하면 기본 벡터 저장소에서 유사성 검색을 수행하고, Neo4j에 벡터와 큰 텍스트 값을 저장하는 부담을 줄일 수 있죠. 만약 벡터 데이터베이스와 Graph Database로 데이터를 분리하기로 결정했다면, 벡터 저장소에 있는 엔터티의 ID 필드와 Neo4j의 Chunk 노드를 동기화해야 해요. 그래야 유사성 검색 결과를 가져와서 그래프 순회에 활용할 수 있거든요.

The ingestion step of knowledge graph generation
지식 그래프 생성의 수집 단계

Neo4j → 텍스트, 텍스트 메타데이터, 임베딩, 엔터티 및 관계

→ 텍스트, 텍스트 메타데이터, 임베딩

후처리

데이터가 Neo4j에 로드되면, 후처리 과정을 통해 엔터티를 확인하고 연결할 수 있어요. 이 작업은 Cypher 쿼리와 Neo4j Graph Data Science 알고리즘 (예: Node Similarity, 커뮤니티 감지) 또는 LLM을 사용해서 중복 노드를 찾거나 엔터티 연결 모델을 처리하는 방식으로 진행할 수 있어요.

The post processing and validation steps of knowledge graph generation
지식 그래프 생성의 후처리 및 검증 단계

Cypher 쿼리를 사용해서 특정 노드 레이블의 내용을 분석하고, 중복된 항목을 하나의 노드로 합칠 수 있어요. 예를 들어 Medication 노드를 살펴보면, 노드가 중복되었는지 확인하기 위해 분석할 수 있는 세 가지 속성이 있어요. 바로 name, genericName, 그리고 brandNames죠. 만약 Medication 노드의 genericName 속성이 같거나, 한 노드의 이름이 다른 노드의 genericName과 같다면, 이들은 동일한 엔터티를 나타내므로 통합해야 해요. 이 경우 brandNames 목록을 연결하고, 해결된 문제를 나타내기 위해 복제본에서 Medication 노드의 name을 선택하면 돼요.

확인

후처리가 완료되면 최종 결과 그래프를 검증할 수 있어요. 이는 후처리에 사용할 수 있는 것과 동일한 여러 방법을 사용해서 할 수 있죠. 간단하게 구현하는 방법은 어휘 및 엔터티 그래프의 `Node`와 `Relationship`에 대한 개수를 반환하는 Cypher `Query`를 사용하는 거예요. 그런 다음 검증 기능에 대한 기대치를 설정할 수 있어요. 예를 들어, `Relationship`이 없는 `Node`(고아 `Node`)를 확인하거나, 해당 `Node`의 예상 개수와 `Node`의 `Relationship`을 확인하고, 이 차이를 측정항목으로 반환할 수 있죠.

PubMed Knowledge Graph에 대한 검증 테이블 결과

위의 예시에서, 우리가 기대하는 것 중 일부는 치료군이 항상 최소한 하나의 약물, 임상 결과, 연구 모집단 및 의학적 상태를 포함해야 한다는 가정에 기초하고 있어요.

문서 파싱

`Knowledge Graph`의 어휘 구성 요소를 생성하는 데 유용한 라이브러리와 서비스가 정말 많아요. Unstructured는 가장 널리 사용되는 것 중 하나인데, 다양한 문서 유형에 대한 구문 분석 방법은 물론 이미지 및 테이블 추출과 같은 고급 서비스도 제공하죠. 어휘 그래프 생성에 대한 자세한 내용을 다룰 때 이 라이브러리를 참고 자료로 사용할 거예요. 더 자세한 내용은 GraphRAG.com에서도 확인할 수 있는데, Neo4j에서 관리하는 `Knowledge Graph` 생성 및 `GraphRAG` 콘텐츠 리소스 사이트예요.

추가 문서 구문 분석 라이브러리에는 다음이 포함되지만 이에 국한되지는 않습니다: Docling, LlamaParse, Amazon Textract, Google Document AI and Azure AI Document Intelligence.

핵심 어휘 데이터 모델

핵심 어휘 데이터 모델에는 두 개의 `Node`가 포함되어 있어요. DocumentChunk죠. 이 `Node`들은 PART_OF_DOCUMENT `Relationship`으로 연결되고, 일련의 Chunk `Node`는 NEXT_CHUNK `Relationship`에 의해 유지돼요. Document `Node`에는 제목, 위치 URL, 작성자 등의 정보가 포함될 수 있고, Chunk `Node`에는 필터링이나 인용에 사용하려는 텍스트, 텍스트 삽입, 페이지 번호 및 기타 메타데이터가 포함될 수 있어요.

The core lexical graph data model
핵심 어휘 그래프 데이터 모델

문서를 처리하려면 문서가 텍스트 형식인지 확인해야 해요. PDF와 같은 일부 문서는 OCR이나 VLM 분석과 같은 방법을 통해 텍스트로 변환해야 하죠. 문서 텍스트와 이미지를 구문 분석한 후에는 분할할 수 있는데, 여기에는 문서를 나눌 수 있는 가장 작은 텍스트 구성 요소로 나누는 작업이 포함돼요. 여기에는 제목, 목록, 그림 캡션, 일반 텍스트 등이 포함되죠. 그런 다음 구성 요소는 구성에 따라 집계되어 그래프에 수집될 `Chunk`를 생성해요.

PDF document processing pipeline using Unstructured
Unstructured를 사용한 PDF 문서 처리 파이프라인

Unstructured를 사용하는 경우, partition_pdf 기능을 사용해서 PDF 문서를 분할하고 `Chunk`로 만들 수 있어요. 아래는 사용된 구성이 포함된 Python 코드 예제예요.

partitioned_doc = partition_pdf("articles/pdf/example.pdf",
strategy="hi_res",
extract_images_in_pdf=True,
extract_image_block_types=["Image", "Table"],
extract_image_block_to_payload=True,
chunking_strategy="by_title",
combine_text_under_n_chars=200,
max_characters=1000,
multipage_sections=True)

위의 예에서는 PDF 구문 분석을 조정하기 위해 많은 인수를 정의해요. 우리는 strategy="hi_res" and extract_images_in_pdf=True로 설정했는데, 문서에서 이미지를 추출하고 싶기 때문이에요. 다른 전략 옵션은 다음과 같아요. "fast"는 텍스트 전용 문서에, "ocr_only"는 텍스트와 이미지가 포함된 문서에서 텍스트만 추출할 때, "vlm"은 비전 언어 모델을 사용하여 복잡한 필기 콘텐츠 또는 다국어 콘텐츠에서 추출할 때 사용하면 좋아요. 또한 이미지와 테이블의 Base64 인코딩을 추출하고 싶으므로 extract_image_block_types=[“Image”, “Table”] and extract_image_block_to_payload=True로 설정해야 해요. 우리는 chunking_strategy="by_title"로 설정했는데, 제목 구성 요소로 구분된 섹션을 식별하여 초기 청크를 생성하려고 시도하는 거죠. 다음을 사용하여 최종 청크의 크기를 정의할 수도 있어요. combine_text_under_n_chars=200 and max_characters=1000. 여기서 하한은 임의적이지만 상한은 평균 단락 길이가 1,000자임을 명시하는 Unstructured의 문서에서 나온 것이에요. 마지막으로 우리는 multipage_sections=True를 선언해서 여러 페이지에 걸쳐 있는 콘텐츠를 결합할 수 있도록 했어요.

이미지 + 표

검색된 컨텍스트를 보완하기 위해 문서에서 이미지와 테이블을 추출할 수 있어요. 구조화되지 않은 것은 파티셔닝 중에 이러한 구성 요소를 자체 구조화되지 않은 요소로 구문 분석한 다음 적절하게 쉽게 처리할 수 있도록 해주죠.

A lexical model containing Unstructured Elements from the Unstructured processing pipeline
구조화되지 않은 처리 파이프라인의 구조화되지 않은 요소를 포함하는 어휘 모델

이미지와 테이블을 외부 저장소에 저장하고 검색 중에 콘텐츠를 검색할 수 있는 포인터를 Neo4j에 생성하도록 선택할 수 있어요. 또는 이미지 콘텐츠의 Base64 인코딩을 생성하고 결과를 Neo4j의 node property로 저장할 수도 있죠. 그런 다음 검색 시 Base64 문자열을 이미지로 변환하고 이를 LLM 요청의 텍스트 콘텐츠와 함께 제공할 수 있어요. 테이블은 마크다운이나 HTML로도 추출할 수 있고요. 테이블을 텍스트 형식으로 추출하면 이 텍스트 문자열을 Neo4j에 node property로 저장할 수도 있어요.

유사성 기반 검색을 위해 이 콘텐츠를 삽입하는 방법을 고려할 때 몇 가지 옵션도 있어요. 공유 임베딩 공간에서 텍스트와 이미지에 대한 유사성 검색을 수행하기 위해 다음과 같은 다중 모드 임베딩 모델을 사용하여 처리할 수 있는데, 예를 들어 CLIP은 텍스트와 이미지를 삽입하죠. 또한 이미지와 표에 대한 텍스트 설명을 생성하여 삽입할 수도 있어요. 이 경우 임베딩 프로세스에는 텍스트 임베딩 모델만 사용하면 돼요. 그런 다음 결과 텍스트 설명을 해당 항목의 node property로 저장할 수 있어요. UnstructuredElement node를 만들거나 텍스트를 사용하여 Chunk node를 만들 수도 있고요. 어느 쪽이든 추가 컨텍스트를 위해 이미지와 함께 설명을 반환할 수 있어요.

Possible table and image node properties
가능한 테이블 및 이미지 node property

확장된 어휘 데이터 모델

핵심 어휘 데이터 모델은 그래프 디자인의 출발점 역할을 해요. 사용 사례에 필요한 모든 기능을 제공할 수 있지만 이 데이터 모델을 강화해야 하는 경우도 있죠.

문서의 구조를 더 잘 반영하기 위해 핵심 어휘 데이터 모델을 변경하고 싶을 수도 있어요. 시작하는 쉬운 방법은 청크를 페이지별로 구성하는 것이죠. 일반적으로 처리된 청크 메타데이터의 일부로 페이지 번호를 받아요. 이는 인용을 위해 저장하는 데 유용한 속성이지만 다음과 같이 구현할 수도 있어요. Page 그래프의 node. 이는 기본 문서 구조를 더 잘 반영하고 일부 검색 프로세스를 단순화하죠. 이 모델을 사용하면 데이터베이스에서 전체 페이지를 쉽게 재구성하고 검색할 수 있어요. 또한 Chunk node를 일치시키고 탐색하여 더 풍부한 컨텍스트를 위해 전체 페이지 텍스트를 찾는 기능도 있고요.

A page-based lexical data model
페이지 기반 어휘 데이터 모델

다음과 같은 다른 PDF 파서 PyMuPDF or 도클링, 각 페이지를 이미지로 자동 추출할 수 있으며 페이지의 각 경계 상자 좌표가 있는 각 요소를 추출할 수 있어요. 이는 원본 문서를 표시하고 원본 텍스트를 강조 표시하는 데 사용할 수 있으며, 이는 특히 복잡한 문서의 유효성을 검사하는 데 유용하죠.

페이지 기반 데이터 모델은 문서 내에 존재하는 섹션을 명시적으로 매핑하여 확장할 수 있어요. 이를 통해 한 번에 문서의 특정 섹션만 가져올 수 있는 보다 구체적인 검색 프로세스가 가능해지죠. 이는 일반적으로 연구 논문의 방법이나 토론 섹션과 같은 전체 섹션에 관심이 있는 경우 유용해요. 아래 예제 데이터 모델에서는 두 가지 모두를 볼 수 있어요. Section and Subsection node는 다음과 관계가 있을 수 있죠. Chunk node. 이는 일부 섹션에 하위 섹션이 없을 수 있기 때문이에요. Chunk node는 전체 섹션 콘텐츠에서 파생되고요.

A sections-based lexical data model
섹션 기반 어휘 데이터 모델

기본 문서 구조와 관련 없는 방식으로 어휘 데이터 모델을 확장할 수도 있어요. 상위-하위 데이터 모델은 애플리케이션에서 벡터 유사성 검색을 구현하거나 큰 텍스트 청크가 있는 경우 향상된 검색 결과를 제공할 수 있죠. 이 데이터 모델에서는 청크(부모)를 더 작은 청크(자식)로 나누고 부모 대신 이를 삽입해요. 여기서 더 작은 청크는 분할 프로세스 중에 위에서 설명한 텍스트 요소와 혼동되면 안 돼요. 검색하는 동안 하위 항목에 대한 유사성 검색을 수행하고, Chunk 노드를 거쳐 상위 노드로 이동해서 Chunk 컨텍스트로 원하는 실제 텍스트 콘텐츠를 수집하는 거죠. 긴 텍스트에는 다양한 주제가 포함될 수 있고, 결과적으로 "잡음"이 포함될 수 있는데, 더 작은 텍스트 조각을 삽입함으로써 우리는 보다 구체적인 의미를 포착하고 유사성 검색에 더 나은 일치를 제공하는 보다 "뾰족한" 삽입을 만들고 있어요. 상위 텍스트에는 하위 텍스트와 입력 쿼리를 지원하는 주변 컨텍스트가 모두 포함되어 있으므로 여전히 상위 텍스트를 반환하려고 하는 거고요.

A parent-child lexical data model
상위-하위 어휘 데이터 모델

다양한 어휘 데이터 모델에 대한 자세한 내용은 GraphRAG.com에서 확인할 수 있어요.

엔터티 추출

Knowledge Graph 생성의 어려운 측면은 도메인 구성 요소의 엔터티 하위 그래프를 생성하는 것이에요. 이 프로세스는 그래프의 데이터 도메인 및 사용 사례에 따라 달라지죠. 또한 구성 가능한 많은 구성 요소와 옵션이 포함된 반복적인 프로세스이기도 하고요. 여기에서는 자체 파이프라인을 개발할 때 지침을 제공할 수 있는 몇 가지 더 나은 엔터티 추출 사례를 살펴볼게요.

엔터티 정의

LLM에 엔터티 추출을 요청하면 LLM이 발견된 엔터티를 매핑하고 JSON 형식으로 반환할 수 있는 요청에 응답 모델을 제공해요. 이러한 응답 모델에 주석을 달아 LLM이 추출할 엔터티를 결정할 때 컨텍스트를 제공할 수 있죠. 이를 수행하는 효과적인 방법은 Pydantic Python 라이브러리를 사용하는 거예요.

Pydantic을 사용하면 응답 모델을 Pydantic 클래스로 생성하여 명시적으로 정의할 수 있어요. Pydantic을 사용할 수도 있고요. Field 객체와 field_validator 함수 데코레이터는 각 개별 클래스 속성의 고급 주석 및 데이터 마사지를 허용하죠. 이는 추출된 콘텐츠가 올바르지 않을 때 검증 기능 내에서 정의할 수 있는 명시적 오류를 모델에서 발생시킬 수 있는 강력한 도구에요. 그런 다음 LLM은 이러한 오류 메시지를 받아 엔터티를 업데이트하여 정의된 모델을 준수할 수 있고요. Pydantic 모델을 사용하면 모델이 LLM에 노출될 때 전달될 클래스 내에서 예제 JSON 추출을 정의할 수도 있어요. 다음은 이러한 개념을 구현하는 방법에 대한 예시 코드에요.

class Medication(BaseModel):
  """
  A substance used for medical treatment - a medicine or drug.
  This is a general representation of a medication.
  A Medication node may have relationships to TreatmentArm nodes that are specific to a particular study.
  """
  medication_class: str = Field(…, description="Drug class (e.g., GLP-1 RA, SGLT2i)")
  mechanism: Optional[str] = Field(None, description="Mechanism of action")
  generic_name: str = Field(…, description="Generic name of the medication")
  brand_names: Optional[List[str]] = Field(None, description="Commercial brand names")
  approval_status: Optional[str] = Field(None, description="FDA approval status")
  class Config:
  json_schema_extra = {
    "examples": [
      {
        "medication_class": "GLP-1 receptor agonist",
        "mechanism": "GLP-1 receptor activation",
        "generic_name": "semaglutide",
        "brand_names": ["ozempic", "wegovy", "rybelsus"],
        "approval_status": "FDA approved"
      }
    ]
  }

@field_validator("generic_name", "medication_class")
def validate_lower_case(cls, v: str) -> str:
  """
  Validate that the field value is all lower case.
  """
  return v.lower()

@field_validator("brand_names")
def validate_brand_names(cls, v: list[str] | None) -> list[str] | None:
  """
  Validate that the brand names are all lower case.
  """
  if v is not None:
    return [name.lower() for name in v]
  return v

일반적으로 LLM이 텍스트 청크에서 추출할 수 있도록 다양한 엔터티 옵션을 전달하는데요. 이를 수행할 때 클래스 및 필드 설명뿐만 아니라 클래스 속성의 이름을 지정하는 방법을 통해 이러한 엔터티를 구별하는 것이 중요해요. 예를 들어, 두 가지를 모두 정의하면 Medication 모델과 MedicalCondition 모델이 있고, 둘 다 nameid 필드만 가지고 있다면 LLM이 잘못된 엔터티를 반환할 수 있어요. 이는 LLM이 이러한 엔터티가 모두 존재한다는 것을 인식하지만 추출된 JSON을 모델과 일치시키려고 시도할 때 해당 구조와 일치하는 첫 번째 모델만 선택하기 때문이죠. 두 엔터티 모두 동일한 JSON 구조를 가지므로 LLM에서 확인한 첫 번째 엔터티 모델만 반환되는 거예요.

Demonstration of how we should name entities
엔터티 이름을 지정하는 방법 시연

여기서 해결 방법은 각 모델에 대해 추출하는 필드 이름을 변경하는 거예요. 그런 다음 추출된 필드를 수집 중에 원하는 속성 이름에 매핑할 수 있죠. 따라서 위의 예에서는 Medication.medication_name은 다음과 같이 Neo4j에 수집될 수 있어요: Medication.name, 그리고 MedicalCondition.condition은 다음과 같이 섭취될 수 있죠: MedicalCondition.name.

너무 많은 응답 모델

우리는 텍스트 덩어리에서 많은 엔터티 유형과 그 관계를 추출하고 싶을 텐데요. 응답 모델의 엔터티 및 관계 수를 늘리면 추출의 복잡성도 증가해요. 이는 모델 옵션 수가 증가함에 따라 본질적으로 추출 실패율을 증가시키기 때문이죠.

이 문제를 해결하는 데 도움이 되는 한 가지 방법은 중첩된 Pydantic 모델에 엔터티 및 관계 모델을 제공하는 것이에요. 처음에는 모든 엔터티 및 관계 모델의 통합이 포함된 목록만 제공하는 것으로 시작했는데요. 모델 수가 증가하고 결과가 좋지 않아 처리하기가 어려워졌어요. 대신 중첩 모델을 응답 모델로 제공하는 것은 후처리에서 구문 분석하기가 더 쉬울 뿐만 아니라 제 경험상 실패율도 줄이는 보다 구조화된 접근 방식이에요. 여기서는 일반적인 ResponseModel 객체를 제공하는데, 각 엔터티 및 관계 모델에 대한 필드를 포함하고 있어요. 그러면 각 필드의 값은 해당 모델의 목록이 되죠. 이제 JSON 응답을 더 쉽게 이해하고 처리할 수 있어요.

class ResponseModel(BaseModel):
    """
    The response model for the extracted entities and relationships.
    """
    clinical_outcome: list[ClinicalOutcome] = Field(default_factory=list, description="The clinical outcomes in the chunk")
    treatment_arm: list[TreatmentArm] = Field(default_factory=list, description="The treatment arms in the chunk")
    study_population: list[StudyPopulation] = Field(default_factory=list, description="The study populations in the chunk")
    medical_condition: list[MedicalCondition] = Field(default_factory=list, description="The medical conditions in the chunk")
    medication: list[Medication] = Field(default_factory=list, description="The medications in the chunk")
    medication_used_in_treatment_arm: list[MedicationUsedInTreatmentArm] = Field(default_factory=list, description="The medications used in treatment arms in the chunk")
    treatment_arm_has_clinical_outcome: list[TreatmentArmHasClinicalOutcome] = Field(default_factory=list, description="The treatment arms that have clinical outcomes in the chunk")
    study_population_has_medical_condition: list[StudyPopulationHasMedicalCondition] = Field(default_factory=list, description="The study populations that have medical conditions in the chunk")
    study_population_in_treatment_arm: list[StudyPopulationInTreatmentArm] = Field(default_factory=list, description="The study populations that are in treatment arms in the chunk")

또 다른 방법은 엔터티 그래프에 존재하는 하위 그래프를 식별하고 별도의 호출로 각 하위 그래프를 추출하는 것이에요. 이렇게 하면 응답 모델의 복잡성이 줄어들지만, 식별하는 각 엔터티 하위 그래프에 대해 각 청크를 여러 번 처리해야 하므로 처리 시간과 재정적 비용이 증가하죠. 이 방법은 가상적으로 항목 추출 실패율을 감소시키며 대규모 항목 스키마에 실행 가능한 옵션이에요.

Example subgraph splits for an entity graph
항목 그래프의 하위 그래프 분할 예시

컨텍스트 관리

엔터티 추출의 핵심 컨텍스트에는 몇 가지 구성 요소가 포함되어 있어요. 최소한 시스템 메시지, 추출 지침, 청크 텍스트 및 응답 모델을 포함해야 하죠. 시스템 메시지에는 LLM의 역할과 LLM의 작동 방식에 대한 일반적인 지침이 포함돼요. 다음 사용자 메시지에는 추출할 지침과 텍스트가 포함되죠. 우리의 응답 모델은 API 요청의 메시지와 함께 포함돼요. 강사 라이브러리와 OpenAI Chat Completions API를 사용하여 구조화된 출력 응답을 요청하고요.

response = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt} ], response_model=ResponseModel, temperature=0.0 )

이 기본 설정은 일반적으로 엔티티 추출에 적합하지만, 현재 추출 프로세스가 이전에 추출된 엔티티에 대한 지식에 의존하는 경우가 있을 수 있어요. 예를 들어 프로세스에 단계 목록이 포함되어 있고 단계에 하위 단계가 포함될 수 있는 교육 문서의 프로세스와 단계를 구문 분석할 수 있죠. 여기서는 텍스트 청크를 순차적으로 처리해야 하며, 현재 추출 프로세스가 현재 단계 순서를 유지하도록 컨텍스트에서 이전에 추출된 단계를 제공하는 것이 필수적이에요. 이게 없으면 현재 위치를 잃게 되고 최종 결과에는 공백이 포함될 거예요. 이 문제를 해결하기 위해 다음 목록을 전달할 수 있습니다.n가장 최근에 추출된 단계를 LLM에 알려 시퀀스의 현재 위치를 알려주는 거죠.

아래 프롬프트는 컨텍스트에서 추출된 단계의 롤링 창을 표시하는 데 사용돼요. 우리는 엔티티를 순차적으로 추출하여 다음을 제공할 수 있어야 합니다.n우리의 가장 최근 엔티티previous_processes변하기 쉬운.

Rules:
* Use the provided schema to extract Processes and Steps from the provided text.
* Follow the schema descriptions strictly.
* If there are existing Processes and Steps, you will have them as reference so you may append to them.
* If you are appending steps to an existing process you must use the existing process name and step ids.
* If a Step has no parent Step, then it must have a previous Step id noted! (only the very first Step in a process should not have a previous Step id)

Previous Processes and Steps:
{previous_processes}

Now extract Processes and Steps from the following text chunk:
{text_chunk}

요약

Knowledge Graph 생성은 Neo4j가 GenAI 생태계에 어떻게 적응하는지에 대한 핵심 구성 요소예요. 이는 새로운 통찰력을 제공하고 신뢰할 수 있고 정확한 컨텍스트를 제공하는 강력한 프로세스죠. 두 가지 핵심 그래프 구성 요소(어휘 및 도메인)는 각각 생성 파이프라인을 개발할 때 고유한 고려 사항이 있으며 다양한 문서 유형 및 데이터 도메인에 따라 크게 달라질 수 있어요. 이 문서에서는 이러한 고려 사항 중 많은 부분을 자세히 설명하고 Knowledge Graph 개발을 안내하는 데 도움이 되는 예제를 제공합니다.

이 분야는 상대적으로 새롭고 빠르게 발전하므로 이러한 방법과 전략은 미래에 변경되거나 구식이 될 수 있지만, 여전히 계속해서 반복하고 개선할 수 있는 기반을 제공합니다.

자원

  • Azure AI 문서 인텔리전스
  • Google 문서 AI
  • GraphRAG 개념
  • Hugging Face CLIP 모델 문서
  • LlamaParse 문서
  • Neo4j 커뮤니티 감지 알고리즘
  • Neo4j-Field GitHub 조직
  • Neo4j 그래프 데이터 과학
  • Neo4j 노드 유사성 알고리즘
  • OpenAI API 참조
  • PubMed 지식 그래프 GitHub 저장소
  • Pydantic 문서
  • PyMuPDF 문서
  • 구조화되지 않은 문서

  • GenAI 도구

에이치시스템즈LogTree는 Neo4j 기반 GraphRAG 플랫폼으로, 데이터를 자동으로 지식그래프화하고 자연어 질의로 즉시 답을 제공합니다.

👉 에이치시스템즈 홈페이지

728x90
반응형

+ Recent posts