딥러닝 8일차

6.2 문장 분류를 위한 CNN 모델

CNN은 합성곱 신경망

6.2.1 CNN 모델 개념

https://excelsior-cjh.tistory.com/79?category=1013831
https://excelsior-cjh.tistory.com/180

요약 : 기존 Multi-Layer Neural Netword의 문제점은 변수의 개수, 네트워크 크기, 학습시간 세가지 문제가 발생. 이를 개선하였다.
글자 인식에 있어서 크기,회전, 변형이 있게 되면, 이에 대해 새로운 학습데이터를 넣어줘야 하는 문제가 발생.
글자의 형상은 고려하지 않고, raw data를 직접 처리하기 때문에 많은 양의 학습데이터가 필요하고, 따라서 학습시간이 길어지게 된다.

훈련 데이터가 부족하면 데이터 증식을 통해 스스로 학습을 하기도 한다.

6.2.2 챗봇 문답 데이터 감정 분류 모델 구현

  • chatbot_data.csv
  • 문장을 감정 클래스별로 분류하는 CNN 모델
  • 임베딩 품질이 좋다면 CNN 모델은 자연어 분류에도 좋은 성능

pandas를 사용한다. pandas를 설치

# 필요한 모듈 임포트
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
from tensorflow.keras.layers import Conv1D, GlobalMaxPool1D, concatenate

판다스에서 DataFrame을 사용하기 위하여 임포트를 한다.



#데이터 읽어오기
train_file = "./ch06/chatbot_data.csv"
data = pd.read_csv(train_file, delimiter=',')
features = data['Q'].tolist()
labels = data['label'].tolist()

Pandas의 read_csv() 함수를 이용해 chatbot_data

chatbot_data.csv는 다음과 같다

그렇게 불러온 data에 대한 정보는 다음과 같다

각 레이블에 대한 설명과 함께 몇 개가 있는지와 데이터의 타입에 대해서 서술하였다.

data에 대한 수학적 통계값 정보들은 다음과 같다

->필요는 없지만 pandas의 DataFrame을 다룰 때 유용하게 사용이 가능할 것이다.

unique = 특별한 질문 또는 답변
freq = 중복된 질문 또는 답변
top = 중복된 질문 또는 답변의 내용

pandas의 데이터프레임의 컬럼 지정 방
1) 컬럼 이름으로 인덱싱, [컬럼 이름] : 컬럼 이름을 문자열 표현, 따옴표 필수
2) . 연산자, 데이터프레임.컴럼명 : 컬럼명을 따옴표로 감싸지 않음, 컬럼명은 데이터프레임의 속성 또는 함수 이름과 일치할 때는 속성이나 함수로 인식하므로 컬럼 지정이 되지 않음
3) .loc, .iloc 컬럼 위치 지정



# 단어 인덱스 시퀀스 벡터
corpus = [preprocessing.test.text_to_word_sequence(text) for text in features]
tokenizer = preprocessing.text.Tokenizer()
tokenizer.fit_on_texts(corpus)
sequences = tokenizer.texts_to_sequences(corpus)
word_index = tokenizer.word_index

질문 리스트(features)에서 문장을 하나씩 꺼내와
text_to_word_sequence() 함수를 이용해 단어 시퀀스(단어 토큰들의 순차적 리스트)를 만듭니다.
이 단어 시퀀스를 말뭉치(corpus) 리스트에 저장. 그다음 텐서플로 토크나이저의 texts_to_sequences() 함수를 이용해 문장 내 모든 단어를 시퀀스 번호로 변환
우리는 변환된 시퀀스 번호를 이용해 단어 임베딩 벡터를 만든다.

tf.keras.preprocessing.text.text_to_word_sequence

https://www.tensorflow.org/api_docs/python/tf/keras/preprocessing/text/text_to_word_sequence

특수 문자와 줄 바꿈을 삭제해준다.

텍스트를 단어들의 리스트로 반환해준다.

그 기능은 다음과 같다

우리가 처리한 corpus는 다음과 같다.

Tokenizer : 이 클래스를 사용하면 각 텍스트를 일련의 정수(각 정수는 사전에 있는 토큰의 인덱스임) 또는 단어 수에 따라 각 토큰의 계수가 이진일 수 있는 벡터로 변환하여 텍스트 말뭉치를 벡터화할 수 있습니다

Tokenizer.fit_on_texts(문장 리스트) : 모든 문장들에 대해서 단어 사전을 만든다.

다음과 같이 사전이 완성된 것을 확인 할 수 있다.


사전에 맞는 시퀀스 번호를 확인 할 수 있다.



MAX_SEQ_LE = 15 # 단어 시퀀스 벡터 크기
padded_seqs = preprocessing.sequence.pad_sequences(sequences, maxlen=MAX_SEQ_LEN, padding='post')

이렇게 만든 벡터는 한 가지 문제가 있습니다. 바로 문장의 길이가 제각각이기 때문에 벡터 크기가 다 다릅니다.
이런 이유로 시퀀스 번호로 변환된 전체 벡터 크기를 동일하게 맞춰야 합니다.
MAX_SEQ_LEN 크기만큼 넉넉하게 늘린다. MAX_SEQ_LEN보다 작은 벡터에는 남는 공간이 생기는데, 이를 0으로 채우는 작업을 해야 합니다.
이런 일련의 과정을 패딩처리라고 부릅니다.
케라스에서는 pad_sequences() 함수를 통해 시퀀스의 패딩 처리를 손쉽게 할 수 있습니다.



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


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(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_ebb = 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=droupout_prob)(hidden)
logits = Dense(3, name='logits')(dropout_hidden)
predictions = Dense(3, activation=tf.nn.softmax)(logits)



hidden = Dense(128, activation=tf.nn.relu)(concat)
dropout_hidden = Dropout(rate=droupout_prob)(hidden)
logits = Dense(3, name='logits')(dropout_hidden)
predictions = Dense(3, 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('Accuuracy: %f' % (accuracy * 100))
print('loss: %f' % (loss))


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

챗봇 문답 데이터 감정 분류 모델 사용

import tensorflow as tf
import pandas as pd

from tensorflow.keras.models import Model, load_model
from tensorflow.keras import preprocessing


# 데이터 읽어오기
train_file = "./chatbot_data.csv"
data = pd.read_csv(train_file, delimiter=',')
features = data['Q'].tolist()
labels = data['label'].tolist()


# 단어 인덱스 시퀀스 벡터
corpus = [preprocessing.text.text_to_word_sequence(text) for text in features]
tokenizer = preprocessing.text.Tokenizer()
tokenizer.fit_on_texts(corpus)
sequences = tokenizer.texts_to_sequences(corpus)


# 단어 시퀀스 벡터 크기
paddend_seqs = preprocessing.sequence.pad_sequences(sequences, maxlen=MAX_SEQ_LEN, padding='post')


# 테스트용 데이터셋 생성
ds = tf.data.Dataset.from_tensor_slices((padded_seqs, labels))
ds = ds.shuffle(len(features))
test_ds = ds.take(2000).batch(20)  # 테스트 데이터셋


# 감정 분류 CNN 모델 불러오기
model = load_model('cnn_model.h5')
model.summary()
model.evaluate(test_ds, verbose=2)


#테스트용 데이터셋의 10212번째 데이터 출력
print("단어 시퀀스 : ", corpus[10212])
print("단어 인덱스 시퀀스 : ", padded_seqs[10212])
print("문장 분류(정답) : ", labels[10212])


#테스트용 데이터셋의 10212번째 데이터 감정 예측
picks = [10212]
predict = model.predict(padded_seqs[picks])
predict_class = tf.math.argmax(predict, axis=1)
print("감정 예측 점수 : ", predict)
print("감정 예측 클래스 : ", predict_class.numpy())

좋은 웹페이지 즐겨찾기