Ray Data와 Milvus를 활용한 RAG 애플리케이션용 대규모 임베딩 추론
검색 증강 생성(RAG)은 엔터프라이즈 생성형 AI에서 가장 인기 있는 사용 사례 중 하나입니다. 대부분의 RAG 튜토리얼은 임베딩 모델과 대규모 언어 모델(LLM) 추론 모두에 OpenAI API를 사용하는 방법을 보여줍니다. 특히 개발 과정에서 왜 자신의 데이터에 접근하기 위해 비용을 지불해야 할까요? 오픈 소스를 사용하면 자신의 데이터에 접근하고 원하는 만큼 빠르게 반복할 수 있습니다.
가장 흥미로운 발견 중 하나는 데이터가 벡터로 변환되는 임베딩 단계에서 Ray Data로 달성한 놀라운 성능 향상이었습니다.
Ray Data와 같은 도구로 풀링된 배치 추론 요청을 사용해 오픈 소스 임베딩을 실행하면 Pandas에 비해 리소스와 시간이 절약되었습니다. 16GB RAM이 장착된 Mac M2 노트북에서 단 4개의 워커만 사용했을 때 Ray Data는 60배 더 빨랐으며, 자세한 내용은 이 블로그의 후반부에서 다룹니다.
우리의 오픈 소스 RAG 스택:
새로운 BGM-M3 임베딩 모델(한 번에 3가지 유형의 벡터 생성: 희소, 밀집, 멀티 벡터)
빠르고 분산된 임베딩 추론을 위한 Ray Data
추론 결과를 임시로 저장하기 위한 AWS S3
Milvus 또는 Zilliz Cloud 벡터 데이터베이스
Kaggle IMDB poster에서 다운로드한 예제 데이터
우리의 오픈 소스 RAG 스택
BGM-M3 임베딩 모델
강력한 희소, 밀집, 멀티 벡터 임베딩. BGE-M3 임베딩 모델은 “멀티” 기능 때문에 별칭이 붙었습니다: 다국어성, 다기능성, 다중 세분성. 100개 이상의 언어에서 작동할 수 있으며, 세 가지 일반적인 검색 방법인 밀집, 희소, 멀티 벡터 임베딩을 동시에 계산할 수 있습니다. 또한 짧은 문장부터 긴 문서(최대 8,192 토큰)까지 다양한 텍스트 길이에서 작동합니다. 이 논문 또는 이 HuggingFace 페이지의 모델에서 더 자세히 알아볼 수 있습니다.
버전 2.4부터 Milvus는 BGE M3에 대한 내장 지원을 제공합니다.
Ray Data
장시간 실행되는 데이터 변환 작업인가요?
Ray Data의 확장 가능한 데이터 처리는 여러 머신(CPU, GPU 등)에 걸쳐 방대한 양의 데이터를 병렬로 더 쉽고 빠르게 처리할 수 있게 해줍니다. Ray Data는 많은 동시 청킹 및 임베딩 변환처럼 데이터를 병렬 프로세스로 분할할 수 있을 때 특히 유용합니다! 내부적으로 Ray Data는 클러스터의 GPU 활용도를 극대화하기 위한 강력한 스트리밍 실행 엔진을 갖추고 있습니다. 온라인 서비스(예: OpenAI embedding API)로 임베딩을 실행하는 것과 비교하면, Ray Data로 오프라인 임베딩 작업을 실행하면 대부분의 비용을 절감할 수 있습니다.
Anyscale은 Ray를 위한 관리형 플랫폼입니다. Anyscale에서 임베딩 작업을 쉽게 확장하여 수백 대의 GPU 머신을 활용할 수 있습니다.
Milvus와 Zilliz
번개처럼 빠른 RAG 앱 뒤에 있는 비결은 강력한 벡터 데이터베이스입니다!_ Milvus는 기업의 대규모 사용을 위해 방대한 양의 데이터를 처리하도록 구축되었습니다. 일부 벡터 데이터베이스와 달리 Milvus는 데이터 요구가 증가함에 따라 유연하게 성장할 수 있습니다 – 저장, 인덱싱, 쿼리를 위한 아키텍처 계층은 독립적으로 스케일업 및/또는 스케일아웃되도록 설계되었습니다. 이를 통해 Milvus가 쿼리가 실시간으로 들어오기 전과 들어오는 동안 오프라인 계산을 지능적으로 수행하므로 RAG 앱이 빨라집니다. 또한 Milvus에는 데이터를 안전하고 체계적으로 유지하는 기능(멀티 테넌시 및 역할 기반 액세스 제어)과 항상 사용 가능하도록 보장하는 기능(고가용성)처럼 비즈니스에 중요한 다양한 부가 기능도 포함되어 있습니다.
Zilliz는 관리형 클라우드 제품이며 오픈 소스 Milvus를 사용합니다.
RAG 도구 설정하기
Milvus, Ray Data, Amazon S3 및 Zilliz용 Python SDK를 사용할 것입니다.
Amazon S3의 경우 AWS account에 가입해야 합니다.
브라우저에서 console.aws.amazon.com > IAM > My security credentials > Create access key로 이동합니다. 키와 비밀 키를 복사하여 로컬에 안전하게 저장합니다.
라이브러리를 설치하고 aws config를 실행합니다. 그러면 AWS 변수가 credentials 파일에 저장됩니다.
pip install boto3
pip install awscli –force-reinstall –upgrade
aws config #키와 비밀 키를 입력하세요
more ~/.aws/credentials #올바르게 보이는지 확인하세요
Ray Data 설치:
pip install -U "ray[data]"
Pymilvus 설치:
pip install -U pymilvus "pymilvus[model]" langchain
BGE-M3 임베딩 모델은 v2.4부터 이미 Pymilvus에 패키징되어 제공됩니다.
import ray, os, pprint, time, boto3
from langchain.text_splitter import RecursiveCharacterTextSplitter
import numpy as np
import pymilvus
print(pymilvus.__version__) # must be >= 2.4.0
from pymilvus.model.hybrid import BGEM3EmbeddingFunction
Zilliz free tier(최대 2개 컬렉션, 각 100만 벡터)를 사용하려면 계정에 가입하고 Starter cluster를 생성하세요.
데이터 준비
이 블로그의 코드는 잘 알려진 공개 Kaggle IMDB poster data를 사용합니다. 약 48,000편의 영화, 리뷰, 포스터 링크 및 더 많은 메타데이터가 포함되어 있습니다.
저는 모든 텍스트 필드(영화 이름, 설명, 리뷰 텍스트)를 ‘text’라는 새 열에 복사하고, CSV보다 더 효율적이므로 Parquet 형식으로 저장했습니다.
임베딩 생성
임베딩을 생성하는 단계는 다음과 같습니다:
- 데이터 청킹: 의미적으로 관련된 텍스트 조각을 함께 유지하기 위해 입력 텍스트를 청크로 분할합니다.
- 추론 모드에서 임베딩 모델을 호출하여 청크의 벡터 표현을 생성합니다.
Ray Data는 다음을 사용하여 이러한 데이터 작업을 병렬화할 수 있습니다:
- 출력이 입력보다 더 많은 행을 갖게 되므로 데이터 청킹에는 flat_map().
- 호출 가능한 Class 메서드 내부에서 임베딩 모델을 호출하는 데는 map_batches().
chunk_size = 512
chunk_overlap = np.round(chunk_size * 0.10, 0)
# Define a LangChain text splitter.
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
length_function=len) #len is a built-in Python function
# 1. Define a regular python function for chunking.
def chunk_row(row, splitter=text_splitter):
# Copy the row columns into metadata.
metadata = row.copy()
del metadata['text'] # Remove text from metadata
# Split the text into chunks.
chunks = splitter.create_documents(
texts=[row["text"]],
metadatas=[metadata])
chunk_list = [{
"text": chunk.page_content,
**chunk.metadata} for chunk in chunks]
return chunk_list
# 2. Define a class with a callable method to compute embeddings.
class ComputeEmbeddings:
def __init__(self):
# Initialize a Milvus built-in sparse-dense-late-interaction-reranking encoder.
# https://huggingface.co/BAAI/bge-m3
self.model = BGEM3EmbeddingFunction(use_fp16=False, device="cpu")
print(f"dense_dim: {self.model.dim['dense']}")
print(f"sparse_dim: {self.model.dim['sparse']}")
def __call__(self, batch):
# Ray data batch is a dictionary where values are array values.
# BGEM3EmbeddingFunction input is docs as a list of strings.
docs = list(batch['text'])
# Encode the documents. bge-m3 dense embeddings are already normalized.
embeddings = self.model(docs)
batch['vector_dense'] = embeddings['dense']
return batch
if __name__ == "__main__":
FILE_PATH = "s3://zilliz/kaggle_imdb.parquet"
# Load and transform data.
ds = ray.data.read_parquet(FILE_PATH)
# 입력 텍스트를 청크로 나눕니다
chunked_ds = ds.flat_map(chunk_row)
# 임베딩 모델을 호출하는 클래스로 임베딩을 계산합니다.
embeddings_ds = chunked_ds.map_batches(ComputeEmbeddings, concurrency=4)
# 임베딩을 parquet part 파일 폴더 형태로 S3에 저장합니다.
embeddings_ds.write_parquet('s3://zilliz/kaggle_imdb_embeddings')
이를 실행하려면 Ray job으로 제출합니다:
코드를 Python 스크립트 파일로 저장하세요. 저는 ray_data_demo.py라고 이름 붙였습니다
노트북에서 로컬로 실행하려면, .py 스크립트 파일과 .parquet 데이터 파일만 있는 깨끗한 디렉터리를 만드세요. 이 깨끗한 디렉터리에는 꼭 필요한 최소한의 것만 넣으세요. 저는 ‘ray_cluster’라고 이름 붙였습니다.
Python 스크립트를 실행하세요. 그러면 Ray cluster가 시작되고 job이 자동으로 제출됩니다.
**
http://127.0.0.1:8265로 이동하세요. Cluster와 Jobs 타이밍을 확인하세요.
임베딩 지연 시간 - 노트북에서 60배 더 빠름
| 접근 방식 | 입력 데이터 크기 | 총 시간 | 스크린샷 |
| Pandas | 100행 | 23초 |
|
| Ray Data | 100행 | 50초 |
|
| Pandas | 45K행 | >4시간 |
|
| Ray Data | 45K행 | 4분 |
|
표: M2 16GB 노트북에서 데이터를 임베딩하는 데 걸린 시간. Ray data batch processing은 단일 노드 Ray cluster에서 concurrency= 4 workers로 수행되었습니다. Pandas는 프로세서가 하나뿐이어서 느렸고, Ray Data에는 4개의 프로세서가 있었습니다. 둘 다 더 큰 cluster에서는 더 빠르게 실행될 것입니다.
S3에서 임베딩된 데이터를 Milvus 또는 Zilliz로 직접 대량 삽입하기
Milvus와 Zilliz는 모두 bulk-insert를 제공합니다. 이를 통해 이미 임베딩된 데이터를 AWS, GCP 또는 Azure에서 직접 가져올 수 있습니다. 아래에 표시된 web console 외에도, Zilliz는 restful API 및 SDK도 제공합니다.
batch로 생성된 대규모 임베딩 corpus의 경우, bulk import를 사용하면 incremental insertion에 비해 machine resource를 크게 절약하고 insertion time을 단축할 수 있습니다. 더 중요한 것은 bulk import로 구축된 vector search index가 incremental insertion으로 만든 것보다 훨씬 더 효율적이라는 점입니다(전역 최적화 vs. 지역 최적화를 생각해 보세요).
Zilliz Cloud web console에서 몇 번의 간단한 클릭으로 bulk import를 편리하게 수행하는 방법을 살펴보겠습니다. 새 collection을 만들고자 하는 Cluster에서 시작하여, AutoID를 사용하고, 올바른 EMBEDDING_DIMENSION을 가진 “vector” column만 포함하며, 편리한 “Dynamic Field” option을 사용한 새 collection을 만든 다음, “Create Collection”을 클릭하세요.
다음으로 “Import Data”를 클릭하고 화면 지침에 따라 Ray Data 작업이 작성한 parquet 파일의 경로를 복사합니다. (S3 버킷이 비공개인 경우, Zilliz Cloud가 그 안의 데이터를 읽을 수 있도록 Access Key와 Secret Key도 지정해야 합니다). Amazon S3, Google Cloud Storage 또는 Azure Blob Storage 클라우드 소스가 모두 지원됩니다. “Import”를 클릭하여 모든 데이터를 벡터 데이터베이스 컬렉션으로 가져오기 시작합니다.
가져오기가 완료되면, Query your data 단계에서 벡터 검색을 더 효율적으로 만들기 위해 선택적으로 컬렉션에서 인덱스 빌드를 클릭할 수 있습니다.
이미지: Zilliz 대량 삽입 화면의 스크린샷.
데이터 쿼리하기
새로 가져온 컬렉션을 테스트하기 위해 질문을 하고 영화 데이터에서 답변을 검색해 보겠습니다.
def mc_run_search(question, output_fields, top_k=2, filter_expression=""):
# Embed the question using the same encoder.
embeddings = model_bgem3([question])
query_embeddings = embeddings['dense']
# Run semantic vector search using your query and the vector database.
results = mc.search(
COLLECTION_NAME,
data=query_embeddings,
search_params=SEARCH_PARAMS,
output_fields=output_fields,
# Milvus can utilize metadata in boolean expressions to filter search.
filter=filter_expression,
limit=top_k,
consistency_level="Eventually"
)
# Assemble retrieved context and context metadata.
# The search result is in the variable `results[0]`, which is type
# 'pymilvus.orm.search.SearchResult'.
METADATA_FIELDS = [f for f in output_fields if f != 'chunk']
formatted_results, context, context_metadata = _utils.client_assemble_retrieved_context(
results, metadata_fields=METADATA_FIELDS, num_shot_answers=top_k)
return formatted_results, context, context_metadata
SAMPLE_QUESTION = "muybridge horse movie"
# Return top k unique results with HNSW index.
TOP_K = 2
# Define output fields to return.
OUTPUT_FIELDS = ["movie_id", "chunk", "PosterLink"]
formatted_results, context, context_metadata = \
mc_run_search(SAMPLE_QUESTION, OUTPUT_FIELDS, TOP_K)
상위 2개의 고유 결과를 반복해 보면, 위 검색 쿼리에서 다음 콘텐츠가 밀접하게 반환된 것을 볼 수 있습니다:
전체 Ray Data script는 GitHub에서 확인할 수 있습니다.
결론
이 블로그에서는 Ray Data와 Milvus Bulk Import 기능을 사용하여 벡터 생성을 크게 가속화하고 이를 벡터 데이터베이스에 효율적으로 일괄 로드하는 방법을 보여주었습니다. 예를 들어, Ray Data를 사용해 102K 행의 데이터를 임베딩하는 데는 4분이 걸렸지만, 단순한 Pandas 접근 방식으로는 4시간이 걸렸습니다! ****또한 Milvus에서 Bulk Import를 사용하면 일반적인 점진적 삽입에 비해 매우 효율적인 벡터 인덱스를 구축하고 리소스와 시간을 절약할 수 있습니다. 자세한 내용은 Ray Data 및 Milvus와 Zilliz Cloud의 Bulk Import 기능을 확인해 보세요!
계속 읽기

Zilliz Cloud Now Available in AWS Europe (Ireland)
Zilliz Cloud launches in AWS eu-west-1 (Ireland) — bringing low-latency vector search, EU data residency, and full GDPR-ready infrastructure to European AI teams. Now live across 30 regions on five cloud providers.

Zilliz Cloud Update: Smarter Autoscaling for Cost Savings, Stronger Compliance with Audit Logs, and More
What's new in Zilliz Cloud? Smarter autoscaling with scale-down, audit logs GA, enhanced SSO, and Milvus 2.6 in Private Preview.

Zilliz Cloud Enterprise Vector Search Powers High-Performance AI on AWS
Zilliz Cloud on AWS powers secure, scalable, ultra-fast vector search for enterprise AI apps, with BYOC, sub-10ms latency, and zero-DevOps simplicity.




