Tensorflow 개발자 자격증 준비하기(4)

Tensorflow 개발자 자격증 준비하기(4)

Natural Language Processing

문자열 데이터를 받아 sarcastic한지 판별하는 문제를 풀어보자. 이전과는 다르게 자연어 처리 문제이다.

1. 문제

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
import json
import tensorflow as tf
import numpy as np
import urllib
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences


def solution_model():
url = 'https://storage.googleapis.com/download.tensorflow.org/data/sarcasm.json'
urllib.request.urlretrieve(url, 'sarcasm.json')

# DO NOT CHANGE THIS CODE OR THE TESTS MAY NOT WORK
vocab_size = 1000
embedding_dim = 16
max_length = 120
trunc_type='post'
padding_type='post'
oov_tok = "<OOV>"
training_size = 20000

sentences = []
labels = []
# YOUR CODE HERE


model = tf.keras.Sequential([
# YOUR CODE HERE. KEEP THIS OUTPUT LAYER INTACT OR TESTS MAY FAIL
tf.keras.layers.Dense(1, activation='sigmoid')
])
return model


# Note that you'll need to save your model as a .h5 like this
# This .h5 will be uploaded to the testing infrastructure
# and a score will be returned to you
if __name__ == '__main__':
model = solution_model()
model.save("mymodel.h5")

2. 데이터 가공

: sarcasm 학습데이터는 json 포맷이다. 받아온 sarcasm.json 파일을 로드해와 sentences와 labels로 나눈다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import json

# sarcasm 데이터 다운로드
urllib.request.urlretrieve('https://storage.googleapis.com/download.tensorflow.org/data/sarcasm.json', 'sarcasm.json')

with open('sarcasm.json') as file:
data = json.load(file)

# 데이터를 sentence와 label로 나눈다.
dataset = []
for elem in data:
sentences.append(elem['headline'])
labels.append(elem['is_sarcastic'])

# 훈련 데이터와 테스트 데이터로 분류
training_size = int(len(data)*0.2)
train_sentences = sentences[:training_size]
train_labels = labels[:training_size]
validation_sentences = sentences[training_size:]
validation_labels = labels[training_size:]

# 데이터셋은 numpy array이어야 한다.
train_labels = np.array(train_labels)
validation_labels = np.array(validation_labels)

sarcasm 데이터셋은 자연어처리를 실습하기 위한 예제이다. 따라서 이전 포스트의 자연어 처리와 관련된 이론내용들을 실제로 적용해볼 수 있다.

자연어 처리의 전처리

자연어 처리의 실습

자연어 처리의 전처리 단계는 다음과 같다.

자연어 처리의 전처리 단계 : 토큰화, 단어집합 생성, 정수인코딩, 패딩

아 맨날 까먹는다. 붕어인가ㅠ

[ 토큰화 ]

1
2
3
4
5
6
7
8
9
from tensorflow.keras.preprocessing.text import Tokenizer

# 변경 불가
vocab_size = 1000
oov_tok = "<OOV>"
max_length = 120
trunc_type='post'
padding_type='post'
embedding_dim = 16
  1. Tokenizer를 정의한다.

    1
    tokenizer = Tokenizer(num_words=vocab_size, oov_token='<OOV>')
    • num_words: 단어 집합 max 사이즈를 지정합니다. 가장 빈도수가 높은 단어부터 저장합니다.
    • oov_token: 단어 집합에 없는 단어를 어떻게 표기할 것인지 지정해줍니다.
  2. 학습시킬 대상 문장을 토큰화하고(단어 단위로 분리하고), 단어집합을 만든다.

    1
    2
    tokenizer.fit_on_texts(sentences)
    word_to_idx = tokenizer.word_index
  3. 학습 대상 문장을 각각의 고유한 정수로 매핑한다.

    1
    2
    train_sequences = tokenizer.texts_to_sequences(train_sentences)
    validation_sequences = tokenizer.texts_to_sequences(validation_sentences)

[ 패딩 ]

: pad_sequences 함수를 사용한다. 옵션값은 아래와 같다.

  • maxlen: 최대 문장 길이를 정의합니다. 최대 문장길이보다 길면, 잘라냅니다.
  • truncating: 문장의 길이가 maxlen보다 길 때 앞을 자를지 뒤를 자를지 정의합니다.
  • padding: 문장의 길이가 maxlen보다 짧을 때 채워줄 값을 앞을 채울지, 뒤를 채울지 정의합니다.
1
2
train_padded = pad_sequences(train_sequences, truncating=trunc_type, padding=padding_type, maxlen=max_length)
validation_padded = pad_sequences(validation_sequences, padding=padding_type, maxlen=max_length)

3. 모델 설계

: Bidirectional LSTM 사용한 모델을 설계한다. 원래 강의노트 모델이 좀 과적합되는 것 같아서 중간에 dropout하나 추가해줌.

1
2
3
4
5
6
7
8
9
model = tf.keras.Sequential([
tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length),
tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(32)),
tf.keras.layers.Dense(24, activation='relu'),
tf.keras.layers.Dropout(0.5),
tf.keras.layers.Dense(1, activation='sigmoid')
])

model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
1
2
3
4
5

625/625 [==============================] - ETA: 0s - loss: 0.3208 - accuracy: 0.8569
Epoch 00020: val_loss improved from 0.35426 to 0.35268, saving model to my_checkpoint3.ckpt
625/625 [==============================] - 565s 904ms/step - loss: 0.3208 - accuracy: 0.8569 - val_loss: 0.3527 - val_accuracy: 0.8393
Epoch 21/30

통과하는 baseline은 정확도 83퍼 중반, loss 0.36이하였다. 임베딩 차원이 너무 작아서 그런지 정확도가 잘 안오른다..ㅠㅠ

4. word2vec 적용

: 이전에 배웠던 word2vec 전처리를 추가해봤다. TED 강의 중 하나의 스크립트로 16차원의 임베딩 테이블을 학습한다.

영어/한국어 Word2Vec 실습

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
import re
from lxml import etree
import urllib.request
import zipfile
from nltk.tokenize import word_tokenize, sent_tokenize
from gensim.models import Word2Vec
import nltk

nltk.download('punkt')

# 데이터 다운로드
urllib.request.urlretrieve("https://wit3.fbk.eu/get.php?path=XML_releases/xml/ted_en-20160408.zip&filename=ted_en-20160408.zip", filename="ted_en-20160408.zip")

# xml 파일로부터 <content>와 </content> 사이의 내용만 가져온다.
with zipfile.ZipFile('ted_en-20160408.zip', 'r') as z:
target_text = etree.parse(z.open('ted_en-20160408.xml', 'r'))
parse_text = '\n'.join(target_text.xpath('//content/text()'))

# 정규 표현식의 sub 모듈을 통해 content 중간에 등장하는 (Audio), (Laughter) 등의 배경음 부분을 제거.
# 해당 코드는 괄호로 구성된 내용을 제거.
content_text = re.sub(r'\([^)]*\)', '', parse_text)

# 입력 코퍼스에 대해서 NLTK를 이용하여 문장 토큰화를 수행.
sent_text=sent_tokenize(content_text)

# 각 문장에 대해서 구두점을 제거하고, 대문자를 소문자로 변환.
normalized_text = []
for string in sent_text:
tokens = re.sub(r"[^a-z0-9]+", " ", string.lower())
normalized_text.append(tokens)

# 각 문장에 대해서 NLTK를 이용하여 단어 토큰화를 수행.
result = [word_tokenize(sentence) for sentence in normalized_text]

model = Word2Vec(sentences=result, size=16, window=5, min_count=5, workers=4, sg=0)
model.wv.save_word2vec_format('eng_w2v') # 워드 임베딩 저장

[Embedding Matrix 생성]

1
2
3
4
5
6
7
embedding_matrix = np.zeros((vocab_size, embedding_dim))

for word, i in word_to_idx.items():
if i >= vocab_size:
break
if word in word2vec.vocab:
embedding_matrix[i] = word2vec.word_vec(word)

TED 강연자료로 학습한 워드임베딩 값으로 모델 학습에 사용할 임베딩 테이블을 생성한다. 생성한 데이터셋 단어집합의 각 단어에 대해 해당 단어의 벡터값이 생성한 eng_w2v에 존재할 경우 임베딩테이블에 값을 넣어준다.

생성한 embedding matrix값은 모델의 Embedding layer에서 아래와 같이 설정함으로서 모델 학습에 활용할 수 있다.

1
tf.keras.layers.Embedding(vocab_size, embedding_dim, weights=[embedding_matrix], input_length=max_length, trainable=True, mask_zero=True)
  • weights 에 생성한 embedding matrix를 넣어준다.
  • trainable=True 로 두면 생성한 embedding matrix를 초기 임베딩 레이어 값으로 사용하되, 모델 학습에 따라서 값을 다시 학습한다. 원래대로라면 이미 만들어진 임베딩 매트릭스 값을 학습하지 않는것이 맞겠지만, 내 경우 TED로 생성한 embedding table이 당연하게도 엄청 좋지는 않아서… 그냥 모델이랑 같이 학습을 시켜주는게 더 효과가 좋았다.

결과적으로 초기값만 좀 달라진 셈인데 어쨌든 효과가 아예 없지는 않았다.

1
2
3
4
Epoch 8/30
625/625 [==============================] - ETA: 0s - loss: 0.3349 - accuracy: 0.8517
Epoch 00008: val_loss improved from 0.36791 to 0.36273, saving model to my_checkpoint2.ckpt
625/625 [==============================] - 123s 197ms/step - loss: 0.3349 - accuracy: 0.8517 - val_loss: 0.3627 - val_accuracy: 0.8344

댓글