Vallina Seq2seq
tf.function을 사용하기 위해 tensorflow 2.0.0-beta1버전을 설치한다.
한글 텍스트의 형태소분석을 위해 konlpy에서 Okt(Original Korean tag, Twitter에서 공개한 오픈소스 라이브러리)를 사용하기 위해 설치해준다.
1 | !pip install tensorflow==2.0.0-beta1 |
- 필요한 라이브러리 import
1 | import random |
- tensorflow 버전이 맞는지 확인
1 | print(tf.__version__) |
하이퍼 파라미터 설정
1 | EPOCHS = 200 |
Encoder
1 | class Encoder(tf.keras.Model): |
Decoder
1 | class Decoder(tf.keras.Model): |
Seq2seq
1 | class Seq2seq(tf.keras.Model): |
학습, 테스트 루프 정의
1 | # Implement training loop |
데이터셋 준비
- http://www.aihub.or.kr에서 text데이터 중 AI chatbot 데이터를 사용할 것이다. 이 데이터를 다운받아 필자는 google storage 서비스를 이용해서 기존의 생성해놓았던 버킷을 통해 데이터를 업로드 한 후, 받아와서 사용할 것이다. 이 방법은 google storage에서 파일을 받아 사용하는 gsutil 방식이며 빠르다는 점이 장점이지만 현재 세션이 종료되거나 새로시작할 경우 다시 실행 시켜주어야 하는 방식이다. 또한 필자처럼 google colab이 아닌 자신의 로컬PC로 실행할 경우 아래 단계는 건너 뛰어도 상관없다.
1 | from google.colab import auth |
chatbot_data.csv 파일이 현재 path에 존재하는지 확인
1
%ls
chatbot_data.csv파일을 pandas DataFrame으로 읽어 어떤 데이터들이 존재하고 추후에 x(Question)와 y(Answer)로 나눠주려면 패턴을 찾아야 하기 때문에 모든 데이터를 볼 것이다. 전체 데이터는 999개이기 떄문에 출력되어지는 row의 수를 1000개로 맞춰준다.
1 | pd.options.display.max_rows = 1000 |
1 | chatbot_data = pd.read_csv('chatbot_data.csv',header=0) |
- 위에서 pandas로 불러들인 QA(Question & Answer) data를 보면 Question과 Answer로 이루어져있다. 즉, 순차적인 데이터인 것이다. 또한 대화의 끝이 나누어져 있지 않아 입력으로 넣어주려면 Data를 Question과 Answer 쌍으로 가공해주어야 할 것이다. 맨처음 줄부터 Question 그다음은 Answer 이순으로 되어있다는 것을 확인할 수 있다.
1 | dataset_file = 'chatbot_data.csv' |
학습 환경 정의
모델 생성, 손실 함수, 최적화 알고리즘, 평가지표 정의
1 | # 모델 생성 |
학습 루프 동작
1 | for epoch in range(EPOCHS): |
테스트 루프
1 | for test_seq, test_labels in test_ds: |
- 예측된 값들을 보면 train data에 과적합된 것을 충분히 알 수 있을 것이다.
이제 여기서 Attention mechanism을 적용시켜보자.
Encoder, Decoder, Seq2seq 부분을 수정하면된다.
Encoder
이전과 다르게
LSTM 구조에서 return_sequences=True를 넣어 전체 Hidden State를 출력하게 해주었다. 이를 Key-Value로 사용할 것이다.
Embedding 결과의 Dimension : (32(batch_szie), 64(sequence의 길이), 64(Embedding Feature의 수))
LSTM의 결과의 Dimension : (32(batch_szie), 64(sequence의 길이), 512(LSTM unit의 갯수))
- H : 32(batch size) 64(sequence의 길이) 512(LSTM unit수)
- h(s0) : 32(batch size) * 512 (LSTM unit수)
- c(c0) : 32(batch size) * 512 (LSTM unit수)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class Encoder(tf.keras.Model):
def __init__(self):
super(Encoder, self).__init__()
# 2000개의 단어들을 64크기의 vector로 Embedding해줌.
self.emb = tf.keras.layers.Embedding(NUM_WORDS, 64)
# return_state는 return하는 Output에 최근의 state를 더해주느냐에 대한 옵션
# 즉, Hidden state와 Cell state를 출력해주기 위한 옵션이라고 볼 수 있다.
# default는 False이므로 주의하자!
# return_sequence=True로하는 이유는 Attention mechanism을 사용할 때 우리가 key와 value는
# Encoder에서 나오는 Hidden state 부분을 사용했어야 했다. 그러므로 모든 Hidden State를 사용하기 위해 바꿔준다.
self.lstm = tf.keras.layers.LSTM(512, return_sequences=True, return_state=True)
def call(self, x, training=False, mask=None):
x = self.emb(x)
H, h, c = self.lstm(x)
return H, h, c
Decoder
LSTM 다음에 Attention 구조를 넣어주고, Encoder의 출력 중 모든 sequence의 Hidden State를 모아놓은 H와 s0, c0, shifted Output을 받아서 Attention value를 구하기 위한 코드를 수정시킨다.
Dimension :
- x : shifted_labels로 맨마지막을 제외한 나머지데이터들 => 32(batch szie) * 64(sequence의 길이)
- s0 : 이전 step의 hidden state => 32(batch size) * 512(LSTM의 Unit 갯수)
- c0 : 이전 step의 cell state => 32(batch size) * 512(LSTM의 Unit 갯수)
- H : Encoder단의 모든 Hidden state를 모은 것 => 32(batch size) 64(sequence의 길이) 512(LSTM의 Feature의 갯수)
embedding 결과 => 32(batch size) 64(sequence의 길이) 64(Embedding Feature의 수)
LSTM의 결과의 Dimension : (32(batch_szie), 64(sequence의 길이), 512(LSTM unit의 갯수))
- S : 32(batch size) 64(sequence의 길이) 512(LSTM unit수)
- h : 32(batch size) * 512 (LSTM unit수)
- c : 32(batch size) * 512 (LSTM unit수)
S_의 Dimension: 32(batch size) 64(sequence의 길이) 512(LSTM unit 수)
- A의 Dimension: 32(batch size) 64(sequence의 길이) 512(LSTM unit 수)
- y의 Dimension: 32(batch size) 64(sequence의 길이) 1024
1 | class Decoder(tf.keras.Model): |
Seq2seq
- 이전의 코드에서 encoder의 출력에 전체 Hidden State를 모아놓은 것과 decoder의 입력으로 이값을 받는 코드를 추가해주었다.
1 | class Seq2seq(tf.keras.Model): |