Vector DB

jeong_hyeok·2025년 8월 4일

벡터 DB란, 벡터 공간 모델을 사용하여 벡터(고정 길이 숫자 목록)와 기타 데이터 항목을 저장하는 데이터베이스이다.

기존 데이터베이스는 정확한 일치와 사전 정의된 관계를 기반으로 작동하는 반면, 콘텐츠에 대한 인간의 이해는 미묘하고 맥락적이며 다차원적이다. 전문가들은 이러한 격차를 '시맨틱 갭'이라고 부른다. 시맨틱 갭은 AI 애플리케이션의 수요에 따라 점점 더 문제가 되고 있다.

벡터 데이터베이스는 이러한 격차를 해소하는 핵심 기술로 부상하여 최신 AI 인프라의 필수 구성 요소로 자리 잡았다.

정확히 일치하는 것을 기준으로 작동하는 기존 데이터베이스와 달리, 벡터 데이터베이스는 쿼리 벡터와 '가장 유사한' 벡터를 찾는 시맨틱 검색에 중점을 둔다.

미리 정의된 형식의 정형 데이터를 관리하는 데 탁월한 기존의 관계형 데이터베이스 시스템과 달리, 벡터 데이터베이스는 숫자 벡터 표현을 통해 비정형 데이터를 처리하는 데 특화되어 있다.

  • 비정형 데이터
    - 미리 정의된 데이터 모델이 없거나 미리 정의된 방식으로 정리되지 않은 정보
    - 텍스트 비정형 데이터예시: 이메일, 텍스트 문서, 소셜 미디어 게시물, 통화 녹취록
    - 비텍스트적 비정형 데이터 예시: 이미지 파일, 멀티미디어 파일, 비디오 파일, 모바일 활동

  • Vector Databases
    - Chroma
    - Weaviate
    - Pinecone
    - Milvus
    - Qdrant

벡터 DB 흐름

  1. 머신 러닝 모델이 비정형 데이터(텍스트, 이미지, 오디오)를 벡터 임베딩으로 변환한다.
  2. 이러한 벡터 임베딩은 관련 메타데이터와 함께 데이터베이스에 저장된다.
  3. 사용자가 쿼리를 수행하면 동일한 모델을 사용하여 벡터 임베딩으로 변환된다.
  4. 데이터베이스는 쿼리 벡터를 저장된 벡터와 비교한다.
  5. 시스템은 벡터 유사성을 기반으로 가장 관련성이 높은 상위 K개의 결과를 반환한다.
  6. 선택적 후처리를 통해 추가 필터를 적용하거나 순위를 재조정할 수 있다.

이 파이프라인은 기존 데이터베이스 접근 방식으로는 불가능했던 방대한 비정형 데이터 컬렉션에 대한 효율적인 의미론적 검색을 가능하게 한다.

  • 임베딩
    - 비정형 텍스트와 같은 고차원 데이터를 벡터와 같이 더 적은 차원의 표현으로 축소하는 것

  • 일반적으로 벡터DB들은 데이터를 RAM에 올려서 검색한다.

Local vector embeddings in Node.js

HuggingFace에는 벡터 임베딩에 사용할 수 있는 오픈소스 모델들이 많다.

Transformers.js는 javascript의 브라우저와 Node.js 두 환경 모두에서 머신 러닝 모델들을 사용할 수 있게 해주는 모듈이다.


모듈 설치

npm install @xenova/transformers

모듈 사용

벡터 임베딩에 많이 사용되는 로컬 모델은 all-MiniLM-L6-v2이다.

사용법은,
1. Transformers.js에서 pipeline 함수 import
2. 제공된 모델(all-MiniLM-L6-v2)을 사용해 "feature-extraction"을 수행할 extractor 생성
3. text를 extractor에 전달
4. tensor 객체가 반환된다. 이를 일반적인 숫자 배열로 변환할 수 있다.

import { pipeline } from "@xenova/transformers";

const extractor = await pipeline(
  "feature-extraction",
  "Xenova/all-MiniLM-L6-v2"
);

const response = await extractor(
  ["A robot may not injure a human being or, through inaction, allow a human being to come to harm."],
  { pooling: "mean", normalize: true }
);

console.log(Array.from(response.data));
// => [-0.004044221248477697,  0.026746056973934174,   0.0071970801800489426, ... ]

여러 개의 text를 한꺼번에 임베딩할 수도 있다.
1. 텍스트들을 배열로 extractor에 전달하고,
2. 응답 객체에서 tolist 메서드를 호출하면 각 텍스트에 대한 벡터들을 배열 형태로 반환 받을 수 있다.

const response = await extractor(
  [
"A robot may not injure a human being or, through inaction, allow a human being to come to harm.",
"A robot must obey the orders given it by human beings except where such orders would conflict with the First Law.",
"A robot must protect its own existence as long as such protection does not conflict with the First or Second Law.",
  ],
  { pooling: "mean", normalize: true }
);

console.log(response.tolist());
// [
//   [ -0.006129210349172354,  0.016346964985132217,   0.009711502119898796, ...],
//   [-0.053930871188640594,  -0.002175076398998499,   0.032391052693128586, ...],
//   [-0.05358131229877472,  0.021030642092227936, 0.0010665050940588117, ...]
// ]

주의할 점

모델이 다룰 수 있는 텍스트의 길이에 주의해야 한다.

예를 들어, all-MiniLM-L6-v2 모델은 최대 256개의 토큰을 다룰 수 있고, 128개 이상의 토큰에서는 좋은 결과를 제공하지 않는다. 따라서, 문장이나 작은 단락에 유용하다.

KNN과 ANN

KNN(K-Nearest Neighbor): 말 그대로 쿼리와 가장 관련성이 높은 k개의 벡터를 찾는 것

  • 모든 벡터와의 거리를 계산하여 가장 거리가 가까운 k개를 찾는다

ANN(Approximate Nearest Neighbor): 완벽한 정확도를 희생시켜서 성능의 이득을 취한다

  • KNN과 달리 항상 진정한 k개의 최근접 벡터를 반환하지 않을 수 있다
  • 대신 속도가 더 빠르다

ANN 알고리즘

  • HNSW(Hierarchical Navigable Small World)
    - 벡터 데이터를 여러 계층으로 나눈다.
    - 상위 계층에서 하위 계층으로 갈수록 밀도가 높아진다(더 많은 벡터가 배치된다).
    - 상위 계층에서 검색 결과에 가장 가까운 위치로 이동한 뒤, 하위 계층으로 내려가 동일한 탐색을 반복한다.

  • LSH(Locality-Sensitive Hashing)
    - 고차원 벡터를 해시 함수를 통해 '비슷한 것끼리 같은 버킷에 들어가도록' 만드는 기법

  • IVF(Inverted File Index)
    1. 벡터를 여러 그룹으로 분리(K-means 클러스터링). 각 그룹은 대표값을 가짐
    2. 우선 대표값들과 비교하여 가장 관련성 높은 그룹을 찾음
    3. 해당 그룹 내 임베딩 벡터들과 거리 계산 수행

  • PQ(Product Quantization)
    - 벡터를 여러 개의 작은 벡터로 나누고, 각 벡터를 압축하여 저장

등의 알고리즘들이 있다.

0개의 댓글