요즘 LLM 모델을 사용하는 방법이 점차 간편해 지고 있어, 자체적으로 LLM을 구축해, 챗봇, 전문가 시스템 등을 본인 서버에서 제공하는 경우가 많아지고 있다. 이 글은 GPU있는 PC에서 직접 실행해 볼 수 있도록, 로컬 호스트 LLM(Large Language Model) 오픈소스 기반 PDF 지식 챗봇 서비스를 간단히 개발해 본다.
이를 위해, 기존 BIM pdf 파일을 검색해 학습하고, LLM에 증강 학습한 후, 이를 간단한 UI로 웹서비스 하는 과정을 간략히 보여주고, 구현한다.
이 글은 로컬 LLM의 편한 개발을 지원하는 OLLAMA, LLM 프롬프트 엔지니어링 프레임웍인 LangChain, 텍스트 임베딩 벡터 데이터베이스 Chroma, 손쉬운 web app 개발 지원 도구인 Streamlit을 사용한다. 이를 이용해, 간단한 BIM 전문 지식을 PDF로 학습한 챗봇을 간단히 개발한다.
LLM 에 관련된 깊은 내용은 아래 링크를 참고한다. 이 글은 여러 참고 자료를 이용해 작성된 것이다. 상세 내용은 레퍼런스를 참고바란다.
- 오픈소스 기반 LLM의 민주화, LLAMA-2 논문 분석 및 기술 요약하기
- ChatGPT와 같은 생성AI 서비스 개발을 위한 간단한 LLAMA-2 설치와 사용법
- 어텐션 기반 트랜스포머 딥러닝 모델 이해, 활용 사례 및 파치토치를 통한 간단한 사용방법 소개
- 트랜스포머 디코더 핵심 코드 구현을 통한 동작 메커니즘 이해하기
- 파이토치로 멀티모달 생성AI 모델, Stable Diffusion 아키텍처를 코딩, 구현
- Computer_vision_deeplearning: computer vision based on deep learning lecture materials, github
LLM은 빅테크 업체 간 경쟁이 심한 분야이다. 관련해, Gemma, MPT-7B과 같은 다른 LLM 모델들도 오픈소스로 공개되고 있어 선택지가 많아지고 있다. 이와 관련해서는 다음을 참고한다.
설치
설치를 위해서는 NVIDIA driver, CUDA, tensorflow, pytorch 등 기본 딥러닝 개발 환경이 설치되어 있어야 한다(최소 구동 GPU RAM 6GB).
설치 순서는 다음과 같다. 1. 기본 패키지를 설치한다. LLM 모델 기반 서비스 개발 지원 라이브러리 LangChain, 앱 App UI 개발 지원 streamlit.io, 텍스트 임베딩 벡터 데이터베이스 Chroma DB 등을 설치한다.
pip install langchain streamlit streamlit_chat pypdf fastembed chardet
pip install chromadb==0.4.15
혹은 pip와 유사한 패키지 설치 관리자인 poetry 설치 후, 다음 사용 패키지들을 pyproject.toml 이름으로 저장한 후, 설치한다.
[tool.poetry]
name = "Local LLM"
version = "0.1.0"
description = ""
readme = "README.md"
[tool.poetry.dependencies]
python = ">=3.9,<3.9.7 || >3.9.7,<3.12"
pypdf = "^3.17.1"
streamlit = "^1.29.0"
streamlit-chat = "^0.1.1"
langchain = "^0.0.343"
fastembed = "^0.1.1"
openai = "^1.3.6"
langchainhub = "^0.1.14"
chromadb = "^0.4.18"
watchdog = "^3.0.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
poetry 설치는 다음과 같다.
poetry install
2. Ollama 웹사이트를 방문해, 로컬 LLM을 지원하는 OLLAMA를 설치한다.
ollama run llama2
3. 다음 Ollama 명령을 터미널에서 실행하여, 많이 사용되는 몇몇 LLM 학습 모델을 다운로드 한다. 상세한 모델 종류는 여기를 참고한다.
ollama pull mistral
ollama pull dolphin-phi
ollama pull llama2
다음 명령으로 다운로드된 모델 이미지를 확인한다.
PDF 기반 전문가 서비스 구축을 위한 RAG 파이프라인 구축
RAG(Retrieval-augmented generation)는 학습된 LLM 모델의 외부 데이터 소스를 활용하여, 검색 모델을 증강하는 기법이다. 이를 통해, 질문이 입력되면, QAChain을 통해, 벡터 스토리지에 해당 PDF 내용 임베딩이 있는 지 확인하고, 이 임베딩 컨텐츠를 LLAMA 서버에 전달하여, 적절한 답변을 얻는다.
RAG는 LLM의 다음 문제를 해결한다.
- 편향성: 훈련 데이터에만 편향된 답변 생성
- 잘못된 정보 생성: 잘못되거나 유해한 정보 생성
- 할루시네이션(Hallucination) 현상: 사실이 아닌 정보를 생성하는 문제
- 비전문성 정보 생성: 학습되지 않은 질문에 대한 답변을 전문가처럼 생성하는 문제
이를 위해, 전문지식이 포함된 PDF파일들을 입력하면, 이를 문서 청크로 분할해, 벡터 저장소에 청크 파일을 임베딩하여 데이터베이스에 저장한다.
다음은 그 구조를 보여준다.
from langchain.vectorstores import Chroma
from langchain.chat_models import ChatOllama
from langchain.embeddings import FastEmbedEmbeddings
from langchain.schema.output_parser import StrOutputParser
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.schema.runnable import RunnablePassthrough
from langchain.prompts import PromptTemplate
from langchain.vectorstores.utils import filter_complex_metadata
class ChatPDF:
vector_store = None
retriever = None
chain = None
def __init__(self):
self.model = ChatOllama(model="mistral") # OLLAMA의 mistral 모델 이용
self.text_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=100) # PDF 텍스트 분할
self.prompt = PromptTemplate.from_template(
"""
<s> [INST] You are an assistant for question-answering tasks. Use the following pieces of retrieved context
to answer the question. If you don't know the answer, just say that you don't know. Use three sentences
maximum and keep the answer concise. [/INST] </s>
[INST] Question: {question}
Context: {context}
Answer: [/INST]
"""
)
def ingest(self, pdf_file_path: str):
docs = PyPDFLoader(file_path=pdf_file_path).load() # 랭체인의 PDF 모듈 이용해 문서 로딩
chunks = self.text_splitter.split_documents(docs) # 문서를 청크로 분할
chunks = filter_complex_metadata(chunks)
vector_store = Chroma.from_documents(documents=chunks, embedding=FastEmbedEmbeddings()) # 임메딩 벡터 저장소 생성 및 청크 설정
self.retriever = vector_store.as_retriever(search_type="similarity_score_threshold",
search_kwargs={
"k": 3,
"score_threshold": 0.5,
},
) # 유사도 스코어 기반 벡터 검색 설정
self.chain = ({"context": self.retriever, "question": RunnablePassthrough()} | self.prompt | self.model | StrOutputParser()) # 프롬프트 입력에 대한 모델 실행, 출력 파서 방법 설정
def ask(self, query: str): # 질문 프롬프트 입력 시 호출
if not self.chain:
return "Please, add a PDF document first."
return self.chain.invoke(query)
def clear(self): # 초기화
self.vector_store = None
self.retriever = None
self.chain = None
웹 기반 App 개발
다음과 같이 UI를 가진 앱을 app.py 파일명으로 코딩한다.
import os, tempfile
import streamlit as st
from streamlit_chat import message
from rag import ChatPDF
st.set_page_config(page_title="ChatPDF") # 앞서 정의한 PDF 임베딩 벡터 데이터베이스 RAG 모듈 임포트
def display_messages(): # 메시지 출력
st.subheader("Chat")
for i, (msg, is_user) in enumerate(st.session_state["messages"]):
message(msg, is_user=is_user, key=str(i))
st.session_state["thinking_spinner"] = st.empty()
def process_input(): # 챗 메시지 입력
if st.session_state["user_input"] and len(st.session_state["user_input"].strip()) > 0:
user_text = st.session_state["user_input"].strip()
with st.session_state["thinking_spinner"], st.spinner(f"Thinking"):
agent_text = st.session_state["assistant"].ask(user_text) # 사용자 입력에서 답변 획득
st.session_state["messages"].append((user_text, True))
st.session_state["messages"].append((agent_text, False))
def read_and_save_file(): # file_upader UI에서 PDF 선택 시 호출
st.session_state["assistant"].clear() # LLM 어시스턴스 초기화
st.session_state["messages"] = []
st.session_state["user_input"] = ""
for file in st.session_state["file_uploader"]:
with tempfile.NamedTemporaryFile(delete=False) as tf:
tf.write(file.getbuffer())
file_path = tf.name
with st.session_state["ingestion_spinner"], st.spinner(f"Ingesting {file.name}"):
st.session_state["assistant"].ingest(file_path) # PDF 파일을 어시스턴스에 전달
os.remove(file_path)
def page():
if len(st.session_state) == 0:
st.session_state["messages"] = []
st.session_state["assistant"] = ChatPDF() # PDF, 벡터 데이터베이스, LLM 모델 호출 역할하는 객체 설정
# UI 정의
st.header("Chat BIM") # 타이틀
st.subheader("Upload a document") # 서브헤더
st.file_uploader("Upload document", type=["pdf"],
key="file_uploader", on_change=read_and_save_file,
label_visibility="collapsed", accept_multiple_files=True,
) # 업로더
st.session_state["ingestion_spinner"] = st.empty()
display_messages() # 메시지 출력
st.text_input("Message", key="user_input", on_change=process_input) # 채팅 입력 버튼 생성
if __name__ == "__main__":
page()
LLM 기반 챗봇 실행하기
다음 명령을 실행한다.
streamlit run app.py
실행되면, BIM 관련 PDF 문서를 업로드한다. 여기서는 Development of a Conceptual Mapping Standard to Link Building and Geospatial Information 논문 PDF파일을 사용하였다.
관련 질문을 하면, 증강 학습된 BIM 내용에 근거해 잘 답변하는 것을 확인할 수 있다. 이로써, BIM 전문가가 대답하는 것 같은 챗봇을 개발해 보았다.
참고로, OLLAMA는 로컬에서 실행될 수 있도록, 경량화되어 다음과 같이 적은 GPU 메모리를 사용한다.
마무리
이 글을 통해, 기존 BIM pdf 파일을 검색해 학습하고, LLM에 증강 학습한 후, 이를 간단한 UI로 웹서비스 하는 과정을 간략히 보여주고, 구현해 보았다. 이를 통해, 어느 정도 수준?의 BIM전문가 챗봇 시스템을 구현하였다.
앞서 언급된 라이브러리를 이용하면, 수많은 PDF파일을 미리 임베딩 벡터 데이터베이스로 만들어 놓고, 좀 더 전문적인 질의 답변을 도출할 수 있다. 이외, LangChain등의 다양한 기능을 사용하면, 좀 더 특화된 서비스 개발이 가능하다.
참고: OLLAMA기반 Image to Text LMM(Large Multi-modal Model) 사용
OLLAMA가 설치되어 있다면, 다양한 멀티모달 LLM 모델을 다운로드하고 실행해 볼 수 있다.
예를 들어, 고양이 사진을 주어, 이를 설명하게 하는 Image to Text를 간단히 실행해 볼 수 있다. 이는 LMM 모델인 LLaVA를 다운로드해 프롬프트를 입력하면 된다.
앞의 사진을 cat.jpg로 저장한 후, 다음 명령을 터미널에서 실행해 본다.
ollama run llava "describe this iamge: ./cat.jpg"
"테이블 다리로 보이는 것 옆에 똑바로 앉아 있는 얼룩무늬 고양이의 이미지입니다. 고양이는 뚜렷한 어두운 줄무늬가 있는 밝은 주황색 털을 가지고 있는데, 이는 흔히 볼 수 있는 패턴입니다. 얼룩 고양이는 눈을 크게 뜨고 카메라를 정면으로 차분한 태도로 바라보고 있는 모습입니다.배경에는 바닥에 다음과 같은 패턴의 러그가 깔려 있습니다. 베이지색과 기타 중성색이 포함됩니다. 전경에 테이블 다리가 있기 때문에 다이닝 룸과 같은 생활 공간처럼 보입니다."
거의 일치한다.
참고: Error: Your app is having trouble loading the streamlit_chat.streamlit_chat component
Error: Your app is having trouble loading the streamlit_chat.streamlit_chat component 에러가 발생할 경우가 있다. 이 경우는 다음과 같이 패키지 설치 파일을 수정하고 재설치하면 해결 될 수 있다(참고-링크).
streamlit-chat = "0.0.2.2"
참고: 커스텀 데이터 학습 파인튜닝 방법
다음은 사용자 데이터 학습, 파인튜닝 방법이다.
참고: OLLAMA 메뉴얼
다음을 참고한다.
참고: LLAMA2 패키지 메뉴얼
- Llama2 (huggingface.co)
- Understanding the Llama2 Tokenizer: Working with the Tokenizer locally using Transformers | by Drowsy Dev | Medium
레퍼런스
- Ollama, Get up and running with large language models, locally.
- Chroma, AI-native open source embedding database
- Streamlit • A faster way to build and share data apps
- Streamlit git and web service on cloud(youtube.com)
- Introduction | 🦜️🔗 Langchain
- Build a Custom LLM with Chat With RTX | NVIDIA
- How to build a Llama 2 chatbot (streamlit.io)
- (Python) Streamlit + Local LLM. Yet-Another-Code-Example for… | by Stef Nestor | Medium
- Build your own RAG and run it locally: Langchain + Ollama + Streamlit | by Duy Huynh | Medium
- Fully Local RAG with Qdrant, Ollama, LangChain and LangServe | by Datadrifters | Medium
- Implementing RAG using Langchain Ollama and Chainlit on Windows using WSL | by Plaban Nayak | AI Planet
- Chat with Your Audio Locally: A Guide to RAG with Whisper, Ollama, and FAISS | by Ingrid Stevens | Medium
- ChatOllama | Ollama Based 100% Local RAG Application | by 01coder | Mar, 2024 | AI Advances (gopubby.com)
- LiteLLM, Llama2 Dream, KoAlpaca, CoLab LLAMA2
- How to Run Open-Source LLM Models Locally | CyberArk Engineering (medium.com)
- Build your own voice assistant and run it locally: Whisper + Ollama + Bark | by Duy Huynh | Mar, 2024 | Medium
- Harnessing AI at the Edge: Building a RAG System with Ollama, Qdrant and Raspberry Pi
- A Comparison of python libraries for PDF Data Extraction for text, images and tables | by Pradeep Bansal | Medium
- Extracting Data from PDFs | Challenges in RAG/LLM Applications (unstract.com)
댓글 없음:
댓글 쓰기