이번 포스팅에서도 랭체인의 컴포넌트에 대해 알아볼거에요.
랭체인의 컴포넌트에는 Model I/O, Retrieval, Composition, Memory, Callbacks 등이 있어요.
모든 컴포넌트를 한 번에 공부하기는 쉽지 않기 때문에!
이번에는 Retrieval에 대해서 학습해볼게요.
# Retrieval
LLM 애플리케이션은 모델이 미리 학습한 데이터 외에도 사용자 또는 사용 목적에 따라 별도이 데이터를 필요로 해요.
즉, 모델 내부가 아닌 외부에서 데이터를 검색한 다음 거대 언어 모델(LLM)에게 전달해야 하죠.
이러한 기능을 RAG(Retrieval Augmented Generation)이라고 합니다!
랭체인은 RAG 애플리케이션을 위한 구성 요소들을 제공하는데!
랭체인에서 제공하는 구성 요소가 무엇이고, 어떤 역할을 하는지 알아볼까요?
# Document Loaders
Document Loaders는 말 그대로 문서(HTML, PDF, 코드 등)의 내용을 읽어오는 역할을 담당해요.
랭체인에는 100개가 넘는 다양한 Document Loaders를 제공하기도 하고,
AirByte나 Unstructured와 같이 주요 공급업체와 연계할 수 있는 기능을 제공하기도 해요.
그럼 예제 소스코드를 통해 어떻게 문서를 읽어오는지 알아볼게요.
from langchain_community.document_loaders import TextLoader
loader = TextLoader("./index.md")
loader.load()
# 결과
[
Document(page_content='---\nsidebar_position: 0\n---\n# Document loaders\n\nUse document loaders to load data from a source as `Document`\'s. A `Document` is a piece of text\nand associated metadata. For example, there are document loaders for loading a simple `.txt` file, for loading the text\ncontents of any web page, or even for loading a transcript of a YouTube video.\n\nEvery document loader exposes two methods:\n1. "Load": load documents from the configured source\n2. "Load and split": load documents from the configured source and split them using the passed in text splitter\n\nThey optionally implement:\n\n3. "Lazy load": load documents into memory lazily\n', metadata={'source': '../docs/docs/modules/data_connection/document_loaders/index.md'})
]
랭체인에서 제공하는 TextLoader를 사용해서 마크다운(.md) 파일을 읽어왔네요.
마크다운뿐만 아니라 다양한 문서를 읽을 수 있다고 했으니
텍스트(.txt) 파일, PDF 파일도 읽어보면서 테스트 해보세요!
참고로 Document Loaders는 웹 페이지의 텍스트 내용도 로드하며,
심지어 YouTube 비디오의 대본을 읽어오기도 한답니다.
Document Loaders를 잘 활용한다면 다양하게 학습 데이터를 수집하고 사용할 수 있겠죠?
그리고 문서 로더는 선택적으로 메모리에 데이터를 지연 로드하기 위한
"지연 로드" 기능도 구현되어 있다고 하니 참고하세요!
# Text Splitting
검색의 핵심은 문서에서 관련성이 높은 데이터만 가져오는 것이죠.
그러기 위해 준비해야 할 과정이 있는데요.
그 중 하나가 "텍스트 분할"이에요.
예를 들어 100 페이지 이상 작성된 논문 PDF에서 특정 단어나 문장을 찾는다고 가정해볼게요.
정보를 찾기위해 논문을 처음부터 끝까지 읽어야 한다면 굉장히 비효율적이겠죠?
먼저 목차를 보고 어떤 단원에서 정보를 찾아야 하는지 확인할거에요.
그리고 단원 안에 있는 하위 목차 중 어디서 찾아야 할지 확인하고 ,
그 하위에 있는 문단 제목을 보며 정보를 찾아가겠죠.
이렇게 찾아야할 범위를 분할해서 순차적으로 찾아가다보면
원하는 데이터를 얻을 수 있어요.
랭체인도 마찬가지에요!
Text Splitters가 바로 이런 역할을 담당해줘요.
랭체인은 여러 개의 Document Transformers를 가지고 있는데
이를 통해 문서를 분할, 결합, 필터링할 수 있어요.
예를 들어 긴 문장이 작성된 문서를 다룰 때 Document Transformers가 문장을 작은 단위로
나누죠.
랭체인에서는 분할된 텍스트의 단위를 청크라고 하고, 이 과정을 청크화라고 합니다.
그리고 랭체인은 Text Splitters를 통해 이 작업을 수행하기 위해 여러 변환 알고리즘과
특정 문서 유형에 최적화된 로직을 제공해줘요.
고수준에서 TextSplitters는 다음과 같이 동작해요.
- 텍스트를 작고 의미있는 덩어리로 나눕니다.
나눠진 덩어리는 단어 단위일 수도 있고, 문장 단위일 수도 있어요! - 그리고 특정 크기가 될 때까지 작은 덩어리들을 결합하여 큰 덩어리로 만듭니다.
특정 크기에 도달했는지는 랭체인에서 제공하는 함수로 측정해요. - 특정 크기에 도달하면 덩어리를 별도의 텍스트로 만들어요.
이 때 덩어리 간 맥락을 유지하기 위해 텍스트가 겹치는 부분이 발생하도록 만들죠!
Text Splitters를 사용자 정의(Customize)하기 위해서는 두 가지를 고려하면 되요.
- 텍스트가 어떻게 분할되는지
- 청크 크기가 어떻게 측정되는지
이 외에 Text Splitters의 타입이나 Text Splitters에 대한 평가 등 다양한 내용이 있지만
이 부분은 공식 문서를 통해 확인해보세요!
# Text Embedding Models
검색의 또 다른 핵심은 문서에 대한 임베딩을 만드는 거에요.
임베딩이 뭐냐고요?
입력된 텍스트의 의미를 파악하고,
문서(HTML, PDF, 코드 등) 또는 데이터 안에 있는 텍스트 중 가장 의미가 유사한 것을
빠르고 효율적으로 찾는 기능이에요.
랭체인은 오픈 소스에서 독점 API에 이르기까지 25개 이상의 다양한 임베딩 공급자와
통합할 수 있도록 표준 인터페이스를 제공해요.
인터페이스 덕분에 모델 간의 전환이 쉬워졌어요.
그리고 필요에 따라 적절한 임베딩을 선택하여 사용할 수 있게 되었죠.
랭체인은 Embeddings Class를 통해 텍스트 임베딩을 하는데요.
Embeddings Class는 텍스트 임베딩 모델과 인터페이싱하도록 설계된 클래스에요.
이 클래스를 통해 OpenAI, Cohere, Hugging Face 등에서 제공하는 다양한 임베딩 모델을
활용할 수 있어요.
Embeddings Class는 두 가지 Method를 제공해요.
하나는 여러 텍스트를 입력으로 받아서 문서를 임베딩하는 Method이고,
다른 하나는 단일 텍스트를 입력으로 받아 쿼리를 임베딩하는 Method에요.
일부 임베딩 제공자가 문서(검색 대상)와 쿼리(검색 쿼리 자체)에 대해 서로 다른 임베딩 Method를
사용하기 때문에 Embeddings Class도 두 가지 Method로 나눠서 구현했다고 해요.
그럼 공식 문서에 작성된 소스코드(OpenAI 기준)를 보며 어떻게 임베딩이 되는지 알아볼게요.
> pip install langchain-openai
먼저 OpenAI의 임베딩 모델을 사용하기 위해 langchain-openai 패키지를 설치해줍니다.
1 from langchain_openai import OpenAIEmbeddings
2
3 embeddings_model = OpenAIEmbeddings(api_key="...")
4 # embeddings_model = OpenAIEmbeddings()
그리고 OpenAIEmbeddings에 OpenAI의 키를 매개변수로 전달해줘요.
참고로 4번 줄에 작성된 것처럼 OpenAIEmbeddings 함수는 매개변수 없이 만들수도 있어요.
5 embeddings = embeddings_model.embed_documents(
6 [
7 "Hi there!",
8 "Oh, hello!",
9 "What's your name?",
10 "My friends call me World",
11 "Hello World!"
12 ]
13 )
14 len(embeddings), len(embeddings[0])
# 결과 : (5, 1536)
먼저 여러 텍스트를 입력으로 받아서 임베딩하는 방법이에요.
embed_documents Method를 통해 여러 텍스트를 입력 값으로 전달하죠.
그러면 임베딩된 입력이 5개이고, 첫 번째 임베딩된 데이터의 길이가 1536인 것을 확인할 수 있어요.
이번에는 단일 텍스트를 임베딩하는 방법을 살펴볼게요.
15 embedded_query = embeddings_model.embed_query("What was the name mentioned in the conversation?")
16 embedded_query[:5]
# 결과
[
0.0053587136790156364,
-0.0004999046213924885,
0.038883671164512634,
-0.003001077566295862,
-0.00900818221271038
]
embed_query에 텍스트 문장을 매개변수로 전달만 하면 돼요.
임베딩이 되면 '결과'와 같은 데이터가 만들어진 것을 볼 수 있어요.
출력된 결과 값은 임베딩 벡터 값이라고도 하는데요.
이 값을 통해 유사도를 검증할 수 있어요.
아! 참고로 여러 텍스트를 입력으로 받는 임베딩도 이런식으로 벡터 값을 가지게 된답니다!
# Vector Stores
텍스트 임베딩을 통해 만들어진 임베딩 값들은 어딘가에 잘 저장되어야 검색을 할 때 사용할 수 있어요.
즉, 임베딩을 저장할 수 있는 데이터베이스가 필요한데 이를 벡터 저장소라고 해요.
랭체인은 오픈 소스 로컬 저장소부터 클라우드 호스팅 독점 저장소까지
50개 이상의 다양한 벡터 저장소를 활용할 수 있도록 표준 인터페이스를 제공해요.
이 인터페이스를 통해 벡터 저장소에서 원하는 결과를 검색할 수 있죠.
이미지가 의미하는 과정을 한 번 살펴볼까요?
- 소스 데이터를 읽어서 임베딩을 한 다음 Vector Stores 인터페이스를 통해 벡터 저장소에
임베딩 값을 저장해요. - 그리고 사용자가 검색을 하면 "입력된 질문"을 임베딩한 후 Vector Stores 인터페이스를
통해 벡터 저장소에서 검색을 해요. - 벡터 스토어에 저장된 데이터 중 가장 유사한 임베딩 벡터(값)를 찾아 결과로 반환해준답니다.
그럼 소스코드로 어떻게 구현하는지 알아볼까요?
1 import os
2 import getpass
3
4 os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key:')
OpenAIEmbeddings를 사용하기 위해 OpenAI의 API 키가 필요하니 OS의 환경변수로 등록할게요.
* 텍스트 임베딩 파트처럼 API 키를 직접 입력해도 상관 없습니다.
5 from langchain_community.document_loaders import TextLoader
6 from langchain_openai import OpenAIEmbeddings
7 from langchain_text_splitters import CharacterTextSplitter
8 from langchain_chroma import Chroma
9
10 # Load the document, split it into chunks, embed each chunk and load it into the vector store.
11 raw_documents = TextLoader('../../../state_of_the_union.txt').load()
12 text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
13 documents = text_splitter.split_documents(raw_documents)
14 db = Chroma.from_documents(documents, OpenAIEmbeddings())
가장 먼저 TextLoader를 통해 임베딩할 데이터를 읽어와요.
그리고 CharacterTextSplitter를 통해 텍스트를 작은 청크 단위로 분할하죠.
마지막으로 분할된 데이터와 OpenAIEmbeddings 함수를
Chroma에게 전달하여 벡터 저장소를 생성하고 저장합니다.
여기까지가 벡터 저장소에 임베딩된 값을 저장하는 과정이에요.
이제 검색을 해봐야겠죠?
15 query = "What did the president say about Ketanji Brown Jackson"
16 docs = db.similarity_search(query)
17 print(docs[0].page_content)
# 결과
Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections.
Tonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.
One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.
15번 째 줄, query 변수에 작성된 내용은 사용자가 입력한 질문이네요
Chroma를 통해 생성한 벡터 저장소(db)에 이 질문을 전달해요!
그럼 Chroma가 벡터 저장소에 저장된 임베딩 값 중 질문과 가장 관련성이 높은(유사도가 높은)
데이터를 찾아 반환해줍니다!
18 embedding_vector = OpenAIEmbeddings().embed_query(query)
19 docs = db.similarity_search_by_vector(embedding_vector)
20 print(docs[0].page_content)
앞에서는 similarity_search 함수에 텍스트로 된 질문을 전달해서 검색했죠?
임베딩된 벡터 값을 통해서 검색할 수도 있어요.
바로 similarity_search_by_vector 함수를 이용하면 되죠!
사용자가 입력한 질문을 OpenAIEmbeddings 함수를 통해 임베딩된 벡터 값으로 변환해요.
그리고 벡터 값을 전달하면 Chroma가 저장소에서 검색을 하죠!
* 참고로 질문의 내용이 같아 검색 결과도 동일하답니다.
이 외에도 Vector Stores는 비동기적 방식으로 검색 기능을 사용할 수 있어요.
자세한 내용은 공식 문서를 참고해보세요.
# Retrivers
데이터를 데이터베이스에 저장했다면 이제 활용해야겠죠?
랭체인에서 지원하는 다양한 검색 알고리즘을 통해 벡터 저장소에서 데이터를 검색할 수 있어요.
그리고 검색 성능을 높이기 위해 몇 가지 알고리즘들(Collection)을 포함하고 있는데요.
- Parent Document Retriever
- 부모 문서당 여러 개의 임베딩을 만들 수 있어, 더 작은 단위를 조회하면서도 더 큰 컨텍스트를 반환할 수 있습니다.
- Self Query Retriever
- 사용자 질문에는 종종 의미론적인 것이 아니라 메타데이터 필터로 가장 잘 표현될 수 있는 어떤 논리를 표현하는 것에 대한 참조가 포함됩니다.
- 셀프 쿼리를 사용하면 쿼리에 있는 다른 메타데이터 필터에서 쿼리의 의미론적 부분을 구문 분석할 수 있습니다.
- Ensemble Retriever
- 때로는 여러 다른 소스에서 문서를 검색하거나 여러 다른 알고리즘을 사용하고 싶을 수 있습니다.
- 앙상블 검색을 사용하면 이를 쉽게 수행할 수 있습니다.
- etc.
Retrievers는 구조화되지 않은 쿼리가 입력되면 이를 통해 문서를 검색하여 반환하는 인터페이스에요.
Vector Stores보다 더 자주 사용되죠.
Retrievers는 문서를 저장할 필요 없이 검색 결과를 반환만 하면 되거든요.
물론 Vector Stores를 백본으로 사용할 수도 있어요.
즉!
Retrievers는 문자열 쿼리를 입력으로 받다 문서 목록을 검색하여 반환해주는 컴포넌트라는 것만
명심하면 되요!
고급 검색 유형이나 Third Party 통합, 그리고 LCEL에서 Retrievers를 사용하는 방법도 있지만
이번 포스팅에서는 다루지 않을거에요.
궁금한 분은 공식 문서를 참고해주세요.
# Indexing
랭체인 Indexing API를 사용하면 벡터 스토어에서 문서를 읽고 동기화할 때 다음 기능을 지원받을 수 있어요.
- 벡터 저장소에 중복된 콘텐츠를 저장하지 않아요.
- 변경되지 않은 콘텐츠를 다시 작성하지 않아요.
- 변경되지 않은 콘텐츠에 대한 임베딩을 다시 계산하지 않아요.
데이터의 동기화와 일관성에 대한 지원을 해주네요.
Indexing의 가장 중요한 점은 API가 여러 변환 단계(ex. 텍스트 청킹)를 거친 문서에서도 작동한다는 점이에요.
그리고 Indexing을 사용하면 검색 시간과 비용을 절약하고,
벡터 검색 결과도 개선해준다고 하니 잘 활용하도록 해요!
# 마무리
오늘은 랭체인의 중요 컴포넌트 중 하나인 Retrieval에 대해 알아봤어요.
랭체인 공식 문서(Documents)를 보며 학습을 해봤는데요.
잘 이해가 되시나요?
최대한 공식 문서를 이해하고 제 방식으로 설명하긴 했는데
분명 설명이 부족한 부분도 있을거에요.
하지만 Retrieval(검색) 기능이 어떻게 동작하는지
전체적인 흐름은 충분히 이해하셨으리라 생각해요~
개념 정리가 끝나면 랭체인을 직접 구현해볼거에요.
그 때 포스팅을 하면서 더 자세히 개념을 정리해볼게요.
글을 읽으면서 잘못된 점이 있으면 언제든 지적해주세요.
더욱 더 공부를 하고 수정을 해나갈게요.
그리고 예제 소스코드는 랭체인 공식 문서에서 가져왔으니,
참고만 해주세요!
# 참고
- LangChain Docs > Components : https://python.langchain.com/v0.1/docs/modules/
How-to guides | 🦜️🔗 LangChain
Here you’ll find answers to “How do I….?” types of questions.
python.langchain.com
- LangChain Docs > Retrieval : https://python.langchain.com/v0.1/docs/modules/data_connection/
Build a Retrieval Augmented Generation (RAG) App: Part 1 | 🦜️🔗 LangChain
One of the most powerful applications enabled by LLMs is sophisticated question-answering (Q&A) chatbots. These are applications that can answer questions about specific source information. These applications use a technique known as Retrieval Augmented Ge
python.langchain.com
'AI' 카테고리의 다른 글
랭체인(LangChain)의 컴포넌트(Components) : Model I/O (0) | 2025.02.05 |
---|---|
랭체인(LangChain)이란? (0) | 2025.01.21 |
OpenAI와 허깅페이스(Hugging face)로 랭체인(LangChain) 구현하기 - 라이브러리 설치, 가상환경에 API 키 등록, 간단한 프롬프트 구현 (0) | 2025.01.14 |
허깅페이스(Hugging Face) 토큰(Token) 생성하기 (0) | 2025.01.07 |
허깅페이스(Hugging Face) 가입하기 (1) | 2025.01.05 |