In [1]:
# reveal.js presentation configuration
from notebook.services.config import ConfigManager

cm = ConfigManager()
cm.update('livereveal', {
              'theme': 'league',
              'transition': 'fade',
              'center': 'false',
              'overview' : 'true',
              'start_slideshow_at': 'selected'
})

# imports
import theano
from theano import tensor
import codecs
import numpy
import sys
from blocks import initialization
from blocks import roles
from blocks.model import Model
from blocks.bricks import Linear, NDimensionalSoftmax
from blocks.bricks.parallel import Fork
from blocks.bricks.recurrent import GatedRecurrent
from blocks.bricks.lookup import LookupTable
from blocks.filter import VariableFilter
from blocks.serialization import load_parameters
from blocks.bricks import NDimensionalSoftmax

Language modeling with RNN

Fabio A. González, Universidad Nacional de Colombia

Setup

  • Training data: Biblia Reina Valera 1960
  • Software:
    • Blocks: "Blocks is a framework that helps you build neural network models on top of Theano"
    • Theano: "Theano is a Python library that allows you to define, optimize, and evaluate mathematical expressions involving multi-dimensional arrays efficiently"

Training data


In [2]:
# Load training file to get vocabulary
text_file = 'biblia.txt' # input file
with codecs.open(text_file, 'r', 'utf-8') as f:
    data = f.read()
    
chars = list(set(data))
vocab_size = len(chars)
char_to_ix = {ch: i for i, ch in enumerate(chars)}
ix_to_char = {i: ch for i, ch in enumerate(chars)}
print "Total number of chars:", len(data)
print "Vocabulary size:", vocab_size


Total number of chars: 978848
Vocabulary size: 85

Training data


In [3]:
print data[21000:22000]


s será medido. 3 ¿Y por qué miras la paja que está en el ojo de tu hermano, y no echas de ver la viga que está en tu propio ojo? 4 ¿O cómo dirás a tu hermano: Déjame sacar la paja de tu ojo, y he aquí la viga en el ojo tuyo? 5 ¡Hipócrita! saca primero la viga de tu propio ojo, y entonces verás bien para sacar la paja del ojo de tu hermano.

6 No deis lo santo a los perros, ni echéis vuestras perlas delante de los cerdos, no sea que las pisoteen, y se vuelvan y os despedacen.

La oración, y la regla de oro

(Lc. 11.9-13; 6.31)

7 Pedid, y se os dará; buscad, y hallaréis; llamad, y se os abrirá. 8 Porque todo aquel que pide, recibe; y el que busca, halla; y al que llama, se le abrirá. 9 ¿Qué hombre hay de vosotros, que si su hijo le pide pan, le dará una piedra? 10 ¿O si le pide un pescado, le dará una serpiente? 11 Pues si vosotros, siendo malos, sabéis dar buenas dádivas a vuestros hijos, ¿cuánto más vuestro Padre que está en los cielos dará buenas cosas a los que le pidan? 12 

Network architecture

Define the layers


In [4]:
# Define the model structure
embedding_size = 256 # number of hidden units per layer

# Input
lookup = LookupTable(length=vocab_size, dim=embedding_size)

# Layer 1
fork1 = Fork(output_names=['linear1', 'gates1'],
             input_dim=embedding_size, output_dims=[embedding_size, embedding_size * 2])
fork1.name = 'fork1'
grnn1 = GatedRecurrent(dim=embedding_size)
grnn1.name = 'grnn1'

# Layer 2
fork2 = Fork(output_names=['linear2', 'gates2'],
             input_dim=embedding_size, output_dims=[embedding_size, embedding_size * 2])
fork2.name = 'fork2'
grnn2 = GatedRecurrent(dim=embedding_size)
grnn2.name = 'grnn2'

# Softmax layer
hidden_to_output = Linear(name='hidden_to_output', input_dim=embedding_size,
                          output_dim=vocab_size)
softmax = NDimensionalSoftmax()

Connect the layers


In [5]:
# Propagate x until top brick to get y_hat predictions
x = tensor.imatrix('features')  # input
y = tensor.imatrix('targets')   # output
embedding = lookup.apply(x)
linear1, gates1 = fork1.apply(embedding)
h1 = grnn1.apply(linear1, gates1)
h1.name = 'h1'
linear2, gates2 = fork2.apply(h1)
h2 = grnn2.apply(linear2, gates2)
h2.name = 'h2'
linear3 = hidden_to_output.apply(h2)
linear3.name = 'linear3'
y_hat = softmax.apply(linear3, extra_ndim=1)
y_hat.name = 'y_hat'

# COST
cost = softmax.categorical_cross_entropy(y, linear3, extra_ndim=1).mean()
cost.name = 'cost'

model = Model(cost)

Load parameters and build Theano graph


In [87]:
# Load model parameters from a file
with open('grnn_best.tar') as model_file:
    model_params = model.get_parameter_dict().keys()
    param_vals = {k:v for k,v in load_parameters(model_file).iteritems() if k in model_params}
    model.set_parameter_values(param_vals)

In [8]:
# Define Theano graph
y, x = model.inputs
softmax = NDimensionalSoftmax()
linear_output = [v for v in model.variables if v.name == 'linear3'][0]
y_hat = softmax.apply(linear_output, extra_ndim=1)
predict = theano.function([x], y_hat)
#theano.printing.pydotprint(predict, outfile="theano_graph.svg", format = 'svg', var_with_name_simple=True)


The output file is available at theano_graph.svg


In [ ]:
#take activations of last element
activations = [h1[-1].flatten(), h2[-1].flatten()]
initial_states = [grnn1.parameters[-1], grnn2.parameters[-1]]
states_as_params = [tensor.vector(dtype=initial.dtype) for initial in initial_states]

#Get prob. distribution of the last element in the last seq of the batch
fprop = theano.function([x] + states_as_params, activations + [y_hat[-1, -1, :]], givens=zip(initial_states, states_as_params))

In [12]:
def sample(x_curr, states_values, fprop, temperature=1.0):
    '''
    Propagate x_curr sequence and sample next element according to
    temperature sampling.
    Return: sampled element and a list of the hidden activations produced by fprop.
    '''
    activations = fprop(x_curr, *states_values)
    probs = activations.pop().astype('float64')
    probs = probs / probs.sum()
    if numpy.random.binomial(1, temperature) == 1:
        sample = numpy.random.multinomial(1, probs).nonzero()[0][0]
    else:
        sample = probs.argmax()

    return sample, activations, probs[sample]

def init_params(primetext=u''):
    if not primetext or len(primetext) == 0:
        primetext = ix_to_char[numpy.random.randint(vocab_size)]
    primetext = ''.join([ch for ch in primetext if ch in char_to_ix.keys()])
    if len(primetext) == 0:
        raise Exception('primetext characters are not in the vocabulary')
    x_curr = numpy.expand_dims(
        numpy.array([char_to_ix[ch] for ch in primetext], dtype='uint8'), axis=1)

    states_values = [initial.get_value() for initial in initial_states]
    return x_curr, states_values
    
def stochastic_sampling(length, primetext=u'', temperature=1.0):
    x_curr, states_values = init_params(primetext)
    sys.stdout.write('Starting sampling\n' + primetext)
    for _ in range(length):
        idx, states_values, probs = sample(x_curr, states_values, fprop, temperature)
        sys.stdout.write(ix_to_char[idx])
        x_curr = [[idx]]

    sys.stdout.write('\n')

def beam_sampling(length, primetext=u'', beam_size=5, temperature=1.0):
    x_curr, states_values = init_params(primetext)
    inputs = [x_curr] * beam_size
    states = [states_values] * beam_size
    logprobs = numpy.zeros((beam_size, 1))
    seqs = numpy.zeros((length+x_curr.shape[0], beam_size))
    seqs[0:x_curr.shape[0], :] = numpy.repeat(x_curr, beam_size, axis=1)
    for k in range(length):
        probs = numpy.zeros((beam_size,beam_size))
        indices = numpy.zeros((beam_size,beam_size), dtype='int32')
        hstates = numpy.empty((beam_size,beam_size), dtype=list)
        for i in range(beam_size):
            for j in range(beam_size):
                indices[i][j], hstates[i][j], probs[i][j] = sample(inputs[i], states[i], fprop, temperature)
        probs = numpy.log(probs) + logprobs
        best_idx = probs.argmax(axis=1)
        inputs = [[[idx]] for idx in indices[range(beam_size), best_idx]]
        states = [hs for hs in hstates[range(beam_size), best_idx]]
        logprobs = probs[range(beam_size), best_idx].reshape((beam_size, 1))
        seqs[k +x_curr.shape[0], :] = numpy.array(inputs).flatten()

    return logprobs.flatten(), numpy.array(seqs).squeeze()

In [93]:
logprobs, seqs = beam_sampling(100, primetext=u'blanco ', beam_size = 7, temperature = 1.0)
for i in logprobs.flatten().argsort()[::-1]:
    print 'log P(s) = {0:3.3f}. Sample: '.format(logprobs.flatten()[i]) + u''.join([ix_to_char[ix] for ix in numpy.array(seqs).squeeze()[:,i]])
    print '~' * 50


log P(s) = -51.402. Sample: blanco de los demonios, y le dijeron: ¿Quién es el que había de entre los hombres, de la ciudad de la multi
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
log P(s) = -58.557. Sample: blanco de ellos, y le dijo: ¿Qué había entregado de la palabra de Dios y de la promesa de la carne, y le di
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
log P(s) = -59.374. Sample: blanco de los demonios, y le dijo: ¿Qué había de la multitud, y no se le dijo: No te dijeron: ¿Quién es el 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
log P(s) = -59.669. Sample: blanco para que no habían sido destruidos de la carne, y le dijo: Señor, ¿qué dijo: ¿Quién es el día de est
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
log P(s) = -60.460. Sample: blanco de los demonios, y le dijo: ¿Qué que había de la ciudad, y le dijo: ¿Qué había de la carne, y le dij
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
log P(s) = -60.686. Sample: blanco de Dios y los demonios, y le dijo: ¿Qué había de esta generación. 10 Porque el que de la ciudad de D
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
log P(s) = -63.916. Sample: blanco de los hombres, y no se levantarán a tu propia vez de la carne, y le dijo: Señor, ¿qué hará con voso
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Sampling from the model

  • The model calculates the probability of the next word given the previous words:
    $$P(w_t | w_{t-1}, w_{t-2},\dots, w_{1})$$
  • We sample from the model using this conditional probability
    for i in [1..n]:
        P = predict_next() 
        bin_var = sample_binomial(temperature)
        if bin_var:
            w_i = sample_multinomial(P) 
        else:
            w_i = P.argmax()
    

Sampling from the model


In [77]:
stochastic_sampling(3000, primetext=u'El sentido de la vida es', temperature=0.3)


Starting sampling
El sentido de la vida esté confianzas de la carones de la Escritura de los doce de antes de los hombres, para los que están testros para que no te dijeron: ¿Quién le había de la carne, y amará al puel, por la fe de la multitud de Jesucristo, y le dijo: ¿Quién cree por las madre del cielo, también la propititad al que había de la carne, y vinieron al que había desada de los profetas, y ó la palabra de Jesucristo. 13 Por lo cual le dio a las cuales nos mandamientos de la casan, y los demonios de la carne, y le dijo: Padre, y le enseñaba de la carne, 4 para que no se maravillas a los cuales hablaban de carne, y le dijo: ¿Es lícito de la carne, y le dijeron: ¿Quién yo te digas queje de los que vayan a los que están de la ley, la cena de los cielos y los de la concie

4 TIMOTEO 1

1 Entonces el que había de los escribas y los demonios. 15 Todo el que me enciano, para que no les había de la conciencia de la ciudad, pues, que este testimonio de Jesucristo, que al que había de aquí, y le dijo: ¿Saego a Epístola del cielo, y le dijo: ¿Qué hace en el mar, 6 y le dijo: De cierto os dijeron: ¿Como está en los cielos.

La predicación de la cual está escrito: 


He aquí, yo solamiento de la multitud, y de cuando ya había de la esperanza de la multitud, y vinieron a la multitud, y les dijo: ¿Qué habío de perdonado en el camino, y le dijo: ¿Había contenciones en la conciencia, y por la fe de la muerta, y la palabra que está en la multitud, y le dijo: Padre que es para hacer ni por las manos de la carne. 25 Porque el que había de la navegado, y temas de la carne? 15 Porque el Señor a él en el día de la carne, la cual está escrito: ¿Qué hace en pie, y había de la carne; porque el otro de la ciudad de la multitud de la carrera que recibirá al pueblo, y le dijo: ¿Quién es el que hablaba nada de los demoniados los hombres, y ley de los demonios, y le dijo: ¿Qué había de la carne, y le enviaron a la multitud, y le dijo: ¿Qué salvará al que había de reposo, y tendrá en la circuncisión, y le después de estas cosas, de los judíos de los que estén en dónde estaba en el día de los discípulos, y le aparedor de la ley, y que no se le dijo Jesús, y le dijo: ¿Quí, pues, se dijo: ¿Quién es el Señor Jesucris? ¿No está escrito: Lo que no se había de la carne, y le dijo: ¿Qué presenta, vino a Pedro, en Señor; pero si desde el mundo obedecen acabarlo de los demonios. 15 Porque el que a mí me ha de entre los hombres, y hablaban desde entre los santos en el camino, y glorificado en el cuerpo de los demonios, y le dijo: Señal de la venida de la gracia de Dios que había recibíra y a la madre, y que no está en la carne, en el hermano de los demonios, y le después de gran manera que no puede el viento de los que habían sido entre lugar. 18 Al oír este está en el considera, a los que estaban en aquellos días de la cera, sino que está en la ciudad, para que nos recibirá al pueblo. 17 Y vino a sus discípulos, y le aparían de los judíos, de otra vez en el día de la cárcel, 30 y le dijo: ¿Qué, pues, que est

Probability of a text

  • The probability of a text is:
    $$P(w_1, \dots, w_n) = P(w_1)\prod_{i=2}^{n}\ P(w_i | w_{1},\dots, w_{i-1})$$

In [61]:
# Function to calculate the probability of a text
def log_likelihood(text):
    text = ''.join([ch for ch in text if ch in char_to_ix])
    x_curr = numpy.expand_dims(numpy.array([char_to_ix[ch] for ch in text], dtype='uint8'), axis=1)
    probs = predict(x_curr).squeeze()
    return sum([numpy.log(probs[i,c]) for i,c in enumerate(x_curr[1:].flatten())])

In [78]:
log_likelihood("buscad, y hallaréis")


Out[78]:
-35.292520381161012

In [79]:
log_likelihood("this is a test")


Out[79]:
-41.525116831064224

Most likely phrases from a bag of words


In [84]:
from itertools import permutations
bow =  [' ', 'hombre', 'ama', 'a', 'el']
perms = [' '.join(perm) for perm in permutations(bow)]
for p, t in sorted([(-log_likelihood(text),text) for text in perms])[:20]:
    print p, t


28.7872946688 el hombre ama a  
30.1335569599   el hombre ama a
30.9096414867   ama el hombre a
31.6734288311 a el hombre ama  
31.9067574043   a el hombre ama
32.2993101903   a hombre el ama
33.9996441508   el ama a hombre
34.0615620876   ama a el hombre
34.3336700925   el hombre a ama
34.639605933 hombre el ama a  
34.6567749001   hombre el ama a
34.7339455399   ama a hombre el
34.8366844942 el ama a hombre  
35.4865090914   hombre ama el a
35.7297849205   a hombre ama el
35.8706177044 ama a el hombre  
35.9484713499   hombre ama a el
36.1151354631 a ama el hombre  
36.5683493641 a hombre el ama  
36.7281361914 ama el hombre a  

Least likely phrases


In [85]:
perms = [' '.join(perm) for perm in permutations(bow)]
for p, t in sorted([(-log_likelihood(text),text) for text in perms])[-20:]:
    print p, t


52.1275147402 ama hombre el   a
52.1970617577 hombre a   el ama
52.2630596872 el a   hombre ama
52.3661701797 el   ama hombre a
52.7321290059 el   hombre a ama
52.8102573471 ama hombre   a el
52.9034054916 ama a el   hombre
52.9764832641 ama   el a hombre
53.0416675795 ama a hombre   el
53.2206440819 a ama el   hombre
53.9376723702 hombre el a   ama
54.0484542205 hombre   el a ama
54.9569975241 hombre a ama   el
55.3035467451 ama hombre   el a
55.6084476279 el   a ama hombre
56.4346062609 el a   ama hombre
56.8539055093 el a ama   hombre
57.2385296579 ama el a   hombre
57.3769435149 a ama hombre   el
57.7220643626 ama hombre a   el

Morphology


In [66]:
from itertools import permutations
from random import shuffle

In [86]:
text = list(u'mnpu')
perms = [''.join(perm) for perm in permutations(text)]
for p, t in sorted([(-log_likelihood(text),text) for text in perms])[:5]:
    print p, t
print "------------------"
for p, t in sorted([(-log_likelihood(text),text) for text in perms])[-5:]:
    print p, t


9.149269104 nump
9.33271074295 mpun
12.42851758 umpn
13.1477912068 nmup
13.6903936863 mnpu
------------------
22.3517570496 nupm
27.4689741135 upnm
29.4543595314 pnmu
29.7688331604 upmn
29.8283925056 pmnu

Structure


In [24]:
print stochastic_sampling(400, u"(Lc. ", temperature = 0.1)


Starting sampling
(Lc. 11.1-1-20)

13 Entonces le dijo: ¿Qué había de la carne, y le después de los que están en luz de la carne, y le dijo: ¿Qué había de la carne, y le dijo: Yo se levantarán a la multitud, y le dijo: ¿Qué había de la carne, y le dijo a los que están estas cosas, le dijeron: ¿Quién está en la conciencia, y le enviaron a la multitud de la carne, y le dijo: ¿Qué había de aquí, y le dijo: ¿Quién es el q
None