Want to Become a Sponsor? Contact Us Now!🎉

벡터 데이터베이스
Spotify의 Annoy 라이브러리를 사용하여 Python에서 벡터 유사도 검색하는 방법

Spotify의 Annoy 라이브러리를 사용하여 Python에서 벡터 유사도 검색하는 방법

Published on

애니버서리니어 오 예 알고리즘인 Annoy가 Python에서 가까운 이웃 검색을 혁신합니다. 핵심 개념을 배우고 구현하는 방법과 머신러닝 전문가들에게 가장 많이 선택되고 있는 이유에 대해 알아보세요.

머신러닝 프로젝트에서 느린이웃 검색에 지친 적이 있나요? 정확도를 희생하지 않고 이 중요한 단계를 빠르게 수행할 수 있는 방법이 있으면 좋겠다고 생각한 적이 있나요? 그러한 여러분의 소망이 이제 실현됩니다. 머신러닝 커뮤니티에 선풍적인 반응을 일으키는 Spotify의 Annoy라는 Python 라이브러리의 세계로 오신 것을 환영합니다.

이 포괄적인 가이드에서는 Annoy에 대해 깊이 알아보고, 내부 작동 방식, Python 구현 및 해당 분야 전문가들에게 가장 많이 선택되고 있는 이유에 대해 알아보겠습니다. 빠르고 효율적인 가장 가까운 이웃 검색의 세계로의 짜릿한 여행을 시작해보세요.

애니버서리니어 오 예(Annoy)란?

세부 사항을 파헤치기 전에 우선 우리가 사용하는 정의를 명확하게 해봅시다. **애니버서리니어 오 예(Annoy, Approximate Nearest Neighbor Oh Yeah)**는 가까운 이웃 검색을 더 효율적으로 처리하기 위해 설계된 알고리즘입니다. 완전 탐색을 수행하는 전통적인 방법과는 달리 Annoy는 검색 공간을 분할하여 과정을 더 빠르게 수행하는 똑똑한 데이터 구조인 이진 탐색 트리를 사용합니다.

  • 전통적인 방법: 느리고 완전 탐색을 수행합니다.
  • Annoy: 이진 탐색 트리를 사용하여 빠르고 근사한 검색을 수행합니다.

Annoy의 장점은 무엇인가요?

다른 가까운 이웃 검색 알고리즘과 라이브러리가 있는데 Annoy를 선택해야 하는 이유에 대해 궁금할 수 있습니다. 다음은 Annoy를 선택해야 하는 몇 가지 주요한 이유입니다:

  • 속도: Annoy는 이진 탐색 트리의 효율적인 사용 덕분에 빠릅니다.
  • 메모리 효율성: Annoy는 메모리 맵 파일을 사용하여 여러 프로세스가 동일한 데이터를 공유할 수 있습니다.
  • 유연성: Annoy는 유클리드 거리, 맨하탄 거리, 각도 거리와 같은 다양한 거리 측정 방식을 지원합니다.
  • 사용의 편의성: Annoy의 Python 라이브러리를 사용하면 쉽게 구현할 수 있습니다.

Annoy는 어떻게 작동하나요?

Annoy가 무엇인지 알았으니 이제 그 작동 방식을 알아봅시다. 핵심적으로 Annoy는 **이진 탐색 트리(Binary Search Trees)**라는 데이터 구조를 사용하여 벡터 공간을 분할합니다. 이는 전통적인 방법인 연결된 그래프나 완전 탐색과 근본적으로 다릅니다.

핵심 데이터 구조: 이진 탐색 트리

Annoy에서 이진 탐색 트리의 각 노드는 데이터 집합의 벡터를 나타냅니다. 트리는 데이터 공간을 재귀적으로 두 개로 분할하여 구성됩니다. 이 분할은 데이터 집합에서 임의로 선택된 두 벡터 사이의 평균 거리에서 동일한 거리를 가지는 하이퍼플레인(hyperplane)으로 수행됩니다.

  • 하이퍼플레인: 벡터 공간을 분할하는 데 사용됩니다.
  • 임의 선택된 벡터: 각 하이퍼플레인을 정의하기 위해 임의로 선택된 두 벡터입니다.

예를 들어, 벡터 (A)와 (B)가 있는 경우, (A)와 (B)에서 동일한 거리에 있는 하이퍼플레인은 공간을 두 개의 부분으로 나눕니다. (A)에 가까운 벡터는 왼쪽 서브트리로, (B)에 가까운 벡터는 오른쪽 서브트리로 이동합니다.

재귀적 분할: Annoy의 진가

벡터 공간의 재귀적 분할 과정에서 실제로 마술이 일어납니다. 트리의 각 노드는 공간을 두 개로 나누는 하이퍼플레인과 연결됩니다. 이 과정은 각 자식 노드에 대해 반복되며, 공간을 더 세분화하여 각 잎 노드에 미리 정의된 수의 원소(예: (K))보다 적은 수가 포함되도록 합니다.

  • 잎 노드: 원소의 수가 (K)보다 작습니다.
  • (K): 공간 분할의 세분화 정도를 제어하는 사용자 정의 매개변수입니다.

이러한 트리 구조를 사용하여 Annoy는 쿼리 벡터가 속하는 분할을 빠르게 식별할 수 있으므로 비교해야 하는 벡터 수를 줄일 수 있습니다. 이것이 Annoy를 빠르고 효율적으로 만드는 것입니다.

Annoy에서의 인덱싱: 단계별 안내

Annoy의 핵심 개념을 이해한 후에는 실제 구현을 통해 이를 확인해보는 것이 중요합니다. 인덱싱은 Annoy를 사용하는 첫 번째 중요한 단계이며, 여기에서 이진 탐색 트리의 마법이 작동합니다.

단계 1: Annoy 라이브러리 설치

첫 번째로, Annoy 라이브러리를 설치해야 합니다. pip를 사용하여 쉽게 설치할 수 있습니다:

pip install annoy

단계 2: 라이브러리 가져오기 및 인덱스 초기화

설치한 후, 라이브러리를 가져오고 Annoy 인덱스를 초기화합니다. 다음과 같이 수행할 수 있습니다:

from annoy import AnnoyIndex
 
# 40개의 차원으로 인덱스 초기화
t = AnnoyIndex(40, 'angular')
  • 40: 각 벡터의 차원 수
  • 'angular': 사용할 거리 측정 방법 (유클리드, 맨하탄 및 각도 거리 사용 가능)

단계 3: 인덱스에 항목 추가하기

이제 인덱스에 항목(벡터)을 추가합니다. 각 항목은 정수 ID로 식별됩니다.

# 3개의 벡터를 인덱스에 추가
t.add_item(0, [1.0, 2.1, 3.2, ...])
t.add_item(1, [4.5, 5.1, 6.3, ...])
t.add_item(2, [7.2, 8.1, 9.4, ...])

단계 4: 인덱스 생성하기

모든 항목을 추가한 후에는 인덱스를 생성합니다. 이 단계에서 Annoy가 이진 탐색 트리를 구성합니다.

# 10개의 트리를 포함하는 인덱스 생성
t.build(10)
  • 10: 인덱스에 포함된 트리의 수. 더 많은 트리는 더 높은 정확도를 의미하지만 쿼리 시간이 느려집니다.

단계 5: 인덱스 저장 및 불러오기

인덱스를 디스크에 저장하고 나중에 쿼리를 위해 불러올 수 있습니다.

# 인덱스 저장
t.save('my_index.ann')
 
# 인덱스 불러오기
u = AnnoyIndex(40, 'angular')
u.load('my_index.ann')

다음 단계를 따라서, 빠르고 효율적인 최근접 이웃 쿼리를 수행할 준비가 된 Annoy 인덱스를 성공적으로 생성했습니다.

최근접 이웃을 위한 Annoy 쿼리하는 방법

인덱스를 작성한 후에, 최근접 이웃 쿼리를 위해 'get_nns_by_item'과 'get_nns_by_vector' 메서드를 사용할 수 있습니다.

'get_nns_by_item' 사용하기

이 메서드는 인덱스에서 지정된 항목에 대한 최근접 이웃을 가져옵니다.

# 항목 0의 5개 최근접 이웃 찾기
print(t.get_nns_by_item(0, 5))

'get_nns_by_vector' 사용하기

대신, 특정 벡터에 대한 최근접 이웃을 찾을 수도 있습니다.

# 주어진 벡터에 대한 5개 최근접 이웃 찾기
print(t.get_nns_by_vector([1.0, 2.1, 3.2, ...], 5))

두 메서드 모두 쿼리 항목 또는 벡터와의 거리에 따라 정렬된 항목 ID 리스트를 반환합니다.

3개의 Python Annpy 예제

예제 1: 기본 초기화 및 인덱스 작성

이 예제에서는 데이터셋으로 Annoy 인덱스를 초기화하고 특정한 개수의 트리로 인덱스를 작성합니다. 이것은 대규모 최근접 이웃 검색에 일반적으로 사용되는 경우입니다.

from annoy import AnnoyIndex
import os
import logging
 
def main(args):
    data = Dataset(args.dataset)
    f = data.base.shape[1]
    t = AnnoyIndex(f)
    idxpath = os.path.join(args.exp_dir, 'sift_annoy_ntrees%d.idx' % ntrees)
    
    if not os.path.exists(idxpath):
        logging.info("항목 추가 중...")
        for i in range(data.nbae):
            t.add_item(i, data.base[i])
        logging.info("인덱스 작성 중...")
        t.build(ntrees)
        logging.info("인덱스 저장 중...")
        t.save(idxpath)

이 예제에서는 진행 상황을 추적하기 위해 로깅을 사용합니다. 인덱스는 디스크에 저장되어 향후 실행에서 빠르게 다시로드할 수 있습니다.

예제 2: 희소 데이터 사용하기

이 예제에서는 희소 데이터와 함께 Annoy 인덱스를 작성하는 방법을 보여줍니다. 이는 데이터셋이 고차원이고 희소한 경우에 유용합니다.

from annoy import AnnoyIndex
import numpy as np
from scipy.sparse import csr_matrix
import os
 
def test_build_sparse_annoy_index(annoy_index_file):
    data = np.random.choice([0, 1], size=(10, 5))
    sparse_data = csr_matrix(data)
    index = AnnoyIndex(5, metric='angular')
    index.load(annoy_index_file)
    assert os.path.exists(annoy_index_file)

이 예제에서는 SciPy 라이브러리의 'csr_matrix'를 사용하여 희소 데이터를 생성합니다. 그런 다음 파일에서 기존 Annoy 인덱스를 로드합니다.

예제 3: 추천 시스템에서 Annoy 사용하기

이 예제에서는 Annoy를 추천 시스템에 통합하여 빠르게 유사한 항목을 찾는 방법을 소개합니다.

import annoy
import logging
 
def fit(self, Ciu, show_progress=True):
    super(AnnoyAlternatingLeastSquares, self).fit(Ciu, show_progress)
    logging.debug("유사한 항목을 위한 Annoy 인덱스 작성")
    
    self.similar_items_index = annoy.AnnoyIndex(self.item_factors.shape[1], 'angular')
    for i, row in enumerate(self.item_factors):
        self.similar_items_index.add_item(i, row)
    self.similar_items_index.build(self.n_trees)

여기에서는 'AnnoyAlternatingLeastSquares' 클래스를 확장하고 유사한 항목을 위한 Annoy 인덱스를 작성하는 메서드를 추가합니다. 이를 통해 추천 시스템은 유사한 항목을 훨씬 빠르게 가져올 수 있습니다.

결론: 빠르고 효율적인 최근접 이웃 검색을 위한 Annoy - 기본 라이브러리

요약하자면, Annoy는 대규모이고 고차원인 데이터 처리 및 빠른 근접 이웃 검색이 필요한 모든 사람들을 위한 필수 도구입니다. 그 속도, 효율성 및 사용 편의성은 추천 시스템부터 자연어 처리 및 이미지 인식까지 다양한 응용 분야에서 선호되는 선택입니다. 일부 다른 알고리즘의 정확도를 제공하지는 않지만, 실제 응용 프로그램에는 대체로 충분한 성능을 제공합니다. 프로젝트에서 최근접 이웃 검색을 구현하려는 경우, Annoy는 분명히 고려해야 할 라이브러리입니다.

Annoy의 주요 사용 사례는 무엇인가요?

Annoy는 주로 추천 시스템, 자연어 처리 및 이미지 인식 작업에서 사용됩니다. 근사적으로 가장 가까운 이웃을 빠르게 찾는 능력으로 인해 이러한 응용 분야에 이상적입니다.

Annoy는 어떻게 속도를 달성하나요?

Annoy는 특히 랜덤 프로젝션 트리라는 트리 숲을 사용하여 공간을 작은 영역으로 분할합니다. 이로써 가장 가까운 이웃이 아닌 데이터 세트의 큰 부분을 빠르게 제외시킴으로써 검색 시간을 단축할 수 있습니다.

Annoy는 모든 종류의 데이터에 적합한가요?

Annoy는 고차원 데이터에 특히 적합합니다. 그러나 낮은 차원의 데이터에도 사용할 수 있습니다. 중요한 것은 트리의 개수 및 search_k 매개변수와 같은 적절한 매개변수를 선택하여 특정 데이터셋에 대한 성능을 최적화하는 것입니다.

Annoy는 다른 최근접 이웃 알고리즘과 어떻게 비교되나요?

Annoy는 일반적으로 K-D 트리나 Ball 트리와 같은 다른 알고리즘보다 빠르고 적은 메모리를 사용합니다. 근사적으로 가장 가까운 이웃 알고리즘이기는 하지만, 정확성은 대부분의 실제 응용 프로그램에 충분합니다.

Python 이외의 언어로 Annoy를 사용할 수 있나요?

네, Annoy는 C++, Java, Lua 등 여러 다른 언어에 대한 바인딩을 제공합니다. 이를 통해 다양한 유형의 프로젝트에 유연하게 통합할 수 있습니다.

Annoy를 최적화하기 위한 일부 고급 기술은 무엇인가요?

일부 고급 기술로는 다른 거리 측정 방법 사용, 특정 사용 사례에 대한 트리 개수 최적화 및 대규모 데이터셋에 대한 메모리 매핑 파일 사용 등이 있습니다. 이러한 기술을 사용하면 Annoy 인덱스의 성능을 향상시킬 수 있습니다.

최신 LLM 뉴스를 알고 싶으신가요? 최신 LLM 리더보드를 확인해보세요!

Banner Ad