슬슬 논문 작성이 막바지에 들어섰다. 나는 현재 학부 연구생 소속으로서 벡터 데이터베이스와 테이블 데이터를 .csv 파일로 뽑아서 embedding 하는 부분을 연구하고 있다. 다른 연구원 한 분과 같이 작업하고 있는데, 간단하게 소개하면 연구 내용은 테이블 데이터(.csv) 파일로 검색하는 RAG를 만드는 내용이다.
좀 더 설명하면 .csv 파일을 통해 검색하는 검색 시스템을 만드는 것이다. 중요한 점은 단순한 검색이 아니라 쿼리용으로 쓰인 테이블 데이터로 검색을 하였을 때, 융합하기 좋은 테이블을 찾는 것이다. 그렇기 때문에 RAG를 구현하는 느낌에다가 이것저것 부가적인 방법을 섞어서 꾸준히 실험을 계속하고 있다.
각설하고, 이러한 과정에서 필요한 것이 Vector Database였다. 수 많은 .csv 데이터를 VDB(Vector DataBase)에 집어넣고서, 필요할 때 VDB에서 꺼내서 사용하는 것이다. 다양한 논문을 살펴본 결과 VDB를 실제로 많이 쓰고 있었고, 이번 포스팅에서는 embedding과 같은 벡터 값을 사용하는 경우 손쉽게 milvus를 통해서 VDB를 다룰 수 있게끔 하기 위해 포스팅을 작성했다.
본론
벡터 데이터베이스란?
벡터 데이터베이스는 고차원의 벡터 데이터를 저장하고 검색하는 데 최적화된 특수한 데이터베이스 시스템이다. 여기서 벡터는 ML 용어로 embedding이라고 부르는 벡터이다.
이미지, 텍스트, 오디오 등 다양한 형태의 데이터를 각각에 맞는 model을 써서 벡터로 나타낼 수 있는데, 벡터로 나타낸 값을 embedding이라고 하고, 이러한 embedding을 저장하는 것이 VDB이다.
그렇다면 왜 VDB가 필요할까? 기존의 RDB나 NoSQL에 올려도 괜찮을 것 같은데, 왜 굳이? 그 이유는 벡터값을 통한 검색은 기존의 RDB 패러다임과는 완전히 다르기 때문이다. 우리가 일반적으로 RDB에서 사용하는 쿼리 최적화 기법 ( indexing, B+ tree .. )이나 SQL을 통한 검색과는 다르게, VDB에서는 애초에 검색 자체를 벡터값으로 검색한다! 그렇기 때문에 쿼리 최적화 기법이나 SQL 같은 기존의 전통적인 접근법이 통하지 않고, 자체적으로 연구하고 있는 인덱스 방법을 쓴다. FLAT, IVF-PQ 등 다양한 인덱싱 방법이 있으며, 아직까지도 활발하게 연구하고 있는 중이다.
그렇기 때문에 이러한 벡터 데이터베이스는 RDB와는 확연히 다른 속도를 가지고 있으며 벡터를 통한 검색에 수많은 메서드가 내장되어 있다. 단순히 우리가 아는 내용들을 파라미터로 전달해주면 손쉽게 벡터 검색을 할 수 있게 되는 것이다. 추천 시스템이나 검색 시스템을 만들 때, 이러한 내용을 VDB를 사용하면 손쉽게 구현할 수 있다.
Milvus
Milvus(밀버스)는 벡터 데이터베이스의 한 종류이다. VDB는 다양하게 존재하는 데, 연구용으로 쓰이며 가장 활발한 오픈소스 커뮤니티를 가지고 있는 것이 Milvus이고, 기업용으로 쓰이며 유로 플랜에 따라 굉장히 많은 기능이 활성화 되어 있는 Pinecone(파인콘) 등이 있다. 기업들은 주로 파인콘을 많이 사용하고, 연구 목적이나 직접 VDB를 구축할 인력 및 인프라가 되는 기업에 한해서 밀버스로 마이그레이션 하는 경우도 꽤나 있다.
아래의 레퍼런스에서 milvus에 대해서 확인할 수 있다.
- [milvus] 공식 홈페이지 : https://milvus.io/
The High-Performance Vector Database Built for Scale | Milvus
Milvus is a powerful vector database tailored for processing and searching extensive vector data. It stands out for its high performance and scalability, rendering it perfect for machine learning, deep learning, similarity search tasks, and recommendation
milvus.io
이번 포스팅에서는 VDB를 직접 다루는 내용이 있는데, milvus는 크게 두 종류로 나뉜다. 단순하게 하나의 노드에서 띄울 수 있는 milvus-standalone과 클러스터링 구조로 분산되어 안전성을 가져가는 milvus-distributed 이다. 전체적인 아키텍처는 아래와 같으나, 우리가 가져갈 스탠드얼론의 구조는 아래와 완전히 일치하지는 않는다.
VDB 띄우기 및 연결
먼저, 다음의 페이지에 있는 내용을 참고했다. 관련된 스크립트는 아래의 링크에서 참고할 수 있으니, 버전 이슈 등의 에러가 발생했다면 아래의 페이지를 참고하는 것이 좋아 보인다.
- [Milvus] Run Milvus with Docker Compose : https://milvus.io/docs/install_standalone-docker-compose.md
먼저, docker가 실행되어 있어야 한다. 그 후에는 docker-compose의 스크립트를 다운로드하여 실행해 보자.
wget https://github.com/milvus-io/milvus/releases/download/v2.5.0-beta/milvus-standalone-docker-compose.yml -O docker-compose.yml
sudo docker-compose up -d
위 docker-compose.yml을 실행하면 etcd와 minio 그리고 standalone이 뜨게 되는데, etcd는 전역적으로 상태를 관리해 주는 애플리케이션이고, minio는 객체를 저장해 주는 역할을 하는 애플리케이션이다.
milvus-standalone은 19530번 포트를 통해서 뜬다. 따라서 직접 milvus database에 접근하기 위해서는 19530번 포트에 붙어야 한다. 이제 해당 VDB를 다루는 내용을 알아보자.
먼저, pymilvus라고 하는 패키지를 다운로드하아야 한다. 다음의 명령어를 통해서 해당 패키지를 다운로드한다. 패키지 용량이 크지 않기 때문에 굳이 가상환경을 다운로드하지 않아도 될 것 같다. ( 필요에 따라 추천 )
pip3 install pymilvus
그리고 간단하게 벡터값을 삽입하고, 벡터값을 통해 벡터 서치하는 내용의 모든 스크립트는 아래와 같다.
from pymilvus import connections, FieldSchema, CollectionSchema, DataType, Collection, utility
# Milvus 연결
connections.connect("default", host="127.0.0.1", port="19530")
# 전역변수 설정
dimension = 8 # 벡터 차원 설정
# Milvus 컬렉션 스키마 정의
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=False),
FieldSchema(name="title", dtype=DataType.VARCHAR, max_length=512),
FieldSchema(name="vector", dtype=DataType.FLOAT_VECTOR, dim=dimension),
]
schema = CollectionSchema(fields, description="Schema to store vector and other related data in Milvus")
# table이 이미 있다면 삭제 ( create_drop과 같은 기능!! )
if utility.has_collection("hello_milvus"):
utility.drop_collection("hello_milvus")
# hello_milvus 테이블에 연결
hello_milvus = Collection("hello_milvus", schema)
# 더미 데이터 생성
entities = [
[1, 2, 3, 4],
['this', 'is', 'dummy', 'data'],
[
[3.2, 7.1, -2.7, 5.4, 0.4, 12.1, -8.3, 4.2],
[5.1, 9.1, 4.7, -3.6, 8.5, 6.9, -2.0, -1.0],
[1.2, 7.2, -3.3, 9.7, -6.7, 0.6, 5.7, -4.5],
[10.1, 2.3, 5.3, 7.4, 3.7, -8.8, -6.9, -1.9]
]
]
insert_result = hello_milvus.insert(entities)
hello_milvus.flush()
# 인덱스 생성 및 로드
print("Creating index with FLAT configuration.")
index_params = {
"index_type": "IVF_FLAT",
"metric_type": "L2",
"params": {"nlist": 128},
}
hello_milvus.create_index("vector", index_params)
hello_milvus.load()
# 검색 파라미터 설정
search_params = {
"metric_type": "L2",
"params": {"nprobe": 10},
}
# 검색 벡터
query_vector = [[10.1, -2.3, -5.4, 7.5, 3.5, -8.7, 6.1, 1.0]]
# 검색 실행
result = hello_milvus.search(query_vector, "vector", search_params, limit=2, output_fields=["title"])
print(result)
위와 전체적으로 구성하였다. 위 코드를 그대로 실행해 보면, 아래와 같은 결과가 나오는 것을 알 수 있다.
Creating index with FLAT configuration.
data: ['["id: 4, distance: 313.1200256347656, entity: {\'title\': \'data\'}", "id: 3, distance: 399.6499938964844, entity: {\'title\': \'dummy\'}"]']
위에서 search_params로 L2를 설정했는데, 이는 유클리드 거리이기 때문에 위와 같은 distance 값이 나온다. 만약 유클리드 거리가 아니라 COSINE 같은 파라미터를 넣어주면 distance가 코사인 유사도로 나오며 그로 인해 음수로 나오는 경우가 있다. 이 점은 milvus의 공식 문서를 참고할 수 있다.
- [milvus] : Index Vector Fields : https://milvus.io/docs/index-vector-fields.md?tab=floating
위는 milvus에서 살펴본 index 타입의 종류이다. 위 스크립트에서는 간단하게 L2로 진행하였지만, 일반적으로는 COSINE 방식을 써서 검색 엔진을 구현한다고 한다.
이제 남은 부분은 VDB에 데이터를 넣기 전에 embedding model을 거쳐서 벡터값을 넣으면 벡터 데이터베이스를 통해 검색 엔진의 구현이 끝난다. 보통 256차원이나 768차원의 embedding이 존재하는데, 이 부분은 적절히 바꿔서 schema를 정의하면 된다.
다음 포스팅에서는 이러한 부분에 적절한 embedding model과 flask를 통해서 간단하게 래핑하여, 띄워 검색 엔진을 만드는 과정에 대해서 포스팅하려고 한다.
마치며
사실 현업에서는 ElasticSearch를 보다 하드하게 다룬다고 들었는데, 기회가 된다면 엘라스틱서치를 한번 써보고 싶다. 또한 엘라스틱서치진영에는 Kibana 와 logstash 등과 함께 ETL을 손쉽게 진행할 수 있기 때문에, 일반적으로는 엘라스틱서치가 핫하다고 한다.
참고
- [milvus] 공식 문서 : https://milvus.io/
감사합니다.
'ML engineer' 카테고리의 다른 글
embedding을 이용한 검색 엔진/추천 시스템 만들기 (3) | 2024.12.02 |
---|---|
embedding이란? (0) | 2024.08.05 |