https://gamefaqs.gamespot.com/ps3/652686-the-last-of-us/faqs/68485
위 사이트에서 해당 게임 대사 스크립트를 받아 올 수 있습니다. 해당 데이터를 활용해, 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
"""
임의의 대사를 뽑아 넣어주고, 아웃풋으로 어떤 값을 뽑아내는지 확인해보았습니다. 문법적이나, 문맥적으로 상당히 어색한 부분이 많습니다.
다만, 아주 짧은 시간내에 어느 정도의 유의미한 결과는 내고 있다는 것을 알 수 있습니다. 좀 더 많은 데이터와 어휘와 레이어를 가지고 훈련을 진행한다면, 지금보다 더 좋은 성능을 낼 수 있을 것으로 보여집니다.
추후에 기회가 된다면, 다양한 시도를 해볼 생각입니다.