Create a RNN model to text generation

  • RNN model at character level
    • Input: n character previous
    • Output: next character
    • Model LSTM
  • Use 'El Quijote' to train the generator

In [1]:
# Header
from __future__ import print_function

import numpy as np
import tensorflow as tf
print('Tensorflow version: ', tf.__version__)
import time

#Show images
import matplotlib.pyplot as plt
%matplotlib inline
# plt configuration
plt.rcParams['figure.figsize'] = (10, 10)        # size of images
plt.rcParams['image.interpolation'] = 'nearest'  # show exact image
plt.rcParams['image.cmap'] = 'gray'  # use grayscale 


# GPU devices visible by python
import os
os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID" 
os.environ["CUDA_VISIBLE_DEVICES"]="0"


path = '/home/ubuntu/data/training/text/quijote/'


Tensorflow version:  1.2.1

Download data and generate sequences

Download quijote from guttenberg project

wget http://www.gutenberg.org/cache/epub/2000/pg2000.txt


In [2]:
#Read book
text = open(path + "pg2000.txt").read().lower()
print('corpus length:', len(text))

# Simplify text to improve the semantic capacities of the model.
delete_chars = [ '"', '#', '$', '%', "'", '(', ')', '*', '-', '/', '0', '1', '2', '3', '4', '5', '6',
                '7', '8', '9', '@', '[', ']', '«', '»', 'à', 'ï', 'ù', '\ufeff']
for ch in delete_chars:
    text=text.replace(ch,"")

print('corpus length deleted:', len(text))

chars = sorted(list(set(text)))
print('Chars list: ', chars)
print('total chars:', len(chars))

#Dictionaries to convert char to num & num to char
char_indices = dict((c, i) for i, c in enumerate(chars))
indices_char = dict((i, c) for i, c in enumerate(chars))


corpus length: 2117498
corpus length deleted: 2108713
Chars list:  ['\n', ' ', '!', ',', '.', ':', ';', '?', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '¡', '¿', 'á', 'é', 'í', 'ñ', 'ó', 'ú', 'ü']
total chars: 43

In [3]:
# cut the text in semi-redundant sequences of maxlen characters
# One sentence of length 20 for each 3 characters
maxlen = 20
step = 3
sentences = []
next_chars = []
for i in range(3000, len(text) - maxlen, step): #Start in character 3000 to exclude Gutenberg header.
    sentences.append(text[i: i + maxlen])
    next_chars.append(text[i + maxlen])
print('nb sequences:', len(sentences))
print(sentences[4996], '-', next_chars[4996])


nb sequences: 701898
s de
santos, sino pr - o

Train the model


In [4]:
'''
X: One row by sentence
    in each row a matrix of bool 0/1 of dim length_sentence x num_chars coding the sentence. Dummy variables
y: One row by sentence
    in each row a vector of bool of lengt num_chars with 1 in the next char position
'''

print('Vectorization...')
X = np.zeros((len(sentences), maxlen, len(chars)), dtype=np.bool)
y = np.zeros((len(sentences), len(chars)), dtype=np.bool)
#X = np.zeros((len(sentences), maxlen, len(chars)), dtype=np.float16)
#y = np.zeros((len(sentences), len(chars)), dtype=np.float16)

for i, sentence in enumerate(sentences):
    for t, char in enumerate(sentence):
        X[i, t, char_indices[char]] = 1
    y[i, char_indices[next_chars[i]]] = 1

print('X shape: ',X.shape)
print('y shape: ',y.shape)


Vectorization...
X shape:  (701898, 20, 43)
y shape:  (701898, 43)

In [5]:
# build the model: 2 stacked LSTM
from tensorflow.contrib.keras import models, layers, optimizers

print('Build model 1')
seq_prev_input = layers.Input(shape=(maxlen, len(chars)), name='prev') 
                
# apply forwards LSTM
forwards1 = layers.LSTM(1024, return_sequences=True,  dropout=0.3, recurrent_dropout=0.3)(seq_prev_input)

forwards2 = layers.LSTM(1024, return_sequences=True,  dropout=0.3, recurrent_dropout=0.3)(forwards1)

forwards3 = layers.LSTM(1024, return_sequences=False, dropout=0.3, recurrent_dropout=0.3)(forwards2)

output = layers.Dense(len(chars), activation='softmax')(forwards3)

model = models.Model(inputs=seq_prev_input, outputs=output)
model.summary()

# try using different optimizers and different optimizer configs
nadam = optimizers.Nadam(lr=0.0002, schedule_decay=0.000025)
model.compile(loss='categorical_crossentropy', optimizer=nadam, metrics=['accuracy'])


Build model 1
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
prev (InputLayer)            (None, 20, 43)            0         
_________________________________________________________________
lstm_1 (LSTM)                (None, None, 1024)        4374528   
_________________________________________________________________
lstm_2 (LSTM)                (None, None, 1024)        8392704   
_________________________________________________________________
lstm_3 (LSTM)                (None, 1024)              8392704   
_________________________________________________________________
dense_1 (Dense)              (None, 43)                44075     
=================================================================
Total params: 21,204,011
Trainable params: 21,204,011
Non-trainable params: 0
_________________________________________________________________

In [6]:
#Plot the model graph
from tensorflow.contrib.keras import utils

# Create model image
utils.plot_model(model, '/tmp/model.png')

# Show image
plt.imshow(plt.imread('/tmp/model.png'))


Out[6]:
<matplotlib.image.AxesImage at 0x7f01a8109748>

In [7]:
#Fit model
history = model.fit(X[:600000], y[:600000], batch_size=256, epochs=12,
           validation_data=(X[600000:], y[600000:]))


Train on 600000 samples, validate on 101898 samples
Epoch 1/12
600000/600000 [==============================] - 505s - loss: 2.4033 - acc: 0.2875 - val_loss: 2.0501 - val_acc: 0.3810
Epoch 2/12
600000/600000 [==============================] - 508s - loss: 1.8432 - acc: 0.4291 - val_loss: 1.7644 - val_acc: 0.4735
Epoch 3/12
600000/600000 [==============================] - 518s - loss: 1.6088 - acc: 0.4989 - val_loss: 1.6457 - val_acc: 0.5136
Epoch 4/12
600000/600000 [==============================] - 518s - loss: 1.4872 - acc: 0.5347 - val_loss: 1.5881 - val_acc: 0.5304
Epoch 5/12
600000/600000 [==============================] - 518s - loss: 1.4123 - acc: 0.5544 - val_loss: 1.5485 - val_acc: 0.5434
Epoch 6/12
600000/600000 [==============================] - 518s - loss: 1.3587 - acc: 0.5696 - val_loss: 1.5309 - val_acc: 0.5489
Epoch 7/12
600000/600000 [==============================] - 518s - loss: 1.3166 - acc: 0.5809 - val_loss: 1.5132 - val_acc: 0.5545
Epoch 8/12
600000/600000 [==============================] - 518s - loss: 1.2822 - acc: 0.5906 - val_loss: 1.5066 - val_acc: 0.5596
Epoch 9/12
600000/600000 [==============================] - 518s - loss: 1.2518 - acc: 0.5990 - val_loss: 1.4951 - val_acc: 0.5640
Epoch 10/12
600000/600000 [==============================] - 518s - loss: 1.2245 - acc: 0.6065 - val_loss: 1.4966 - val_acc: 0.5655
Epoch 11/12
600000/600000 [==============================] - 518s - loss: 1.2002 - acc: 0.6128 - val_loss: 1.4907 - val_acc: 0.5687
Epoch 12/12
600000/600000 [==============================] - 518s - loss: 1.1766 - acc: 0.6200 - val_loss: 1.4892 - val_acc: 0.5692
Train on 600000 samples, validate on 101898 samples Epoch 1/12 600000/600000 [==============================] - 498s - loss: 2.4591 - acc: 0.2761 - val_loss: 2.0847 - val_acc: 0.3696 Epoch 2/12 600000/600000 [==============================] - 502s - loss: 1.8640 - acc: 0.4239 - val_loss: 1.7899 - val_acc: 0.4668 Epoch 3/12 600000/600000 [==============================] - 500s - loss: 1.6327 - acc: 0.4928 - val_loss: 1.6655 - val_acc: 0.5043 Epoch 4/12 600000/600000 [==============================] - 498s - loss: 1.5090 - acc: 0.5279 - val_loss: 1.5971 - val_acc: 0.5277 Epoch 5/12 600000/600000 [==============================] - 502s - loss: 1.4298 - acc: 0.5502 - val_loss: 1.5617 - val_acc: 0.5406 Epoch 6/12 600000/600000 [==============================] - 499s - loss: 1.3731 - acc: 0.5663 - val_loss: 1.5364 - val_acc: 0.5490 Epoch 7/12 600000/600000 [==============================] - 503s - loss: 1.3293 - acc: 0.5780 - val_loss: 1.5153 - val_acc: 0.5558 Epoch 8/12 600000/600000 [==============================] - 498s - loss: 1.2930 - acc: 0.5880 - val_loss: 1.5035 - val_acc: 0.5598 Epoch 9/12 600000/600000 [==============================] - 484s - loss: 1.2626 - acc: 0.5956 - val_loss: 1.4958 - val_acc: 0.5621 Epoch 10/12 600000/600000 [==============================] - 484s - loss: 1.2349 - acc: 0.6040 - val_loss: 1.4885 - val_acc: 0.5675 Epoch 11/12 600000/600000 [==============================] - 484s - loss: 1.2103 - acc: 0.6106 - val_loss: 1.4891 - val_acc: 0.5670 Epoch 12/12 600000/600000 [==============================] - 484s - loss: 1.1856 - acc: 0.6176 - val_loss: 1.4867 - val_acc: 0.5681

In [8]:
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize'] = (8, 8)

plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])
plt.show()



In [9]:
# Save model

models.save_model(model, path + 'models/text_generation_model1024.h5')

Evaluate model


In [12]:
# Load model

model1 = models.load_model(path + 'models/text_generation_model1024.h5')

In [13]:
maxlen = 20


def sample(a, diversity=1.0):
    '''
    helper function to sample an index from a probability array
    - Diversity control the level of randomless
    '''
    a = np.log(a) / diversity
    a = np.exp(a) / np.sum(np.exp(a), axis=0)
    a /= np.sum(a+0.0000001) #Precission error
    return np.argmax(np.random.multinomial(1, a, 1))


def generate_text(sentence, diversity, current_model, num_char=400):
    sentence_init = sentence
    generated = ''
    for i in range(400):
        x = np.zeros((1, maxlen, len(chars)))
        for t, char in enumerate(sentence):
            x[0, t, char_indices[char]] = 1.
        preds = current_model.predict(x, verbose=0)[0]
        next_index = sample(preds, diversity)
        next_char = indices_char[next_index]
        generated += next_char
        sentence = sentence[1:] + next_char
    print()
    print('DIVERSITY: ',diversity)
    print(sentence_init + generated)

In [14]:
sentence = 'mire vuestra merced '
generate_text(sentence, 0.2, model1)


DIVERSITY:  0.2
mire vuestra merced a su padre, y el de la cabeza y de la mano en la cuenta de la mano en la cuerda, y el cual no le dejasen de ver en la caballeriza, y de la mano en la cabeza, y que el trabajo de la caballería de la mancha, que es tan buena como si los había para ellos que los encantadores de los malos de la mano en la cabeza, y dijo:

a lo que el duque dijo don quijote, que es la memoria de la mano en la cabeza y 

In [15]:
sentence = 'mire vuestra merced '
generate_text(sentence, 0.2, model1)
generate_text(sentence, 0.5, model1)
generate_text(sentence, 1,   model1)
generate_text(sentence, 1.2, model1)


sentence = 'a mi señora dulcinea'
generate_text(sentence, 0.2, model1)
generate_text(sentence, 0.5, model1)
generate_text(sentence, 1,   model1)
generate_text(sentence, 1.2, model1)


sentence = 'el caballero andante'
generate_text(sentence, 0.2, model1)
generate_text(sentence, 0.5, model1)
generate_text(sentence, 1,   model1)
generate_text(sentence, 1.2, model1)


DIVERSITY:  0.2
mire vuestra merced se ha de hacer esta sazón don quijote, que es tan buen señor don quijote, y que el trabajo que estaba en el mundo de la mancha, que es tan buen señor don quijote, que es tan como el que le había de ser de la mano en la condesa y de la mano en la venta que el castillo de la mancha, que el caballero de la mano y de la mano y de la mano en la cabeza, y sin duda, que es menester un poco de la mano en 

DIVERSITY:  0.5
mire vuestra merced tan siempre en los caballeros andantes, de malda fe de ser esos años de su hija y con mucha presteza del
toboso, y que no hay más valiente que allí le están en la tierra y la mano por el de la mano en la mano de su amo que un hombre de la silla, y los presentes de los que decían.

sancho dijo don quijote, y del cual se le había dicho, y de la cual no hay para qué todo lo cual respondió don quijote

DIVERSITY:  1
mire vuestra merced por hacer dijo don quijote; porque sea alto de bendite y respondió sancho.

de los días y los fravequísimos por el zuento
que los días soldea del jado un bato que pedía y que no debe ha de parar confuso, y querría, vio que en tanto trabajo de mi ínpe, y que jamás haya sin hacer
de dios paréceme; que le consintió del bestia.
yole
acebcar el ago, movimo de poco
monteniente y sentado entre los escude

DIVERSITY:  1.2
mire vuestra merced se ha
de hacer, sin poco tágar
alzamente, sino pór la orden de la cóleria, ni dueles,
y plítima y ante y valen; y aun de
cendida, porque en los reglos ni otro, al percinesco o la una ventana, las labradoras de
rasca ¿figura es ása. y, aunque las mejores antuverseles le pustáredes, sino los jamás de gobernador y grandeiero, jrocia; porque le habían
húbito pedía, y
a los granjes
dijo sancho que en s

DIVERSITY:  0.2
a mi señora dulcinea del toboso, y que el que los hallares en
el mundo que le había de ser de los caballeros andantes que en la memoria de la mano en la cosa de su amo y de mi padre y lo que parece que se le habían de hacer esta nueva en la cabeza, y que el trabajo de la caballería de la mancha, que es tan buena como si los habían de ser de la mano y de manera que el cielo no le había de ser de los caballeros andante

DIVERSITY:  0.5
a mi señora dulcinea, desta lanza en las manos del alma que es la vida de la lanza, y aun de cuanto más de los señores del ánimo de la cabeza, y de los
escuderos de los caballeros andantes. pero, con todo eso, se fue un grande estremo de la hora, que no se aventajaba a ver si se le ha de ser de los caballeros andantes. viendo la cabeza hermosura, que es que no hay más dicho y aconteció
que el duque de mí me ha de ser

DIVERSITY:  1
a mi señora dulcinea del temor, acordó por los ojos sobre la fila, o ya es asímilla,
   el traje en zas veces que tengo de dos horas de memoria, yo me manda al caballero de los perernagios, preguntaron en brazo a cuestas cuáles de nuestro mío y
fin el dulto de bacía y valiente al pajo de alcanzar lo que le
dulces para lotes, por lo menos de
saber quién malice o autros vuestras voces; los cuales entraron más fiembro!


DIVERSITY:  1.2
a mi señora dulcinea que
continule leído, y que ganar
ninguna dal mundo!

tú imaginación y rompase.

eso no fuere poder por ser luego, toda rescubierte y verdadera venturosas pasos como los días, yo, volviendo entre en
umbañado, pero ¡ombusiclero ni azándo las descapitán los tarderes miligros del aire, sobre el uno, de cuatro reobles y dejallado y paciones de
maese pedro? recebían de valieron, aunque me
diera ni tant

DIVERSITY:  0.2
el caballero andante, que es posible de la mano en la mano y de la cual ni más de la mano en la cuenta de su amo y con la mano en la mano y de aquellos que están en el mundo que le dijese que el cielo le había de ser de la mancha, que es tan buen señor don quijote, y de la mancha que le dejó el cura, y el cual visto por la mano, y que el tal caballero de la caballería que es la memoria la cabeza, y que el que le dies

DIVERSITY:  0.5
el caballero andante, y de los caballeros andantes. en esto de los que le había visto la ventana, y de los caballeros de los caballeros andantes que yo puede dar a mi padre y en un castillo de la noche, y es que se le estaba de poco más de la mano en todo cuanto has dicho, que no será más de lo que pasó el mayor que le habían de hacer por el retirar el barbero en tu ligero, y todo lo cual ya de allí
a pierna de la qu

DIVERSITY:  1
el caballero andante, naviso de lo que quiere yo vea sino
hablar por todo aquella noche, que te saca que es él solos dijo lotario los avisar por donde no era? verdad, le llegó
a dorotea, que procuran topado el libro de mi tierra, a aquel aldea, el labrador
de la señora don quijote por una, no es posible que resitn melindrosa; y no se me da placer el
hombre, que quedarán
tantos
hidalgos, aunque virásolemente, y no te 

DIVERSITY:  1.2
el caballero andante con el que la quiteria, se úsoló la derácueza y son tantas impertilecesas
que don quijote que, si se replárado y ama de ser procicios
de gautas veumas ¡entra mí para imitar se le esto, y, digió su donor con justa invozce como una, oyendo
escribe a los ojos blancas de don qiien pudo ser medio abríar lo que los escribe lomohillos, él comiédade muy
discreta respondió suele defecho,
que si las costas
('\n\nDIVERSITY: ', 0.2, '\n') mire vuestra merced decís, y que se le pareció que en la cabeza de la cabeza, y el caballero del caballero de la cabeza, y los demás de los demás de los demás de los demás de la mano de la mujer de la mano de la mano, y aun a su señora dulcinea del toboso, y el cura que el caballero de la mano que le pareció que estaba en la misma cosa que en la mitad del caballero de la mano, se le dijo: -no sé -respondi ('\n\nDIVERSITY: ', 0.5, '\n') mire vuestra merced que no está en la cabeza. pero, en efeto, pues todo aquello que está en el mundo que cada uno debía de ser con la misma sierra parte de la muerte de la mano, con todo el mundo me la conoció, que tenía por el primero que están el rostro en las manos, y al señor don quijote -respondió sancho-, porque en la puerta de los sucesos del buen estado de la desencantada de los ojos. -¿qué mal ('\n\nDIVERSITY: ', 1, '\n') mire vuestra merced que, aunque, desdicho y tiempo viene, a cuya cabeza destos tres, los informaciones que dejara de serle, que me fueron? ¿admirado, y, creyendo que me va y enfermo. y esto que tienen entonces las requiebros que algunos limpios en un ampeoso como si improvisentes en sus insimulables y en los míos al que el honesto, en la mano de mucho premio y don quijote fingióno los dos o sabidores, y, acom ('\n\nDIVERSITY: ', 1.2, '\n') mire vuestra merced paso que no sa cluero-. subió, señor, le hubiera vuelto el duque, que pica, yo fue pose�ría de guardar cierto. para los demás, esperando la misma tragua debe de haberlas hallado su santa hijo ni debían para mí, porque yo hay de platar pre subir tú turba -dijo el deleitable-; otras porturas, sino como alabanzas y comedimientos puciese los días de lo que von mirado después de visión de ('\n\nDIVERSITY: ', 0.2, '\n') de lo que sucedió a la mano de la mano de la mano de los ojos de la mano de la mano de la mano, y aun más que se le dijese la mano de la mano y en la mitad del caballero del caballero de la mano de la mano, y que el parte de la mano, y que en la mitad del caballero de la mano, sino a la puerta de la mano de la mano de la mano de los demás de la cabeza, y el cura y la cabeza de la mano y en la cabeza de la cabeza, ('\n\nDIVERSITY: ', 0.5, '\n') de lo que sucedió a camila, por ser tan alta de la mano y en voz brazo? ¿qué es lo que pudiera, señor don quijote que en las fermosuras que después que le sacarán con don quijote había de ser la duquesa, la cual no le había de ser muy buena como gausa, y si este deseo de ser mejor que en la cabeza está a los dos de la mano, y su amo no le puede dar a su casa por los manos de mano, diciendo: -pues, ¿qué ('\n\nDIVERSITY: ', 1, '\n') de lo que sucedió a las más faltas cuentan haciendo: -�qcorrían por esto, bueno -respondió cómo está aquel mano traía furia por ella estajo más que andaba y fingión de sus apartieron de modo que no hay vasto qué hizo con galdáis que soy sin duda, duque ni en la más buena gran señora dulcinea; y así lo han don quijote y no bastaba junto a nuestra señora dulcinea, ahora la entienda, el aposento a ('\n\nDIVERSITY: ', 1.2, '\n') de lo que sucedió al admiración, dijo: -eso me esplevo en razón encantada, y su venimo tiempo, que pasaba tan hirtoria. es don quijote, le dijo: y cuando jamás: si ya entienden en mi padre desde aquí vean fuerza, como yo costa yo he oído decir, el rey vención que ésto, que lo més comenzó a vuestra merced colgaré por la orden y parece que pudieron semplarse. el cual, si de la mono. acudi� ('\n\nDIVERSITY: ', 0.2, '\n') de allí a poco comenzó a su caballero andante, que en la más hermosa de la mano, y el cura que el mundo tenía con el cura y el de la mano de la cabeza de la mano de la mano de la mano de la mano de la cabeza, y a los demás de los cuatro de la mano de la mano de la mano y en la mitad del caballero de la cabeza, y le dijo: -¡oh sancho -dijo el cura-, que no se le habían de ser manos de los demás de los de la ('\n\nDIVERSITY: ', 0.5, '\n') de allí a poco comenzó a la vida de la mano de arriba al mundo. pero, con todo eso, ha de ser el rostro de las linajes de su escudero. por el rey en el mundo de la industria que en su caballero andante; y, aunque se volvió a camila y de la mano de la muerte de mi cabeza? y así, como el tal caballero andante, que los demás juramentos, y con la mitad del toboso, y así, por decir que os son de allí a los deseos ('\n\nDIVERSITY: ', 1, '\n') de allí a poco comenzó a dos crazos de entender que tratar la nueva al desde nuevas andantes de solos con los detros donde me hubieran de subir la sumiera y rabia la duquesa a un hacer con un gato, que le dijo juntar la locura para su amo, para que quisieron decir alguna, vino todas aquellos demás caballeros. pero, sancho, tanto, viendo camila la primero tiene: prodición que yo le dio caminar las belloz ('\n\nDIVERSITY: ', 1.2, '\n') de allí a poco comer. otros días se le pidía hablar de cerra que el gate predice y otras puntas del cordel bestia. -yo no por eso, decaría sobre la venta, porque él saslos demás sin él que llevase a buscar de la suma, sancho milático, cuando posían los zogados. y si así, mi rendido a cuerpo, ni en llopar salió el baece-, que el hábísimo que viene por los tratas y tontos que sean, y el de los

In [ ]: