티스토리 뷰
BM25와 sentence Transformer를 이용해 QA 봇을 만든다.
5개의 유사도가 가장 높은 샘플을 뽑고, 그를 이용해 그 안에서 Gemma가 답변하게 만든다.
# chunk list
chunk_list = []
with open(CHUNK_FN, encoding="utf-8") as f:
for line in f:
row = json.loads(line)
chunk_list.append(row['chunk'])
len(chunk_list)
SentenceTransformer 사용 및 embedding 만들기
# SentenceTransformer 모델 생성
tf_model = SentenceTransformer(MODEL_ID)
# chunk embeddings 생성
chunk_embeddings = tf_model.encode(chunk_list)
chunk_embeddings.shape
여기서 채택한 임베딩을 질문에 통과시켜 얻은 유사도를 사용한다.
def query_sentence_transformer(model, chunk_embeddings, query, top_n=10):
query_embedding = tf_model.encode([query])
# score 계산
doc_scores = np.matmul(chunk_embeddings, query_embedding.T)
doc_scores = doc_scores.reshape(-1)
# score 순서로 정렬
rank = np.argsort(-doc_scores)
# top-n
result = []
for i in rank[:top_n]:
result.append((i, doc_scores[i]))
return result
모델 양자화 및 불러오기
불러오는 모델도 양자화된 모델을 사용한다. 속도 향상 및 Gpu 메모리 상의 이점을 위해서 사용
# declare 4 bits quantize
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16
)
# load 4 bits model
model = AutoModelForCausalLM.from_pretrained(SLLM_MODEL_ID,
device_map='auto',
quantization_config=quantization_config,
token=HF_TOKEN)
# load tokenizer
tokenizer = AutoTokenizer.from_pretrained(SLLM_MODEL_ID,
add_special_tokens=True,
token=HF_TOKEN)
tokenizer.padding_side = 'right'
pipe = pipeline("text-generation",
model=model,
tokenizer=tokenizer,
max_new_tokens=512)
pipe
프롬프트 정의
def gen_prompt(pipe, chunk_list, query):
content = ["당신이 가진 식을 의존하지 말고 '문서1'부터 '문서5'를 읽고해서 '질문'에 대해서 답변해 주세요.:"]
for i, chunk in enumerate(reversed(chunk_list)):
content.append(f"문서{5-i}: {chunk}")
content.append(f"질문: {query}")
content = "\n\n".join(content)
messages = [
{
"role": "user",
"content": content
}
]
prompt = pipe.tokenizer.apply_chat_template(messages,
tokenize=False,
add_generation_prompt=True)
return prompt
모델로부터 해당 결과를 리턴받아야 한다.
# 프롬프트 생성 및 질문을 sllm에게 묻고 결과를 리턴하는 함수
def gen_response(pipe, chunk_list, query):
prompt = gen_prompt(pipe, chunk_list, query)
outputs = pipe(
prompt,
do_sample=True,
temperature=0.2,
top_k=50,
top_p=0.95,
add_special_tokens=True
)
return outputs[0]["generated_text"][len(prompt):]
실험적으로는 다음과 같은 챗봇이 된다.
while True:
cnt=0
query = input('질문 > ')
query = query.strip()
if len(query) == 0:
break
result_transformer = query_sentence_transformer(tf_model, chunk_embeddings, query)
print_list = []
for i,score in result_transformer[:5]:
print_list.append(chunk_list[i])
result = gen_response(pipe, print_list, query)
print(f'답변 > {result}\n\n')
필자의 단독 실험
Gemma 2B vs Gemma 7B top rate
2B에서는 할루시네이션 현상이 조금 있다.
그러나 7B에서는 할루시네이션 현상이 줄어들고, 대답도 조금 더 깔끔하게 한다.
메타인지에 대한 대역이 3.8B 부근이라는 논문을 봤었던 것 같은데, 그 영향도 조금 있는 것 같다.
DPR 적용
dpr = passage, context같은 문장이 있다.
두번째로는 질문이 있다. = 지미 카터가 태어난 해는?
question bert가 따로 있고, passage bert가 따로 있다.
그 둘을 dot product해서
dpr을 쓸 때는 어떤 데이터로 학습되었는지를 알아야 한다.
한글로 학습되어야만 한글로 할 수 있다.
https://jjonhwa.github.io/booststudy/2021/10/19/booststudy-paper-DPR_for_ODQA_Paper_Reading/
chatgpt로 데이터를 만들고 싶다면 어떻게 해야 할까?
당신이 가진 질문을 넣는다.
똑같이 문서~질문 쌍으로 해서, 답변을 얻는다.
그 답을 하기를 원하는 것이다.
그렇게 해서 fine tuning을 할 때, 답변을 선별해서 그 답변을 튜닝하는 것이다.
chunk_list가 없다면
네이버 가서 복사해서 쓴다.
https://github.com/TmaxEdu/KorDPR
https://github.com/redlion0929/KorDPR_NLP
https://huggingface.co/Taekyoon/dpr_context/tree/main
https://huggingface.co/Taekyoon/dpr_question
다른 조에서 배운 것
도서자료 요약 텍스트 데이터를 활용해서 API를 확보했다.
책 내용에 대해서 요약해주거나 책 자체를 추천해주는 데이터
신문기사 모델
데이터가 많아야 질문을 잘 답변해준다.
RSS 피드별 데이터를
데이터는 많은데 , gpu ram이 부족한 경우도 있었다. url에 대한 구별도 필요할 거 같다.
그리고 Llama3를 추가로 사용했다.
실질적으로 길게, 디테일하게 표현해준다.
나무위키 데이터베이스 덤프
ram 부족으로 인해 실패.
chunk_list 하나하나가 너무 크다.
검색 문서의 개수를 늘려야 성능이 향상된다.
BM25 + Sentence Transformers
Transformer를 사용해 벡터 계산 후 BM25를 사용해 tokenization.
BM25를 이용해서 찾은 문서가 많이 들어갔을 떄 오히려 노이즈로 작용하여 성능 하락
BM25는 5개, sentence Transformer은 10 비율로 조절했다.
Langchain pdf Loader
Kobert의 뉴스 데이터도 사용
나무위키 데이터는 양질의 데이터가 아니다. 맥락에 맞지 않는 데이터가 너무 많다.
Attention의 메모리
n이 커지면 n의 제곱만큼 차지. query key의 제곱만큼 dot product를 한다.
문장이 길어지면 길어질수록 유사도 계산에 대한 메모리 사용량도 늘어나고, 연산량도 늘어난다.
처음으로 배웠을 때는 코드가 어려웠는데, 지금은 모든 모델을 그냥 땡겨 와서 쓴다.
이제 엔지니어링이 중요한 시대. 데이터를 정제(clear)하는 게 더 중요하다.
쉽게 말하자면 저런 형태로 되어 있어서, n*n일수록 연산량이 늘어난다.
6차 미니프로젝트 소감
유사도는 어느정도 감이 잡혔으나, 확장해 나갈 게 많은 프로젝트였다고 생각한다.
무엇보다 챗봇이라고 하면 이미 GPT가 있는 상태에서 어떤 차별점을 가질 것인가를 알아야 한다.
필자는 이 답이 '인격'에 있다고 생각한다. 사람이 뭔가 느낄 수 있도록 그런 식의 인격을 만들어내는 걸 목표로 하는 게 좋을 것 같다.
추후 필자가 시도해볼 프로젝트
신문 기사 검색 엔진 + 투자 모델 연계
DPR +bm25 이용
Gemma를 이용해서, 하루 시작에 최신 기사를 유사도 기반으로 정렬한다.
그 후에 해당 연계를 통해 관련 있는 태그의 종목을 예측한다.