728x90
반응형
  • Graph Data Science

Semantic Search를 사용하면 검색 시스템은 쿼리의 정확한 키워드가 문서에 없더라도 쿼리의 의미와 일치하는 문서를 찾을 수 있어요. 이러한 유연한 검색 기능은 여러 분야에서 정말 중요하죠. Retrieval-Augmented Generation(RAG) GenAI 애플리케이션에서도 핵심적인 역할을 한답니다.

RAG 애플리케이션은 Semantic Search를 활용해서 관련 문서를 찾고, 검색된 문서를 기반으로 질문에 답변하도록 Large Language Model(LLM)에 요청하는 방식으로 작동해요.

Vector 요약을 위해 긴 문서를 작은 덩어리(chunk)로 나누는 방법이 있는데요. 만약 chunk가 너무 크면 Vector 요약이 사용자의 질문과 관련될 수 있는 문서의 사소한 주제를 제대로 잡아내지 못할 수 있어요. 반대로 chunk가 너무 작으면 사용자의 질문에 대답하는 데 중요한 맥락(context)이 여러 텍스트 chunk에 걸쳐 흩어질 수 있죠.

이 문제에 대한 한 가지 해결책은 문서에서 주제를 추출해서 Semantic Search의 기초로 사용하는 거예요. 사용자의 질문과 가장 유사한 주제가 Vector 저장소에서 발견되면, 해당 주제를 참조하는 모든 문서를 검색할 수 있게 되는 거죠.

Neo4j는 주제 기반 Semantic Search를 위한 강력한 도구 세트를 제공하고 있어요. Neo4j Graph Database 플랫폼을 사용하면 문서 및 관련 주제를 Knowledge Graph로 표현할 수 있죠. Neo4j의 Vector Search 기능을 사용하면 주제와 문서의 Vector 표현에 대한 Semantic Search가 가능해져요. Neo4j Graph Data Science(GDS)와 함께라면 Graph 알고리즘을 사용해서 중복된 주제를 식별하고 병합해서 검색을 더욱 효율적으로 만들 수도 있답니다.

영화 줄거리가 포함된 Database에서 Graph 기반 주제 클러스터로 Semantic Search를 수행하면, 기본 문서만 대상으로 Semantic Search를 수행하는 것보다 27% 더 많은 관련 문서가 제공된다는 사실을 발견했어요.

최근 영화 데이터세트

저는 제가 다운로드한 영화 정보에 대한 Semantic Search를 위한 주제 모델링을 테스트해보기로 했어요. TMDB.org와 Neo4j AuraDS Graph Database를 사용했죠. 데이터 세트를 2023년 9월 1일 이후에 출시된 타이틀로 제한했는데요. 그 이유는 해당 출시 날짜가 제가 프로젝트에 사용한 Large Language Model의 학습 마감 날짜보다 늦었기 때문이에요. 이렇게 하면 LLM이 영화를 설명하기 위해 LLM이 학습 데이터에서 영화에 대해 배웠을 수 있는 정보가 아니라, Neo4j에서 검색된 데이터를 사용하고 있음을 확신할 수 있거든요. 데이터 세트의 영화에는 오스카상 수상작부터 학생들이 제작한 단편 영화까지 다양한 영화가 포함되어 있었어요.

결과 데이터 세트에는 16,156개의 영화 노드가 있었어요. 각 노드에는 제목 `Property`와 영화 줄거리 요약이 포함된 개요 `Property`가 있었죠. Neo4j에 영화를 로드하는 데 사용한 코드는 다운로드_TMDB_movies.ipynb 노트북, 그리고 프로젝트 저장소에 있답니다.

Neo4j Bloom에 제목과 개요가 표시된 영화 `Node`에요.

LLM을 사용하여 테마 추출

영화 데이터를 Neo4j에 로드한 후, LLM에게 영화 제목과 개요에서 핵심 주제를 찾아달라고 요청했어요. 테마는 특정 개체, 설정 또는 아이디어를 나타낼 수 있죠. 저는 사람들이 이직을 검색할 때 생각할 수 있는 요소를 찾고 싶었어요. 어떤 유형의 테마가 발견되었는지 분류하려고 하지 않았기 때문에, 알고리즘이 날짜, 사람, 조직과 같은 특정 유형의 개체를 식별하려고 시도하는 기존의 Named Entity Recognition(ER)보다 작업이 다소 간단했어요.

제가 사용한 `Prompt`는 다음과 같아요:

You are a movie expert. 
You are given the tile and overview of the plot of a movie.
Summarize the most memorable themes, settings, and public figures in the movie
into a list of up to eight one-to-two word phrases. 
Only include the names of people if the person is a famous public figure.
Prioritize any phrases that appear in the movie's title.
You can provide fewer than eight phrases.
Return the phrases as a pipe separated list. 
Return only the list without a heading.

저는 최근 출시된 모델을 시험해 볼 기회를 찾고 있었기 때문에, 이 추출을 위해 Anthropic의 Claude 3 Sonnet 모델을 사용했어요. 다른 Large Language Model(LLM)도 이 작업을 잘 수행할 거라고 생각해요.

다음은 제가 모델에 보낸 입력의 예시입니다:

title: Maestro
overview: A towering and fearless love story chronicling the lifelong 
relationship between Leonard Bernstein and Felicia Montealegre Cohn Bernstein. 
A love letter to life and art, Maestro at its core is an emotionally epic 
portrayal of family and love.

LLM이 저에게 돌려준 내용은 다음과 같죠:

meastro|family bonds|Emotional epic|Fearless passion|Lifelong relationship|
Towering love|Art devition

LLM의 이러한 응답을 Neo4j의 `HAS_THEME` `Relationship`을 통해 영화 `Node`에 연결된 테마 `Node`로 전환했어요.

테마(`Node`, 파란색)는 `HAS_THEME` `Relationship`을 통해 영화(`Node`, 노란색)에 연결됩니다.

노트북 theme.ipynb 추출 프로젝트 저장소에는 이 프로세스 단계에 대한 코드가 포함되어 있어요.

테마 정리 및 Vector Embedding 생성

LLM은 파이프로 구분된 테마 목록만 반환하는 완벽한 작업을 수행하지 못했어요. 어떤 경우에는 목록 앞에 "이 영화에서 기억에 남는 주제, 설정 및 공인은 다음과 같습니다...."와 같은 추가 텍스트가 붙었죠. 또 어떤 경우에는 LLM이 테마를 찾을 수 없어 빈 목록 대신 테마를 말하는 문장을 반환하기도 했어요. 심지어 LLM이 개요에 설명된 내용이 요약하기에는 너무 민감하거나 명시적이라고 판단하는 경우도 있었답니다. 노트북에서 이러한 응답을 정리하는 데 사용한 코드를 테마를 정리하고 embeddings.ipynb를 받으세요. 프로젝트 저장소에서 찾아볼 수 있어요. LLM의 예측할 수 없는 특성 때문에 코드를 실행할 경우 테마를 정리하기 위해 약간 다른 단계를 수행해야 할 수도 있다는 점, 참고해주세요!

테마를 정리한 후 OpenAI의 text-embedding-3-small 모델을 사용하여 테마에 대한 `Embedding` `Vector`를 생성했어요. 저는 이러한 `Vector`를 Neo4j의 테마 `Node`에 `Property`로 저장했답니다. 또한 영화 개요와 연결된 영화 제목이 포함된 문자열에 대한 `Embedding`도 생성했죠. 이러한 `Embedding`은 Neo4j의 Movie `Node`에 `Property`로 저장되었어요.

Neo4j Graph Data Science를 사용한 클러스터 테마

LLM은 언어를 다루는 데 있어서 놀라운 일을 하지만, 결과를 표준화하도록 하는 것은 어렵죠. 저는 LLM이 유사한 동의어인 몇 가지 주제를 식별했다는 것을 발견했어요. 중복되거나 매우 밀접하게 관련된 테마를 결합하여 테마에 대한 Semantic Search를 보다 효율적으로 할 수 있기를 바랐어요. 테마 클러스터링 및 중복 제거를 위한 모든 코드는 클러스터 테마.ipynb 프로젝트 저장소의 노트북에 있답니다.

전통적인 NLP 기술을 사용하여 어간을 공유하는 테마 찾기

저는 전통적인 Natural Language Processing 기술을 사용해서, 줄기(stem)라고 불리는 동일한 어근을 기반으로 하는 단어를 식별하는 것부터 시작했어요. 저는 WordNet Lemmatizer에서 NLTK Python 패키지를 사용해서 일반적인 어근을 찾기 위해 테마에서 접두사와 접미사를 잘라냈죠. HAS_STEM 관계를 사용해서 테마에 연결된 그래프에 stem `Node`를 만들었어요. 가장 많은 Movie `Node`에 연결된 stem 그룹의 Theme `Node`에서 `Vector Embedding`을 Stem `Node`의 `Vector Embedding`으로 할당했답니다.

관련 테마(파란색 `Node`)가 있는 줄기(녹색 `Node`)의 예시입니다.

줄기를 공유하지 않는 유사한 주제 탐색

그래프에는 줄기를 공유하지 않음에도 불구하고 매우 유사한 의미를 지닌 다른 그룹의 주제가 있었어요. 이를 식별하기 위해 그래프에서 일부 Theme `Node`를 선택하고 `Vector Index`에 대해 `Query`를 실행해서 가장 유사한 `Embedding`을 가진 다른 Theme `Node`를 찾았죠. 저는 아래에서 코사인 유사성에 대한 합리적인 컷오프를 찾으려고 노력했는데, 이는 아마도 두 테마의 동의어가 아닐 거예요. 또한 데이터 세트에 테마가 있을 수 있는 가까운 동의어 수에 대한 기준점을 찾으려고 노력했답니다.

다음은 "underwater"라는 주제의 예시입니다.

"underwater"와 가장 유사한 주제를 보여주는 표입니다.

저는 "해저", "수중", "수중 세계", "해저 세계"가 모두 본질적으로 "수중"과 같은 것이라고 생각했어요. "수중 음악" 테마는 제가 별도의 테마로 보존하고 싶은 새로운 아이디어를 소개하기 시작하죠. 0.880904 이상의 유사성 컷오프 또는 4 이하의 상위 k는 "수중"으로 묶어서는 안 되는 테마를 제외해요.

다음은 '패스트푸드' 표의 첫 번째 부분입니다.

저는 “패스트푸드 레스토랑”의 처음 두 가지 테마인 “패스트푸드 버거”가 하나의 컨셉이 될 만큼 밀접하게 연관되어 있다고 생각했어요. "길거리 음식" 테마는 다른 그룹으로 분류할 가치가 있는 것 같죠? 0.804882를 초과하는 유사성 컷오프 또는 2보다 작거나 같은 상위 k는 길거리 음식과 패스트푸드 간의 구별을 유지한답니다.

'아프리카'와 가장 유사한 주제는 완전히 다른 대륙인 '아시아'였어요. 이 두 테마를 클러스터에 병합할 수 없도록 하려면 0.829958 이상의 유사성 기준이 필요하죠.

'아프리카'에 대한 유사한 주제를 보여주는 표입니다.

K Nearest Neighbor 알고리즘을 사용하여 IS_SIMILAR `Relationship` 생성

여러 다른 테마의 유사성을 살펴본 후 유사성 컷오프 0.83과 상위 k 2를 사용하기로 결정했어요. Graph Projection 여기에는 Stem `Node`와 Stems와 관련되지 않은 모든 테마 `Node`가 포함됩니다. 그런 다음 저는 K Nearest Neighbor 코사인 유사성이 유사성 컷오프보다 높은 `Node`와 제가 선택한 상위 k 임계값 사이에 IS_SIMILAR `Relationship`을 추가하여 `Graph Projection`을 변경하는 알고리즘을 사용했어요.

약하게 연결된 구성 요소 커뮤니티 테스트

The Weakly Connected Components 커뮤니티 감지 알고리즘은 방향이 지정되지 않은 경로로 연결된 모든 `Node`를 동일한 구성 요소에 할당해요. IS_SIMILAR `Relationship`을 생성할 때 매우 엄격한 기준을 선택했다면 WCC는 동의어 클러스터를 식별하는 데 실행 가능한 접근 방식이었을 거예요. A가 B의 동의어이고 B가 C의 동의어이면 A는 C의 동의어라는 전이적 가정을 할 수 있죠.

저는 WCC를 운영했고 그 결과가 이상적이지 않다는 것을 발견했어요. 저는 WCC 유사성 임계값을 0.875로 설정해 보았는데, 이는 KNN에 사용한 컷오프보다 높았어요. 매우 밀접하게 관련된 주제들만 하나로 묶고 싶었거든요. 가장 큰 WCC 커뮤니티에는 29개의 주제가 포함되어 있었어요. 모두 크리스마스와 관련이 있지만 '크리스마스 테러'와 '크리스마스 매직'은 아마도 매우 다른 분위기의 영화와 관련이 있을 거예요.

컷오프가 0.875인 최대 WCC 커뮤니티의 테마입니다.

0.875와 같은 높은 유사성 컷오프를 설정하는 데 따른 또 다른 문제는 '건조한 풍경'과 '황량한 풍경'과 같은 테마가 서로 다른 커뮤니티에서 발생한다는 것이에요. WCC 대신 Leiden 커뮤니티 감지 알고리즘을 사용해 보기로 결정했답니다.

Leiden 커뮤니티는 더 크거나 작은 커뮤니티에 맞게 조정될 수 있습니다.

The Leiden 커뮤니티 감지 알고리즘은 커뮤니티 내에서 시작하고 끝나는 커뮤니티 `Relationship`의 비율이 `Relationship`이 무작위로 배포된 경우 예상보다 높은 그래프 내 커뮤니티를 식별해요.

감마라는 파라미터를 조정해서 Leiden이 더 많거나 적은 수의 커뮤니티를 생성하도록 할 수 있어요. 제 생각에 감마는 커뮤니티로 표시된 `Node` 그룹이 관계가 무작위로 분산된 경우 예상되는 것보다 *얼마나 더 서로 연결되어 있는지*를 지정하는 것 같아요. 감마가 증가하면 클러스터 정의 기준이 더욱 엄격해지죠. 감마 값이 높으면 크고 느슨하게 연결된 커뮤니티는 기준을 통과하지 못하고, 작고 조밀하게 연결된 커뮤니티만 남게 돼요.

Leiden을 실행하기 전에 `Graph` 투영을 수정해야 했어요. Leiden은 무방향 `Graph`에서 실행되어야 하지만 KNN은 방향 관계를 생성하거든요. `gds.graph.relationships.toUndirected()` 프로시저를 사용해서 `Graph` 투영의 IS_SIMILAR 관계를 UNDIRECTED_SIMILAR 관계로 변환했어요.

Leiden은 가중치 관계를 사용해서 실행할 수도 있어요. K Nearest Neighbors에 대해 선택한 유사성 컷오프 때문에 UNDIRECTED_SIMILAR 관계의 모든 유사성 점수는 0.83에서 1.0 사이였어요. 저는 최소-최대 스케일링 공식을 사용해서 해당 가중치를 0.0에서 1.0 사이의 범위로 변환해서 가까운 연결이 먼 연결보다 상대적으로 더 영향력이 있도록 만들었죠.

1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0의 알파 값으로 Leiden을 실행해서 테스트해봤어요.

감마 값이 증가하는 커뮤니티 규모 표

표를 보면 감마가 증가하면 가장 큰 커뮤니티의 크기가 93개 `Node`에서 9개 `Node`로 감소했다는 걸 알 수 있어요. 소규모 커뮤니티일수록 테마 간의 미묘한 차이를 포착할 가능성이 더 높다고 생각했죠.

여러 테마를 선택하고 다양한 감마 수준으로 해당 커뮤니티에 포함된 다른 테마를 살펴봤어요.

감마 32.0에서는 크리스마스와 관련된 모든 테마가 휴일과 관련이 있었어요. 하지만 '크리스마스 테러'와 '크리스마스 기적'은 제가 WCC를 시도했을 때처럼 여전히 함께 있었죠. 감마를 128 이상으로 높이면 다른 테마는 사라지지만 "Xmas"와 "크리스마스"는 함께 유지돼요.

다양한 감마 수준에서 여러 가지 테마를 살펴본 결과 동일한 Leiden 커뮤니티의 감마 수준 256.0에서 테마를 동일한 테마 그룹으로 수집하기로 결정했어요. 각 커뮤니티마다 ThemeGroup `Node`를 만들고, 커뮤니티의 테마 `Node`를 ThemeGroup에 연결하는 IN_GROUP 관계를 만들었죠.

잃어버린 사랑과 관련된 테마(`Node`, 파란색)가 포함된 ThemeGroup(`Node`, 분홍색)의 예

테마 그룹이 준비되면 `Graph`를 `Query`해서 동일하거나 유사한 테마를 공유하는 영화를 찾을 수 있었어요. 아래 예에서는 두 편의 영화가 '부유한 동네'라는 주제를 공유하고 있죠. 두 테마가 모두 동일한 테마 그룹에 속하므로 '부유한 집'이라는 주제를 가진 세 번째 영화와도 관련이 있는 거에요.

테마 그룹(`Node`, 분홍색)에서 테마(`Node`, 파란색)가 있는 영화(`Node`, 노란색)를 시각화

테마 그룹 요약

테마 그룹이 결정된 후 OpenAI의 ChatGPT-3.5-turbo에 테마 그룹에 대한 간단한 설명을 작성해 달라고 요청했어요. 이 작업의 코드는 테마 그룹 요약.ipynb 노트북에 있답니다. 저는 "이 영화는 주제를 다룬다..." 패턴에 따라 설명의 첫 번째 문장을 제공했어요. 시작 문장과 함께 주제를 포함하는 최대 20개의 제목과 개요로 구성된 샘플을 LLM에 제공했죠. 제가 사용한 `Prompt`는 다음과 같아요.

You are a movie expert. 
You will be given a list of information about movies and the first 
sentence of a short paragraph that summarizes themes in the movies.
Write one or two additional sentences to complete the paragraph.
Do not repeat the first sentence in your answer.
Use the example movie information to guide your description of the 
themes but do not include the titles of any movies in your sentences.

'현지인 대 방문자', '게스트', '게스트', '방문자', '방문자'가 포함된 테마 그룹을 고려해서 LLM은 다음 요약을 내놓았어요.

이 영화는 "현지인 대 방문객", "손님", "손님", "방문자" 및 "방문자"라는 주제를 다룹니다. 이 영화는 현지인과 방문객 사이의 역동성을 탐구하며 종종 다양한 배경을 가진 개인이 상호 작용할 때 발생하는 긴장과 음모를 강조합니다. 내러티브는 손님과 호스트, 방문객과 현지인 사이에 형성될 수 있는 환대, 관계, 예상치 못한 연결의 복잡성을 탐구합니다. "현지인 대 방문객" 및 "손님"이라는 주제가 스토리텔링에 복잡하게 얽혀 있어 인간의 경험과 상호 작용에 대한 풍부한 태피스트리를 제공합니다.

'사기', '사기 예술가', '사기', '사기' 테마 그룹이 다음 요약을 작성했어요.

이 영화들은 "사기", "사기 예술가", "사기", "사기"라는 주제를 다루고 있습니다. 이 영화는 등장 인물이 목표를 달성하기 위해 사기와 사기에 의존하는 속임수와 조작의 세계를 탐구합니다. 내러티브는 이러한 행동의 결과를 탐구하여 인간 본성의 복잡성과 개인적인 이익을 추구하는 과정에서 옳고 그름 사이의 모호한 경계를 드러냅니다. 속임수를 통한 등장인물의 자기 발견 여정은 사회의 어두운 측면과 개인이 자신의 욕망을 충족하기 위해 기꺼이 가고자 하는 길이를 조명합니다.

각 요약에는 시작 문장과 관련된 모든 주제가 포함되어 있어서 요약에서 어떤 키워드도 제외되지 않았어요. LLM은 한두 단어로 된 주제에 대한 맥락을 제공하기 위해 영화 예를 잘 활용하는 것 같았죠.

LLM에서 생성된 테마 그룹에 대한 긴 설명 외에도 "<테마의 연결 목록>에 관한 영화" 패턴을 따르는 간단한 설명을 만들었어요. 위의 주제 그룹에 대한 간략한 요약은 "사기, 사기꾼, 사기 및 사기에 관한 영화"였죠. 이를 통해 LLM이 제공하는 추가 컨텍스트가 더 나은 검색 결과를 가져오는지 여부를 테스트할 수 있었어요.

각 테마 그룹과 관련된 세 가지 벡터 속성을 만들었어요. OpenAI의 text-embedding-3-small 모델을 사용하여 짧은 요약과 긴 요약의 Vector Embedding을 생성했죠. 세 번째 임베딩은 테마 그룹과 관련된 테마 키워드 임베딩의 평균이었어요.

다양한 벡터 인덱스를 기반으로 리트리버 비교

이 시점에서 영화 콘텐츠와 관련된 5개의 벡터 Index를 만들었어요.

  • 다음을 다루는 영화 Node에 대한 Index: 제목 및 개요
  • 다음을 다루는 테마 Node에 대한 Index:
  • 다음을 다루는 ThemeGroup Node의 Index: LLM이 생성한 긴 테마 요약
  • 다음을 다루는 ThemeGroup Node의 Index: '...에 관한 영화' 주제에 대한 간략한 요약
  • 다음을 다루는 ThemeGroup Node의 Index: 테마 벡터의 평균 (ThemeGroup과 관련된 테마 Node에서)

어떤 Index가 관련 영화를 검색하는 데 가장 효과적인지 확인하기 위해 동일한 질문 세트로 다섯 가지 Index를 모두 테스트했어요. 각 Index에서 각 질문에 대해 최대 50개의 영화를 찾을 수 있도록 허용했죠. 각 지표에 대한 질문과 일치하는 결과 영화의 수를 세어 회상에 중점을 두었어요. 검색의 다운스트림 프로세스에서 오탐 필터링 및 문서 순위 지정을 처리할 수 있기 때문에 해당 측정항목을 선택했어요. 이러한 비교를 위한 코드는 검색기.ipynb 비교 노트북에 담겨있어요.

제가 테스트한 질문은 다음과 같아요.

  • 화가에 관한 다큐멘터리에는 어떤 것이 있나요?
  • 클래식 음악을 소재로 한 영화에는 어떤 것이 있나요?
  • 아이스하키에 관한 영화에는 어떤 것이 있나요?
  • 야구를 소재로 한 영화는 어떤 것이 있나요?
  • 누가 해냈는지 영화에는 어떤 것이 있나요?
  • 블랙 코미디에는 어떤 것들이 있나요?
  • 1960년대 유럽을 배경으로 한 작품은 무엇인가요?
  • 콜롬비아 이전 시대의 미국을 다룬 영화에는 어떤 것이 있나요?
  • 새에 관한 영화에는 어떤 것이 있나요?
  • 개에 관한 영화에는 어떤 것이 있나요?

Neo4j Knowledge Graph에서 데이터를 검색하면 제 Query와 유사한 벡터를 Semantic Search한 다음 그래프의 Relationship을 탐색하여 제가 찾고 있던 영화를 찾을 수 있는 유연성이 생겼어요.

영화 Index의 경우 벡터가 Query 벡터와 가장 근접하게 일치하는 50개의 영화를 검색했어요. 이것이 가장 기본적인 검색이었죠. 제가 사용한 Cypher Query는 다음과 같아요.

CALL db.index.vector.queryNodes("movie_text_vectors", 50, $query_vector) 
YIELD node, score 
RETURN $queryString AS query,
"movie" AS index,
score, node.tmdbId AS tmdbId, node.title AS title, node.overview AS overview, 
node{question: $queryString, .title, .overview} AS map
ORDER BY score DESC

테마 Index의 경우 벡터가 가장 가까운 25개의 테마 Node를 검색한 다음 그래프의 HAS_THEME Relationship을 사용하여 해당 테마와 관련된 모든 영화를 찾았어요. 주제 유사성 기준으로 정렬된 테마 관련 상위 50개 영화를 Query에 반환한 다음 영화 유사도를 Query에 반환했죠. 저는 이 Cypher Query를 사용했어요.

CALL db.index.vector.queryNodes("theme_vectors", 50, $query_vector) 
YIELD node, score
MATCH (node)<-[:HAS_THEME]-(m)
RETURN $queryString AS query,
"theme" AS index,
collect(node.description) AS theme, 
gds.similarity.cosine(m.embedding, $query_vector) AS score, 
m.tmdbId AS tmdbId, m.title AS title, m.overview AS overview,
m{question: $queryString, .title, .overview} AS map
ORDER BY score DESC, gds.similarity.cosine(m.embedding, $query_vector) DESC
LIMIT 50

테마 그룹 관련 Index의 경우 Query 벡터와 가장 일치하는 25개의 테마 그룹을 찾았어요. IN_GROUP Relationship을 사용하여 해당 테마 그룹과 관련된 모든 테마를 찾았죠. 그런 다음 HAS_THEME Relationship을 사용하여 해당 테마와 관련된 모든 영화를 찾았어요. 주제 그룹 유사성별로 정렬된 상위 50개 영화를 Query에 반환한 다음 영화 유사성을 Query에 반환했죠. 다음은 테마 그룹의 긴 요약 Index를 기반으로 영화를 검색하는 데 사용한 Cypher Query예요. 간략한 요약 및 평균 Index에 대한 Query는 Index 이름을 제외하고 동일했어요.

CALL db.index.vector.queryNodes("theme_group_long_summary_vectors", 
  25, $query_vector) YIELD node, score
MATCH (node)<-[:IN_GROUP]-()<-[:HAS_THEME]-(m)
RETURN $queryString AS query,
"theme_group_long" AS index,
collect(node.descriptions) AS theme,
gds.similarity.cosine(m.embedding, $query_vector) AS score, 
m.tmdbId AS tmdbId, m.title AS title, m.overview AS overview,
m{question: $queryString, .title, .overview} AS map
ORDER BY score DESC, 
gds.similarity.cosine(m.embedding, $query_vector) DESC
LIMIT 50

저는 ChatGPT-3.5-turbo에게 검색된 영화가 질문과 일치하는지 판단하도록 요청했어요. 그리고 제가 직접 영화가 제 질문 의도와 맞는지도 판단했죠. 어떤 경우에는 좀 아슬아슬한 상황도 있었어요. 소프트볼 영화가 야구 질문에 포함되어야 할까요? 공포 영화가 다크 코미디로 여겨질 만큼 야심차게 보이는 시점은 언제일까요? 최대한 일관성 있게 답변하려고 노력했고, 답변을 채점할 때는 어떤 index가 영화를 반환했는지 모르게 했어요. 제가 내린 판단과 ChatGPT의 판단이 약 74% 정도 일치한다는 것을 알게 되었어요. 전반적으로 ChatGPT보다 검색된 영화를 질문과 더 관련 있다고 판단하는 경우가 많았어요.

모든 질문에 대해 관련성이 있다고 판단된 영화 수를 합산해 보니, 테마 그룹 index가 다른 index보다 훨씬 좋은 성능을 보였어요. 영화 index보다 관련성이 27% 더 높은 영화를 찾았죠. 또한 Graph Data Science를 사용해서 ThemeGroup node를 생성하는 것이 LLM에서 생성된 원시 테마 node를 사용하는 것보다 더 나은 결과를 얻을 수 있다는 것도 알 수 있었어요.

Index별 검색 결과 전체 모습이에요.

아래 차트는 쿼리별 결과를 보여주고 있어요. 긴 요약 index는 꾸준히 좋은 성능을 보였고, 기본 영화 index보다 항상 뛰어났지만 모든 질문에서 가장 뛰어난 index는 아니었어요.

테마 index는 다크 코미디를 찾는 데 특히 효과적이었어요. '호러 코미디'라는 주제를 잘 다듬어서 긴 요약 index가 어둠에 대한 주제를 선호하게 만들었죠.

Short Summary index는 클래식 음악 관련 질문에서 다른 index보다 뛰어난 성능을 보였어요. 다른 index에서는 놓친 'composer'라는 단어를 포함해서 테마 그룹을 선택했기 때문이에요.

각 index에 대한 질문별 결과예요.

ChatGPT-3.5-turbo가 관련성이 있다고 판단한 전체 영화 수를 살펴보면, 긴 요약 index가 여전히 다른 index보다 우수했지만 영화 index를 13% 앞섰을 뿐이에요. 이는 LLM이 검색된 문서를 분석하고 결과를 사용자에게 다시 전달할 때 무엇을 바꿔야 할지 결정하는 Retrieval-Augmented Generation 애플리케이션에서 기대할 수 있는 현실적인 결과일 수 있어요.

결론

Semantic Search에서 주제 모델링을 위해 Neo4j Graph Data Science를 사용하면서 세 가지 주요 이점을 확인했어요.

  1. Neo4j Graph Data Science로 생성된 긴 요약 테마 그룹 index는 다른 index 전략보다 더 많은 수의 관련 검색 결과를 제공했어요.
  2. 테마 그룹을 만들면 구조화되지 않은 텍스트가 데이터 분석 및 시각화에 사용할 수 있는 Knowledge Graph로 바뀌었어요.
  3. Neo4j에서 주제 모델링을 수행하면 주제 추출, 형태소 분석 및 주제 클러스터링 계층을 통해 제 작업을 정리하고 보여줄 수 있는 좋은 방법이 되었어요. 데이터 수집 및 정리, 알고리즘 분석, Vector Embedding 기반 검색이 모두 동일한 그래프 플랫폼에서 이루어졌죠.


  • RAG
  • Semantic Search

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

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

728x90
반응형

+ Recent posts