SW/Python

Python : Keras : RNN : 대사 주고 받는 인공지능 : 예제, 사용법, 활용법

얇은생각 2020. 2. 11. 07:30
반응형

script ai

 

https://gamefaqs.gamespot.com/ps3/652686-the-last-of-us/faqs/68485

 

The Last of Us - Game Script - PlayStation 3 - By Shotgunnova - GameFAQs

 

gamefaqs.gamespot.com

 

위 사이트에서 해당 게임 대사 스크립트를 받아 올 수 있습니다. 해당 데이터를 활용해, RNN을 활용해, 대사를 주고 받을 수 있는 인공지능을 만들어 보도록 하겠습니다.

 

 

%matplotlib inline
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

from keras.layers import *
from keras.models import *
from keras.utils import *
from sklearn.preprocessing import *
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
import re
import random

 

필요한 라이브러리들을 임포트합니다.

 

 

path = "lastofus.txt"

text = ''

with open(path, encoding='UTF8') as f:
    lines = f.readlines()
    text = text.join([l for l in lines if re.match(r'^[A-Z].*:', l)])

k = []
for t in text.split('\n')[:1000]:
    k.append(t)

k

"""
["Joel: Tommy, I-...Tommy. Tommy, listen to me. He's the contractor, okay? I",
 'Sarah: Hey.',
 'Joel: Scoot.',
 'Sarah: Fun day at work, huh?',
 "Joel: What are you still doing up? It's late.",
 'Sarah: Oh crud. What time is it?',
 "Joel: It's way past your bedtime.",
 "Sarah: But it's still today.",
 'Joel: Honey, please not right now. I do not have the energy for this.',
 """

 

이제 스크립트를 저장한 파일을 불러옵니다. 그 다음, 정규화 식을 통해, 각 라인마다 문자들만 가지고 옵니다. 그다음, 줄바꿈이 들어가 있는 문자들은 잘게 쪼개주고, 리스트에 담아줍니다. 

리스트에 담은 형태는 위 주석을 통해 확인할 수 있습니다.

 

 

 

token = Tokenizer(lower=False, filters='.,?;\'\"-')
token.fit_on_texts(k)

train_seq = token.texts_to_sequences(k)
train_mat = token.texts_to_matrix(k)
train_seq = pad_sequences(train_seq, maxlen=10)

train_seq

"""
array([[ 130,  626,    8, ...,  627,  104,    3],
       [   0,    0,    0, ...,    0,   17,   58],
       [   0,    0,    0, ...,    0,    1,  628],
       ...,
       [  57, 1382,   22, ...,   21,   55,  418],
       [   0,    0,    0, ...,    0,    7,  328],
       [   0,    0,    0, ...,    1, 1384,    9]])
"""

 

이제 문자 데이터를 훈련할 수 있도록 숫자데이터로 바꾸어줍니다. 프레임워크가 바로 문자를 인식하지 못하기 때문에 위와 같은 전처리를 해주어야 합니다. 

좋은 라이브러리 함수들이 제공되어 어렵지 않게 구현할 수 있습니다. 다만 아쉬운점은 이런 과정없이 바로바로 훈려이 가능한 라이브러리들이 생기면 좋을 것 같습니다. 

 

 

 

X = train_seq
Y = np.vstack((X[1:], X[0]))

X = X.reshape(-1, 10, 1)
Y = Y.reshape(-1, 10, 1)

Y = to_categorical(Y)

Y.shape

"""
(1000, 10, 1385)
"""

 

이제 훈련 데이터를 설정하여 줍니다. 입력데이터로 대사를 넣어주고 출력데이터로는 다음 대사를 넣어줍니다.

 

model = Sequential()
model.add(Bidirectional(SimpleRNN(128, return_sequences=True), input_shape=(10, 1)))
model.add(Bidirectional(SimpleRNN(128, return_sequences=True)))
model.add(Dense(1385))
model.add(Activation('softmax'))

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

model.fit(X, Y, epochs=20, batch_size=1)

"""
Epoch 1/20
1000/1000 [==============================] - 22s 22ms/step - loss: 5.0193 - acc: 0.3227
Epoch 2/20
1000/1000 [==============================] - 18s 18ms/step - loss: 4.6459 - acc: 0.3264
Epoch 3/20
1000/1000 [==============================] - 22s 22ms/step - loss: 4.5030 - acc: 0.3278
Epoch 4/20
1000/1000 [==============================] - 24s 24ms/step - loss: 4.3591 - acc: 0.3259 2s - loss: 4.3318 - acc: 
Epoch 5/20
1000/1000 [==============================] - 23s 23ms/step - loss: 4.1959 - acc: 0.3258
Epoch 6/20
1000/1000 [==============================] - 21s 21ms/step - loss: 4.0528 - acc: 0.3259
Epoch 7/20
1000/1000 [==============================] - 20s 20ms/step - loss: 3.9161 - acc: 0.3247
Epoch 8/20
1000/1000 [==============================] - 21s 21ms/step - loss: 3.7635 - acc: 0.3246
Epoch 9/20
1000/1000 [==============================] - 19s 19ms/step - loss: 3.6335 - acc: 0.3250
Epoch 10/20
1000/1000 [==============================] - 20s 20ms/step - loss: 3.5002 - acc: 0.3236
Epoch 11/20
1000/1000 [==============================] - 18s 18ms/step - loss: 3.3590 - acc: 0.3342
Epoch 12/20
1000/1000 [==============================] - ETA: 0s - loss: 3.2272 - acc: 0.341 - 21s 21ms/step - loss: 3.2280 - acc: 0.3415
Epoch 13/20
1000/1000 [==============================] - 20s 20ms/step - loss: 3.1175 - acc: 0.3482 0s - loss: 3.1131 
Epoch 14/20
1000/1000 [==============================] - 20s 20ms/step - loss: 3.0063 - acc: 0.3553
Epoch 15/20
1000/1000 [==============================] - 21s 21ms/step - loss: 2.8884 - acc: 0.3697
Epoch 16/20
1000/1000 [==============================] - 21s 21ms/step - loss: 2.7634 - acc: 0.3806
Epoch 17/20
1000/1000 [==============================] - 21s 21ms/step - loss: 2.6790 - acc: 0.3967
Epoch 18/20
1000/1000 [==============================] - 21s 21ms/step - loss: 2.5652 - acc: 0.4083
Epoch 19/20
1000/1000 [==============================] - 21s 21ms/step - loss: 2.4927 - acc: 0.4165
Epoch 20/20
1000/1000 [==============================] - 21s 21ms/step - loss: 2.4060 - acc: 0.4295
"""

 

입력데이터로 대사를 10개 넣어줍니다. 그 다음, 스크립트에 나와있는 다음 대사를 아웃풋으로 넣어주고 훈련을 진행합니다. 정확도는 약 42% 정도 나오는 것을 확인할 수 있습니다. 

이제 대사를 넣어보고 어떤 출력 데이터를 보여주는지 확인해보도록 하겠습니다.

 

 

idx_word = {}
for w in token.word_index:
    idx_word[token.word_index[w]] = w

for i in range(10):
    temp = ''
    ran = random.randrange(0, len(X))
    pred = model.predict(np.expand_dims(X[ran], axis=0))
    pred = np.argmax(pred, axis=2)

    for line in pred:
        for word in line:        
            if word != 0:            
                temp += idx_word[word]
                temp += ' '
        temp += '\n'
    print(k[ran])
    print(temp)
    
"""
Joel: Sarah! Okay. Move your hands, baby.
Fireflies Joel: with there 

Joel: Just gimme a minute.
Daddy oh girl 

Tommy: Get back! There's too many of 'em. This way! Through the alley! Go!
place with necessary 

Joel: Yep.
Right honey 

Joel: Please. It's my daughter. I think her leg's broken.
the of shipment Fireflies! 

Joel: Tess, how're you holdin' up?
to taken roof 

Man: Hear they took Marianne?
breathin Woman: s stuff 

Reporter: Lady, get the hell outta here right--
Sarah: Uh what the Fireflies 

Sentry: Fucking Robert. This rat better be good for it.
telling zone is done it some 

Marlene: This way. It's not far now.
think How s holding up 
"""

 

임의의 대사를 뽑아 넣어주고, 아웃풋으로 어떤 값을 뽑아내는지 확인해보았습니다. 문법적이나, 문맥적으로 상당히 어색한 부분이 많습니다.

다만, 아주 짧은 시간내에 어느 정도의 유의미한 결과는 내고 있다는 것을 알 수 있습니다. 좀 더 많은 데이터와 어휘와 레이어를 가지고 훈련을 진행한다면, 지금보다 더 좋은 성능을 낼 수 있을 것으로 보여집니다.

추후에 기회가 된다면, 다양한 시도를 해볼 생각입니다.

반응형