Retrival 기반의 챗봇을 개발하다 보면
정확도(질문의 유사도)를 가지고 유사도가 가장 비슷한 질문을 찾아서
해당하는 답변을 리턴해주는 방식을 많이 사용하곤 한다. (유사도를 구하는 방법은 매우 다양함)
하지만 유사도가 높다고 해서 "좋은 답변"일까?
챗봇의 성능이 아무리 좋다고 하더라도 단답이나 장문의 답을 한다면 좋은 챗봇이 아니라고 생각한다.
챗봇이 아니라 일상 대화에서도
단답형 :
Q : 너 밥 먹었어?
A : 응.... , 네.... , 예~....
장답형 :
Q : 너 밥 먹었어?
A : 밥을 먹었는데 밥은 현미밥이었고 반찬은 내가 좋아하는 ~~ 그런데 밥 먹다가 전화가 와서 전화를 받다가 다시 밥을 먹고~~ 주절주절
이렇게 얘기를 하면 대화가 단절되거나 어떻게 답해야 할지 모를 것이다. ;;;
적절한 길이의 답변:
Q :너 밥 먹었어?
A : 응! 밥 방금 먹었는데 너무 배부르다 ㅎㅎ
난 위 예시 정도의 답변의 길이가 주제에 따라 다르겠지만 챗봇에서는 적절하지 않을까 생각한다.
설령 정확도가 조금 떨어지더라도 단답을 하는 것보다.
적절한 길이의 답을 하는 것이 대화가 오히려 재미있을 수 있다.
그래서 텍스트의 길이를 가지고 페널티를 주는 방법을 생각해보았다.(찾아보면 좋은 방법이 있을 것 같지만 일단은....)
너무 짧은 경우와 , 너무 긴 경우에 대한 페널티(penalty)를 적용하여 값을 보정하는 것이다.
핵심은 제거와 교체가 아닌 보정하는 것.
보정의 초점을 맞춰서 생각해봄.
먼저 내가 원하는 적합한 길이의 값이 필요하다.
이 값은 전체 대화 데이터의 평균값(mean)이 될 수도 있고 중앙값(median)이 될 수도 있다.
개인적으로 길이가 너무 길거나 짧은 데이터가 많다면
평균보다 이상치(outlier)의 영향이 적은 중앙값을 사용하는 것을 권장한다.
내가 원하는 길이가 15라고 가정해보자.
best_len = 15
score = [ 12.496439, 8.1158695, 7.694651, 7.16416, 7.134159 ]
text_length = [ 8, 9, 20, 153, 20]
길이에 대한 페널티를 적용하지 않는 다면 가장 짧은 길이 8의 답변이 스코어가 높기 때문에 채택될 확률이 매우 높고,
텍스트 길이가 가장긴 153의 답변이 등장할 확률도 적지 않다. 이런 값을 보정
편리한 브로드캐스팅 기능을 위해 넘파이(numpy)배열로 변경
import numpy as np
score = np.array(score)
text_length = np.array(text_length)
패널티 값을 구하자!
penalty_value = np.abs(text_length - best_len) / best_len # best_len = 15
*np.abs() : 절댓값을 구해주는 넘파이 함수
수식을 하나씩 살펴보면 먼저 각각의 텍스트 길이에서 원하는 길이(best_len)를 뺀 값을 구해준다.
text_length - best_len
결과 : [ -7, -6, 5, 138, 5]
이후에 절댓값을 취해줌
np.abs(text_length - best_len)
결과 : [ 7, 6, 5, 138, 5]
위 결과는 내가 원하는 길이와 얼마나 동떨어져있는지를 알 수 있다. ( 너무 길어도 너무 짧아도 수치가 높아짐 )
즉, 값이 적을수록 원하는 길이와 가깝다는 뜻
이 값을 그대로 사용해도 되지만 score 스케일과 어느 정도 맞춰주기 위해서 다시 best_len으로 나눠주었다.
np.abs(text_length - best_len) / best_len
결과 : [0.46666667, 0.4 , 0.33333333, 9.2 , 0.33333333]
이 값을 각각 score 값에서 빼주게 되면 페널티를 적용한 결과가 나온다.
score - penalty_value
페널티 보정 전 스코어 : [ 12.496439, 8.1158695, 7.694651, 7.16416, 7.134159 ]
보정 결과 : [12.02977233, 7.7158695 , 7.36131767, -2.03584 , 6.80082567]
*길이가 매우 길었던 데이터의 경우 7.16점에서 -2.03까지 내려갔다.
음수 값이 있어서 불편하다면 제곱을 한 뒤 루트를 씌워서 비율은 유지한 채 음수 값을 양수로 바꿀 수 있다.
np.sqrt((score - penalty_value) ** 2)
[12.02977233, 7.7158695 , 7.36131767, 2.03584 , 6.80082567]
이 스코어 값을 사용할 경우
값의 차이는 크게 나지 않지만
길이가 길면 길 수록 짧으면 짧을수록 페널티 점수(-)를 받게 된다.
값의 차이를 크게 늘리고 싶다면 np.sqrt를 적용하지 않은 (score - penalty_value) ** 2 값만 사용해도 좋다.
def len_penalty(scores, sentences, optimum_len):
scores = np.array(scores)
length = [len(i) for i in sentences]
length = np.array(length)
penalty_value = np.abs(length - optimum_len) / optimum_len
return scores - penalty_value
'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 |
[챗봇] 아주 간단한 검색기반 챗봇 만들기(feat.pororo) (1) | 2021.08.31 |