A.I
Explolation 6 작사가 만들기 본문
작사가 인공지능 만들기¶
1. 시퀀스¶
- 시퀀스는 데이터의 나열로 정렬과는 다르다
- 종류에는 리스트(list), 튜플(tuple), 레인지(range), 문자열(string)이 있다.
- 시퀀스 명령어
- sum(number_list)
- center(['가', '나', '다', '라', '마']) = '다'
- mirror([1, 2, 3]) = [1, 2, 3, 2, 1]
- minmax([92, -21, 0, 104, 51, 76, -92]) = [-92, 104]
- mean([92, -21, 0, 104, 51, 76, -92]) = 30.0
- append(x) 요소 x를 시퀀스의 끝(오른쪽)에 추가
- insert(i, x) 요소 x를 시퀀스의 i 위치에 삽입
- extend(seq) 대상 시퀀스를 시퀀스의 끝에 연결 , += 로 대체가능
- pop() 시퀀스의 마지막 요소를 꺼냄
- remove(x) 시퀀스에서 요소 x를 찾아 처음 발견된 것을 제거
- clear() 시퀀스의 모든 요소를 제거
- range(시작, 종료, 간격)) 시작값부터, 종료값에 이르기 전의 간격만큼씩 증가하는 등차수열 시퀀스를 생성
- tuple('일월화수목금토') = ('일', '월', '화', '수', '목', '금', '토')
- '/'.join(('가난하다고', '외로움을', '모르겠는가')) = '가난하다고/외로움을/모르겠는가'
In [1]:
sentence = " 나는 밥을 먹었다 "
source_sentence = "<start>" + sentence
target_sentence = sentence + "<end>"
print("Source 문장:", source_sentence)
print("Target 문장:", target_sentence)
Source 문장: <start> 나는 밥을 먹었다 Target 문장: 나는 밥을 먹었다 <end>
2. 실습 데이터 다듬기¶
mkdir -p ~/aiffel/lyricist/data
mkdir -p ~/aiffel/lyricist/models
wget https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt
mv shakespeare.txt ~/aiffel/lyricist/data
In [2]:
import re # 정규표현식을 위한 Regex 지원 모듈 (문장 데이터를 정돈하기 위해)
import numpy as np # 변환된 문장 데이터(행렬)을 편하게 처리하기 위해
import tensorflow as tf # 대망의 텐서플로우!
import os
# 파일을 읽기모드로 열어 봅니다.
file_path = os.getenv('HOME') + '/aiffel/lyricist/data/shakespeare.txt'
with open(file_path, "r") as f:
raw_corpus = f.read().splitlines() # 텍스트를 라인 단위로 끊어서 list 형태로 읽어옵니다.
print(raw_corpus[:9]) # 앞에서부터 10라인만 화면에 출력해 볼까요?
['First Citizen:', 'Before we proceed any further, hear me speak.', '', 'All:', 'Speak, speak.', '', 'First Citizen:', 'You are all resolved rather to die than to famish?', '']
In [3]:
for idx, sentence in enumerate(raw_corpus):
if len(sentence) == 0: continue # 길이가 0인 문장은 건너뜁니다.
if sentence[-1] == ":": continue # 문장의 끝이 : 인 문장은 건너뜁니다.
if idx > 9: break # 일단 문장 10개만 확인해 볼 겁니다.
print(sentence)
Before we proceed any further, hear me speak. Speak, speak. You are all resolved rather to die than to famish?
In [4]:
def preprocess_sentence(sentence):
sentence = sentence.lower().strip() # 소문자로 바꾸고 양쪽 공백을 삭제
# 아래 3단계를 거쳐 sentence는 스페이스 1개를 delimeter로 하는 소문자 단어 시퀀스로 바뀝니다.
sentence = re.sub(r"([?.!,¿])", r" \1 ", sentence) # 패턴의 특수문자를 만나면 특수문자 양쪽에 공백을 추가
sentence = re.sub(r'[" "]+', " ", sentence) # 공백 패턴을 만나면 스페이스 1개로 치환
sentence = re.sub(r"[^a-zA-Z?.!,¿]+", " ", sentence) # a-zA-Z?.!,¿ 패턴을 제외한 모든 문자(공백문자까지도)를 스페이스 1개로 치환
sentence = sentence.strip()
sentence = '<start> ' + sentence + ' <end>' # 이전 스텝에서 본 것처럼 문장 앞뒤로 <start>와 <end>를 단어처럼 붙여 줍니다
return sentence
print(preprocess_sentence("This @_is ;;;sample sentence.")) # 이 문장이 어떻게 필터링되는지 확인해 보세요.
<start> this is sample sentence . <end>
In [5]:
corpus = []
for sentence in raw_corpus:
if len(sentence) == 0: continue
if sentence[-1] == ":": continue
corpus.append(preprocess_sentence(sentence))
corpus[:10]
Out[5]:
['<start> before we proceed any further , hear me speak . <end>', '<start> speak , speak . <end>', '<start> you are all resolved rather to die than to famish ? <end>', '<start> resolved . resolved . <end>', '<start> first , you know caius marcius is chief enemy to the people . <end>', '<start> we know t , we know t . <end>', '<start> let us kill him , and we ll have corn at our own price . <end>', '<start> is t a verdict ? <end>', '<start> no more talking on t let it be done away , away ! <end>', '<start> one word , good citizens . <end>']
2-1. 벡터화¶
In [6]:
def tokenize(corpus):
# 텐서플로우에서 제공하는 Tokenizer 패키지를 생성
tokenizer = tf.keras.preprocessing.text.Tokenizer(
num_words=7000, # 전체 단어의 개수
filters=' ', # 별도로 전처리 로직을 추가할 수 있습니다. 이번에는 사용하지 않겠습니다.
oov_token="<unk>" # out-of-vocabulary, 사전에 없었던 단어는 어떤 토큰으로 대체할지
)
tokenizer.fit_on_texts(corpus) # 우리가 구축한 corpus로부터 Tokenizer가 사전을 자동구축하게 됩니다.
# 이후 tokenizer를 활용하여 모델에 입력할 데이터셋을 구축하게 됩니다.
tensor = tokenizer.texts_to_sequences(corpus) # tokenizer는 구축한 사전으로부터 corpus를 해석해 Tensor로 변환합니다.
# 입력 데이터의 시퀀스 길이를 일정하게 맞추기 위한 padding 메소드를 제공합니다.
# maxlen의 디폴트값은 None입니다. 이 경우 corpus의 가장 긴 문장을 기준으로 시퀀스 길이가 맞춰집니다.
tensor = tf.keras.preprocessing.sequence.pad_sequences(tensor, padding='post')
print(tensor,tokenizer)
return tensor, tokenizer
tensor, tokenizer = tokenize(corpus)
[[ 2 143 40 ... 0 0 0] [ 2 110 4 ... 0 0 0] [ 2 11 50 ... 0 0 0] ... [ 2 149 4553 ... 0 0 0] [ 2 34 71 ... 0 0 0] [ 2 945 34 ... 0 0 0]] <keras_preprocessing.text.Tokenizer object at 0x7f045208a550>
In [7]:
print(tensor[:3, :10])
[[ 2 143 40 933 140 591 4 124 24 110] [ 2 110 4 110 5 3 0 0 0 0] [ 2 11 50 43 1201 316 9 201 74 9]]
In [8]:
# 단어사전 보기
for idx in tokenizer.index_word:
print(idx, ":", tokenizer.index_word[idx])
if idx >= 10: break
1 : <unk> 2 : <start> 3 : <end> 4 : , 5 : . 6 : the 7 : and 8 : i 9 : to 10 : of
In [9]:
src_input = tensor[:, :-1] # tensor에서 마지막 토큰을 잘라내서 소스 문장을 생성합니다. 마지막 토큰은 <end>가 아니라 <pad>일 가능성이 높습니다.
tgt_input = tensor[:, 1:] # tensor에서 <start>를 잘라내서 타겟 문장을 생성합니다.
print(src_input[0])
print(tgt_input[0])
[ 2 143 40 933 140 591 4 124 24 110 5 3 0 0 0 0 0 0 0 0] [143 40 933 140 591 4 124 24 110 5 3 0 0 0 0 0 0 0 0 0]
2-2. 데이터셋 구성¶
In [10]:
BUFFER_SIZE = len(src_input)
BATCH_SIZE = 256
steps_per_epoch = len(src_input) // BATCH_SIZE
VOCAB_SIZE = tokenizer.num_words + 1 # tokenizer가 구축한 단어사전 내 7000개와, 여기 포함되지 않은 0:<pad>를 포함하여 7001개
dataset = tf.data.Dataset.from_tensor_slices((src_input, tgt_input)).shuffle(BUFFER_SIZE)
dataset = dataset.batch(BATCH_SIZE, drop_remainder=True)
dataset
Out[10]:
<BatchDataset shapes: ((256, 20), (256, 20)), types: (tf.int32, tf.int32)>
2-3. 인공지능 학습시키기¶
In [11]:
class TextGenerator(tf.keras.Model):
def __init__(self, vocab_size, embedding_size, hidden_size):
super(TextGenerator, self).__init__()
self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_size)
self.rnn_1 = tf.keras.layers.LSTM(hidden_size, return_sequences=True)
self.rnn_2 = tf.keras.layers.LSTM(hidden_size, return_sequences=True)
self.linear = tf.keras.layers.Dense(vocab_size)
def call(self, x):
out = self.embedding(x)
out = self.rnn_1(out)
out = self.rnn_2(out)
out = self.linear(out)
return out
embedding_size = 256 # 워드 벡터의 차원수, 단어가 추상적으로 표현되는 크기
hidden_size = 1024 # 얼마나 많은 일꾼을 둘 것인가?
model = TextGenerator(tokenizer.num_words + 1, embedding_size , hidden_size)
In [12]:
for src_sample, tgt_sample in dataset.take(1): break
model(src_sample)
# Tensor 의미 Dense 레이어의 출력 차원수=7001, 256은 이전 스텝에서 지정한 배치 사이즈,
# 20 =LSTM은 자신에게 입력된 시퀀스의 길이만큼 동일한 길이의 시퀀스를 출력한다는 의미, 위에서 max_len의 값
Out[12]:
<tf.Tensor: shape=(256, 20, 7001), dtype=float32, numpy= array([[[-1.09503744e-04, -2.66038027e-04, 1.61262171e-04, ..., 7.12273468e-05, -1.78884380e-04, -7.88546677e-05], [-5.24945906e-04, -6.16457022e-04, 1.56712747e-04, ..., 2.88078678e-04, -2.54918705e-04, -1.71693959e-04], [-3.83677077e-04, -1.41947845e-03, 2.48901706e-05, ..., 1.14092560e-04, -3.45633336e-04, -1.00471596e-04], ..., [ 3.41132196e-04, 2.80687062e-04, -1.27682032e-03, ..., -3.37913958e-03, -2.53436365e-03, 6.23094337e-03], [ 3.43901396e-04, 2.23180919e-04, -1.29413244e-03, ..., -3.43460031e-03, -2.64650327e-03, 6.49290113e-03], [ 3.46364046e-04, 1.59334901e-04, -1.31501467e-03, ..., -3.46608646e-03, -2.74379947e-03, 6.70350762e-03]], [[-1.09503744e-04, -2.66038027e-04, 1.61262171e-04, ..., 7.12273468e-05, -1.78884380e-04, -7.88546677e-05], [-3.53556941e-04, -5.21699025e-04, 1.98064954e-04, ..., -1.13553679e-05, -1.94126551e-04, -1.68330225e-04], [-4.83308046e-04, -7.80841801e-04, 2.58042419e-04, ..., 4.73929358e-05, 4.20798096e-05, -2.15771186e-04], ..., [ 3.00826650e-04, 7.37190945e-04, -8.61183915e-04, ..., -3.47979111e-03, -2.64903065e-03, 6.05806941e-03], [ 3.00999614e-04, 5.93947130e-04, -9.22160980e-04, ..., -3.59214540e-03, -2.78340932e-03, 6.39873603e-03], [ 3.02087137e-04, 4.57974151e-04, -9.86588188e-04, ..., -3.65778897e-03, -2.89457804e-03, 6.66888105e-03]], [[-1.09503744e-04, -2.66038027e-04, 1.61262171e-04, ..., 7.12273468e-05, -1.78884380e-04, -7.88546677e-05], [-3.84492945e-04, -3.64089268e-04, 3.25991976e-04, ..., 1.10048895e-05, -3.57718178e-04, -8.68598290e-05], [-6.77279313e-04, -6.82862010e-04, 3.08692361e-05, ..., 1.59115632e-04, -2.23534749e-04, -4.47068043e-04], ..., [ 1.35950453e-03, 1.02694286e-03, -5.78598818e-04, ..., -2.85810325e-03, -1.90688437e-03, 4.15437715e-03], [ 1.26881583e-03, 8.75800324e-04, -6.60347927e-04, ..., -3.12676746e-03, -2.13274430e-03, 4.78356145e-03], [ 1.17001426e-03, 7.27669336e-04, -7.44504738e-04, ..., -3.32794967e-03, -2.32544937e-03, 5.31776017e-03]], ..., [[-1.09503744e-04, -2.66038027e-04, 1.61262171e-04, ..., 7.12273468e-05, -1.78884380e-04, -7.88546677e-05], [ 1.50320528e-04, -5.80814201e-04, 9.92836503e-05, ..., 2.46621930e-04, -9.41252219e-05, -1.32977933e-04], [ 5.68573014e-04, -8.60746892e-04, 4.05564060e-05, ..., 4.33892448e-04, 2.04501703e-04, -3.80947575e-04], ..., [-1.26197219e-05, 1.31433026e-03, -4.90649079e-04, ..., -5.93723787e-04, -1.63870002e-03, 6.01561274e-04], [ 1.05442734e-04, 1.32487447e-03, -5.94703015e-04, ..., -1.19170302e-03, -1.76686281e-03, 1.46705098e-03], [ 1.93702072e-04, 1.27013866e-03, -6.77839911e-04, ..., -1.74174097e-03, -1.91307196e-03, 2.33437261e-03]], [[-1.09503744e-04, -2.66038027e-04, 1.61262171e-04, ..., 7.12273468e-05, -1.78884380e-04, -7.88546677e-05], [ 8.88003706e-06, -2.79865082e-04, 2.17730936e-04, ..., 3.75051721e-04, -4.56935260e-04, -8.35519313e-05], [ 4.81599476e-04, -1.44774618e-04, 4.81120573e-04, ..., 6.81629404e-04, -5.78431180e-04, -4.18748532e-04], ..., [ 6.04796398e-04, 3.65391170e-04, -1.57054595e-03, ..., -2.94022937e-03, -2.24803947e-03, 5.70444902e-03], [ 5.90451935e-04, 3.26276291e-04, -1.52789277e-03, ..., -3.12726921e-03, -2.38775276e-03, 6.09018700e-03], [ 5.72171703e-04, 2.75272294e-04, -1.49850734e-03, ..., -3.26383975e-03, -2.51251995e-03, 6.40389137e-03]], [[-1.09503744e-04, -2.66038027e-04, 1.61262171e-04, ..., 7.12273468e-05, -1.78884380e-04, -7.88546677e-05], [-6.34297685e-05, -5.72176185e-04, 1.91584200e-04, ..., 2.09278063e-04, -4.90543025e-04, -2.15708860e-04], [-5.06511860e-05, -6.17284968e-04, 8.03251241e-05, ..., 6.42384577e-04, -8.86102440e-04, -8.76478680e-06], ..., [ 9.91196488e-04, 1.15849299e-03, -7.06313294e-04, ..., -1.87489705e-03, -1.39990891e-03, 3.22627951e-03], [ 9.89660039e-04, 1.10199174e-03, -7.49616651e-04, ..., -2.33235327e-03, -1.63205678e-03, 3.94317554e-03], [ 9.62445571e-04, 1.01856375e-03, -7.90783553e-04, ..., -2.70124571e-03, -1.84647460e-03, 4.58181184e-03]]], dtype=float32)>
In [13]:
model.summary()
Model: "text_generator" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= embedding (Embedding) multiple 1792256 _________________________________________________________________ lstm (LSTM) multiple 5246976 _________________________________________________________________ lstm_1 (LSTM) multiple 8392704 _________________________________________________________________ dense (Dense) multiple 7176025 ================================================================= Total params: 22,607,961 Trainable params: 22,607,961 Non-trainable params: 0 _________________________________________________________________
In [14]:
optimizer = tf.keras.optimizers.Adam()
loss = tf.keras.losses.SparseCategoricalCrossentropy(
from_logits=True,
reduction='none'
)
model.compile(loss=loss, optimizer=optimizer)
model.fit(dataset, epochs=10)
Epoch 1/10 93/93 [==============================] - 13s 141ms/step - loss: 3.4679 Epoch 2/10 93/93 [==============================] - 13s 142ms/step - loss: 2.7972 Epoch 3/10 93/93 [==============================] - 13s 143ms/step - loss: 2.6873 Epoch 4/10 93/93 [==============================] - 13s 144ms/step - loss: 2.5935 Epoch 5/10 93/93 [==============================] - 14s 147ms/step - loss: 2.5330 Epoch 6/10 93/93 [==============================] - 13s 143ms/step - loss: 2.4800 Epoch 7/10 93/93 [==============================] - 13s 144ms/step - loss: 2.4189 Epoch 8/10 93/93 [==============================] - 13s 145ms/step - loss: 2.3633 Epoch 9/10 93/93 [==============================] - 13s 145ms/step - loss: 2.3126 Epoch 10/10 93/93 [==============================] - 13s 145ms/step - loss: 2.2646
Out[14]:
<tensorflow.python.keras.callbacks.History at 0x7f04527c34d0>
In [16]:
def generate_text(model, tokenizer, init_sentence="<start>", max_len=20):
# 테스트를 위해서 입력받은 init_sentence도 일단 텐서로 변환합니다.
test_input = tokenizer.texts_to_sequences([init_sentence])
test_tensor = tf.convert_to_tensor(test_input, dtype=tf.int64)
end_token = tokenizer.word_index["<end>"]
# 텍스트를 실제로 생성할때는 루프를 돌면서 단어 하나씩 생성해야 합니다.
while True:
predict = model(test_tensor) # 입력받은 문장의 텐서를 입력합니다.
predict_word = tf.argmax(tf.nn.softmax(predict, axis=-1), axis=-1)[:, -1] # 우리 모델이 예측한 마지막 단어가 바로 새롭게 생성한 단어가 됩니다.
# 우리 모델이 새롭게 예측한 단어를 입력 문장의 뒤에 붙여 줍니다.
test_tensor = tf.concat([test_tensor,
tf.expand_dims(predict_word, axis=0)], axis=-1)
# 우리 모델이 <end>를 예측했거나, max_len에 도달하지 않았다면 while 루프를 또 돌면서 다음 단어를 예측해야 합니다.
if predict_word.numpy()[0] == end_token: break
if test_tensor.shape[1] >= max_len: break
generated = ""
# 생성된 tensor 안에 있는 word index를 tokenizer.index_word 사전을 통해 실제 단어로 하나씩 변환합니다.
for word_index in test_tensor[0].numpy():
generated += tokenizer.index_word[word_index] + " "
return generated # 이것이 최종적으로 모델이 생성한 자연어 문장입니다.
In [17]:
generate_text(model, tokenizer, init_sentence="<start> he")
Out[17]:
'<start> he is a <unk> of the <unk> of the king , <end> '
3. 작사가 만들기¶
- wget https://aiffelstaticprd.blob.core.windows.net/media/documents/song_lyrics.zip
- unzip song_lyrics.zip -d ~/aiffel/lyricist/data/lyrics #lyrics 폴더에 압축풀기
In [114]:
import glob # txt파일 읽어오는 패키지
import os
txt_file_path = os.getenv('HOME')+'/aiffel/lyricist/data/lyrics/*'
txt_list = glob.glob(txt_file_path)
raw_corpus = []
# 여러개의 txt 파일을 모두 읽어서 raw_corpus 에 담습니다.
for txt_file in txt_list:
with open(txt_file, "r") as f:
raw = f.read().splitlines()
raw_corpus.extend(raw)
print("데이터 크기:", len(raw_corpus))
print("Examples:\n", raw_corpus[:3])
데이터 크기: 187088 Examples: ['The Cat in the Hat', 'By Dr. Seuss', 'The sun did not shine.']
In [115]:
for idx, sentence in enumerate(raw_corpus):
if len(sentence) == 0: continue # 길이가 0인 문장은 건너뜁니다.
if sentence[-1] == ":": continue # 문장의 끝이 : 인 문장은 건너뜁니다.
if idx > 9: break # 일단 문장 10개만 확인해 볼 겁니다.
corpus[:10]
Out[115]:
['<start> the cat in the hat <end>', '<start> by dr . seuss <end>', '<start> the sun did not shine . <end>', '<start> it was too wet to play . <end>', '<start> so we sat in the house <end>', '<start> all that cold cold wet day . <end>', '<start> i sat there with sally . <end>', '<start> we sat there we two . <end>', '<start> and i said how i wish <end>', '<start> we had something to do ! <end>']
In [116]:
def preprocess_sentence(sentence):
sentence = sentence.lower().strip() # 소문자로 바꾸고 양쪽 공백을 삭제
# 아래 3단계를 거쳐 sentence는 스페이스 1개를 delimeter로 하는 소문자 단어 시퀀스로 바뀝니다.
sentence = re.sub(r"([?.!,¿])", r" \1 ", sentence) # 패턴의 특수문자를 만나면 특수문자 양쪽에 공백을 추가
sentence = re.sub(r'[" "]+', " ", sentence) # 공백 패턴을 만나면 스페이스 1개로 치환
sentence = re.sub(r"[^a-zA-Z?.!,¿]+", " ", sentence) # a-zA-Z?.!,¿ 패턴을 제외한 모든 문자(공백문자까지도)를 스페이스 1개로 치환
sentence = sentence.strip()
sentence = '<start> ' + sentence + ' <end>' # 이전 스텝에서 본 것처럼 문장 앞뒤로 <start>와 <end>를 단어처럼 붙여 줍니다
return sentence
print(preprocess_sentence("This @_is ;;;sample sentence.")) # 이 문장이 어떻게 필터링되는지 확인해 보세요.
<start> this is sample sentence . <end>
In [117]:
def tokenize(corpus):
# 텐서플로우에서 제공하는 Tokenizer 패키지를 생성
tokenizer = tf.keras.preprocessing.text.Tokenizer(
num_words=12000, # 전체 단어의 개수
filters=' ', # 별도로 전처리 로직을 추가할 수 있습니다. 이번에는 사용하지 않겠습니다.
oov_token="<unk>" # out-of-vocabulary, 사전에 없었던 단어는 어떤 토큰으로 대체할지
)
tokenizer.fit_on_texts(corpus) # 우리가 구축한 corpus로부터 Tokenizer가 사전을 자동구축하게 됩니다.
# 이후 tokenizer를 활용하여 모델에 입력할 데이터셋을 구축하게 됩니다.
tensor = tokenizer.texts_to_sequences(corpus) # tokenizer는 구축한 사전으로부터 corpus를 해석해 Tensor로 변환합니다.
# 입력 데이터의 시퀀스 길이를 일정하게 맞추기 위한 padding 메소드를 제공합니다.
# maxlen의 디폴트값은 None입니다. 이 경우 corpus의 가장 긴 문장을 기준으로 시퀀스 길이가 맞춰집니다.
tensor = tf.keras.preprocessing.sequence.pad_sequences(tensor, padding='post', maxlen=15)
print(tensor,tokenizer)
return tensor, tokenizer
tensor, tokenizer = tokenize(corpus)
[[ 2 6 903 ... 0 0 0] [ 2 122 2584 ... 0 0 0] [ 2 6 304 ... 0 0 0] ... [ 2 312 1 ... 0 0 0] [ 5 34 45 ... 1161 143 3] [ 5 34 45 ... 1161 143 3]] <keras_preprocessing.text.Tokenizer object at 0x7f02b07f3d90>
In [118]:
src_input = tensor[:, :-1] # tensor에서 마지막 토큰을 잘라내서 소스 문장을 생성합니다. 마지막 토큰은 <end>가 아니라 <pad>일 가능성이 높습니다.
tgt_input = tensor[:, 1:] # tensor에서 <start>를 잘라내서 타겟 문장을 생성합니다.
print(src_input[0])
print(tgt_input[0])
[ 2 6 903 14 6 1350 3 0 0 0 0 0 0 0] [ 6 903 14 6 1350 3 0 0 0 0 0 0 0 0]
In [119]:
BUFFER_SIZE = len(src_input)
BATCH_SIZE = 256
steps_per_epoch = len(src_input) // BATCH_SIZE
VOCAB_SIZE = tokenizer.num_words + 1
dataset = tf.data.Dataset.from_tensor_slices((src_input, tgt_input)).shuffle(BUFFER_SIZE)
dataset = dataset.batch(BATCH_SIZE, drop_remainder=True)
dataset
Out[119]:
<BatchDataset shapes: ((256, 14), (256, 14)), types: (tf.int32, tf.int32)>
In [120]:
from sklearn.model_selection import train_test_split
enc_train, enc_val, dec_train, dec_val = train_test_split(src_input,
tgt_input,
test_size=0.2,
random_state=7)
print("Source Train:", enc_train.shape)
print("Target Train:", dec_train.shape)
Source Train: (140599, 14) Target Train: (140599, 14)
In [121]:
class TextGenerator(tf.keras.Model):
def __init__(self, vocab_size, embedding_size, hidden_size):
super(TextGenerator, self).__init__()
self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_size)
self.rnn_1 = tf.keras.layers.LSTM(hidden_size, return_sequences=True)
self.rnn_2 = tf.keras.layers.LSTM(hidden_size, return_sequences=True)
self.linear = tf.keras.layers.Dense(vocab_size)
def call(self, x):
out = self.embedding(x)
out = self.rnn_1(out)
out = self.rnn_2(out)
out = self.linear(out)
return out
embedding_size = 512 # 워드 벡터의 차원수, 단어가 추상적으로 표현되는 크기
hidden_size = 1024 # 얼마나 많은 일꾼을 둘 것인가?
model = TextGenerator(tokenizer.num_words + 1, embedding_size , hidden_size)
print("ok")
ok
In [122]:
# the save point
checkpoint_dir = os.getenv('HOME')+'/aiffel/lyricist/models/lyricist'
cp_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_dir,
save_weights_only=True,
monitor='val_loss',
mode='auto',
save_best_only=True,
verbose=1)
print("✅")
✅
In [123]:
optimizer = tf.keras.optimizers.Adam()
loss = tf.keras.losses.SparseCategoricalCrossentropy(
from_logits=True,
reduction='none'
)
model.compile(loss=loss, optimizer=optimizer)
model.fit(dataset, epochs=10)
Epoch 1/10 686/686 [==============================] - 97s 142ms/step - loss: 3.5632 Epoch 2/10 686/686 [==============================] - 100s 146ms/step - loss: 3.0631 Epoch 3/10 686/686 [==============================] - 101s 148ms/step - loss: 2.8573 Epoch 4/10 686/686 [==============================] - 100s 146ms/step - loss: 2.6951 Epoch 5/10 686/686 [==============================] - 100s 146ms/step - loss: 2.5512 Epoch 6/10 686/686 [==============================] - 100s 146ms/step - loss: 2.4186 Epoch 7/10 686/686 [==============================] - 100s 146ms/step - loss: 2.2944 Epoch 8/10 686/686 [==============================] - 100s 146ms/step - loss: 2.1772 Epoch 9/10 686/686 [==============================] - 100s 146ms/step - loss: 2.0651 Epoch 10/10 686/686 [==============================] - 100s 146ms/step - loss: 1.9574
Out[123]:
<tensorflow.python.keras.callbacks.History at 0x7f032172c090>
In [124]:
def generate_text(model, tokenizer, init_sentence="<start>", max_len=20):
# 테스트를 위해서 입력받은 init_sentence도 일단 텐서로 변환합니다.
test_input = tokenizer.texts_to_sequences([init_sentence])
test_tensor = tf.convert_to_tensor(test_input, dtype=tf.int64)
end_token = tokenizer.word_index["<end>"]
# 텍스트를 실제로 생성할때는 루프를 돌면서 단어 하나씩 생성해야 합니다.
while True:
predict = model(test_tensor) # 입력받은 문장의 텐서를 입력합니다.
predict_word = tf.argmax(tf.nn.softmax(predict, axis=-1), axis=-1)[:, -1] # 우리 모델이 예측한 마지막 단어가 바로 새롭게 생성한 단어가 됩니다.
# 우리 모델이 새롭게 예측한 단어를 입력 문장의 뒤에 붙여 줍니다.
test_tensor = tf.concat([test_tensor,
tf.expand_dims(predict_word, axis=0)], axis=-1)
# 우리 모델이 <end>를 예측했거나, max_len에 도달하지 않았다면 while 루프를 또 돌면서 다음 단어를 예측해야 합니다.
if predict_word.numpy()[0] == end_token: break
if test_tensor.shape[1] >= max_len: break
generated = ""
# 생성된 tensor 안에 있는 word index를 tokenizer.index_word 사전을 통해 실제 단어로 하나씩 변환합니다.
for word_index in test_tensor[0].numpy():
generated += tokenizer.index_word[word_index] + " "
return generated # 이것이 최종적으로 모델이 생성한 자연어 문장입니다.
print("ok")
ok
In [125]:
generate_text(model, tokenizer, init_sentence="<start> i love", max_len=20)
Out[125]:
'<start> i love you , i m not gonna crack <end> '
'AIFFEL' 카테고리의 다른 글
Explolation 8 영화 추천 시스템 만들기 (0) | 2021.01.29 |
---|---|
Explolation7 닮은 꼴 연예인 찾기 (0) | 2021.01.26 |
Exploration5 오디오 음성 데이터 분류 (0) | 2021.01.19 |
Exploration4 영화 리뷰 감성 분류 (0) | 2021.01.14 |
Exploration 3 카메라 스티커 인식 (0) | 2021.01.12 |