NLP/NLU Specialization Notes
  • Natural Language Processing/Understanding Notes, Resources
  • Natural Language Processing With Attention Models
    • NLP With Attention Models
  • Natural Language Processing With Attention Models Notebooks
    • Assignment 1: Neural Machine Translation
    • Basic Attention Operation: Ungraded Lab
    • Calculating the Bilingual Evaluation Understudy (BLEU) score: Ungraded Lab
    • Scaled Dot-Product Attention: Ungraded Lab
    • Assignment 2: Transformer Summarizer
    • The Three Ways of Attention and Dot Product Attention: Ungraded Lab Notebook
    • Masking
    • Positional Encoding
    • Assignment 3: Question Answering
    • Assignment 3: Question Answering
    • Question Answering with BERT and HuggingFace
    • Question Answering with BERT and HuggingFace 🤗 (Fine-tuning)
    • SentencePiece and BPE
  • Natural Language Processing With Classification And Vector Spaces
    • NLP With Classification and Vector Spaces
  • Natural Language Processing With Sequence Models
    • NLP With Sequence Models
  • Natural Language Processing with Classification and Vector Spaces Notebooks
    • Assignment 1: Logistic Regression
    • Preprocessing
    • Building and Visualizing word frequencies
    • Visualizing tweets and the Logistic Regression model
    • Assignment 2: Naive Bayes
    • Assignment 3: Hello Vectors
    • Linear algebra in Python with NumPy
    • Manipulating word embeddings
    • Another explanation about PCA
    • Assignment 4 - Naive Machine Translation and LSH
    • Vector manipulation in Python
    • Hash functions and multiplanes
  • Natural Language Processing with Probabilistic Models
    • NLP With Probabilistic Models
  • Natural Language Processing with Probabilistic Models Notebooks
    • Assignment 1: Autocorrect
    • NLP Course 2 Week 1 Lesson : Building The Model - Lecture Exercise 01
    • NLP Course 2 Week 1 Lesson : Building The Model - Lecture Exercise 02
    • Assignment 2: Parts-of-Speech Tagging (POS)
    • Parts-of-Speech Tagging - First Steps: Working with text files, Creating a Vocabulary and Handling Unknown Words
    • Parts-of-Speech Tagging - Working with tags and Numpy
    • Assignment 3: Language Models: Auto-Complete
    • N-grams Corpus preprocessing
    • Building the language model
    • Out of vocabulary words (OOV)
    • Assignment 4: Word Embeddings
    • Word Embeddings First Steps: Data Preparation
    • Word Embeddings: Intro to CBOW model, activation functions and working with Numpy
    • Word Embeddings: Training the CBOW model
    • Word Embeddings: Hands On
    • Word Embeddings: Ungraded Practice Notebook
  • Natural Language Processing with Sequence Models Notebooks
    • Assignment 1: Deep N-grams
    • Hidden State Activation : Ungraded Lecture Notebook
    • Assignment 1: Sentiment with Deep Neural Networks
    • Vanilla RNNs and GRUs
    • Lab 1: TensorFlow Tutorial and Some Useful Functions
    • Calculating perplexity using numpy: Ungraded Lecture Notebook
    • Assignment 2 - Named Entity Recognition (NER)
    • Vanishing Gradients and Exploding Gradients in RNNs : Ungraded Lecture Notebook
    • Evaluate a Siamese model: Ungraded Lecture Notebook
    • Assignment 3: Question duplicates
    • Modified Triplet Loss : Ungraded Lecture Notebook
    • Creating a Siamese model: Ungraded Lecture Notebook
  • Stanford
    • Stanford CS 224U,224N
  • Udacity
    • NLP Nanodegree
  • Previous
  • Next
  • Building the language model
    • Language model evaluation

Building the language model¶

Count matrix¶

To calculate the n-gram probability, you will need to count frequencies of n-grams and n-gram prefixes in the training dataset. In some of the code assignment exercises, you will store the n-gram frequencies in a dictionary.

In other parts of the assignment, you will build a count matrix that keeps counts of (n-1)-gram prefix followed by all possible last words in the vocabulary.

The following code shows how to check, retrieve and update counts of n-grams in the word count dictionary.

In [ ]:
Copied!
# manipulate n_gram count dictionary

n_gram_counts = {
    ('i', 'am', 'happy'): 2,
    ('am', 'happy', 'because'): 1}

# get count for an n-gram tuple
print(f"count of n-gram {('i', 'am', 'happy')}: {n_gram_counts[('i', 'am', 'happy')]}")

# check if n-gram is present in the dictionary
if ('i', 'am', 'learning') in n_gram_counts:
    print(f"n-gram {('i', 'am', 'learning')} found")
else:
    print(f"n-gram {('i', 'am', 'learning')} missing")

# update the count in the word count dictionary
n_gram_counts[('i', 'am', 'learning')] = 1
if ('i', 'am', 'learning') in n_gram_counts:
    print(f"n-gram {('i', 'am', 'learning')} found")
else:
    print(f"n-gram {('i', 'am', 'learning')} missing")
# manipulate n_gram count dictionary n_gram_counts = { ('i', 'am', 'happy'): 2, ('am', 'happy', 'because'): 1} # get count for an n-gram tuple print(f"count of n-gram {('i', 'am', 'happy')}: {n_gram_counts[('i', 'am', 'happy')]}") # check if n-gram is present in the dictionary if ('i', 'am', 'learning') in n_gram_counts: print(f"n-gram {('i', 'am', 'learning')} found") else: print(f"n-gram {('i', 'am', 'learning')} missing") # update the count in the word count dictionary n_gram_counts[('i', 'am', 'learning')] = 1 if ('i', 'am', 'learning') in n_gram_counts: print(f"n-gram {('i', 'am', 'learning')} found") else: print(f"n-gram {('i', 'am', 'learning')} missing")

The next code snippet shows how to merge two tuples in Python. That will be handy when creating the n-gram from the prefix and the last word.

In [ ]:
Copied!
# concatenate tuple for prefix and tuple with the last word to create the n_gram
prefix = ('i', 'am', 'happy')
word = 'because'

# note here the syntax for creating a tuple for a single word
n_gram = prefix + (word,)
print(n_gram)
# concatenate tuple for prefix and tuple with the last word to create the n_gram prefix = ('i', 'am', 'happy') word = 'because' # note here the syntax for creating a tuple for a single word n_gram = prefix + (word,) print(n_gram)

In the lecture, you've seen that the count matrix could be made in a single pass through the corpus. Here is one approach to do that.

In [ ]:
Copied!
import numpy as np
import pandas as pd
from collections import defaultdict
def single_pass_trigram_count_matrix(corpus):
    """
    Creates the trigram count matrix from the input corpus in a single pass through the corpus.
    
    Args:
        corpus: Pre-processed and tokenized corpus. 
    
    Returns:
        bigrams: list of all bigram prefixes, row index
        vocabulary: list of all found words, the column index
        count_matrix: pandas dataframe with bigram prefixes as rows, 
                      vocabulary words as columns 
                      and the counts of the bigram/word combinations (i.e. trigrams) as values
    """
    bigrams = []
    vocabulary = []
    count_matrix_dict = defaultdict(dict)
    
    # go through the corpus once with a sliding window
    for i in range(len(corpus) - 3 + 1):
        # the sliding window starts at position i and contains 3 words
        trigram = tuple(corpus[i : i + 3])
        
        bigram = trigram[0 : -1]
        if not bigram in bigrams:
            bigrams.append(bigram)        
        
        last_word = trigram[-1]
        if not last_word in vocabulary:
            vocabulary.append(last_word)
        
        if (bigram,last_word) not in count_matrix_dict:
            count_matrix_dict[bigram,last_word] = 0
            
        count_matrix_dict[bigram,last_word] += 1
    
    # convert the count_matrix to np.array to fill in the blanks
    count_matrix = np.zeros((len(bigrams), len(vocabulary)))
    for trigram_key, trigam_count in count_matrix_dict.items():
        count_matrix[bigrams.index(trigram_key[0]), \
                     vocabulary.index(trigram_key[1])]\
        = trigam_count
    
    # np.array to pandas dataframe conversion
    count_matrix = pd.DataFrame(count_matrix, index=bigrams, columns=vocabulary)
    return bigrams, vocabulary, count_matrix

corpus = ['i', 'am', 'happy', 'because', 'i', 'am', 'learning', '.']

bigrams, vocabulary, count_matrix = single_pass_trigram_count_matrix(corpus)

print(count_matrix)
import numpy as np import pandas as pd from collections import defaultdict def single_pass_trigram_count_matrix(corpus): """ Creates the trigram count matrix from the input corpus in a single pass through the corpus. Args: corpus: Pre-processed and tokenized corpus. Returns: bigrams: list of all bigram prefixes, row index vocabulary: list of all found words, the column index count_matrix: pandas dataframe with bigram prefixes as rows, vocabulary words as columns and the counts of the bigram/word combinations (i.e. trigrams) as values """ bigrams = [] vocabulary = [] count_matrix_dict = defaultdict(dict) # go through the corpus once with a sliding window for i in range(len(corpus) - 3 + 1): # the sliding window starts at position i and contains 3 words trigram = tuple(corpus[i : i + 3]) bigram = trigram[0 : -1] if not bigram in bigrams: bigrams.append(bigram) last_word = trigram[-1] if not last_word in vocabulary: vocabulary.append(last_word) if (bigram,last_word) not in count_matrix_dict: count_matrix_dict[bigram,last_word] = 0 count_matrix_dict[bigram,last_word] += 1 # convert the count_matrix to np.array to fill in the blanks count_matrix = np.zeros((len(bigrams), len(vocabulary))) for trigram_key, trigam_count in count_matrix_dict.items(): count_matrix[bigrams.index(trigram_key[0]), \ vocabulary.index(trigram_key[1])]\ = trigam_count # np.array to pandas dataframe conversion count_matrix = pd.DataFrame(count_matrix, index=bigrams, columns=vocabulary) return bigrams, vocabulary, count_matrix corpus = ['i', 'am', 'happy', 'because', 'i', 'am', 'learning', '.'] bigrams, vocabulary, count_matrix = single_pass_trigram_count_matrix(corpus) print(count_matrix)

Probability matrix¶

The next step is to build a probability matrix from the count matrix.

You can use an object dataframe from library pandas and its methods sum and div to normalize the cell counts with the sum of the respective rows.

In [ ]:
Copied!
# create the probability matrix from the count matrix
row_sums = count_matrix.sum(axis=1)
# divide each row by its sum
prob_matrix = count_matrix.div(row_sums, axis=0)

print(prob_matrix)
# create the probability matrix from the count matrix row_sums = count_matrix.sum(axis=1) # divide each row by its sum prob_matrix = count_matrix.div(row_sums, axis=0) print(prob_matrix)

The probability matrix now helps you to find a probability of an input trigram.

In [ ]:
Copied!
# find the probability of a trigram in the probability matrix
trigram = ('i', 'am', 'happy')

# find the prefix bigram 
bigram = trigram[:-1]
print(f'bigram: {bigram}')

# find the last word of the trigram
word = trigram[-1]
print(f'word: {word}')

# we are using the pandas dataframes here, column with vocabulary word comes first, row with the prefix bigram second
trigram_probability = prob_matrix[word][bigram]
print(f'trigram_probability: {trigram_probability}')
# find the probability of a trigram in the probability matrix trigram = ('i', 'am', 'happy') # find the prefix bigram bigram = trigram[:-1] print(f'bigram: {bigram}') # find the last word of the trigram word = trigram[-1] print(f'word: {word}') # we are using the pandas dataframes here, column with vocabulary word comes first, row with the prefix bigram second trigram_probability = prob_matrix[word][bigram] print(f'trigram_probability: {trigram_probability}')

In the code assignment, you will be searching for the most probable words starting with a prefix. You can use the method str.startswith to test if a word starts with a prefix.

Here is a code snippet showing how to use this method.

In [ ]:
Copied!
# lists all words in vocabulary starting with a given prefix
vocabulary = ['i', 'am', 'happy', 'because', 'learning', '.', 'have', 'you', 'seen','it', '?']
starts_with = 'ha'

print(f'words in vocabulary starting with prefix: {starts_with}\n')
for word in vocabulary:
    if word.startswith(starts_with):
        print(word)
# lists all words in vocabulary starting with a given prefix vocabulary = ['i', 'am', 'happy', 'because', 'learning', '.', 'have', 'you', 'seen','it', '?'] starts_with = 'ha' print(f'words in vocabulary starting with prefix: {starts_with}\n') for word in vocabulary: if word.startswith(starts_with): print(word)

Language model evaluation¶

Train/validation/test split¶

In the videos, you saw that to evaluate language models, you need to keep some of the corpus data for validation and testing.

The choice of the test and validation data should correspond as much as possible to the distribution of the data coming from the actual application. If nothing but the input corpus is known, then random sampling from the corpus is used to define the test and validation subset.

Here is a code similar to what you'll see in the code assignment. The following function allows you to randomly sample the input data and return train/validation/test subsets in a split given by the method parameters.

In [ ]:
Copied!
# we only need train and validation %, test is the remainder
import random
def train_validation_test_split(data, train_percent, validation_percent):
    """
    Splits the input data to  train/validation/test according to the percentage provided
    
    Args:
        data: Pre-processed and tokenized corpus, i.e. list of sentences.
        train_percent: integer 0-100, defines the portion of input corpus allocated for training
        validation_percent: integer 0-100, defines the portion of input corpus allocated for validation
        
        Note: train_percent + validation_percent need to be <=100
              the reminder to 100 is allocated for the test set
    
    Returns:
        train_data: list of sentences, the training part of the corpus
        validation_data: list of sentences, the validation part of the corpus
        test_data: list of sentences, the test part of the corpus
    """
    # fixed seed here for reproducibility
    random.seed(87)
    
    # reshuffle all input sentences
    random.shuffle(data)

    train_size = int(len(data) * train_percent / 100)
    train_data = data[0:train_size]
    
    validation_size = int(len(data) * validation_percent / 100)
    validation_data = data[train_size:train_size + validation_size]
    
    test_data = data[train_size + validation_size:]
    
    return train_data, validation_data, test_data

data = [x for x in range (0, 100)]

train_data, validation_data, test_data = train_validation_test_split(data, 80, 10)
print("split 80/10/10:\n",f"train data:{train_data}\n", f"validation data:{validation_data}\n", 
      f"test data:{test_data}\n")

train_data, validation_data, test_data = train_validation_test_split(data, 98, 1)
print("split 98/1/1:\n",f"train data:{train_data}\n", f"validation data:{validation_data}\n", 
      f"test data:{test_data}\n")
# we only need train and validation %, test is the remainder import random def train_validation_test_split(data, train_percent, validation_percent): """ Splits the input data to train/validation/test according to the percentage provided Args: data: Pre-processed and tokenized corpus, i.e. list of sentences. train_percent: integer 0-100, defines the portion of input corpus allocated for training validation_percent: integer 0-100, defines the portion of input corpus allocated for validation Note: train_percent + validation_percent need to be <=100 the reminder to 100 is allocated for the test set Returns: train_data: list of sentences, the training part of the corpus validation_data: list of sentences, the validation part of the corpus test_data: list of sentences, the test part of the corpus """ # fixed seed here for reproducibility random.seed(87) # reshuffle all input sentences random.shuffle(data) train_size = int(len(data) * train_percent / 100) train_data = data[0:train_size] validation_size = int(len(data) * validation_percent / 100) validation_data = data[train_size:train_size + validation_size] test_data = data[train_size + validation_size:] return train_data, validation_data, test_data data = [x for x in range (0, 100)] train_data, validation_data, test_data = train_validation_test_split(data, 80, 10) print("split 80/10/10:\n",f"train data:{train_data}\n", f"validation data:{validation_data}\n", f"test data:{test_data}\n") train_data, validation_data, test_data = train_validation_test_split(data, 98, 1) print("split 98/1/1:\n",f"train data:{train_data}\n", f"validation data:{validation_data}\n", f"test data:{test_data}\n")

Perplexity¶

In order to implement the perplexity formula, you'll need to know how to implement m-th order root of a variable.

\begin{equation*} PP(W)=\sqrt[M]{\prod_{i=1}^{m}{\frac{1}{P(w_i|w_{i-1})}}} \end{equation*}

Remember from calculus:

\begin{equation*} \sqrt[M]{\frac{1}{x}} = x^{-\frac{1}{M}} \end{equation*}

Here is a code that will help you with the formula.

In [ ]:
Copied!
# to calculate the exponent, use the following syntax
p = 10 ** (-250)
M = 100
perplexity = p ** (-1 / M)
print(perplexity)
# to calculate the exponent, use the following syntax p = 10 ** (-250) M = 100 perplexity = p ** (-1 / M) print(perplexity)

That's all for the lab for "N-gram language model" lesson of week 3.


Documentation built with MkDocs.

Keyboard Shortcuts

Keys Action
? Open this help
n Next page
p Previous page
s Search