Facebook Research open sourced a great project recently - fastText, a fast (no surprise) and effective method to learn word representations and perform text classification. I was curious about comparing these embeddings to other commonly used embeddings, so word2vec seemed like the obvious choice, especially considering fastText embeddings are an extension of word2vec.
I've used gensim to train the word2vec models, and the analogical reasoning task (described in Section 4.1 of [2]) for comparing the word2vec and fastText models. I've compared embeddings trained using the skipgram architecture.
import nltk
from smart_open import smart_open
nltk.download('brown')
# Only the brown corpus is needed in case you don't have it.
# Generate brown corpus text file
with smart_open('brown_corp.txt', 'w+') as f:
for word in nltk.corpus.brown.words():
f.write('{word} '.format(word=word))
# Make sure you set FT_HOME to your fastText directory root
FT_HOME = 'fastText/'
# download the text8 corpus (a 100 MB sample of cleaned wikipedia text)
import os.path
if not os.path.isfile('text8'):
!wget -c http://mattmahoney.net/dc/text8.zip
!unzip text8.zip
# download and preprocess the text9 corpus
if not os.path.isfile('text9'):
!wget -c http://mattmahoney.net/dc/enwik9.zip
!unzip enwik9.zip
!perl {FT_HOME}wikifil.pl enwik9 > text9
[nltk_data] Downloading package brown to /home/jayant/nltk_data... [nltk_data] Package brown is already up-to-date! --2016-08-23 03:29:12-- http://mattmahoney.net/dc/text8.zip Resolving mattmahoney.net (mattmahoney.net)... 98.139.135.129 Connecting to mattmahoney.net (mattmahoney.net)|98.139.135.129|:80... connected. HTTP request sent, awaiting response... 416 Requested Range Not Satisfiable The file is already fully retrieved; nothing to do. Archive: text8.zip inflating: text8 --2016-08-23 03:29:15-- http://mattmahoney.net/dc/enwik9.zip Resolving mattmahoney.net (mattmahoney.net)... 98.139.135.129 Connecting to mattmahoney.net (mattmahoney.net)|98.139.135.129|:80... connected. HTTP request sent, awaiting response... 416 Requested Range Not Satisfiable The file is already fully retrieved; nothing to do. Archive: enwik9.zip inflating: enwik9
MODELS_DIR = 'models/'
!mkdir -p {MODELS_DIR}
lr = 0.05
dim = 100
ws = 5
epoch = 5
minCount = 5
neg = 5
loss = 'ns'
t = 1e-4
from gensim.models import Word2Vec, KeyedVectors
from gensim.models.word2vec import Text8Corpus
# Same values as used for fastText training above
params = {
'alpha': lr,
'size': dim,
'window': ws,
'iter': epoch,
'min_count': minCount,
'sample': t,
'sg': 1,
'hs': 0,
'negative': neg
}
def train_models(corpus_file, output_name):
output_file = '{:s}_ft'.format(output_name)
if not os.path.isfile(os.path.join(MODELS_DIR, '{:s}.vec'.format(output_file))):
print('Training fasttext on {:s} corpus..'.format(corpus_file))
%time !{FT_HOME}fasttext skipgram -input {corpus_file} -output {MODELS_DIR+output_file} -lr {lr} -dim {dim} -ws {ws} -epoch {epoch} -minCount {minCount} -neg {neg} -loss {loss} -t {t}
else:
print('\nUsing existing model file {:s}.vec'.format(output_file))
output_file = '{:s}_ft_no_ng'.format(output_name)
if not os.path.isfile(os.path.join(MODELS_DIR, '{:s}.vec'.format(output_file))):
print('\nTraining fasttext on {:s} corpus (without char n-grams)..'.format(corpus_file))
%time !{FT_HOME}fasttext skipgram -input {corpus_file} -output {MODELS_DIR+output_file} -lr {lr} -dim {dim} -ws {ws} -epoch {epoch} -minCount {minCount} -neg {neg} -loss {loss} -t {t} -maxn 0
else:
print('\nUsing existing model file {:s}.vec'.format(output_file))
output_file = '{:s}_gs'.format(output_name)
if not os.path.isfile(os.path.join(MODELS_DIR, '{:s}.vec'.format(output_file))):
print('\nTraining word2vec on {:s} corpus..'.format(corpus_file))
# Text8Corpus class for reading space-separated words file
%time gs_model = Word2Vec(Text8Corpus(corpus_file), **params); gs_model
# Direct local variable lookup doesn't work properly with magic statements (%time)
locals()['gs_model'].wv.save_word2vec_format(os.path.join(MODELS_DIR, '{:s}.vec'.format(output_file)))
print('\nSaved gensim model as {:s}.vec'.format(output_file))
else:
print('\nUsing existing model file {:s}.vec'.format(output_file))
evaluation_data = {}
train_models('brown_corp.txt', 'brown')
Training fasttext on brown_corp.txt corpus.. Read 1M words Progress: 100.0% words/sec/thread: 42250 lr: 0.000001 loss: 2.291523 eta: 0h0m Train time: 46.000000 sec CPU times: user 1.03 s, sys: 152 ms, total: 1.18 s Wall time: 54.3 s Training fasttext on brown_corp.txt corpus (without char n-grams).. Read 1M words Progress: 100.0% words/sec/thread: 78714 lr: 0.000001 loss: 2.360077 eta: 0h0m Train time: 25.000000 sec CPU times: user 908 ms, sys: 124 ms, total: 1.03 s Wall time: 32.5 s Training word2vec on brown_corp.txt corpus.. CPU times: user 41.4 s, sys: 0 ns, total: 41.4 s Wall time: 18 s Saved gensim model as brown_gs.vec
train_models(corpus_file='text8', output_name='text8')
Training fasttext on text8 corpus.. Read 17M words Progress: 100.0% words/sec/thread: 33709 lr: 0.000001 loss: 1.833047 eta: 0h0m Train time: 926.000000 sec CPU times: user 42.1 s, sys: 4.63 s, total: 46.8 s Wall time: 15min 42s Training fasttext on text8 corpus (without char n-grams).. Read 17M words Progress: 100.0% words/sec/thread: 66780 lr: 0.000001 loss: 1.878257 eta: 0h0m Train time: 479.000000 sec CPU times: user 29.8 s, sys: 3.46 s, total: 33.3 s Wall time: 8min 16s Training word2vec on text8 corpus.. CPU times: user 16min 16s, sys: 3.91 s, total: 16min 20s Wall time: 6min 42s Saved gensim model as text8_gs.vec
train_models(corpus_file='text9', output_name='text9')
Training fasttext on text9 corpus.. Read 124M words Progress: 100.0% words/sec/thread: 34687 lr: 0.000001 loss: 1.617828 eta: 0h0m Train time: 6519.000000 sec CPU times: user 5min 38s, sys: 37.4 s, total: 6min 16s Wall time: 1h 49min 49s Training fasttext on text9 corpus (without char n-grams).. Read 124M words Progress: 100.0% words/sec/thread: 67558 lr: 0.000001 loss: 1.645820 eta: 0h0m Train time: 3465.000000 sec CPU times: user 3min 32s, sys: 23.4 s, total: 3min 56s Wall time: 59min 10s Training word2vec on text9 corpus.. CPU times: user 2h 6min 34s, sys: 36.6 s, total: 2h 7min 10s Wall time: 53min 38s Saved gensim model as text9_gs.vec
# download the file questions-words.txt to be used for comparing word embeddings
!wget https://raw.githubusercontent.com/tmikolov/word2vec/master/questions-words.txt
--2016-08-21 10:31:16-- https://raw.githubusercontent.com/tmikolov/word2vec/master/questions-words.txt Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.100.133 Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.100.133|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 603955 (590K) [text/plain] Saving to: ‘questions-words.txt’ 100%[======================================>] 6,03,955 554KB/s in 1.1s 2016-08-21 10:31:17 (554 KB/s) - ‘questions-words.txt’ saved [603955/603955]
Once you have downloaded or trained the models and downloaded questions-words.txt
, you're ready to run the comparison.
import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
# Training times in seconds
evaluation_data['brown'] = [(18, 54.3, 32.5)]
evaluation_data['text8'] = [(402, 942, 496)]
evaluation_data['text9'] = [(3218, 6589, 3550)]
def print_accuracy(model, questions_file):
print('Evaluating...\n')
acc = model.accuracy(questions_file)
sem_correct = sum((len(acc[i]['correct']) for i in range(5)))
sem_total = sum((len(acc[i]['correct']) + len(acc[i]['incorrect'])) for i in range(5))
sem_acc = 100*float(sem_correct)/sem_total
print('\nSemantic: {:d}/{:d}, Accuracy: {:.2f}%'.format(sem_correct, sem_total, sem_acc))
syn_correct = sum((len(acc[i]['correct']) for i in range(5, len(acc)-1)))
syn_total = sum((len(acc[i]['correct']) + len(acc[i]['incorrect'])) for i in range(5,len(acc)-1))
syn_acc = 100*float(syn_correct)/syn_total
print('Syntactic: {:d}/{:d}, Accuracy: {:.2f}%\n'.format(syn_correct, syn_total, syn_acc))
return (sem_acc, syn_acc)
word_analogies_file = 'questions-words.txt'
accuracies = []
print('\nLoading Gensim embeddings')
brown_gs = KeyedVectors.load_word2vec_format(MODELS_DIR + 'brown_gs.vec')
print('Accuracy for Word2Vec:')
accuracies.append(print_accuracy(brown_gs, word_analogies_file))
print('\nLoading FastText embeddings')
brown_ft = KeyedVectors.load_word2vec_format(MODELS_DIR + 'brown_ft.vec')
print('Accuracy for FastText (with n-grams):')
accuracies.append(print_accuracy(brown_ft, word_analogies_file))
2016-08-23 10:32:14,416 : INFO : loading projection weights from models/brown_gs.vec
Loading Gensim embeddings
2016-08-23 10:32:15,938 : INFO : loaded (15173, 100) matrix from models/brown_gs.vec 2016-08-23 10:32:16,060 : INFO : precomputing L2-norms of word weight vectors
Accuracy for Word2Vec: Evaluating...
2016-08-23 10:32:16,746 : INFO : family: 20.3% (37/182) 2016-08-23 10:32:21,631 : INFO : gram1-adjective-to-adverb: 0.0% (0/702) 2016-08-23 10:32:22,421 : INFO : gram2-opposite: 0.0% (0/132) 2016-08-23 10:32:29,033 : INFO : gram3-comparative: 2.1% (22/1056) 2016-08-23 10:32:29,698 : INFO : gram4-superlative: 0.0% (0/210) 2016-08-23 10:32:33,248 : INFO : gram5-present-participle: 0.3% (2/650) 2016-08-23 10:32:37,647 : INFO : gram7-past-tense: 0.9% (11/1260) 2016-08-23 10:32:41,156 : INFO : gram8-plural: 5.4% (30/552) 2016-08-23 10:32:42,238 : INFO : gram9-plural-verbs: 0.9% (3/342) 2016-08-23 10:32:42,240 : INFO : total: 2.1% (105/5086) 2016-08-23 10:32:42,247 : INFO : loading projection weights from models/brown_ft.vec
Semantic: 37/182, Accuracy: 20.33% Syntactic: 68/4904, Accuracy: 1.39% Loading FastText embeddings
2016-08-23 10:32:45,542 : INFO : loaded (15173, 100) matrix from models/brown_ft.vec 2016-08-23 10:32:45,636 : INFO : precomputing L2-norms of word weight vectors
Accuracy for FastText (with n-grams): Evaluating...
2016-08-23 10:32:46,240 : INFO : family: 15.4% (28/182) 2016-08-23 10:32:50,291 : INFO : gram1-adjective-to-adverb: 73.4% (515/702) 2016-08-23 10:32:50,752 : INFO : gram2-opposite: 81.1% (107/132) 2016-08-23 10:32:56,089 : INFO : gram3-comparative: 58.7% (620/1056) 2016-08-23 10:32:57,538 : INFO : gram4-superlative: 67.1% (141/210) 2016-08-23 10:33:00,715 : INFO : gram5-present-participle: 65.8% (428/650) 2016-08-23 10:33:06,537 : INFO : gram7-past-tense: 13.9% (175/1260) 2016-08-23 10:33:10,094 : INFO : gram8-plural: 55.4% (306/552) 2016-08-23 10:33:11,211 : INFO : gram9-plural-verbs: 74.0% (253/342) 2016-08-23 10:33:11,212 : INFO : total: 50.6% (2573/5086)
Semantic: 28/182, Accuracy: 15.38% Syntactic: 2545/4904, Accuracy: 51.90%
The accuracy
takes an optional parameter restrict_vocab
, which limits the vocabulary of model considered for fast approximate evaluation (default is 30000).
Word2Vec embeddings seem to be slightly better than fastText embeddings at the semantic tasks, while the fastText embeddings do significantly better on the syntactic analogies. Makes sense, since fastText embeddings are trained for understanding morphological nuances, and most of the syntactic analogies are morphology based.
Let me explain that better.
According to the paper [1], embeddings for words are represented by the sum of their n-gram embeddings. This is meant to be useful for morphologically rich languages - so theoretically, the embedding for apparently
would include information from both character n-grams apparent
and ly
(as well as other n-grams), and the n-grams would combine in a simple, linear manner. This is very similar to what most of our syntactic tasks look like.
Example analogy:
amazing amazingly calm calmly
This analogy is marked correct if:
embedding(amazing)
- embedding(amazingly)
= embedding(calm)
- embedding(calmly)
Both these subtractions would result in a very similar set of remaining ngrams. No surprise the fastText embeddings do extremely well on this.
Let's do a small test to validate this hypothesis - fastText differs from word2vec only in that it uses char n-gram embeddings as well as the actual word embedding in the scoring function to calculate scores and then likelihoods for each word, given a context word. In case char n-gram embeddings are not present, this reduces (at least theoretically) to the original word2vec model. This can be implemented by setting 0 for the max length of char n-grams for fastText.
print('Loading FastText embeddings')
brown_ft_no_ng = KeyedVectors.load_word2vec_format(MODELS_DIR + 'brown_ft_no_ng.vec')
print('Accuracy for FastText (without n-grams):')
accuracies.append(print_accuracy(brown_ft_no_ng, word_analogies_file))
evaluation_data['brown'] += [[acc[0] for acc in accuracies], [acc[1] for acc in accuracies]]
2016-08-23 10:33:57,845 : INFO : loading projection weights from models/brown_ft_no_ng.vec
Loading FastText embeddings
2016-08-23 10:33:59,447 : INFO : loaded (15173, 100) matrix from models/brown_ft_no_ng.vec 2016-08-23 10:33:59,538 : INFO : precomputing L2-norms of word weight vectors
Accuracy for FastText (without n-grams): Evaluating...
2016-08-23 10:34:00,435 : INFO : family: 17.0% (31/182) 2016-08-23 10:34:02,610 : INFO : gram1-adjective-to-adverb: 0.1% (1/702) 2016-08-23 10:34:03,012 : INFO : gram2-opposite: 0.8% (1/132) 2016-08-23 10:34:06,230 : INFO : gram3-comparative: 2.9% (31/1056) 2016-08-23 10:34:06,876 : INFO : gram4-superlative: 0.5% (1/210) 2016-08-23 10:34:09,941 : INFO : gram5-present-participle: 1.1% (7/650) 2016-08-23 10:34:14,521 : INFO : gram7-past-tense: 0.7% (9/1260) 2016-08-23 10:34:16,379 : INFO : gram8-plural: 5.3% (29/552) 2016-08-23 10:34:17,978 : INFO : gram9-plural-verbs: 2.0% (7/342) 2016-08-23 10:34:17,980 : INFO : total: 2.3% (117/5086)
Semantic: 31/182, Accuracy: 17.03% Syntactic: 86/4904, Accuracy: 1.75%
A-ha! The results for FastText with no n-grams and Word2Vec look a lot more similar (as they should) - the differences could easily result from differences in implementation between fastText and Gensim, and randomization. Especially telling is that the semantic accuracy for FastText has improved slightly after removing n-grams, while the syntactic accuracy has taken a giant dive. Our hypothesis that the char n-grams result in better performance on syntactic analogies seems fair. It also seems possible that char n-grams hurt semantic accuracy a little. However, the brown corpus is too small to be able to draw any definite conclusions - the accuracies seem to vary significantly over different runs.
Let's try with a larger corpus now - text8 (collection of wiki articles). I'm also curious about the impact on semantic accuracy - for models trained on the brown corpus, the difference in the semantic accuracy and the accuracy values themselves are too small to be conclusive. Hopefully a larger corpus helps, and the text8 corpus likely has a lot more information about capitals, currencies, cities etc, which should be relevant to the semantic tasks.
accuracies = []
print('Loading Gensim embeddings')
text8_gs = KeyedVectors.load_word2vec_format(MODELS_DIR + 'text8_gs.vec')
print('Accuracy for word2vec:')
accuracies.append(print_accuracy(text8_gs, word_analogies_file))
print('Loading FastText embeddings (with n-grams)')
text8_ft = KeyedVectors.load_word2vec_format(MODELS_DIR + 'text8_ft.vec')
print('Accuracy for FastText (with n-grams):')
accuracies.append(print_accuracy(text8_ft, word_analogies_file))
print('Loading FastText embeddings')
text8_ft_no_ng = KeyedVectors.load_word2vec_format(MODELS_DIR + 'text8_ft_no_ng.vec')
print('Accuracy for FastText (without n-grams):')
accuracies.append(print_accuracy(text8_ft_no_ng, word_analogies_file))
evaluation_data['text8'] += [[acc[0] for acc in accuracies], [acc[1] for acc in accuracies]]
2016-08-23 10:34:18,050 : INFO : loading projection weights from models/text8_gs.vec
Loading Gensim embeddings
2016-08-23 10:34:25,739 : INFO : loaded (71290, 100) matrix from models/text8_gs.vec 2016-08-23 10:34:25,927 : INFO : precomputing L2-norms of word weight vectors
Accuracy for word2vec: Evaluating...
2016-08-23 10:34:28,601 : INFO : capital-common-countries: 67.0% (339/506) 2016-08-23 10:34:40,531 : INFO : capital-world: 45.3% (658/1452) 2016-08-23 10:34:42,060 : INFO : currency: 20.9% (56/268) 2016-08-23 10:34:53,593 : INFO : city-in-state: 23.2% (365/1571) 2016-08-23 10:34:57,068 : INFO : family: 59.2% (181/306) 2016-08-23 10:35:03,180 : INFO : gram1-adjective-to-adverb: 14.3% (108/756) 2016-08-23 10:35:06,024 : INFO : gram2-opposite: 11.4% (35/306) 2016-08-23 10:35:17,130 : INFO : gram3-comparative: 50.2% (633/1260) 2016-08-23 10:35:21,890 : INFO : gram4-superlative: 30.8% (156/506) 2016-08-23 10:35:29,381 : INFO : gram5-present-participle: 26.6% (264/992) 2016-08-23 10:35:40,566 : INFO : gram6-nationality-adjective: 76.9% (1054/1371) 2016-08-23 10:35:51,950 : INFO : gram7-past-tense: 29.2% (389/1332) 2016-08-23 10:36:01,483 : INFO : gram8-plural: 52.0% (516/992) 2016-08-23 10:36:07,185 : INFO : gram9-plural-verbs: 30.9% (201/650) 2016-08-23 10:36:07,187 : INFO : total: 40.4% (4955/12268) 2016-08-23 10:36:07,194 : INFO : loading projection weights from models/text8_ft.vec
Semantic: 1599/4103, Accuracy: 38.97% Syntactic: 3356/8165, Accuracy: 41.10% Loading FastText embeddings (with n-grams)
2016-08-23 10:36:15,551 : INFO : loaded (71290, 100) matrix from models/text8_ft.vec 2016-08-23 10:36:15,761 : INFO : precomputing L2-norms of word weight vectors
Accuracy for FastText (with n-grams): Evaluating...
2016-08-23 10:36:20,631 : INFO : capital-common-countries: 57.5% (291/506) 2016-08-23 10:36:31,906 : INFO : capital-world: 44.6% (647/1452) 2016-08-23 10:36:34,651 : INFO : currency: 17.2% (46/268) 2016-08-23 10:36:49,641 : INFO : city-in-state: 18.0% (272/1511) 2016-08-23 10:36:51,402 : INFO : family: 51.0% (156/306) 2016-08-23 10:36:59,474 : INFO : gram1-adjective-to-adverb: 76.3% (577/756) 2016-08-23 10:37:01,244 : INFO : gram2-opposite: 62.7% (192/306) 2016-08-23 10:37:14,023 : INFO : gram3-comparative: 60.5% (762/1260) 2016-08-23 10:37:18,805 : INFO : gram4-superlative: 55.3% (280/506) 2016-08-23 10:37:28,822 : INFO : gram5-present-participle: 55.6% (552/992) 2016-08-23 10:37:39,523 : INFO : gram6-nationality-adjective: 93.1% (1276/1371) 2016-08-23 10:37:52,889 : INFO : gram7-past-tense: 38.0% (506/1332) 2016-08-23 10:38:01,988 : INFO : gram8-plural: 87.8% (871/992) 2016-08-23 10:38:07,999 : INFO : gram9-plural-verbs: 57.5% (374/650) 2016-08-23 10:38:08,013 : INFO : total: 55.7% (6802/12208) 2016-08-23 10:38:08,032 : INFO : loading projection weights from models/text8_ft_no_ng.vec
Semantic: 1412/4043, Accuracy: 34.92% Syntactic: 5390/8165, Accuracy: 66.01% Loading FastText embeddings
2016-08-23 10:38:16,212 : INFO : loaded (71290, 100) matrix from models/text8_ft_no_ng.vec 2016-08-23 10:38:16,423 : INFO : precomputing L2-norms of word weight vectors
Accuracy for FastText (without n-grams): Evaluating...
2016-08-23 10:38:19,529 : INFO : capital-common-countries: 71.7% (363/506) 2016-08-23 10:38:31,840 : INFO : capital-world: 49.1% (713/1452) 2016-08-23 10:38:33,427 : INFO : currency: 25.0% (67/268) 2016-08-23 10:38:45,850 : INFO : city-in-state: 22.3% (337/1511) 2016-08-23 10:38:47,491 : INFO : family: 56.5% (173/306) 2016-08-23 10:38:55,203 : INFO : gram1-adjective-to-adverb: 19.0% (144/756) 2016-08-23 10:38:56,777 : INFO : gram2-opposite: 13.7% (42/306) 2016-08-23 10:39:07,522 : INFO : gram3-comparative: 45.1% (568/1260) 2016-08-23 10:39:12,079 : INFO : gram4-superlative: 28.3% (143/506) 2016-08-23 10:39:21,022 : INFO : gram5-present-participle: 22.3% (221/992) 2016-08-23 10:39:32,463 : INFO : gram6-nationality-adjective: 78.6% (1078/1371) 2016-08-23 10:39:45,108 : INFO : gram7-past-tense: 32.0% (426/1332) 2016-08-23 10:39:52,383 : INFO : gram8-plural: 54.6% (542/992) 2016-08-23 10:39:57,698 : INFO : gram9-plural-verbs: 23.2% (151/650) 2016-08-23 10:39:57,707 : INFO : total: 40.7% (4968/12208)
Semantic: 1653/4043, Accuracy: 40.89% Syntactic: 3315/8165, Accuracy: 40.60%
With the text8 corpus, we observe a similar pattern. Semantic accuracy falls by a small but significant amount when n-grams are included in FastText, while FastText with n-grams performs far better on the syntactic analogies. FastText without n-grams are largely similar to Word2Vec.
My hypothesis for semantic accuracy being lower for the FastText-with-ngrams model is that most of the words in the semantic analogies are standalone words and are unrelated to their morphemes (eg: father, mother, France, Paris), hence inclusion of the char n-grams into the scoring function actually makes the embeddings worse.
This trend is observed in the original paper too where the performance of embeddings with n-grams is worse on semantic tasks than both word2vec cbow and skipgram models.
Let's do a quick comparison on an even larger corpus - text9
accuracies = []
print('Loading Gensim embeddings')
text9_gs = KeyedVectors.load_word2vec_format(MODELS_DIR + 'text9_gs.vec')
print('Accuracy for word2vec:')
accuracies.append(print_accuracy(text9_gs, word_analogies_file))
print('Loading FastText embeddings (with n-grams)')
text9_ft = KeyedVectors.load_word2vec_format(MODELS_DIR + 'text9_ft.vec')
print('Accuracy for FastText (with n-grams):')
accuracies.append(print_accuracy(text9_ft, word_analogies_file))
print('Loading FastText embeddings')
text9_ft_no_ng = KeyedVectors.load_word2vec_format(MODELS_DIR + 'text9_ft_no_ng.vec')
print('Accuracy for FastText (without n-grams):')
accuracies.append(print_accuracy(text9_ft_no_ng, word_analogies_file))
evaluation_data['text9'] += [[acc[0] for acc in accuracies], [acc[1] for acc in accuracies]]
2016-08-23 10:40:04,786 : INFO : loading projection weights from models/text9_gs.vec
Loading Gensim embeddings
2016-08-23 10:40:30,884 : INFO : loaded (218316, 100) matrix from models/text9_gs.vec
Accuracy for word2vec: Evaluating...
2016-08-23 10:40:31,775 : INFO : precomputing L2-norms of word weight vectors 2016-08-23 10:40:34,765 : INFO : capital-common-countries: 84.2% (426/506) 2016-08-23 10:40:48,132 : INFO : capital-world: 74.6% (1221/1636) 2016-08-23 10:40:49,018 : INFO : currency: 21.9% (39/178) 2016-08-23 10:41:06,134 : INFO : city-in-state: 41.8% (891/2133) 2016-08-23 10:41:10,016 : INFO : family: 67.1% (255/380) 2016-08-23 10:41:15,973 : INFO : gram1-adjective-to-adverb: 28.7% (233/812) 2016-08-23 10:41:19,624 : INFO : gram2-opposite: 33.3% (114/342) 2016-08-23 10:41:30,799 : INFO : gram3-comparative: 67.1% (846/1260) 2016-08-23 10:41:35,522 : INFO : gram4-superlative: 51.8% (262/506) 2016-08-23 10:41:42,860 : INFO : gram5-present-participle: 51.8% (482/930) 2016-08-23 10:41:58,254 : INFO : gram6-nationality-adjective: 91.2% (1250/1371) 2016-08-23 10:42:11,573 : INFO : gram7-past-tense: 49.3% (657/1332) 2016-08-23 10:42:21,335 : INFO : gram8-plural: 75.2% (794/1056) 2016-08-23 10:42:27,170 : INFO : gram9-plural-verbs: 47.4% (333/702) 2016-08-23 10:42:27,172 : INFO : total: 59.4% (7803/13144) 2016-08-23 10:42:27,179 : INFO : loading projection weights from models/text9_ft.vec
Semantic: 2832/4833, Accuracy: 58.60% Syntactic: 4971/8311, Accuracy: 59.81% Loading FastText embeddings (with n-grams)
2016-08-23 10:42:52,360 : INFO : loaded (218316, 100) matrix from models/text9_ft.vec
Accuracy for FastText (with n-grams): Evaluating...
2016-08-23 10:42:53,500 : INFO : precomputing L2-norms of word weight vectors 2016-08-23 10:42:58,763 : INFO : capital-common-countries: 84.6% (428/506) 2016-08-23 10:43:14,010 : INFO : capital-world: 71.8% (1174/1636) 2016-08-23 10:43:16,987 : INFO : currency: 22.5% (40/178) 2016-08-23 10:43:38,323 : INFO : city-in-state: 33.0% (703/2133) 2016-08-23 10:43:41,400 : INFO : family: 59.5% (226/380) 2016-08-23 10:43:49,864 : INFO : gram1-adjective-to-adverb: 55.5% (451/812) 2016-08-23 10:43:53,560 : INFO : gram2-opposite: 50.6% (173/342) 2016-08-23 10:44:04,678 : INFO : gram3-comparative: 72.9% (919/1260) 2016-08-23 10:44:09,314 : INFO : gram4-superlative: 58.1% (294/506) 2016-08-23 10:44:16,302 : INFO : gram5-present-participle: 55.7% (518/930) 2016-08-23 10:44:29,878 : INFO : gram6-nationality-adjective: 94.5% (1296/1371) 2016-08-23 10:44:42,831 : INFO : gram7-past-tense: 48.0% (640/1332) 2016-08-23 10:44:52,464 : INFO : gram8-plural: 83.5% (882/1056) 2016-08-23 10:44:58,566 : INFO : gram9-plural-verbs: 55.7% (391/702) 2016-08-23 10:44:58,576 : INFO : total: 61.9% (8135/13144) 2016-08-23 10:44:58,589 : INFO : loading projection weights from models/text9_ft_no_ng.vec
Semantic: 2571/4833, Accuracy: 53.20% Syntactic: 5564/8311, Accuracy: 66.95% Loading FastText embeddings
2016-08-23 10:45:24,650 : INFO : loaded (218316, 100) matrix from models/text9_ft_no_ng.vec
Accuracy for FastText (without n-grams): Evaluating...
2016-08-23 10:45:25,641 : INFO : precomputing L2-norms of word weight vectors 2016-08-23 10:45:29,053 : INFO : capital-common-countries: 87.7% (444/506) 2016-08-23 10:45:44,191 : INFO : capital-world: 77.3% (1264/1636) 2016-08-23 10:45:45,169 : INFO : currency: 19.7% (35/178) 2016-08-23 10:46:03,906 : INFO : city-in-state: 39.2% (836/2133) 2016-08-23 10:46:08,098 : INFO : family: 62.9% (239/380) 2016-08-23 10:46:14,351 : INFO : gram1-adjective-to-adverb: 30.7% (249/812) 2016-08-23 10:46:18,093 : INFO : gram2-opposite: 32.7% (112/342) 2016-08-23 10:46:28,567 : INFO : gram3-comparative: 70.2% (885/1260) 2016-08-23 10:46:33,014 : INFO : gram4-superlative: 49.4% (250/506) 2016-08-23 10:46:39,864 : INFO : gram5-present-participle: 48.1% (447/930) 2016-08-23 10:46:52,864 : INFO : gram6-nationality-adjective: 93.1% (1277/1371) 2016-08-23 10:47:03,413 : INFO : gram7-past-tense: 47.2% (629/1332) 2016-08-23 10:47:15,591 : INFO : gram8-plural: 78.3% (827/1056) 2016-08-23 10:47:21,488 : INFO : gram9-plural-verbs: 43.0% (302/702) 2016-08-23 10:47:21,491 : INFO : total: 59.3% (7796/13144)
Semantic: 2818/4833, Accuracy: 58.31% Syntactic: 4978/8311, Accuracy: 59.90%
%matplotlib inline
import matplotlib.pyplot as plt
def plot(ax, data, corpus_name='brown'):
width = 0.25
pos = [(i, i + width, i + 2*width) for i in range(len(data))]
colors = ['#EE3224', '#F78F1E', '#FFC222']
acc_ax = ax.twinx()
# Training time
ax.bar(pos[0],
data[0],
width,
alpha=0.5,
color=colors
)
# Semantic accuracy
acc_ax.bar(pos[1],
data[1],
width,
alpha=0.5,
color=colors
)
# Syntactic accuracy
acc_ax.bar(pos[2],
data[2],
width,
alpha=0.5,
color=colors
)
ax.set_ylabel('Training time (s)')
acc_ax.set_ylabel('Accuracy (%)')
ax.set_title(corpus_name)
acc_ax.set_xticks([p[0] + 1.5 * width for p in pos])
acc_ax.set_xticklabels(['Training Time', 'Semantic Accuracy', 'Syntactic Accuracy'])
# Proxy plots for adding legend correctly
proxies = [ax.bar([0], [0], width=0, color=c, alpha=0.5)[0] for c in colors]
models = ('Gensim', 'FastText', 'FastText (no-ngrams)')
ax.legend((proxies), models, loc='upper left')
ax.set_xlim(pos[0][0]-width, pos[-1][0]+width*4)
ax.set_ylim([0, max(data[0])*1.1] )
acc_ax.set_ylim([0, max(data[1] + data[2])*1.1] )
plt.grid()
# Plotting the bars
fig = plt.figure(figsize=(10,15))
for corpus, subplot in zip(sorted(evaluation_data.keys()), [311, 312, 313]):
ax = fig.add_subplot(subplot)
plot(ax, evaluation_data[corpus], corpus)
plt.show()
The results from text9 seem to confirm our hypotheses so far. Briefly summarising the main points -
These preliminary results seem to indicate fastText embeddings are significantly better than word2vec at encoding syntactic information. This is expected, since most syntactic analogies are morphology based, and the char n-gram approach of fastText takes such information into account. The original word2vec model seems to perform better on semantic tasks, since words in semantic analogies are unrelated to their char n-grams, and the added information from irrelevant char n-grams worsens the embeddings. It'd be interesting to see how transferable these embeddings are for different kinds of tasks by comparing their performance in a downstream supervised task.