인공지능/CNN

Keras CNN functional로 글써보기

쿠와와 2020. 12. 10. 22:31
# 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')



'인공지능 > CNN' 카테고리의 다른 글

이미지 증식과 분석, 사전학습된 것 적용시키기 (전이 학습)  (0) 2020.12.09
이미지 증식  (0) 2020.12.09
이미지 사이즈 조절 및 이미지 분석 (feat. VGG)  (0) 2020.12.04
VGG  (0) 2020.12.04
LeNet5  (0) 2020.12.04