오늘은 미니프로젝트6 2일차로 어제에 이어 자연어처리 프로젝트를 진행했습니다.
어제는 간단한 모델을 통해서 챗봇을 진행했다면 오늘은 LSTM과 pre-trained된 모델을 가지고 챗봇을 만들어 보았습니다만 pre-trained 모델은 FastText를 사용했는데 잘못 설계했는지 계속 예측을 이상하게 해서 이번 포스팅에는 LSTM만 작성하도록 하겠습니다.
# 각각의 토큰에 인덱스 부여하는 토크나이저 선언
tokenizer = Tokenizer()
# .fit_on_tests 이용하여 토크나이저 만들기
clean_train_questions_a = tokenizer.fit_on_texts(clean_train_questions)
clean_test_questions_a = tokenizer.fit_on_texts(clean_test_questions)
tokenizer을 선언해줍니다.
x_train = tokenizer.texts_to_sequences(clean_train_questions)
x_val = tokenizer.texts_to_sequences(clean_test_questions)
tokenizer가 되었다면 시퀀스를 진행해줍니다.
# 각 토큰과 인덱스로 구성된 딕셔너리 생성
word_to_index = tokenizer.word_index
# <PAD> 토큰 추가 및 0으로 매핑
word_to_index['<PAD>'] = 0
# 인덱스를 토큰으로 매핑하는 딕셔너리 생성
index_to_word = {index: word for word, index in word_to_index.items()}
from tensorflow.keras.preprocessing.sequence import pad_sequences
import numpy as np
# MAX_SEQUENCE_LENGTH 설정
MAX_SEQUENCE_LENGTH = 50
# train 데이터의 문장을 시퀀스 데이터로 변환
x_train = tokenizer.texts_to_sequences(clean_train_questions)
x_train = pad_sequences(x_train, maxlen=MAX_SEQUENCE_LENGTH, padding='post', truncating='post')
# test 데이터의 문장을 시퀀스 데이터로 변환
x_test = tokenizer.texts_to_sequences(clean_test_questions)
x_test = pad_sequences(x_test, maxlen=MAX_SEQUENCE_LENGTH, padding='post', truncating='post')
# y_train과 y_test 생성
y_train = np.array(train_df['type'])
y_test = np.array(test_df['type'])
학습을 위한 문장 크기를 맞춰줍니다.
# vocab_size 다시 정의
vocab_size = len(tokenizer.word_index)
# 모델 구성
model = Sequential()
model.add(Embedding(input_dim=vocab_size+1, output_dim=100, input_length=MAX_SEQUENCE_LENGTH))
model.add(LSTM(units=128, activation='relu'))
model.add(Dense(units=1, activation='sigmoid'))
# 모델 컴파일
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# 모델 요약 출력
model.summary()
후에 간단한 모델링을 진행해줍니다.
import gensim
pre_wv_model = gensim.models.Word2Vec.load(path + 'ko.bin')
먼저 Word2Vec을 위한 ko.bin파일로부터 모델을 가져옵니다.
# 모델의 벡터크기 조회
vector_size = pre_wv_model.vector_size
print("Word2Vec 모델의 벡터 크기:", vector_size)
# train 데이터의 문장을 시퀀스 데이터로 변환
x_train = tokenizer.texts_to_sequences(clean_train_questions)
x_train = pad_sequences(x_train, maxlen=MAX_SEQUENCE_LENGTH, padding='post', truncating='post')
def get_sent_embedding(row, wv_model):
question = row['Q']
embedding = []
for word in question.split():
if word in wv_model.wv:
embedding.append(wv_model.wv[word])
else:
embedding.append(np.zeros(wv_model.vector_size))
return embedding
# train_df에 대해 get_sent_embedding 함수를 적용하여 임베딩 결과 저장
train_df['embedding'] = train_df.apply(lambda row: get_sent_embedding(row, wv_model), axis=1)
임베딩을 결과를 train 데이터프레임에 저장해주는 코드를 작성해줍니다.
import random
# 예측을 위한 입력 형태로 변환하는 함수
def prepare_input(question):
# 입력된 질문을 토큰화
question_tokens = tokenizer.texts_to_sequences([question])
# 시퀀스 데이터로 변환
question_seq = pad_sequences(question_tokens, maxlen=MAX_SEQUENCE_LENGTH, padding='post', truncating='post')
return question_seq
# 선택된 질문
selected_question = "액정을 부셔버렸어요 어떻게 하죠?"
# 예측을 위한 입력 형태로 변환
input_question = prepare_input(selected_question)
# 모델을 이용하여 type 0 또는 1로 분류
prediction = model.predict(input_question)
predicted_type = 1 if prediction[0][0] > 0.5 else 0
# train 데이터의 문장을 시퀀스 데이터로 변환
x_train = tokenizer.texts_to_sequences(train_df['Q'])
x_train = pad_sequences(x_train, maxlen=MAX_SEQUENCE_LENGTH, padding='post', truncating='post')
# 선택된 질문과 train 데이터의 모든 Q와의 코사인 유사도 계산
cosine_similarities = cosine_similarity(input_question, x_train)
# 코사인 유사도가 가장 높은 index 선택
most_similar_index = cosine_similarities.argmax()
# 선택된 Q의 intent에 맵핑된 답변 중 하나를 무작위로 선택
selected_answer = random.choice(train_df[train_df.index == most_similar_index]['A'].values)
print("입력된 질문:", selected_question)
print("예측된 타입:", predicted_type)
print("가장 유사한 질문:", train_df.iloc[most_similar_index]['Q'])
print("선택된 답변:", selected_answer)
해당 코드를 통해 이제 입력된 질문을 통해 답변을 해주는 챗봇이 완성됩니다.
날 잡고 자연어처리에 대해 조금 더 공부해야할 것 같습니다.
※공부하고 있어 다소 틀린점이 있을 수 있습니다. 언제든지 말해주시면 수정하도록 하겠습니다.
※용어에 대해 조금 공부 더 해서 수정하겠습니다.