ML & DL

RNN을 이용한 텍스트 생성 연습

yennle 2022. 4. 7. 22:04
728x90

https://wikidocs.net/45101

위의 사이트를 보고 RNN을 이용한 텍스트 생성을 연습해보았다.

 

전체적인 과정은 이렇다.

먼저 여러 문장들을 단어별로 나누고

문장을 앞에서부터 잘라 여러 배열을 만들고

모델 학습을 통해 단어 혹은 문장 뒤에 올 새로운 단어를 예측하는 것이다.

 

 

먼저 라이브러리부터 임포트!

import numpy as np
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, Dense, SimpleRNN

 

학습시키고자 하는 문자열들을 내가 임의로 작성했다.

text="""오늘은 날씨가 맑다\n
날씨가 좋으면 찾아가겠어요\n
내일이 오늘보다 춥다고 한다\n
하늘이 맑으면 기분이 좋다\n
걷다가 고개를 올려 하늘을 봐요\n
사람이 걷다가 넘어질 수도 있지\n
고개를 넘는 사람들을 보았다\n"""

 

단어 집합(딕셔너리)을 생성한다.

# 단어 집합 생성

tokenizer = Tokenizer()
tokenizer.fit_on_texts([text])
vocab_size = len(tokenizer.word_index) + 1

 

그리고 훈련 데이터를 생성한다.

예를 들어 "하늘이 맑으면", "하늘이 맑으면 기분이", "하늘이 맑으면 기분이 좋다" 

이런식으로 단어들을 묶어줄 것이다.

그리고 "하늘이", "하늘이 맑으면", "하늘이 맑으면 기분이"가 X

"맑으면", "기분이", "좋다" 가 y가 될 것이다.

이것을 문자열로 저장을 하는 것은 아니고 위에 부여한 인덱스를 포함한 배열로 만들 것이다.

일단을 함께 한 배열에 저장하고 뒤에서 X와 y를 나누어 줄 것이다.

# 훈련 데이터 생성

# 아직 x, y를 분리하지 않은 데이터이다.
# 배열의 마지막 값이 y값이 된다. ex) 1,2,5,6,2,1,8...

sequences = list()
for line in text.split("\n"):
    encoded = tokenizer.texts_to_sequences([line])[0]
    for i in range(2,len(encoded)+1):
        sequences.append(encoded[:i])

 

그리고 데이터의 길이를 똑같이 맞춰줄 것이다.

위에 사진에 보면 길이가 들쑥날쑥 다 다른 것을 확인할 수 있다.

이것을 가장 긴 길이를 기준으로 0을 넣어 크기를 맞춰준다.

이것을 패딩이라고 한다.

# 가장 긴 샘플을 찾고, 그 길이로 패딩

# 길이를 맞추는데, 앞에 0으로 채워넣는다.

max_len = max(len(s) for s in sequences)
sequences = pad_sequences(sequences, maxlen=max_len, padding='pre') # post는 뒤에

 

아까 X와 y가 한 배열에 함께 들어있다고 했고, 마지막 숫자가 y의 인덱스라고 했다.

그것을 나누어 준다.

# x, y 분리

X = sequences[:,:-1]
y = sequences[:,-1]

 

그 다음은 y값을 원-핫 인코딩을 해준다.

# 원-핫 인코딩

y = to_categorical(y, num_classes=vocab_size)

 

이제 데이터 준비는 끝났다.

모델을 만들어준다.

# RNN 모델 설계하기

model = Sequential()
model.add(Embedding(vocab_size, 10))  # vocab_size를 10으로 임베딩하겠다
model.add(SimpleRNN(32))  # 32는 은닉층의 차원
model.add(Dense(vocab_size, activation = 'softmax')) # 출력층엔 차원은 vacab_size

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

 

모델을 학습한다.

epochs는 학습의 횟수를 의미한다.

model.fit(X,y, epochs=200, verbose=2)

 

마지막으로 테스를 해보자!!!!!

def makeSentence(word, cnt):
    
    for i in range(cnt):
        encoded = tokenizer.texts_to_sequences([word])[0]
        encoded = pad_sequences([encoded], maxlen=max_len, padding='pre')
        result = model.predict(encoded, verbose=0)
        result = np.argmax(result, axis=1)  # 가장 큰 값의 인덱스를 리턴하는 함수

        word += " " + list(tokenizer.word_index.items())[result[0]][0]
    
    print(word)

 

결과가 내 맘같지 않다,,ㅎㅎㅎㅎㅎㅎㅎㅎㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

 

728x90