Unsloth에서 말뭉치(Corpus) 학습
○ base 모델: llama-3-8b-KrBllossom
- 로드 설정은 기본 Unsloth FastLanguageModel.from_pretrained()의 예제코드 값 사용 (4bit 양자화 포함)
○ peft 설정: r값을 16이 아닌 8로 설정하여 LoRA 개입을 줄이고 dropout값을 0.1로 지정하여 과적합을 방지
○ 데이터: /home/data/의 하위 디렉토리를 전부 포함한 모든 xlsx파일의 “sentence”컬럼
→ “sentecne”컬럼 외에 더 학습해야하는 컬럼이 있을 수도 있으나 편의상 해당 컬럼만 선택
→ 모든 xlsx파일의 sentence컬럼을 한 파일의 한 컬럼으로 합쳐서 저장 (109955개 데이터)
파일: /home/data/data-00000-of-00001.arrow
○ 텍스트 전처리: 단순하게 각 문장 뒷부분마다 EOS_TOKEN을 붙임 → formatting_func(example) 함수
○ 훈련 방법: 단순 텍스트 문장 말뭉치 훈련에 맞는 SFTTrainer 사용
- 여기서 packing = True로 설정하여 109955개 데이터를 4862개 데이터로
압축하여 훈련하면 훈련 시간은 약 2시간 23분이 걸림
- Packing = False로 설정할 경우 109955개 전체를 학습하여 약 8시간이 걸림
→ packing = False 설정시, 학습이 잘 안되는 문제 발생
→ 규제를 높이는 방향으로 파라미터 조정
○ 훈련 결과: (packing=True): 607steps에서 loss 약 2.869, grad_norm 약 0.345
(packing=False): 13744steps에서 loss 3.121, grad_norm 5.48
→ 둘 다 학습이 실패하지는 않았으나 packing 옵션을 준 것이 상대적으로 안정적인 학습을 진행
○ 모델 저장: 일단 말뭉치 학습한 모델을 저장
Unsloth에서 ORPO QA 학습
○ base 모델: /home/Llama-3-8B_UnslothCorpus (no packing 말뭉치 학 모델 사용)
- 로드 설정은 기본 Unsloth FastLanguageModel.from_pretrained()의 예제코드 값을 사용 (4bit 양자화 포함)
○ peft 설정: 데이터 개수가 6297개로 적음을 고려하여 r값과 alpha를 16이 아닌 32로 설정 → LoRA 영향력↑
dropout = 0.1 → 과적합 방지
○ 데이터: /home/QA_forUnsloth.parquet
- ORPO in Unsloth용 맞춤 6297개 지시문, 질문, 답변, 오답 컬럼이 있는 데이터.
○ 텍스트 전처리: format_prompt(sample) 함수 → 공식 예제에 나온 함수 그대로 사용
○ 훈련 방법: Unsloth 공식 예제에 나온 ORPOTrainer값 기준에서 조정
- max_steps은 따로 지정하지 않음. → 예제처럼 max_steps = 30 지정시, 매우 빠른 훈련 종료 (1분 30초)
- batch size = 4, gradient steps = 8 → 지나치게 많은 step으로 인한 과적합을 방지.
- unsloth 환경에서는 ORPO 공식 learning_rate값보다 default값이 더 효율적 → loss값이 더 줄어듦
- lr_scheduler_type = "cosine"로 설정 → 196 steps에서 약 30분의 훈련시간 소요
○ 훈련 결과: loss 2.491, grad_norm 0.558, rewards/accuracy 0.937
→ loss와 norm이 안정적인 낮은 값을 기록하고, accuracy가 1로 과적합되는 경우를 피하면서 높은 값을 유지한 훈련이다.
○ 모델 저장: UnslothORPO 파일에 저장
○ 추론 성능: 드물게 정답과 가깝게 말하는 경우도 존재한다. 그러나 답변이 같은 질문에 일관되지 않다.
주제와 관련있게 답변하지만 정답이 아닌 경우가 대부분이며 환각도 잦다.
RAG 적용
Langchain의 RetrievalQA 형태
- 과제: OpenAIEmbeddings을 사용하여 외부 연결 토큰이 항상 필요하므로 ‘로컬 기반’이라고 보기 어려움
- 기존 OpenAIEmbeddings을 사용하여 저장한 DB를 로컬 임베딩 모델로 로드하면 차원 불일치 오류 발생
InvalidDimensionException: Dimensionality of ( ) does not match index dimensionality ( )
○ 로컬화 방법: 로컬 임베딩 모델로 DB를 저장하고 로드 (bge-m3)
- DB 저장 코드에서 로컬 모델로 변경 후 HuggingFaceBgeEmbeddings을 통해 bge 모델 연결
→ /home/rag_data/vector_DB에 저장
○ 실행 방법: [3]chatbot_RAG.py을 터미널에서 실행 후 노란색 지시문에 따라서 원하는 내용을 수행한다.
sloth: 나무늘보
따라서 unsloth을 정상적으로 사용한다면 그림과 같이 나무늘보 그림이 나온다.
질문하고 답변받고 답변 출처를 챗봇이 무엇을 참고했는지 확인한다.
잘못 입력한 경우 다시 입력하면 된다.
q를 통해 py파일을 종료할 수 있다.
이런 py 파일 코드는?
import transformers, torch
import time, re, os
from langchain_huggingface import HuggingFacePipeline
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceBgeEmbeddings
from langchain.chains import RetrievalQA, ConversationalRetrievalChain
from unsloth import FastLanguageModel
# 모델 로드
max_seq_length = 4096
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "UnslothORPO",
max_seq_length = max_seq_length,
# dtype = None,
# load_in_4bit = True ,
)
# 파이프라인 설정
text_generation_pipeline = transformers.pipeline(
model=model,
tokenizer=tokenizer,
task="text-generation",
repetition_penalty=1.1,
return_full_text=True,
max_new_tokens=2048,
output_scores=True,
)
# llm 설정
llm = HuggingFacePipeline(pipeline=text_generation_pipeline)
# 벡터DB 설정
vectorstore = Chroma(persist_directory = '/rag_data/vector_DB',
embedding_function = HuggingFaceBgeEmbeddings(model_name="/model/bge-m3",
model_kwargs={'device': 'cuda'})
)
# Retriever 설정
retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 4})
# RetrievalQA 설정
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
verbose=True,
return_source_documents=True,
)
# 챗봇
def chatbot():
while True:
print("\033[93m안녕하세요. 궁금한 점을 물어보세요.\033[0m")
user_input = input()
if user_input.lower() == 'q':
print("\033[93m채팅을 종료합니다.\033[0m")
break
start_time = time.time()
test_qa = qa_chain.invoke(user_input) # 호출시 invoke를 사용해야 불필요한 경고문을 줄일 수 있다.
end_time = time.time()
# 질문 텍스트
print("\033[1;91mQuestion:\033[0m\n", test_qa['query'])
# 답변 텍스트
helpful_answer = test_qa['result'].split('Helpful Answer:')[1].strip()
# 정규 표현식으로 '(출처:', '출처:', '$$' 등 불필요한 부분 제거
helpful_answer_clean = re.split(r'\(출처:|출처:|\$\$|If you have|OR', helpful_answer)[0].strip()
print("\n\n\033[1;96mAnswer:\033[0m\n", helpful_answer_clean)
# 시간 텍스트
print(f"\n\n\033[1;95mTotal time:\033[0m {round(end_time-start_time, 2)} sec.")
while True:
print("\n\033[93m답변의 출처가 궁금하면 s를, 계속해서 질문하려면 enter를, 채팅을 종료하려면 q를 입력하세요.\033[0m\n")
next_action = input().strip().lower()
if next_action == 's':
for i in test_qa['source_documents']:
print(i, '\n')
elif next_action == 'q':
print("\033[93m채팅을 종료합니다.\033[0m")
return
elif next_action == '':
break
else:
print("\n\033[41m잘못된 입력입니다. 다시 시도해주세요.\033[0m\n")
if __name__ == "__main__":
chatbot()
물론 추가로 RAG 작업, 파인튜닝 작업이 필요할 수 있다. 이것은 어디까지나 예시일 뿐.
'Project > Personal' 카테고리의 다른 글
KCB 가계부채위험도 평가모델 개발 (0) | 2024.04.22 |
---|---|
Avoid Diabetes to Live Alive (0) | 2023.08.18 |
Mustard and Lott (1997) Replication (0) | 2023.08.18 |
Quality of Life (0) | 2023.08.18 |
The Correlation between Lifestyle or Medical Factors and Diabetes (0) | 2023.08.18 |