# Day_28_02_chosun.py
import tensorflow as tf
import numpy as np
import re
# 파일 다운로드
# url = 'http://bit.ly/2Mc3SOV'
# file_path = tf.keras.utils.get_file(
# 'chosun.txt', url, cache_dir='.', cache_subdir='data')
# print(file_path)
# str 정리 한글처리부분 ( 불용어 )
def clean_str(string):
string = re.sub(r"[^가-힣0-9]", " ", string)
# string = re.sub(r",", " , ", string)
# string = re.sub(r"!", " ! ", string)
# string = re.sub(r"\(", " \( ", string)
# string = re.sub(r"\)", " \) ", string)
# string = re.sub(r"\?", " \? ", string)
string = re.sub(r"\s{2,}", " ", string)
return string.strip()
def get_data():
f = open('data/chosun.txt', 'r', encoding='utf-8')
long_text = f.read()
long_text = clean_str(long_text) # 불용어 제거
long_text = long_text[:1000] # 데이터 너무 많이 써서 자를꺼임
f.close()
tokens = long_text.split()
voca = sorted(set(tokens)) + ['UNK']
return tokens, voca
def model_chosun_1():
# 긴 문장 -> 토큰 분리 -> 인덱스로 변환 -> x, y 생성
tokens, vocab = get_data()
# print(len(tokens), len(vocab)) # 338 113
word2idx = {w: i for i, w in enumerate(vocab)}
# idx2work = {w: i for i, w in enumerate(vocab)}
idx2word = np.array(vocab) # 0~112 까지 숫자가 단어가 됨 디코딩 할 때 사용
# print(tokens[:5]) # ['태조', '이성계', '선대의', '가계', '목조']
# print(list(work2idx.items())[:3]) # [(',', 0), ('001', 1), ('002', 2)]
# print(idx2work[:3]) # [',' '001' '002']
tokens_idx = [word2idx[w] for w in tokens]
# tokens_idx = [vocab.index(w) for w in tokens] # 이렇게도 쓸 수 있지만 위에가 더 좋음
# 25글자로 그 다음 글자 예측
seq_len = 25
vocab_size = len(vocab)
# x 로 y 추측
x = [tokens_idx[i: i + seq_len] for i in range(len(tokens) - seq_len)]
y = [tokens_idx[i + seq_len] for i in range(len(tokens) - seq_len)]
# ---------- 19_03 에서 가져옴 ------------- #
# add 대신 처음부터 넣어줄 수 있음
model = tf.keras.Sequential([
tf.keras.layers.Input(shape=[seq_len]),
tf.keras.layers.Embedding(vocab_size, 100),
tf.keras.layers.LSTM(128, return_sequences=False),
tf.keras.layers.Dense(vocab_size, activation='softmax')
])
model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.01),
loss=tf.keras.losses.sparse_categorical_crossentropy, # 숫자 하나의 배열 사용하니깐
metrics=['acc'])
# 훈련
model.fit(x, y, epochs=10, batch_size=32, verbose=0)
model.save('data/chosun_model_1.h5')
# sent = '동헌에 나가 활을 쏘다'
# act_like_writer_1(sent, model, word2idx, idx2word, seq_len)
def model_chosun_2():
# 긴 문장 -> 토큰 분리 -> 인덱스로 변환 -> x, y 생성
tokens, vocab = get_data()
word2idx = {w: i for i, w in enumerate(vocab)}
idx2word = np.array(vocab) # 0~112 까지 숫자가 단어가 됨 디코딩 할 때 사용
tokens_idx = [word2idx[w] for w in tokens] # 숫자로 된 토큰 만듬
# 25글자로 그 다음 글자 예측
seq_len = 25
vocab_size = len(vocab)
# x = [tokens_idx[i: i + seq_len] for i in range(len(tokens) - seq_len)]
# y = [tokens_idx[i + seq_len] for i in range(len(tokens) - seq_len)]
# ---------------------------------------------------- #
# Dataset 생성 -> (seq_len + 1) 분할 -> (x, y,) 튜플 변환 -> shuffle -> 배치 크기 분할 -> 원하는 만큼 반복
sent_slices = tf.data.Dataset.from_tensor_slices(tokens_idx) # 변환하고 싶은 데이터 전달
# print(type(sent_slices))
# print(sent_slices.take(2)) # take == 이터레이터(Iterator)
# Dataset은 모두다 take 함수를 가지고 있음 활용할 줄 알아야 함
# for take in sent_slices.take(2):
# print(take.numpy(), take)
# 194 tf.Tensor(194, shape=(), dtype=int32)
# 149 tf.Tensor(149, shape=(), dtype=int32)
# 내가 지정한 크기대로 자르는 것 슬라이딩 아님 그래서 데이터 숫자 줄어들었음
# 좋은 코드는 아닌거 같음 왜? 다른 것들은 예측 결과를 얻을 수 없으니깐
sent_sequences = sent_slices.batch(seq_len + 1, drop_remainder=True) # 옵션은 자르고 남은 자투리 버리겠다 선언한 것
# for take in sent_sequences.take(2):
# print(take.numpy())
# [194 149 104 10 74 154 174 ... 50 137 106 152 130 207 46 47]
# [155 166 46 30 145 34 207 ... 90 207 159 141 114 123 84 195]
# 데이터 재 정의 튜플로 묶음
sent_xy = sent_sequences.map(lambda chunk: (chunk[:-1], chunk[-1]))
# for xx, yy in sent_xy.take(2):
# print(xx.numpy(), yy.numpy())
# [194 149 104 10 74 154 174 ... 50 137 106 152 130 207 46] 47
# [155 166 46 30 145 34 207 ... 90 207 159 141 114 123 84] 195
# 섞어준다면 좀 더 좋은 효과를 낼 수 있을 것이라고 예상
step_per_epoch = len(tokens_idx) // (seq_len + 1) # 최소의 기준은 매계체의 수 -> 중복된 데이터가 뽑이지 않도록
sent_shuffled = sent_xy.shuffle(buffer_size=step_per_epoch) # 버퍼 사이즈가 너무 작으면 제대로 셔플 안됨
# for xx, yy in sent_shuffled.take(2):
# print(xx.numpy(), yy.numpy())
# [ 41 32 51 207 132 72 41 ... 137 65 168 169 122 72 41] 170
# [117 91 98 48 207 163 136 ... 126 207 150 136 41 151 207] 188
# 데이터를 원하는 만큼 묶을 것임
batch_size = 32
sent_batches = sent_shuffled.batch(batch_size) # 32개의 토큰을 반환 할 것임
# for xx, yy in sent_batches.take(2):
# print(xx.shape, yy.shape) # (12, 25) (12,)
# print(xx.numpy(), yy.numpy())
# 무제한 반복
sent_infinite = sent_batches.repeat() # 여기 카운터를 넣으면 그 숫자만큼 반복
model = tf.keras.Sequential([
tf.keras.layers.Input(shape=[seq_len]),
tf.keras.layers.Embedding(vocab_size, 100),
tf.keras.layers.LSTM(128, return_sequences=False),
tf.keras.layers.Dense(vocab_size, activation='softmax')
])
model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.01),
loss=tf.keras.losses.sparse_categorical_crossentropy, # 숫자 하나의 배열 사용하니깐
metrics=['acc'])
# 훈련
# step_per_epoch 한번 에 몇번 돌아가는지 알려줘야함 (step 갯수)
model.fit(sent_infinite, epochs=10, steps_per_epoch=step_per_epoch, verbose=0) # 한번에 몇개씩 가져오는지 알고 있기 때문에 배치 사이즈 x
model.save('data/chosun_model_2.h5')
sent = '동헌에 나가 활을 쏘다'
act_like_writer_1(sent, model, word2idx, idx2word, seq_len)
def act_like_writer_1(sent, model, word2idx, idx2word, seq_len):
current = sent.split()
# 하나씩 앞에꺼 25개를 계속 보면서 예측해 나갈 것임
for i in range(100):
tokens = current[-seq_len:] # seq_len 보다 남은 것이 적어도 error 나지 않음
tokens_idx = [word2idx[w] if w in word2idx else word2idx['UNK'] for w in tokens]
# pad -> 2차원 데이터를 처리함 그래서 tokens_idx 에 대괄호 넣어준 것임
tokens_pad = tf.keras.preprocessing.sequence.pad_sequences(
[tokens_idx], maxlen=seq_len, value=word2idx['UNK'] # padding 값에 내가 지정한 값 넣을꺼임
)
preds = model.predict(tokens_pad)
# print(preds.shape) # (1, 113)
# preds_arg = np.argmax(preds, axis=1)
preds_arg = np.argmax(preds[0])
# print(preds_arg) # 4
current.append(idx2word[preds_arg])
print(current)
def act_like_writer_2(sent, model, word2idx, idx2word, seq_len):
TOKEN_UNK = word2idx['UNK']
# 내가 가진 sentense 만큼 토큰으로 만든후에 숫자로 바꿈
current = [word2idx[w] if w in word2idx else TOKEN_UNK for w in sent.split()]
filed = seq_len - len(current)
if filed <= 0:
filed = 0
else:
# current = current + [TOKEN_UNK] * filed # 문제가 어려워 질 것임 중간 언노운이 너무 많이 나와 결과 도출 힘듬
current = [TOKEN_UNK] * filed + current # 앞에 있는 언노운 건너뛰면 됨
# print(current)
# 하나씩 앞에꺼 25개를 계속 보면서 예측해 나갈 것임
for i in range(100):
tokens_idx = current[-seq_len:] # seq_len 보다 남은 것이 적어도 error 나지 않음
# 파이썬 내부에서 인덱싱하는 가온데 에러가 있는거 같다고 생각함 -> 위에꺼랑 비교해서 np.array 감싸줘야함
tokens_pad = np.array([tokens_idx]) # 2차원으로 만들어줌
preds = model.predict(tokens_pad)
preds_arg = np.argmax(preds[0])
# print(i, preds_arg, idx2word[preds_arg]) #
current.append(preds_arg)
valid = current[filed + len(sent.split()):]
print(sent, ' '.join(idx2word[valid]))
# 모델을 읽어와서 전달받은 문장으로부터 새로운 문장을 생성하는 함수를 만드세요
def load_model(sent, model_path):
tokens, vocab = get_data()
word2idx = {w: i for i, w in enumerate(vocab)}
idx2word = np.array(vocab)
model = tf.keras.models.load_model(model_path)
act_like_writer_2(sent, model, word2idx, idx2word, seq_len=25)
# model_chosun_1()
# model_chosun_2()
# load_model('태조 이성계 아들 이방헌', 'data/chosun_model_1.h5')
load_model('태조 이성계 아들 이방헌', 'data/chosun_model_2.h5')