Elasticsearch에서 지연 상호작용 모델 확장하기 - 2부

이 문서에서는 디스크 공간 사용량을 줄이고 계산 효율성을 개선하는 등 대규모 프로덕션 워크로드에 대비하여 지연 상호작용 벡터를 준비하는 기술을 살펴봅니다.

Elasticsearch는 여러분의 사용 사례에 가장 적합한 검색 솔루션을 구축하는 데 도움이 되는 새로운 기능으로 가득 차 있습니다. 최신 검색 AI 경험 구축에 관한 실습형 웨비나에서 이러한 기능을 실제로 활용하는 방법을 배워 보세요. 지금 무료 클라우드 체험을 시작하거나, 내 로컬 기기에서 Elastic을 사용해 볼 수 있습니다.

ColPali에 대한 이전 블로그에서는 Elasticsearch로 시각적 검색 애플리케이션을 생성하는 방법을 살펴보았습니다. 주로 ColPali와 같은 모델이 애플리케이션에 가져다주는 가치에 초점을 맞췄지만, 이러한 모델은 E5와 같은 바이인코더를 사용하는 벡터 검색에 비해 성능상의 단점이 있습니다.

1부의 예시를 바탕으로, 이 블로그는 다양한 기법과 Elasticsearch의 강력한 벡터 검색 도구를 사용하여 대규모 프로덕션 작업에 적합한 지연 상호작용 벡터를 준비하는 방법을 탐구합니다.

전체 코드 예시는 GitHub에서 확인하실 수 있습니다.

후기 상호작용 모델의 과제

ColPali는 인덱스에 있는 문서에 대해 페이지당 1000개 이상의 벡터를 생성합니다.

이로 인해 지연 상호작용 벡터를 사용할 때 두 가지 문제가 발생합니다.

  1. 디스크 공간: 이러한 모든 벡터를 디스크에 저장하면 상당한 저장 공간 사용량이 발생하며, 이는 확장 시 비용이 많이 들 수 있습니다.
  2. 계산: maxSimDotProduct() 비교를 사용하여 문서 순위를 매길 때는 각 문서에 대한 이러한 모든 벡터를 쿼리의 N 벡터와 비교해야 합니다.

이러한 문제를 해결하기 위한 몇 가지 기술을 살펴보겠습니다.

지연 상호작용 모델을 최적화하는 기술

비트 벡터

디스크 공간을 줄이기 위해 이미지를 비트 벡터로 압축할 수 있습니다. 다음과 같은 간단한 Python 함수를 사용하여 멀티 벡터를 비트 벡터로 변환할 수 있습니다.

이 함수의 핵심 개념은 간단합니다. 0보다 큰 값은 1이 되고, 0보다 작은 값은 0이 됩니다. 그 결과 0과 1로 이루어진 배열이 생성되며, 이를 16진수 스트링으로 변환하여 비트 벡터를 나타냅니다.

인덱스 매핑을 위해 element_type 매개변수를 bit로 설정했습니다.

인덱스에 새로운 비트 벡터를 모두 작성한 후, 다음 코드를 사용하여 비트 벡터의 순위를 매길 수 있습니다.

약간의 정확도를 희생하는 대신 비트 마스크, SIMD 등과 같은 최적화를 활용할 수 있는 해밍 거리(maxSimInvHamming(...))를 사용할 수 있습니다. 비트 벡터와 해밍 거리에 대한 자세한 내용은 블로그에서 확인하세요.

대안으로, 쿼리 벡터를 비트 벡터로 변환하지 않고 완전한 충실도의 지연 상호작용 벡터로 검색할 수 있습니다.

이는 비대칭 유사도 함수를 사용하여 벡터를 비교합니다.

두 비트 벡터 사이의 일정한 해밍 거리에 대해 생각해 보겠습니다. 문서 벡터 D가 있다고 가정해 봅시다.

그리고 쿼리 벡터 Q가 있습니다.

간단한 이진 양자화는 벡터 D10101101로, Q11111011로 변환합니다. 해밍 거리를 찾으려면 직접적인 비트 연산이 필요하며, 이는 매우 빠릅니다. 이 경우 해밍 거리는 01010110이며 비트 수는 4입니다. 따라서 점수 계산은 해당 해밍 거리의 역수가 됩니다. 유사한 벡터일수록 해밍 거리가 작아지므로 이를 반전시키면 유사한 벡터일수록 더 높은 점수를 받을 수 있습니다. 구체적으로 여기서는 1/4 = 0.25가 됩니다.

그러나 각 차원의 크기가 어떻게 줄어드는지 주목하세요. 11입니다. 그래서 Q의 경우, 0.010.79 사이의 차이점이 사라집니다. >0에 따라 단순히 양자화하기 때문에 Q 벡터가 양자화되지 않은 곳에서 작은 트릭을 수행할 수 있습니다. 이렇게 하면 매우 빠른 비트 단위 연산을 할 수는 없지만, D가 여전히 양자화되어 있기 때문에 스토리지 비용을 낮게 유지할 수 있습니다.

간단히 말해서, 이는 Q에 제공된 정보를 유지하여 거리 추정 품질을 향상시키고 저장 공간을 최소화합니다.

비트 벡터를 사용하면 디스크 공간과 쿼리 시 계산 부하를 크게 줄일 수 있습니다. 하지만 할 수 있는 일은 더 많습니다.

평균 벡터

수십만 개의 문서에 걸쳐 검색을 확장하려면 비트 벡터가 제공하는 성능상의 이점만으로는 충분하지 않습니다. 이러한 유형의 워크로드에 맞게 확장하려면 벡터 검색을 위해 Elasticsearch의 HNSW 인덱스 구조를 활용해야 합니다.

ColPali는 문서당 약 천 개의 벡터를 생성하는데, 이는 HNSW 그래프에 추가하기에는 너무 많습니다. 따라서 벡터의 수를 줄여야 합니다. 이를 위해 ColPali에 의해 생성된 모든 문서 벡터의 평균을 계산하여 이미지를 임베드할 때 문서의 의미에 대한 단일 표현을 만들 수 있습니다.

현재로서는 Elastic 자체 내에서 이 작업이 불가능하며, Elasticsearch에서 벡터를 수집하기 전에 벡터를 전처리해야 합니다.

Logstash 또는 수집 파이프라인으로 이 작업을 수행할 수 있지만 여기서는 간단한 Python 함수를 사용하겠습니다:

또한 내적 유사도를 사용할 수 있도록 벡터를 정규화하고 있습니다.

모든 ColPali 벡터를 평균 벡터로 변환한 후, 이를 dense_vector 필드에 인덱싱할 수 있습니다.

이로 인해 지연 상호작용 벡터와 함께 더 많은 정보를 저장하게 되므로 전체 디스크 사용량이 증가할 것이라는 점을 고려해야 합니다. 추가로, 수십억 개의 벡터에 대한 검색을 확장할 수 있도록 HNSW 그래프를 보유하기 위해 추가 RAM을 사용할 것입니다. RAM 사용량을 줄이기 위해 우리는 인기 있는 BBQ 기능을 활용할 수 있습니다. 그 결과, 기존 방식으로는 불가능했던 대규모 데이터 세트에 대한 빠른 검색 결과를 얻을 수 있습니다.

이제 knn 쿼리로 검색하면 가장 관련성 높은 문서를 찾을 수 있습니다.

불행히도 이전의 베스트 매칭은 3위로 떨어졌습니다.

이 문제를 해결하기 위해 다단계 검색을 수행할 수 있습니다. 첫 번째 단계에서는 knn 쿼리를 사용하여 수백만 개의 문서에서 쿼리에 가장 적합한 후보를 검색하고 있습니다. 두 번째 단계에서는 ColPali 지연 상호작용 벡터의 충실도가 높은 상위 k(여기서는 10)만 다시 순위를 매깁니다. 

여기서는 8.18에서 도입된 재점수 검색기를 사용하여 결과를 다시 순위 지정합니다. 점수를 재지정한 후 다시 가장 좋은 매치가 첫 번째 위치에 있음을 확인합니다.

참고: 프로덕션 애플리케이션에서는 최대 시뮬레이션 함수가 여전히 비교적 성능이 좋으므로 10보다 훨씬 높은 k를 사용할 수 있습니다.

토큰 풀링

토큰 풀링은 중복 정보(예: 흰색 배경 패치)를 풀링하여 멀티 벡터 임베딩의 시퀀스 길이를 줄입니다. 이 기법은 페이지의 신호 대부분을 보존하면서 임베딩 수를 줄입니다.

토큰 풀링은 클러스터링 알고리즘을 사용하여 문서 내에서 유사한 토큰 임베딩을 클러스터로 그룹화하는 방식으로 작동합니다. 그런 다음, 각 클러스터 내 벡터의 평균을 계산하여 단일한 집계 표현을 생성합니다. 이 집계된 벡터는 그룹 내의 원래 토큰을 대체하여 문서 신호의 상당한 손실 없이 총 벡터 수를 줄입니다.

ColPali 논문에서는 대부분의 데이터 세트에 대해 초기 풀 팩터 값을 3으로 제안하고 있는데, 이는 원래 성능의 97.8%를 유지하면서 총 벡터 수를 66.7%로 줄입니다. 

그러나 주의해야 합니다. 매우 밀도가 높고 텍스트가 많으며 공백이 거의 없는 문서를 포함하는 "Shift" 데이터 세트는 풀 요소가 증가함에 따라 성능이 급격히 저하됩니다.

풀링된 벡터를 생성하려면 colpali_engine 라이브러리를 사용할 수 있습니다.

이제 차원이 약 66.7% 감소한 벡터를 가지고 있습니다. 평소처럼 인덱싱하고 maxSimDotProduct() 함수로 검색할 수 있습니다.

검색 결과의 정확도가 약간 떨어지는 대신, 우수한 검색 결과를 얻을 수 있습니다.

힌트: pool_factor 값을 높이면(100~200) 평균 벡터 솔루션과 여기서 논의한 솔루션 사이의 중간 지점을 찾을 수도 있습니다. 문서당 약 5~10개의 벡터가 있을 때, 중첩된 필드에 인덱스를 만들어 HNSW 인덱스를 활용하는 것이 현실적이 됩니다.

코스 인코더 vs. 지연 상호작용 vs. 바이 인코더

지금까지 배운 바에 따르면, ColPali 또는 ColBERT와 같은 지연 상호작용 모델은 다른 AI 검색 기술과 비교할 때 어떤 위치에 있을까요?

최대 시뮬레이션 함수는 크로스 인코더에 비해 저렴하지만, 쿼리-문서 쌍마다 두 벡터를 비교하는 바이 인코더를 사용한 벡터 검색보다 여전히 더 많은 비교와 계산이 필요합니다.

이러한 이유로, 지연 상호작용 모델은 일반적으로 상위 k 검색 결과의 순위를 재조정하는 데에만 사용하는 것을 권장합니다. 또한 이를 필드 유형 이름인 rank_vectors로 캡처합니다.

그렇다면 크로스 인코더는 어떨까요? 쿼리 시점에 실행 비용이 저렴하기 때문에 지연 상호작용 모델이 더 나은 것일까요? 종종 그렇듯이 답은 '상황에 따라 다르다'입니다. 크로스 인코더는 일반적으로 더 높은 품질의 결과를 생성하지만, 쿼리 문서 쌍이 트랜스포머 모델을 통해 완전한 패스를 수행해야 하기 때문에 많은 계산 자원이 필요합니다. 또한 벡터 인덱싱이 필요하지 않고 상태 비저장 방식으로 작동할 수 있다는 장점도 있습니다. 그 결과는 다음과 같습니다.

  • 디스크 공간 사용량 감소
  • 더욱 간소화된 시스템
  • 더 높은 검색 결과 품질
  • 더 높은 대기 시간으로 인해 순위 재지정을 깊이 수행할 수 없음

반면, 지연 상호작용 모델은 이 계산 일부를 인덱싱할 때 분산시켜 쿼리 비용을 낮출 수 있습니다. 그 대가로 벡터를 인덱싱해야 하므로 인덱스 파이프라인이 더 복잡해지고 이러한 벡터를 저장하는 데 더 많은 디스크 공간이 필요합니다.

특히 ColPali의 경우, 이미지에서 정보를 분석하는 것은 많은 데이터를 포함하기 때문에 매우 비용이 많이 듭니다. 이 경우 쿼리 시점에 이 정보를 평가하는 것은 너무 리소스 집약적이고 느리기 때문에 ColPali와 같은 후기 상호 작용 모델을 사용하는 것이 유리합니다.

ColBERT와 같은 지연 상호작용 모델의 경우, 대부분의 크로스 인코더(예: elastic-rerank-v1)처럼 텍스트 데이터를 기반으로 작동하기 때문에 디스크 공간 절약 및 간편성 측면에서 크로스 인코더를 사용하는 것이 더 유리할 수 있습니다.

사용 사례에 대한 장단점을 비교하고 Elasticsearch가 제공하는 다양한 도구를 실험하여 최고의 검색 애플리케이션을 구축하는 것이 좋습니다.

결론

이 블로그에서는 Elasticsearch에서 대규모 벡터 검색을 위한 ColPali와 같은 지연 상호작용 모델을 최적화하는 다양한 기술을 탐구했습니다. 지연 상호작용 모델은 검색 효율성과 순위 품질 사이에서 뛰어난 균형을 제공하지만, 저장 공간 및 계산과 관련된 문제점도 야기합니다.

이러한 과제를 해결하기 위해 다음을 살펴보았습니다.

  • 비트 벡터를 사용하여 해밍 거리 또는 비대칭 최대 유사도와 같은 효율적인 유사도 계산을 활용하면서 디스크 공간을 크게 줄일 수 있습니다.
  • 여러 임베딩을 하나의 밀집 표현으로 압축하기 위해 평균 벡터를 사용하여 HNSW 인덱싱을 통한 효율적인 검색을 가능하게 합니다.
  • 토큰 풀링을 통해 의미적 무결성을 유지관리하면서 중복된 임베딩을 지능적으로 병합하여 쿼리 시 계산 오버헤드를 줄입니다.

Elasticsearch는 사용자의 요구에 따라 검색 애플리케이션을 맞춤 설정하고 최적화할 수 있는 강력한 툴킷을 제공합니다. 검색 속도, 순위 품질 또는 저장 공간 효율성 중 무엇을 우선시하든, 이러한 도구와 기술을 사용하면 실제 응용 분야에 필요한 성능과 품질의 균형을 맞출 수 있습니다.

관련 콘텐츠

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

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

직접 사용해 보세요