728x90
반응형

안녕하세요! 테디노트님의 RAG 비법노트 37일차 학습 기록입니다.

오늘은 어제(36일차)까지 배운 OpenAI 임베딩을 한 단계 발전시켜, 임베딩을 캐싱(Cache) 하는 방법을 실습했습니다.

RAG 파이프라인에서는 문서를 임베딩할 때마다 모델이 호출되기 때문에, 동일한 데이터를 여러 번 처리하면 비용과 시간이 낭비됩니다.

이를 해결하는 방법이 바로 CacheBackedEmbeddings 입니다.


1. CacheBackedEmbeddings란?

CacheBackedEmbeddings는 임베딩 결과를 저장해두었다가

같은 텍스트를 다시 임베딩할 때 모델을 재호출하지 않고 캐시된 결과를 사용하는 래퍼(wrapper) 클래스입니다.

즉, “이미 계산된 임베딩은 다시 계산하지 않는다”는 개념입니다.

이 방식은 특히 대량 문서 처리나 벡터 DB 구축 시 큰 효율을 가져옵니다.


2. 작동 원리

  • 텍스트를 해시(hash)하여 캐시의 Key로 사용
  • 해시된 Key와 임베딩 벡터를 Key-Value 저장소(ByteStore) 형태로 저장
  • 다음에 같은 텍스트를 요청하면 → 모델 호출 대신 캐시에서 즉시 반환

핵심 초기화 함수는 다음과 같습니다. 👇

CacheBackedEmbeddings.from_bytes_store(
    underlying_embeddings,        # 실제 임베딩 모델 (예: OpenAIEmbeddings)
    document_embedding_cache,     # 캐시 저장소 (LocalFileStore, InMemoryByteStore 등)
    namespace=embedding.model     # 네임스페이스(중복 방지용)
)

💡 주의

다른 임베딩 모델을 동시에 쓸 때 충돌이 날 수 있으므로

namespace에는 보통 모델 이름(embedding.model)을 넣습니다.


3. LocalFileStore로 임베딩 캐싱 (영구 저장)

먼저, 로컬 디스크에 캐시를 저장하는 방식입니다.

한 번 생성된 임베딩은 ./cache/ 폴더에 저장되어, 다음 실행 시 재활용됩니다.

from langchain.storage import LocalFileStore
from langchain_openai import OpenAIEmbeddings
from langchain.embeddings import CacheBackedEmbeddings
from langchain_community.vectorstores.faiss import FAISS

# 1. 기본 임베딩 설정
embedding = OpenAIEmbeddings(model="text-embedding-3-small")

# 2. 로컬 저장소 설정
store = LocalFileStore("./cache/")

# 3. 캐시 지원 임베딩 객체 생성
cached_embedder = CacheBackedEmbeddings.from_bytes_store(
    underlying_embeddings=embedding,
    document_embedding_cache=store,
    namespace=embedding.model
)

# 4. 캐시 키 확인
list(store.yield_keys())

 

이제 문서를 로드하고 분할한 뒤, FAISS 벡터 저장소를 생성해봅니다.

from langchain.document_loaders import TextLoader
from langchain_text_splitters import CharacterTextSplitter

# 문서 로드 및 분할
raw_documents = TextLoader("./data/appendix-keywords.txt").load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
documents = text_splitter.split_documents(raw_documents)

# 첫 번째 실행 (임베딩 계산 포함)
%time db = FAISS.from_documents(documents, cached_embedder)

# 두 번째 실행 (캐시 활용, 훨씬 빠름)
%time db2 = FAISS.from_documents(documents, cached_embedder)

 

실행 결과 비교 예시

첫 번째 실행: CPU times: user 100 ms, sys: 19.3 ms, total: 120 ms
Wall time: 1.32 s
두 번째 실행: CPU times: user 4.61 ms, sys: 2.18 ms, total: 6.79 ms
Wall time: 6.96 ms

📈 캐싱 덕분에 속도는 수십 배 빨라지고,

비용(OpenAI API 호출 수) 또한 크게 절감됩니다.


4. InMemoryByteStore로 임베딩 캐싱 (비영구 저장)

이번엔 메모리 기반 캐시를 사용해봅시다.

이 방식은 실행 중에만 데이터를 유지하며, 프로그램 종료 시 사라집니다.

from langchain.embeddings import CacheBackedEmbeddings
from langchain.storage import InMemoryByteStore
from langchain_openai import OpenAIEmbeddings

embedding = OpenAIEmbeddings(model="text-embedding-3-small")
store = InMemoryByteStore()

cached_embedder = CacheBackedEmbeddings.from_bytes_store(
    embedding, store, namespace=embedding.model
)

💡 InMemoryByteStore

  • 장점: 빠름, 임시 테스트용으로 적합
  • 단점: 프로그램 종료 시 데이터 손실 (디스크 저장 아님)

 

Local vs InMemory 비교

구분 클래스 저장 위치  영구성 사용예시
LocalFileStore langchain.storage.LocalFileStore 로컬 디스크(./cache/) ✅ 영구 RAG 서비스 구축 시
InMemoryByteStore langchain.storage.InMemoryByteStore 메모리 내 ❌ 비영구 실험, 테스트 환경

🧾 마무리

오늘은 CacheBackedEmbeddings를 활용하여 임베딩을 효율적으로 캐싱하는 방법을 학습했습니다.

💬 한 줄 요약

RAG의 성능을 높이는 가장 쉬운 최적화는 “이미 계산된 임베딩을 다시 계산하지 않는 것”이다.

 

읽어주셔서 감사합니다!

728x90
반응형

+ Recent posts