AI - LangChain(RAG) 학습내용 (4)

KyungminLee·2024년 5월 9일

Chatgpt

목록 보기
1/4
post-thumbnail

1. RAG

Introduction

"RAG"는 "Retrieval-Augmented Generation"의 약자로, "검색-증강 생성"이라는 의미를 가지고 있습니다. 이는 자연어 처리(NLP) 및 기계 학습 분야, 특히 챗봇이나 질문-응답 시스템과 같은 고급 언어 모델을 구축하는 데 사용되는 기술입니다.

RAG에 대한 간략한 개요는 다음과 같습니다:

  1. 검색과 생성의 결합: RAG는 NLP의 두 가지 주요 구성 요소인 정보 검색과 응답 생성을 결합합니다. 검색 부분은 관련 정보를 찾기 위해 대규모 데이터베이스나 문서 컬렉션을 검색하는 과정을 포함합니다. 생성 부분은 검색된 정보를 바탕으로 일관되고 맥락적으로 적절한 텍스트를 생성하는 과정입니다.

  2. 작동 방식: RAG 시스템에서 질문이나 프롬프트가 주어지면 모델은 먼저 질문에 대한 답변을 제공하는 데 유용한 정보를 포함할 수 있는 관련 문서나 텍스트를 검색합니다. 그런 다음 이 검색된 정보를 생성 모델에 공급하여 일관된 응답을 합성합니다.

  3. 장점: RAG의 주요 장점은 모델이 외부 지식을 활용할 수 있게 하여 보다 정확하고 상세하며 맥락적으로 관련된 답변을 제공할 수 있다는 것입니다. 이는 특정 지식이나 사실적 정보가 필요한 질문에 특히 유용합니다.

  4. 응용 분야: RAG는 챗봇, 질문-응답 시스템 및 정확하고 맥락적으로 관련된 정보를 제공하는 것이 중요한 다른 AI 도구와 같은 다양한 응용 분야에 사용됩니다. 특히 모델이 다양한 주제와 데이터를 기반으로 이해하고 응답을 생성해야 하는 상황에서 유용합니다.

  5. 개발 및 사용: AI 및 기계 학습 커뮤니티에서 RAG는 다양한 연구 논문과 구현이 개발되고 있으며 주요 초점 중 하나입니다. 이는 학습된 정보뿐만 아니라 외부 소스에서 새롭고 관련된 정보를 통합하여 응답의 질과 관련성을 향상시키는 더 정교한 AI 시스템으로 나아가는 단계를 나타냅니다.

2. Retrieval

Data Loaders and Splitters

많은 LLM 애플리케이션에는 모델 학습 세트에 포함되지 않은 사용자별 데이터가 필요합니다. 이를 달성하는 주요 방법은 검색 증강 생성(RAG)을 사용하는 것입니다. 이 프로세스에서는 생성 단계를 수행할 때 외부 데이터를 검색한 다음 LLM에 전달합니다.

Document loaders

Document loaders 문서 로더는 다양한 소스에서 문서를 로드합니다.

Text Splitting

Text Splitting 검색의 핵심은 문서에서 관련 부분만 가져오는 것입니다. 여기에는 검색을 위해 문서를 준비하기 위한 몇 가지 변환 단계가 포함됩니다. 여기서 가장 중요한 것 중 하나는 큰 문서를 작은 덩어리로 분할(또는 청크)하는 것입니다. LangChain은 이를 위한 몇 가지 변환 알고리즘과 특정 문서 유형(코드, 마크다운 등)에 최적화된 로직을 제공합니다. 방시 - https://chunkviz.up.railway.app/

Text embedding models (Vectors) - LLM 비용 발생

검색의 또 다른 핵심 부분은 문서에 대한 임베딩을 만드는 것입니다. 임베딩은 텍스트의 의미를 포착하여 유사한 텍스트의 다른 조각을 빠르고 효율적으로 찾을 수 있도록 합니다. (2차원에서 1000차원까지 열 수 있는 벡터 상에서 유사한 단어를 찾는 것).

Another key part of retrieval is creating embeddings for documents. Embeddings capture the semantic meaning of the text, allowing you to quickly and efficiently find other pieces of a text that are similar

Vector stores

벡터 저장소는 임베드된 데이터를 저장하고 벡터 검색을 대신 수행합니다. 임베드된 데이터를 캐싱하여 임베딩 비용을 감소시킬 수 있습니다.

A vector store takes care of storing embedded data and performing vector search for you.

Retrievers

리트리버는 비정형 쿼리가 주어졌을 때 문서를 반환하는 인터페이스입니다. 리트리버는 문서를 저장할 필요 없이 단지 반환(또는 검색)만 할 수 있습니다. 벡터 저장소는 리트리버의 대표적인 저장소로 사용되지만 다른 유형의 리트리버도 있습니다.

A retriever is an interface that returns documents given an unstructured query. A retriever does not need to be able to store documents, only to return (or retrieve) them. Vector stores can be used as the backbone of a retriever, but there are other types of retrievers as well.

⭐️ 1~6의 순서 RAG 작동 원리⭐️

from langchain.chat_models import ChatOpenAI
from langchain.document_loaders import UnstructuredFileLoader # Load files using Unstructured
from langchain.text_splitter import RecursiveCharacterTextSplitter # Splitting text by recursively look at characters.
from langchain.embeddings import OpenAIEmbeddings, CacheBackedEmbeddings # OpenAI embedding models & caching results from embedding models
from langchain.storage import LocalFileStore # BaseStore interface that works on the local file system.
from langchain.vectorstores.faiss import FAISS # ChromaDB vector store

loader = UnstructuredFileLoader('./files/chapter_one.txt')  # 1. Document loaders
splitter = RecursiveCharacterTextSplitter() # 2. Text Splitting
result_docs = loader.load_and_split(text_splitter=splitter) # 1+2 results

embeddings_model = OpenAIEmbeddings() # 3. Text embedding models
cache_directory = LocalFileStore('./.cache/') # 4. Vector stores or Local Storages (Caching)

cached_embeddings = CacheBackedEmbeddings.from_bytes_store(embeddings_model, cache_directory)  # 4. Local Vector Storages (Caching)
vector_storage = FAISS.from_documents(result_docs, cached_embeddings) # 4. Vector stores (first time - LLM charging invoked)

results = vector_storage.similarity_search('where does winston live?') # 5. Retrievers

results # 6. Documents return

캐싱된 벡터 내용 (cached in local vector storage)

3. RetrievalQA (LLM Chain)

vector_storage에 저장되어있는 docs에 질의를 일으키는 LLM Chain. (off-the-shelf)

chain_type="refine" | “stuff” | “map_reduce” 사용자가 선택하여 사용 가능하며, LLM Chain을 사용하기 때문에 체인에 적혀있는 prompt를 사용자 정의할 수 없다. (자세한 내용은 랭스미스로 확인 가능)

https://python.langchain.com/docs/use_cases/summarization/#option-3.-refine

from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI

llm = ChatOpenAI()

vector_storage = FAISS.from_documents(result_docs, cached_embeddings) # 4. Vector stores (first time - LLM charging invoked)

# 5. Chain for question-answering against an index.
# 벡터화한 저장소에 검색하는 방법 정의 Using LLMChain (off - the - shelf chain version) 사용
chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="map_reduce",
    retriever = vector_storage.as_retriever()
)

chain.run('Describe Victory Mansions')

Stuff LCEL Chain # retriever 문서의 수가 적은 경우

기존의 LLMChain chain_type=”stuff” 기능을 LCEL Chain으로 변경한 것

from langchain.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnablePassthrough

# Custom Stuff LCEL Chain
retriver = vector_storage.as_retriever()
prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "You are a helpful assistant. Answer questions using only the following context. If you don't know the answer just say you don't know, don't make it up:\n\n{context}",
        ),
        ("human", "{question}"),
    ]
)

myStuffChain = {"context":retriver, "question": RunnablePassthrough()} | prompt | llm
myStuffChain.invoke('Describe Victory Mansions')

Map Reduce LCEL Chain # retriever 문서의 수가 많은 경우

기존의 LLMChain chain_type=”map_reduce” 기능을 LCEL Chain으로 변경한 것

from langchain.schema.runnable import RunnablePassthrough, RunnableLambda # Adding values to chain state

# Map Reduce LCEL Chain
retriever = vector_storage.as_retriever()

# logic
# 1. list of docs (vector_storage or cached storages)
# 2. summarize themes in the group of docs -> for doc in list of docs | prompt | llm
# 3. extract a final summary from summary list -> for response in list of lls response | put them all together
# 4. final doc | prompt | llm

map_doc_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            """
            Use the following portion of a long document to see if any of the text is relevant to answer the question. Return any relevant text verbatim. If there is no relevant text, return : ''
            -------
            {context}
            """,
        ),
        ("human", "{question}"),
    ]
)

map_doc_chain = map_doc_prompt | llm # 각 문서에 대해 요약하는 체인

def map_docs(inputs):
    documents = inputs["documents"]
    question = inputs["question"]
    results = []
    for document in documents:
        result = map_doc_chain.invoke(
            {"context": document.page_content, "question": question}
        ).content
        results.append(result)
    results = "\n\n".join(results) # 질문에 대한 각 문서의 내용을 붙여서 하나의 string으로 반환
    return results


# 하나에 대해 뱉어야 함
map_chain = {
    "documents" : retriever, # 문서 여러개
    "question": RunnablePassthrough(),
} | RunnableLambda(map_docs) # 하나로 반환하는 메서드

final_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            """
            Given the following extracted parts of a long document and a question, create a final answer.
            If you don't know the answer, just say that you don't know. Don't try to make up an answer.
            ------
            {context}
            """,
        ),
        ("human", "{question}"),
    ]
)

map_reduce_chain = {"context":map_chain, "question": RunnablePassthrough()} | final_prompt | llm # 모든 문서에 대해 요약하는 체인
map_reduce_chain.invoke('How many ministries are mentioned')

참조

동일 질문에 대한 파인튜닝/RAG 응답 비교

profile
끊임없이 발전해가는 개발자.

0개의 댓글