NLP/실습

LangChain RAG 실습 4(Llama3)

miimu 2025. 9. 4. 15:47

한국어를 어느 정도 잘 생성할 수 있는 Llama3 모델을 로드하기 위해 HuggingFace 모델을 사용하기로 했다.

 

참고한 사이트는 다음과 같다.

https://littlefoxdiary.tistory.com/128

 

Llama3 한국어 성능 테스트 | Colab에서 Meta-Llama-3 모델 사용해보기🦙

GPT-4에 비견할만한 성능을 가진다는 Meta의 오픈소스 LLM Llama3를 사용해보자! Llama 3 모델 Llama 3 모델 특징8B & 70B 파라미터 규모의 모델으로, MMLU, HumanEval  등 벤치마크 태스크에서 경쟁모델보다

littlefoxdiary.tistory.com

https://huggingface.co/learn/cookbook/ko/advanced_ko_rag

 

한국어 Advanced RAG 구현: Hugging Face와 LangChain 활용한 쿡북 - Hugging Face Open-Source AI Cookbook

 

huggingface.co

 

1. ChromaDB 준비하기

먼저 저번처럼, 크롤링 했던 뉴스 기사를 사용하기로 했다.

import os
import json

data_path = './drive/MyDrive/실습/RAG/data/'
json_paths = [data_path + json_file for json_file in os.listdir(data_path)]
from langchain_community.llms import Ollama
from langchain.chains import RetrievalQA
from langchain_chroma import Chroma
from sentence_transformers import SentenceTransformer
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain.docstore.document import Document
from langchain_community.document_loaders import JSONLoader

 

이번엔 다른 한국어 임베딩 모델을 사용하기로 했다.

# embedding 설정
embeddings = HuggingFaceEmbeddings(model_name="snunlp/KR-SBERT-V40K-klueNLI-augSTS")

 

 

ChromaDB 생성은 저번과 같다.

# ChromaDB Path
DB_PATH = "./drive/MyDrive/실습/RAG/db"

# Json 파일 Path
data_path = './drive/MyDrive/실습/RAG/data/'
json_paths = [data_path + json_file for json_file in os.listdir(data_path)]
# 뉴스 기사 데이터 ChromaDB에 저장


# Document 로드 후 DB에 저장
for i, json_path in enumerate(json_paths) :
    loader = JSONLoader(
        file_path=json_path,
        jq_schema=".[] | .link + \" \" + .pDate + \" \" + .title + \" \" + .description",
        text_content=False
    )

    docs = loader.load()

    if i == 0 :
        db = Chroma.from_documents(
            documents=docs,
            embedding=embeddings,
            collection_name="2025_news_huggingface_llama",
            persist_directory=DB_PATH
        )

        db.get()

    else :
        db.add_documents(
            documents=docs,
            embedding=embeddings,
            collection_name="2025_news_huggingface_llama",
            persist_directory=DB_PATH
        )
        db.get()

 

ChromaDB를 Retriever로 설정한다.

# Retriever
persist_db = Chroma(
    persist_directory=DB_PATH,
    embedding_function=embeddings,
    collection_name="2025_news_huggingface_llama"
)

retriever = persist_db.as_retriever()

 

테스트 예시

# test
retriever.invoke("롤 lck 경기 일정별 결과 알려줘.")

 

프롬프트 템플릿 설정

Llama3에 맞는 템플릿을 사용한다. 한국어로 답변을 생성하기 위한 문장을 따로 설정했다.

또한 참고한 문서의 url을 함께 출력하도록 템플릿 문구를 수정했다.

from langchain import PromptTemplate

template = """
    <|begin_of_text|>
    <|start_header_id|>system<|end_header_id|>
    당신은 QA(Question-Answering)을 수행하는 Assistant입니다. 다음의 Context를 이용하여 Question에 답변하세요.
    최소 3문장 최대 5문장으로 답변하세요.
    주어진 Context가 없다면 "정보가 부족하여 답변할 수 없습니다."를 출력하세요. 답변에 참고한 기사의 url을 함께 출력해주세요. 답변은 한국어로만 출력해주세요.
    <|eot_id|>
    <|start_header_id|>user<|end_header_id|>
    Context: {context}
    Question: {question}
    <|eot_id|>
    <|start_header_id|>assistant<|end_header_id|>
    Answer:
    """

prompt = PromptTemplate(input_variables=['context', 'question'], template=template)

prompt.pretty_print()

 

LangChain Model 설정

from langchain.schema.runnable import RunnablePassthrough
from langchain.schema.output_parser import StrOutputParser
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
from transformers import pipeline
import torch
# !pip install bitsandbytes
!pip install -U bitsandbytes==0.47.0

 

중간에 Meta의 Llama3 모델을 불러올 수 있는 HuggingFace Token값을 지정한다.

위의 참고 링크 블로그에서 Meta로부터 Access 받는 방법을 설명한다.

token = YOUR_TOKEN

 

모델 이름을 설정한다.

Colab 환경에서 작동하는 것을 목표로 하여 Instruct 모델을 사용한다.

def format_docs(docs) :
    print(docs)
    return "\n\n".join(doc.page_content for doc in docs)

model_name = "meta-llama/Meta-Llama-3-8B-Instruct"
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_use_double_quant=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.float16,
    llm_int8_enable_fp32_cpu_offload=True,
)

 

토크나이저와 LLM 모델을 설정한다.

tokenizer = AutoTokenizer.from_pretrained(model_name, use_auth_token=token)
LLM = pipeline(
    "text-generation",
    model=model_name,
    tokenizer=tokenizer,
    model_kwargs={
        "torch_dtype": torch.bfloat16,
        "quantization_config": bnb_config # Add quantization config here
        },
    device_map="auto",
    token=token,
    do_sample=True,
    temperature=0.2,
    repetition_penalty=1.1,
    max_new_tokens=500,
    return_full_text=False,
)
from langchain.llms import HuggingFacePipeline

llm = HuggingFacePipeline(pipeline=LLM)
rag_chain = {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser()

 

RAG 실행

question = "오늘은 2025년 6월 4일입니다. 최근 한화와 T1의 전적이 어떻게 되나요?"

result = rag_chain.invoke(question)
print(result)

 

위와 같은 코드로 질문에 대한 답변을 생성했다.

 

question1 = "오늘은 2025 6월 4일입니다. 최근 살인 사건과 관련한 뉴스는 무엇이 있나요?"

result = rag_chain.invoke(question1)
print(result)

 

잘렸지만 뒷 부분에 참고한 뉴스의 url이 첨부되어 있다.

그러나, 같은 내용을 여러 개의 뉴스 url으로 인해 다른 사건인 것처럼 나열하는 문제가 있었다.

 

 

마무리

일단 이번 실습은 RAG에 익숙해지는 것을 목표로 했다.

ChatGPT 뿐만 아니라 Llama3를 활용하여 한국어로 답변을 생성할 수 있도록 하는 것이 목표였다.

 

이 과정에서, Llama3의 문제점을 몇 개 발견했다.

첫 번째로, 위에서 언급한 것처럼 같은 내용의 뉴스 url으로 인해 다른 사건인 것처럼 나열하는 문제와

두 번째로, 결과로 남기지는 않았지만 영어로 답변을 출력하는 문제는 여전히 있었다.

 

다음에는 프롬프트와 RAG와 관련한 지식을 좀더 공부하여 위의 문제점을 해결하는 프로젝트를 연습해보고자 한다.