2022년 8월 20일 토요일

tensorflow keras / RNN, LSTM char

 

RNN에서 대표적인 예제라고 한다면 text 예제가 많이 있습니다. 

앞에서 숫자 예제를 만들어 봤는데 이번에는 해당 예제를 변형하여 text예제를 만들어 볼까합니다.

text는 애국가가 나오고 다음 한 글자를 예측하는 형태가 됩니다.

소스 코드가 이전과 거의 비슷하기 때문에 입력 shape과 one hot encoding을 중점적으로 보면 쉽게 이해가 될것입니다.


입력 데이터

앞서 언급했듯이 입력은 애국가가 됩니다. 훈련을 위한 x , y 만들어야 하는데 여기 예제에서는 *(이전 데이터)입력 (time_step_size) 4개를 가지고 다음번 1개를 예측해야하므로 다 대 일(Many-to-One) 구조로 데이터를 만들었습니다.

원본 text

동해 물과 백두산이 마르고 닳도록
하느님이 보우하사 우리나라 만세.
...

x_data.txt

동해 물
해 물과
 물과 
물과 백
과 백두
 백두산
백두산이
...

y_data.txt

과
 
백
두
산
이
 
...


이러한 문자 처리에서는 몇가지 알아둬야 할 사항이 있습니다. 

1. python3 utf-8에서는 한글이 한개의 문자가 됩니다. 즉 len의 길이가 byte단위로 측정되는것이 아니기 길이가 1이 됩니다.

len("동해 물") 은 4가 되게 됩니다. (공백도 1이 됩니다.)

2. text학습을 위해서는 결국 숫자(tensor)로 변환해야 하는데 그것을 encoding이라고 부른다. 일종의 약속(변환 테이블)을 해야 합니다, 즉 가=1, 나=2 이런식으로 하게됩니다. 그런데 모든 문자에 대해서 코드화를 해놓으면 불필요한 메모리가 낭비가 되다보니 사용하는 문자들만 하게 됩니다. 즉 동=1, 해=2, 물=3 이런식으로 됩니다.

문자 기반으로 할 경우의 예를 위와 같이 들었고 단어 기반의 경우 단어 단위로 encoding을 하게 됩니다.

3. 엔코딩을 했더라도 한가지 추가 고민이 필요합니다. 동=1, 해=2, 물=3 로 했을때 1,3이 어떤 관계냐는것 입니다. 연산을 하게될텐데 y결과가 3이었다면 동=1 에 뭔가를 더 더해서 물=3이 될 수 있느냐는건데(예를들어서 동+동+동 을 하게되면 물이 되느냐 그런 질문입니다.) 문자에서는 전혀 관계가 없다는것 입니다. 그래서 코드화된 숫자를 다시 하나의 feature로 가지도록 one hot encoding을 사용하는것입니다.

이부분은 다음 함수를 이용해서 전환을 하게 됩니다. from tensorflow.keras.utils import to_categorical   


여기에서는 데이터 생성을 하면서 2번 엔코딩 테이블까지 만들어서 pickle로 저장하게 됩니다.

char_vocab = sorted(list(set(timed_y_data)))
vocab_size = len(char_vocab)
print(char_vocab)
print(vocab_size)

set은 텍스트의 중복 데이터를 지워줍니다. sorted는 list를 정렬해주게 됩니다.

[' ', ',', '.', '가', '갑', '강', '거', '고', '공', '과', '괴', '구', '궁', '기', '길', '나', '남', '높', '느', '늘', '님', '다', '단', '달', '닳', '대', '데', '도', '동', '두', '듯', '라', '람', '랑', '려', '로', '록', '르', '른', '름', '리', '마', '만', '맘', '무', '물', '바', '밝', '백', '변', '보', '불', '사', '산', '삼', '상', '서', '성', '세', '소', '슴', '심', '없', '에', '여', '우', '위', '으', '은', '을', '이', '일', '저', '전', '즐', '천', '철', '충', '편', '하', '한', '함', '해', '화', '활']
85

문자가 들어오면 index로 변환하는 dict 입니다.

char_to_index = dict((char, index) for index, char in enumerate(char_vocab))
print(char_to_index)

결과

{' ': 0, ',': 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, '리': 40, '마': 41, '만': 42, '맘': 43, '무': 44, '물': 45, '바': 46, '밝': 47, '백': 48, '변': 49, '보': 50, '불': 51, '사': 52, '산': 53, '삼': 54, '상': 55, '서': 56, '성': 57, '세': 58, '소': 59, '슴': 60, '심': 61, '없': 62, '에': 63, '여': 64, '우': 65, '위': 66, '으': 67, '은': 68, '을': 69, '이': 70, '일': 71, '저': 72, '전': 73, '즐': 74, '천': 75, '철': 76, '충': 77, '편': 78, '하': 79, '한': 80, '함': 81, '해': 82, '화': 83, '활': 84}

이번에는 반대로 숫자가 들어오면 문자로 변환 해주는 dict 입니다.

index_to_char = {}
for key, value in char_to_index.items():
    index_to_char[value] = key
print(index_to_char)

결과

{0: ' ', 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: '름', 40: '리', 41: '마', 42: '만', 43: '맘', 44: '무', 45: '물', 46: '바', 47: '밝', 48: '백', 49: '변', 50: '보', 51: '불', 52: '사', 53: '산', 54: '삼', 55: '상', 56: '서', 57: '성', 58: '세', 59: '소', 60: '슴', 61: '심', 62: '없', 63: '에', 64: '여', 65: '우', 66: '위', 67: '으', 68: '은', 69: '을', 70: '이', 71: '일', 72: '저', 73: '전', 74: '즐', 75: '천', 76: '철', 77: '충', 78: '편', 79: '하', 80: '한', 81: '함', 82: '해', 83: '화', 84: '활'}


데이터 생성 전체 소스

import pickle

timed_y_data = """
동해 물과 백두산이 마르고 닳도록
하느님이 보우하사 우리나라 만세.
무궁화 삼천리 화려 강산
대한 사람, 대한으로 길이 보전하세.
남산 위에 저 소나무, 철갑을 두른 듯
바람 서리 불변함은 우리 기상일세.
무궁화 삼천리 화려 강산
대한 사람, 대한으로 길이 보전하세.
가을 하늘 공활한데 높고 구름 없이
밝은 달은 우리 가슴 일편단심일세.
무궁화 삼천리 화려 강산
대한 사람, 대한으로 길이 보전하세.
이 기상과 이 맘으로 충성을 다하여
괴로우나 즐거우나 나라 사랑하세.
무궁화 삼천리 화려 강산
대한 사람, 대한으로 길이 보전하세.
"""

timed_y_data = timed_y_data.strip()
timed_y_data = timed_y_data.replace("\n","")
print(timed_y_data)

time_step_size = 4
x_list = []
y_list = []

for xx in range(len(timed_y_data)-time_step_size):
    x_list.append(timed_y_data[xx:xx+time_step_size])
    y_list.append(timed_y_data[xx+time_step_size])

print(x_list)
print(y_list)

char_vocab = sorted(list(set(timed_y_data)))
vocab_size = len(char_vocab)
print(char_vocab)
print(vocab_size)
char_to_index = dict((char, index) for index, char in enumerate(char_vocab))
print(char_to_index)
index_to_char = {}
for key, value in char_to_index.items():
    index_to_char[value] = key
print(index_to_char)

file = open("x_data.txt", "w", encoding="utf-8")
for data in x_list:
    file.write(data)
    file.write("\n")
file.close()

file = open("y_data.txt", "w", encoding="utf-8")
for data in y_list:
    file.write(data)
    file.write("\n")
file.close()

with open('char_to_index.pickle', 'wb') as fw:
    pickle.dump(char_to_index, fw)
with open('index_to_char.pickle', 'wb') as fw:
    pickle.dump(index_to_char, fw)


소스 변경점

기존 RNN 예제 대비 변경점은 3가지 정도입니다.


1. pickle를 이용한 엔코딩 데이터 처리

이 부분은 data 저장하는 곳에서 저장했기 때문에 load가 필요합니다.

import pickle

with open('char_to_index.pickle', 'rb') as fr:
    char_to_index = pickle.load(fr)

with open('index_to_char.pickle', 'rb') as fr:
    index_to_char = pickle.load(fr)


2. CustomDataset 변경

기존에는 csv 파일을 읽어오기만 했었는데 여기에서는 x, y를 읽어서 다시 onehot encoding이 필요합니다.

그래서 값을 읽어오는 부분이 변경되었습니다. 앞에서 문자크기가 85개이기 때문에 onehot encoding시에도 85개의 feature가 필요합니다. 

    def __getitem__(self, index):
        """
        주어진 인덱스 index 에 해당하는 샘플을 데이터셋에서 불러오고 반환합니다.
        """
        if self.base_idx is not None:
            index = index + self.base_idx
        x_data = self.x_data_lines[index].replace("\n", "")
        #print(x_data)
        x_data = np.array([to_categorical(char_to_index[i], 85) for i in x_data])
        #print(x_data.shape)
        #print(x_data)
        y_data = self.y_data_lines[index].replace("\n", "")
        #print(y_data)
        y_data = to_categorical(char_to_index[y_data], 85)
        #print(y_data)
        #x_data = np.reshape(x_data, (-1, 85))
        return x_data, y_data

아래 코드에 의해서 (batch, 4, 85) 크기를 가지는 data 입력이 됩니다.

x_data = np.array([to_categorical(char_to_index[i], 85) for i in x_data])

물론 여기에서는 (4, 85) 형태를 리턴해주면 CustomDataloader 에 의해서 batch로 묶여지게 됩니다.


3. 모델

모델은 입력 shape 을 적당히 변경하고 작은 경우 훈련이 거의 되지 않아서 LSTM 크기를 좀 늘렸습니다. Dense는 85개의 입력을 받고 이것을 softmax 활성화 함수를 사용하였습니다.

inputs = keras.Input(shape=(4, 85))
x = layers.LSTM(60)(inputs)
x = layers.Dense(85, activation='softmax')(x)

그리고 Loss함수는 CategoricalCrossentropy 를 사용하였고 Adam Optimizer를 사용하였습니다.

INPUT_OPTIMIZER = tf.keras.optimizers.Adam(learning_rate=lr)
LOSS = tf.keras.losses.CategoricalCrossentropy()


전체 소스

from tensorflow import keras
from tensorflow.keras.utils import Sequence
from tensorflow.keras import layers
import tensorflow as tf
from tensorflow.keras.utils import to_categorical
import numpy as np
import math
import pickle

with open('char_to_index.pickle', 'rb') as fr:
    char_to_index = pickle.load(fr)

with open('index_to_char.pickle', 'rb') as fr:
    index_to_char = pickle.load(fr)

batch_size = 20
lr = 1e-3
n_epochs = 50
train_data_ratio = 0.8


class CustomDataset:
    def __init__(self, x_tensor_filename, y_tensor_filename, base_idx=None, cnt=None):
        self.x_fn = x_tensor_filename
        self.y_fn = y_tensor_filename
        self.base_idx = base_idx
        self.cnt = cnt
        if self.base_idx is None:
            with open(y_tensor_filename, 'r', encoding='utf-8') as fp:
                for count, line in enumerate(fp):
                    pass
            self.total_len = count + 1
        else:
            self.total_len = cnt

        with open(x_tensor_filename, "r", encoding='utf-8') as f:
            self.x_data_lines = [line for line in f]
        with open(y_tensor_filename, "r", encoding='utf-8') as f:
            self.y_data_lines = [line for line in f]

    def __getitem__(self, index):
        """
        주어진 인덱스 index 에 해당하는 샘플을 데이터셋에서 불러오고 반환합니다.
        """
        if self.base_idx is not None:
            index = index + self.base_idx
        x_data = self.x_data_lines[index].replace("\n", "")
        #print(x_data)
        x_data = np.array([to_categorical(char_to_index[i], 85) for i in x_data])
        #print(x_data.shape)
        #print(x_data)
        y_data = self.y_data_lines[index].replace("\n", "")
        #print(y_data)
        y_data = to_categorical(char_to_index[y_data], 85)
        #print(y_data)
        #x_data = np.reshape(x_data, (-1, 85))
        return x_data, y_data

    def __len__(self):
        """
        데이터셋의 샘플 개수를 반환합니다.
        """
        return self.total_len


class CustomDataloader(Sequence):
    def __init__(self, _dataset, batch_size=1, shuffle=False):
        self.dataset = _dataset
        self.batch_size = batch_size
        self.total_len = math.ceil(len(self.dataset) / self.batch_size)
        self.shuffle = shuffle
        self.indexer = np.arange(len(self.dataset))
        self.on_epoch_end()

    def __getitem__(self, index):
        indexer = self.indexer[index * self.batch_size:(index + 1) * self.batch_size]
        batch_x = [self.dataset[i][0] for i in indexer]
        batch_y = [self.dataset[i][1] for i in indexer]
        #[[5.         5.00999946 5.0199977  5.02999448 5.03998959]
        # [5.00999946 5.0199977  5.02999448 5.03998959 5.04998279]
        # ...
        return np.array(batch_x), np.array(batch_y)

    def __len__(self):
        return self.total_len

    def on_epoch_end(self):
        if self.shuffle:
            np.random.shuffle(self.indexer)
            print("shuffle")


temp_dataset = CustomDataset("x_data.txt", "y_data.txt")
train_dataset_cnt = int(len(temp_dataset) * train_data_ratio)
val_dataset_cnt = len(temp_dataset) - train_dataset_cnt
train_dataset = CustomDataset("x_data.txt", "y_data.txt", 0, train_dataset_cnt)
val_dataset = CustomDataset("x_data.txt", "y_data.txt", train_dataset_cnt, val_dataset_cnt)
print(len(temp_dataset), train_dataset_cnt, val_dataset_cnt)
train_loader = CustomDataloader(train_dataset, batch_size=batch_size, shuffle=False)
val_loader = CustomDataloader(val_dataset, batch_size=batch_size, shuffle=False)

# https://keras.io/api/layers/
inputs = keras.Input(shape=(4, 85))
x = layers.LSTM(60)(inputs)
x = layers.Dense(85, activation='softmax')(x)
outputs = x
model = keras.Model(inputs, outputs)
model.summary()

# https://keras.io/api/optimizers/
# https://keras.io/api/losses/
INPUT_OPTIMIZER = tf.keras.optimizers.Adam(learning_rate=lr)
LOSS = tf.keras.losses.CategoricalCrossentropy()
model.compile(optimizer=INPUT_OPTIMIZER, loss=LOSS)
model.fit(train_loader, batch_size=batch_size, epochs=n_epochs, validation_data=val_loader)
model.save("model_lstm.keras")

결과

279 223 56
Model: "model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_1 (InputLayer)        [(None, 4, 85)]           0         
                                                                 
 lstm (LSTM)                 (None, 60)                35040     
                                                                 
 dense (Dense)               (None, 85)                5185      
                                                                 
=================================================================
Total params: 40,225
Trainable params: 40,225
Non-trainable params: 0
_________________________________________________________________
Epoch 1/50
12/12 [==============================] - 1s 23ms/step - loss: 4.4400 - val_loss: 4.4262
Epoch 2/50
12/12 [==============================] - 0s 4ms/step - loss: 4.4039 - val_loss: 4.3948
Epoch 3/50
12/12 [==============================] - 0s 4ms/step - loss: 4.3606 - val_loss: 4.3512
Epoch 4/50
12/12 [==============================] - 0s 4ms/step - loss: 4.2906 - val_loss: 4.2747
Epoch 5/50
12/12 [==============================] - 0s 4ms/step - loss: 4.1535 - val_loss: 4.1075
Epoch 6/50
12/12 [==============================] - 0s 4ms/step - loss: 3.8944 - val_loss: 3.8651
Epoch 7/50
12/12 [==============================] - 0s 4ms/step - loss: 3.7237 - val_loss: 3.8388
Epoch 8/50
12/12 [==============================] - 0s 4ms/step - loss: 3.6581 - val_loss: 3.7765
Epoch 9/50
12/12 [==============================] - 0s 4ms/step - loss: 3.5942 - val_loss: 3.7746
Epoch 10/50
12/12 [==============================] - 0s 4ms/step - loss: 3.5545 - val_loss: 3.7465
Epoch 11/50
12/12 [==============================] - 0s 4ms/step - loss: 3.5149 - val_loss: 3.7227
Epoch 12/50
12/12 [==============================] - 0s 4ms/step - loss: 3.4697 - val_loss: 3.7108
Epoch 13/50
12/12 [==============================] - 0s 4ms/step - loss: 3.4246 - val_loss: 3.6961
Epoch 14/50
12/12 [==============================] - 0s 4ms/step - loss: 3.3848 - val_loss: 3.6519
Epoch 15/50
12/12 [==============================] - 0s 3ms/step - loss: 3.3276 - val_loss: 3.6426
Epoch 16/50
12/12 [==============================] - 0s 3ms/step - loss: 3.2704 - val_loss: 3.5921
Epoch 17/50
12/12 [==============================] - 0s 4ms/step - loss: 3.2082 - val_loss: 3.5731
Epoch 18/50
12/12 [==============================] - 0s 4ms/step - loss: 3.1412 - val_loss: 3.5327
Epoch 19/50
12/12 [==============================] - 0s 4ms/step - loss: 3.0699 - val_loss: 3.4891
Epoch 20/50
12/12 [==============================] - 0s 4ms/step - loss: 2.9889 - val_loss: 3.4257
Epoch 21/50
12/12 [==============================] - 0s 4ms/step - loss: 2.9030 - val_loss: 3.3826
Epoch 22/50
12/12 [==============================] - 0s 4ms/step - loss: 2.8177 - val_loss: 3.3197
Epoch 23/50
12/12 [==============================] - 0s 4ms/step - loss: 2.7216 - val_loss: 3.2720
Epoch 24/50
12/12 [==============================] - 0s 4ms/step - loss: 2.6283 - val_loss: 3.2156
Epoch 25/50
12/12 [==============================] - 0s 4ms/step - loss: 2.5297 - val_loss: 3.1799
Epoch 26/50
12/12 [==============================] - 0s 4ms/step - loss: 2.4361 - val_loss: 3.1063
Epoch 27/50
12/12 [==============================] - 0s 4ms/step - loss: 2.3331 - val_loss: 3.0865
Epoch 28/50
12/12 [==============================] - 0s 4ms/step - loss: 2.2367 - val_loss: 3.0124
Epoch 29/50
12/12 [==============================] - 0s 4ms/step - loss: 2.1327 - val_loss: 2.9974
Epoch 30/50
12/12 [==============================] - 0s 4ms/step - loss: 2.0387 - val_loss: 2.9617
Epoch 31/50
12/12 [==============================] - 0s 4ms/step - loss: 1.9494 - val_loss: 2.9479
Epoch 32/50
12/12 [==============================] - 0s 3ms/step - loss: 1.8460 - val_loss: 2.9146
Epoch 33/50
12/12 [==============================] - 0s 4ms/step - loss: 1.7531 - val_loss: 2.9316
Epoch 34/50
12/12 [==============================] - 0s 4ms/step - loss: 1.6658 - val_loss: 2.8914
Epoch 35/50
12/12 [==============================] - 0s 4ms/step - loss: 1.5826 - val_loss: 2.9087
Epoch 36/50
12/12 [==============================] - 0s 4ms/step - loss: 1.5006 - val_loss: 2.9242
Epoch 37/50
12/12 [==============================] - 0s 4ms/step - loss: 1.4244 - val_loss: 2.9103
Epoch 38/50
12/12 [==============================] - 0s 4ms/step - loss: 1.3474 - val_loss: 2.9748
Epoch 39/50
12/12 [==============================] - 0s 4ms/step - loss: 1.2815 - val_loss: 2.9538
Epoch 40/50
12/12 [==============================] - 0s 3ms/step - loss: 1.2125 - val_loss: 3.0242
Epoch 41/50
12/12 [==============================] - 0s 3ms/step - loss: 1.1398 - val_loss: 2.9633
Epoch 42/50
12/12 [==============================] - 0s 4ms/step - loss: 1.0929 - val_loss: 3.0585
Epoch 43/50
12/12 [==============================] - 0s 4ms/step - loss: 1.0249 - val_loss: 3.0636
Epoch 44/50
12/12 [==============================] - 0s 4ms/step - loss: 0.9632 - val_loss: 3.0656
Epoch 45/50
12/12 [==============================] - 0s 4ms/step - loss: 0.9112 - val_loss: 3.0943
Epoch 46/50
12/12 [==============================] - 0s 4ms/step - loss: 0.8600 - val_loss: 3.0998
Epoch 47/50
12/12 [==============================] - 0s 3ms/step - loss: 0.8218 - val_loss: 3.1171
Epoch 48/50
12/12 [==============================] - 0s 4ms/step - loss: 0.7693 - val_loss: 3.1759
Epoch 49/50
12/12 [==============================] - 0s 4ms/step - loss: 0.7254 - val_loss: 3.1406
Epoch 50/50
12/12 [==============================] - 0s 4ms/step - loss: 0.6885 - val_loss: 3.1862

validation loss가 마지막에 증가하는것을 봤을때 제대로 훈련되는것 같지는 않습니다.

생각해보면 데이터가 너무 부족합니다. 즉 부족한 text에서 train, validation을 분해했으니 실제 train이 안되는 부분도 있을것 같네요


tester code

이번에는 훈련된 데이터를 이용해서 실제 동작이 어떻게 되는지 확인해보도록 하겠습니다.

처음 4글자가 주어지면 계속해서 예측해서 출력하도록 제작해보았습니다.

여기에서는 np.argmax() 라는 것을 사용했는데 최대값의 index를 넘겨주게 됩니다.

즉 85개의 onehot encoding으로 예측값이 들어가있을텐데 85개 feature중 어디의 값이 가장 큰것인지 index를 넘겨주게 됩니다.


import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
import pickle
from tensorflow.keras.utils import to_categorical


if __name__ == '__main__':

    with open('char_to_index.pickle', 'rb') as fr:
        char_to_index = pickle.load(fr)

    with open('index_to_char.pickle', 'rb') as fr:
        index_to_char = pickle.load(fr)

    flag = False
    fig, ax = plt.subplots()
    for model_name in ["model_lstm.keras"]:
        model = tf.keras.models.load_model(model_name)
        model.summary()
        x_list = []
        x_list.extend(" 삼천리")
        predict_count = 30

        for idx in range(predict_count):
            print("xlist",x_list)
            x_data = np.array([[to_categorical(char_to_index[i], 85) for i in x_list]])
            print(x_data.shape)
            y = model.predict(x_data, batch_size=1)
            #print("y:", y)
            result = np.argmax(y, axis=1)
            print("result:", result)
            newchar = index_to_char[int(result)]
            print("newchar:", newchar)
            x_list.append(newchar)
            x_list.pop(0)

결과

Model: "model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_1 (InputLayer)        [(None, 4, 85)]           0         
                                                                 
 lstm (LSTM)                 (None, 60)                35040     
                                                                 
 dense (Dense)               (None, 85)                5185      
                                                                 
=================================================================
Total params: 40,225
Trainable params: 40,225
Non-trainable params: 0
_________________________________________________________________
xlist [' ', '삼', '천', '리']
(1, 4, 85)
1/1 [==============================] - 0s 275ms/step
result: [0]
newchar:  
xlist ['삼', '천', '리', ' ']
(1, 4, 85)
1/1 [==============================] - 0s 8ms/step
result: [83]
newchar: 화
xlist ['천', '리', ' ', '화']
(1, 4, 85)
1/1 [==============================] - 0s 10ms/step
result: [34]
newchar: 려
xlist ['리', ' ', '화', '려']
(1, 4, 85)
1/1 [==============================] - 0s 9ms/step
result: [0]
newchar:  
xlist [' ', '화', '려', ' ']
(1, 4, 85)
1/1 [==============================] - 0s 9ms/step
result: [5]
newchar: 강
xlist ['화', '려', ' ', '강']
(1, 4, 85)
1/1 [==============================] - 0s 8ms/step
result: [53]
newchar: 산
xlist ['려', ' ', '강', '산']
(1, 4, 85)
1/1 [==============================] - 0s 9ms/step
result: [25]
newchar: 대
xlist [' ', '강', '산', '대']
(1, 4, 85)
1/1 [==============================] - 0s 8ms/step
result: [80]
newchar: 한
xlist ['강', '산', '대', '한']
(1, 4, 85)
1/1 [==============================] - 0s 11ms/step
result: [0]
newchar:  
xlist ['산', '대', '한', ' ']
(1, 4, 85)
1/1 [==============================] - 0s 10ms/step
result: [52]
newchar: 사
xlist ['대', '한', ' ', '사']
(1, 4, 85)
1/1 [==============================] - 0s 9ms/step
result: [32]
newchar: 람
xlist ['한', ' ', '사', '람']
(1, 4, 85)
1/1 [==============================] - 0s 9ms/step
result: [1]
newchar: ,
xlist [' ', '사', '람', ',']
(1, 4, 85)
1/1 [==============================] - 0s 8ms/step
result: [0]
newchar:  
xlist ['사', '람', ',', ' ']
(1, 4, 85)
1/1 [==============================] - 0s 8ms/step
result: [25]
newchar: 대
xlist ['람', ',', ' ', '대']
(1, 4, 85)
1/1 [==============================] - 0s 8ms/step
result: [80]
newchar: 한
xlist [',', ' ', '대', '한']
(1, 4, 85)
1/1 [==============================] - 0s 8ms/step
result: [67]
newchar: 으
xlist [' ', '대', '한', '으']
(1, 4, 85)
1/1 [==============================] - 0s 9ms/step
result: [35]
newchar: 로
xlist ['대', '한', '으', '로']
(1, 4, 85)
1/1 [==============================] - 0s 8ms/step
result: [0]
newchar:  
xlist ['한', '으', '로', ' ']
(1, 4, 85)
1/1 [==============================] - 0s 9ms/step
result: [14]
newchar: 길
xlist ['으', '로', ' ', '길']
(1, 4, 85)
1/1 [==============================] - 0s 9ms/step
result: [57]
newchar: 성
xlist ['로', ' ', '길', '성']
(1, 4, 85)
1/1 [==============================] - 0s 9ms/step
result: [0]
newchar:  
xlist [' ', '길', '성', ' ']
(1, 4, 85)
1/1 [==============================] - 0s 9ms/step
result: [50]
newchar: 보
xlist ['길', '성', ' ', '보']
(1, 4, 85)
1/1 [==============================] - 0s 13ms/step
result: [73]
newchar: 전
xlist ['성', ' ', '보', '전']
(1, 4, 85)
1/1 [==============================] - 0s 9ms/step
result: [0]
newchar:  
xlist [' ', '보', '전', ' ']
(1, 4, 85)
1/1 [==============================] - 0s 8ms/step
result: [58]
newchar: 세
xlist ['보', '전', ' ', '세']
(1, 4, 85)
1/1 [==============================] - 0s 12ms/step
result: [2]
newchar: .
xlist ['전', ' ', '세', '.']
(1, 4, 85)
1/1 [==============================] - 0s 8ms/step
result: [3]
newchar: 가
xlist [' ', '세', '.', '가']
(1, 4, 85)
1/1 [==============================] - 0s 8ms/step
result: [44]
newchar: 무
xlist ['세', '.', '가', '무']
(1, 4, 85)
1/1 [==============================] - 0s 8ms/step
result: [0]
newchar:  
xlist ['.', '가', '무', ' ']
(1, 4, 85)
1/1 [==============================] - 0s 8ms/step
result: [79]
newchar: 하

차례로 결과를 읽어보면 일부 내용이 약간 맞지 않는 부분이 있지만... 그래도 정상적으로 나오는것 같습니다.


sourcecode/keras/04_rnn_char at main · donarts/sourcecode · GitHub


댓글 없음:

댓글 쓰기