This is a preliminary report on the text prediction capstone project for the Johns Hopkins Data Science certification delivered by coursera.org. The goal is to construct a langauge model based on a set of data from blogs, online news articles and twitter posts so that it can be used for a text prediction algorithm. This report demonstrates that the data have been analyzed and that some preliminary considerations have been undertaken toward the goal of producing the language model and text prediction application.
I am treating three data files as the corpus for this project: a file of blog posts, one of twitter posts, and one of news articles. Four sets of such files were provided: one in English, Russian, Finnish, and German. As per the Dr. Peng’s instruction, this project is to focus on the English language files, and that’s what I’ve done here. The code to produce this document is mostly hidden, as the instructions indicate the audience should be a non-technical manager.
Below are the line, word and character counts for the three files:
lines | words | chars | filename |
---|---|---|---|
899288 | 37334690 | 210160014 | final/en_US/en_US.blogs.txt |
2360148 | 30374206 | 167105338 | final/en_US/en_US.twitter.txt |
1010242 | 34372720 | 205811889 | final/en_US/en_US.news.txt |
The files were read in and tokenized using the tokenizers
package. From the tokens, unigram and bigram frequencies were computed – that is occurrences of single words and pairs of words were counted up and recorded. Trigram (occurrences of three words) frequencies will also be computed, but the process I’ve been using for tokenization may need to be optimized to be able to store trigram occurrences more efficiently. More specifically, I will probably need a batched approach instead of invoking functions directly on the entire data set.
One of the activities that took more time than expected was exploring different approaches to storing word and n-gram frequencies, as well as pre-computed predictions. Specifically, I tested out using data frames, hashmaps, environments as hashmaps, and I also researched the use of tries, which seem especially attractive for this purpose. However, I didn’t find a flexible enough trie implementation in R (one where attributes could be set on words as well as where exact key matches could be queried). My current thinking is that I will use character vectors for index_to_word, hashmap for word_to_index, and matrices for index_of_context_words -> index_of_prediction. This is still very preliminary, though.
Below are some stats on the unigram (single word) frequency distribution in the corpus (the text data). A small fraction of the words make up the majority of all encountered occurences. After passing the tokens through a 1) profanity filter and 2) a “word” filter looking for only alphabetic characters and punctuation, 628,394 individual “words” were encountered. These are not all valid English words, however, as other scripts were observed in the data.
summary(word_freq)
## word frequency
## Length:628394 Min. : 1
## Class :character 1st Qu.: 1
## Mode :character Median : 1
## Mean : 152
## 3rd Qu.: 4
## Max. :4771927
head(word_freq,5)
## word frequency
## 1 the 4771927
## 2 to 2764230
## 3 and 2422450
## 4 in 1657973
## 5 i 1657335
The following plot (y-axis on log scale) shows word frequencies for each word, where the words are sorted from most frequent to least. The x-axis numbers are simply the integer id’s of each word, where 1 would be the most frequent word and 628,394 would be the least frequent word. The y-axis represents the number of occurrences of each word.
The following plot shows the cumulative proportion of all encountered words, from most frequent to least. That is to say at position x = 1, the plot shows what proportion of all encountered words is represented by the most frequent word. At x = 2, we see the proportion of the two most frequent words, then the three most, etc. The two horizontal lines are drawn at 0.5 (50%) and 0.9 (90%).
More precisely, the number of most frequent words comprising 50%, 90%, 95% and 99% are calculated directly. The code is shown here in case the reviewer knows a better method and can enlighten me. The code I came up with seems just a bit more effortful than I suspect is required.
# numerically, same question: subset the cumsum vector to include only values above the threshold
# and then see how many values were filtered by that operation -- that will be k - 1 words where
# k is the number of most frequent words comprising the threshold fraction of the corpus. Probably
# a more direct way to do this -- would like to know.
idx50 <- length(x) - length(x[x >= 0.5]) + 1
idx90 <- length(x) - length(x[x >= 0.9]) + 1
idx95 <- length(x) - length(x[x >= 0.95]) + 1
idx99 <- length(x) - length(x[x >= 0.99]) + 1
wordcounts <- c(idx50,idx90,idx95,idx99)
top_n_words | percent_of_corpus |
---|---|
171 | 50% |
7860 | 90% |
18496 | 95% |
106315 | 99% |
Below are some stats on the bigram (word pair) frequency distribution. While there were around 600,000 words encountered, there are more than 15,000,000 bigrams encountered.
summary(n2gram_freq)
## w1 w2 frequency
## Length:15305828 Length:15305828 Min. : 1.0
## Class :character Class :character 1st Qu.: 1.0
## Mode :character Mode :character Median : 1.0
## Mean : 6.1
## 3rd Qu.: 2.0
## Max. :408712.0
head(n2gram_freq,5)
## w1 w2 frequency
## 1 in the 408712
## 2 to the 213968
## 3 for the 201253
## 4 on the 197500
## 5 to be 162757
The following plot (y-axis on log scale) shows bigram frequencies for each bigram, where the bigrams are sorted from most frequent to least. While the most frequent word in the corpus was found more than 4,000,000 times, the most frequent bigram was found about 400,000 times, an order of magnitude of difference. Still the shape of the curve is similar to the unigram plot.
The following plot shows the cumulative proportion of all encountered bigrams, from most frequent to least. Whereas 171 words comprise 50% of all words encountered in the corpus, it takes almost 60,000 bigrams to account for half of all the bigrams encountered.
Again, the number of most frequent bigrams comprising 50%, 90%, 95% and 99% are calculated directly.
top_n_bigrams | percent_of_corpus |
---|---|
56881 | 50% |
6001743 | 90% |
10653786 | 95% |
14375420 | 99% |
I need to do more work on efficiently handling and loading trigram data – even now, the bigram operations take quite a while. The ideas I plan to implement going forward are based primarily on two sources. The first is Arnaldo Pedro Figueira Figueira’s coursera videos, which can be found on youtube. In them he describes bigram and trigram models, evaluating a model using perplexity, and dealing with the limitaions of the data set by using discounting. The second is Andrew Ng’s deeplearning.ai curriculum wherein he introduces the idea of word embeddings. I’m curious to see if, with reasonable computational effort, word embeddings can be used to increase the accuracy of the prediction model where the input text has very few examples in the corpus (through similarity mapping). Thanks for reading, and I’m looking forward to your feedback. Good luck with your project!