SBERT(Sentence BERT)를 활용한 챗봇 생성

SBERT

  • SBERT는 기본적으로 BERT의 문장 임베딩의 성능을 우수하게 개선시킨 모델입니다. SBERT는 위에서 언급한 BERT의 문장 임베딩을 응용하여 BERT를 파인 튜닝합니다.
  1. 문장 쌍 분류 태스크로 파인 튜닝

    SBERT를 학습하는 첫번째 방법은 문장 쌍 분류 태스크. 대표적으로는 NLI(Natural Language Inferencing) 문제를 푸는 것입니다. NLI는 두 개의 문장이 주어지면 수반(entailment) 관계인지, 모순(contradiction) 관계인지, 중립(neutral) 관계인지를 맞추는 문제입니다.
  2. 문장 쌍 회귀 태스크로 파인 튜닝

    SBERT를 학습하는 두번째 방법은 문장 쌍으로 회귀 문제를 푸는 것으로 대표적으로 STS(Semantic Textual Similarity) 문제를 푸는 경우입니다. STS란 두 개의 문장으로부터 의미적 유사성을 구하는 문제를 말합니다.

SBERT의 임베딩을 사용한 챗봇만들기

  • SBERT에서 임베딩 부분만 사용(STS)
  • df의 임베딩 값과 질문의 임베딩값 사이의 유사도를 코사인 유사도로 구하여 답을 하는 구조로 구성되어 있음.
  • streamlit을 사용하여(웹 애플리케이션) 챗봇 생성
  • 전처리는 감성대화말뭉치(from AI허브)에서 Q,A부분만 가져와서 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#Thinkbig_chatbot SBERT(sentence_transformers)
import streamlit as st
from streamlit_chat import message
# streamlit_chat은 python 3.8버전 이상에서만 제대로 동작
import pandas as pd
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
import json
import os
from typing import Literal, Optional, Union
import streamlit.components.v1 as components

@st.cache(allow_output_mutation=True)
# cache : 모델을 여러번 부르지 않고 한번만 불러오는 역할
# Streamlit의 캐시 주석으로 함수를 표시하면 함수가 호출될 때마다 다음 세 가지를 확인해야 한다고 Streamlit에 알린다.
def cached_model():
model = SentenceTransformer('jhgan/ko-sroberta-multitask')
return model
# 모델은 미리 학습된 SentenceTransformer을 가져와서 사용
# SentenceTransformer는 최신 문장, 텍스트 및 이미지 임베딩을 위한 python 프레임워크
# 이 프레임워크를 사용하여 100개 이상의 언어에 대한 문장/텍스트 임베딩을 계산
# 그런 다음 이러한 임베딩을 예를 들어 코사인 유사도와 비교하여 유사한 의미를 가진 문장을 찾는다.


# wellness_dataset_final.csv : ai허브의 감성대화 데이터와 챗봇 데이터를 병합한 후 전처리한 데이터
@st.cache(allow_output_mutation=True)
def get_dataset():
df = pd.read_csv('wellness_dataset_final.csv') # 임베딩된 데이터셋 로드
df['embedding'] = df['embedding'].apply(json.loads) # 임베딩
return df

# streamlit에 위에서 정의한 함수를 사용하여 모델과 데이터셋 로드
model = cached_model()
df = get_dataset()

# 화면에 표시되는 순서대로 출력
st.title('자연어처리 프로젝트') # 제목
st.header('심리상담 챗봇') # 헤더
# st.subheader("서브헤더")
# st.text("텍스트")
st.markdown("❤️chatbot_think_big") # 마크다운
#st.subheader("")
st.markdown("""
🙂 자연어처리 1차 팀프로젝트를 위한 심리 상담 챗봇입니다.
"""
"""
💜 SentenceTransformer를 사용하여 문장을 임베딩하고 이를 코사인 유사도와 함께 비교하여 가장 유사한 답변을 채택합니다.
""")

# 왼쪽 sidebar 부분
st.sidebar.header("NLP PROJECT")
st.sidebar.subheader("TEAM : Think_Big")
st.sidebar.subheader("팀원")
st.sidebar.text("조인환(팀장)")
st.sidebar.text("김영진")
st.sidebar.text("최예은")
st.sidebar.text("백서윤")

# session_state : 각 사용자 세션에 대해 재실행 간에 변수를 공유하고 상태를 저장하고 유지
# 챗봇이 대화한 내용을 저장하는 generated session_state를 만든다
if 'generated' not in st.session_state:
st.session_state['generated'] = []
# 유저가 대화한 내용을 저장하는 past session_state를 만든다
if 'past' not in st.session_state:
st.session_state['past'] = []
## session_state를 사용하면 streamlit이 자동으로 재실행되도 초기화가 되지 않도록 한다.

# form을 만들어서 유저의 입력박스와 전송 버튼을 만든다
with st.form('form', clear_on_submit=True):
# clear_on_submit=True : 전송 버튼을 누르면 텍스트 박스가 자동으로 지워진다.
user_input = st.text_input('당신: ', '')
submitted = st.form_submit_button('전송')

# 유저의 input에 질문이 입력되면 인코딩하여 벡터화한다
if submitted and user_input:
embedding = model.encode(user_input)

# 유저의 질문과 데이터의 질문들을 코사인 유사도로 비교한 후,
# 가장 유사도가 높은 답변을 출력
df['distance'] = df['embedding'].map(lambda x: cosine_similarity([embedding], [x]).squeeze())
answer = df.loc[df['distance'].idxmax()]

# 유저의 질문을 past에 저장
st.session_state.past.append(user_input)
# 챗봇의 답변을 generated에 저장
st.session_state.generated.append(answer['A'])

# 유저의 질문과 챗봇의 답변을 메세지창에 출력하도록 하는 구문
for i in range(len(st.session_state['past'])):
message(st.session_state['past'][i], is_user=True, key=str(i) + '_user')
if len(st.session_state['generated']) > i:
message(st.session_state['generated'][i], key=str(i) + '_bot')
Author

InhwanCho

Posted on

2022-12-10

Updated on

2022-12-10

Licensed under

Comments