최근 인공지능 기술이 크게 발전함에 따라 인공지능을 뒷받침하는 다양한 기술들이 연구되고 있다. 그 중 GPT와 같은 프로덕트를 구현하는 데 사용되는 LLM(Large Language Model)과 이를 위한 데이터베이스인 VDB(Vector Database)도 활발한 연구가 이루어지고 있다.
이러한 벡터 데이터베이스(VDB)는 벡터값을 저장하기 위해 특화된 데이터베이스인데, 주로 embedding과 함께 쓰이게 된다. embedding은 모든 데이터들을 고차원의 벡터값으로 변환한 것이다. 다양한 데이터에 맞는 embedding model을 사용하여 어떠한 데이터든 임베딩으로 나타낼 수 있다. ( text embedding model, image embedding model, etc ... )
이러한 LLM 모델과 함께 RAG(Retrieval-Augmented Generation)이라는 것 또한 부상하기 시작했는데, 정보 검색과 텍스트 생성을 결합한 기법을 의미한다. 이 접근법은 LLM보다 정확하고 신뢰성 있는 정보를 전달한다. 이러한 RAG의 구현에 embedding과 VDB가 함께 쓰인다.
이 외에도 multi-modal embedding이라고 하는 분야도 있다. text, image, sound 등 다양한 데이터를 하나의 벡터 공간에 나타내어, 사자 울음소리로 검색을 했는데, 사자 이미지를 찾아준다거나, 라이언킹과 같은 영화 제목을 찾아준다거나 하는 연구이다. 이러한 연구 또한 최근 활발하게 진행되고 있다.
본론
embedding?
embedding은 주로 자연어 처리(NLP)에서 활용되는 용어로, 복잡한 데이터를 고정된 크기의 벡터로 나타내는 것이다. 128차원이나 768차원 등 다양한 차원으로 표현된 것을 embedding이라고 한다. 텍스트를 처리하는 자연어 처리 분야 이외에도 다양한 모델을 이용해 다양한 데이터를 벡터로 나타낼 수 있다.
좀 더 복잡하게 서술하면, 머신 러닝 모델에서 파생된 수치 표현으로 비정형 데이터의 의미적 의미를 가진 데이터이다. 이러한 임베딩은 신경망이나 트랜스포머 아키텍처를 통해 데이터 내의 복잡한 상관관계를 분석하여 생성되며, 각 점이 문서의 단어와 같은 데이터 객체의 의미에 해당하는 밀집한 벡터로 변환된다. 데이터가 변환되면 아래와 같이 벡터 공간에 위치하게 되며, 비슷한 의미를 갖는 데이터들은 비교적 가까운 위치에 놓이게 된다.
이렇게 벡터 공간 위에 나타내게 되면 각 노드간의 거리를 구하는 다양한 방법을 통해서 검색을 구현한다. 가장 가까운 k개의 이웃을 검색하는 방법을 k-NN(k-nearest neighbors)이라고 한다.
이렇게 데이터를 벡터값으로 바꾸기 위해서는 embedding model이라는 것이 필요하다. image를 임베딩으로 만들기 위해서는 image embedding model이 필요하고, text를 임베딩으로 만들기 위해서는 text embedding model이 필요하다. 이러한 임베딩 모델은 이미 ML 관련 사이트에서 다양한 모델 프리셋들을 찾아볼 수가 있다.
- huggingface : https://huggingface.co/models
임베딩이라는 벡터값으로 바꾸는 것이기 때문에 일반적으로 text2Vec, image2vec 이러한 네이밍으로 다양한 모델을 확인할 수 있다. 이번 포스팅에서 다루고자 하는 것은 embedding에 대한 개략적인 인사이트이기 때문에 embedding model에 대한 설명은 이 정도에서 넘어간다.
Vector DB Index
위와 같은 임베딩 값은 고차원의 벡터값으로 나온다. 이를 단순히 평범한 데이터베이스나 레디스에 저장해도 크게 문제가 없지만 일반적으로는 이러한 임베딩을 위해서 벡터 데이터베이스에 저장하곤 한다. 왜 벡터 데이터베이스인가? 그 이유는 일반적으로 벡터 데이터베이스에서 제공하는 다양한 메서드들의 힘을 빌리기 위해서이다.
벡터값은 일반적인 RDB와는 엄청나게 다르기 때문에, SQL은 당연히 사용할 수 없을 것이고 인덱싱(Indexing)을 하는 방법도 다르다. 벡터 데이터베이스의 인덱스 방법은 대중적으로는 세 가지가 있다.
1. Flat Index
플랫 인덱스 방법은 가장 간단한 인덱싱 방법으로 별도의 인덱스 방법 없이 저장하는 것이다. 벡터 검색시에 말 그대로 k개의 근접한 이웃들을 그대로 제공한다. 따라서 모든 인덱싱 방법 중에서 가장 정확한 결과를 제공한다. 단순히 정직하게 벡터 공간 위해 나타내지기 때문에 그대로 가장 가까운 k의 근접한 이웃을 찾아주지만, 엄청나게 느린 검색 결과를 갖고 있다.
이러한 방법은 크게 효율적이지 않아 보이지만, 소규모의 저차원 벡터를 다루는 경우에는 꽤 나쁘지 않은 성능을 가지고 있다.
2. Graph Index
그래프 기반 방식의 인덱스는 노드와 엣지를 사용해 네트워크와 같은 구조로 구성한다. 노드는 벡터 임베딩을 나타내고 엣지는 임베딩 간의 관계를 나타낸다. 아래와 같은 구조로 되어있으며, Entry Point에서 시작하여, 점점 노드를 탐색하다가 Query Vector에서 가장 가까운 벡터를 찾을 때 까지 탐색하는 자료구조이다.
이러한 Graph Index 방식 기반의 인덱싱 방법으로는 HNSW가 있다. 계층적 탐색가능한 작은 단어(Hierarchical Navigable Small Words)는 두 개의 임베딩 노드가 근접한 정도에 따라 연결되는 근접 그래프로 유클리드 거
리를 써서 구현한다. 각 노드들은 '친구 목록'을 가지고 있으며 그래프 기반의 알고리즘은 미리 정의된 진입점에서 시작하여 쿼리 벡터에 가장 가까운 이웃을 찾을 때까지 연결된 친구 노드를 탐색한다.
최상위 레이어는 가장 긴 거리를 가지지만 가장 높은 차수의 정점을 포함하고, 점점 내려가면서 더 많은 노드들을 탐색할 수 있도록 하게 한다. 이 탐색방법(HNSW)은 쿼리 벡터와 가장 가까운 이웃의 거리가 로컬 최소값에 도달할 때까지 계속되는 방법이다.
이러한 접근 방법은 Flat 방식과 다르게 고차원의 대규모 데이터에 적합한 인덱싱 방법이다. 따라서 일반적으로 HNSW 방법이나 추후에 설명할 IVFPQ 방법을 통해서 인덱싱을 진행하게 된다.
3. Inverted Index ( and Product Quantization )
반전 인덱싱 방법은 문서 콘텐츠를 효율적으로 찾을 수 있는 방법을 제공하기 때문에 검색 엔진에서 널리 사용된다. 검색 엔진 데이터베이스에 웹 페이지와 같은 문서 모음이 있다고 해보자. 각 문서에는 특정 키워드가 포함되어 있고, 특정 키워드가 포함된 문서를 빠르게 찾을 수 있게 구현한다고 하자. 이러한 내용을 인덱스하는 것이 반대로 인덱싱을 하는것이라 Inverted Index 방법이라고 부른다. 사진으로 나타내면 아래와 같다.
이러한 과정을 통해서 보르노이 셀로 나타낼 수 있게 되는데, 보르노이 셀은 쿼리 벡터와 프로브 매개변수에 의해 정의된다. 검색은 쿼리 백터가 위치한 셀과 주변 셀의 가장 가까운 중심점으로 제한된다. 이러한 백터 서치 방법을 IVFPQ 방법이라고 부른다.
아래에서 프로브 매개변수가 3이면 쿼리 벡터에서 가장 가까운 3개의 보르노이 셀을 가져오는 방법이다.
이러한 방법은 메모리 사용량과 검색 시간이 크게 줄어드는 장점을 얻을 수 있다. HNSW와 마찬가지로 고차원의 벡터와 빠른 최접 이웃 검색이 필요한 경우에 적합하다. 실제로 IVFPQ는 리소스가 많이 필요한 환경, 고차원 벡터, 대규모 데이터베이스에 이르기까지 HNSW와 비슷한 이점을 가지고 있다.
HNSW vs IVFPQ
HNSW가 더 좋은 경우에는 빠른 성능이 중요한 경우이다. 정확성은 조금 떨어지지만 효율적으로 빠르게 찾는 게 목표라면 좀 더 추천할만하다. 또한 PQ를 사용하지 않기 때문에 인덱스를 구성하는 단계가 더 적다.
IVFPQ가 더 좋은 경우에는 정확도가 중요한 경우이다. 정확한 이웃 검색과 근사 이웃 검색에 모두 사용되지만, 정확한 결과가 필요한 시나리오에서 특히 강하다. 또한 제품을 양자화시키기 때문에(PQ) 메모리 효율이 매우 높다.
Vector Distance
벡터 서치와 함께 인덱싱 방법에 대해서 알아봤는데 벡터 쿼리를 통해서 데이터를 찾는 방법에 대해서 알아보았다. 그렇다면 이번에는 벡터 사이의 거리를 어떻게 측정할 수 있는 지 알아보려고 한다.
1. 유클리드 거리 (Euclidan Distance)
유클리드 거리는 두 벡터 간의 직선 거리를 측정한다. 2차원 평면에서 두 점 사이의 거리와 유사하게 생각할 수 있다.
유클리드 거리의 특징으로는 직관적이고 계산 방법이 간단하지만, 데이터의 크기에 민감하게 반응하여 데이터의 스케일을 조정하지 않으면 왜곡된 결과를 초래할 수 있다는 점이다.
2. 코사인 유사도 (Cosine Similarity)
코사인 유사도는 두 벡터 간의 각도를 기반으로 유사성을 측정한다. 벡터의 크기보다는 방향이 얼마나 유사한지를 평가하는 방법이다.
특징으로는 값이 1에 가까울수록 두 벡터가 동일한 방향을 가리키며, -1에 가까울수록 반대 방향을 가리킨다는 것이다. 벡터의 크기에 영향을 받지 않으며, 주로 텍스트 분석이나 문서 유사도 계산에 많이 사용된다.
3. 맨해튼 거리 (Manhattan Distance)
맨해튼 거리(또는 택시 거리라고도 부름)는 격자형 도로를 따라 이동하는 방식으로 두 점 사이의 거리를 측정한다.
특징으로는 차원별로 거리 차이를 계산해 합상하기 때문에 고차원 공간에서 유클리드 거리보다 직관적일 수 있다는 점이다. 데이터의 특정 속성들이 독립적으로 변화하는 경우에 유용하다.
4. 체비쇼프 거리 (Chebyshev Distance)
체비쇼프 거리는 두 벡터 사이에서 가장 큰 절대 차이를 측정한다.
특징으로는 모든 차원에서 최대 차이를 기준으로 거리를 측정하는 방법으로, outlier 탐지 등에 사용된다.
5. 자카드 유사도 (Jaccard Similarity)
자카드 유사도는 집합 간의 유사성을 측정하는 방법으로 두 집합 간의 교집합 크기를 합집합 크기로 나눈 값이다. 따라서 0과 1사이의 값을 가지며, 텍스트 데이터의 중복성 또는 비슷함을 측정하는 데 주로 사용된다.
특징으로는 이진 벡터 또는 집합 데이터에서 유용하며, 코사인 유사도와 비슷하지만 자카드 유사도는 벡터의 절대적인 크기보다는 비트 수준에서의 유사성을 더 잘 반영한다는 특징이 있다.
Milvus
앞서 다양한 인덱싱 방법과 거리 측정 방법에 대해서 알아보았다. 이러한 수많은 방법들에 대해서 개괄적으로 서술하기만한 이유는 이미 대부분의 서비스들이 이미 구현되어있는 오픈소스들이 많기 때문이다. 다음은 milvus라고 불리는 VDB의 공식 문서이다
- [milvus] docs : https://milvus.io/docs
이 글을 쓰는 현재 기준으로는 2.4.X 버전이 릴리즈 되었으며, 해당 공식 문서에서 User Guide를 통해 다양한 검색 방법과 인덱스 방법에 대해서 확인할 수 있다.
우리가 앞서 봤었던 Metric Types(유클리드, 내적, 코사인 유사도) 및 Index Type(FLAT, IVF, HNSW) 등을 확인할 수 있다. 따라서 이러한 벡터 데이터베이스가 이미 오픈 소스로 많은 부분이 개발되어 있고, 벡터 데이터베이스에 데이터를 넣기 위해 필요한 embedding model또한 손쉽게 찾아볼 수 있다.
다음 포스팅으로는 이러한 milvus라는 데이터베이스와 함께 간단한 embedding model을 접합시켜, 추천 시스템을 만드는 방법에 대해서 알아보려고 한다.
참고
- [Medium] Vector Indexing: A Roadmap for Vector Databases : https://medium.com/kx-systems/vector-indexing-a-roadmap-for-vector-databases-65866f07daf5
- [Milvus] docs : https://milvus.io/docs
감사합니다.
'ML engineer' 카테고리의 다른 글
embedding을 이용한 검색 엔진/추천 시스템 만들기 (3) | 2024.12.02 |
---|---|
[Milvus] Vector Database란? (1) | 2024.11.28 |