PostgreSQL과 함께 Supabase의 벡터 데이터베이스 사용하기

Using Supabase’s vector database with PostgreSQL

생성적 AI가 전통적인 데이터베이스를 뒤집는 10가지 방법

현대 기술은 점점 더 인공지능(AI)과 기계학습(ML)을 통합하여 사용자에게 맞춤화된 애플리케이션을 제공합니다. AI와 ML은 이러한 혁신의 핵심이며, 스트리밍 플랫폼에서 즐길만한 영화를 추천하거나 음성 인식 기기가 사용자의 명령을 이해하고 반응하는 것에 사용됩니다.

이러한 AI와 ML 시스템에서 중요한 요소 중 하나는 ‘벡터’와 ‘임베딩’입니다. 이 개념의 중요성을 아래에 간략히 설명하겠습니다.

벡터와 임베딩

  1. 데이터 표현: 기계 학습에서는 원시 데이터를 알고리즘이 이해할 수 있는 숫자 형태로 변환해야 합니다. 벡터와 임베딩은 텍스트, 이미지, 또는 사용자 행동과 같은 복잡한 데이터를 숫자 형태로 표현하는 간단하면서도 강력한 방법입니다.
  2. 고차원 공간: 임베딩은 고차원 공간에 위치하게 되는데, 이는 데이터 포인트 간의 복잡한 관계를 캡쳐할 수 있게 합니다. 이것은 자연어 이해, 이미지 인식, 추천 시스템 등에서 특히 중요합니다.
  3. 효율성: 고차원 벡터는 KD-트리, 해시 테이블 또는 더 전문화된 데이터베이스와 같은 최적화된 데이터 구조를 사용하여 효율적으로 처리할 수 있습니다. 이로 인해 전체 ML 과정이 더 빠르고 확장 가능해집니다.
  4. 문맥 인식: 고차원 공간에서 데이터의 본질을 캡쳐함으로써, 벡터와 임베딩은 시스템이 문맥을 더 잘 이해하게 합니다. 이것은 사용자의 행동과 선호를 기반으로 영화나 제품을 추천하는 추천 엔진이나, 말한 단어의 의미를 이해하는 음성 인식과 같은 기능의 기초입니다.
  5. 전이 학습: 한 작업에 대해 생성된 임베딩은 때로는 다른 작업에도 재사용될 수 있습니다. 이를 전이 학습이라고 하며, 이를 통해 ML 모델의 더 빠른 개발과 배포가 가능합니다.

요약하면, 벡터와 임베딩은 원시 데이터와 기계 학습 알고리즘 사이의 다리 역할을 하며, 복잡하고 비구조화된 정보를 기계가 쉽게 해석하고 배울 수 있는 형식으로 변환합니다. 이러한 변환은 개별 사용자의 요구에 점점 더 민첩하고 적응할 수 있는 지능형 시스템의 개발에 있어 중심적인 역할을 합니다.

이 글에서는 데이터베이스의 맥락에서 벡터와 임베딩을 다루는 방법에 대해 살펴보며, 특히 오픈 소스 Firebase 대안인 Supabase에 중점을 둡니다. 세련된 디자인과 포괄적인 기능 세트로 Supabase는 사용하기 쉽고 확장 가능한 백엔드 솔루션을 찾고 있는 개발자들 사이에서 인기를 얻고 있습니다.

우리는 Supabase를 사용하여 PostgreSQL 데이터베이스에서 벡터 데이터를 관리하는 방법을 탐구합니다. 이를 통해 데이터 처리 능력을 활용합니다. 또한 OpenAI를 사용한 임베딩 생성에 더 깊게 다룹니다. 이는 복잡한 데이터를 의미 있는 수치 표현으로 효율적으로 변환할 수 있는 방법을 제공하여, 우리가 애플리케이션에서 AI의 힘을 활용할 수 있게 합니다.

벡터 이해하기

고수준에서 볼 때, 벡터는 순서가 있는 일련의 숫자를 담고 있는 컨테이너로 간주될 수 있습니다. 수학에서 벡터는 종종 공간에서의 한 점을 나타내며, 각 숫자는 다른 차원을 따라의 위치에 해당합니다. 컴퓨터 과학과, 더 구체적으로는 기계 학습에서 벡터는 더 중추적인 역할을 합니다; 벡터는 데이터의 수학적 표현으로 작용합니다.

기계 학습 모델에서 다양한 유형의 과일을 표현하고자 하는 간단한 예를 생각해 봅시다. 각 과일을 설명하는 벡터를 생성할 수 있으며, 벡터 내의 각 요소나 숫자는 과일의 구별되는 속성이나 특징을 나타냅니다.

예를 들어 사과를 들어봅시다. 사과의 벡터는 이렇게 생겼을 수 있습니다: [1, 150, 8.5, 10]. 이 벡터의 네 개의 숫자는 다음을 나타낼 수 있습니다:

  • 첫 번째 숫자 1은 과일의 종류를 나타낼 수 있습니다(1은 사과, 2는 바나나 등).
  • 두 번째 숫자 150은 과일의 무게를 그램 단위로 나타낼 수 있습니다.
  • 세 번째 숫자 8.5는 과일의 단맛을 1에서 10까지의 척도로 나타낼 수 있습니다.
  • 네 번째 숫자 10은 과일의 빨간색 정도를 1에서 10까지의 척도로 나타낼 수 있습니다.

이러한 방식으로 모델 내의 벡터 [1, 150, 8.5, 10]은 특정 속성을 가진 사과를 고유하게 표현합니다.

임베딩 이해하기

벡터는 숫자 데이터를 표현하는 간단한 방법입니다만, 텍스트나 이미지와 같은 많은 데이터 유형은 숫자로 변환하는 데 도움이 필요합니다. 이때 임베딩이 사용됩니다. 임베딩은 더 복잡하고 고차원의 데이터를 저차원의 밀집된 벡터 공간에서 표현하도록 설계된 벡터의 한 종류입니다.

예를 들어, “apple”이라는 단어를 컴퓨터가 이해하고 유사한 단어와 연관짓도록 표현하고자 한다고 상상해 봅시다. “apple”이라는 단어를 300개의 숫자로 구성된 임베딩 벡터로 변환하는 알고리즘을 사용할 수 있습니다.

이 임베딩 벡터는 단어 자체뿐만 아니라 다른 단어와의 관계까지 포착합니다. 예를 들어, “apple”에 대한 임베딩은 “fruit”에 대한 임베딩과의 거리가 “car”에 대한 임베딩과의 거리보다 가까울 것입니다. 이는 “apple”과 “fruit” 사이의 더 가까운 의미론적 관계를 반영합니다.

임베딩의 사용 사례

임베딩은 특히 텍스트 형태의 고차원 데이터를 저차원 공간에서 표현하기 위한 강력한 도구입니다. 데이터 의미를 포착하는 능력 덕분에 임베딩은 검색, 클러스터링, 추천, 이상 탐지, 다양성 측정, 그리고 분류와 같은 다양한 애플리케이션에서 귀중하게 사용됩니다. 이러한 사용 사례에 대해 좀 더 자세히 알아봅시다.

  1. 검색(Search): 임베딩을 사용하면 문서나 상품, 심지어는 이미지와 같은 데이터에 대한 검색 결과를 더 정확하게 얻을 수 있습니다. 이는 임베딩이 데이터의 의미적 유사성을 포착할 수 있기 때문입니다.
  2. 클러스터링(Clustering): 임베딩은 데이터 포인트들 사이의 의미적 관계를 파악하여, 유사한 것들끼리 그룹화하는 클러스터링에 유용합니다.
  3. 추천(Recommendations): 온라인 쇼핑이나 스트리밍 서비스에서, 임베딩은 사용자의 선호나 이전의 행동을 분석하여 개인화된 추천을 생성하는 데 사용될 수 있습니다.
  4. 이상 탐지(Anomaly Detection): 임베딩은 데이터의 정상적인 패턴과 이상한 패턴을 구분하는 데 도움이 됩니다. 이를 통해 보안 위협이나 부정 행위를 탐지할 수 있습니다.
  5. 다양성 측정(Diversity Measurement): 데이터 셋 내에서의 다양성을 측정하거나 비교하는 데에 임베딩이 활용될 수 있습니다.
  6. 분류(Classification): 텍스트, 이미지, 사운드 등을 특정 카테고리로 분류하는 작업에서도 임베딩이 중요한 역할을 할 수 있습니다.

임베딩의 이러한 다양한 사용 사례는 그 유용성과 뛰어난 표현 능력을 증명합니다.

검색(Search)

사용자가 쿼리를 입력할 때, 우리는 쿼리 문자열과 가장 관련성이 높은 결과를 보여주고 싶습니다. 임베딩을 사용하면 검색 인덱스의 각 항목과 쿼리 문자열을 벡터로 변환할 수 있습니다. 쿼리와 가장 관련성이 높은 항목들은 쿼리 벡터와 가장 가까운 벡터를 가진 항목들입니다. 이를 통해 더 문맥적으로 관련된 검색 경험을 제공할 수 있습니다.

이러한 접근 방식은 단순히 키워드 일치만을 고려하는 전통적인 검색 알고리즘과는 달리, 데이터의 의미론적 유사성을 고려하기 때문에 훨씬 더 정교한 검색 결과를 생성합니다. 예를 들어, “사과”라는 쿼리에 대해 “과일”이라는 단어가 포함된 문서나 항목이 더 높은 순위에 오를 수 있습니다. 이는 “사과”와 “과일”이 의미론적으로 유사하기 때문에 가능한 것입니다.

클러스터링

클러스터링은 유사성을 기반으로 텍스트 문자열을 그룹화하는 작업을 포함합니다. 각 텍스트 문자열을 임베딩으로 표현함으로써 각 임베딩 쌍 사이의 거리를 계산할 수 있습니다. 의미론적으로 유사한 텍스트 문자열은 더 가까운 임베딩을 가질 것이므로 그룹화될 수 있습니다. 이것은 주제 모델링이나 고객 세분화와 같은 작업에서 특히 유용할 수 있습니다.

예를 들어, 주제 모델링에서는 여러 텍스트 문서가 주어졌을 때, 이 문서들을 주제별로 분류하고 싶을 수 있습니다. 임베딩을 사용하면 문서들 사이의 의미론적 유사성을 측정할 수 있어, 유사한 주제를 가진 문서들을 함께 그룹화할 수 있습니다.

고객 세분화의 경우에는, 고객의 행동이나 피드백을 텍스트 데이터로 수집한 후, 이를 임베딩으로 변환할 수 있습니다. 이렇게 하면 고객들을 유사한 성향이나 필요에 따라 그룹화할 수 있으며, 이를 통해 더 효율적인 마케팅 전략을 세울 수 있습니다.

추천

추천 시스템에서 항목(예: 책이나 영화)이 임베딩으로 표현된다면, 사용자가 이전에 상호 작용한 항목과 유사한 항목을 추천할 수 있습니다. 이 유사성은 다른 항목의 임베딩 간의 거리에 의해 결정되며, 더 개인화된 추천을 제공할 수 있습니다.

예를 들어, 사용자가 과거에 특정 장르의 영화를 좋아했다면, 그 장르와 유사한 임베딩을 가진 다른 영화를 추천할 수 있습니다. 또는 사용자가 특정 작가의 책을 읽었다면, 그 작가와 유사한 스타일이나 주제를 가진 다른 작가의 책을 추천할 수 있습니다.

이러한 방식은 추천 시스템이 단순히 ‘가장 인기 있는 항목’이나 ‘최근에 추가된 항목’만을 제안하는 것보다 훨씬 더 개인화된 경험을 제공할 수 있습니다. 임베딩을 사용하면 사용자의 선호나 행동을 더 깊이 이해하고, 이를 기반으로 더 정확하고 관련성 높은 추천을 할 수 있습니다.

이상 탐지

이상치는 대다수와 상당히 다른 데이터 포인트를 의미합니다. 텍스트 문자열을 임베딩으로 변환함으로써, 벡터 공간에서 나머지와 가장 먼 벡터로 이상치를 발견할 수 있습니다. 이것은 사기 탐지나 텍스트 데이터의 오류 탐지와 같은 애플리케이션에서 사용될 수 있습니다.

예를 들어, 금융 거래 데이터에서 사기 탐지를 수행할 때, 각 거래를 임베딩으로 표현할 수 있습니다. 대부분의 정상적인 거래는 임베딩 공간에서 서로 가까워질 것이며, 사기성 거래는 이들과 상당히 다른 위치에 있을 가능성이 높습니다. 따라서, 이러한 이상치를 식별함으로써 사기성 거래를 빠르게 탐지할 수 있습니다.

또한 텍스트 데이터에서의 오류 탐지에도 이용될 수 있습니다. 예를 들어, 기계 번역, 자연어 처리, 또는 로그 파일에서의 오류를 탐지하기 위해 텍스트 문자열을 임베딩으로 변환 후, 일반적인 패턴과 크게 벗어난 벡터를 찾아낼 수 있습니다. 이를 통해 문제가 될 수 있는 이상치를 빠르게 식별하고 조치를 취할 수 있습니다.

다양성 측정

임베딩을 사용하면 텍스트 문자열 집합의 다양성을 측정할 수 있습니다. 임베딩의 분포를 분석함으로써, 데이터셋의 다양성을 정량화할 수 있습니다. 예를 들어, 밀집되어 있는 벡터 집합은 다양성이 낮음을 나타내며, 더 넓게 퍼져 있는 집합은 높은 다양성을 나타냅니다. 이것은 콘텐츠 추천에서 다양성을 확보하거나, 소셜 미디어 데이터에서 의견의 다양성을 이해하는 등 여러 유용한 응용 분야가 있습니다.

예를 들어, 콘텐츠 추천 시스템에서는 사용자에게 너무 편향된 또는 동일한 유형의 콘텐츠만을 제공하는 것을 피하기 위해 다양성을 측정할 수 있습니다. 임베딩의 분포를 분석하여 다양한 콘텐츠를 추천하도록 알고리즘을 조정할 수 있습니다.

또한, 소셜 미디어 플랫폼에서는 사용자들의 의견이 얼마나 다양한지를 파악하는 데에 임베딩을 사용할 수 있습니다. 예를 들어, 특정 이슈나 주제에 대한 사용자 의견이 한쪽으로 치우쳐져 있는지, 아니면 다양한 의견이 잘 반영되어 있는지를 확인할 수 있습니다. 이러한 정보는 플랫폼 운영자나 연구자에게 유용한 통찰력을 제공할 수 있습니다.

분류

텍스트 문자열을 분류하기 위해 임베딩을 사용하여 텍스트를 벡터로 변환하고, 이 벡터들을 기반으로 기계 학습 모델을 훈련시킬 수 있습니다. 새로운 텍스트 문자열에 대한 임베딩을 생성하고, 모델은 이 임베딩이 다른 클래스의 임베딩과 얼마나 가까운지를 기반으로 클래스를 예측합니다. 이것은 스팸 탐지, 감정 분석, 문서 분류와 같은 작업에 사용될 수 있습니다.

예를 들어, 스팸 메일 탐지에서는 각 이메일 메시지를 임베딩으로 변환한 뒤, 이를 사용하여 스팸인지 아닌지를 분류하는 모델을 훈련시킬 수 있습니다. 감정 분석에서는 사용자 리뷰나 피드백을 임베딩으로 변환하여, 긍정적인지 부정적인지를 판단하는 모델을 훈련시킬 수 있습니다. 문서 분류에서는 각 문서를 임베딩으로 변환하고, 이를 기반으로 문서가 어떤 카테고리에 속하는지를 판단하는 모델을 만들 수 있습니다.

이러한 방식은 텍스트 데이터를 다루는 다양한 분야에서 매우 유용하며, 임베딩을 통해 고차원의 복잡한 텍스트 데이터를 저차원의 밀집 벡터로 변환함으로써, 기계 학습 알고리즘이 더 효율적으로 작동할 수 있게 합니다.

Supabase에서 벡터 활성화

pgvector 는 PostgreSQL 확장 기능 중 하나로, 데이터베이스에 직접 벡터 임베딩을 저장하고 쿼리할 수 있게 해줍니다.

첫 번째 단계는 벡터 확장을 활성화하는 것입니다. 이 작업은 Supabase의 웹 인터페이스를 통해 수행할 수 있습니다. ‘데이터베이스(Database)’로 이동한 후 ‘확장 기능(Extensions)’을 선택하면 됩니다.

이렇게 하면 Supabase 데이터베이스에서 pgvector 확장 기능을 활성화할 수 있습니다. 이 확장 기능을 활성화하면, 벡터 데이터를 쉽게 저장하고 검색할 수 있게 되므로, 앞서 언급한 다양한 용도(검색, 클러스터링, 추천, 이상 탐지, 다양성 측정, 분류 등)에 활용할 수 있습니다.

모달의 필드에서, ‘public schema’를 선택한 후 벡터 버튼을 토글하고 ‘확장 기능 활성화(Enable extension)’를 클릭합니다.

이렇게 하면 pgvector 확장 기능이 Supabase 데이터베이스 내의 public schema에 활성화됩니다. 이제 이 확장 기능을 사용하여 벡터 데이터를 저장하고 쿼리할 수 있게 되므로, 다양한 애플리케이션에서 높은 차원의 데이터를 효율적으로 다룰 수 있게 됩니다.

다음으로, 포스트와 각각의 임베딩을 저장할 ‘posts’ 테이블을 생성합니다.

이렇게 하면 ‘posts’ 테이블 내에 각 포스트와 관련된 벡터 임베딩을 저장할 수 있게 됩니다. 테이블을 생성하는 과정에서는 pgvector 확장 기능이 제공하는 데이터 타입을 사용하여, 임베딩 데이터를 효율적으로 저장하도록 설계할 수 있습니다.

이렇게 설정하면, 다양한 분석과 연산을 데이터베이스 레벨에서 직접 수행할 수 있게 되어, 애플리케이션의 성능과 효율성을 높일 수 있습니다.

pgvector 확장 기능은 ‘vector’라는 새로운 데이터 타입을 활성화합니다. 위에서 언급한 ‘posts’ 테이블에서는 이 새로운 ‘vector’ 데이터 타입을 사용하는 ’embedding’이라는 컬럼을 생성합니다. 또한 원본 제목과 내용을 저장할 텍스트 컬럼도 추가합니다. 이 컬럼들은 특정 임베딩을 생성하는 데 사용된 원본 데이터입니다.

이렇게 설정하면, 각 포스트에 대한 임베딩 정보와 원본 제목 및 내용을 함께 저장할 수 있게 됩니다. ‘vector’ 데이터 타입을 사용하면, 고차원의 임베딩 데이터를 효율적으로 저장하고 쿼리할 수 있으므로, 데이터베이스 작업이 더욱 간편해지고 성능도 향상됩니다.

OpenAI를 사용한 임베딩 생성

OpenAI는 주어진 텍스트 문자열에서 언어 모델을 사용하여 임베딩을 생성하는 데 도움을 주는 API를 제공합니다. 텍스트 데이터(블로그 포스트, 문서, 또는 우리 조직의 지식 저장소 등)를 입력하면, 부동소수점 숫자의 벡터가 출력됩니다. 이 벡터는 기본적으로 입력 텍스트의 “컨텍스트”를 캡슐화합니다.

예를 들어, 블로그 포스트나 기술 문서를 입력으로 주면, OpenAI의 언어 모델은 이러한 텍스트의 의미나 컨텍스트를 파악하여 고차원의 텍스트 데이터를 저차원의 벡터 형태로 변환할 수 있습니다. 이렇게 생성된 임베딩은 Supabase의 ‘posts’ 테이블에 ’embedding’ 컬럼으로 저장될 수 있고, 이를 활용해 다양한 분석과 작업을 수행할 수 있습니다.

OpenAI를 사용하여 임베딩을 생성하기 위해, 우선 개발 환경을 설정하겠습니다. 필요한 의존성은 다음 명령어를 사용하여 설치합니다:

npm i express @supabase/supabase-js openai dotenv

의존성에는 서버 작업을 처리하기 위한 Express 프레임워크, Supabase 데이터베이스와 상호 작용하기 위한 Supabase 클라이언트, 임베딩을 생성하기 위한 OpenAI 패키지, 그리고 환경 변수를 관리하기 위한 dotenv 패키지가 포함됩니다.

이러한 패키지들은 각각 서버의 기본 구조를 설정하고 데이터베이스와 통신하며, 텍스트 데이터를 벡터 형태의 임베딩으로 변환하고, 환경 변수를 안전하게 관리하는 데 사용됩니다. 이렇게 환경을 설정한 후, OpenAI API를 사용하여 텍스트 데이터의 임베딩을 생성하고, 이를 Supabase 데이터베이스에 저장할 수 있습니다.

필요한 모듈을 import한 후에는 express() 메서드를 사용하여 Express 애플리케이션을 초기화합니다. Supabase 클라이언트는 Supabase URL과 키에 대한 환경 변수를 사용하여 생성됩니다. 이 클라이언트는 우리의 Supabase 데이터베이스와 상호작용합니다.

OpenAI API의 경우, 환경 변수에서 OpenAI 조직 ID와 API 키를 사용하여 OpenAI 설정 객체를 생성합니다. 설정이 준비되면 new openai.OpenAIApi() 메서드를 사용하여 OpenAI API 클라이언트를 인스턴스화합니다.

마지막으로, Express 애플리케이션을 시작하여 3001 포트에서 요청을 수신하도록 설정합니다. 이제 우리의 애플리케이션은 OpenAI와 Supabase와 상호 작용하여 임베딩을 생성하고 저장할 준비가 되었습니다.

이렇게 하면 사용자가 애플리케이션에 특정 텍스트 데이터를 입력하거나 요청을 보내면, OpenAI API를 통해 해당 데이터의 임베딩을 생성할 수 있습니다. 생성된 임베딩은 Supabase 데이터베이스에 저장되어, 다양한 분석과 작업에 활용될 수 있습니다.

애플리케이션은 이제 텍스트 데이터를 벡터 형태로 효과적으로 변환하고, 이를 안전하고 효율적으로 저장하며, 필요한 분석 및 작업을 수행할 수 있습니다. 이로써 AI와 데이터베이스 기술을 통합하여 더욱 강력한 애플리케이션을 구축할 수 있습니다.

const express = require("express");
require("dotenv").config();
const supabase = require("@supabase/supabase-js");
const openai = require("openai");

const app = express();

const supabaseClient = supabase.createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_KEY
);

const openAiConfiguration = new openai.Configuration({
  organization: process.env.OPENAI_ORG,
  apiKey: process.env.OPENAI_API_KEY,
});

이 코드 스니펫은 Node.js 및 Express를 사용한 웹 애플리케이션에서 필요한 모듈을 import하고, Supabase 및 OpenAI API 클라이언트를 초기화하는 기본 구조를 보여줍니다.

  1. express 모듈을 import하여 Express 애플리케이션을 생성합니다.
  2. dotenv 모듈을 import하여 환경 변수를 로드합니다. 이를 통해 .env 파일에 저장된 설정값을 사용할 수 있게 됩니다.
  3. @supabase/supabase-js 모듈을 import하여 Supabase 클라이언트를 생성합니다. 환경 변수에서 Supabase URL과 API 키를 가져와서 클라이언트를 초기화합니다.
  4. openai 모듈을 import하여 OpenAI API에 필요한 설정을 진행합니다. 환경 변수에서 OpenAI 조직 ID와 API 키를 사용합니다.

이렇게 초기 설정을 마치면, 이후에는 이 클라이언트들을 사용하여 텍스트 데이터의 임베딩을 생성하고 데이터베이스에 저장하는 등의 작업을 진행할 수 있습니다. 이 코드는 웹 애플리케이션의 기본 틀을 제공하며, 여기에 로직을 추가하여 필요한 기능을 구현할 수 있습니다.

아래는 두 개의 API 엔드포인트와 임베딩을 생성하는 도우미 함수를 구현하는 방법에 대한 간단한 예시입니다. 이 예시는 설명을 위한 것으로, 모든 예외 처리나 가능한 오류는 다루지 않았습니다.

// OpenAI를 사용해 임베딩을 생성하는 도우미 함수
async function generateEmbedding(text) {
  const openaiClient = new openai.OpenAIApi(openAiConfiguration);
  const result = await openaiClient.createEmbedding({
    text: text
  });
  return result.data.embedding;
}

// 게시물과 해당 임베딩을 저장하는 API 엔드포인트
app.post("/posts", async (req, res) => {
  const text = req.body.text;
  const embedding = await generateEmbedding(text);

  const { data, error } = await supabaseClient
    .from("posts")
    .insert([{ text: text, embedding: embedding }]);

  if (error) {
    return res.status(400).json({ error });
  }
  return res.status(200).json({ data });
});

// 유사한 게시물을 검색하는 API 엔드포인트
app.get("/posts/similar", async (req, res) => {
  const queryText = req.query.text;
  const queryEmbedding = await generateEmbedding(queryText);

  // 유사한 임베딩을 가진 게시물을 찾는 로직을 구현해야 함
  // 이 부분은 단순한 예시입니다
  const similarPosts = await findSimilarPosts(queryEmbedding);

  return res.status(200).json({ similarPosts });
});

// 서버 시작
app.listen(3001, () => {
  console.log("Server running on http://localhost:3001");
});
  1. generateEmbedding: 이 도우미 함수는 주어진 텍스트 문자열에 대한 임베딩을 OpenAI API를 사용하여 생성합니다. 이 함수는 비동기 함수이므로 결과를 얻기 위해 await을 사용할 수 있습니다.
  2. /posts: 이 API 엔드포인트는 클라이언트로부터 텍스트를 받고, 도우미 함수를 사용하여 임베딩을 생성합니다. 그 다음, 원래의 텍스트와 그 임베딩을 Supabase의 “posts” 테이블에 저장합니다.
  3. /posts/similar: 이 API 엔드포인트는 텍스트 쿼리를 받아 임베딩을 기반으로 유사한 게시물을 찾습니다. findSimilarPosts 함수는 예시용이므로, 실제로는 여러분의 특별한 상황에 맞게 구현해야 합니다.
  4. 마지막으로, Express 애플리케이션은 시작되어 3001 포트에서 리스닝합니다.

이 예시를 바탕으로 에러 처리, 사용자 인증 등 추가적인 로직을 구현할 수 있습니다.

검색 기능 구현하기

게시물에 대한 임베딩을 생성하면, 코사인 거리와 같은 벡터 수학 연산을 사용하여 그 유사성을 계산하는 것은 상당히 간단해집니다. 코사인 거리의 주요 사용 사례 중 하나는 애플리케이션에서 검색 기능을 구현하는 것입니다.

이 글을 쓰는 시점에서, Supabase 클라이언트를 사용하여 벡터를 비교하는 API 지원은 없습니다. 따라서 임베딩에 대한 유사성 검색을 용이하게 하기 위해 데이터베이스에 함수를 생성해야 할 것입니다.

다음의 PostgreSQL 함수인 ‘match_posts’는 주어진 쿼리와 게시물의 벡터 임베딩 간의 코사인 거리를 기반으로 유사한 게시물을 찾도록 설계되었습니다.

임베딩을 기반으로 한 코사인 유사도를 사용하여 검색 기능을 구현하기 위해, PostgreSQL에서 커스텀 함수를 만들 수 있습니다. 아래는 이러한 함수를 정의하는 방법에 대한 간단한 예입니다.

CREATE OR REPLACE FUNCTION match_posts(query_vector float8[])
RETURNS TABLE(id integer, similarity float8) AS $$
BEGIN
  RETURN QUERY
  SELECT
    id,
    query_vector <-> embedding AS similarity  -- `<->`는 코사인 거리를 계산하는 연산자입니다
  FROM
    posts
  ORDER BY
    similarity ASC;  -- 요구 사항에 따라 DESC를 사용할 수도 있습니다
END;
$$ LANGUAGE plpgsql;

이 예시에서 match_posts는 PostgreSQL 함수로, 부동 소수점 숫자의 배열(query_vector)을 입력으로 받습니다. 이 입력은 쿼리 텍스트의 벡터 임베딩입니다. 함수는 쿼리 벡터와의 코사인 유사도를 기준으로 포스트 ID를 반환하는 테이블을 출력합니다.

  • 여기서 연산자 <->는 데이터베이스에서 코사인 거리를 계산하는 방법을 나타내는 플레이스홀더입니다.
  • 함수는 posts 테이블을 쿼리하며, 각 포스트에는 idembedding 필드가 있습니다.
  • 포스트는 유사도 점수를 기준으로 오름차순으로 정렬됩니다. 즉, 가장 유사한 포스트가 먼저 반환됩니다. 특정 요구 사항에 따라 내림차순으로 정렬할 수도 있습니다.

이 함수를 정의한 후, 주어진 쿼리와 유사한 포스트를 찾기 위해 애플리케이션에서 이 함수를 호출할 수 있습니다.

SELECT * FROM match_posts('{0.1, 0.2, 0.3, 0.4}');

이 SQL 명령은 쿼리 벡터 {0.1, 0.2, 0.3, 0.4}와 얼마나 유사한지에 따라 정렬된 포스트 ID와 유사도 점수의 목록을 반환합니다.

이 방법을 통해 임베딩과 커스텀 데이터베이스 함수를 사용하여 애플리케이션에 고급 검색 기능을 통합할 수 있습니다.

결론

이 가이드에서는 Supabase의 PostgreSQL 확장인 pgvector가 OpenAI와 함께 어떻게 벡터 임베딩을 관리하고 처리할 수 있는지에 대해 살펴보았습니다. 우리는 벡터와 임베딩의 기본적인 개념에 대해 알아보고, 이들이 어떻게 유사성 검색과 같은 고급 기능을 가능하게 하는지에 대해 살펴보았습니다.

또한 코드 측면에서도 다루었는데, 기본적인 Express 서버를 설정하여 API 요청을 처리하는 것부터, OpenAI로 임베딩을 생성하고 데이터베이스 내에서 이를 사용하는 방법까지 알아보았습니다. 이러한 도구를 활용함으로써, 우리는 더 지능적이고 문맥에 맞는 애플리케이션을 개발할 수 있습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다