Question and Answer Chat Bots



Loading the Data

We will be working with the Babi Data Set from Facebook Research.

Full Details: https://research.fb.com/downloads/babi/

  • Jason Weston, Antoine Bordes, Sumit Chopra, Tomas Mikolov, Alexander M. Rush, "Towards AI-Complete Question Answering: A Set of Prerequisite Toy Tasks", http://arxiv.org/abs/1502.05698

In [1]:
import pickle
import numpy as np

In [2]:
with open("train_qa.txt", "rb") as fp:   # Unpickling
    train_data =  pickle.load(fp)

In [3]:
with open("test_qa.txt", "rb") as fp:   # Unpickling
    test_data =  pickle.load(fp)

Exploring the Format of the Data


In [4]:
type(test_data)


Out[4]:
list

In [5]:
type(train_data)


Out[5]:
list

In [6]:
len(test_data)


Out[6]:
1000

In [7]:
len(train_data)


Out[7]:
10000

In [8]:
train_data[0]


Out[8]:
(['Mary',
  'moved',
  'to',
  'the',
  'bathroom',
  '.',
  'Sandra',
  'journeyed',
  'to',
  'the',
  'bedroom',
  '.'],
 ['Is', 'Sandra', 'in', 'the', 'hallway', '?'],
 'no')

In [9]:
' '.join(train_data[0][0])


Out[9]:
'Mary moved to the bathroom . Sandra journeyed to the bedroom .'

In [10]:
' '.join(train_data[0][1])


Out[10]:
'Is Sandra in the hallway ?'

In [11]:
train_data[0][2]


Out[11]:
'no'

Setting up Vocabulary of All Words


In [12]:
# Create a set that holds the vocab words
vocab = set()

In [13]:
all_data = test_data + train_data

In [14]:
for story, question , answer in all_data:
    # In case you don't know what a union of sets is:
    # https://www.programiz.com/python-programming/methods/set/union
    vocab = vocab.union(set(story))
    vocab = vocab.union(set(question))

In [15]:
vocab.add('no')
vocab.add('yes')

In [16]:
vocab


Out[16]:
{'.',
 '?',
 'Daniel',
 'Is',
 'John',
 'Mary',
 'Sandra',
 'apple',
 'back',
 'bathroom',
 'bedroom',
 'discarded',
 'down',
 'dropped',
 'football',
 'garden',
 'got',
 'grabbed',
 'hallway',
 'in',
 'journeyed',
 'kitchen',
 'left',
 'milk',
 'moved',
 'no',
 'office',
 'picked',
 'put',
 'the',
 'there',
 'to',
 'took',
 'travelled',
 'up',
 'went',
 'yes'}

In [17]:
vocab_len = len(vocab) + 1 #we add an extra space to hold a 0 for Keras's pad_sequences

In [18]:
max_story_len = max([len(data[0]) for data in all_data])

In [19]:
max_story_len


Out[19]:
156

In [20]:
max_question_len = max([len(data[1]) for data in all_data])

In [21]:
max_question_len


Out[21]:
6

Vectorizing the Data


In [22]:
vocab


Out[22]:
{'.',
 '?',
 'Daniel',
 'Is',
 'John',
 'Mary',
 'Sandra',
 'apple',
 'back',
 'bathroom',
 'bedroom',
 'discarded',
 'down',
 'dropped',
 'football',
 'garden',
 'got',
 'grabbed',
 'hallway',
 'in',
 'journeyed',
 'kitchen',
 'left',
 'milk',
 'moved',
 'no',
 'office',
 'picked',
 'put',
 'the',
 'there',
 'to',
 'took',
 'travelled',
 'up',
 'went',
 'yes'}

In [23]:
# Reserve 0 for pad_sequences
vocab_size = len(vocab) + 1


In [24]:
from keras.preprocessing.sequence import pad_sequences
from keras.preprocessing.text import Tokenizer


Using TensorFlow backend.

In [25]:
# integer encode sequences of words
tokenizer = Tokenizer(filters=[])
tokenizer.fit_on_texts(vocab)

In [26]:
tokenizer.word_index


Out[26]:
{'.': 13,
 '?': 12,
 'apple': 3,
 'back': 18,
 'bathroom': 23,
 'bedroom': 29,
 'daniel': 21,
 'discarded': 5,
 'down': 22,
 'dropped': 4,
 'football': 37,
 'garden': 36,
 'got': 34,
 'grabbed': 15,
 'hallway': 27,
 'in': 8,
 'is': 30,
 'john': 10,
 'journeyed': 35,
 'kitchen': 11,
 'left': 31,
 'mary': 25,
 'milk': 7,
 'moved': 28,
 'no': 6,
 'office': 24,
 'picked': 1,
 'put': 33,
 'sandra': 2,
 'the': 32,
 'there': 16,
 'to': 14,
 'took': 9,
 'travelled': 20,
 'up': 17,
 'went': 26,
 'yes': 19}

In [27]:
train_story_text = []
train_question_text = []
train_answers = []

for story,question,answer in train_data:
    train_story_text.append(story)
    train_question_text.append(question)

In [28]:
train_story_seq = tokenizer.texts_to_sequences(train_story_text)

In [29]:
len(train_story_text)


Out[29]:
10000

In [30]:
len(train_story_seq)


Out[30]:
10000

In [31]:
# word_index = tokenizer.word_index

Functionalize Vectorization


In [32]:
def vectorize_stories(data, word_index=tokenizer.word_index, max_story_len=max_story_len,max_question_len=max_question_len):
    '''
    INPUT: 
    
    data: consisting of Stories,Queries,and Answers
    word_index: word index dictionary from tokenizer
    max_story_len: the length of the longest story (used for pad_sequences function)
    max_question_len: length of the longest question (used for pad_sequences function)


    OUTPUT:
    
    Vectorizes the stories,questions, and answers into padded sequences. We first loop for every story, query , and
    answer in the data. Then we convert the raw words to an word index value. Then we append each set to their appropriate
    output list. Then once we have converted the words to numbers, we pad the sequences so they are all of equal length.
    
    Returns this in the form of a tuple (X,Xq,Y) (padded based on max lengths)
    '''
    
    
    # X = STORIES
    X = []
    # Xq = QUERY/QUESTION
    Xq = []
    # Y = CORRECT ANSWER
    Y = []
    
    
    for story, query, answer in data:
        
        # Grab the word index for every word in story
        x = [word_index[word.lower()] for word in story]
        # Grab the word index for every word in query
        xq = [word_index[word.lower()] for word in query]
        
        # Grab the Answers (either Yes/No so we don't need to use list comprehension here)
        # Index 0 is reserved so we're going to use + 1
        y = np.zeros(len(word_index) + 1)
        
        # Now that y is all zeros and we know its just Yes/No , we can use numpy logic to create this assignment
        #
        y[word_index[answer]] = 1
        
        # Append each set of story,query, and answer to their respective holding lists
        X.append(x)
        Xq.append(xq)
        Y.append(y)
        
    # Finally, pad the sequences based on their max length so the RNN can be trained on uniformly long sequences.
        
    # RETURN TUPLE FOR UNPACKING
    return (pad_sequences(X, maxlen=max_story_len),pad_sequences(Xq, maxlen=max_question_len), np.array(Y))

In [33]:
inputs_train, queries_train, answers_train = vectorize_stories(train_data)

In [34]:
inputs_test, queries_test, answers_test = vectorize_stories(test_data)

In [35]:
inputs_test


Out[35]:
array([[ 0,  0,  0, ..., 32, 29, 13],
       [ 0,  0,  0, ..., 32, 36, 13],
       [ 0,  0,  0, ..., 32, 36, 13],
       ...,
       [ 0,  0,  0, ..., 32,  3, 13],
       [ 0,  0,  0, ..., 32, 36, 13],
       [ 0,  0,  0, ...,  3, 16, 13]])

In [36]:
queries_test


Out[36]:
array([[30, 10,  8, 32, 11, 12],
       [30, 10,  8, 32, 11, 12],
       [30, 10,  8, 32, 36, 12],
       ...,
       [30, 25,  8, 32, 29, 12],
       [30,  2,  8, 32, 36, 12],
       [30, 25,  8, 32, 36, 12]])

In [37]:
answers_test


Out[37]:
array([[0., 0., 0., ..., 0., 0., 0.],
       [0., 0., 0., ..., 0., 0., 0.],
       [0., 0., 0., ..., 0., 0., 0.],
       ...,
       [0., 0., 0., ..., 0., 0., 0.],
       [0., 0., 0., ..., 0., 0., 0.],
       [0., 0., 0., ..., 0., 0., 0.]])

In [38]:
sum(answers_test)


Out[38]:
array([  0.,   0.,   0.,   0.,   0.,   0., 503.,   0.,   0.,   0.,   0.,
         0.,   0.,   0.,   0.,   0.,   0.,   0.,   0., 497.,   0.,   0.,
         0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,
         0.,   0.,   0.,   0.,   0.])

In [39]:
tokenizer.word_index['yes']


Out[39]:
19

In [40]:
tokenizer.word_index['no']


Out[40]:
6

Creating the Model


In [41]:
from keras.models import Sequential, Model
from keras.layers.embeddings import Embedding
from keras.layers import Input, Activation, Dense, Permute, Dropout
from keras.layers import add, dot, concatenate
from keras.layers import LSTM

Placeholders for Inputs

Recall we technically have two inputs, stories and questions. So we need to use placeholders. Input() is used to instantiate a Keras tensor.


In [42]:
input_sequence = Input((max_story_len,))
question = Input((max_question_len,))

Building the Networks

To understand why we chose this setup, make sure to read the paper we are using:

Encoders

Input Encoder m


In [43]:
# Input gets embedded to a sequence of vectors
input_encoder_m = Sequential()
input_encoder_m.add(Embedding(input_dim=vocab_size,output_dim=64))
input_encoder_m.add(Dropout(0.3))

# This encoder will output:
# (samples, story_maxlen, embedding_dim)

Input Encoder c


In [44]:
# embed the input into a sequence of vectors of size query_maxlen
input_encoder_c = Sequential()
input_encoder_c.add(Embedding(input_dim=vocab_size,output_dim=max_question_len))
input_encoder_c.add(Dropout(0.3))
# output: (samples, story_maxlen, query_maxlen)

Question Encoder


In [45]:
# embed the question into a sequence of vectors
question_encoder = Sequential()
question_encoder.add(Embedding(input_dim=vocab_size,
                               output_dim=64,
                               input_length=max_question_len))
question_encoder.add(Dropout(0.3))
# output: (samples, query_maxlen, embedding_dim)

Encode the Sequences


In [46]:
# encode input sequence and questions (which are indices)
# to sequences of dense vectors
input_encoded_m = input_encoder_m(input_sequence)
input_encoded_c = input_encoder_c(input_sequence)
question_encoded = question_encoder(question)
Use dot product to compute the match between first input vector seq and the query

In [47]:
# shape: `(samples, story_maxlen, query_maxlen)`
match = dot([input_encoded_m, question_encoded], axes=(2, 2))
match = Activation('softmax')(match)

Add this match matrix with the second input vector sequence


In [48]:
# add the match matrix with the second input vector sequence
response = add([match, input_encoded_c])  # (samples, story_maxlen, query_maxlen)
response = Permute((2, 1))(response)  # (samples, query_maxlen, story_maxlen)

Concatenate


In [49]:
# concatenate the match matrix with the question vector sequence
answer = concatenate([response, question_encoded])

In [50]:
answer


Out[50]:
<tf.Tensor 'concatenate_1/concat:0' shape=(?, 6, 220) dtype=float32>

In [51]:
# Reduce with RNN (LSTM)
answer = LSTM(32)(answer)  # (samples, 32)

In [52]:
# Regularization with Dropout
answer = Dropout(0.5)(answer)
answer = Dense(vocab_size)(answer)  # (samples, vocab_size)

In [53]:
# we output a probability distribution over the vocabulary
answer = Activation('softmax')(answer)

# build the final model
model = Model([input_sequence, question], answer)
model.compile(optimizer='rmsprop', loss='categorical_crossentropy',
              metrics=['accuracy'])

In [54]:
model.summary()


__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_1 (InputLayer)            (None, 156)          0                                            
__________________________________________________________________________________________________
input_2 (InputLayer)            (None, 6)            0                                            
__________________________________________________________________________________________________
sequential_1 (Sequential)       multiple             2432        input_1[0][0]                    
__________________________________________________________________________________________________
sequential_3 (Sequential)       (None, 6, 64)        2432        input_2[0][0]                    
__________________________________________________________________________________________________
dot_1 (Dot)                     (None, 156, 6)       0           sequential_1[1][0]               
                                                                 sequential_3[1][0]               
__________________________________________________________________________________________________
activation_1 (Activation)       (None, 156, 6)       0           dot_1[0][0]                      
__________________________________________________________________________________________________
sequential_2 (Sequential)       multiple             228         input_1[0][0]                    
__________________________________________________________________________________________________
add_1 (Add)                     (None, 156, 6)       0           activation_1[0][0]               
                                                                 sequential_2[1][0]               
__________________________________________________________________________________________________
permute_1 (Permute)             (None, 6, 156)       0           add_1[0][0]                      
__________________________________________________________________________________________________
concatenate_1 (Concatenate)     (None, 6, 220)       0           permute_1[0][0]                  
                                                                 sequential_3[1][0]               
__________________________________________________________________________________________________
lstm_1 (LSTM)                   (None, 32)           32384       concatenate_1[0][0]              
__________________________________________________________________________________________________
dropout_4 (Dropout)             (None, 32)           0           lstm_1[0][0]                     
__________________________________________________________________________________________________
dense_1 (Dense)                 (None, 38)           1254        dropout_4[0][0]                  
__________________________________________________________________________________________________
activation_2 (Activation)       (None, 38)           0           dense_1[0][0]                    
==================================================================================================
Total params: 38,730
Trainable params: 38,730
Non-trainable params: 0
__________________________________________________________________________________________________

In [55]:
# train
history = model.fit([inputs_train, queries_train], answers_train,batch_size=32,epochs=120,validation_data=([inputs_test, queries_test], answers_test))


Train on 10000 samples, validate on 1000 samples
Epoch 1/120
10000/10000 [==============================] - 7s 701us/step - loss: 0.8846 - acc: 0.4966 - val_loss: 0.6938 - val_acc: 0.4970
Epoch 2/120
10000/10000 [==============================] - 4s 364us/step - loss: 0.7022 - acc: 0.4987 - val_loss: 0.6935 - val_acc: 0.5030
Epoch 3/120
10000/10000 [==============================] - 3s 347us/step - loss: 0.6958 - acc: 0.5042 - val_loss: 0.6937 - val_acc: 0.4970
Epoch 4/120
10000/10000 [==============================] - 3s 348us/step - loss: 0.6946 - acc: 0.5097 - val_loss: 0.6977 - val_acc: 0.4970
Epoch 5/120
10000/10000 [==============================] - 3s 345us/step - loss: 0.6943 - acc: 0.5073 - val_loss: 0.6932 - val_acc: 0.5030
Epoch 6/120
10000/10000 [==============================] - 3s 347us/step - loss: 0.6954 - acc: 0.4873 - val_loss: 0.6938 - val_acc: 0.4970
Epoch 7/120
10000/10000 [==============================] - 3s 345us/step - loss: 0.6946 - acc: 0.4970 - val_loss: 0.6953 - val_acc: 0.4970
Epoch 8/120
10000/10000 [==============================] - 3s 347us/step - loss: 0.6948 - acc: 0.4955 - val_loss: 0.6939 - val_acc: 0.4970
Epoch 9/120
10000/10000 [==============================] - 3s 346us/step - loss: 0.6944 - acc: 0.4937 - val_loss: 0.6933 - val_acc: 0.5030
Epoch 10/120
10000/10000 [==============================] - 4s 360us/step - loss: 0.6939 - acc: 0.5011 - val_loss: 0.6937 - val_acc: 0.4970
Epoch 11/120
10000/10000 [==============================] - 4s 365us/step - loss: 0.6941 - acc: 0.5051 - val_loss: 0.6934 - val_acc: 0.5030
Epoch 12/120
10000/10000 [==============================] - 4s 352us/step - loss: 0.6941 - acc: 0.5014 - val_loss: 0.6955 - val_acc: 0.4970
Epoch 13/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.6936 - acc: 0.5104 - val_loss: 0.6943 - val_acc: 0.4940
Epoch 14/120
10000/10000 [==============================] - 3s 350us/step - loss: 0.6938 - acc: 0.5045 - val_loss: 0.6938 - val_acc: 0.4950
Epoch 15/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.6914 - acc: 0.5216 - val_loss: 0.6944 - val_acc: 0.5030
Epoch 16/120
10000/10000 [==============================] - 4s 351us/step - loss: 0.6830 - acc: 0.5467 - val_loss: 0.6825 - val_acc: 0.5250
Epoch 17/120
10000/10000 [==============================] - 4s 356us/step - loss: 0.6663 - acc: 0.5840 - val_loss: 0.6656 - val_acc: 0.6020
Epoch 18/120
10000/10000 [==============================] - 4s 368us/step - loss: 0.6404 - acc: 0.6339 - val_loss: 0.6247 - val_acc: 0.6690
Epoch 19/120
10000/10000 [==============================] - 4s 364us/step - loss: 0.6049 - acc: 0.6829 - val_loss: 0.5708 - val_acc: 0.7210
Epoch 20/120
10000/10000 [==============================] - 4s 356us/step - loss: 0.5569 - acc: 0.7290 - val_loss: 0.5159 - val_acc: 0.7460
Epoch 21/120
10000/10000 [==============================] - 4s 358us/step - loss: 0.5180 - acc: 0.7549 - val_loss: 0.4775 - val_acc: 0.7870
Epoch 22/120
10000/10000 [==============================] - 4s 354us/step - loss: 0.4891 - acc: 0.7774 - val_loss: 0.4449 - val_acc: 0.7970
Epoch 23/120
10000/10000 [==============================] - 4s 354us/step - loss: 0.4528 - acc: 0.8020 - val_loss: 0.4142 - val_acc: 0.8190
Epoch 24/120
10000/10000 [==============================] - 4s 354us/step - loss: 0.4253 - acc: 0.8161 - val_loss: 0.4205 - val_acc: 0.8280
Epoch 25/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.4009 - acc: 0.8354 - val_loss: 0.4094 - val_acc: 0.8280
Epoch 26/120
10000/10000 [==============================] - 3s 348us/step - loss: 0.3815 - acc: 0.8432 - val_loss: 0.3919 - val_acc: 0.8240
Epoch 27/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.3653 - acc: 0.8496 - val_loss: 0.3926 - val_acc: 0.8450
Epoch 28/120
10000/10000 [==============================] - 4s 351us/step - loss: 0.3535 - acc: 0.8549 - val_loss: 0.3939 - val_acc: 0.8430
Epoch 29/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.3435 - acc: 0.8581 - val_loss: 0.3716 - val_acc: 0.8320
Epoch 30/120
10000/10000 [==============================] - 4s 350us/step - loss: 0.3403 - acc: 0.8603 - val_loss: 0.3677 - val_acc: 0.8340
Epoch 31/120
10000/10000 [==============================] - 4s 351us/step - loss: 0.3302 - acc: 0.8570 - val_loss: 0.3681 - val_acc: 0.8430
Epoch 32/120
10000/10000 [==============================] - 3s 348us/step - loss: 0.3295 - acc: 0.8593 - val_loss: 0.3476 - val_acc: 0.8380
Epoch 33/120
10000/10000 [==============================] - 3s 347us/step - loss: 0.3239 - acc: 0.8628 - val_loss: 0.3521 - val_acc: 0.8430
Epoch 34/120
10000/10000 [==============================] - 4s 350us/step - loss: 0.3171 - acc: 0.8677 - val_loss: 0.3443 - val_acc: 0.8390
Epoch 35/120
10000/10000 [==============================] - 3s 347us/step - loss: 0.3168 - acc: 0.8629 - val_loss: 0.3507 - val_acc: 0.8340
Epoch 36/120
10000/10000 [==============================] - 4s 351us/step - loss: 0.3121 - acc: 0.8664 - val_loss: 0.3558 - val_acc: 0.8310
Epoch 37/120
10000/10000 [==============================] - 4s 351us/step - loss: 0.3107 - acc: 0.8662 - val_loss: 0.3411 - val_acc: 0.8430
Epoch 38/120
10000/10000 [==============================] - 4s 355us/step - loss: 0.3061 - acc: 0.8698 - val_loss: 0.3460 - val_acc: 0.8400
Epoch 39/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.3065 - acc: 0.8671 - val_loss: 0.3493 - val_acc: 0.8400
Epoch 40/120
10000/10000 [==============================] - 4s 352us/step - loss: 0.3060 - acc: 0.8688 - val_loss: 0.3446 - val_acc: 0.8410
Epoch 41/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.3000 - acc: 0.8696 - val_loss: 0.3542 - val_acc: 0.8450
Epoch 42/120
10000/10000 [==============================] - 4s 354us/step - loss: 0.3039 - acc: 0.8665 - val_loss: 0.3692 - val_acc: 0.8350
Epoch 43/120
10000/10000 [==============================] - 3s 348us/step - loss: 0.3015 - acc: 0.8695 - val_loss: 0.3513 - val_acc: 0.8400
Epoch 44/120
10000/10000 [==============================] - 3s 347us/step - loss: 0.2986 - acc: 0.8694 - val_loss: 0.3577 - val_acc: 0.8320
Epoch 45/120
10000/10000 [==============================] - 3s 346us/step - loss: 0.2952 - acc: 0.8730 - val_loss: 0.3496 - val_acc: 0.8400
Epoch 46/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.2969 - acc: 0.8681 - val_loss: 0.3424 - val_acc: 0.8450
Epoch 47/120
10000/10000 [==============================] - 3s 347us/step - loss: 0.2923 - acc: 0.8721 - val_loss: 0.3549 - val_acc: 0.8280
Epoch 48/120
10000/10000 [==============================] - 3s 346us/step - loss: 0.2911 - acc: 0.8732 - val_loss: 0.4681 - val_acc: 0.8140
Epoch 49/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.2917 - acc: 0.8703 - val_loss: 0.3502 - val_acc: 0.8390
Epoch 50/120
10000/10000 [==============================] - 3s 344us/step - loss: 0.2900 - acc: 0.8746 - val_loss: 0.3515 - val_acc: 0.8400
Epoch 51/120
10000/10000 [==============================] - 4s 352us/step - loss: 0.2855 - acc: 0.8757 - val_loss: 0.3499 - val_acc: 0.8360
Epoch 52/120
10000/10000 [==============================] - 4s 354us/step - loss: 0.2864 - acc: 0.8735 - val_loss: 0.3531 - val_acc: 0.8410
Epoch 53/120
10000/10000 [==============================] - 4s 356us/step - loss: 0.2864 - acc: 0.8772 - val_loss: 0.3905 - val_acc: 0.8270
Epoch 54/120
10000/10000 [==============================] - 3s 346us/step - loss: 0.2857 - acc: 0.8752 - val_loss: 0.3618 - val_acc: 0.8390
Epoch 55/120
10000/10000 [==============================] - 3s 346us/step - loss: 0.2819 - acc: 0.8742 - val_loss: 0.3501 - val_acc: 0.8380
Epoch 56/120
10000/10000 [==============================] - 3s 348us/step - loss: 0.2853 - acc: 0.8775 - val_loss: 0.3484 - val_acc: 0.8400
Epoch 57/120
10000/10000 [==============================] - 4s 355us/step - loss: 0.2767 - acc: 0.8804 - val_loss: 0.3463 - val_acc: 0.8410
Epoch 58/120
10000/10000 [==============================] - 4s 355us/step - loss: 0.2802 - acc: 0.8780 - val_loss: 0.3763 - val_acc: 0.8350
Epoch 59/120
10000/10000 [==============================] - 3s 350us/step - loss: 0.2844 - acc: 0.8777 - val_loss: 0.3483 - val_acc: 0.8420
Epoch 60/120
10000/10000 [==============================] - 4s 350us/step - loss: 0.2759 - acc: 0.8828 - val_loss: 0.3819 - val_acc: 0.8340
Epoch 61/120
10000/10000 [==============================] - 4s 351us/step - loss: 0.2722 - acc: 0.8799 - val_loss: 0.3596 - val_acc: 0.8400
Epoch 62/120
10000/10000 [==============================] - 4s 366us/step - loss: 0.2704 - acc: 0.8845 - val_loss: 0.3751 - val_acc: 0.8400
Epoch 63/120
10000/10000 [==============================] - 4s 372us/step - loss: 0.2691 - acc: 0.8854 - val_loss: 0.3745 - val_acc: 0.8430
Epoch 64/120
10000/10000 [==============================] - 4s 356us/step - loss: 0.2698 - acc: 0.8865 - val_loss: 0.3562 - val_acc: 0.8400
Epoch 65/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.2674 - acc: 0.8875 - val_loss: 0.3534 - val_acc: 0.8400
Epoch 66/120
10000/10000 [==============================] - 4s 356us/step - loss: 0.2622 - acc: 0.8888 - val_loss: 0.3763 - val_acc: 0.8390
Epoch 67/120
10000/10000 [==============================] - 3s 347us/step - loss: 0.2593 - acc: 0.8885 - val_loss: 0.3670 - val_acc: 0.8470
Epoch 68/120
10000/10000 [==============================] - 3s 347us/step - loss: 0.2567 - acc: 0.8937 - val_loss: 0.3699 - val_acc: 0.8560
Epoch 69/120
10000/10000 [==============================] - 4s 361us/step - loss: 0.2573 - acc: 0.8951 - val_loss: 0.3676 - val_acc: 0.8430
Epoch 70/120
10000/10000 [==============================] - 4s 364us/step - loss: 0.2489 - acc: 0.8962 - val_loss: 0.3564 - val_acc: 0.8510
Epoch 71/120
10000/10000 [==============================] - 4s 363us/step - loss: 0.2479 - acc: 0.8961 - val_loss: 0.3605 - val_acc: 0.8460
Epoch 72/120
10000/10000 [==============================] - 4s 353us/step - loss: 0.2406 - acc: 0.9026 - val_loss: 0.3605 - val_acc: 0.8560
Epoch 73/120
10000/10000 [==============================] - 4s 354us/step - loss: 0.2404 - acc: 0.9020 - val_loss: 0.3490 - val_acc: 0.8510
Epoch 74/120
10000/10000 [==============================] - 4s 358us/step - loss: 0.2374 - acc: 0.9045 - val_loss: 0.3400 - val_acc: 0.8470
Epoch 75/120
10000/10000 [==============================] - 4s 381us/step - loss: 0.2299 - acc: 0.9060 - val_loss: 0.3453 - val_acc: 0.8490
Epoch 76/120
10000/10000 [==============================] - 4s 352us/step - loss: 0.2301 - acc: 0.9046 - val_loss: 0.3372 - val_acc: 0.8490
Epoch 77/120
10000/10000 [==============================] - 4s 353us/step - loss: 0.2250 - acc: 0.9076 - val_loss: 0.3354 - val_acc: 0.8510
Epoch 78/120
10000/10000 [==============================] - 3s 346us/step - loss: 0.2147 - acc: 0.9087 - val_loss: 0.3416 - val_acc: 0.8490
Epoch 79/120
10000/10000 [==============================] - 4s 351us/step - loss: 0.2111 - acc: 0.9119 - val_loss: 0.3774 - val_acc: 0.8520
Epoch 80/120
10000/10000 [==============================] - 4s 351us/step - loss: 0.2148 - acc: 0.9139 - val_loss: 0.3209 - val_acc: 0.8650
Epoch 81/120
10000/10000 [==============================] - 3s 347us/step - loss: 0.2045 - acc: 0.9158 - val_loss: 0.3157 - val_acc: 0.8650
Epoch 82/120
10000/10000 [==============================] - 3s 345us/step - loss: 0.1916 - acc: 0.9194 - val_loss: 0.3012 - val_acc: 0.8700
Epoch 83/120
10000/10000 [==============================] - 3s 350us/step - loss: 0.1881 - acc: 0.9228 - val_loss: 0.2922 - val_acc: 0.8670
Epoch 84/120
10000/10000 [==============================] - 3s 348us/step - loss: 0.1783 - acc: 0.9266 - val_loss: 0.2849 - val_acc: 0.8770
Epoch 85/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.1933 - acc: 0.9209 - val_loss: 0.3006 - val_acc: 0.8730
Epoch 86/120
10000/10000 [==============================] - 4s 352us/step - loss: 0.1824 - acc: 0.9279 - val_loss: 0.2729 - val_acc: 0.8810
Epoch 87/120
10000/10000 [==============================] - 3s 350us/step - loss: 0.1779 - acc: 0.9282 - val_loss: 0.2774 - val_acc: 0.8840
Epoch 88/120
10000/10000 [==============================] - 3s 345us/step - loss: 0.1710 - acc: 0.9303 - val_loss: 0.2758 - val_acc: 0.8810
Epoch 89/120
10000/10000 [==============================] - 3s 348us/step - loss: 0.1658 - acc: 0.9345 - val_loss: 0.2854 - val_acc: 0.8880
Epoch 90/120
10000/10000 [==============================] - 4s 358us/step - loss: 0.1637 - acc: 0.9347 - val_loss: 0.2634 - val_acc: 0.8930
Epoch 91/120
10000/10000 [==============================] - 4s 351us/step - loss: 0.1577 - acc: 0.9358 - val_loss: 0.2546 - val_acc: 0.8910
Epoch 92/120
10000/10000 [==============================] - 4s 354us/step - loss: 0.1611 - acc: 0.9387 - val_loss: 0.2445 - val_acc: 0.9080
Epoch 93/120
10000/10000 [==============================] - 4s 368us/step - loss: 0.1560 - acc: 0.9381 - val_loss: 0.2369 - val_acc: 0.9040
Epoch 94/120
10000/10000 [==============================] - 4s 352us/step - loss: 0.1470 - acc: 0.9409 - val_loss: 0.2764 - val_acc: 0.8950
Epoch 95/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.1475 - acc: 0.9428 - val_loss: 0.2634 - val_acc: 0.8980
Epoch 96/120
10000/10000 [==============================] - 3s 350us/step - loss: 0.1418 - acc: 0.9454 - val_loss: 0.2367 - val_acc: 0.9070
Epoch 97/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.1436 - acc: 0.9453 - val_loss: 0.2460 - val_acc: 0.9120
Epoch 98/120
10000/10000 [==============================] - 4s 350us/step - loss: 0.1434 - acc: 0.9430 - val_loss: 0.2593 - val_acc: 0.9130
Epoch 99/120
10000/10000 [==============================] - 3s 348us/step - loss: 0.1348 - acc: 0.9465 - val_loss: 0.2851 - val_acc: 0.9000
Epoch 100/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.1406 - acc: 0.9431 - val_loss: 0.2609 - val_acc: 0.9040
Epoch 101/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.1342 - acc: 0.9478 - val_loss: 0.2705 - val_acc: 0.9050
Epoch 102/120
10000/10000 [==============================] - 3s 346us/step - loss: 0.1352 - acc: 0.9475 - val_loss: 0.2505 - val_acc: 0.9010
Epoch 103/120
10000/10000 [==============================] - 3s 345us/step - loss: 0.1291 - acc: 0.9502 - val_loss: 0.2708 - val_acc: 0.9080
Epoch 104/120
10000/10000 [==============================] - 4s 351us/step - loss: 0.1250 - acc: 0.9523 - val_loss: 0.2634 - val_acc: 0.9120
Epoch 105/120
10000/10000 [==============================] - 3s 347us/step - loss: 0.1203 - acc: 0.9519 - val_loss: 0.2725 - val_acc: 0.9070
Epoch 106/120
10000/10000 [==============================] - 4s 350us/step - loss: 0.1187 - acc: 0.9540 - val_loss: 0.2557 - val_acc: 0.9170
Epoch 107/120
10000/10000 [==============================] - 3s 347us/step - loss: 0.1182 - acc: 0.9531 - val_loss: 0.2664 - val_acc: 0.9090
Epoch 108/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.1181 - acc: 0.9530 - val_loss: 0.2334 - val_acc: 0.9130
Epoch 109/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.1158 - acc: 0.9554 - val_loss: 0.2899 - val_acc: 0.9120
Epoch 110/120
10000/10000 [==============================] - 3s 350us/step - loss: 0.1167 - acc: 0.9567 - val_loss: 0.2754 - val_acc: 0.9090
Epoch 111/120
10000/10000 [==============================] - 4s 350us/step - loss: 0.1120 - acc: 0.9561 - val_loss: 0.2898 - val_acc: 0.9100
Epoch 112/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.1118 - acc: 0.9588 - val_loss: 0.2541 - val_acc: 0.9140
Epoch 113/120
10000/10000 [==============================] - 3s 346us/step - loss: 0.1083 - acc: 0.9583 - val_loss: 0.2511 - val_acc: 0.9110
Epoch 114/120
10000/10000 [==============================] - 4s 359us/step - loss: 0.1131 - acc: 0.9560 - val_loss: 0.2496 - val_acc: 0.9180
Epoch 115/120
10000/10000 [==============================] - 4s 364us/step - loss: 0.1050 - acc: 0.9599 - val_loss: 0.3021 - val_acc: 0.9170
Epoch 116/120
10000/10000 [==============================] - 3s 342us/step - loss: 0.1038 - acc: 0.9619 - val_loss: 0.2673 - val_acc: 0.9160
Epoch 117/120
10000/10000 [==============================] - 4s 350us/step - loss: 0.1100 - acc: 0.9599 - val_loss: 0.2855 - val_acc: 0.9160
Epoch 118/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.1012 - acc: 0.9610 - val_loss: 0.2887 - val_acc: 0.9200
Epoch 119/120
10000/10000 [==============================] - 4s 352us/step - loss: 0.1040 - acc: 0.9618 - val_loss: 0.2774 - val_acc: 0.9120
Epoch 120/120
10000/10000 [==============================] - 3s 349us/step - loss: 0.1010 - acc: 0.9615 - val_loss: 0.3139 - val_acc: 0.9120

Saving the Model


In [72]:
filename = 'chatbot_120_epochs.h5'
model.save(filename)

Evaluating the Model

Plotting Out Training History


In [57]:
import matplotlib.pyplot as plt
%matplotlib inline
print(history.history.keys())
# summarize history for accuracy
plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()


dict_keys(['val_loss', 'val_acc', 'loss', 'acc'])

Evaluating on Given Test Set


In [73]:
model.load_weights(filename)
pred_results = model.predict(([inputs_test, queries_test]))

In [74]:
test_data[0][0]


Out[74]:
['Mary',
 'got',
 'the',
 'milk',
 'there',
 '.',
 'John',
 'moved',
 'to',
 'the',
 'bedroom',
 '.']

In [75]:
story =' '.join(word for word in test_data[0][0])
print(story)


Mary got the milk there . John moved to the bedroom .

In [76]:
query = ' '.join(word for word in test_data[0][1])
print(query)


Is John in the kitchen ?

In [77]:
print("True Test Answer from Data is:",test_data[0][2])


True Test Answer from Data is: no

In [78]:
#Generate prediction from model
val_max = np.argmax(pred_results[0])

for key, val in tokenizer.word_index.items():
    if val == val_max:
        k = key

print("Predicted answer is: ", k)
print("Probability of certainty was: ", pred_results[0][val_max])


Predicted answer is:  no
Probability of certainty was:  0.9999999

Writing Your Own Stories and Questions

Remember you can only use words from the existing vocab


In [79]:
vocab


Out[79]:
{'.',
 '?',
 'Daniel',
 'Is',
 'John',
 'Mary',
 'Sandra',
 'apple',
 'back',
 'bathroom',
 'bedroom',
 'discarded',
 'down',
 'dropped',
 'football',
 'garden',
 'got',
 'grabbed',
 'hallway',
 'in',
 'journeyed',
 'kitchen',
 'left',
 'milk',
 'moved',
 'no',
 'office',
 'picked',
 'put',
 'the',
 'there',
 'to',
 'took',
 'travelled',
 'up',
 'went',
 'yes'}

In [80]:
# Note the whitespace of the periods
my_story = "John left the kitchen . Sandra dropped the football in the garden ."
my_story.split()


Out[80]:
['John',
 'left',
 'the',
 'kitchen',
 '.',
 'Sandra',
 'dropped',
 'the',
 'football',
 'in',
 'the',
 'garden',
 '.']

In [81]:
my_question = "Is the football in the garden ?"

In [82]:
my_question.split()


Out[82]:
['Is', 'the', 'football', 'in', 'the', 'garden', '?']

In [83]:
mydata = [(my_story.split(),my_question.split(),'yes')]

In [84]:
my_story,my_ques,my_ans = vectorize_stories(mydata)

In [85]:
pred_results = model.predict(([ my_story, my_ques]))

In [86]:
#Generate prediction from model
val_max = np.argmax(pred_results[0])

for key, val in tokenizer.word_index.items():
    if val == val_max:
        k = key

print("Predicted answer is: ", k)
print("Probability of certainty was: ", pred_results[0][val_max])


Predicted answer is:  yes
Probability of certainty was:  0.97079676

Great Job!