[처음 배우는 딥러닝 챗봇] Python을 이용한 딥러닝 챗봇 제작기 (3)

3 minute read

단어 사전 구축 및 시퀀스 생성

챗봇의 의도 분류와 개체명 인식 모델의 학습을 위해 단어 사전을 구축한다. /train_tools/dict 디렉터리에 create_dict.py 파일을 생성하여 다음과 같이 코드를 작성한다. 이 코드를 작성하기 전에 TensorFlow 모듈을 설치해야 한다. 또한 단어 사전 구축을 위해 저자 분의 Github 에서 말뭉치 데이터(corpus.txt) 를 /train_tools/dict 디렉터리에 저장한다.

이 과정에서 또 오류가 발생했다. cudart64_110.dll 오류 가 발생하였다. 일반적으로 NVIDIA 그래픽 카드를 사용자는 CUDA Toolkit 을 설치하는 것으로 해결되지만, 나와 같이 AMD 그래픽 카드를 사용하거나 그래픽 카드가 없는 컴퓨터를 사용하는 경우에는 CPU 버전으로 실행해야 한다. 따라서 책에 나와있는 코드에 다음과 같은 내용을 추가해야 한다.

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
#
# 챗봇에서 사용하는 사전 파일 생성
#
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
from utils.Preprocess import Preprocess
from tensorflow.keras import preprocessing
import pickle


# 말뭉치 데이터 읽어오기
def read_corpus_data(filename):
    with open(filename, 'r') as f:
        data = [line.split('\t') for line in f.read().splitlines()]
        data = data[1:]  # 헤더 제거
    return data


# 말뭉치 데이터 가져오기
corpus_data = read_corpus_data('./corpus.txt')

# 말뭉치 데이터에서 키워드만 추출해서 사전 리스트 생성
p = Preprocess()
dict = []
for c in corpus_data:
    pos = p.pos(c[1])
    for k in pos:
        dict.append(k[0])

# 사전에 사용될 word2index 생성
# 사전의 첫 번째 인덱스에는 OOV 사용
tokenizer = preprocessing.text.Tokenizer(oov_token='OOV')
tokenizer.fit_on_texts(dict)
word_index = tokenizer.word_index

# 사전 파일 생성
f = open("chatbot_dict.bin", "wb")
try:
    pickle.dump(word_index, f)
except Exception as e:
    print(e)
finally:
    f.close()

단어 사전 테스트

앞서 작성한 코드가 잘 작동하는지를 확인해보자. /test 디렉터리에 chatbot_dict_test.py 파일을 생성하여 다음과 같이 코드를 작성한다.

import pickle
from utils.Preprocess import Preprocess

# 단어 사전 불러오기
f = open("../train_tools/dict/chatbot_dict.bin", "rb")
word_index = pickle.load(f)
f.close()

sent = input()

# 전처리 객체 생성
p = Preprocess(userdic='../utils/user_dic.tsv')

# 형태소 분석기 실행
pos = p.pos(sent)

# 품사 태그 없이 키워드 출력
keywords = p.get_keywords(pos, without_tag=True)
for word in keywords:
    try:
        print(word, word_index[word])
    except KeyError:
        # 해당 단어가 사전에 없는 경우 OOV 처리
        print(word, word_index['OOV'])

의도 분류 모델

챗봇 엔진은 화자의 질의가 입력되면, 전처리 과정을 통해 해당 문장의 의도를 분류하게 된다. 이 때 모델 분류는 CNN 모델을 이용한다. 또한 책의 예제의 의도 분류는 다음과 같다.

  • 인사(0): 텍스트가 인사말인 경우
  • 욕설(1): 텍스트가 욕설인 경우
  • 주문(2): 텍스트가 주문 관련 내용인 경우
  • 예약(3): 텍스트가 예약 관련 내용인 경우
  • 기타(4): 어떤 의도에도 포함되지 않는 경우

먼저, 챗봇 엔진에서 사용하는 파라미터를 정의한다. /config 디렉터리에 GlobalParams.py 파일을 생성하여 다음과 같이 코드를 작성한다.

# 단어 시퀀스 벡터 크기
MAX_SEQ_LEN = 15


def GlobalParams():
    global MAX_SEQ_LEN

의도 분류 모델 학습

챗봇의 의도 분류와 개체명 인식 모델을 학습시킨다. /models/intent 디렉터리에 chatbot_data.py 파일을 생성하여 다음과 같이 코드를 작성한다. 이 코드를 작성하기 전에 TensorFlow 모듈을 설치해야 한다. 또한 단어 사전 구축을 위해 저자 분의 Github 에서 학습 데이터셋(total_train_data.csv) 을 /models/intent 디렉터리에 저장한다.

# 필요한 모듈 임포트
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import pandas as pd
import tensorflow as tf
from tensorflow.keras import preprocessing
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Embedding, Dense, Dropout, Conv1D, GlobalMaxPool1D, concatenate

# 데이터 읽어오기
train_file = "total_train_data.csv"
data = pd.read_csv(train_file, delimiter=',')
queries = data['query'].tolist()
intents = data['intent'].tolist()

from utils.Preprocess import Preprocess
p = Preprocess(word2index_dic='../../train_tools/dict/chatbot_dict.bin',
               userdic='../../utils/user_dic.tsv')

# 단어 시퀀스 생성
sequences = []
for sentence in queries:
    pos = p.pos(sentence)
    keywords = p.get_keywords(pos, without_tag=True)
    seq = p.get_wordidx_sequence(keywords)
    sequences.append(seq)

# 단어 인덱스 시퀀스 벡터 생성
# 단어 시퀀스 벡터 크기
from config.GlobalParams import MAX_SEQ_LEN
padded_seqs = preprocessing.sequence.pad_sequences(sequences, maxlen=MAX_SEQ_LEN, padding='post')

# 학습용, 검증용, 테스트용 데이터셋 생성
# 학습셋:검증셋:테스트셋 = 7:2:1
ds = tf.data.Dataset.from_tensor_slices((padded_seqs, intents))
ds = ds.shuffle(len(queries))

train_size = int(len(padded_seqs) * 0.7)
val_size = int(len(padded_seqs) * 0.2)
test_size = int(len(padded_seqs) * 0.1)

train_ds = ds.take(train_size).batch(20)
val_ds = ds.skip(train_size).take(val_size).batch(20)
test_ds = ds.skip(train_size + val_size).take(test_size).batch(20)

# 하이퍼파라미터 설정
dropout_prob = 0.5
EMB_SIZE = 128
EPOCH = 5
VOCAB_SIZE = len(p.word_index) + 1  # 전체 단어 수

# CNN 모델 정의
input_layer = Input(shape=(MAX_SEQ_LEN,))
embedding_layer = Embedding(VOCAB_SIZE, EMB_SIZE, input_length=MAX_SEQ_LEN)(input_layer)
dropout_emb = Dropout(rate=dropout_prob)(embedding_layer)

conv1 = Conv1D(
    filters=128,
    kernel_size=3,
    padding='valid',
    activation=tf.nn.relu)(dropout_emb)
pool1 = GlobalMaxPool1D()(conv1)

conv2 = Conv1D(
    filters=128,
    kernel_size=4,
    padding='valid',
    activation=tf.nn.relu)(dropout_emb)
pool2 = GlobalMaxPool1D()(conv2)

conv3 = Conv1D(
    filters=128,
    kernel_size=5,
    padding='valid',
    activation=tf.nn.relu)(dropout_emb)
pool3 = GlobalMaxPool1D()(conv3)

# 3, 4, 5-gram 이후 합치기
concat = concatenate([pool1, pool2, pool3])

hidden = Dense(128, activation=tf.nn.relu)(concat)
dropout_hidden = Dropout(rate=dropout_prob)(hidden)
logits = Dense(5, name='logits')(dropout_hidden)
predictions = Dense(5, activation=tf.nn.softmax)(logits)

# 모델 생성
model = Model(inputs=input_layer, outputs=predictions)
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# 모델 학습
model.fit(train_ds, validation_data=val_ds, epochs=EPOCH, verbose=1)

# 모델 평가(테스트 데이터셋 이용)
loss, accuracy = model.evaluate(test_ds, verbose=1)
print('Accuracy: %f' % (accuracy * 100))
print('loss: %f' % (loss))

# 모델 저장
model.save('intent_model.h5')

출처

조경래 님이 집필하신 처음 배우는 딥러닝 챗봇 교재를 참고하여 글을 작성하였다.

예제 또한 저자 분의 Github 에서 발췌하였다.

Leave a comment