전에 공부했던 시퀀스 투 시퀀스(seq2seq)로 구현했던 챗봇은 디코더를 통해 단어를 찾아 이어 붙이는 모델이었다.
하지만 문맥이 매끄럽지 않은 부분이 있었고 아웃풋의 시퀀스 수를 결정하는 것이 부자연스러웠다.
<END> 토큰이 나와야 하는 조건이 정확하지 않아 보였고 <Many to Many> 문제가 생각보다 쉽지 않아 보였음.
그래서 이번엔 간단하게 검색 기반 모델을 사용해보고자 한다.
데이터 셋은 전에 사용했던 일상생활(직장, 연애 , 등등 )에서 사용되는 간단한 질문과 답변으로 구성되어있는 데이터를 사용.
진짜 봇처럼 느껴지는 단호박 답변들이 11823건이 있다 ㅋㅋㅋ..
<데이터 출처 기입 요망>
내 계획은 이렇다.
1. 일단 질문(Q)들을 전부 임베딩 시킨다.
2. 임베딩 값을 미리 저장한다.
3. 새로 입력한 텍스트를 임베딩 시켜서 가장 비슷한 질문을 찾는다.
4. 가장 비슷한 질문의 답변을 현재 질문에 답변으로 사용한다.
문장 임베딩 ( Sentence Embedding )
단어 임베딩은 많이 알고 사용해 보았지만 문장 임베딩은 Doc2vec 말고는 사용해본 경험이 없어서
문장 임베딩 종류에 대해서 알아보았고,
fastText를 기반으로 만든 Sent2Vec와 ELMO , BERT 등이 있었다.
무엇을 사용할지 고민했지만 일단 제일 간단한 Doc2vec 모델을 먼저 사용해 보기로 했다.
( 전에 사용한 코드가 있어서 데이터 셋만 교체해서 진행 )
하지만 임베딩 결과값이 랜덤 시드값에 따라 너무 차이가 났고 테스트해 보았을 때 부정확했다.
그러던 중에 카카오 브레인에서 제공하는 자연어 처리 플랫폼 Pororo를 발견했고,
이걸 사용하기로 했다. 문장 임베딩뿐만 아니라 다른 여러 기능을 그냥 다운로드하여서 사용할 수 있었고
사전 학습된 모델을 사용할 수 있었다(BERT) 너무 날로 먹는 건 아닌가 싶었지만..
* torch 버전은 1.6을 사용해야 하는데 M1 맥북 특성상 1.9 버전에서 다운그레이드가 안되어 애먹었다.
결국 구글 코랩 사용
!pip install pororo
https://kakaobrain.github.io/pororo/text_cls/sent2vec.html
한국어의 경우 카카오 브레인에서 자체 개발한 brainsbert.base 모델을 사용했다고 한다.
(영어 , 중국어 , 일본어 다 가능 )
se = Pororo(task="sentence_embedding", lang="ko")
se("나는 동물을 좋아하는 사람이야")
[128.78, 200.12, 245.321, ...] # (1, hidden dim)
단 두줄에 끝나는 마법. 유레카
챗봇 코드
필요 패키지
from pororo import Pororo
import pandas as pd
from sentence_transformers import util
import torch
import numpy as np
구글 드라이브 연동 , 임베딩 진행상황을 보기 위한 tqdm
from google.colab import drive
drive.mount('/gdrive')
from tqdm import tqdm
tqdm.pandas()
데이터 불러오기
DATA_IN_PATH = '/gdrive/MyDrive/Retrieval_chatbot/'
df = pd.read_csv(DATA_IN_PATH + 'ChatbotData.csv',sep=',')
임베딩 : 모든 질문에 대한 임베딩 값을 구함.
se = Pororo(task="sentence_embedding", lang="ko")
df['embedding_vectors'] = df['Q'].progress_map(lambda x : se(x))
데이터 변환 : 코사인 유사도를 구해주기 위해 미리 구해놓은 임베딩 데이터를 tensor로 변환.
embedding_data = torch.tensor(df['embedding_vectors'].tolist())
인풋 데이터를 임베딩 하고 tensor 타입으로 변환
def return_sim_question(input_sentence):
input_sentence = se(input_sentence)
input_sentence = torch.tensor(input_sentence)
return input_sentence
챗봇 구현 함수
def chatbot(message):
message = message.strip()
# 입력 문장 임베딩
embedding_sentence = return_sim_question(message)
# 미리 구해진 임베딩 데이터와 현재 임베딩 데이터의 코사인 유사도 추출
cos_sim = util.pytorch_cos_sim(embedding_sentence,embedding_data)
cos_sim = cos_sim.cpu()
#유사도가 가장 비슷한 질문 인덱스 반환
best_sim_idx = int(np.argmax(cos_sim))
#print(df['Q'][best_sim_idx])
#유사도가 가장 비슷한 질문에 해당하는 답변 제공
answer = df['A'][best_sim_idx]
return answer
결과 :
(1). 데이트 장소 조언
사용자가 입력한 질문 : 첫 데이트는 어디가 좋을까요
사용자가 입력한 질문과 가장 비슷한 질문 : 첫 데이트에 뭐하지
사용자가 입력한 질문과 가장 비슷한 질문의 답변 : 야경이 멋져요. 야경구경 가세요.
(2). 결혼 고민
(3). 연애
* 이런 경우 "전"을 빼놓기 때문에 이런 결과를 내뱉는 거 같다.
느낀 점
(1). 확실히 생성 모델보다는 검색 모델이 틀린 답을 하더라도 실제 사용하는 문장이기 때문에 문맥이 매끄럽다.
(2). 임베딩이 가장 중요한데 카카오 브레인이 깔끔하게 해결해줬다. 날먹 ㅋㅋ
(3). 가장 유사한 문장을 추리고 나서 또 다른 검증을 통해 적절한 대답을 유추하면 더 좋은 모델이 될 것 같다.
'Machine learning > Chatbot' 카테고리의 다른 글
[chatbot]. 핑퐁 빌더 API 연동하기 (1) | 2022.12.05 |
---|---|
[챗봇] faiss로 빠르게 유사도 검색하기(Similarity Search) (0) | 2022.07.18 |
[챗봇]. 편집거리 알고리즘을 통한 오타 확인 (0) | 2022.04.29 |
[챗봇]. BERT 임베딩 벡터를(Embedding Vectors) RDBMS의 저장해야하는 이유 (1) | 2022.02.13 |
[챗봇]. 대화 텍스트 길이(length)에 대한 패널티(penalty)를 주는 방법 (0) | 2021.11.14 |