일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- mifare
- lightsail
- 정치기 필기
- ML
- djangorestframework
- Jupyterlab
- aws
- 머신러닝
- pytorch
- Github
- ai_캠프
- pandas
- sLLM
- team_project
- EC2
- 파이썬
- seaborn
- 로컬 런타임
- Python
- 티스토리챌린지
- django
- ai캠프
- mysql
- 오블완
- chromeextention
- ollama
- finpilot
- conda
- ai 캠프
- streamlit
- Today
- Total
greatsangho의 이야기
캠프 64일차 - 3차 프로젝트(LLM, RAG를 이용한 내외부 데이터셋 기반 질의응답 시스템) 본문
캠프 64일차 - 3차 프로젝트(LLM, RAG를 이용한 내외부 데이터셋 기반 질의응답 시스템)
greatsangho 2024. 11. 26. 23:59데이터 주제는 사람들이 가입한 보험의 보장내용을 잘 모르거나, 보장 받고자 하는 내용에 대한 내용을 챗봇을 통해 서비스를 하는 상황을 생각하여 '보험 약관 기반 보장 내용 확인'이라는 아이디어를 제시하였다. 이에 한 기업을 선택하여 운전자 보험에서 어떠한 상황에 대해 보장을 받을 수 있는지 대답을 듣는 시스템을 만들고자 하였다.
약관 PDF를 from langchain_community.document_loaders import PDFPlumberLoader로 읽어왔고,
RAG를 이용하기 위한 벡터 데이터베이스로 Chroma를 사용하였다.
텍스트를 임베팅하기 위한 모델로는 한국어에 대해 성능이 좋다고 되어있는 모델인 ' jhgan/ko-sroberta-multitask'를 찾아 적용하였다.
path_ins = '/content/drive/MyDrive/datafile/운전자보험.pdf'
from langchain_community.document_loaders import PDFPlumberLoader
loader = PDFPlumberLoader(path_ins)
data = loader.load()
이와 같이 Document 타입으로 데이터를 불러온다.
데이터는 페이지 별로 구분되어 있으며 page_content에 페이지의 내용이 저장된다.
data[6].page_content
이런 경우 0부터 시작하므로 7번째 페이지의 내용을 볼 수 있다.
model_name = 'jhgan/ko-sroberta-multitask'
embedding_model = HuggingFaceEmbeddings(model_name=model_name)
vector_db = Chroma(persist_directory='path_ins', embedding_function=embedding_model)
for row in data:
if isinstance(row.page_content,str):
vector_db.add_texts(
texts=[row.page_content],
metadatas=[{'page': row.metadata['page']}],
ids=[f"page_{row.metadata['page']}"]
)
# 저장 - commit과 비슷
vector_db.persist()
Chromadb에 벡터로 텍스트를 변환하여 저장하며, 이때 임베딩 모델을 활용하여 변환한다. 벡터DB를 저장하기 위해 Chroma에 .persist()를 해준다.
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain.prompts import PromptTemplate
# 실시간 LLM 응답
llm = ChatOpenAI(
# model_name='gpt-3.5-turbo',
model_name='gpt-4o-mini',
streaming=True,
callbacks=[StreamingStdOutCallbackHandler()],
temperature=0.5 # Adjust temperature for response creativity
)
# vector_db에 대한 retriever 설정으로 서칭 가능하도록 함
retriever = vector_db.as_retriever(
search_type='mmr', # Maximal Marginal Relevance (MMR) for diverse results
search_kwargs={'k': 3, 'fetch_k': 10} # Retrieve top 3 results from 10 candidates
)
# RetrievalQA.from_llm으로 llm 옵션, retriever, soruce_document를 사용한다.
chain = RetrievalQA.from_llm(
llm=llm,
retriever=retriever,
return_source_documents=True # Include source documents in the response
)
# custom prompt template를 통해 보험 상품을 설명하도록 가이드한다.
custom_prompt = PromptTemplate(
input_variables=["context", "question"],
template="""
You are an expert in insurance claims. Based on the following context, provide clear and actionable advice in korean:
Context:
{context}
Question:
{question}
"""
)
# chain을 이용해 답변을 생성한다.
query = '자동차 접촉 사고 시 어떤 보장을 받을 수 있어?'
answer = chain(query)
print(answer)
이와 같이 해당 문서를 바탕으로 답변을 하는 것을 알 수 있다.
---------------------------------------------------------------------------------------------------
이와 같은 방법은 OpenAPI를 사용하여 구현하는 기능이다. 해당 기능은 쉽고 간편하며, 정확하고 가볍다는 장점이 있다.
하지만 직접 huggingface에 있는 언어모델을 활용하고 싶었기 때문에 한국어 학습이 된 llama3 모델을 가져와 RAG를 적용해 보았다.
PDF를 가져오는 과정은 동일하게 진행하고, 대신 Chromadb에 넣기 쉽도록 각 페이지 별로 구분되어 있는 리스트로 문서를 변환하였다.
pages = []
for row in data:
if isinstance(row.page_content, str):
pages.append(row.page_content)
pages[:5]
그 다음에는 chromadb.Client()를 설정, .get_or_create_collection()으로 db를 생성하였다.
문장을 변환하기 위한 모델로 동일한 모델을 사용하였다.
벡터 db에 저장할 때 pdf 특성 상 줄바꿈이 문장이 끝나지 않은 상태에서 일어난다는 점과, 입력 데이터가 크면 그 안에서 모델이 스스로 필요한 부분을 가져올 것이기 때문에 위와 마찬가지로 페이지별로 벡터화를 진행하였다.
import chromadb
from sentence_transformers import SentenceTransformer
# Chroma 클라이언트 생성
client = chromadb.Client()
collection = client.get_or_create_collection(name="insurance_documents")
# SentenceTransformer 모델 로드
model_name = 'jhgan/ko-sroberta-multitask' # 'sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2'
embedding_model = SentenceTransformer(model_name)
# 텍스트를 문장 단위로 분리하고 벡터화
sentences = pages # 줄바꿈 기준으로 분리
embeddings = embedding_model.encode(sentences)
# Chroma에 문서 추가
collection.add(
documents=sentences,
embeddings=embeddings.tolist(),
ids=[f"doc_{i}" for i in range(len(sentences))]
)
이제 llama3 모델을 사용해보자. 허깅페이스에서 찾은 것 중 가볍고 정확한 모델을 사용해보고자 해당 모델을 선정하였다. llama3모델을 그대로 가져오면 A100 GPU를 사용해도 램이 부족해 세션 다운이 일어나는 경우가 빈번하기 때문에 fp16으로 불러온다. 이렇게 하면 T4 GPU에서도 구동이 가능하다는 장점이 있다.
# Load model directly
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
# 모델 경로 (Hugging Face 허브 또는 로컬 경로)
model_name = "Bllossom/llama-3.2-Korean-Bllossom-3B"
# 토크나이저 로드
tokenizer = AutoTokenizer.from_pretrained(model_name)
# 모델 로드 (FP16 설정)
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.float16, # FP16 설정
device_map="auto" # GPU/CPU 자동 할당
)
# 모델 준비 완료
print("모델이 성공적으로 FP16으로 로드되었습니다!")
여기서 질문을 임베딩하여 가까운 답변을 가져와 이를 llama 모델에 넣어보았다.
def query_with_rag(question, collection):
# 질문 임베딩 생성
question_embedding = embedding_model.encode([question])
# Chroma에서 유사한 문서 검색
results = collection.query(
query_embeddings=question_embedding.tolist(),
n_results=5 # 상위 5개 결과 반환
)
# 검색된 문서 내용 결합
related_texts = "\n".join(results['documents'][0])
# LLaMA 3.2 입력 준비 (프롬프트 생성)
prompt = f"다음 정보를 바탕으로 질문에 요약하여 250자 안으로 답하세요:\n\n{related_texts}\n\n질문: {question}\n답변:"
# LLaMA 3.2로 답변 생성
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens=200)
# 답변 디코딩 및 반환
return tokenizer.decode(outputs[0], skip_special_tokens=True)
# 예시 질문 실행
question = "이 보험 상품의 보장 범위는 무엇인가요?"
answer = query_with_rag(question, collection)
output = answer.split('답변:')[-1]
print(f'답변 : {output}')
question = "접촉 사고 시 보장 사항"
answer = query_with_rag(question, collection)
output = answer.split('답변:')[-1]
print(f'답변 : {output}')
question = "점심 메뉴 알려줘"
answer = query_with_rag(question, collection)
output = answer.split('답변:')[-1]
print(f'답변 : {output}')
이와 같이 답변을 잘 해주는 것을 확인할 수 있다. GPT로 RAG를 구현한 경우 문서 기반이기 때문에 점심식사에 대한 답변을 못해주지만, 지금과 같이 구현하면 유사한 문서를 참고만 하기 때문에 유연하게 답변을 하게 된다. 물론 서비스를 위해서는 조건을 주어야 할 것이다.
'프로그래밍 > SK AI 캠프' 카테고리의 다른 글
캠프 67일차 - 한 페이지에 정리하는 Git CLI 사용법 (0) | 2024.11.29 |
---|---|
캠프 65일차 - RAG를 활용한 챗봇 만들기 프로젝트 완료 (2) | 2024.11.27 |
캠프 63일차 - 자연어-이미지 멀티모달(텍스트 기반 이미지 생성) (0) | 2024.11.25 |
다시 훑어보는 Pytorch 및 자연어 처리 기초 (0) | 2024.11.24 |
SK AI캠프 14주차 후기 (1) | 2024.11.23 |