Elasticsearch와 SigLIP-2로 산봉우리에 대한 멀티모달 검색

SigLIP-2 임베딩과 Elasticsearch kNN 벡터 검색을 사용해 텍스트 대 이미지 및 이미지 대 이미지 다중 모드 검색을 구현하는 방법을 알아보세요. 프로젝트 초점: 에베레스트 트레킹에서 아마다블람 산 정상 사진 찾기.

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

사진 앨범을 의미별로 검색하고 싶었던 적이 있나요? "파란색 재킷을 입고 벤치에 앉아 있는 내 사진 보여줘", "에베레스트산 사진 보여줘", "사케와 초밥" 등의 검색어를 사용해 보세요. 커피 한 잔(또는 좋아하는 음료)을 들고 계속 읽으세요. 이 블로그에서는 멀티모달 하이브리드 검색 애플리케이션을 구축하는 방법을 설명합니다. 멀티모달이란 앱이 단어뿐만 아니라 텍스트, 이미지, 오디오 등 다양한 종류의 입력을 이해하고 검색할 수 있다는 뜻입니다. 하이브리드란 키워드 매칭, kNN 벡터 검색, 지오펜싱과 같은 기술을 결합하여 더 선명한 결과를 제공하는 것을 의미합니다.

이를 위해 Google의 SigLIP-2를 사용해 이미지와 텍스트 모두에 대한 벡터 임베딩을 생성하고 이를 Elasticsearch 벡터 데이터베이스에 저장합니다. 쿼리 시 텍스트 또는 이미지와 같은 검색 입력을 임베딩으로 변환하고 빠른 kNN 벡터 검색을 실행하여 결과를 검색합니다. 이 설정을 통해 텍스트 대 이미지 및 이미지 대 이미지 검색을 효율적으로 수행할 수 있습니다. 스트림릿 UI는 텍스트 기반 검색을 통해 앨범에서 일치하는 사진을 찾아서 볼 수 있을 뿐만 아니라 업로드된 이미지에서 산봉우리를 식별하고 사진 앨범에서 해당 산의 다른 사진을 볼 수 있는 프론트엔드를 제공함으로써 이 프로젝트에 활기를 불어넣었습니다.
또한 검색 정확도를 개선하기 위해 취한 조치와 실용적인 팁과 요령에 대해서도 설명합니다. 더 자세히 살펴볼 수 있도록 GitHub 리포지토리와 Colab 노트북을 제공합니다.

시작 방법

이 블로그 게시물은 에베레스트 베이스캠프 트레킹에서 찍은 아마 다블람 산의 모든 사진을 보여 달라는 10살짜리 아이의 요청에 영감을 받아 작성했습니다. 사진첩을 훑어보면서 이름을 알 수 없는 다른 산봉우리도 몇 개 더 찾아달라는 요청을 받았습니다.

이를 통해 재미있는 컴퓨터 비전 프로젝트가 될 수 있겠다는 생각이 들었습니다. 우리가 달성하고자 했던 목표:

  • 이름으로 산봉우리 사진 찾기
  • 이미지에서 산봉우리 이름을 맞추고 사진 앨범에서 비슷한 봉우리를 찾습니다.
  • 컨셉 쿼리가 작동하도록 하기(사람, , 기도 깃발 등)

드림팀 구성: SigLIP-2, Elasticsearch & Streamlit

이 작업을 수행하려면 텍스트('Ama Dablam')와 이미지(내 앨범의 사진)를 모두 의미 있게 비교할 수 있는 벡터, 즉 동일한 벡터 공간으로 변환해야 한다는 것이 금방 분명해졌습니다. 이렇게 하면 검색은 "가장 가까운 이웃을 찾는 것"에 불과합니다.

[@portabletext/react] Unknown block type "block", specify a component for it in the `components.types` prop

최근 Google에서 출시한 SigLIP-2가 여기에 잘 맞습니다. 작업별 교육( 제로 샷 설정) 없이 임베딩을 생성할 수 있으며, 라벨이 없는 사진과 이름과 언어가 다른 봉우리라는 사용 사례에 적합하게 작동합니다. 텍스트 ↔ 이미지 매칭을 위해 학습되었기 때문에 쿼리 언어나 철자가 다르더라도 트레킹에서 찍은 산 사진과 짧은 텍스트 프롬프트가 임베딩으로 비슷하게 표시됩니다.

SigLIP-2는 강력한 속도 대비 품질 균형을 제공하고, 다양한 입력 해상도를 지원하며, CPU와 GPU 모두에서 실행됩니다. SigLIP-2는 기존 CLIP과 같은 이전 모델에 비해 야외 촬영에 더욱 견고하게 설계되었습니다. 테스트하는 동안 SigLIP-2는 일관되게 신뢰할 수 있는 결과를 생성했습니다. 또한 지원도 매우 잘 되어 있어 이 프로젝트의 확실한 선택이 될 것입니다.

다음으로 임베딩과 파워 검색을 저장할 벡터 데이터베이스가 필요합니다. 이미지 임베딩에 대한 코사인 kNN 검색을 지원할 뿐만 아니라 단일 쿼리에서 지오펜스 및 텍스트 필터를 적용할 수 있어야 합니다. Elasticsearch는 벡터(dense_vector 필드의 HNSW kNN)를 매우 잘 처리하고 텍스트, 벡터, 위치 기반 쿼리를 결합하는 하이브리드 검색을 지원하며 필터링과 정렬 기능을 기본으로 제공합니다. 또한 수평으로 확장할 수 있어 몇 장의 사진에서 수천 장으로 쉽게 늘릴 수 있습니다. 공식 Elasticsearch Python 클라이언트는 배관을 단순하게 유지하며 프로젝트와 깔끔하게 통합됩니다. 마지막으로 검색 쿼리를 입력하고 결과를 볼 수 있는 경량 프론트엔드가 필요합니다. 파이썬 기반의 빠른 데모를 원한다면 Streamlit이 적합합니다. 파일 업로드, 반응형 이미지 그리드, 정렬 및 지오펜싱을 위한 드롭다운 메뉴 등 우리에게 필요한 기본 요소를 제공합니다. 로컬에서 쉽게 복제하고 실행할 수 있으며 Colab 노트북에서도 작동합니다.

구현

Elasticsearch 인덱싱 설계 및 인덱싱 전략

이 프로젝트에는 peaks_catalogphotos 의 두 가지 인덱스를 사용할 것입니다.

Peaks_catalog 인덱스

이 색인은 에베레스트 베이스캠프 트레킹 중에 볼 수 있는 주요 산봉우리를 간결하게 정리한 카탈로그 역할을 합니다. 이 색인에 포함된 각 문서는 에베레스트 산과 같은 하나의 산봉우리에 해당합니다. 각 산봉우리 문서에는 이름/별칭, 위도-경도 좌표(선택 사항), SigLIP-2 텍스트 프롬프트(+ 참조 이미지 옵션)를 혼합하여 구축한 단일 프로토타입 벡터가 저장됩니다.

인덱스 매핑:

필드유형목적/참고 사항벡터/인덱싱
id키워드아마다블람안정적인 슬러그/ID-
이름텍스트 + 키워드 하위 필드["아마다블람","아마다블람"]별칭/다국어 이름; 정확한 필터를 위한 names.raw-
latlongeo_point{"lat":27.8617,"lon":86.8614}위도/경도 조합의 피크 GPS 좌표(선택 사항)-
elev_m정수6812고도(선택 사항)-
text_embeddense_vector768이 피크에 대한 혼합 프로토타입(프롬프트 및 선택적으로 1~3개의 참조 이미지)index:true, 유사성:"코사인", index_옵션:{type:"hnsw", m:16, ef_construction:128}

이 색인은 주로 이미지에서 산봉우리를 식별하는 등 이미지 대 이미지 검색에 사용됩니다. 또한 이 인덱스를 사용하여 텍스트-이미지 검색 결과를 개선합니다.

peaks_catalog 요약하면, "어떤 산입니까?" 라는 질문을 가장 가까운 이웃에 초점을 맞춘 문제로 변환하여 이미지 데이터의 복잡성에서 개념적 이해를 효과적으로 분리하는 것입니다.

peaks_catalog 인덱스의 인덱싱 전략: EBC 트레킹 중 가장 눈에 띄는 봉우리 목록을 만드는 것부터 시작합니다. 각 봉우리에 대해 지리적 위치, 이름, 동의어, 고도를 yaml 파일에 저장합니다. 다음 단계는 각 피크에 대한 임베딩을 생성하여 text_embed 필드에 저장하는 것입니다. 강력한 임베딩을 생성하기 위해 다음 기술을 사용합니다:

  • 다음을 사용하여 텍스트 프로토타입을 만듭니다:
    • 봉우리 이름
    • 프롬프트 앙상블 (여러 개의 다른 프롬프트를 사용하여 동일한 질문에 답하기) 등을 예로 들 수 있습니다:
      • "네팔 히말라야의 산봉우리 {name} 의 자연 사진"
      • "{name} 쿰부 지역의 랜드마크 봉우리, 고산 풍경"
      • "{name} 산 정상, 눈, 바위 능선"
    • 선택적 안티 콘셉트 (SigLIP-2에 일치하지 않을 대상을 알려줌): '그림, 일러스트, 포스터, 지도, 로고'에 대해 작은 벡터를 빼서 실제 사진에 편향되도록 합니다.
  • 피크의 참조 이미지가 제공된 경우 선택적으로 이미지 프로토타입을 생성합니다.

그런 다음 텍스트와 이미지 프로토타입을 혼합하여 최종 임베딩을 생성합니다. 마지막으로 모든 필수 필드가 포함된 문서가 색인됩니다:

peaks_catalog 색인의 샘플 문서입니다:

사진 색인

이 기본 색인은 앨범의 모든 사진에 대한 자세한 정보를 저장합니다. 각 문서는 다음 정보를 포함하는 단일 사진을 나타냅니다:

  • 사진 앨범에서 사진의 상대 경로입니다. 검색 UI에서 일치하는 이미지를 보거나 이미지를 로드하는 데 사용할 수 있습니다.
  • 사진의 GPS 및 시간 정보.
  • SigLIP-2에서 생성된 이미지 인코딩을 위한 고밀도 벡터입니다.
  • predicted_peaks 를 사용하면 피크 이름을 기준으로 필터링할 수 있습니다.

    인덱스 매핑
필드유형목적/참고 사항벡터 / 인덱싱
경로키워드데이터/이미지/IMG_1234.HEICUI에서 썸네일/전체 이미지를 여는 방법-
clip_imagedense_vector768SigLIP-2 이미지 임베딩index:true, 유사성:"코사인", index_옵션:{type:"hnsw", m:16, ef_construction:128}
예측된 피크키워드["아마다블람","푸모리"]인덱스 시점의 Top-K 추측(저렴한 UX 필터/패싯)-
gpsgeo_point{"lat":27.96,"lon":86.83}지리적 필터 사용-
shot_timedate2023-10-18T09:41:00Z캡처 시간: 정렬/필터링-

사진 색인 색인 전략: 앨범의 각 사진에 대해 다음을 수행합니다:
이미지 메타데이터에서 이미지 shot_timegps 정보를 추출합니다.

  • SigLIP-2 이미지 임베딩: 이미지를 모델에 전달하고 벡터를 L2 정규화합니다. clip_image 필드에 임베딩을 저장합니다.
  • 피크를 예측하여 predicted_peaks 필드에 저장합니다. 이를 위해 먼저 이전 단계에서 생성된 사진의 이미지 벡터를 가져온 다음 peaks_catalog 인덱스의 text_embed 필드에 대해 빠른 kNN 검색을 실행합니다. 상위 3~4개 봉우리는 유지하고 나머지는 무시합니다.
  • 이미지 이름과 경로에 해시를 수행하여 _id 필드를 계산합니다. 이렇게 하면 여러 번 실행한 후에도 중복이 발생하지 않습니다.

사진의 모든 필드를 결정한 후에는 일괄 색인을 사용하여 사진 문서를 일괄 색인합니다:

사진 색인의 샘플 문서입니다:

요약하자면, 사진 인덱스는 앨범에 있는 모든 사진을 빠르고 필터링이 가능하며 kNN으로 저장할 수 있는 저장소입니다. 매핑은 일부러 최소화하여 빠르게 검색하고, 깔끔하게 표시하고, 공간과 시간별로 결과를 분류할 수 있는 구조로만 구성했습니다. 이 인덱스는 두 가지 검색 사용 사례를 모두 지원합니다. 두 인덱스를 생성하는 Python 스크립트는 여기에서 찾을 수 있습니다.

아래의 Kibana 지도 시각화에서는 사진 앨범의 문서를 녹색 점으로, peaks_catalog 인덱스의 산봉우리를 빨간색 삼각형으로 표시하며, 녹색 점이 에베레스트 베이스캠프 트레킹 코스와 잘 정렬되어 있습니다.

검색 사용 사례

이름으로 검색(텍스트-이미지): 이 기능을 사용하면 텍스트 검색을 통해 산봉우리 사진(및 '기도 깃발'과 같은 추상적인 개념까지)을 찾을 수 있습니다. 이를 위해 텍스트 입력은 SigLIP-2를 사용하여 텍스트 벡터로 변환됩니다. 강력한 텍스트 벡터 생성을 위해 인덱스에서 텍스트 임베딩을 생성할 때와 동일한 전략을 peaks_catalog 사용합니다. 즉, 텍스트 입력을 작은 프롬프트 앙상블과 결합하고, 작은 반개념 벡터를 뺀 다음 L2 정규화를 적용하여 최종 쿼리 벡터를 생성하는 것입니다. 그런 다음 photos.clip_image 필드에서 kNN 쿼리를 실행하여 가장 가까운 이미지를 찾기 위해 코사인 유사성을 기반으로 가장 일치하는 상위 피크를 검색합니다. 선택적으로 지역 및 날짜 필터를 적용하거나 photos.predicted_peaks 용어 필터를 쿼리의 일부로 적용하여 검색 결과의 연관성을 높일 수 있습니다(아래 쿼리 예시 참조). 이렇게 하면 트레킹에서 실제로 보이지 않는 유사 봉우리를 제외하는 데 도움이 됩니다.

지리적 필터를 사용한 Elasticsearch 쿼리:

이미지로 검색(이미지 대 이미지): 이 기능을 사용하면 사진에서 산을 식별하고 사진 앨범 내에서 같은 산의 다른 이미지를 찾을 수 있습니다. 이미지가 업로드되면 SigLIP-2 이미지 인코더가 이미지를 처리하여 이미지 벡터를 생성합니다. 그런 다음 peaks_catalog.text_embed 필드에서 kNN 검색을 수행하여 가장 일치하는 피크 이름을 식별합니다. 그 후, 일치하는 피크 이름에서 텍스트 벡터를 생성하고 사진 인덱스에서 또 다른 kNN 검색을 수행하여 해당 사진을 찾습니다.

Elasticsearch 쿼리:

1단계: 일치하는 피크 이름 찾기

2단계: photos 색인에서 검색을 수행하여 일치하는 사진을 찾습니다(텍스트-이미지 검색 사용 사례에 표시된 것과 동일한 쿼리):

스트림라이트 UI

모든 것을 하나로 모으기 위해 두 가지 검색 사용 사례를 모두 수행할 수 있는 간단한 Streamlit UI를 만들었습니다. 왼쪽 레일에는 스크롤 가능한 피크 목록( photos.predicted_peaks 에서 집계됨)이 체크박스와 미니맵/지리 필터와 함께 표시됩니다. 상단에는 이름으로 검색하기 상자와 사진 업로드에서 식별하기 버튼이 있습니다. 가운데 창에는 반응형 썸네일 그리드가 있어 kNN 점수, 예상 피크 배지, 캡처 시간을 보여줍니다. 각 이미지에는 전체 해상도 미리 보기를 위한 이미지 보기 버튼이 포함되어 있습니다.

이미지를 업로드하여 검색합니다: 피크를 예측하고 사진 앨범에서 일치하는 피크를 찾습니다.

텍스트로 검색: 텍스트에서 앨범에서 일치하는 피크 찾기

결론

아마 다블람 사진만 볼 수 있나요? 를 작고 작동하는 멀티모달 검색 시스템으로 전환했습니다. 우리는 원시 트레킹 사진을 찍어 SigLIP-2 임베딩으로 변환하고, Elasticsearch를 사용해 벡터를 통한 빠른 kNN과 간단한 지리적/시간 필터를 통해 의미별로 적합한 이미지를 표시했습니다. 그 과정에서 저희는 혼합된 프로토타입의 작은 peaks_catalog 인덱스(식별용)와 이미지 벡터 및 EXIF의 확장 가능한 photos 인덱스(검색용) 등 두 가지 인덱스를 사용하여 문제를 분리했습니다. 실용적이고 재현 가능하며 쉽게 확장할 수 있습니다.

튜닝을 원한다면 몇 가지 설정으로 조정할 수 있습니다:

  • 쿼리 시간 설정: k (반환할 이웃 수) 및 num_candidates (최종 점수 산출 전 검색 범위). 이러한 설정은 여기 블로그에서 설명합니다.
  • 인덱스 시간 설정: m (그래프 연결성) 및 ef_construction (빌드 시간 정확도 대 메모리). 쿼리의 경우 ef_search 을 너무 높게 설정하면 일반적으로 약간의 지연 시간 절충을 통해 더 나은 리콜을 얻을 수 있습니다. 이러한 설정에 대한 자세한 내용은 이 블로그를 참조하세요.

앞으로 멀티모달다국어 검색을 위한 기본 모델/랭커가 곧 Elastic 생태계에 출시될 예정이므로 이미지/텍스트 검색과 하이브리드 랭킹이 더욱 강력해질 것입니다. ir.elastic.co+1

직접 체험해보고 싶으신가요?

이것으로 우리의 여정은 끝났고 이제 돌아올 시간입니다. 도움이 되었기를 바라며, 이 기능을 중단(또는 개선)하신다면 어떤 점이 달라졌는지 알려주시기 바랍니다.

관련 콘텐츠

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

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

직접 사용해 보세요