[Python] FAQ 챗봇 모델 만들기

MS API와 scikit learn패키지를 이용해 챗봇만들기

1. MS API로 FAQ챗봇만들기

필요한 것들

  • 1) 챗봇 기본지식 : 엑셀파일로 질문 대답시트를 작성하여 Q&A메이커에 업로드
  • 2) 파이썬 기본 지식 : 자연어 처리를 조금 해야함
  • 3) 챗봇 프레임웍 : MS Q&A메이커를 사용해서 질문에 대한 대답을 분류

1) 챗봇 기본지식 만들기

  • 자기소개 FAQ챗봇 만들기
  • 엑셀 파일로 자신의 나이, 직업, 취미 등에 대한 문답을 작성하기
In [58]:
import pandas as pd
In [2]:
q_a = pd.read_csv("knowledge_2.csv")
q_a.head()
Out[2]:
분류 질문 대답
0 1 이름이 뭐에요? 니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근
1 1 성함이 어떻게 되시죠 니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근
2 1 이름이 궁금합니다. 니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근
3 1 이름 알려주세요 니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근
4 1 이름이 뭐니? 니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근

2) MS Q&A메이커 로그인

  • http://qnamaker.ai 접속하여 로그인
  • Create new service -> 서비스명 입력하고 -> 엑셀파일 업로드 -> Create
  • 업로드할 파일은 질문과 답변만 있는 파일이어야함
  • 업로드 후 퍼블리시 하면 다음과 같은 api정보를 얻을수 있음
In [ ]:
POST /knowledgebases/90c16509-508f-4eef-afd9-1d639d3671b3/generateAnswer
Host: https://westus.api.cognitive.microsoft.com/qnamaker/v2.0
Ocp-Apim-Subscription-Key: 901f5c6d20b84eab800341bb0f04b26f
Content-Type: application/json
{"question":"hi"}

API로 질문을 보내서 답변을 받아오자!

  • key1에 위에 첫번째줄 90c16509-508f-4eef-afd9-1d639d3671b3
  • key2에 위에 세번째줄 901f5c6d20b84eab800341bb0f04b26f
In [4]:
key_1 ='90c16509-508f-4eef-afd9-1d639d3671b3'
key_2 = '901f5c6d20b84eab800341bb0f04b26f'

url_1 = 'https://westus.api.cognitive.microsoft.com/qnamaker/v2.0/knowledgebases/'
url_2 = '/generateAnswer'
url = url_1 + key_1 + url_2

headers = {'Content-Type':'application/json; charset=utf-8', 
           'Ocp-Apim-Subscription-Key' : key_2}
In [5]:
import requests

def answer(msg):
    ans = requests.post(url, headers=headers,  json={'question': msg})
    return(ans.json()['answers'][0]['answer'])
In [6]:
answer("이름이 뭐에요")
Out[6]:
'니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근'

3) 성능을 높여보자!

  • 질문이 들어왔을때 띄어쓰기 해서 QNA MAKER로 질문을 보내준다면?
  • 챗봇의 기반지식에 있는 질문을 형태소 QNA MAKER로 넣어준다면?
  • 좀 더 다양한 입력에도 더 잘 대답하는 챗봇을 만들수 있다!
  • 띄어쓰기 + 자연어처리(조사제거, 노멀라이즈, 스템)

3.1) 띄어쓰기 API를 사용해보자!

In [7]:
def spacing(sent):
    spacing = requests.put('http://35.201.156.140:8080/spacing', 
                           data={'sent':sent}).json()
    return(spacing)
In [8]:
spacing("밥은먹고 다니냐")['sent']
Out[8]:
'밥은 먹고 다니냐 '

3.2) 조사제거하여 질문형태를 단순화하자!

In [9]:
from konlpy.tag import Twitter
twitter = Twitter()

def nlp(question):
    string_analyzed = twitter.pos(question, norm=True, stem=True)
    analyzed = []
    for words in string_analyzed:
        if words[1] != 'Josa':
            analyzed.append(words[0])
    message = ' '.join(analyzed)
    return str(message)
In [10]:
nlp("밥은 먹고 다니냐")
Out[10]:
'밥 먹다 다니다'
  • 띄어쓰기 후 형태소분석하여 조사없는 질문 생성
In [11]:
def make_question(msg):
    spacing_msg = spacing(msg)['sent']
    nlp_msg = nlp(spacing_msg)
    return(nlp_msg)
In [12]:
make_question("밥은먹고다니냐")
Out[12]:
'밥 먹다 다니다'

3.3) 기존 챗봇 기반지식도 형태소 분석해서 넣자

  • 질문만 형태소분석하고 답변은 그대로 놔두면 됨
In [13]:
q_a['질문'][0]
Out[13]:
'이름이 뭐에요?'
In [14]:
nlp_question = []

for i in q_a['질문']:
    nlp_question.append(nlp(i))
In [15]:
q_a['질문_2'] = nlp_question
  • Q&A메이커에 질문만 지금 만든 질문2로 바꿔주면 됨
In [16]:
q_a.head()
Out[16]:
분류 질문 대답 질문_2
0 1 이름이 뭐에요? 니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근 이름 뭐 ?
1 1 성함이 어떻게 되시죠 니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근 성함 어떻다 되다
2 1 이름이 궁금합니다. 니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근 이름 궁금하다 .
3 1 이름 알려주세요 니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근 이름 알다
4 1 이름이 뭐니? 니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근 이름 뭐 ?
In [278]:
q_a.to_csv('q_a_know.csv')

4) 최종정리

  • 그냥 바로 Q&A메이커에 보내면
In [17]:
qs = ['이름이뭐니', '무슨일해?', '몇살이냐', '어디살아요', '취미가뭐에요']
for i in qs :
    print(answer(i))
니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근
피도 눈물도 없는 광기의 매드 사이언티스트?
No good match found in the KB
경기도 수원이야...너 혹시 수원에 산다면...너 내 동료가 되라!!
내 취미는 Rock and Roll~!! Love and Peace!! 난 기타를 좋아해!! 나랑 같이 기타를 치지 않을래?
  • 띄어쓰기 및 형태소 분석해서 Q&A 메이커에 보내면
In [18]:
def get_ans(question) :
    nlp_question = make_question(question) # 띄어쓰기 및 형태소 분석
    return(answer(nlp_question))           # Q&A메이커 API에 전달
In [19]:
for i in qs :
    print(get_ans(i))
니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근
피도 눈물도 없는 광기의 매드 사이언티스트?
나이는 중요하지 않아! 중요한건 마음가짐이지! 만 19세는 넘었다는것만 알려주지...
경기도 수원이야...너 혹시 수원에 산다면...너 내 동료가 되라!!
내 취미는 Rock and Roll~!! Love and Peace!! 난 기타를 좋아해!! 나랑 같이 기타를 치지 않을래?

2. Scikit learn 라이브러리로 FAQ챗봇 만들기

In [45]:
data = pd.read_csv("knowledge_2.csv")
data.columns = ['class', 'question', 'answer']
data.head(2)
Out[45]:
class question answer
0 1 이름이 뭐에요? 니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근
1 1 성함이 어떻게 되시죠 니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근

기계학습을 위한 데이터 준비

  • 질문을 x_data로 준비
  • 답변에 대한 클래스를 y_data로 준비
In [46]:
x_data = data['question']
y_data = pd.Categorical(data['class'])
In [57]:
x_data[0:5]
Out[57]:
0       이름이 뭐에요?
1    성함이 어떻게 되시죠
2     이름이 궁금합니다.
3       이름 알려주세요
4        이름이 뭐니?
Name: question, dtype: object
In [48]:
y_data[0:10]
Out[48]:
[1, 1, 1, 1, 1, 2, 2, 2, 2, 2]
Categories (5, int64): [1, 2, 3, 4, 5]
  • CountVectorizer 함수로 문서를 매트릭스로 변환하고 TF-IDF적용함
  • 서포트 벡터머신 알고리즘으로 질문에 대한 답변을 분류
In [49]:
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.linear_model import SGDClassifier
from sklearn.pipeline import Pipeline
from konlpy.tag import Twitter

def get_noun(text):
    tokenizer = Twitter()
    nouns = tokenizer.nouns(text)
    return [n for n in nouns]
In [50]:
cv = CountVectorizer(tokenizer=get_noun)
cv.fit_transform(x_data)
Out[50]:
<28x27 sparse matrix of type '<class 'numpy.int64'>'
    with 51 stored elements in Compressed Sparse Row format>
In [51]:
text_clf_svm = Pipeline([('vect', CountVectorizer(tokenizer=get_noun)),
                         ('tfidf', TfidfTransformer()),
                         ('clf-svm', SGDClassifier(loss='hinge', 
                                                   penalty='l2', 
                                                   alpha=1e-3, 
                                                   n_iter=5, 
                                                   random_state=42))])
  • 학습 및 예측
In [52]:
text_clf_svm.fit(x_data, y_data)
Out[52]:
Pipeline(steps=[('vect', CountVectorizer(analyzer='word', binary=False, decode_error='strict',
        dtype=<class 'numpy.int64'>, encoding='utf-8', input='content',
        lowercase=True, max_df=1.0, max_features=None, min_df=1,
        ngram_range=(1, 1), preprocessor=None, stop_words=None,
        strip...     penalty='l2', power_t=0.5, random_state=42, shuffle=True, verbose=0,
       warm_start=False))])
In [53]:
text_clf_svm.predict(['취미는 뭐에요'])
Out[53]:
array([4], dtype=int64)
  • 답변이 분류된 클래스에 따라 해당 답변 제시를 위한 함수 생성
  • 1~5라벨에 대한 답변 준비한 뒤 클래스 인덱스에 따라 리스트에서 빼옴
In [54]:
answer = ["니노 막시무스 카이저소제 소냐도르 앤 스파르타 김성근", 
          "나이는 중요하지 않아! 중요한건 마음가짐이지! 만 19세는 넘었다는것만 알려주지", 
          "피도 눈물도 없는 광기의 매드 사이언티스트", 
          "내 취미는 Rock & Roll~!! Love & Peace!! 난 기타를 좋아해!! 나랑 같이 기타를 치지 않을래?", 
          "경기도 수원이야 너 혹시 수원에 산다면 너 내 동료가 되라!"]
In [55]:
def svm_asn(question):
    idx = int(text_clf_svm.predict([question]))
    return(answer[idx-1])    
In [56]:
print(svm_asn("취미는 뭐에요"))
print(svm_asn("어디 살아요"))
print(svm_asn("연세가 어떻게 되시죠"))
print(svm_asn("직업이 어떻게 되시나요"))
내 취미는 Rock & Roll~!! Love & Peace!! 난 기타를 좋아해!! 나랑 같이 기타를 치지 않을래?
경기도 수원이야 너 혹시 수원에 산다면 너 내 동료가 되라!
나이는 중요하지 않아! 중요한건 마음가짐이지! 만 19세는 넘었다는것만 알려주지
피도 눈물도 없는 광기의 매드 사이언티스트

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다