이전 글에서는 형태소 단위의 임베딩을 설명하였고, 프로젝트에서도 이를 활용하여 키워드의 유사도를 비교해 적합한 응답을 매칭하는 방식을 고려하였지만 진행 과정에서 예상치 못한 문제점이 발생하였습니다.
"취미가 무엇인가요?"라는 질문과 "여가 시간에 주로 무엇을 하나요?"라는 질문을 가정해 보겠습니다. 두 질문은 서로 다른 단어를 사용하지만 실제로는 유사한 의미를 가지므로 "저의 취미는 알고리즘 문제 풀기입니다."라는 응답을 기대하였습니다. 하지만 키워드 매칭 방식은 문맥을 고려하지 않기 때문에 두 질문간의 유사성을 인식하지 못하고 잘못된 응답을 하는 문제가 발생하였습니다.
물론 질문/응답 데이터셋에 수동으로 다양한 어휘의 동일한 질문을 추가하는 방식으로 해결이 가능하지만, 이는 모든 어휘에 대응이 필요하다는 점에서 근본적인 문제의 해결이 아닙니다. 따라서 키워드 매칭 방법은 최종적으로 제외하였고, 문맥을 고려 할 수 있는 고차원 모델 도입의 필요성을 실감하여 다양한 모델을 구현하고 비교하였습니다.
다음은 프로젝트 진행 과정에서 테스트한 주요 모델과 결과입니다.
Seq2Seq
Vanilla Transformer
질문과 무관하거나 잘못된 응답을 생성하는 것을 할루시네이션 문제라고 합니다. NLP 모델이 왜곡되거나 잘못된 응답을 생성하지 않기 위해서 할루시네이션 문제는 반드시 극복해야 할 문제이며, 이를 해결하기 위해 곳곳에서 활발한 연구 및 개발이 진행중입니다. 필자 또한 프로젝트를 진행하는 과정에서 할루시네이션 문제에 직면하였고, 문맥을 효과적으로 이해 할 수 있는 Transformer의 특성과 질문에 적절한 응답을 매칭 할 수 있는 "키워드 매칭"의 특성을 접목하여 이러한 문제를 완화 할 수 있는 아이디어를 떠올렸습니다.
아이디어의 흐름은 다음과 같습니다.
Transformer 모델은 인코딩을 통해 질문을 잠재 벡터로 변환하고, 디코딩을 통해 응답을 생성합니다. 이러한 과정에서 문맥상 비슷한 질문은 비슷한 잠재 벡터로 변환 될 것입니다. 이를 디코딩하는 과정에서 할루시네이션 문제가 발생하므로 디코딩을 진행하지 않고, 유사도를 평가해 미리 정의된 응답 중 가장 적합한 결과를 제공하여 할루시네이션 문제를 완화 할 수 있었습니다.
앞서 설명한 아이디어를 Vanilla Transformer 모델에 적용하면서, 자연스럽게 XLM 모델을 접할 수 있었습니다. XLM 모델은 Facebook AI Research에서 개발한 Transformer 기반 다국어 사전 학습 모델로, 여러 언어의 의미를 공통된 표현 공간에서 학습하는 데 최적화되어 있습니다.
Vanilla Transformer 모델과 XLM 모델을 비교하며 구현해 본 결과, XLM 모델의 성능이 월등히 높은 것을 확인할 수 있었습니다. 특히, 동일한 데이터셋에서의 평가에서도 XLM 모델은 더 나은 문맥 이해와 응답 매칭 성능을 보였습니다.
이러한 성능의 차이는 학습 데이터의 양과 품질의 차이로 보여집니다. Vanilla Transformer는 제한된 양의 데이터로 학습이 진행되었기 때문에 다양한 문맥적 의미를 충분히 학습하지 못 했지만, XLM 모델은 대규모 다국어 데이터셋으로 학습이 진행된 사전 학습 모델이기 때문에 비교적 포괄적인 문맥을 학습 할 수 있었던 것으로 생각됩니다.
또한, XLM 모델은 다국어 처리에 강점이 있는 모델로, 여러 언어의 공통된 벡터 표현을 학습하므로 문맥적 유사성을 훨씬 잘 표현 할 수 있었던 것 역시 성능 차이의 핵심적인 요소라고 생각합니다.
따라서, 프로젝트에서는 XLM 모델을 이용해 인코딩을 진행하고, 코사인 유사도를 계산하여 최종 결과를 출력하도록 구현하였습니다. 자세한 사항은 아래 코드의 주석으로 작성 해 두었습니다.
import numpy as np import pandas as pd from numpy import dot from numpy.linalg import norm from sentence_transformers import SentenceTransformer import os class Chatbot: def __init__(self): # train 데이터 로드 self.train_data = pd.read_csv(os.getcwd() +'/data/ChatbotData.csv') print("DataSet Loaded") # XLM 모델 로드 self.model = SentenceTransformer(os.getcwd() +'/data/xlm-100langs_model') print("Model Loaded") # train 데이터 임베딩 진행 self.train_data['embedding'] = self.train_data.apply(lambda row: self.model.encode(row.Q), axis = 1) # 코사인 유사도 계산 함수 def cos_sim(self, A, B): return dot(A, B) / (norm(A) * norm(B)) # 질문 임베딩 및 유사도 계산하여 최종 응답 반환 def return_answer(self, question): embedding = self.model.encode(question) self.train_data['score'] = self.train_data.apply(lambda x: self.cos_sim(x['embedding'], embedding), axis=1) return self.train_data.loc[self.train_data['score'].idxmax()]['A'] # 사용자에게 질문 입력 받고 응답 출력 def main(): chatbot = Chatbot() while True: question = input("User: ") answer = chatbot.return_answer(question) print("Chatbot: " + answer) if __name__ == "__main__": main()
이번 글에서는 다양한 모델의 장단점을 확인하고, 최종 모델을 선정하여 질문을 입력하고 적절한 응답을 출력하기 위한 파이프라인을 구축하였습니다. 다음 글에서는 구현한 모델을 Django 프레임워크를 통해 웹 애플리케이션을 작성하고 이를 Docker를 통해 서비스로 배포하는 과정을 다룰 예정입니다.