순환신경망 (Recurrent Neueal Network, RNN)

: 시퀀스 단위의 입력을 시퀀스 단위의 출력으로 처리하는 모델

: 시계열 데이터 처리 - 자연어, 번역, 이미지 캡션, 채팅, 주식 ...

: LSTM, GRU, ..

 

 - RNN

wikidocs.net/22886

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

 

* tf_rnn.ipynb

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import SimpleRNN, LSTM

 

model = Sequential()

model.add(SimpleRNN(3, input_shape = (2, 10)))
#model.add(SimpleRNN(3, input_length = 2, input_dim = 10))

print(model.summary())                # Total params: 42

from tensorflow.keras.layers. import SimpleRNN

SimpleRNN(a, batch_input_shape = (b, c, d)) :  출력 수(a), batch_size(b), sequence(c), 입력수(d)

model = Sequential()

model.add(LSTM(3, input_shape = (2, 10)))

print(model.summary())                # Total params: 168

from tensorflow.keras.layers. import LSTM

LSTM(a, batch_input_shape = (b, c, d))출력 수(a), batch_size(b), sequence(c), 입력수(d)

 

model = Sequential()

model.add(SimpleRNN(3, batch_input_shape = (8, 2, 10)))
# batch_size : 8, sequence : 2, 입력수 : 10, 출력 수 : 3

print(model.summary())                # Total params: 42
model = Sequential()

model.add(LSTM(3, batch_input_shape = (8, 2, 10)))

print(model.summary())                # Total params: 168

 

model = Sequential()

model.add(SimpleRNN(3, batch_input_shape = (8, 2, 10), return_sequences=True))

print(model.summary())                # Total params: 42
model = Sequential()

model.add(LSTM(3, batch_input_shape = (8, 2, 10), return_sequences=True))

print(model.summary())               # Total params: 168

 

NLP(자연어 처리)

 

자연어 : 순차적

문장 -> 단어/문자열/형태소/자소(자음/모음) -> code화(숫자) -> one -hot encoding or word2vec(단어간 관계 예측) -> embeding처리

 

4개의 숫자를 통해 그 다음 숫자를 예측하는 RNN 모델 생성

 

 * tf_rnn2.ipynb

import tensorflow as tf
import numpy as np

x = []
y = []
for i in range(6):              # 0 ~ 5
    lst = list(range(i, i + 4)) # 0 ~ 3, 1 ~ 4, 2 ~ 5 ...
    print(lst)
    x.append(list(map(lambda c:[c / 10], lst)))
    y.append((i + 4) /10)

print(x)
# [[[0.0], [0.1], [0.2], [0.3]], [[0.1], [0.2], [0.3], [0.4]], [[0.2], [0.3], [0.4], [0.5]], [[0.3], [0.4], [0.5], [0.6]], [[0.4], [0.5], [0.6], [0.7]], [[0.5], [0.6], [0.7], [0.8]]]
print(y)
# [0.4, 0.5, 0.6, 0.7, 0.8, 0.9]

x = np.array(x)
y = np.array(y)

for i in range(len(x)): # 0 ~ 5
    print(x[i], y[i])
# [[0. ]
#  [0.1]
#  [0.2]
#  [0.3]] 0.4
# [[0.1]
#  [0.2]
#  [0.3]
#  [0.4]] 0.5
# ...
model = tf.keras.Sequential([
    #tf.keras.layers.SimpleRNN(units=10, activation='tanh', input_shape=[4, 1]),  # Total params: 131
    #tf.keras.layers.LSTM(units=10, activation='tanh', input_shape=[4, 1]),       # Total params: 491
    tf.keras.layers.GRU(units=10, activation='tanh', input_shape=[4, 1]),         # Total params: 401

    tf.keras.layers.Dense(1)
])

model.compile(optimizer='adam', loss='mse')
print(model.summary())

model.fit(x, y, epochs=100, verbose=0)
print('예측값 :', model.predict(x))
# 예측값 : [[0.380357  ]
#  [0.50256383]
#  [0.6151931 ]
#  [0.7163592 ]
#  [0.8050702 ]
#  [0.88109314]]
print('실제값 :', y)
# 실제값 : [0.4 0.5 0.6 0.7 0.8 0.9]
print()

print(model.predict(np.array([[[0.6], [0.7], [0.9]]])))
# [[0.76507646]]
print(model.predict(np.array([[[-0.1], [0.2], [0.4], [0.9]]])))
# [[0.6791251]]

 

Dimension for RNN

 : RNN모형을 구현할 때 핵심이 되는 데이터 구조

 : many-to-many, many-to-one, one-to-many

 

 * tf_rnn3.ipynb

import numpy as np
import tensorflow as tf
from tensorflow import keras

# many-to-one
x = np.array([[[1], [2], [3]], [[2], [3], [4]], [[3], [4], [5]]], dtype=np.float32)
y = np.array([[4], [5], [6]])
print(x.shape, y.shape) # (3, 3, 1) (3, 1)

# function API 사용
layer_input = keras.Input(shape=(3, 1))
layer_rnn = keras.layers.SimpleRNN(100, activation='tanh')(layer_input)
layer_output = keras.layers.Dense(1)(layer_rnn)

model = keras.Model(layer_input, layer_output)
model.compile(loss = 'mse', optimizer='adam')
model._name = 'many-to-one'
print(model.summary())
# Layer (type)                 Output Shape              Param #   
# =================================================================
# input_2 (InputLayer)         [(None, 3, 1)]            0         
# _________________________________________________________________
# simple_rnn_1 (SimpleRNN)     (None, 100)               10200     
# _________________________________________________________________
# dense_1 (Dense)              (None, 1)                 101       
# =================================================================
# Total params: 10,301
model.fit(x, y, epochs=100, batch_size=1, verbose=1)
print('pred :', model.predict(x).flatten())
# pred : [3.902048  5.1808596 5.8828716]
print('real :', y.flatten())
# real : [4 5 6]
# many-to-many
x = np.array([[[1], [2], [3]], [[2], [3], [4]], [[3], [4], [5]]], dtype=np.float32)
y = np.array([[4], [5], [6]])
print(x.shape, y.shape) # (3, 3, 1) (3, 1)

# function API 사용
layer_input = keras.Input(shape=(3, 1))
layer_rnn = keras.layers.SimpleRNN(100, activation='tanh', return_sequences=True)(layer_input)
layer_output = keras.layers.TimeDistributed(keras.layers.Dense(1))(layer_rnn)

model = keras.Model(layer_input, layer_output)
model.compile(loss = 'mse', optimizer='adam')
model._name = 'many-to-many'
print(model.summary())
# Layer (type)                 Output Shape              Param #   
# =================================================================
# input_5 (InputLayer)         [(None, 3, 1)]            0         
# _________________________________________________________________
# simple_rnn_4 (SimpleRNN)     (None, 3, 100)            10200     
# _________________________________________________________________
# dense_4 (Dense)              (None, 3, 1)              101       
# =================================================================
# Total params: 10,301
model.fit(x, y, epochs=100, batch_size=1, verbose=1)
print('pred :', model.predict(x).flatten())
# pred : [3.429767  3.9655545 4.002289  5.02977   5.0609956 4.999564  6.3015547 5.9251485 6.001438 ]

print('real :', y.flatten())
# real : [4 5 6]

SimpleRNN(100, activation='tanh'return_sequences=True)

TimeDistributed(Dense(1))

 

# stacked many-to-one
x = np.array([[[1], [2], [3]], [[2], [3], [4]], [[3], [4], [5]]], dtype=np.float32)
y = np.array([[4], [5], [6]])
print(x.shape, y.shape) # (3, 3, 1) (3, 1)

# function API 사용
layer_input = keras.Input(shape=(3, 1))
layer_rnn1 = keras.layers.SimpleRNN(100, activation='tanh', return_sequences=True)(layer_input)
layer_rnn2 = keras.layers.SimpleRNN(100, activation='tanh', return_sequences=True)(layer_rnn1)
layer_output = keras.layers.Dense(1)(layer_rnn2)

model = keras.Model(layer_input, layer_output)
model.compile(loss = 'mse', optimizer='adam')
model._name = 'stacked-many-to-many'
print(model.summary())
# Layer (type)                 Output Shape              Param #   
# =================================================================
# input_8 (InputLayer)         [(None, 3, 1)]            0         
# _________________________________________________________________
# simple_rnn_8 (SimpleRNN)     (None, 3, 100)            10200     
# _________________________________________________________________
# simple_rnn_9 (SimpleRNN)     (None, 3, 100)            20100     
# _________________________________________________________________
# dense_7 (Dense)              (None, 3, 1)              101       
# =================================================================
# Total params: 30,401
model.fit(x, y, epochs=100, batch_size=1, verbose=1)
print('pred :', model.predict(x).flatten())
# pred : [3.6705296 3.9618802 3.9781656 5.160208  5.0984097 5.0837207 6.031624 5.93854   5.9503284]

print('real :', y.flatten())
# real : [4 5 6]

 

 - 자연어 처리

wikidocs.net/book/2155

 

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

 

 - 한국어 불용어

www.ranks.nl/stopwords/korean

 

Korean Stopwords

 

www.ranks.nl

 

문자열 토큰화 + LSTM 감성분류

 * tf_rnn4.ipynb

# token, corpus, vocabulary, one-hot, word2vec, tfidf,

from tensorflow.keras.preprocessing.text import Tokenizer

samples = ['The cat say on the mat.', 'The dog ate my homework.'] # list type

# token 처리 1 - word index
token_index = {}
for sam in samples:
    for word in sam.split(sep=' '):
        if word not in token_index:
            #print(word)
            token_index[word] = len(token_index)
print(token_index)
# {'The': 0, 'cat': 1, 'say': 2, 'on': 3, 'the': 4, 'mat.': 5, 'dog': 6, 'ate': 7, 'my': 8, 'homework.': 9}

print()
# token 처리 2 - word index
# tokenizer = Tokenizer(num_words=3) # num_words=3 빈도가 높은 3개의 토큰 만 작업에 참여
tokenizer = Tokenizer()
tokenizer.fit_on_texts(samples)
token_seq = tokenizer.texts_to_sequences(samples)   # 문자열을 index로 표현
print(token_seq)
# [[1, 2, 3, 4, 1, 5], [1, 6, 7, 8, 9]]
print(tokenizer.word_index)                        # 특수 문자 제거 및 대문자를 소문자로 변환
# {'the': 1, 'cat': 2, 'say': 3, 'on': 4, 'mat': 5, 'dog': 6, 'ate': 7, 'my': 8, 'homework': 9}

from tensorflow.keras.preprocessing.text import Tokenizer

 

 - Tokenizer API

www.tensorflow.org/api_docs/python/tf/keras/preprocessing/text/Tokenizer

 

tf.keras.preprocessing.text.Tokenizer  |  TensorFlow Core v2.4.1

Text tokenization utility class.

www.tensorflow.org

tokenizer = Tokenizer(num_words=3) : num_words=3 빈도가 높은 3개의 토큰 만 작업에 참여

token_seq = tokenizer.texts_to_sequences(samples)

tokenizer.fit_on_texts(data) : 

tokenizer.word_index : 

token_mat = tokenizer.texts_to_matrix(samples, mode='binary')    # 있으면 1 없으면 0
# token_mat = tokenizer.texts_to_matrix(samples, mode='freq')      # 빈도수로 표현
# token_mat = tokenizer.texts_to_matrix(samples, mode='tfidf')   # 단어의 중요정도를 가중치로 표현
# token_mat = tokenizer.texts_to_matrix(samples, mode='count')
print(token_mat)
# [[0. 2. 1. 1. 1. 1. 0. 0. 0. 0.]
#  [0. 1. 0. 0. 0. 0. 1. 1. 1. 1.]]

# [[0.         0.86490296 0.69314718 0.69314718 0.69314718 0.69314718
#   0.         0.         0.         0.        ]
#  [0.         0.51082562 0.         0.         0.         0.
#   0.69314718 0.69314718 0.69314718 0.69314718]]

# [[0.         0.33333333 0.16666667 0.16666667 0.16666667 0.16666667
#   0.         0.         0.         0.        ]
#  [0.         0.2        0.         0.         0.         0.
#   0.2        0.2        0.2        0.2       ]]

# [[0. 1. 1. 1. 1. 1. 0. 0. 0. 0.]
#  [0. 1. 0. 0. 0. 0. 1. 1. 1. 1.]]

print(tokenizer.word_counts)
# OrderedDict([('the', 3), ('cat', 1), ('say', 1), ('on', 1), ('mat', 1), ('dog', 1), ('ate', 1), ('my', 1), ('homework', 1)])
print(tokenizer.document_count)  # 2
print(tokenizer.word_docs)
# defaultdict(<class 'int'>, {'the': 2, 'on': 1, 'cat': 1, 'mat': 1, 'say': 1, 'my': 1, 'ate': 1, 'dog': 1, 'homework': 1})

from tensorflow.keras.utils import to_categorical
token_seq = to_categorical(token_seq[0], num_classes=6)
print(token_seq)
# [[0. 1. 0. 0. 0. 0.]
#  [0. 0. 1. 0. 0. 0.]
#  [0. 0. 0. 1. 0. 0.]
#  [0. 0. 0. 0. 1. 0.]
#  [0. 1. 0. 0. 0. 0.]
#  [0. 0. 0. 0. 0. 1.]]

 

 - 영화 리뷰 자료로 간단한 감성분석

import numpy as np
docs = ['너무 재밌어요', '또 보고 싶어요', '참 잘 만든 영화네요', '친구에게 추천할래요', '배우가 멋져요',\
        '별로예요', '지루하고 재미없어요', '연기가 어색해요', '재미없어요', '돈 아까워요']
classes = np.array([1,1,1,1,1,0,0,0,0,0])

token = Tokenizer()
token.fit_on_texts(docs)
print(token.word_index)
# {'재미없어요': 1, '너무': 2, '재밌어요': 3, '또': 4, '보고': 5, '싶어요': 6, '참': 7, '잘': 8, '만든': 9, '영화네요': 10, '친구에게': 11,
# '추천할래요': 12, '배우가': 13, '멋져요': 14, '별로예요': 15, '지루하고': 16, '연기가': 17, '어색해요': 18, '돈': 19, '아까워요': 20}

x = token.texts_to_sequences(docs)
print('토큰화 결과 :', x)
# 토큰화 결과 : [[2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12], [13, 14], [15], [16, 1], [17, 18], [1], [19, 20]]

from tensorflow.keras.preprocessing.sequence import pad_sequences
padded_x = pad_sequences(x, 4) # 패딩 : 서로 다른 길이의 데이터를 가장 긴 데이터의 길이로 맞춘다.
# 병렬 연산을 위해서 여러 문장의 길이를 임의로 동일하게 맞춰주는 작업이 필요
print(padded_x)
# [[ 0  0  2  3]
#  [ 0  4  5  6]
#  [ 7  8  9 10]
#  [ 0  0 11 12]
#  [ 0  0 13 14]
#  [ 0  0  0 15]
#  [ 0  0 16  1]
#  [ 0  0 17 18]
#  [ 0  0  0  1]
#  [ 0  0 19 20]]
# 모델
word_size = len(token.word_index) + 1
print(word_size) # 22

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten, Embedding, LSTM
model = Sequential()
model.add(Embedding(word_size, output_dim=8, input_length=4)) # model.add(Embedding(vocabulary, output_dim, input_length))
model.add(LSTM(32, activation='tanh'))
model.add(Flatten()) # FC Layer
model.add(Dense(32, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
model.fit(padded_x, classes, epochs=10, verbose=1)
print('acc :', model.evaluate(padded_x, classes)[1])
# acc : 1.0
print('pred :', model.predict(padded_x).flatten())
# pred : [0.50115323 0.5027714  0.5044522  0.5016046  0.50048715 0.49916682  0.4971649  0.49867162 0.49731275 0.4972709 ]
print('real :', classes)
# real : [1 1 1 1 1 0 0 0 0 0]

 

RNN을 이용한 텍스트 생성

 : 모델이 문맥을 학습한 후 텍스트를 작성

 

 * tf_rnn5_token.ipynb

from tensorflow.keras.preprocessing.text import Tokenizer

text = """운동장에 눈이 많이 쌓여 있다
그 사람의 눈이 빛난다
맑은 눈이 사람 마음을 곱게 만든다"""

tok = Tokenizer()
tok.fit_on_texts([text])
encoded = tok.texts_to_sequences([text])
print(encoded)
# [[2, 1, 3, 4, 5, 6, 7, 1, 8, 9, 1, 10, 11, 12, 13]]
print(tok.word_index)
# {'눈이': 1, '운동장에': 2, '많이': 3, '쌓여': 4, '있다': 5, '그': 6, '사람의': 7, '빛난다': 8, '맑은': 9, '사람': 10, '마음을': 11, '곱게': 12, '만든다': 13}

vocab_size = len(tok.word_index) + 1
print('단어 집합의 크기 :%d'%vocab_size)
# 단어 집합의 크기 :14
sequences = list()   # feature
for line in text.split('\n'):
    encoded = tok.texts_to_sequences([line])[0]
    #print(encoded)
    # [2, 1, 3, 4, 5]
    # [6, 7, 1, 8]
    # [9, 1, 10, 11, 12, 13]
    for i in range(1, len(encoded)):
        sequ = encoded[:i + 1]
        #print(sequ)
        # [2, 1]
        # [2, 1, 3]
        # [2, 1, 3, 4]
        # [2, 1, 3, 4, 5]
        # [6, 7]
        # [6, 7, 1]
        # [6, 7, 1, 8]
        # [9, 1]
        # [9, 1, 10]
        # [9, 1, 10, 11]
        # [9, 1, 10, 11, 12]
        # [9, 1, 10, 11, 12, 13]
        sequences.append(sequ)
print(sequences)
# [[2, 1], [2, 1, 3], [2, 1, 3, 4], [2, 1, 3, 4, 5], [6, 7], [6, 7, 1], [6, 7, 1, 8], [9, 1], [9, 1, 10], [9, 1, 10, 11], [9, 1, 10, 11, 12], [9, 1, 10, 11, 12, 13]]

print('학습에 참여할 샘플 수 :', len(sequences)) # 12
print(max(len(i) for i in sequences)) # 6

# padding
from tensorflow.keras.preprocessing.sequence import pad_sequences
max_len = max(len(i) for i in sequences)
psequences = pad_sequences(sequences, maxlen = max_len, padding='pre')
# psequences = pad_sequences(sequences, maxlen = max_len, padding='post')
print(psequences)
# [[ 0  0  0  0  2  1]
#  [ 0  0  0  2  1  3]
#  [ 0  0  2  1  3  4]
#  [ 0  2  1  3  4  5]
#  [ 0  0  0  0  6  7]
#  [ 0  0  0  6  7  1]
#  [ 0  0  6  7  1  8]
#  [ 0  0  0  0  9  1]
#  [ 0  0  0  9  1 10]
#  [ 0  0  9  1 10 11]
#  [ 0  9  1 10 11 12]
#  [ 9  1 10 11 12 13]]

import numpy as np
psequences = np.array(psequences)
x = psequences[:, :-1] # feature
y = psequences[:, -1]  # label
print(x)
# [[ 0  0  0  0  2]
#  [ 0  0  0  2  1]
#  [ 0  0  2  1  3]
#  [ 0  2  1  3  4]
#  [ 0  0  0  0  6]
#  [ 0  0  0  6  7]
#  [ 0  0  6  7  1]
#  [ 0  0  0  0  9]
#  [ 0  0  0  9  1]
#  [ 0  0  9  1 10]
#  [ 0  9  1 10 11]
#  [ 9  1 10 11 12]]
print(y)
# [ 1  3  4  5  7  1  8  1 10 11 12 13]
from tensorflow.keras.utils import to_categorical
y = to_categorical(y, num_classes = vocab_size)
print(y)
# [[0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
#  [0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
#  [0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
#  [0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
#  [0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
#  [0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
#  [0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
#  [0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
#  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
#  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
#  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
#  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]]
# model
from tensorflow.keras.layers import Embedding, Dense, SimpleRNN, LSTM, Flatten
from tensorflow.keras.models import Sequential

model = Sequential()
model.add(Embedding(vocab_size, 32, input_length=max_len - 1))
model.add(LSTM(32, activation='tanh'))
model.add(Flatten())
model.add(Dense(32, activation='relu'))
model.add(Dense(vocab_size, activation='softmax'))

model.compile(loss = 'categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
print(model.summary())        # Total params: 10,286
model.fit(x, y, epochs=200, verbose=2)
print(model.evaluate(x, y))   # [0.07081326842308044, 1.0]
def sentence_gen(model, t, current_word, n):
    init_word = current_word
    sentence = ''
    for _ in range(n):
        encoded = t.texts_to_sequences([current_word])[0]
        encoded = pad_sequences([encoded], maxlen = max_len - 1, padding = 'pre')
        result = np.argmax(model.predict(encoded))
        # print(result)
        for word, index in t.word_index.items():
            #print('word:', word, ', index:', index)
            # word: 눈이 , index: 1
            # word: 운동장에 , index: 2
            # word: 많이 , index: 3
            # word: 쌓여 , index: 4
            # word: 있다 , index: 5
            # word: 그 , index: 6
            # word: 사람의 , index: 7
            # word: 빛난다 , index: 8
            # word: 맑은 , index: 9
            # word: 사람 , index: 10
            # word: 마음을 , index: 11
            # word: 곱게 , index: 12
            # word: 만든다 , index: 13
            if index == result:
                break
        current_word = current_word + ' ' + word
        sentence = sentence + ' '  + word # 예측단어를 문장에 저장
    sentence = init_word + sentence
    return sentence


print(sentence_gen(model, tok, '운동장에', 1))
print(sentence_gen(model, tok, '운동장에', 3))
print(sentence_gen(model, tok, '맑은', 1))
print(sentence_gen(model, tok, '맑은', 2))
print(sentence_gen(model, tok, '맑은', 3))
print(sentence_gen(model, tok, '한국', 3))
print(sentence_gen(model, tok, '파이썬', 5))
# 운동장에 눈이
# 운동장에 눈이 많이 쌓여
# 맑은 눈이
# 맑은 눈이 사람
# 맑은 눈이 사람 마음을
# 한국 눈이 눈이 사람

 

소설을 학습하여 새로운 소설생성

 * tf_rnn6_토지소설.ipynb

 

 - RNN 관련

cafe.daum.net/flowlife/S2Ul/33

 

Daum 카페

 

cafe.daum.net

import numpy as np
import random, sys
import tensorflow as tf
f = open("rnn_test_toji.txt", 'r', encoding="utf-8")
text = f.read()
#print(text)
f.close();

print('텍스트 행 수: ', len(text))  # 306967
print(set(text))  # set 집합형 함수를 이용해 중복 제거{'얻', '턴', '옮', '쩐', '제', '평',...

chars = sorted(list(set(text)))     # 중복이 제거된 문자를 하나하나 읽어 들여 정렬 
print(chars)                        # ['\n', ' ', '!', ... , '0', '1', ... 'a', 'c', 'f', '...
print('사용되고 있는 문자 수:', len(chars))   # 1469

char_indices = dict((c, i) for i, c in enumerate(chars)) # 문자와 ID
indices_char = dict((i, c) for i, c in enumerate(chars)) # ID와 문자
print(char_indices)            # ... '것': 106, '겄': 107, '겅': 108,...
print(indices_char)            # ... 106: '것', 107: '겄', 108: '겅',...
# 텍스트를 maxlen개의 문자로 자르고 다음에 오는 문자 등록하기
maxlen = 20
step = 3
sentences = []
next_chars = []

for i in range(0, len(text) - maxlen, step):
    #print(text[i: i + maxlen])
    sentences.append(text[i: i + maxlen])
    next_chars.append(text[i + maxlen])

print('학습할 구문 수:', len(sentences))        # 102316

print('텍스트를 ID 벡터로 변환')
X = np.zeros((len(sentences), maxlen, len(chars)), dtype=np.bool)
y = np.zeros((len(sentences), len(chars)), dtype=np.bool)
print(X[:3])
print(y[:3])

for i, sent in enumerate(sentences):
    #print(sent)
    for t, char in enumerate(sent):
        #print(t, ' ', char)
        X[i, t, char_indices[char]] = 1
    y[i, char_indices[next_chars[i]]] = 1

print(X[:5])  # 찾은 글자에만 True, 나머지는 False 기억
print(y[:5])

# 모델 구축하기(LSTM(RNN의 개량종)) -------------
# 하나의 LSTM 층과 그 뒤에 Dense 분류층 추가
model = tf.keras.Sequential()
model.add(tf.keras.layers.LSTM(128, activation='tanh', input_shape=(maxlen, len(chars))))

model.add(tf.keras.layers.Dense(128))
model.add(tf.keras.layers.Activation('relu'))
model.add(tf.keras.layers.Dense(len(chars)))
model.add(tf.keras.layers.Activation('softmax'))

opti = tf.keras.optimizers.Adam(lr=0.001)
model.compile(loss='categorical_crossentropy', optimizer=opti, metrics=['acc'])

from tensorflow.keras.callbacks import EarlyStopping

es = EarlyStopping(patience = 5, monitor='loss')
model.fit(X, y, epochs=500, batch_size=64, verbose=2, callbacks=[es])

print(model.evaluate(X, y))
# 확률적 샘플링 처리 함수(무작위적으로 샘플링하기 위함)
# 모델의 예측이 주어졌을 때 새로운 글자를 샘플링 
def sample_func(preds, variety=1.0):        # 후보를 배열에서 꺼내기
    # array():복사본, asarray():참조본 생성 - 원본 변경시 복사본은 변경X 참조본은 변경O
    preds = np.asarray(preds).astype('float64') 
    preds = np.log(preds) / variety         # 로그확률 벡터식을 코딩
    exp_preds = np.exp(preds)               # 자연상수 얻기
    preds = exp_preds / np.sum(exp_preds)   # softmax 공식 참조
    probas = np.random.multinomial(1, preds, 1)  # 다항식분포로 샘플 얻기
    return np.argmax(probas)

for num in range(1, 2):   # 학습시키고 텍스트 생성하기 반복    1, 60
    print()
    print('--' * 30)
    print('반복 =', num)

    # 데이터에서 한 번만 반복해서 모델 학습
    model.fit(X, y, batch_size=128, epochs=1, verbose=0) 

    # 임의의 시작 텍스트 선택하기
    start_index = random.randint(0, len(text) - maxlen - 1)

    for variety in [0.2, 0.5, 1.0, 1.2]:     # 다양한 문장 생성
        print('\n--- 다양성 = ', variety)    # 다양성 = 0.2 -> 다양성 =  0.5 -> ...
        generated = ''
        sentence = text[start_index: start_index + maxlen]
        generated += sentence
        print('--- 시드 = "' + sentence + '"')  # --- 시드 = "께 간뎅이가 부어서, 시부릴기력 있거"...
        sys.stdout.write(generated)

        # 시드를 기반으로 텍스트 자동 생성. 시드 텍스트에서 시작해서 500개의 글자를 생성
        for i in range(500):
            x = np.zeros((1, maxlen, len(chars))) # 지금까지 생성된 글자를 원핫인코딩 처리

            for t, char in enumerate(sentence):
                x[0, t, char_indices[char]] = 1.

            # 다음에 올 문자를 예측하기(다음 글자를 샘플링)
            preds = model.predict(x, verbose=0)[0]
            next_index = sample_func(preds, variety)    # 다양한 문장 생성을 위함
            next_char = indices_char[next_index]

            # 출력하기
            generated += next_char
            sentence = sentence[1:] + next_char
            sys.stdout.write(next_char)
            sys.stdout.flush()
        print()

 

 - Google colab

cafe.daum.net/flowlife/S2Ul/24

 

Daum 카페

 

cafe.daum.net

 - 리눅스 기본 명령어

cafe.daum.net/flowlife/9A8Q/161

 

리눅스 기본 명령어

vmwarehttps://www.vmware.com/kr.html무료사용 제품VMware Workstation Playerhttps://www.centos.org/vmware에 centos 설치 하기https://jhnyang.tistory.com/280https://www.ubuntu-kr.org/1. 데비안(Debian)Debian은

cafe.daum.net

 - 가상환경 tool

vmware

virtual box

 

 - telnet 와 ssh의 차이

m.blog.naver.com/PostView.nhn?blogId=ahnsh09&logNo=40171391492&proxyReferer=https:%2F%2Fwww.google.com%2F

 

telnet 와 ssh의 차이

telnet이란? 원격 접속 서비스로서 특정 사용자가 네트워크를 통해 다른 컴퓨터에 연결하여 그 컴퓨터에서 ...

blog.naver.com

 

뉴욕타임즈 기사의 일부 자료로 RNN 학습 모델을 만들어 기사 생성하기

 

 * tf_rnn7_뉴욕타임즈기사.ipynb

import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/pykwon/python/master/testdata_utf8/articlesapril.csv')
print(df.head())
print(df.count)
print(df.columns)
# Index(['articleID', 'articleWordCount', 'byline', 'documentType', 'headline', 'keywords', 'multimedia', 
#'newDesk', 'printPage', 'pubDate','sectionName', 'snippet', 'source', 'typeOfMaterial', 'webURL'], dtype='object')

print(len(df.columns)) # 15
print(df['headline'].head())
# 0    Former N.F.L. Cheerleaders’ Settlement Offer: ...
# 1    E.P.A. to Unveil a New Rule. Its Effect: Less ...
# 2                              The New Noma, Explained
# 3                                              Unknown
# 4                                              Unknown
print(df.headline.values)
# ['Former N.F.L. Cheerleaders’ Settlement Offer: $1 and a Meeting With Goodell'
#  'E.P.A. to Unveil a New Rule. Its Effect: Less Science in Policymaking.'
#  'The New Noma, Explained' ...
#  'Gen. Michael Hayden Has One Regret: Russia'
#  'There Is Nothin’ Like a Tune' 'Unknown']

headline = []
headline.extend(list(df.headline.values))
print(headline[:10])
# ['Former N.F.L. Cheerleaders’ Settlement Offer: $1 and a Meeting With Goodell',
# 'E.P.A. to Unveil a New Rule. Its Effect: Less Science in Policymaking.', 'The New Noma, Explained', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'Unknown', 'How a Bag of Texas Dirt  Became a Times Tradition', 'Is School a Place for Self-Expression?']

# Unknown 값은 노이즈로 판단해 제거
print(len(headline)) # 1324
headline = [n for n in headline if n != 'Unknown']
print(len(headline)) # 1214

# 구굿점 제거, 소문자 처리
print('He하이llo 가a나b123다'.encode('ascii', errors="ignore").decode()) # Hello ab123
from string import punctuation
print(", python.'".strip(punctuation))                                   #  python
print(", py thon.'".strip(punctuation + ' '))                            # py thon
#-------------------------------------------------------------------

def repre_func(s):
    s = s.encode('utf8').decode('ascii', 'ignore')
    return ''.join(c for c in s if c not in punctuation).lower()

text = [repre_func(s) for s in headline]
print(text[:10])
# ['former nfl cheerleaders settlement offer 1 and a meeting with goodell', 'epa to unveil a new rule its effect less science in policymaking', 'the new noma explained', 'how a bag of texas dirt  became a times tradition', 'is school a place for selfexpression', 'commuter reprogramming', 'ford changed leaders looking for a lift its still looking', 'romney failed to win at utah convention but few believe hes doomed', 'chain reaction', 'he forced the vatican to investigate sex abuse now hes meeting with pope francis']

a.extend(b) : a에 b의 각 원소를 추가.

a.encode('ascii', errors="ignore").decode() : a에서 영문/숫자 아닌값 무시하여 제거.

from string import punctuation

a.strip(punctuation) : 구둣점 제거

 

 - 단어 집합 생성

 

from keras_preprocessing.text import Tokenizer
tok = Tokenizer()
tok.fit_on_texts(text)
print(tok.word_index)
# {'the': 1, 'a': 2, 'to': 3, 'of': 4, 'in': 5, 'for': 6, 'and': 7,  ...

vocab_size = len(tok.word_index) + 1
print('vocab_size :', vocab_size)   # 3494

sequences = list()
for line in text:
    enc = tok.texts_to_sequences([line])[0]
    for i in range(1, len(enc)):
        se = enc[:i + 1]
        sequences.append(se)

print(sequences[:11])
# [[99, 269], [99, 269, 371], [99, 269, 371, 1115], [99, 269, 371, 1115, 582], [99, 269, 371, 1115, 582, 52], [99, 269, 371, 1115, 582, 52, 7], [99, 269, 371, 1115, 582, 52, 7, 2], [99, 269, 371, 1115, 582, 52, 7, 2, 372], [99, 269, 371, 1115, 582, 52, 7, 2, 372, 10], [99, 269, 371, 1115, 582, 52, 7, 2, 372, 10, 1116], [100, 3]]

index_to_word = {}
for key, value in tok.word_index.items():
    index_to_word[value] = key
print(index_to_word)
# {1: 'the', 2: 'a', 3: 'to', 4: 'of', 5: 'in', 6: 'for', 7: 'and', ...
print(index_to_word[150])       # fire

max_len = max(len(i) for i in sequences)
print('max_len :', max_len)     # 24

from tensorflow.keras.preprocessing.sequence import pad_sequences
sequences = pad_sequences(sequences, maxlen = max_len, padding = 'pre')
print(sequences[:3])
# [[   0    0    0    0    0    0    0    0    0    0    0    0    0    0     0    0    0    0    0    0    0    0   99  269]
#  [   0    0    0    0    0    0    0    0    0    0    0    0    0    0     0    0    0    0    0    0    0   99  269  371]
#  [   0    0    0    0    0    0    0    0    0    0    0    0    0    0     0    0    0    0    0    0   99  269  371 1115]]

import numpy as np
sequences = np.array(sequences)
x = sequences[:, :-1] # feature
y = sequences[:, -1]  # label
print(x[:3])
# [[  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0    0   0   0   0  99]
#  [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0    0   0   0  99 269]
#  [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0    0   0  99 269 371]]

print(y[:3])
# [ 269  371 1115]

from tensorflow.keras.utils import to_categorical
y = to_categorical(y, num_classes=vocab_size)
print(y[0])
# [0. 0. 0. ... 0. 0. 0.]
from tensorflow.keras.layers import Embedding, Dense, LSTM
from tensorflow.keras.models import Sequential

model = Sequential()
model.add(Embedding(vocab_size, 32, input_length = max_len -1))
model.add(LSTM(128, activation='tanh'))
model.add(Dense(vocab_size, activation='softmax'))

model.compile(loss = 'categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(x, y, epochs=50, verbose=2, batch_size=32)
print(model.evaluate(x, y))
# [1.3969029188156128, 0.7689350247383118]
def sentence_gen(model, t, current_word, n):
    init_word = current_word
    sentence = ''
    for _ in range(n):
        encoded = t.texts_to_sequences([current_word])[0]
        encoded = pad_sequences([encoded], maxlen = max_len - 1, padding = 'pre')
        result = np.argmax(model.predict(encoded))
        # print(result)
        for word, index in t.word_index.items():
            #print('word:', word, ', index:', index)
            if index == result:
                break
        current_word = current_word + ' ' + word
        sentence = sentence + ' '  + word # 예측단어를 문장에 저장
    sentence = init_word + sentence
    return sentence

print(sentence_gen(model, tok, 'i', 10))
print(sentence_gen(model, tok, 'how', 10))
print(sentence_gen(model, tok, 'how', 100))
print(sentence_gen(model, tok, 'good', 200))
print(sentence_gen(model, tok, 'python', 10))
# i brain injuries are tied to dementia abuse him slippery crashes
# how to serve a deranged tyrant stoically a pope fields for
# how to serve a deranged tyrant stoically a pope fields for a cathedral todo meet in a cathedral strike president apply for her police in privatized scientists about fast denmark says shot was life according at 92 was michael whims of webs and comey memoir too life aids still alive too african life on still loss to exfbi chief in new york lifts renewable sources to doing apply at 92 for say he police at pope francis say it was was too aids to behind was back to 92 was back to type not too common beach reimaginedjurassic african apartheid on
# good calls off trip to latin america citing crisis in syria not to invade back at meeting from pope francis doomed it recalls it was back to be focus of them to comey francis say risk risk it recalls about it us potent tolerance of others or products slippery leak of journalist it just hes aids hes risk it comey francis rude it was back to was not too was was rude francis it was endorse rival endorse rude was still alive 1738 african was shot him didnt him didnt it was endorse rival too was was it was endorse rival too rude apply to them to comey he officials to back to smiles at pope francis say it recalls it was back on not from uk officials of not 2002 not too pope francis too was too doomed francis not trying to them war uk officials say lawyers apply to agreement from muppets children say been mainstream it us border architect of misconduct to not francis it was say to invade endorse rival was behind apply to agreement on nafta children about gay draws near to director for north korea us children pledges recalls it was too rude francis risk
# python to men pushed to the edge investigation syria trump about

자연어 생성 글자 단위, 단어단위, 자소 단위

 

자연어 생성 : 단어 단위 생성

 

* tf_rnn8_토지_단어단위.ipynb

# 토지 또는 조선왕조실록 데이터 파일 다운로드
# https://github.com/wikibook/tf2/blob/master/Chapter7.ipynb
import tensorflow as tf
import numpy as np 

path_to_file = tf.keras.utils.get_file('toji.txt', 'https://raw.githubusercontent.com/pykwon/etc/master/rnn_test_toji.txt')
#path_to_file = 'silrok.txt'
# 데이터 로드 및 확인. encoding 형식으로 utf-8 을 지정해야합니다.
train_text = open(path_to_file, 'rb').read().decode(encoding='utf-8')

# 텍스트가 총 몇 자인지 확인합니다.
print('Length of text: {} characters'.format(len(train_text))) # Length of text: 695685 characters

 

# 처음 100 자를 확인해봅니다.
print(train_text[:100])
# 제 1 편 어둠의 발소리
# 1897년의 한가위.
# 까치들이 울타리 안 감나무에 와서 아침 인사를 하기도 전에, 무색 옷에 댕기꼬리를 늘인 
# 아이들은 송편을 입에 물고 마을길을 쏘

# 훈련 데이터 입력 정제
import re
# From https://github.com/yoonkim/CNN_sentence/blob/master/process_data.py
def clean_str(string):    
    string = re.sub(r"[^가-힣A-Za-z0-9(),!?\'\`]", " ", string)
    string = re.sub(r"\'ll", " \'ll", 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)
    string = re.sub(r"\'{2,}", "\'", string)
    string = re.sub(r"\'", "", string)
    return string

train_text = train_text.split('\n')
train_text = [clean_str(sentence) for sentence in train_text]
train_text_X = []
for sentence in train_text:
    train_text_X.extend(sentence.split(' '))
    train_text_X.append('\n')
    
train_text_X = [word for word in train_text_X if word != '']

print(train_text_X[:20])  
# ['제', '1', '편', '어둠의', '발소리', '\n', '1897년의', '한가위', '\n', '까치들이', '울타리', '안', '감나무에', '와서', '아침', '인사를', '하기도', '전에', ',', '무색']

# 단어 토큰화
# 단어의 set을 만듭니다.
vocab = sorted(set(train_text_X))
vocab.append('UNK')   # 텍스트 안에 존재하지 않는 토큰을 나타내는 'UNK' 사용
print ('{} unique words'.format(len(vocab)))

# vocab list를 숫자로 맵핑하고, 반대도 실행합니다.
word2idx = {u:i for i, u in enumerate(vocab)}
idx2word = np.array(vocab)

text_as_int = np.array([word2idx[c] for c in train_text_X])

# word2idx 의 일부를 알아보기 쉽게 print 해봅니다.
print('{')
for word,_ in zip(word2idx, range(10)):
    print('  {:4s}: {:3d},'.format(repr(word), word2idx[word]))
print('  ...\n}')

print('index of UNK: {}'.format(word2idx['UNK']))

# 토큰 데이터 확인. 20개만 확인
print(train_text_X[:20])  
print(text_as_int[:20])

# 기본 데이터셋 만들기
seq_length = 25  # 25개의 단어가 주어질 경우 다음 단어를 예측하도록 데이터를 만듦
examples_per_epoch = len(text_as_int) // seq_length
sentence_dataset = tf.data.Dataset.from_tensor_slices(text_as_int)

# seq_length + 1 은 처음 25개 단어와 그 뒤에 나오는 정답이 될  1 단어를 합쳐 함께 반환하기 위함
# drop_remainder=True 남는 부분은 제거 속성
sentence_dataset = sentence_dataset.batch(seq_length + 1, drop_remainder=True)

for item in sentence_dataset.take(1):
    print(idx2word[item.numpy()])
    print(item.numpy())

# 학습 데이터셋 만들기
# 26개의 단어가 각각 입력과 정답으로 묶어서 ([25단어], 1단어) 형태의 데이터를 반환하기 위한 작업
def split_input_target(chunk):
    return [chunk[:-1], chunk[-1]]

train_dataset = sentence_dataset.map(split_input_target)
for x,y in train_dataset.take(1):
    print(idx2word[x.numpy()])
    print(x.numpy())
    print(idx2word[y.numpy()])
    print(y.numpy())

# 데이터셋 shuffle, batch 설정
BATCH_SIZE = 64
steps_per_epoch = examples_per_epoch // BATCH_SIZE
BUFFER_SIZE = 5000

train_dataset = train_dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)

# 단어 단위 생성 모델 정의
total_words = len(vocab)
model = tf.keras.Sequential([
    tf.keras.layers.Embedding(total_words, 100, input_length=seq_length),
    tf.keras.layers.LSTM(units=100, return_sequences=True),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.LSTM(units=100),
    tf.keras.layers.Dense(total_words, activation='softmax')
])

model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.summary()

# 단어 단위 생성 모델 학습
from tensorflow.keras.preprocessing.sequence import pad_sequences

def testmodel(epoch, logs):
    if epoch % 5 != 0 and epoch != 49:
        return
    test_sentence = train_text[0]

    next_words = 100
    for _ in range(next_words):
        test_text_X = test_sentence.split(' ')[-seq_length:]
        test_text_X = np.array([word2idx[c] if c in word2idx else word2idx['UNK'] for c in test_text_X])
        test_text_X = pad_sequences([test_text_X], maxlen=seq_length, padding='pre', value=word2idx['UNK'])

        output_idx = model.predict_classes(test_text_X)
        test_sentence += ' ' + idx2word[output_idx[0]]
    
    print()
    print(test_sentence)
    print()

# 모델을 학습시키며 모델이 생성한 결과물을 확인하기 위해 LambdaCallback 함수 생성
testmodelcb = tf.keras.callbacks.LambdaCallback(on_epoch_end=testmodel)

history = model.fit(train_dataset.repeat(), epochs=50, 
                steps_per_epoch=steps_per_epoch, 
                callbacks=[testmodelcb], verbose=2)

model.save('rnnmodel.hdf5')
del model
from tensorflow.keras.models import load_model
model=load_model('rnnmodel.hdf5')

# 임의의 문장을 사용한 생성 결과 확인
test_sentence = '최참판댁 사랑은 무인지경처럼 적막하다'
#test_sentence = '동헌에 나가 공무를 본 후 활 십오 순을 쏘았다'

next_words = 500
for _ in range(next_words):
    # 임의 문장 입력 후 뒤에서 부터 seq_length 만킁ㅁ의 단어(25개) 선택
    test_text_X = test_sentence.split(' ')[-seq_length:]  
    
    # 문장의 단어를 인덱스 토큰으로 바꿈. 사전에 등록되지 않은 경우에는 'UNK' 코큰값으로 변경
    test_text_X = np.array([word2idx[c] if c in word2idx else word2idx['UNK'] for c in test_text_X])
    # 문장의 앞쪽에 빈자리가 있을 경우 25개 단어가 채워지도록 패딩
    test_text_X = pad_sequences([test_text_X], maxlen=seq_length, padding='pre', value=word2idx['UNK'])
    
    # 출력 중에서 가장 값이 큰 인덱스 반환
    output_idx = model.predict_classes(test_text_X) 
    test_sentence += ' ' + idx2word[output_idx[0]] # 출력단어는 test_sentence에 누적해 다음 스테의 입력으로 활용

print(test_sentence)


# LambdaCallback
# keras에서 여러가지 상황에서 콜백이되는 class들이 만들어져 있는데, LambdaCallback 등의 Callback class들은 
# 기본적으로 keras.callbacks.Callback class를 상속받아서 특정 상황마다 콜백되는 메소드들을 재정의하여 사용합니다. 
# LambdaCallback는 lambda 평션을 작성하여 생성자에 넘기는 방식으로 사용 할 수 있습니다. 
# callback 시 받는 arg는 Callbakc class에 정의 되어 있는대로 맞춰 주어야 합니다. 
# on_epoch_end메소드로 정의하여 epoch이 끝날 때 마다 확인해보도록 하겠습니다.
# 아래 처럼 lambda 함수를 작성하여 LambdaCallback를 만들어 주고, 이때 epoch, logs는 신경 안쓰시고 arg 형태만 맞춰주면 됩니다.
# from keras.callbacks import LambdaCallback
# print_weights = LambdaCallback(on_epoch_end=lambda epoch, logs: print(model.layers[3].get_weights()))

 

 - jamo tools

dschloe.github.io/python/tensorflow2.0/ch7_4_naturallanguagegeneration2/

 

Tensorflow 2.0 Tutorial ch7.4 - (2) 단어 단위 생성

공지 본 Tutorial은 교재 시작하세요 텐서플로 2.0 프로그래밍의 강사에게 국비교육 강의를 듣는 사람들에게 자료 제공을 목적으로 제작하였습니다. 강사의 주관적인 판단으로 압축해서 자료를 정

dschloe.github.io

 

 * tf_rnn9_토지_자소단위.ipynb

!pip install jamotools

import tensorflow as tf
import numpy as np
import jamotools

path_to_file = tf.keras.utils.get_file('toji.txt', 'https://raw.githubusercontent.com/pykwon/etc/master/rnn_test_toji.txt')
#path_to_file = 'silrok.txt'
# 데이터 로드 및 확인. encoding 형식으로 utf-8 을 지정해야합니다.
train_text = open(path_to_file, 'rb').read().decode(encoding='utf-8')

# 텍스트가 총 몇 자인지 확인합니다.
print('Length of text: {} characters'.format(len(train_text))) # Length of text: 695685 characters
print()

# 처음 100 자를 확인해봅니다.
s = train_text[:100]
print(s)

# 한글 텍스트를 자모 단위로 분리해줍니다. 한자 등에는 영향이 없습니다.
s_split = jamotools.split_syllables(s)
print(s_split)
Length of text: 695685 characters

제 1 편 어둠의 발소리
1897년의 한가위.
까치들이 울타리 안 감나무에 와서 아침 인사를 하기도 전에, 무색 옷에 댕기꼬리를 늘인 
아이들은 송편을 입에 물고 마을길을 쏘
ㅈㅔ 1 ㅍㅕㄴ ㅇㅓㄷㅜㅁㅇㅢ ㅂㅏㄹㅅㅗㄹㅣ
1897ㄴㅕㄴㅇㅢ ㅎㅏㄴㄱㅏㅇㅟ.
ㄲㅏㅊㅣㄷㅡㄹㅇㅣ ㅇㅜㄹㅌㅏㄹㅣ ㅇㅏㄴ ㄱㅏㅁㄴㅏㅁㅜㅇㅔ ㅇㅘㅅㅓ ㅇㅏㅊㅣㅁ ㅇㅣㄴㅅㅏㄹㅡㄹ ㅎㅏㄱㅣㄷㅗ ㅈㅓㄴㅇㅔ, ㅁㅜㅅㅐㄱ ㅇㅗㅅㅇㅔ ㄷㅐㅇㄱㅣㄲㅗㄹㅣㄹㅡㄹ ㄴㅡㄹㅇㅣㄴ 
ㅇㅏㅇㅣㄷㅡㄹㅇㅡㄴ ㅅㅗㅇㅍㅕㄴㅇㅡㄹ ㅇㅣㅂㅇㅔ ㅁㅜㄹㄱㅗ ㅁㅏㅇㅡㄹㄱㅣㄹㅇㅡㄹ ㅆㅗ

import jamotools

jamotools.split_syllables(s) : 

 

# 7.45 자모 결합 테스트
s2 = jamotools.join_jamos(s_split)
print(s2)
print(s == s2)

# 7.46 자모 토큰화
# 텍스트를 자모 단위로 나눕니다. 데이터가 크기 때문에 약간 시간이 걸립니다.
train_text_X = jamotools.split_syllables(train_text)
vocab = sorted(set(train_text_X))
vocab.append('UNK')
print ('{} unique characters'.format(len(vocab))) # 179 unique characters

# vocab list를 숫자로 맵핑하고, 반대도 실행합니다.
char2idx = {u:i for i, u in enumerate(vocab)}
idx2char = np.array(vocab)

text_as_int = np.array([char2idx[c] for c in train_text_X])
print(text_as_int) # [69 81  2 ...  2  1  0]

# word2idx 의 일부를 알아보기 쉽게 print 해봅니다.
print('{')
for char,_ in zip(char2idx, range(10)):
    print('  {:4s}: {:3d},'.format(repr(char), char2idx[char]))
print('  ...\n}')

print('index of UNK: {}'.format(char2idx['UNK']))
제 1 편 어둠의 발소리
1897년의 한가위.
까치들이 울타리 안 감나무에 와서 아침 인사를 하기도 전에, 무색 옷에 댕기꼬리를 늘인 
아이들은 송편을 입에 물고 마을길을 쏘
True
179 unique characters
[69 81  2 ...  2  1  0]
{
  '\n':   0,
  '\r':   1,
  ' ' :   2,
  '!' :   3,
  '"' :   4,
  "'" :   5,
  '(' :   6,
  ')' :   7,
  ',' :   8,
  '-' :   9,
  ...
}
index of UNK: 178

 

# 7.47 토큰 데이터 확인
print(train_text_X[:20])
print(text_as_int[:20])

# 7.48 학습 데이터세트 생성
seq_length = 80
examples_per_epoch = len(text_as_int) // seq_length
print('examples_per_epoch :', examples_per_epoch)
# examples_per_epoch : 16815
char_dataset = tf.data.Dataset.from_tensor_slices(text_as_int)

char_dataset = char_dataset.batch(seq_length+1, drop_remainder=True) # drop_remainder : 잔여 데이터 제거
for item in char_dataset.take(1):
    print(idx2char[item.numpy()])
#     ['ㅈ' 'ㅔ' ' ' '1' ' ' 'ㅍ' 'ㅕ' 'ㄴ' ' ' 'ㅇ' 'ㅓ' 'ㄷ' 'ㅜ' 'ㅁ' 'ㅇ' 'ㅢ' ' ' 'ㅂ'
#  'ㅏ' 'ㄹ' 'ㅅ' 'ㅗ' 'ㄹ' 'ㅣ' '\r' '\n' '1' '8' '9' '7' 'ㄴ' 'ㅕ' 'ㄴ' 'ㅇ' 'ㅢ' ' '
#  'ㅎ' 'ㅏ' 'ㄴ' 'ㄱ' 'ㅏ' 'ㅇ' 'ㅟ' '.' '\r' '\n' 'ㄲ' 'ㅏ' 'ㅊ' 'ㅣ' 'ㄷ' 'ㅡ' 'ㄹ' 'ㅇ'
#  'ㅣ' ' ' 'ㅇ' 'ㅜ' 'ㄹ' 'ㅌ' 'ㅏ' 'ㄹ' 'ㅣ' ' ' 'ㅇ' 'ㅏ' 'ㄴ' ' ' 'ㄱ' 'ㅏ' 'ㅁ' 'ㄴ'
#  'ㅏ' 'ㅁ' 'ㅜ' 'ㅇ' 'ㅔ' ' ' 'ㅇ' 'ㅘ' 'ㅅ']
    print('item.numpy() :', item.numpy())
# item.numpy() : [69 81  2 13  2 74 82 49  2 68 80 52 89 62 68 95  2 63 76 54 66 84 54 96
#   1  0 13 20 21 19 49 82 49 68 95  2 75 76 49 46 76 68 92 10  1  0 47 76
#  71 96 52 94 54 68 96  2 68 89 54 73 76 54 96  2 68 76 49  2 46 76 62 49
#  76 62 89 68 81  2 68 85 66]

def split_input_target(chunk):
    return [chunk[:-1], chunk[-1]]

train_dataset = char_dataset.map(split_input_target)
for x,y in train_dataset.take(1):
    print(idx2char[x.numpy()])
    print(x.numpy())
    print(idx2char[y.numpy()])
    print(y.numpy())
    
BATCH_SIZE = 64
steps_per_epoch = examples_per_epoch // BATCH_SIZE
BUFFER_SIZE = 5000

train_dataset = train_dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)
ㅈㅔ 1 ㅍㅕㄴ ㅇㅓㄷㅜㅁㅇㅢ ㅂㅏㄹ
[69 81  2 13  2 74 82 49  2 68 80 52 89 62 68 95  2 63 76 54]
examples_per_epoch : 16815
['ㅈ' 'ㅔ' ' ' '1' ' ' 'ㅍ' 'ㅕ' 'ㄴ' ' ' 'ㅇ' 'ㅓ' 'ㄷ' 'ㅜ' 'ㅁ' 'ㅇ' 'ㅢ' ' ' 'ㅂ'
 'ㅏ' 'ㄹ' 'ㅅ' 'ㅗ' 'ㄹ' 'ㅣ' '\r' '\n' '1' '8' '9' '7' 'ㄴ' 'ㅕ' 'ㄴ' 'ㅇ' 'ㅢ' ' '
 'ㅎ' 'ㅏ' 'ㄴ' 'ㄱ' 'ㅏ' 'ㅇ' 'ㅟ' '.' '\r' '\n' 'ㄲ' 'ㅏ' 'ㅊ' 'ㅣ' 'ㄷ' 'ㅡ' 'ㄹ' 'ㅇ'
 'ㅣ' ' ' 'ㅇ' 'ㅜ' 'ㄹ' 'ㅌ' 'ㅏ' 'ㄹ' 'ㅣ' ' ' 'ㅇ' 'ㅏ' 'ㄴ' ' ' 'ㄱ' 'ㅏ' 'ㅁ' 'ㄴ'
 'ㅏ' 'ㅁ' 'ㅜ' 'ㅇ' 'ㅔ' ' ' 'ㅇ' 'ㅘ' 'ㅅ']
item.numpy() : [69 81  2 13  2 74 82 49  2 68 80 52 89 62 68 95  2 63 76 54 66 84 54 96
  1  0 13 20 21 19 49 82 49 68 95  2 75 76 49 46 76 68 92 10  1  0 47 76
 71 96 52 94 54 68 96  2 68 89 54 73 76 54 96  2 68 76 49  2 46 76 62 49
 76 62 89 68 81  2 68 85 66]
['ㅈ' 'ㅔ' ' ' '1' ' ' 'ㅍ' 'ㅕ' 'ㄴ' ' ' 'ㅇ' 'ㅓ' 'ㄷ' 'ㅜ' 'ㅁ' 'ㅇ' 'ㅢ' ' ' 'ㅂ'
 'ㅏ' 'ㄹ' 'ㅅ' 'ㅗ' 'ㄹ' 'ㅣ' '\r' '\n' '1' '8' '9' '7' 'ㄴ' 'ㅕ' 'ㄴ' 'ㅇ' 'ㅢ' ' '
 'ㅎ' 'ㅏ' 'ㄴ' 'ㄱ' 'ㅏ' 'ㅇ' 'ㅟ' '.' '\r' '\n' 'ㄲ' 'ㅏ' 'ㅊ' 'ㅣ' 'ㄷ' 'ㅡ' 'ㄹ' 'ㅇ'
 'ㅣ' ' ' 'ㅇ' 'ㅜ' 'ㄹ' 'ㅌ' 'ㅏ' 'ㄹ' 'ㅣ' ' ' 'ㅇ' 'ㅏ' 'ㄴ' ' ' 'ㄱ' 'ㅏ' 'ㅁ' 'ㄴ'
 'ㅏ' 'ㅁ' 'ㅜ' 'ㅇ' 'ㅔ' ' ' 'ㅇ' 'ㅘ']
[69 81  2 13  2 74 82 49  2 68 80 52 89 62 68 95  2 63 76 54 66 84 54 96
  1  0 13 20 21 19 49 82 49 68 95  2 75 76 49 46 76 68 92 10  1  0 47 76
 71 96 52 94 54 68 96  2 68 89 54 73 76 54 96  2 68 76 49  2 46 76 62 49
 76 62 89 68 81  2 68 85]
ㅅ
66

 

# 7.49 자소 단위 생성 모델 정의
total_chars = len(vocab)
model = tf.keras.Sequential([
    tf.keras.layers.Embedding(total_chars, 100, input_length=seq_length),
    tf.keras.layers.LSTM(units=400, activation='tanh'),
    tf.keras.layers.Dense(total_chars, activation='softmax')
])

model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.summary()   # Total params: 891,279
# 7.50 자소 단위 생성 모델 학습
from tensorflow.keras.preprocessing.sequence import pad_sequences

def testmodel(epoch, logs):
    if epoch % 5 != 0 and epoch != 99:
        return
    
    test_sentence = train_text[:48]
    test_sentence = jamotools.split_syllables(test_sentence)

    next_chars = 300
    for _ in range(next_chars):
        test_text_X = test_sentence[-seq_length:]
        test_text_X = np.array([char2idx[c] if c in char2idx else char2idx['UNK'] for c in test_text_X])
        test_text_X = pad_sequences([test_text_X], maxlen=seq_length, padding='pre', value=char2idx['UNK'])

        output_idx = model.predict_classes(test_text_X)
        test_sentence += idx2char[output_idx[0]]
    
    print()
    print(jamotools.join_jamos(test_sentence))
    print()

testmodelcb = tf.keras.callbacks.LambdaCallback(on_epoch_end=testmodel)

history = model.fit(train_dataset.repeat(), epochs=50, steps_per_epoch=steps_per_epoch, \
                    callbacks=[testmodelcb], verbose=2)
Epoch 1/50
262/262 - 37s - loss: 2.9122 - accuracy: 0.2075
/usr/local/lib/python3.7/dist-packages/tensorflow/python/keras/engine/sequential.py:450: UserWarning: `model.predict_classes()` is deprecated and will be removed after 2021-01-01. Please use instead:* `np.argmax(model.predict(x), axis=-1)`,   if your model does multi-class classification   (e.g. if it uses a `softmax` last-layer activation).* `(model.predict(x) > 0.5).astype("int32")`,   if your model does binary classification   (e.g. if it uses a `sigmoid` last-layer activation).
  warnings.warn('`model.predict_classes()` is deprecated and '

제 1 편 어둠의 발소리
1897년의 한가위.
까치들이 울타리 안 감나무에 와서 안이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알이이 알잉

Epoch 2/50
262/262 - 7s - loss: 2.3712 - accuracy: 0.3002
Epoch 3/50
262/262 - 7s - loss: 2.2434 - accuracy: 0.3256
Epoch 4/50
262/262 - 7s - loss: 2.1652 - accuracy: 0.3414
Epoch 5/50
262/262 - 7s - loss: 2.1132 - accuracy: 0.3491
Epoch 6/50
262/262 - 7s - loss: 2.0670 - accuracy: 0.3600

제 1 편 어둠의 발소리
1897년의 한가위.
까치들이 울타리 안 감나무에 와서 았다.  "아난 강이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는 갈이는

Epoch 7/50
262/262 - 7s - loss: 2.0299 - accuracy: 0.3709
Epoch 8/50
262/262 - 7s - loss: 1.9852 - accuracy: 0.3810
Epoch 9/50
262/262 - 7s - loss: 1.9415 - accuracy: 0.3978
Epoch 10/50
262/262 - 7s - loss: 1.9119 - accuracy: 0.4020
Epoch 11/50
262/262 - 7s - loss: 1.8684 - accuracy: 0.4153

제 1 편 어둠의 발소리
1897년의 한가위.
까치들이 울타리 안 감나무에 와서 아니라고 날 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 ㄱ

Epoch 12/50
262/262 - 7s - loss: 1.8237 - accuracy: 0.4272
Epoch 13/50
262/262 - 7s - loss: 1.7745 - accuracy: 0.4429
Epoch 14/50
262/262 - 7s - loss: 1.7272 - accuracy: 0.4625
Epoch 15/50
262/262 - 7s - loss: 1.6779 - accuracy: 0.4688
Epoch 16/50
262/262 - 7s - loss: 1.6217 - accuracy: 0.4902

제 1 편 어둠의 발소리
1897년의 한가위.
까치들이 울타리 안 감나무에 와서 안 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 간 가

Epoch 17/50
262/262 - 7s - loss: 1.5658 - accuracy: 0.5041
Epoch 18/50
262/262 - 7s - loss: 1.4984 - accuracy: 0.5252
Epoch 19/50
262/262 - 7s - loss: 1.4413 - accuracy: 0.5443
Epoch 20/50
262/262 - 7s - loss: 1.3629 - accuracy: 0.5704
Epoch 21/50
262/262 - 7s - loss: 1.2936 - accuracy: 0.5923

제 1 편 어둠의 발소리
1897년의 한가위.
까치들이 울타리 안 감나무에 와서 아니요."
  "아는 말이 잡아낙에 사람 가나가 가라. 가나구가 가람 가나고. 사남이 바람이 그렇게 없는 노루가 가나가오. 아니라."
  "아는 말이 잡아낙에 사람 가나가 가라. 가나구가 가람 가나고. 사남이 바람이 그렇게 없는 노루가 가나가오. 아니라."
  "아는 말이 잡아낙에 사람 가나가 ㄱ

Epoch 22/50
262/262 - 7s - loss: 1.2142 - accuracy: 0.6217
Epoch 23/50
262/262 - 7s - loss: 1.1281 - accuracy: 0.6505
Epoch 24/50
262/262 - 7s - loss: 1.0444 - accuracy: 0.6786
Epoch 25/50
262/262 - 7s - loss: 0.9711 - accuracy: 0.7047
Epoch 26/50
262/262 - 7s - loss: 0.8712 - accuracy: 0.7445

제 1 편 어둠의 발소리
1897년의 한가위.
까치들이 울타리 안 감나무에 와서 아니요."
  "예, 서방이 타람 자기 있는 놀을 벤  앙이는  곡서방을 마지 않았다. 장모수는 잠 밀 앞은 알 앞은 것이다. 그러나 속으로 나랑치를  그렇더면 정을 비한 것은 알굴이 말고 마른 안 부리 전 물어지를 하는 것이다. 그런 소릴 긴데 없는  갈아조 말 앞은 ㅇ

Epoch 27/50
262/262 - 7s - loss: 0.8168 - accuracy: 0.7620
Epoch 28/50
262/262 - 7s - loss: 0.7244 - accuracy: 0.7985
Epoch 29/50
262/262 - 7s - loss: 0.6301 - accuracy: 0.8362
Epoch 30/50
262/262 - 7s - loss: 0.5399 - accuracy: 0.8695
Epoch 31/50
262/262 - 7s - loss: 0.4745 - accuracy: 0.8950

제 1 편 어둠의 발소리
1897년의 한가위.
까치들이 울타리 안 감나무에 와서 아니요."
 "그건 치신하게 소기일이고 나랑치가  가래 참아노려 하고 사람을 딸려들  하더나건서반다.
 "줄에 나무장 다과 있는데 마을 세나강이 사이오. 나익은 노영은 물을 딸로나 잉인이의 얼굴이  없고 바람을 들었다. 그 천덕이 속을 거밀렀다. 지녁해지직 때 났는데 이 ㅇ

Epoch 32/50
262/262 - 7s - loss: 0.3956 - accuracy: 0.9234
Epoch 33/50
262/262 - 7s - loss: 0.3326 - accuracy: 0.9429
Epoch 34/50
262/262 - 7s - loss: 0.2787 - accuracy: 0.9577
Epoch 35/50
262/262 - 7s - loss: 0.2249 - accuracy: 0.9738
Epoch 36/50
262/262 - 7s - loss: 0.1822 - accuracy: 0.9837

제 1 편 어둠의 발소리
1897년의 한가위.
까치들이 울타리 안 감나무에 와서 아니요."
 "그래 갱째기를 하였던 것이다. 그러나 소습으로  있을 물었다. 가나게, 한장한다. 
 "안 빌리 장에서  나였다. 체신기린 조한 시릴 세에 있이는 노랭이 되었다. 나무지 족은 야우는 물을 만다.  울씨는 지소 가라! 담하는 누눌이 말씨갔다. 
 "일서 좀은 이융이의 ㄴ

Epoch 37/50
262/262 - 7s - loss: 0.1399 - accuracy: 0.9902
Epoch 38/50
262/262 - 7s - loss: 0.1123 - accuracy: 0.9942
Epoch 39/50
262/262 - 7s - loss: 0.0864 - accuracy: 0.9968
Epoch 40/50
262/262 - 7s - loss: 0.0713 - accuracy: 0.9979
Epoch 41/50
262/262 - 7s - loss: 0.0552 - accuracy: 0.9989

제 1 편 어둠의 발소리
1897년의 한가위.
까치들이 울타리 안 감나무에 와서 아니요."
  "전을 짐 집 갚고 불랑이 떳어지는 닷마닥네서 쑤어직 때 가자. 다동이 타타자그마."
  "전을 지작한고, 그런 세닉을 바들 가는고 마진 오를 기는 불림이 최치른다. 한 일을 물었다.
  "눈저 살아노, 흔자하는 나루에."
  "저는 물을 물어져들  자몬 아니요."
  

Epoch 42/50
262/262 - 7s - loss: 0.0431 - accuracy: 0.9994
Epoch 43/50
262/262 - 7s - loss: 0.0325 - accuracy: 0.9998
Epoch 44/50
262/262 - 7s - loss: 0.2960 - accuracy: 0.9110
Epoch 45/50
262/262 - 7s - loss: 0.1939 - accuracy: 0.9540
Epoch 46/50
262/262 - 7s - loss: 0.0542 - accuracy: 0.9979

제 1 편 어둠의 발소리
1897년의 한가위.
까치들이 울타리 안 감나무에 와서 아니요."
  "전을 떰은 탕첫
은 안나무네."
  "그건 심심해 사람을 놀었다. 음....간 들지 휜 오를 
까끄치올을 쓸어얐다. 베여 갈인 덧못이야. 그건 시김이 잡아나게 생가가지가 다라고들 하고 사람들 색했으면 조신 소리를 필징이 갔다. 체공인 든장은 무소 그러핬다

Epoch 47/50
262/262 - 7s - loss: 0.0265 - accuracy: 0.9999
Epoch 48/50
262/262 - 7s - loss: 0.0189 - accuracy: 1.0000
Epoch 49/50
262/262 - 7s - loss: 0.0156 - accuracy: 1.0000
Epoch 50/50
262/262 - 7s - loss: 0.0131 - accuracy: 1.0000
model.save('rnnmodel2.hdf5')

# 7.51 임의의 문장을 사용한 생성 결과 확인
from tensorflow.keras.preprocessing.sequence import pad_sequences
test_sentence = '최참판댁 사랑은 무인지경처럼 적막하다'
test_sentence = jamotools.split_syllables(test_sentence)

next_chars = 5000
for _ in range(next_chars):
    test_text_X = test_sentence[-seq_length:]
    test_text_X = np.array([char2idx[c] if c in char2idx else char2idx['UNK'] for c in test_text_X])
    test_text_X = pad_sequences([test_text_X], maxlen=seq_length, padding='pre', value=char2idx['UNK'])
    
    output_idx = model.predict_classes(test_text_X)
    test_sentence += idx2char[output_idx[0]]
    

print(jamotools.join_jamos(test_sentence))
/usr/local/lib/python3.7/dist-packages/tensorflow/python/keras/engine/sequential.py:450: UserWarning: `model.predict_classes()` is deprecated and will be removed after 2021-01-01. Please use instead:* `np.argmax(model.predict(x), axis=-1)`,   if your model does multi-class classification   (e.g. if it uses a `softmax` last-layer activation).* `(model.predict(x) > 0.5).astype("int32")`,   if your model does binary classification   (e.g. if it uses a `sigmoid` last-layer activation).
  warnings.warn('`model.predict_classes()` is deprecated and '
최참판댁 사랑은 무인지경처럼 적막하다가 최차는 야우는 물을 물었다.
  "내가 적에서 이자사 아니요."
  "전을 떰은 안 불이라 강천 갓촉을 농해 났이 마으러서 같은 노웅이 것을  치문이 참만난 함잉이의 송순이라 강을 덜걱을  눈치를  최철었다.
  "오를 놀짝은 노영은 뭇
이들을 달려들  도만 살알이 되었다. 나무지 족은 야움을 놀을 허렸다. 그건 신기를 물을 달려들 딸을 동렸다. 
“선으로 바람 사람을 바습으로 나왔다. 
간가나게, 사람들 사람을  했는 노래원이 머를 세 있는 것 같았다.
  강산이 족은 양피는 가날이 아니요."
  "예, 산고 가탕이 다시 죽얼 지 구천하게  그러세 얼굴을 달련 덧을  부칠리 질 없는 농은 언분네요."
  "그건 소리가 아니물이 용닌이 되수  성을 흠금글고 달려가지 아나갔다. 
 "그러나 내간이고 내발이 탂어서 달려가지 않나. 무고 고랭각은 다사 죽어러들어지 않았다. 
간곡이얐다. 지낙해지 안 가기요."
 "잔은 안 불린 것 같았다.
  강산이 종굴이  왔고 싶은 상모수는 곳한 곰집은 것 같았다.
  강산이 족은 양피는 가날이 아니요."
  "예, 산고 가탕이 다시 죽얼 지 구천하게  그러세 얼굴을 달련 덧을  부칠리 질 없는 농은 언분네요."
  "그건 소리가 아니물이 용닌이 되수  성을 흠금글고 달려가지 아나갔다. 
 "그러나 내간이고 내발이 탂어서 달려가지 않나. 무고 고랭각은 다사 죽어러들어지 않았다. 
간곡이얐다. 지낙해지 안 가기요."
 "잔은 안 불린 것 같았다.
  강산이 종굴이  왔고 싶은 상모수는 곳한 곰집은 것 같았다.
  강산이 족은 양피는 가날이 아니요."
  "예, 산고 가탕이 다시 죽얼 지 구천하게  그러세 얼굴을 달련 덧을  부칠리 질 없는 농은 언분네요."
  "그건 소리가 아니물이 용닌이 되수  성을 흠금글고 달려가지 아나갔다. 
 "그러나 내간이고 내발이 탂어서 달려가지 않나. 무고 고랭각은 다사 죽어러들어지 않았다. 
간곡이얐다. 지낙해지 안 가기요."
 "잔은 안 불린 것 같았다.
  강산이 종굴이  왔고 싶은 상모수는 곳한 곰집은 것 같았다.
  강산이 족은 양피는 가날이 아니요."
  "예, 산고 가탕이 다시 죽얼 지 구천하게  그러세 얼굴을 달련 덧을  부칠리 질 없는 농은 언분네요."
  "그건 소리가 아니물이 용닌이 되수  성을 흠금글고 달려가지 아나갔다. 
 "그러나 내간이고 내발이 탂어서 달려가지 않나. 무고 고랭각은 다사 죽어러들어지 않았다. 
간곡이얐다. 지낙해지 안 가기요."
 "잔은 안 불린 것 같았다.
  강산이 종굴이  왔고 싶은 상모수는 곳한 곰집은 것 같았다.
  강산이 족은 양피는 가날이 아니요."
  "예, 산고 가탕이 다시 죽얼 지 구천하게  그러세 얼굴을 달련 덧을  부칠리 질 없는 농은 언분네요."
  "그건 소리가 아니물이 용닌이 되수  성을 흠금글고 달려가지 아나갔다. 
 "그러나 내간이고 내발이 탂어서 달려가지 않나. 무고 고랭각은 다사 죽어러들어지 않았다. 
간곡이얐다. 지낙해지 안 가기요."
 "잔은 안 불린 것 같았다.
  강산이 종굴이  왔고 싶은 상모수는 곳한 곰집은 것 같았다.
  강산이 족은 양피는 가날이 아니요."
  "예, 산고 가탕이 다시 죽얼 지 구천하게  그러세 얼굴을 달련 덧을  부칠리 질 없는 농은 언분네요."
  "그건 소리가 아니물이 용닌이 되수  성을 흠금글고 달려가지 아나갔다. 
 "그러나 내간이고 내발이 탂어서 달려가지 않나. 무고 고랭각은 다사 죽어러들어지 않았다. 
간곡이얐다. 지낙해지 안 가기요."
 "잔은 안 불린 것 같았다.
  강산이 종굴이  왔고 싶은 상모수는 곳한 곰집은 것 같았다.
  강산이 족은 양피는 가날이 아니요."
  "예, 산고 가탕이 다시 죽얼 지 구천하게  그러세 얼굴을 달련 덧을  부칠리 질 없는 농은 언분네요."
  "그건 소리가 아니물이 용닌이 되수  성을 흠금글고 달려가지 아나갔다. 
 "그러나 내간이고 내발이 탂어서 달려가지 않나. 무고 고랭각은 다사 죽어러들어지 않았다. 
간곡이얐다. 지낙해지 안 가기요."
 "잔은 안 불린 것 같았다.
  강산이 종굴이  왔고 싶은 상모수는 곳한 곰집은 것 같았다.
  강산이 족은 양피는 가날이 아니요."
  "예, 산고 가탕이 다시 죽얼 지 구천하게  그러세 얼굴을 달련 덧을  부칠리 질 없는 농은 언분네요."
  "그건 소리가 아니물이 용닌이 되수  성을 흠금글고 달려가지 아나갔다. 
 "그러나 내간이고 내발이 탂어서 달려가지 않나. 무고 고랭각은 다사 죽어러들어지 않았다. 
간곡이얐다. 지낙해지 안 가기요."
 "잔은 안 불린 것 같았다.
  강산이 종굴이  왔고 싶은 상모수는 곳한 곰집은 것 같았다.
  강산이 족은 양피는 가날이 아니요."
  "예, 산고 가탕이 다시 죽얼 지 구천하게  그러세 얼굴을 달련 덧을  부칠리 질 없는 농은 언분네요."
  "그건 소리가 아니물이 용닌이 되수  성을 흠금글고 달려가지 아나갔다. 
 "그러나 내간이고 내발이 탂어서 달려가지 않나. 무고 고랭각은 다사 죽어러들어지 않았다. 
간곡이얐다. 지낙해지 안 가기요."
 "잔은 안 불린 것 같았다.
  강산이 종굴이  왔고 싶은 상모수는 곳한 곰지

 

RNN을 이용한 스펨메일 분류(이진 분류)

 

 * tf_rnn10_스팸메일분류.ipynb

import pandas as pd

data = pd.read_csv('https://raw.githubusercontent.com/pykwon/python/master/testdata_utf8/spam.csv', encoding='latin1')
print(data.head())
print('샘플 수 : ', len(data))                       # 샘플 수 :  5572
del data['Unnamed: 2']
del data['Unnamed: 3']
del data['Unnamed: 4']
print(data.head())
#      v1  ... Unnamed: 4
# 0   ham  ...        NaN
# 1   ham  ...        NaN
# 2  spam  ...        NaN
# 3   ham  ...        NaN
# 4   ham  ...        NaN
print(data.v1.unique())                              # ['ham' 'spam']
data['v1'] = data['v1'].replace(['ham', 'spam'], [0, 1])
print(data.head())

# Null 여부 확인
print(data.isnull().values.any())                     # False
print(data.info())

# 중복 데이터 확인
print(data['v2'].nunique())                           # 5169

data.drop_duplicates(subset=['v2'], inplace=True)
print('중복 데이터 제거 후 샘플 수 : ', len(data))    # 5169

print(data.groupby('v1').size().reset_index(name='count'))
#    v1  count
# 0   0   4516
# 1   1    653

# feature(v2), label(v1) 분리
xdata = data['v2']
ydata = data['v1']
print(xdata[:3])
# 0    Go until jurong point, crazy.. Available only ...
# 1                        Ok lar... Joking wif u oni...
# 2    Free entry in 2 a wkly comp to win FA Cup fina...

print(ydata[:3])
# 0    0
# 1    0
# 2    1

 

 - token 처리

from tensorflow.keras.preprocessing.text import Tokenizer
tok = Tokenizer()
tok.fit_on_texts(xdata)
print(tok.word_index) # {'i': 1, 'to': 2, 'you': 3, 'a': 4, 'the': 5, 'u': 6, 'and': 7, 'in': 8, 'is': 9, 'me': 10 ...
sequences = tok.texts_to_sequences(xdata)
print(xdata[:5])
# 0    Go until jurong point, crazy.. Available only ...
# 1                        Ok lar... Joking wif u oni...
# 2    Free entry in 2 a wkly comp to win FA Cup fina...
# 3    U dun say so early hor... U c already then say...
# 4    Nah I don't think he goes to usf, he lives aro...
print(sequences[:5])
# [[47, 433, 4013, 780, 705, 662, 64, 8, 1202, 94, 121, 434, 1203, ...
word_index = tok.word_index
print(word_index)
# {'i': 1, 'to': 2, 'you': 3, 'a': 4, 'the': 5, 'u': 6, 'and': 7, 'in': 8, 'is': 9, 'me': 10, ...
print(len(word_index)) # 8920
# 전체 자료 중 등장빈도 수, 비율 확인
threshold = 2                   # 등장빈도 수를 제한
total_count = len(word_index)   # 전체 단어 수
rare_count = 0                  # 빈도 수가 threshold 보다 작은 경우
total_freq = 0                  # 전체 단어 빈도 수 총합 비율
rare_freq = 0                   # 빈도 수 가 threshold보다 작은 경우의 단어 빈도 수 총합 비율 전체 자료 중 등장빈도 수, 비율 확인
threshold = 2                   # 등장빈도 수를 제한
total_count = len(word_index)   # 전체 단어 수
rare_count = 0                  # 빈도 수가 threshold 보다 작은 경우
total_freq = 0                  # 전체 단어 빈도 수 총합 비율
rare_freq = 0                   # 빈도 수 가 threshold보다 작은 경우의 단어 빈도 수 총합 비율

# dict type의 단어/빈도수 얻기
for key, value in tok.word_counts.items():
    #print('k:{} va:{}'.format(key, value))
    # k:jd va:1
    # k:accounts va:1
    # k:executive va:2
    # k:parents' va:2
    # k:picked va:7
    # k:downstem va:1
    # k:08718730555 va:
    total_freq = total_freq + value

    if value < threshold:
        rare_count = rare_count + 1
        rare_freq = rare_freq + value

print('등장빈도가 1회인 단어 수 :', rare_count)                                # 등장빈도가 1회인 단어 수 : 4908
print('등장빈도가 1회인 단어 비율 :', (rare_count / total_count) * 100)        # 등장빈도가 1회인 단어 비율 : 55.02242152466368
print('전체 중 등장빈도가 1회인 단어 비율 :', (rare_freq / total_freq) * 100)  # 전체 중 등장빈도가 1회인 단어 비율 : 6.082538108811501

tok = Tokenizer(num_words= total_count - rare_count + 1)
vocab_size = len(word_index) + 1
print('단어 집합 크기 :', vocab_size)   # 단어 집합 크기 : 8921

# train/test 8:2
n_of_train = int(len(sequences) * 0.8)
n_of_test = int(len(sequences) - n_of_train)
print('train lenghth :', n_of_train)    # train lenghth : 4135
print('test lenghth :', n_of_test)      # test lenghth : 1034

# 메일의 길이 확인
x_data = sequences
print('메일의 최대 길이 :', max((len(i) for i in x_data)))          # 메일의 최대 길이 : 189
print('메일의 평균 길이 :', (sum(map(len, x_data)) / len(x_data)))  # 메일의 평균 길이 : 15.610369510543626

# 시각화
import matplotlib.pyplot as plt
plt.hist([len(siz) for siz in x_data], bins=50)
plt.xlabel('length')
plt.ylabel('count')
plt.show()

from tensorflow.keras.preprocessing.sequence import pad_sequences
max_len = max((len(i) for i in x_data))
data = pad_sequences(x_data, maxlen=max_len)
print(data.shape)       # (5169, 189)

# train/test 분리
import numpy as np
x_train = data[:n_of_train]
y_train = np.array(ydata[:n_of_train])
x_test = data[n_of_train:]
y_test = np.array(ydata[n_of_train:])
print(x_train.shape, x_train[:2])   # (4135, 189)
print(y_train.shape, y_train[:2])   # (4135,)
print(x_test.shape, y_test.shape)   # (1034, 189) (1034,)

 

# 모델
from tensorflow.keras.layers import LSTM, Embedding, Dense, Dropout
from tensorflow.keras.models import Sequential

model = Sequential()
model.add(Embedding(vocab_size, 32))
model.add(LSTM(32, activation='tanh'))
model.add(Dense(32, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(1, activation='sigmoid'))
print(model.summary())      # Total params: 294,881

model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])
history = model.fit(x_train, y_train, epochs=5, batch_size=32, validation_split=0.25, verbose=2)
print('loss, acc :', model.evaluate(x_test, y_test))    # loss, acc : [0.05419406294822693, 0.9893617033958435]

# print(x_test[0])
# loss, acc 변화에 대한 시각화
epochs = range(1, len(history.history['acc']) + 1)
plt.plot(epochs, history.history['loss'])
plt.plot(epochs, history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train loss', 'validation_loss'])
plt.show()

plt.plot(epochs, history.history['acc'])
plt.plot(epochs, history.history['val_acc'])
plt.xlabel('epoch')
plt.ylabel('acc')
plt.legend(['train acc', 'validation_acc'])
plt.show()

로이터 뉴스 분류하기

 

wikidocs.net/22933

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

Keras를 이용한 One-hot encoding, Embedding

cafe.daum.net/flowlife/S2Ul/19

dacon.io/codeshare/1892

wikidocs.net/33520

 

word2vec tf idf

blog.naver.com/PostView.nhn?blogId=happyrachy&logNo=221285427229&parentCategoryNo=&categoryNo=16&viewDate=&isShowPopularPosts=false&from=postView

 

 

 * tf_rnn11_뉴스카테고리분류.ipynb

import numpy as np
import tensorflow as tf
from tensorflow.keras.datasets import reuters
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Embedding
from tensorflow.keras.preprocessing import sequence
from tensorflow.keras.utils import to_categorical

np.random.seed(3)
tf.random.set_seed(3)

#print(reuters.load_data())
# (x_train, y_train), (x_test, y_test) = reuters.load_data()
# print(x_train.shape, y_train.shape, x_test.shape, y_test.shape) # (8982,) (8982,) (2246,) (2246,)

(x_train, y_train), (x_test, y_test) = reuters.load_data(num_words=1000, test_split=0.2) # test_split=0.2 default, num_words=1000 : 빈도 순위 1000이하 값만 출력
#(x_train, y_train), (x_test, y_test) = reuters.load_data(num_words=None, test_split=0.2) # test_split=0.2 default
print(x_train.shape, y_train.shape, x_test.shape, y_test.shape) # (8982,) (8982,) (2246,) (2246,)

category = np.max(y_train) + 1
print('category :', category)   # category : 46
print(x_train[:3])              # 숫자가 작을 수록 빈도수가 높음
# [list([1, 2, 2, 8, 43, 10, 447, 5, 25, 207, 270, 5, 2, 111, 16, 369, 186, 90, 67, 7, 89, ... 109, 15, 17, 12])
#  list([1, 2, 699, 2, 2, 56, 2, 2, 9, 56, 2, 2, 81, 5, 2, 57, 366, 737, 132, 20, 2, 7, 2, ...  2, 505, 17, 12])
#  list([1, 53, 12, 284, 15, 14, 272, 26, 53, 959, 32, 818, 15, 14, 272, 26, 39, 684, 70, ... 59, 11, 17, 12])]
print(y_train[:3])              # [3 4 3]
print(len(s) for s in x_train)

import matplotlib.pyplot as plt
plt.hist([len(s) for s in x_train], bins = 50)
plt.xlabel('length')
plt.ylabel('number')
plt.show()

 - 데이터 구성

word_index = reuters.get_word_index()
print(word_index)       # {'mdbl': 10996, 'fawc': 16260, 'degussa': 12089, 'woods': 8803, 'hanging': 13796, ... }

index_to_word = {}
for k, v in word_index.items():
    index_to_word[v] = k

print(index_to_word)    # {10996: 'mdbl', 16260: 'fawc', 12089: 'degussa', 8803: 'woods', 13796: 'hanging ... }
print(index_to_word[1]) # the
print(index_to_word[10])    # for
print(index_to_word[100])   # group

print(x_train[0])                                       # [1, 2, 2, 8, 43, 10, 447, 5, 25, 207, 270, 5, 2, 111, 16, 369, ...
print(' '.join(index_to_word[i] for i in x_train[0]))   # he of of mln loss for plc said at only ended said of  ... 

 

 - 모델

x_train = sequence.pad_sequences(x_train, maxlen=100)
x_test = sequence.pad_sequences(x_test, maxlen=100)
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
print(x_test)
# [[  0   0   0 ...  15  17  12]
#  [  0   0   0 ... 505  17  12]
#  [ 19 758  15 ...  11  17  12]
#  ...
#  [  0   0   0 ... 407  17  12]
#  [ 88   2  72 ... 364  17  12]
#  [125   2  21 ... 113  17  12]]
#print(y_test)

model = Sequential()
model.add(Embedding(1000, 100))
model.add(LSTM(100, activation='tanh'))
model.add(Dense(46, activation='softmax'))
print(model.summary())  # Total params: 185,046

model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

history = model.fit(x_train, y_train, batch_size=64, epochs=50, validation_data=(x_test, y_test), verbose=2)

 

 - 시각화

vloss = history.history['val_loss']
loss = history.history['loss']
x_len = np.arange(len(loss))
plt.plot(x_len, vloss, marker='.', c='red', label='train val_loss')
plt.plot(x_len, loss, marker='o', c='blue', label='train loss')
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend()
plt.show()

vacc = history.history['val_accuracy']
acc = history.history['accuracy']
x_len = np.arange(len(acc))
plt.plot(x_len, vacc, marker='.', c='red', label='train val_acc')
plt.plot(x_len, acc, marker='o', c='blue', label='train acc')
plt.xlabel('epoch')
plt.ylabel('acc')
plt.legend()
plt.show()


 - IMDB

wikidocs.net/24586

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

 

tf 2 에서 RNN(LSTM) sample

cafe.daum.net/flowlife/S2Ul/21

 

 

 * tf_rnn12_IMDB감성분류.ipynb

import numpy as np
import tensorflow as tf
from tensorflow.keras.datasets import imdb
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Embedding
from tensorflow.keras.preprocessing import sequence
from tensorflow.keras.utils import to_categorical
import matplotlib.pyplot as plt

np.random.seed(3)
tf.random.set_seed(3)
# print(imdb.load_data())
vocab_size = 10000
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=vocab_size)
print(x_train.shape, y_train.shape, x_test.shape, y_test.shape) # (25000,) (25000,) (25000,) (25000,)

print(y_train[:3])                              # [1 0 0]
num_classes = max(y_train) + 1
print('num_classes :',num_classes)              # num_classes : 2
print(set(y_train), ' ', np.unique(y_train))    # {0, 1}   [0 1]

print(x_train[0])                               # [1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, ...
print(y_train[0])                               # 1

 

 - 시각화 : 훈련용 리뷰 분포

len_result = [len(s) for s in x_train]
print('리뷰 최대 길이 :', np.max(len_result))  # 2494
print('리뷰 평균 길이 :', np.mean(len_result)) # 238.71364

plt.subplot(1, 2, 1)
plt.boxplot(len_result)
plt.subplot(1, 2, 2)
plt.hist(len_result, bins=50)
plt.show()

 

 - 긍/부정 빈도수

unique_ele, counts_ele = np.unique(y_train, return_counts=True)
print(np.asarray((unique_ele, counts_ele)))
# [[    0     1]
#  [12500 12500]]

 

 - index에 대한 단어 출력

word_to_index = imdb.get_word_index()
index_to_word = {}
for k, v in word_to_index.items():
    index_to_word[v] = k
print(index_to_word)        # {34701: 'fawn', 52006: 'tsukino', 52007: 'nunnery', 16816: 'sonja', ...
print(index_to_word[1])     # the
print(index_to_word[1408])  # woods

print(x_train[0])           # [1, 14, 22, 16, 43, 530, 973, 1622, ...
print(y_train[0])           # 1
print(' '.join([index_to_word[index] for index in x_train[0]]))     # the as you with out themselves powerful lets loves their ...

 

 - LSTM으로 감성분류

from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
from tensorflow.keras.models import load_model

max_len = 500
x_train = pad_sequences(x_train, maxlen=max_len)
x_test = pad_sequences(x_test, maxlen=max_len)
# print(x_train[0])

model = Sequential()
model.add(Embedding(vocab_size, 100))
model.add(LSTM(120, activation='tanh'))
model.add(Dense(1, activation='sigmoid'))

print(model.summary())      # Total params: 1,106,201

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['acc'])
es = EarlyStopping(monitor='val_loss', mode='auto', patience=3, baseline=0.01)
ms = ModelCheckpoint('tfrmm12.h5', monitor='val_acc', mode='max', save_best_only = True)
model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=100, batch_size=64, verbose=2, callbacks=[es, ms])

loaded_model = load_model('tfrmm12.h5')
print('acc :',loaded_model.evaluate(x_test, y_test)[1])     # acc : 0.8718400001525879
print('loss :',loaded_model.evaluate(x_test, y_test)[0])    # loss : 0.3080214262008667

 

 - CNN으로 텍스트 분류

from tensorflow.keras.layers import Conv1D, GlobalMaxPooling1D, MaxPooling1D, Dropout

model = Sequential()
model.add(Embedding(vocab_size, 256))
model.add(Conv1D(256, kernel_size=3, padding='valid', activation='relu', strides=1))
model.add(GlobalMaxPooling1D())
model.add(Dense(1, activation='sigmoid'))
print(model.summary())          # Total params: 2,757,121

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['acc'])
es = EarlyStopping(monitor='val_loss', mode='auto', patience=3, baseline=0.01)
ms = ModelCheckpoint('tfrmm12_1.h5', monitor='val_acc', mode='max', save_best_only = True)
history = model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=100, batch_size=64, verbose=2, callbacks=[es, ms])

loaded_model = load_model('tfrmm12_1.h5')
print('acc :',loaded_model.evaluate(x_test, y_test)[1])     # acc : 0.8984400033950806
print('loss :',loaded_model.evaluate(x_test, y_test)[0])    # loss : 0.24771703779697418

 

 - 시각화

vloss = history.history['val_loss']
loss = history.history['loss']
x_len = np.arange(len(loss))
plt.plot(x_len, vloss, marker='+', c='black', label='val_loss')
plt.plot(x_len, loss, marker='s', c='red', label='loss')
plt.legend()
plt.grid()
plt.show()

import re
def sentiment_predict(new_sentence):
  new_sentence = re.sub('[^0-9a-zA-Z ]', '', new_sentence).lower()

  # 정수 인코딩
  encoded = []
  for word in new_sentence.split():
    # 단어 집합의 크기를 10,000으로 제한.
    try :
      if word_to_index[word] <= 10000:
        encoded.append(word_to_index[word]+3)
      else:
        encoded.append(2)   # 10,000 이상의 숫자는 <unk> 토큰으로 취급.
    except KeyError:
      encoded.append(2)   # 단어 집합에 없는 단어는 <unk> 토큰으로 취급.

  pad_new = pad_sequences([encoded], maxlen = max_len) # 패딩
  
  # 예측하기
  score = float(loaded_model.predict(pad_new)) 
  if(score > 0.5):
    print("{:.2f}% 확률로 긍정!.".format(score * 100))
  else:
    print("{:.2f}% 확률로 부정!".format((1 - score) * 100))
# 99.57% 확률로 긍정!.
# 53.55% 확률로 긍정!.

# 긍/부정 분류 예측
#temp_str = "This movie was just way too overrated. The fighting was not professional and in slow motion."
temp_str = "This movie was a very touching movie."
sentiment_predict(temp_str)

temp_str = " I was lucky enough to be included in the group to see the advanced screening in Melbourne on the 15th of April, 2012. And, firstly, I need to say a big thank-you to Disney and Marvel Studios."
sentiment_predict(temp_str)

 


네이버 영화 리뷰 데이터를 이용해 분류 모델 작성

한국어 불용어, 토크나이징 툴

cafe.daum.net/flowlife/9A8Q/156

 

 * tf_rnn13_naver감성분류.ipynb

#! pip install konlpy

import numpy as np
import pandas as pd
import matplotlib as plt
import re
from konlpy.tag import Okt
from tensorflow.keras.layers import Embedding, Dense, LSTM, Dropout
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
train_data = pd.read_table('https://raw.githubusercontent.com/pykwon/python/master/testdata_utf8/ratings_train.txt')
test_data = pd.read_table('https://raw.githubusercontent.com/pykwon/python/master/testdata_utf8/ratings_test.txt')
print(train_data[:3], len(train_data))  # 150000
print(test_data[:3], len(test_data))    # 50000
#          id                           document  label
# 0   9976970                아 더빙.. 진짜 짜증나네요 목소리      0
# 1   3819312  흠...포스터보고 초딩영화줄....오버연기조차 가볍지 않구나      1
# 2  10265843                  너무재밓었다그래서보는것을추천한다      0
print(train_data.columns)               # Index(['id', 'document', 'label'], dtype='object')
# imsi = train_data.sample(n=1000, random_state=123)
# print(imsi)

 

 - 데이터 전처리

# 데이터 전처리
print(train_data['document'].nunique(), test_data['document'].nunique())    # 146182 49157 => 중복자료가 있다.

train_data.drop_duplicates(subset=['document'], inplace=True)               # 중복 제거
print(len(train_data['document']))  # 146183
print(set(train_data['label']))     # {0 부정, 1 긍정}

train_data['label'].value_counts().plot(kind='bar')
plt.show()

print(train_data.groupby('label').size())
# label
# 0    73342
# 1    72841

 - Null 값 확인

print(train_data.isnull().values.any()) # True
print(train_data.isnull().sum())
# id          0
# document    1
# label       0
print(train_data.loc[train_data.document.isnull()])
#             id document  label
# 25857  2172111      NaN      1

train_data = train_data.dropna(how='any')
print(train_data.isnull().values.any())     # False
print(len(train_data))                      # 146182

 

 - 순수 한글 관련 자료 이외의 구둣점 등은 제거

print(train_data[:3])
train_data['document'] = train_data['document'].str.replace("[^ㄱ-ㅎ ㅏ-ㅣ 가-힣]","")
print(train_data[:3])

train_data['document'].replace('', np.nan, inplace=True)
print(train_data.isnull().sum())
# id            0
# document    391
# label         0

train_data = train_data.dropna(how='any')
print(train_data.isnull().values.any()) # False
print(len(train_data))                  # 145791
# test
test_data.drop_duplicates(subset=['document'], inplace=True)               # 중복 제거
test_data['document'] = test_data['document'].str.replace("[^ㄱ-ㅎ ㅏ-ㅣ 가-힣]","")
test_data['document'].replace('', np.nan, inplace=True)
test_data = test_data.dropna(how='any')
print(test_data.isnull().values.any())  # False
print(len(test_data))                   # 48995

 

 - 불용어 제거 & 형태소 분류

# 불용어 제거
stopwords = ['아','휴','아이구','아이쿠','아이고','어','나','우리','저희','따라','의해','을','를','에','의','가','으로','로','에게','뿐이다','의거하여']

# 형태소 분류
okt = Okt()
x_train = []
for sen in train_data['document']:
    imsi = []
    imsi = okt.morphs(sen, stem=True)   # stem=True : 어간 추출
    imsi = [word for word in imsi if not word in stopwords]
    x_train.append(imsi)

print(x_train[:3])
# [['더빙', '진짜', '짜증나다', '목소리'], ['흠', '포스터', '보고', '초딩', '영화', '줄', '오버', '연기', '조차', '가볍다', '않다'], ['너', '무재', '밓었', '다그', '래서', '보다', '추천', '한', '다']]

x_test = []
for sen in test_data['document']:
    imsi = []
    imsi = okt.morphs(sen, stem=True)   # stem=True : 어간 추출
    imsi = [word for word in imsi if not word in stopwords]
    x_test.append(imsi)

print(x_test[:3])
# [['굳다', 'ㅋ'], ['뭐', '야', '이', '평점', '들', '은', '나쁘다', '않다', '점', '짜다', '리', '는', '더', '더욱', '아니다'], ['지루하다', '않다', '완전', '막장', '임', '돈', '주다', '보기', '에는']]

 

 - 워드 임베딩

tok = Tokenizer()
tok.fit_on_texts(x_train)
print(tok.word_index)
# {'이': 1, '영화': 2, '보다': 3, '하다': 4, '도': 5, '들': 6, '는': 7, '은': 8, '없다': 9, '이다': 10, '있다': 11, '좋다': 12, ...

 

 - 등장 빈도수를 확인해서 비중이 적은 자료는 배제

threshold = 3
total_cnt = len(tok.word_index)
rare_cnt = 0
total_freq = 0
rare_freq = 0

for k, v in tok.word_counts.items():
    total_freq = total_freq + v
    if v < threshold:
        rare_cnt = rare_cnt + 1
        rare_freq = rare_freq + v

print('total_cnt :', total_cnt)                         # 43753
print('rare_cnt :', rare_cnt)                           # 24340
print('rare_freq :', (rare_cnt / total_cnt) * 100)      # 55.63047105341348
print('total_cnt :', (rare_freq / total_freq) * 100)    # 1.71278110414947
# 2회 이하인 단어 전체 비중 1.7%이므로 2회 이하인 단어들은 배제해도 문제가 없을 것 같다

 

 - OOV(Out of Vocabulary) : 단어 사전에 없으면 index자체를 할 수 없게 되는데 이런 문제를 OOV

vocab_size = total_cnt - rare_cnt + 2
print('vocab_size 크기 :', vocab_size)                  # 19415

tok = Tokenizer(vocab_size, oov_token='OOV')
tok.fit_on_texts(x_train)
x_train = tok.texts_to_sequences(x_train)
x_test = tok.texts_to_sequences(x_test)
print(x_train[:3])
# [[462, 23, 268, 665], [953, 463, 47, 609, 3, 221, 1454, 31, 967, 682, 25], [393, 2447, 1, 2317, 5669, 4, 227, 17, 15]]

y_train = np.array(train_data['label'])
y_test = np.array(test_data['label'])

 

 - 비어 있는 샘플은 제거

drop_train = [index for index, sen in enumerate(x_train) if len(sen) < 1]

x_train = np.delete(x_train, drop_train, axis = 0)
y_train = np.delete(y_train, drop_train, axis = 0)
print(len(x_train), ' ', len(y_train))

print('리뷰 최대 길이 :', max(len(i) for i in x_train))             # 75
print('리뷰 평균 길이 :', sum(map(len, x_train)) / len(x_train))    # 12.169516185172293

plt.hist([len(s) for s in x_train], bins = 50)
plt.show()

 

 - 전체 샘플 중에서 길이가 max_len 이하인 샘플 비율이 몇 % 인지 확인 함수 작성

def below_threshold_len(max_len, nested_list):
    cnt = 0
    for s in nested_list:
        if len(s) < max_len:
            cnt = cnt + 1
    print('전체 샘플 중에서 길이가 %s 이하인 샘플 비율 : %s'%(max_len, (cnt / len(nested_list)) * 100 ))
    # 전체 샘플 중에서 길이가 30 이하인 샘플 비율 : 92.13574660633485

max_len = 30
below_threshold_len(max_len, x_train)   # 92% 정도가 30 이하의 길이를 가짐

x_train = pad_sequences(x_train, maxlen=max_len)
x_test = pad_sequences(x_test, maxlen=max_len)
print(x_train[:5])
# [[    0     0     0     0     0     0     0     0     0     0     0     0
#       0     0     0     0     0     0     0     0     0     0     0     0
#       0     0   462    23   268   665]
#  [    0     0     0     0     0     0     0     0     0     0     0     0
#       0     0     0     0     0     0     0   953   463    47   609     3
#     221  1454    31   967   682    25]
#  [    0     0     0     0     0     0     0     0     0     0     0     0
#       0     0     0     0     0     0     0     0     0   393  2447     1
#    2317  5669     4   227    17    15]
#  [    0     0     0     0     0     0     0     0     0     0     0     0
#       0     0     0     0     0     0     0     0     0  6492   112  8118
#     225    62     8    10    33  3604]
#  [    0     0     0     0     0     0     0     0     0     0     0     0
#       0  1029     1    36  9143    31   837     3  2579    27  1114   246
#       5 14239     1  1080   260   246]]

 

 - 모델

model = Sequential()
model.add(Embedding(vocab_size, 100))
model.add(LSTM(128, activation='tanh'))
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.25))
model.add(Dense(1, activation='sigmoid'))
print(model.summary())  # Total params: 2,075,389

model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])

es = EarlyStopping(monitor='val_loss', mode = 'min', verbose=1, patience=3)
mc = ModelCheckpoint('tfrnn13.h5', monitor='val_acc', mode = 'max', save_best_only=True)
history = model.fit(x_train, y_train, epochs=10, callbacks=[es, mc], batch_size=64, validation_split=0.2)

 

 - 저장된 모델로 나머지 작업

load_model = load_model('tfrnn13.h5')
print('acc :', load_model.evaluate(x_test, y_test)[1])  # acc : 0.847739577293396
print('loss :', load_model.evaluate(x_test, y_test)[0]) # loss : 0.35434380173683167

 

 - 예측

def new_pred(new_sentence):
    new_sentence = okt.morphs(new_sentence, stem = True)
    new_sentence = [word for word in new_sentence if not word in stopwords]
    encoded = tok.texts_to_sequences([new_sentence])
    pad_new = pad_sequences(encoded, maxlen=max_len)
    pred = float(load_model.predict(pad_new))
    if pred < 0.5:
        print('{:.2f}% 확률로 긍정'.format(pred * 100))
    else:
        print('{:.2f}% 확률로 부정'.format((1 - pred) * 100))

new_pred('영화가 재밌네요')
new_pred('심하다 지루하고 졸려')
new_pred('주인공이 너무 멋있어 추천하고 싶네요')
# 6.13% 확률로 부정
# 0.05% 확률로 긍정
# 0.87% 확률로 부정

 


Sequence-to-Sequence

 - 시퀀스 투 시퀀스

wikidocs.net/24996

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

 

 - 영어를 불어로 번역하는 작업

 * tf_rnn14_s2s번역.ipynb

import pandas as pd
import urllib3
import zipfile
import shutil
import os
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.utils import to_categorical

...

Attension

RNN 관련

cafe.daum.net/flowlife/S2Ul/33

 

자연어처리모델.pdf

attention.pdf

 

RNN → LSTM → Seq2Seq → Transformer → GPT-1 → BERT → GPT-3

 

 

자연어 처리를 위한 작업 2

cafe.daum.net/flowlife/S2Ul/29

 

 - 양방향 LSTM + 어텐션 메커니즘(BiLSTM with Attention Mechanism)

wikidocs.net/48920

 

 - IMDB 리뷰 데이터로 감성 분류 : LSTM + Attension (Transformer 기반 기술)

 * tf_rnn15_attention.ipynb

# IMDB 리뷰 데이터로 감성 분류 : LSTM + Attension (Transformer 기반 기술)
# 양방향 LSTM과 어텐션 메커니즘(BiLSTM with Attention mechanism)

from tensorflow.keras.datasets import imdb
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.preprocessing.sequence import pad_sequences

vocab_size = 10000
(X_train, y_train), (X_test, y_test) = imdb.load_data(num_words = vocab_size)

print('리뷰의 최대 길이 : {}'.format(max(len(l) for l in X_train)))         # 리뷰의 최대 길이 : 2494
print('리뷰의 평균 길이 : {}'.format(sum(map(len, X_train))/len(X_train)))  # 리뷰의 평균 길이 : 238.71364

max_len = 500
X_train = pad_sequences(X_train, maxlen=max_len)
X_test = pad_sequences(X_test, maxlen=max_len)

...

 

 

 

 

'BACK END > Deep Learning' 카테고리의 다른 글

[딥러닝] GAN  (0) 2021.04.12
[딥러닝] Tensorflow - 이미지 분류  (0) 2021.04.01
[딥러닝] Keras - Logistic  (0) 2021.03.25
[딥러닝] Keras - Linear  (0) 2021.03.23
[딥러닝] TensorFlow  (0) 2021.03.22

+ Recent posts

1