LLM을 사용할 때 주의해야할 점으로 환각 현상이 있습니다. 환각 현상, 또는 편하게 할루시네이션이라고 부르는 이 문제는 GPT-3.5를 사용하다보면 자주 확인할 수 있는 사항인데, LLM이 잘못되거나 없는 정보를 생성해버리는 문제를 말합니다.
이러한 문제를 해결하기 위해 모델링 부분에서는 파인튜닝이나 RLHF, DPO 등의 분야가 발전했고, 프롬프팅 분야에서는 RAG(Retrieval-Augmented Generation) 등 데이터 소스에서 정보를 가져와서 모델의 추론에 도움을 주는 분야가 발전하게 되었습니다.
LLM 모델이 데이터 소스에서 정보를 가져오기 위해서는 모델이 알아보기 쉽게 임베딩 처리를 한 문서 데이터베이스가 필요한데 이를 Vector store 라고 보통 부릅니다. 이 벡터 스토어에서 LLM이 필요한 데이터를 검색하는 기능이 Retriever 라고 할 수 있습니다.
이번에는 이 두가지 기능을 설명하는 튜토리얼 중 벡터스토어 내용을 정리해보도록 하겠습니다. 참고 문서는 랭체인의 공식 튜토리얼입니다.
Vector stores and retrievers | 🦜️🔗 LangChain
제가 작업한 노트북 파일은 아래 링크에서 확인할 수 있습니다.
https://github.com/saeu5407/langchain/blob/master/벡터스토어 예제.ipynb
먼저 벡터스토어에 적용할 임의의 문서를 로드해보겠습니다.
langchain_core.documents.Document는 page_content, metadata를 입력으로 받을 수 있습니다. 예제에서는 몇 가지 다른 메타데이터를 가진 문서를 정의했습니다. 이 클래스 외에도 다양한 방법으로 문서를 로드할 수 있습니다.
from langchain_core.documents import Document
documents = [
Document(
page_content="Dogs are great companions, known for their loyalty and friendliness.",
metadata={"source": "mammal-pets-doc"},
),
Document(
page_content="Cats are independent pets that often enjoy their own space.",
metadata={"source": "mammal-pets-doc"},
),
Document(
page_content="Goldfish are popular pets for beginners, requiring relatively simple care.",
metadata={"source": "fish-pets-doc"},
),
Document(
page_content="Parrots are intelligent birds capable of mimicking human speech.",
metadata={"source": "bird-pets-doc"},
),
Document(
page_content="Rabbits are social animals that need plenty of space to hop around.",
metadata={"source": "mammal-pets-doc"},
),
]
Vector Store
구조화되지 않은 텍스트 등을 저장하고 검색하는 일반적인 방법으로는 이 텍스트를 그대로 사용하는 것이 아닌 임베딩 방법을 통해 벡터화하여 유사도 분석 등을 통해 검색을 하는 방법이 있습니다.
LangChain VectorStore 객체는 Document 객체나 텍스트 등을 스토어에 추가하고 다양한 유사도 분석 메트릭을 사용하여 쿼리하는 방법을 가지고 있습니다. LangChain은 다양한 벡터 스토어를 포함했으며, 클라우드에서 사용하는 벡터 스토어 등도 포함되어 있습니다.
랭체인에서 사용할 수 있는 다양한 벡터스토어는 링크에서 확인할 수 있습니다. 여기에서는 메모리 내 구현을 포함하는 Chroma를 사용하여 LangChain VectorStores의 사용 방법을 확인해보도록 하겠습니다.
벡터스토어를 사용하기 위해서는 텍스트를 임베딩 벡터로 변환하기 위한 임베딩 모델이 필요한데요. LangChain은 이러한 임베딩 모델도 다양하게 포함했습니다. 사용할 수 있는 임베딩들은 링크에서 확인할 수 있습니다.
여기서는 OpenAI 임베딩을 사용하여 구현해보도록 하겠습니다.
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
vectorstore = Chroma.from_documents(
documents, # 정의해둔 문서를 사용
embedding=OpenAIEmbeddings(), # OpenAI 임베딩 사용
)
.from_documents
메서드를 통해 문서를 벡터스토어에 등록할 수 있습니다.
인스턴스화 한 후에도 문서를 추가 등록하는 등의 다양한 방법은 링크에서 확인할 수 있습니다.
이렇게 문서가 포함된 벡터스토어를 인스턴스화하면 쿼리를 수행할 수 있으며 다음과 같은 쿼리 방법을 포함합니다. 메서드는 일반적으로 출력에 문서 객체 목록을 포함합니다.
- 동기식 및 비동기식 처리
- 문자열 쿼리별, 벡터별 처리
- 유사성 점수 반환 여부
- 유사성 및 최대 한계 관련성 파라미터 설정
Examples
이제 벡터스토어에 등록된 문서에 대해 유사도를 쿼리해보도록 하겠습니다.
vectorstore.similarity_search("cat")
[Document(page_content='Cats are independent pets that often enjoy their own space.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Dogs are great companions, known for their loyalty and friendliness.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Rabbits are social animals that need plenty of space to hop around.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Parrots are intelligent birds capable of mimicking human speech.', metadata={'source': 'bird-pets-doc'})]
비동기식으로도 쿼리할 수 있습니다.
await vectorstore.asimilarity_search("cat")
[Document(page_content='Cats are independent pets that often enjoy their own space.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Dogs are great companions, known for their loyalty and friendliness.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Rabbits are social animals that need plenty of space to hop around.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Parrots are intelligent birds capable of mimicking human speech.', metadata={'source': 'bird-pets-doc'})]
유사도 점수는 다음과 같이 쿼리할 수 있습니다.
# Note that providers implement different scores; Chroma here
# returns a distance metric that should vary inversely with
# similarity.
vectorstore.similarity_search_with_score("cat")
[(Document(page_content='Cats are independent pets that often enjoy their own space.', metadata={'source': 'mammal-pets-doc'}),
0.375326931476593),
(Document(page_content='Dogs are great companions, known for their loyalty and friendliness.', metadata={'source': 'mammal-pets-doc'}),
0.4833090305328369),
(Document(page_content='Rabbits are social animals that need plenty of space to hop around.', metadata={'source': 'mammal-pets-doc'}),
0.4958883225917816),
(Document(page_content='Parrots are intelligent birds capable of mimicking human speech.', metadata={'source': 'bird-pets-doc'}),
0.4974174499511719)]
임베딩 간 비교를 해보면 동일하다는 걸 확인할 수 있습니다.
embedding = OpenAIEmbeddings().embed_query("cat")
vectorstore.similarity_search_by_vector(embedding)
[Document(page_content='Cats are independent pets that often enjoy their own space.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Dogs are great companions, known for their loyalty and friendliness.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Rabbits are social animals that need plenty of space to hop around.', metadata={'source': 'mammal-pets-doc'}),
Document(page_content='Parrots are intelligent birds capable of mimicking human speech.', metadata={'source': 'bird-pets-doc'})]
추가 상세 자료
벡터스토어에 대한 상세한 자료는 아래 공식 링크에서 확인할 수 있습니다.
LangChain VectorStore API
LangChain How To
추가 예제
공식 문서 외에 몇 가지 예제를 확인해보도록 하겠습니다.
벡터스토어를 사용한 서비스를 진행하다보면 중간에 문서를 추가해야하는 상황이 생기거나, 잠깐 벡터스토어를 셧다운 한 후에 다시 로드해야 할 경우가 있을 것 같다는 생각이 드는데요. 이에 대해 알아보도록 하겠습니다.
벡터스토어에 문서 추가하기
먼저 텍스트를 추가하는 방법입니다. 기존에는 Documents 클래스를 사용해서 문서를 만들었지만 저는 귀찮아서 add_text를 해보도록 하겠습니다.
저는 동기적으로 추가하고자 add_texts를 사용해보았습니다. add_texts, add_documents 뿐만 아니라 비동기 설정이 가능한 메서드등을 사용할 수 있습니다.
vectorstore.add_texts(["Shrimps are so delicious"])
해당 문서가 잘 들어갔나 확인해보도록 하겠습니다.
await vectorstore.asimilarity_search("shrimp")
[Document(page_content='Shrimps are so delicious'),
Document(page_content='Goldfish are popular pets for beginners, requiring relatively simple care.', metadata={'source': 'fish-pets-doc'}),
Document(page_content='Parrots are intelligent birds capable of mimicking human speech.', metadata={'source': 'bird-pets-doc'}),
Document(page_content='Rabbits are social animals that need plenty of space to hop around.', metadata={'source': 'mammal-pets-doc'})]
이외에도 vectorstore._collection.delete
등을 사용하여 기존 문서를 삭제하거나 업데이트할 수 있습니다.
벡터스토어 저장 및 로드하기
이번에는 벡터스토어를 잠깐 저장해두고 다시 로드할 수 있는지 테스트해보도록 하겠습니다.
예시로 사용하고 있는 벡터스토어는 Chroma로 벡터스토어마다 방법이 다르니 쓰시는 구조에 맞춰 공식 문서에서 검색하셔야 합니다.
Chroma의 경우 맨 처음 벡터스토어를 설정할 때 persist_directory를 설정하여 벡터스토어를 저장할 수 있습니다.
# 실행 때 지정해야 저장됨
vectorstore = Chroma.from_documents(
documents, # 정의해둔 문서를 사용
embedding=OpenAIEmbeddings(), # OpenAI 임베딩 사용
persist_directory="./chroma_db") # 저장 경로 설정
아까 애드한 문서는 저장이 안되어 있는 걸 확인할 수 있습니다.
# add_text한 것이기 때문에 shrimp 관련 내용이나 업데이트, 딜리트한 내용은 사라짐
await vectorstore.asimilarity_search("shrimp")
이제 다시 애드해둔다면 잘 저장되겠죠?
# 다시 애드해둔다면?
vectorstore.add_texts(["Shrimps are so delicious"])
저장된 벡터스토어는 다음과 같이 로드할 수 있습니다.
vectorstore_load = Chroma(persist_directory="./chroma_db", embedding_function=OpenAIEmbeddings())
# 다시 애드하면 다행히 있다!
await vectorstore_load.asimilarity_search("shrimp")
[Document(page_content='Shrimps are so delicious'),
Document(page_content='Goldfish are popular pets for beginners, requiring relatively simple care.', metadata={'source': 'fish-pets-doc'}),
Document(page_content='Parrots are intelligent birds capable of mimicking human speech.', metadata={'source': 'bird-pets-doc'}),
Document(page_content='Rabbits are social animals that need plenty of space to hop around.', metadata={'source': 'mammal-pets-doc'})]
다음에는 Retriever를 정리해보도록 하겠습니다.
https://python.langchain.com/v0.2/docs/tutorials/retrievers/
https://github.com/saeu5407/langchain/blob/master/벡터스토어 예제.ipynb