벡터 검색에 대한 간단한 소개

이 글은 시맨틱 검색이라고도 하는 벡터 검색의 복잡성과 Elasticsearch에서 어떻게 구현되는지에 대해 자세히 설명하는 3편의 시리즈 중 첫 번째 글입니다.

벡터 검색부터 강력한 REST API까지, Elasticsearch는 개발자에게 가장 폭넓은 검색 도구 키트를 제공합니다. GitHub의 샘플 노트북을 살펴보고 새로운 기능을 시험해 보세요. 무료 체험판을 시작하거나 지금 바로 Elasticsearch를 로컬에서 실행할 수도 있습니다.

이 글은 시맨틱 검색이라고도 하는 벡터 검색의 복잡성과 Elasticsearch에서 어떻게 구현되는지에 대해 자세히 설명하는 3편의 시리즈 중 첫 번째 글입니다.

이 첫 번째 파트에서는 벡터 임베딩의 기본 사항과 벡터 검색의 내부 작동 방식에 대한 일반적인 소개에 중점을 둡니다.

첫 번째 글에서 배운 모든 지식으로 무장하고 두 번째 글에서는 Elasticsearch에서 벡터 검색을 설정하는 방법에 대해 자세히 안내합니다.

세 번째 파트에서는 앞선 두 파트에서 배운 내용을 활용하고, 그 지식을 바탕으로 Elasticsearch에서 강력한 하이브리드 검색 쿼리를 작성하는 방법에 대해 자세히 알아보겠습니다.

이 글의 본론으로 들어가기 전에 시간을 거슬러 올라가 시맨틱 검색의 핵심 개념인 벡터의 역사에 대해 살펴봅시다.

벡터는 새로운 것이 아닙니다.

2022년 11월 ChatGPT가 등장한 이후 "벡터 검색"에 대해 듣거나 읽지 않고는 단 하루도 지나가지 않는다는 사실에 모두가 동의할 것입니다. 어디에나 있고 너무 널리 퍼져 있어 이제 막 나온 새로운 첨단 기술이라는 인상을 받기도 하지만, 사실 이 기술은 60년 이상 전부터 사용되어 온 기술입니다! 이 주제에 대한 연구는 1960년대 중반에 시작되었으며, 1978년 코넬 대학교의 정보 검색 전문가인 제라드 살튼과 그의 동료들이 최초의 연구 논문을 발표했습니다. 고밀도 및 희소 벡터 모델에 대한 Salton의 연구는 현대 벡터 검색 기술의 근간을 이루고 있습니다.

지난 20년 동안 그의 연구를 기반으로 한 다양한 벡터 DBMS가 개발되어 시장에 출시되었습니다. 여기에는 2019년에 벡터 검색 작업을 시작한 Apache Lucene 프로젝트 기반의 Elasticsearch가 포함됩니다.

벡터는 이제 어디에나 있고 널리 퍼져 있으므로 벡터를 활용하기 전에 먼저 벡터의 기본 이론과 내부 작동 원리를 잘 이해하는 것이 중요합니다. 자세히 알아보기 전에 어휘 검색과 벡터 검색의 차이점을 간단히 살펴봄으로써 두 검색이 어떻게 다르고 어떻게 서로를 보완할 수 있는지 더 잘 이해할 수 있도록 하겠습니다.

벡터 검색을 소개하는 쉬운 방법은 익숙한 기존의 어휘 검색과 비교하는 것입니다. 일반적으로 시맨틱 검색이라고도 하는 벡터 검색과 어휘 검색은 작동 방식이 매우 다릅니다. 어휘 검색은 우리 모두가 Elasticsearch에서 수년 동안 사용해온 검색입니다. 간단히 요약하자면, 색인되고 쿼리되는 내용의 실제 의미를 이해하려고 노력하는 것이 아니라, 사용자가 쿼리에 입력하는 단어의 리터럴 또는 그 변형(어간, 동의어 등)을 TF-IDF와 같은 유사성 알고리즘을 사용하여 이전에 데이터베이스에 색인된 모든 리터럴과 어휘적으로 일치시키기 위해 많은 노력을 기울이는 것입니다.

보시다시피 왼쪽 상단에 있는 세 개의 문서는 토큰화되어 분석되고 있습니다. 그런 다음 결과 용어가 역 인덱스에 색인되어 분석된 용어를 해당 용어가 포함된 문서 ID에 간단히 매핑합니다. 모든 용어는 한 번만 표시되며 어떤 문서에서도 공유되지 않는다는 점에 유의하세요. "멋진 독일어 선생님"을 검색하면 세 문서 모두 다른 점수로 일치하지만, 그 중 어느 것도 쿼리의 진정한 의미를 파악하지 못합니다.

아래 그림 2에서 볼 수 있듯이 다의어 또는 동음이의어, 즉 철자는 같지만 다른 의미 (오른쪽, 손바닥, 방망이, 평균 등)를 가진 단어를 다룰 때는 더욱 까다로워집니다. 세 가지 의미를 가질 수 있는 '오른쪽'이라는 단어를 예로 들어 어떤 일이 일어나는지 살펴봅시다.

"나는 옳지 않다"를 검색하면 첫 번째 반환된 결과와 정반대의 의미를 가진 문서가 반환됩니다. 완전히 동일한 용어를 검색하지만 순서를 달리하여 다른 의미를 생성하는 경우(예: "우회전 "과 "우회전 ") 완전히 동일한 결과가 표시됩니다(예: 세 번째 문서 "우회전"). 물론 쿼리가 지나치게 단순화되어 있고 구문 검색과 같은 고급 쿼리를 사용하지는 않지만, 이는 어휘 검색이 색인된 내용과 검색된 내용의 진정한 의미를 이해하지 못한다는 것을 보여주는 예시입니다. 명확하지 않더라도 걱정하지 마세요. 세 번째 글에서 이 예시를 다시 살펴보고 벡터 검색이 이 경우에 어떻게 도움이 되는지 알아보겠습니다.

구조화된 데이터를 색인하는 방법(매핑, 텍스트 분석, 수집 파이프라인 등)과 쿼리를 작성하는 방법(영리하게 만들어진 DSL 쿼리, 쿼리 용어 분석 등)을 제어할 수 있다면 어휘 검색 엔진으로 놀라운 일을 할 수 있다는 것은 의심의 여지가 없습니다! 어휘 검색 기능에 관한 Elasticsearch의 실적은 정말 놀랍습니다. 지난 몇 년 동안 어휘 검색 분야를 대중화하고 개선한 성과는 정말 놀랍습니다.

그러나 자유 텍스트 질문을 해야 하는 사용자에게 비정형 데이터 (이미지, 동영상, 오디오, 원시 텍스트 등)에 대한 쿼리 지원을 제공해야 하는 경우 어휘 검색만으로는 부족합니다. 또한 곧 살펴보겠지만 쿼리가 텍스트가 아닌 이미지일 수도 있습니다. 이러한 상황에서 어휘 검색이 부적절한 주된 이유는 비정형 데이터는 정형 데이터와 동일한 방식으로 색인화하거나 쿼리할 수 없기 때문입니다. 비정형 데이터를 다룰 때는 의미론이 중요한 역할을 합니다. 시맨틱이란 무엇을 의미하나요? 아주 간단하게 말하자면, 의미입니다!

이미지 검색 엔진(예: Google 이미지 검색 또는 렌즈)의 간단한 예를 들어 보겠습니다. 이미지를 끌어다 놓으면 Google 시맨틱 검색 엔진이 쿼리한 이미지와 가장 유사한 이미지를 찾아서 반환합니다. 아래 그림 3에서 왼쪽에는 독일 셰퍼드 사진이 있고 오른쪽에는 검색된 모든 유사한 사진이 표시되며, 첫 번째 결과는 제공된 사진과 동일한 사진(즉, 가장 유사한 사진)입니다.

사람에게는 간단하고 논리적으로 들리지만 컴퓨터에게는 완전히 다른 이야기입니다. 이것이 바로 벡터 검색이 가능하게 하고 달성하는 데 도움이 되는 것입니다. 최근 전 세계가 목격했듯이 벡터 검색의 잠재력은 엄청납니다. 이제 후드를 열고 그 아래에 무엇이 숨어 있는지 알아봅시다.

벡터 임베딩

앞서 살펴본 것처럼 어휘 검색 엔진을 사용하면 텍스트와 같은 구조화된 데이터를 용어의 실제 의미와 관계없이 검색 시 일치할 수 있는 용어로 쉽게 토큰화할 수 있습니다. 그러나 비정형 데이터는 이미지, 동영상, 오디오 등 다양한 형태로 존재할 수 있으며, 동일한 토큰화 프로세스에 전혀 적합하지 않습니다. 또한 시맨틱 검색의 전체 목적은 데이터가 나타내는 의미에 따라 검색할 수 있도록 데이터를 색인하는 것입니다. 이를 어떻게 달성할 수 있을까요? 그 답은 두 단어에 있습니다: 바로 머신 러닝입니다! 더 정확하게는 딥러닝!

딥러닝은 데이터의 진정한 의미를 점진적으로 추출할 수 있는 여러 처리 계층으로 구성된 인공 신경망 기반 모델에 의존하는 머신러닝의 특정 영역입니다. 이러한 신경망 모델이 작동하는 방식은 인간의 뇌에서 많은 영감을 받았습니다. 아래 그림 4는 입력 및 출력 계층과 여러 개의 숨겨진 계층이 있는 신경망의 모습을 보여줍니다:

신경망의 진정한 위업은 하나의 비정형 데이터를 일련의 부동 소수점 값으로 변환할 수 있다는 점인데, 이를 임베딩 벡터 또는 간단히 임베딩이라고 합니다. 인간은 벡터를 2차원 또는 3차원 공간에서 시각화하면 벡터가 무엇인지 꽤 잘 이해할 수 있습니다. 벡터의 각 구성 요소는 2D x-y 평면 또는 3D x-y-z 공간의 좌표를 나타냅니다.

그러나 신경망 모델이 작동하는 임베딩 벡터는 수백 또는 수천 개의 차원을 가질 수 있으며 단순히 다차원 공간의 한 점을 나타낼 수 있습니다. 각 벡터 차원은 비정형 데이터의 특징 또는 특성을 나타냅니다. 이미지를 2048차원의 임베딩 벡터로 변환하는 딥러닝 모델을 통해 이를 설명해 보겠습니다. 이 모델은 그림 3에서 사용한 독일 셰퍼드 그림을 아래 표에 표시된 임베딩 벡터로 변환합니다. 여기서는 첫 번째와 마지막 세 요소만 표시하지만 테이블에는 2,042개의 열/차원이 더 있습니다.

is_redis_dogblue_skyno_gras게르만 셰퍼드is_tree
독일 셰퍼드 임베딩0.01210.95720.87350.11980.97120.0512

각 열은 모델의 차원이며 기본 신경망이 모델링하고자 하는 기능 또는 특성을 나타냅니다. 모델에 주어진 각 입력은 해당 입력이 2048개의 각 차원과 얼마나 유사한지에 따라 특성화됩니다. 따라서 임베딩 벡터의 각 요소 값은 해당 입력과 특정 차원의 유사성을 나타냅니다. 이 예시에서는 모델이 개와 독일 셰퍼드 사이의 높은 유사성과 푸른 하늘의 존재를 감지한 것을 볼 수 있습니다.

용어의 일치 여부만 알 수 있는 어휘 검색과 달리, 벡터 검색을 사용하면 비정형 데이터 조각이 모델이 지원하는 각 차원과 얼마나 유사한지 훨씬 더 잘 파악할 수 있습니다. 이처럼 임베딩 벡터는 비정형 데이터를 환상적으로 의미적으로 표현하는 역할을 합니다.

비법 소스

이제 비정형 데이터가 딥러닝 신경망에 의해 여러 차원에 걸쳐 데이터의 유사성을 포착하는 임베딩 벡터로 잘게 쪼개지는 방법을 알았으니, 이러한 벡터의 매칭이 어떻게 작동하는지 이해해야 합니다. 그 답은 매우 간단합니다. 서로 가까운 벡터를 임베딩하면 의미적으로 유사한 데이터 조각을 나타냅니다. 따라서 벡터 데이터베이스를 쿼리할 때 먼저 모든 비정형 데이터 색인에 사용된 것과 동일한 모델을 사용하여 검색 입력(이미지, 텍스트 등)을 임베딩 벡터로 변환하고, 최종 목표는 해당 쿼리 벡터와 가장 가까운 이웃 벡터를 찾는 것입니다. 따라서 쿼리 벡터와 데이터베이스에 색인된 모든 기존 벡터 사이의 '거리' 또는 '유사성'을 측정하는 방법만 알아내면 됩니다.

거리 및 유사성

다행히도 두 벡터 사이의 거리를 측정하는 것은 벡터 산술 덕분에 쉽게 해결할 수 있는 문제입니다. 이제 Elasticsearch와 같은 최신 벡터 검색 데이터베이스에서 지원하는 가장 널리 사용되는 거리 및 유사도 함수에 대해 살펴보겠습니다. 경고, 수학이 앞서갑니다!

L1 거리

맨해튼 거리라고도 하는 두 벡터 x와 y의 L1 거리는 모든 요소의 쌍별 절대 차이를 합산하여 측정합니다. 당연히 거리 d가 작을수록 두 벡터는 더 가까워집니다. 아래에서 볼 수 있듯이 공식은 매우 간단합니다:

시각적으로 L1 거리는 아래 그림 5와 같이 설명할 수 있습니다:

x = (1, 2), y = (4, 3)과 같은 두 벡터 x와 y를 예로 들면 두 벡터의 L1 거리는 | 1 - 4 | + | 2 - 3 | = 4가 됩니다.

L2 거리

유클리드 거리라고도 하는 두 벡터 x와 y의 L2 거리는 먼저 모든 요소의 쌍별 차이의 제곱을 합한 다음 그 결과의 제곱근을 구하여 측정합니다. 기본적으로 두 점 사이의 최단 경로(빗변이라고도 함)입니다. L1과 마찬가지로, 거리 d가 작을수록 두 벡터는 더 가까워집니다:

L2 거리는 아래 그림 6에 나와 있습니다:

L1 거리에서 사용한 것과 동일한 두 개의 샘플 벡터 x와 y를 재사용하면 이제 L2 거리를 (14)2+(23)2=10으로(1 - 4)^2 + (2 - 3)^2 = 10으로 계산할 수 있습니다. 10의 제곱근을 구하면 3.16이 됩니다.

린프 거리

체비셰프 또는 체스판 거리라고도 하는 두 벡터 x와 y의 Linf(무한대의 경우) 거리는 간단히 두 요소 사이의 최장 거리 또는 축/차원 중 하나를 따라 측정한 최장 거리로 정의됩니다. 공식은 매우 간단하며 아래와 같습니다:

린프 거리의 표현은 아래 그림 7에 나와 있습니다:

다시, 동일한 두 개의 샘플 벡터 x와 y를 사용하여 최대 ( | 1 - 4 | , | 2 - 3 | ) = 최대 (3, 1) = 3으로 L 무한대 거리를 계산할 수 있습니다.

코사인 유사도

L1, L2, Linf와 달리 코사인 유사도는 두 벡터 x와 y 사이의 거리를 측정하는 것이 아니라 상대 각도, 즉 둘이 거의 같은 방향을 가리키고 있는지 여부를 측정합니다. 유사도 s가 높을수록 두 벡터가 "더 가깝다"는 의미입니다. 공식은 다시 매우 간단하며 아래와 같습니다:

두 벡터 간의 코사인 유사성을 표현하는 방법은 아래 그림 8에 나와 있습니다:

또한 코사인 값은 항상 [-1, 1] 간격에 있으므로 아래 그림 9와 같이 -1은 반대 유사성(즉, 두 벡터 사이의 180° 각도), 0은 무관한 유사성(즉, 90° 각도), 1은 동일성(즉, 0° 각도)을 의미합니다:


다시 한 번 동일한 샘플 벡터 x와 y를 재사용하고 위의 공식을 사용하여 코사인 유사도를 계산해 보겠습니다. 먼저 두 벡터의 내적을 (14)+(23)=10으로(1 - 4) + (2 - 3) = 10으로 계산할 수 있습니다. 그런 다음 두 벡터의 길이(크기라고도 함)를 곱합니다: (12+22)1/2(1^2 + 2^2)^{1/2} + (4^2 + 3^2)^{1/2} = 11.18034. 마지막으로 도트 곱을 곱한 길이 10 / 11.18034 = 0.894427(즉, 26° 각도)로 나누면 1에 매우 가깝기 때문에 두 벡터는 매우 유사하다고 볼 수 있습니다.

도트 제품 유사성

코사인 유사도의 한 가지 단점은 두 벡터 사이의 각도만 고려하고 크기(즉, 길이)는 고려하지 않기 때문에 두 벡터가 대략 같은 방향을 가리키지만 한쪽이 다른 쪽보다 훨씬 길어도 둘 다 비슷한 것으로 간주된다는 점입니다. 스칼라 또는 내적 곱이라고도 하는 도트 곱 유사성은 벡터의 각도와 크기를 모두 고려하여 훨씬 더 정확한 유사성 지표를 제공함으로써 이를 개선합니다.

도트 제품 유사성을 계산하는 데는 두 가지 동등한 공식이 사용됩니다. 첫 번째는 앞서 코사인 유사도의 분자에서 살펴본 것과 동일합니다:

두 번째 공식은 두 벡터의 길이에 두 벡터 사이의 각도의 코사인을 곱하기만 하면 됩니다:

도트 제품 유사성은 아래 그림 10에 시각화되어 있습니다:

마지막으로 샘플 x 및 y 벡터를 가져와 앞서 코사인 유사도 때와 마찬가지로 첫 번째 공식을 사용하여 (1 4- 4 ) + (2 3- 3 ) = 10으로 도트 곱 유사도를 계산합니다.

두 번째 공식을 사용하여 두 벡터의 길이를 곱합니다: (12+22)1/2(1^2 + 2^2)^{1/2} + (4^2 + 3^2)^{1/2} = 11.18034에 두 벡터 사이의 26° 각도의 코사인을 곱하면 11.18034 cos- cos (26°) = 10을 구할 수 있습니다.

한 가지 주목할 점은 모든 벡터가 먼저 정규화되면 (즉, 길이가 1이면) 도트 곱 유사도는 코사인 유사도(|x| |y| = 1이므로), 즉 두 벡터 사이의 각도의 코사인과 정확히 동일해진다는 점입니다. 나중에 살펴보겠지만, 벡터를 정규화하는 것은 벡터의 크기를 무의미하게 만들어 유사성이 단순히 각도에 초점을 맞추기 위해 채택하는 좋은 관행입니다. 또한 수십억 개의 벡터를 처리할 때 큰 문제가 될 수 있는 인덱싱 및 쿼리 시 거리 계산의 속도를 높여줍니다.

빠른 요약

지금까지 많은 정보를 살펴보았으니 잠시 멈춰서 현재 상황을 간단히 정리해 보겠습니다. 우리는 배웠습니다...

  • ...시맨틱 검색은 비정형 데이터를 다차원 임베딩 벡터로 변환하는 데 탁월한 딥러닝 신경망 모델을 기반으로 합니다.
  • ...모델의 각 차원은 비정형 데이터의 기능 또는 특성을 나타냅니다.
  • ...임베딩 벡터는 주어진 비정형 데이터 조각이 각 차원과 얼마나 유사한지를 나타내는 유사도 값의 시퀀스(각 차원에 대해 하나씩)입니다.
  • ... 두 벡터가 "더 가깝게"(즉, 가장 가까운 이웃) 있을수록 의미적으로 유사한 개념을 더 많이 나타냅니다.
  • ...거리 함수(L1, L2, Linf)를 사용하면 두 벡터가 얼마나 가까운지 측정할 수 있습니다.
  • ...유사도 함수(코사인과 도트 곱)를 사용하면 두 벡터가 얼마나 같은 방향으로 향하고 있는지 측정할 수 있습니다.

이제 마지막으로 살펴봐야 할 부분은 벡터 검색 엔진 자체입니다. 쿼리가 들어오면 먼저 쿼리를 벡터화한 다음 벡터 검색 엔진이 해당 쿼리 벡터에 가장 가까운 이웃 벡터를 찾습니다. 쿼리 벡터와 데이터베이스의 모든 벡터 사이의 거리 또는 유사성을 측정하는 무차별 대입 방식은 작은 데이터 세트에서는 효과가 있지만 벡터의 수가 증가하면 금방 부족해집니다. 다르게 말하면 수백만, 수십억, 심지어 수조 개의 벡터를 색인하고 합리적인 시간 내에 쿼리 벡터의 가장 가까운 이웃을 찾을 수 있는 방법은 무엇일까요? 따라서 정밀도를 크게 떨어뜨리지 않으면서 최대한 빠르게 가장 가까운 이웃을 찾아낼 수 있도록 최적의 벡터 인덱싱 방법을 찾아내야 합니다.

벡터 검색 알고리즘 및 기법

수년 동안 많은 연구팀이 매우 영리한 벡터 검색 알고리즘을 개발하기 위해 많은 노력을 기울여 왔습니다. 여기에서는 주요 기능에 대해 간략하게 소개하겠습니다. 사용 사례에 따라 어떤 것이 다른 것보다 더 적합한 경우도 있습니다.

앞서 데이터베이스에 존재하는 모든 벡터와 쿼리 벡터를 비교하는 무차별 대입 방식을 언급하면서 선형 검색 또는 플랫 인덱싱에 대해 간략하게 살펴봤습니다. 작은 데이터 세트에서는 잘 작동할 수 있지만, 벡터와 차원 수가 증가하면 성능이 급격히 저하됩니다(복잡도 O(n))).

다행히도 임베딩 벡터 사이의 거리를 미리 계산하고 클러스터, 트리, 해시 또는 그래프를 사용하여 유사한 벡터를 서로 가깝게 유지하는 방식으로 저장하고 구성하는 근사 최인접 이웃 (ANN)이라는 보다 효율적인 접근 방식이 있습니다. 이러한 접근 방식은 일반적으로 100%% 정확도를 보장하지 않기 때문에 '근사치'라고 합니다. 궁극적인 목표는 유사한 벡터를 포함할 가능성이 가장 높은 영역에만 집중하기 위해 검색 범위를 최대한 빨리, 그리고 최대한 많이 줄이거나 벡터의 차원을 줄이는 것입니다.

K-차원 트리

K 차원 트리 또는 KD 트리는 이진 검색 트리를 일반화한 것으로, K 차원 공간에 포인트를 저장하고 벡터가 색인되는 작은 왼쪽과 오른쪽 트리로 검색 공간을 계속 이등분하여 작동합니다. 검색 시 알고리즘은 가장 가까운 이웃(그림 11의 녹색 점)을 찾기 위해 쿼리 벡터(그림 11의 빨간색 점) 주변의 트리 가지 몇 개를 방문하기만 하면 됩니다. k개 이상의 이웃이 요청되면 알고리즘이 더 많은 이웃을 찾을 때까지 노란색 영역이 확장됩니다.

KD 트리 알고리즘의 가장 큰 장점은 일부 국소화된 트리 가지에만 빠르게 집중할 수 있어 대부분의 벡터를 고려 대상에서 제외할 수 있다는 점입니다. 그러나 이 알고리즘은 차원 수가 증가할수록 저차원 공간보다 더 많은 브랜치를 방문해야 하므로 효율성이 떨어집니다.

반전된 파일 색인

거꾸로 파일 인덱스(IVF) 방식은 서로 가까운 벡터를 공유 중심점에 할당하는 공간 분할 알고리즘이기도 합니다. 2D 공간에서는 그림 12와 같이 보로노이 다이어그램으로 이를 가장 잘 시각화할 수 있습니다:

위의 2D 공간은 20개의 클러스터로 분할되어 있으며, 각 클러스터의 중심은 검은색 점으로 표시되어 있음을 알 수 있습니다. 공간의 모든 임베딩 벡터는 중심이 가장 가까운 클러스터에 할당됩니다. 검색 시 알고리즘은 먼저 쿼리 벡터에 가장 가까운 중심을 찾아 집중해야 할 클러스터를 파악한 다음, 해당 영역과 필요한 경우 주변 영역까지 제로화하여 가장 가까운 이웃을 찾을 수 있습니다.

이 알고리즘은 고차원 공간에서 사용할 때 KD 트리와 동일한 문제를 겪습니다. 이를 차원의 저주라고 하며, 공간의 부피가 너무 커져서 모든 데이터가 희박해 보이고 더 정확한 결과를 얻기 위해 필요한 데이터의 양이 기하급수적으로 늘어날 때 발생합니다. 데이터가 희소하면 이러한 공간 분할 알고리즘이 데이터를 클러스터로 구성하기가 더 어려워집니다. 다행히도 아래에 자세히 설명된 것처럼 이 문제를 완화하는 다른 알고리즘과 기술이 있습니다.

양자화

양자화는 임베딩 벡터의 정밀도를 낮춤으로써 데이터베이스의 전체 크기를 줄일 수 있는 압축 기반접근 방식입니다. 이는 부동 소수점 벡터 값을 정수 값으로 변환하는 스칼라 양자화(SQ)를 사용하여 달성할 수 있습니다. 이렇게 하면 데이터베이스의 크기가 8배까지 줄어들 뿐만 아니라 메모리 사용량도 감소하고 검색 시 벡터 간의 거리 계산 속도도 빨라집니다.

또 다른 기법은 제품 양자화(PQ) 로, 먼저 공간을 저차원 하위 공간으로 나눈 다음 클러스터링 알고리즘(K-평균과 유사)을 사용하여 서로 가까운 벡터를 각 하위 공간에 그룹화합니다.

양자화는 차원 수가 줄어드는 차원 축소, 즉 벡터가 단순히 짧아지는 차원 축소와는 다릅니다.

계층적 탐색 가능한 작은 세계(HNSW)

이름만 보고 복잡해 보이더라도 걱정하지 마세요, 실제로는 그렇지 않으니까요! 간단히 말해 계층적 탐색 가능한 작은 세계는 매우 인기 있고 효율적인 다층 그래프 기반 알고리즘입니다. 아파치 루씬을 비롯한 다양한 벡터 데이터베이스에서 사용됩니다. 아래 그림 13에서 HNSW의 개념적 표현을 볼 수 있습니다.

최상위 레이어에서는 벡터들 사이에 가장 긴 연결 고리를 가진 극소수의 벡터, 즉 유사성이 가장 낮은 연결된 벡터의 그래프를 볼 수 있습니다. 더 낮은 레이어로 내려갈수록 더 많은 벡터를 발견할 수 있으며, 그래프는 점점 더 밀도가 높아져 서로 가까운 벡터가 많아집니다. 가장 낮은 레이어에서 모든 벡터를 찾을 수 있으며, 가장 유사한 벡터가 서로 가장 가까운 곳에 위치합니다.

검색 시 알고리즘은 임의의 진입점의 최상위 레이어에서 시작하여 쿼리 벡터에 가장 가까운 벡터(회색 점으로 표시됨)를 찾습니다. 그런 다음 한 레이어 아래로 이동하여 가장 낮은 레이어에 도달하여 쿼리 벡터와 가장 가까운 이웃을 찾을 때까지 위 레이어에 남긴 동일한 벡터에서 시작하여 동일한 과정을 한 레이어씩 차례로 반복합니다.

지역 민감 해싱(LSH)

지금까지 소개한 다른 모든 접근 방식과 같은 맥락에서, 위치 정보에 민감한 해싱은 검색 속도를 높이기 위해 검색 공간을 대폭 줄이려고 합니다. 이 기술을 사용하면 임베딩 벡터가 유사성 정보를 보존하면서 해시값으로 변환되므로 검색 공간은 궁극적으로 탐색해야 하는 그래프나 트리 대신 조회할 수 있는 간단한 해시 테이블이 됩니다. 해시 기반 방법의 가장 큰 장점은 임의의 (큰) 수의 차원을 포함하는 벡터를 고정 크기 해시에 매핑할 수 있어 정밀도를 크게 떨어뜨리지 않으면서 검색 시간을 크게 단축할 수 있다는 것입니다.

일반적으로 데이터를 해싱하는 방법, 특히 벡터를 임베딩하는 방법에는 여러 가지가 있지만 이 글에서는 각 방법에 대해 자세히 다루지는 않겠습니다. 기존의 해시 방식은 일반적으로 매우 비슷해 보이는 데이터에 대해 매우 다른 해시를 생성합니다. 임베딩 벡터는 실수 값으로 구성되므로 벡터 산술에서 서로 매우 가까운 것으로 간주되는 두 개의 샘플 실수 값(예: 0.73 및 0.74)을 가져와 몇 가지 일반적인 해싱 함수를 통해 실행해 보겠습니다. 아래 결과를 보면 일반적인 해싱 함수는 입력 간의 유사성을 유지하지 못한다는 것을 알 수 있습니다.

해싱 기능0.730.74
MD51342129d04cd2924dd06cead4cf0a3ca0aec1b15371bd979cfa66b0a50ebecc5
SHA149d2c3e0e44bff838e1db571a121be5ea874e8d9a534e76482ade9d9fe4bff3035a7f31f2f363d77
SHA25699d03fc3771fe6848d675339fc49eeb1cb8d99a12e6358173336b99a2ec530ea5ecbc825ba5c16856edfdaf0abc5c6c41d0d8a9c508e34188239521dc7645663

기존의 해싱 방식은 유사한 데이터 조각 간의 해싱 충돌을 최소화하려고 하지만, 지역 민감 해싱의 주요 목표는 정반대로, 즉 해싱 충돌을 최대화하여 유사한 데이터가 높은 확률로 같은 버킷에 속하도록 하는 것입니다. 이렇게 하면 다차원 공간에서 서로 가까이 있는 벡터를 임베딩하면 동일한 버킷에 속하는 고정된 크기의 값으로 해시됩니다. LSH는 해시된 벡터가 근접성을 유지할 수 있도록 해주기 때문에 데이터 클러스터링과 가장 가까운 이웃 검색에 매우 유용한 기술입니다.

모든 무거운 작업은 해시를 계산해야 하는 인덱싱 시점에 이루어지며, 검색 시에는 가장 가까운 임베딩 벡터가 포함된 버킷을 조회하기 위해 쿼리 벡터만 해시하면 됩니다. 후보 버킷이 발견되면 일반적으로 쿼리 벡터에 가장 가까운 이웃 벡터를 식별하기 위해 두 번째 라운드가 진행됩니다.

결론을 내리겠습니다.

벡터 검색을 소개하기 위해 이 글에서 꽤 많은 내용을 다루어야 했습니다. 어휘 검색과 벡터 검색의 차이점을 비교한 후, 딥러닝 신경망 모델이 어떻게 비정형 데이터의 의미를 파악하고 그 의미를 고차원 임베딩 벡터(모델의 각 차원에 따라 데이터의 유사성을 나타내는 부동 소수점 숫자 시퀀스)로 변환하는지에 대해 알아봤습니다. 벡터 검색과 어휘 검색은 경쟁하는 것이 아니라 상호 보완적인 정보 검색 기술이라는 점도 주목할 필요가 있습니다(이 시리즈의 3부에서 하이브리드 검색에 대해 자세히 살펴보겠습니다).

그 후, 벡터 검색의 기본 구성 요소인 거리(및 유사도) 함수를 도입하여 두 벡터의 근접성을 측정하고 벡터가 나타내는 개념의 유사성을 평가할 수 있게 했습니다.

마지막으로 트리, 그래프, 클러스터 또는 해시를 기반으로 하는 가장 인기 있는 벡터 검색 알고리즘과 기법의 다양한 종류를 살펴보았는데, 선형 무차별 대입 검색처럼 전체 공간을 둘러볼 필요 없이 다차원 공간의 특정 영역을 빠르게 좁혀 가장 가까운 이웃을 찾는 것이 목표입니다.

읽고 있는 내용이 마음에 드신다면 이 시리즈의 다른 부분도 꼭 확인해 보세요:

자주 묻는 질문

벡터 검색과 어휘 검색의 차이점은 무엇인가요?

어휘 검색은 색인 및 쿼리된 내용의 실제 의미를 이해하려고 하지 않고 단어의 리터럴 또는 그 변형과 일치시킵니다. 이와 대조적으로 벡터 검색은 데이터가 나타내는 의미에 따라 검색할 수 있는 방식으로 데이터를 색인화합니다.

관련 콘텐츠

최첨단 검색 환경을 구축할 준비가 되셨나요?

충분히 고급화된 검색은 한 사람의 노력만으로는 달성할 수 없습니다. Elasticsearch는 여러분과 마찬가지로 검색에 대한 열정을 가진 데이터 과학자, ML 운영팀, 엔지니어 등 많은 사람들이 지원합니다. 서로 연결하고 협력하여 원하는 결과를 얻을 수 있는 마법 같은 검색 환경을 구축해 보세요.

직접 사용해 보세요