RAG (LangChain + Chroma + Llama3 8B)

 Ollama 사용법

ref. https://velog.io/@judy_choi/LLaMA3-%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-RAG-%EA%B5%AC%EC%B6%95-Ollama-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%A0%95%EB%A6%AC

명령어

설치

curl -fsSL https://ollama.com/install.sh | sh

모델 다운로드 (Pull)
https://ollama.com/
에서 Docker Hub 처럼 모델을 다운로드(Pull) 할 수 있다.

# ollama pull <model>
ollama pull llama3

모델 실행

# ollama run <model>
ollama run llama3

현재 로컬의 모델 조회

ollama list

HuggingFace 의 gguf 모델을 Ollama 로 실행하기

HuggingFace 에 업로드된 gguf 모델을 Ollama 를 이용해 로컬에서 돌려봅니다.

gguf?

  • GGUF(Georgi Gerganov Unified Format)
  • 오픈소스 모델을 로컬 환경에서 쉽고 빠르게 실행할 수 있는 파일 형식
  • GGML을 사용하여 대형 모델을 실행하는 프로그램과 모델을 저장하는 파일 형식
    • GGML : 컴퓨터에서도 큰 모델을 빠르게 돌릴 수 있는 ML용 라이브러리
  • 모델을 빠르고 쉽게 불러오고 저장할 수 있게 해주는 바이너리(0, 1) 형식으로 설계됨
  • 개발자들은 보통 PyTorch 같은 프로그래밍 도구를 사용해 모델을 만든 후, GGML에서 쓸 수 있도록 GGUF 형식으로 저장한다

1. gguf 모델 다운로드


# wget <허깅페이스 모델 url>
wget https://huggingface.co/teddylee777/Llama-3-Open-Ko-8B-gguf/resolve/main/Llama-3-Open-Ko-8B-Q8_0.gguf

2. GGUF 파일 경로에 Modelfile 파일 작성

FROM Llama-3-Open-Ko-8B-Q8_0.gguf

TEMPLATE """{{- if .System }}
<s>{{ .System }}</s>
{{- end }}
<s>Human:
{{ .Prompt }}</s>
<s>Assistant:
"""

SYSTEM """A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions."""

PARAMETER temperature 0
PARAMETER num_predict 3000
PARAMETER num_ctx 4096
PARAMETER stop <s>
PARAMETER stop </s>

3. 모델 생성(추가)

# ollama create <생성 및 실행할 모델명> -f Modelfile
ollama create llama3-ko -f Modelfile

이렇게 생성한 모델을 ollama run llama3-ko 와 같이 즉시 실행할 수도 있고
RAG 등과 함께 사용할 때 다음 코드 형태로 사용할 수도 있다.

from langchain_community.chat_models import ChatOllama

# Ollama 를 이용해 로컬에서 LLM 실행
model = ChatOllama(model="llama3-ko")

Llama3-KO 를 이용해 RAG 를 구현

RAG 에 사용할 PDF로 근로기준법을 다운로드하여 사용했습니다.
https://www.law.go.kr/법령/근로기준법

필요한 라이브러리 임포트

import os
import warnings
warnings.filterwarnings("ignore")

Text(PDF) Loader

from langchain_community.document_loaders import PyMuPDFLoader

# PyMuPDFLoader 을 이용해 PDF 파일 로드
loader = PyMuPDFLoader("labor_low.pdf")
pages = loader.load()

TextSplitter

from langchain.text_splitter import RecursiveCharacterTextSplitter

# 문서를 문장으로 분리
## 청크 크기 500, 각 청크의 50자씩 겹치도록 청크를 나눈다
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
)
docs = text_splitter.split_documents(pages)

Text Vector Embedding


from langchain.embeddings import HuggingFaceEmbeddings

# 문장을 임베딩으로 변환하고 벡터 저장소에 저장
embeddings = HuggingFaceEmbeddings(
    model_name='BAAI/bge-m3',
    #model_kwargs={'device':'cpu'},
    model_kwargs={'device':'cuda'},
    encode_kwargs={'normalize_embeddings':True},
)

VectorStore(Chroma)

# 벡터 저장소 생성
from langchain.vectorstores import Chroma
vectorstore = Chroma.from_documents(docs, embeddings)


# 벡터 저장소 경로 설정
## 현재 경로에 'vectorstore' 경로 생성
vectorstore_path = 'vectorstore'
os.makedirs(vectorstore_path, exist_ok=True)

# 벡터 저장소 생성 및 저장
vectorstore = Chroma.from_documents(docs, embeddings, persist_directory=vectorstore_path)
# 벡터스토어 데이터를 디스크에 저장
vectorstore.persist()
print("Vectorstore created and persisted")

Model

from langchain_community.chat_models import ChatOllama

# Ollama 를 이용해 로컬에서 LLM 실행
## llama3-ko-instruct 모델 다운로드는 Ollama 사용법 참조
model = ChatOllama(model="llama3-ko-instruct", temperature=0)

Retriever

retriever = vectorstore.as_retriever(search_kwargs={'k': 3})

LangChain

from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate


# Prompt 템플릿 생성
template = '''친절한 챗봇으로서 상대방의 요청에 최대한 자세하고 친절하게 답하자. 모든 대답은 한국어(Korean)으로 대답해줘.":
{context}

Question: {question}
'''

prompt = ChatPromptTemplate.from_template(template)

def format_docs(docs):
    return '\n\n'.join([d.page_content for d in docs])

# RAG Chain 연결
rag_chain = (
    {'context': retriever | format_docs, 'question': RunnablePassthrough()}
    | prompt
    | model
    | StrOutputParser()
)

# Chain 실행
query = "연장근로수당에 대해 알려 줘"
answer = rag_chain.invoke(query)

print("Query:", query)
print("Answer:", answer)

Output

  • teddylee777/Llama-3-Open-Ko-8B-gguf 모델

    • Answer: 연장근로수당은 근로기준법 제56조에 따라 통상임금의 50%를 가산하여 지급해야 합니다
  • teddylee777/Llama-3-Open-Ko-8B-Instruct-preview-gguf 모델

    • Answer: 연장근로수당은 주휴일에 지급됩니다.
      제71조(연장근로수당) ① 사용자는 근로시간을 초과하여 근로한 경우에는 그 초과된 시간에 대하여 연장근로수당을 지급하여야 한다. 다만, 다음 각 호의 어느 하나에 해당하는 경우에는 그러하지 아니하다.
      1. 「근로기준법」 제56조제2항에 따라 휴일근로를 한 경우
      2. 「근로기준법」 제57조제2항에 따라 야간근로를 한 경우
      3. 「근로기준법」 제58조제2항에 따라 휴일에 근로한 경우
      4. 「근로기준법」 제59조제2항에 따라 휴일근로 및 야간근로를 한 경우


chroma db import 방법 업데이트

from langchain_community.vectorstores.chroma import Chroma

Chroma.max_marginal_relevance_search() 와 Chroma.search() 의 차이점

Chroma.max_marginal_relevance_search()Chroma.search()`는 모두 Chroma 벡터 데이터베이스에서 문서를 검색하는 데 사용되는 함수이지만, 다음과 같은 주요 차이점이 있습니다.

1. 검색 방식:

  • Chroma.max_marginal_relevance_search():

    • 최대 여변 관련성(MMR) 검색 알고리즘을 사용하여 검색합니다.
    • 검색 쿼리와 유사하면서 서로 중복되지 않는 문서를 찾는 데 중점을 둡니다.
    • 쿼리와 유사한 상위 N개의 문서를 반환합니다.
  • Chroma.search():

    • 여백 검색(cosine similarity) 알고리즘을 사용하여 검색합니다.
    • 검색 쿼리와 가장 유사한 문서를 찾는 데 중점을 둡니다.
    • 쿼리와 가장 유사한 단일 문서를 반환합니다.

2. 반환 값:

  • Chroma.max_marginal_relevance_search():

    • 검색 결과 문서 리스트를 반환합니다. 각 문서는 id, embedding, score (관련성 점수) 속성을 포함하는 사전 또는 객체 형식입니다.
  • Chroma.search():

    • 검색 결과 문서를 반환합니다. 문서는 id, embedding, score (관련성 점수) 속성을 포함하는 사전 또는 객체 형식입니다.

3. 사용 예시:

Chroma.max_marginal_relevance_search():

Python
# Chroma 벡터 데이터베이스 생성 및 문서 추가 (이전 코드 생략) ...

# 검색 쿼리 설정
query = "검색 쿼리 내용"

# MMR 검색 수행
results = vectorstore.max_marginal_relevance_search(query, k=10)

# 검색 결과 출력
for result in results:
  print(f"문서 ID: {result['id']}")
  print(f"관련성 점수: {result['score']}")
  print(f"문서 내용: {result['page_content']}")
  print("----------------------")

Chroma.search():

Python
# Chroma 벡터 데이터베이스 생성 및 문서 추가 (이전 코드 생략) ...

# 검색 쿼리 설정
query = "검색 쿼리 내용"

# 여백 검색 수행
result = vectorstore.search(query)

# 검색 결과 출력
print(f"문서 ID: {result['id']}")
  print(f"유사도: {result['score']}")
  print(f"문서 내용: {result['page_content']}")
  print("----------------------")

4. 결론:

  • **Chroma.max_marginal_relevance_search()**는 쿼리와 유사하면서 서로 중복되지 않는 문서를 빠르게 찾는 데 적합합니다.
  • **Chroma.search()**는 쿼리와 가장 유사한 단일 문서를 찾는 데 사용됩니다.

Chroma.max_marginal_relevance_search() 와 Chroma.as_retriever() 의 차이점

Chroma.max_marginal_relevance_search()Chroma.as_retriever()`는 모두 Chroma 벡터 데이터베이스에서 문서를 검색하는 데 사용되는 함수이지만, 다음과 같은 주요 차이점이 있습니다.

1. 검색 방식:

  • Chroma.max_marginal_relevance_search():

    • 최대 여변 관련성(MMR) 검색 알고리즘을 사용하여 검색합니다.
    • 검색 쿼리와 유사하면서 서로 중복되지 않는 문서를 찾는 데 중점을 둡니다.
    • 쿼리와 유사한 상위 N개의 문서를 반환합니다.
  • Chroma.as_retriever():

    • 다양한 검색 알고리즘을 사용하여 검색할 수 있는 리트리버 객체를 반환합니다.
    • MMR 검색 외에도 여백 검색(cosine similarity), TF-IDF 검색 등을 사용할 수 있습니다.
    • 검색 쿼리와 일치하는 모든 문서를 반환합니다.

2. 반환 값:

  • Chroma.max_marginal_relevance_search():

    • 검색 결과 문서 리스트를 반환합니다. 각 문서는 id, embedding, score (관련성 점수) 속성을 포함하는 사전 또는 객체 형식입니다.
  • Chroma.as_retriever():

    • Retriever 객체를 반환합니다. 이 객체를 사용하여 다양한 검색 작업을 수행할 수 있습니다.

3. 사용 예시:

Chroma.max_marginal_relevance_search():

Python
# Chroma 벡터 데이터베이스 생성 및 문서 추가 (이전 코드 생략) ...

# 검색 쿼리 설정
query = "검색 쿼리 내용"

# MMR 검색 수행
results = vectorstore.max_marginal_relevance_search(query, k=10)

# 검색 결과 출력
for result in results:
  print(f"문서 ID: {result['id']}")
  print(f"관련성 점수: {result['score']}")
  print(f"문서 내용: {result['page_content']}")
  print("----------------------")

Chroma.as_retriever():

Python
# Chroma 벡터 데이터베이스 생성 및 문서 추가 (이전 코드 생략) ...

# 리트리버 객체 생성
retriever = vectorstore.as_retriever()

# 검색 쿼리 설정
query = "검색 쿼리 내용"

# 여백 검색 수행
results = retriever.cosine_similarity_search(query, k=10)

# 검색 결과 출력
for result in results:
  print(f"문서 ID: {result['id']}")
  print(f"유사도: {result['score']}")
  print(f"문서 내용: {result['page_content']}")
  print("----------------------")

4. 결론:

  • **Chroma.max_marginal_relevance_search()**는 쿼리와 유사하면서 서로 중복되지 않는 문서를 빠르게 찾는 데 적합합니다.
  • **Chroma.as_retriever()**는 다양한 검색 알고리즘을 사용하여 더 정교한 검색을 수행하고 싶을 때 사용합니다.

How to create a langchain doc from an str?


I've searched all over langchain documentation on their official website but I didn't find how to create a langchain doc from a str variable in python so I searched in their GitHub code and I found this :
  doc=Document(
                page_content="text",
                metadata={"source": "local"}
            )

PS: I added the metadata attribute
then I tried using that doc with my chain:
Memory and Chain:

memory = ConversationBufferMemory(memory_key="chat_history", input_key="human_input")
chain = load_qa_chain(
    llm, chain_type="stuff", memory=memory, prompt=prompt
)

the call method:

  chain({"input_documents": doc, "human_input": query})

prompt template:

template = """You are a senior financial analyst analyzing the below document and having a conversation with a human.
{context}
{chat_history}
Human: {human_input}
senior financial analyst:"""

prompt = PromptTemplate(
    input_variables=["chat_history", "human_input", "context"], template=template
)

but I am getting the following error:

AttributeError: 'tuple' object has no attribute 'page_content'

when I tried to check the type and the page content of the Document object before using it with the chain I got this

print(type(doc))
<class 'langchain.schema.Document'>
print(doc.page_content)
"text"

Chroma.from_documents와 Chroma.add_documents의 차이점

Chroma.from_documentsChroma.add_documents는 모두 Chroma 벡터 데이터베이스에 문서를 추가하는 데 사용되는 메서드이지만, 다음과 같은 주요 차이점이 있습니다.

1. 사용 목적:

  • Chroma.from_documents:

    • 기존 문서 리스트를 기반으로 Chroma 벡터 데이터베이스를 새로 생성합니다.
    • 각 문서에 대한 임베딩 벡터를 계산하고 데이터베이스에 저장합니다.
    • 데이터베이스가 없는 경우 또는 새로운 데이터 세트로 데이터베이스를 초기화할 때 사용됩니다.
  • Chroma.add_documents:

    • 기존 Chroma 벡터 데이터베이스에 새로운 문서를 추가합니다.
    • 문서의 임베딩 벡터는 외부에서 계산되어야 합니다.
    • 데이터베이스가 이미 생성된 경우 새로운 문서를 추가할 때 사용됩니다.

2. 입력 파라미터:

  • Chroma.from_documents:

    • documents: 문서 리스트. 각 문서는 텍스트 내용, 메타데이터 등을 포함하는 사전 또는 객체 형식이어야 합니다.
    • embedding: 임베딩 함수. 문서의 텍스트 내용을 임베딩 벡터로 변환하는 함수를 지정합니다.
    • kwargs: 추가적인 옵션을 지정하는 키워드 인수 사전 (선택적).
  • Chroma.add_documents:

    • documents: 문서 리스트. 각 문서는 id, embedding, metadata (선택적), tags (선택적) 속성을 포함하는 사전 또는 객체 형식이어야 합니다.
    • kwargs: 추가적인 옵션을 지정하는 키워드 인수 사전 (선택적).

3. 반환 값:

  • Chroma.from_documents:

    • Chroma 벡터 데이터베이스 객체를 반환합니다.
  • Chroma.add_documents:

    • 반환 값이 없습니다.

4. 사용 예시:

Chroma.from_documents:

Python
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings.openai import OpenAIEmbeddings

# 문서 로드
documents = load_documents("documents.txt")

# 임베딩 함수 생성
embeddings = OpenAIEmbeddings()

# Chroma 벡터 데이터베이스 생성
vectorstore = Chroma.from_documents(documents, embeddings)

# ... Chroma 벡터 데이터베이스 사용 ...

Chroma.add_documents:

Python
# ... Chroma 벡터 데이터베이스 생성 및 임베딩 계산 (이전 코드 생략) ...

# 새로운 문서 추가
new_documents = [
    {
        "id": "new_doc1",
        "embedding": [0.1, 0.2, 0.3],  # 외부에서 계산된 임베딩 벡터
        "metadata": {"title": "새로운 문서 제목"},
        "tags": ["새로운 태그1", "새로운 태그2"],
    },
    {
        # ... 다른 새로운 문서 ...
    },
]

vectorstore.add_documents(new_documents)

# ... Chroma 벡터 데이터베이스 사용 ...

  • ref
    • https://www.inflearn.com/questions/1193196/chroma-db-import-%EA%B0%80-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8%EB%90%9C-%EA%B2%83-%EA%B0%99%EC%8A%B5%EB%8B%88%EB%8B%A4
    • https://stackoverflow.com/questions/76551067/how-to-create-a-langchain-doc-from-an-str


댓글 쓰기

0 댓글