An experiment in word embeddings

Five Word2Vec Models, Two Corpora, One Classifier

This experiment compares five routes to a Word2Vec model: two trained with gensim on movie reviews, a frozen pretrained model, a fine-tuned version of those pretrained weights, and a skip-gram implementation written from scratch. The three models trained from scratch are then retrained on text8, 17 million words of Wikipedia, to see how much the training text matters. The same pipeline measures sentiment accuracy, lexical structure, and runtime cost.

Corpora SST-2 phrases, 633,720 tokens; text8, 17,005,207 Vocabulary 14,309 (SST), 135,335 (text8) Dimensions 300 Test split 1,821 sentences Machine M2 Air; PACE ICE A100 for C (text8)
Where things stand

Eight embeddings, one classifier

A, B and C appear twice, once trained on SST and once retrained on text8. Every model goes through the same evaluation. I look up each word vector, average the vectors into one sentence representation, fit logistic regression, and test on the same held-out SST-2 sentences. The classifier is never tuned for an individual model. This keeps the comparison focused on the embeddings.

ASkip-gram
+ negative sampling
trained on SST, 634k tokens
Sentiment accuracy
0.761
Word similarity +0.029
gensim · 11.8 s to train
ASkip-gram
+ negative sampling
trained on text8, 17M tokens
Sentiment accuracy
0.722
Word similarity +0.694
gensim · 889.3 s to train
BCBOW
+ hierarchical softmax
trained on SST, 634k tokens
Sentiment accuracy
0.729
Word similarity -0.036
gensim · 3.6 s to train
BCBOW
+ hierarchical softmax
trained on text8, 17M tokens
Sentiment accuracy
0.724
Word similarity +0.666
gensim · 176.6 s to train
CSkip-gram + NS
written from scratch
trained on SST, 634k tokens
Sentiment accuracy
0.744
Word similarity +0.005
PyTorch, from scratch · 160.2 s to train
CSkip-gram + NS
written from scratch
trained on text8, 17M tokens
Sentiment accuracy
0.715
Word similarity +0.644
PyTorch, A100 GPU · 658.7 s to train
GGoogleNews-300
frozen
trained on GoogleNews, 100B tokens
Sentiment accuracy
0.798
Word similarity +0.700
downloaded · 2.8 s to load
G-ftGoogleNews
fine-tuned on SST
trained on GoogleNews, then SST
Sentiment accuracy
0.792
Word similarity +0.585
gensim · 13.8 s to fine-tune

Sentiment accuracy is the share of the 1,821 SST-2 test sentences classified correctly. Word similarity is the Spearman correlation between the model's cosine similarities and human ratings on WordSim-353: 1 is perfect agreement, 0 is none.

The warm start needs its own control. Comparing A with G-ft looks natural, but it mixes two changes. G-ft also uses a lower learning rate (0.005 against 0.025) and fewer epochs (8 against 10). A2 uses A's random initialization with G-ft's exact schedule, leaving the starting weights as the only difference. A2 reaches 0.664, while G-ft reaches 0.792. On that controlled comparison, the warm start adds +0.127, not the +0.031 suggested by A versus G-ft. A's more aggressive schedule partly makes up for its random start, so the loose comparison understates the benefit.
ModelArchitectureLossTrained onAccuracyF1OOV
A (SST)Skip-gramNegative sampling, k=10633,720 tok0.76110.77570.066
A (text8)Skip-gramNegative sampling, k=1017,005,207 tok0.72210.74130.169
B (SST)CBOWHierarchical softmax633,720 tok0.72930.75010.066
B (text8)CBOWHierarchical softmax17,005,207 tok0.72430.74900.169
C (SST)Skip-gramNegative sampling, k=10633,720 tok0.74410.76100.066
C (text8)Skip-gramNegative sampling, k=1017,005,207 tok0.71500.73400.169
GSkip-gramNegative sampling, k=5100,000,000,000 tok0.79790.81070.271
G-ftSkip-gramNegative sampling, k=10633,720 tok0.79190.80890.066
A2Skip-gramNegative sampling, k=10633,720 tok0.66450.70700.066

A2 is a control, not a sixth model. Its 0.664 score is the lowest in the table, but that result is useful: it makes the claim about G-ft's warm start testable. The three text8 rows are A, B and C trained on Wikipedia instead of SST. More training text covers them.

Held-out sentiment, example by example

Five test examples, ranked by the five main models

These examples show the held-out sentiment task at sentence level. For each model, the table gives the classifier's two labels with their probabilities and the top-5 closest training sentences by cosine similarity. Neighbour agreement is the fraction of those retrieved sentences whose gold label matches the test example. It gives a small, concrete view of what evidence sits nearby in the embedding space.

I selected all five by rule, not by eye. The rule appears above each example. Accuracy within this small set is A 3/5, B 3/5, C 3/5, G 4/5, G-ft 3/5.

Example 1 · plain positive

this is one of polanski 's best films .

gold label: positive
ModelRank 1Rank 2ResultNeighbour agreement
A (SST)positive 0.838negative 0.162correct1.0
B (SST)positive 0.694negative 0.306correct1.0
C (SST)positive 0.656negative 0.344correct1.0
Gpositive 0.855negative 0.145correct1.0
G-ftpositive 0.808negative 0.192correct1.0
G-ft retrieves this is one of the year 's best films . · 0.99is one of the year 's best films . · 0.98is one of the year 's best films · 0.97

Training sentences are ordered by cosine similarity. A green tint means the retrieved sentence's label matches the test sentence's gold label. This is separate from the gold tint in the neighbour tables.

Example 2 · plain negative

arnold 's jump from little screen to big will leave frowns on more than a few faces .

gold label: negative
ModelRank 1Rank 2ResultNeighbour agreement
A (SST)positive 0.593negative 0.407wrong1.0
B (SST)negative 0.534positive 0.466correct1.0
C (SST)negative 0.546positive 0.454correct0.8
Gnegative 0.668positive 0.332correct0.0
G-ftpositive 0.512negative 0.488wrong1.0
G-ft retrieves big fat liar is little more than home alone raised to a new , self-deprecating … · 0.93is little more than home alone raised to a new , self-deprecating level . · 0.92the film runs on a little longer than it needs to · 0.92

Training sentences are ordered by cosine similarity. A green tint means the retrieved sentence's label matches the test sentence's gold label. This is separate from the gold tint in the neighbour tables.

Example 3 · positive despite negation

finally , a genre movie that delivers -- in a couple of genres , no less .

gold label: positive
ModelRank 1Rank 2ResultNeighbour agreement
A (SST)negative 0.596positive 0.404wrong0.4
B (SST)negative 0.643positive 0.357wrong0.2
C (SST)negative 0.597positive 0.403wrong0.4
Gpositive 0.680negative 0.320correct0.4
G-ftnegative 0.508positive 0.492wrong0.8
G-ft retrieves neither quite a comedy nor a romance , more of an impish divertissement of … · 0.95singer/composer bryan adams contributes a slew of songs -- a few potential hits … · 0.94singer/composer bryan adams contributes a slew of songs -- a few potential hits … · 0.94

Training sentences are ordered by cosine similarity. A green tint means the retrieved sentence's label matches the test sentence's gold label. This is separate from the gold tint in the neighbour tables.

Example 4 · contrastive clause

but what saves lives on the freeway does not necessarily make for persuasive viewing .

gold label: negative
ModelRank 1Rank 2ResultNeighbour agreement
A (SST)negative 0.592positive 0.408correct1.0
B (SST)positive 0.574negative 0.426wrong0.8
C (SST)positive 0.614negative 0.386wrong0.6
Gpositive 0.565negative 0.435wrong0.8
G-ftnegative 0.502positive 0.498correct0.8
G-ft retrieves does n't show enough of the creative process or even of what was created … · 0.92if swimfan does catch on , it may be because teens are looking for something to … · 0.92what might have been acceptable on the printed page of iles ' book does … · 0.92

Training sentences are ordered by cosine similarity. A green tint means the retrieved sentence's label matches the test sentence's gold label. This is separate from the gold tint in the neighbour tables.

Example 5 · negative with stacked negation

no movement , no yuks , not much of anything .

gold label: negative
ModelRank 1Rank 2ResultNeighbour agreement
A (SST)negative 0.972positive 0.028correct1.0
B (SST)negative 0.955positive 0.045correct1.0
C (SST)negative 0.860positive 0.140correct0.6
Gnegative 0.964positive 0.036correct1.0
G-ftnegative 0.989positive 0.011correct1.0
G-ft retrieves ... no charm , no laughs , no fun , no reason to watch . · 0.92no charm , no laughs , no fun , no reason to watch . · 0.92no charm , no laughs , no fun , no reason to watch · 0.91

Training sentences are ordered by cosine similarity. A green tint means the retrieved sentence's label matches the test sentence's gold label. This is separate from the gold tint in the neighbour tables.

Example 3 separates GoogleNews from every model trained on SST. In this sentence, no less is an intensifier rather than a negation. Of the five main models, only the frozen GoogleNews model reads it that way. Every SST-trained model, including G-ft, predicts negative.
Example 5 deserves a closer look. All five main models get it right with high confidence, and retrieval shows why. The nearest training sentence is no charm , no laughs , no fun, a near-duplicate of the test sentence's stacked-negation pattern. The models succeed through lexical overlap, not by composing the negation. The same shortcut fails on Example 3 but happens to work here.
Intrinsic evaluation

Top-ranked neighbours for five queries

The five queries are meant to expose differences between the models: a polysemous noun, a sentiment adjective, a domain-sensitive word, a rare film term, and the most frequent word in English.

word 0.00 A gold-tinted chip appears only in row G-ft. It marks a neighbour that G does not return for the same query, so it entered the neighbourhood after fine-tuning on SST. All other chips are grey.

plot

ModelTop-1Top-2 to 5Top-6 to 10
A (SST)twists 0.48pawn 0.46device 0.46court 0.46oh-so 0.45nifty 0.45cliché-riddled 0.44stop-and-start 0.44triteness 0.44convenient 0.44
A (text8)ridolfi 0.55popish 0.51subplot 0.51gattaca 0.49plots 0.49babington 0.48whodunit 0.48counterpointed 0.48replicants 0.48dramatized 0.48
B (SST)ending 0.32accomplishment 0.28sweetness 0.26flourish 0.25despair 0.25flashy 0.25mastering 0.25clancy 0.25compressed 0.24sure-fire 0.24
B (text8)storylines 0.37storyline 0.36gattaca 0.36sequel 0.35story 0.34plots 0.32character 0.32villain 0.32flashback 0.32horror 0.31
C (SST)twists 0.86device 0.84oh-so 0.84halfwit 0.83court 0.83stop-and-start 0.81maneuvers 0.81sillified 0.81holes 0.80nurses 0.80
C (text8)ridolfi 0.59subplot 0.58plots 0.58popish 0.56storyline 0.55gattaca 0.55conspirators 0.53storylines 0.53fawkes 0.53macguffin 0.52
Gplots 0.76Plot 0.65plotting 0.63plotted 0.60plotline 0.59plotters 0.54storyline 0.53conspiracies 0.52conspiracy 0.51foiled_plot 0.50
G-ftplots 0.77plotting 0.68plotted 0.62plotline 0.56storyline 0.52conspiracies 0.51conspiracy 0.51oh-so 0.50conspirators 0.48subplots 0.47

terrible

ModelTop-1Top-2 to 5Top-6 to 10
A (SST)fitting 0.70synergistic 0.67stultifyingly 0.67reconstruction 0.66bloodwork 0.66besotted 0.59impulse 0.59propelled 0.58freaky 0.58regard 0.57
A (text8)horrible 0.55terrifying 0.51thulsa 0.51irreparable 0.50toils 0.50betrayals 0.49hitlerite 0.49frailty 0.48devastations 0.48calamity 0.48
B (SST)thrilling 0.40chump 0.36ballistic 0.34motion 0.34paint-by-numbers 0.33view 0.33derrida 0.33ecks 0.33cinderella 0.33raises 0.33
B (text8)disastrous 0.35devastating 0.34horrible 0.30tragic 0.29severe 0.29unfortunate 0.29appalling 0.29danger 0.28bad 0.28lendl 0.28
C (SST)+ 0.85besotted 0.84allegedly 0.83unfulfilling 0.83dungeons 0.83phenomenal 0.82lanes 0.82retread 0.82inspired 0.81snl 0.81
C (text8)horrible 0.72horrific 0.71terrifying 0.70irreparable 0.69misfortune 0.69pestilence 0.68calamity 0.68wanton 0.67sorrow 0.67cruel 0.67
Ghorrible 0.92horrendous 0.85dreadful 0.80awful 0.75horrid 0.72atrocious 0.69horrific 0.68bad 0.68appalling 0.68horrible_horrible 0.67
G-fthorrible 0.89dreadful 0.80awful 0.78atrocious 0.72horrid 0.71appalling 0.70horrific 0.68lousy 0.67hideous 0.67clockstoppers 0.67

star

ModelTop-1Top-2 to 5Top-6 to 10
A (SST)raving 0.59trek 0.59high-powered 0.58pop-induced 0.57jake 0.57bestowed 0.57anteing 0.56wattage 0.56lone 0.56indomitability 0.55
A (text8)trek 0.70stars 0.64eridani 0.56spangled 0.55procyon 0.55duetted 0.54ophiuchus 0.54gazer 0.54shatterpoint 0.54teleporters 0.54
B (SST)winners 0.33kevin 0.31splendid 0.30has 0.30acting 0.30pulls 0.30watstein 0.29contributes 0.29dustin 0.28teaming 0.28
B (text8)stars 0.57attractiva 0.36spoor 0.36kzin 0.35planet 0.35galaxies 0.34brightest 0.34bedivere 0.34mckellen 0.34amias 0.33
C (SST)jake 0.88rising 0.87gyllenhaal 0.85charismatic 0.85top-billed 0.82pop-induced 0.77willis 0.74bestowed 0.73high-powered 0.73kevin 0.73
C (text8)stars 0.64trek 0.63centauri 0.52ophiuchus 0.50prequels 0.49brightest 0.49procyon 0.49shatterpoint 0.48eclipsing 0.47gazer 0.47
Gstars 0.78superstar 0.73starlet 0.64megastar 0.62heart_throb 0.57heartthrob 0.54superstars 0.53standout 0.52stardom 0.52hunk 0.51
G-ftsuperstar 0.75stars 0.74gyllenhaal 0.60top-billed 0.60pop-induced 0.58first-rate 0.58standout 0.58starring 0.56huston 0.56legend 0.55

cinematography

ModelTop-1Top-2 to 5Top-6 to 10
A (SST)rinzler 0.71water-born 0.70shimmering 0.70hennings 0.69lisa 0.69l. 0.69conrad 0.68italicized 0.68robotically 0.68pitted 0.67
A (text8)expressionistic 0.63bafta 0.60shazam 0.58screenwriting 0.57cine 0.57cinematographer 0.57filmmaking 0.57elmes 0.57rashomon 0.57desaturated 0.56
B (SST)color 0.38gantz 0.38beaches 0.37mattei 0.37hotels 0.36improbability 0.36acting 0.35lumpen 0.35procedural 0.35reggio 0.34
B (text8)film 0.41horror 0.37photography 0.36oscar 0.36films 0.36director 0.35picture 0.34projector 0.33vangelis 0.33smeeton 0.33
C (SST)hennings 0.90water-born 0.88dubbed 0.88naturalistic 0.87murky 0.87shimmering 0.86phenomenal 0.86robotically 0.86atrocious 0.84acute 0.84
C (text8)bafta 0.74cine 0.73versatility 0.73elmes 0.73directorial 0.73vangelis 0.73filmmaking 0.72expressionistic 0.71wuxia 0.70filmfare 0.70
Gcamerawork 0.77cinematographer 0.75Cinematography 0.70Cinematographer 0.67cinematography_editing 0.63cinematographers 0.63Best_Cinematography 0.62visual_effects 0.60filmmaking 0.60visuals 0.59
G-ftcamerawork 0.76cinematographer 0.75visuals 0.61screenplay 0.59photography 0.59choreography 0.58filmmaking 0.56helmer 0.55lensing 0.54narration 0.54

the

ModelTop-1Top-2 to 5Top-6 to 10
A (SST)of 0.40deleting 0.36outward 0.35tully 0.351987 0.35unambitious 0.35screenplays 0.35hubristic 0.35homeric 0.35indulges 0.35
A (text8)of 0.73and 0.71in 0.70a 0.69to 0.60s 0.58one 0.56platforming 0.56which 0.56thq 0.55
B (SST)our 0.22his 0.21current 0.19dong 0.16whose 0.15german 0.15of 0.15my 0.15in 0.14art 0.14
B (text8)a 0.44its 0.42this 0.37another 0.35their 0.31s 0.29an 0.29our 0.28his 0.28lindesfarne 0.26
C (SST)inside-show-biz 0.94producing 0.93pierce 0.93silent 0.931790 0.93savaged 0.93democratic 0.93shoulder 0.92worlds 0.92indulges 0.92
C (text8)nonparticipation 0.63xviith 0.63maastrichtian 0.62kingdomtide 0.62jedda 0.62jurua 0.62latitudinal 0.62of 0.62centromedian 0.61unpredicted 0.61
Gthis 0.59in 0.54that 0.53ofthe 0.52another 0.47however 0.47one 0.47entire 0.46its 0.46which 0.46
G-ftof 0.63. 0.592002 0.58spielberg 0.54entire 0.54salton 0.53self-conscious 0.53pacino 0.53revenge-of-the-nerds 0.52auteuil 0.52
G and G-ft behave differently from A, B and C trained on SST. G returns clear synonyms and inflections. G-ft preserves them while adding film-specific associations: visuals, screenplay and photography enter cinematography, and storyline enters plot. The three SST-trained models return almost noise for every query. Their 633,720-token corpus does not contain enough co-occurrence evidence to organize the words reliably. The similarity benchmarks measure that gap directly.
The text8 rows are A, B and C retrained on Wikipedia. With 17,005,207 tokens, their neighbours become sensible: terrible gives horrible for all three, and B's plot gives storylines and storyline. Some neighbours now come from encyclopedia topics rather than film reviews, such as trek for star. More training text compares the two corpora in full.
Compositionality

Vector arithmetic and analogies

king - man + woman = queen is the canonical test of whether an embedding space supports this kind of arithmetic. Below it are four queries built from film-review vocabulary. Every word in those four occurs in SST, so the SST-trained models cannot miss them for lack of vocabulary. The standard Google analogy benchmark follows.

king - man + woman expected queen

ModelTop answerRanks 2 to 4
A (SST)scorpion 0.54lion 0.50lear 0.50nuts 0.49
A (text8)queen 0.48infanta 0.43hainault 0.43forkbeard 0.43
B (SST)sugar 0.39abbott 0.36ernest 0.36wildcard 0.35
B (text8)queen 0.44kings 0.39jehu 0.37monarch 0.34
C (SST)middle-aged 0.71lion 0.71creek 0.70haynes 0.70
C (text8)queen 0.51kings 0.42consort 0.42isabella 0.41
Gqueen 0.71monarch 0.62princess 0.59crown_prince 0.55
G-ftqueen 0.70princess 0.60prince 0.57queens 0.56

actor - man + woman expected actress

ModelTop answerRanks 2 to 4
A (SST)davies 0.47macdowell 0.46michel 0.46stoop 0.46
A (text8)actress 0.70comedienne 0.54singer 0.54gingold 0.50
B (SST)enigma 0.32actress 0.32unhinged 0.31role 0.30
B (text8)actress 0.69novelist 0.46musician 0.43writer 0.43
C (SST)succumb 0.65academy 0.62madonna 0.61appearing 0.59
C (text8)actress 0.71comedienne 0.55dancer 0.54socialite 0.53
Gactress 0.86actresses 0.66thesp 0.63Actress 0.62
G-ftactress 0.82actresses 0.69singer 0.55actors 0.55

better - good + bad expected worse

ModelTop answerRanks 2 to 4
A (SST)concert 0.37antitrust 0.36einstein 0.36hungry-man 0.36
A (text8)worse 0.48smarter 0.42mattered 0.40anemic 0.40
B (SST)i.q. 0.32franchise 0.29further 0.29funnier 0.28
B (text8)more 0.41less 0.39fewer 0.38tougher 0.37
C (SST)enacted 0.71badder 0.69videotape 0.69runner 0.69
C (text8)worse 0.54happier 0.46slower 0.46smarter 0.45
Gworse 0.73uglier 0.57sooner 0.53dumber 0.52
G-ftworse 0.66uglier 0.55differently 0.53scarier 0.51

worst - bad + good expected best

ModelTop answerRanks 2 to 4
A (SST)moronic 0.36drowns 0.36resources 0.36bloated 0.35
A (text8)best 0.45razzie 0.42evar 0.41saddest 0.40
B (SST)parents 0.31teen-driven 0.28made 0.28shining 0.28
B (text8)greatest 0.33best 0.29toughest 0.27last 0.26
C (SST)moronic 0.69screenplays 0.69simmer 0.69sporting 0.66
C (text8)best 0.45greatest 0.44razzie 0.44evar 0.41
Gbest 0.69strongest 0.63greatest 0.57weakest 0.57
G-ftbest 0.58strongest 0.57finest 0.56highest 0.56

comedy - funny + scary expected horror

ModelTop answerRanks 2 to 4
A (SST)school-age 0.42zippy 0.42quadrangle 0.41dumb 0.41
A (text8)docu 0.51sitcoms 0.51screwball 0.51slapstick 0.51
B (SST)flick 0.36opens 0.28drama 0.28innovations 0.27
B (text8)screwball 0.33comedies 0.32scene 0.31mgm 0.31
C (SST)gross-out 0.76psychotic 0.76pandering 0.75aspires 0.74
C (text8)screwball 0.63drama 0.63comedies 0.62stardom 0.61
Ghorror_flick 0.55horror 0.51horror_thriller 0.51terrifying 0.51
G-ftgross-out 0.53frightening 0.50scarier 0.50dramas 0.50
Every model trained on enough text solves the canonical analogy. G, G-ft and the three text8 models all rank queen first for king - man + woman. Trained on SST alone, A, B and C return unrelated words.
Film vocabulary does not rescue the SST-trained models. A, B and C trained on SST miss the film queries too. The one near hit is B (SST), which ranks actress second for actor - man + woman. G and G-ft put the expected word first for the actor, better and worst queries. The text8 models do better but not uniformly: all three rank actress first, A (text8) and C (text8) also solve the better and worst queries, and B (text8) solves neither. For comedy - funny + scary, G returns horror_flick and horror, and G-ft returns gross-out and frightening. The SST-only models fail even on their own vocabulary.

Why A, B and C fail

An analogy works when the offset between two words, such as man to woman, points the same way for other pairs, such as king to queen and actor to actress. A model learns a shared direction like that only after it sees each word in many different contexts. In the 6,920 training sentences, the expected answers appear between 11 and 131 times, and even the most common query word appears only 214 times. The table gives each count next to the count in the phrase rows the models were trained on.

QueryOccurrences: sentences / phrase rows
king - man + womanking 13 / 50man 65 / 313woman 29 / 153queen 11 / 43
actor - man + womanactor 26 / 163man 65 / 313woman 29 / 153actress 16 / 89
better - good + badbetter 108 / 532good 214 / 1341bad 149 / 907worse 17 / 122
worst - bad + goodworst 40 / 271bad 149 / 907good 214 / 1341best 131 / 807
comedy - funny + scarycomedy 212 / 1053funny 181 / 1105scary 19 / 97horror 36 / 187

The phrase counts overstate what the models learned from. Most phrase rows are fragments of those same sentences (see the data section), so queen appears 43 times but in only 11 places in the original text. GoogleNews was trained on about 100 billion tokens, more than 150,000 times the 633,720 tokens here. The similarity benchmarks agree: on WordSim-353, A scores +0.029, B -0.036 and C +0.005, against G's +0.700.

Retraining A, B and C on text8 confirms the explanation. With 17,005,207 tokens and the same hyperparameters, all three answer queen, and their analogy accuracy rises to 0.372, 0.314 and 0.353. See More training text.

Sentiment classification holds up anyway. A reaches 0.761 on SST-2 against G's 0.798. A classifier over averaged vectors only needs positive and negative words to separate roughly. That is a much coarser property than consistent analogy offsets, so the two evaluations measure different things.

ModelAnalogy accuracyQuestions attemptedCoverageCandidate pool
A (SST)0.00063,563 of 19,54418.2%top 50,000
A (text8)0.372115,790 of 19,54480.8%top 50,000
B (SST)0.00203,563 of 19,54418.2%top 50,000
B (text8)0.314215,790 of 19,54480.8%top 50,000
C (SST)0.00223,563 of 19,54418.2%top 50,000
C (text8)0.353515,790 of 19,54480.8%top 50,000
G0.756115,434 of 19,54479.0%top 50,000
G-ft0.67503,563 of 19,54418.2%top 50,000

Accuracy needs to be read alongside coverage. gensim drops any question with an out-of-vocabulary word, so the SST-trained models are scored on only 18.2% of the set against G's 79.0%. G-ft's 0.675 comes from structure inherited through the warm start, not learned from SST.

Qualitative check

Do similar words cluster together?

A model stores each word as 300 numbers, which can't be drawn directly. PCA and t-SNE are two standard ways to squeeze them down to two, so every word becomes a dot on a page. PCA keeps the two directions along which the words vary most. t-SNE tries to keep each word's nearest neighbours close, at the cost of distorting larger distances. If a model has learned what words mean, related words should land near each other.

To test that, I picked 32 words before looking at any plot, eight from each of four groups: positive words (good, charming), negative words (dull, tedious), filmmaking terms (screenplay, editing) and genres (horror, western). Each dot is coloured by its group.

Clusters are easy to imagine in a scatter plot, so each one also gets a silhouette score. It compares how close a word sits to its own group against how close it sits to the nearest other group, and runs from -1 to 1. Near 0 means the groups are mixed together; higher means they form separate clumps. The plots below show G-ft, the clearest case, and A trained on each corpus.

goodgreatwonderfulbrilliantcharmingbeautifulfunnydelightfulbadterribleawfuldullboringstupidtediousblandcinematographyscreenplaydirectioneditingperformancesscriptactingsoundtrackcomedydramathrillerdocumentaryhorrorromancewesternmusicalpositive sentimentnegative sentimentfilm craftgenre

PCA, Model G-ft. The two axes keep 16.2% and 9.6% of the variance. Silhouette score +0.164.

goodgreatwonderfulbrilliantcharmingbeautifulfunnydelightfulbadterribleawfuldullboringstupidtediousblandcinematographyscreenplaydirectioneditingperformancesscriptactingsoundtrackcomedydramathrillerdocumentaryhorrorromancewesternmusicalpositive sentimentnegative sentimentfilm craftgenre

t-SNE, Model G-ft. Perplexity 10. Silhouette score +0.187.

goodgreatwonderfulbrilliantcharmingbeautifulfunnydelightfulbadterribleawfuldullboringstupidtediousblandcinematographyscreenplaydirectioneditingperformancesscriptactingsoundtrackcomedydramathrillerdocumentaryhorrorromancewesternmusicalpositive sentimentnegative sentimentfilm craftgenre

PCA, Model A (SST). The two axes keep 8.6% and 7.5% of the variance. Silhouette score +0.036.

goodgreatwonderfulbrilliantcharmingbeautifulfunnydelightfulbadterribleawfuldullboringstupidtediousblandcinematographyscreenplaydirectioneditingperformancesscriptactingsoundtrackcomedydramathrillerdocumentaryhorrorromancewesternmusicalpositive sentimentnegative sentimentfilm craftgenre

t-SNE, Model A (SST). Perplexity 10. Silhouette score +0.051.

goodgreatwonderfulbrilliantcharmingbeautifulfunnydelightfulbadterribleawfuldullboringstupidtediousblandcinematographyscreenplaydirectioneditingperformancesscriptactingsoundtrackcomedydramathrillerdocumentaryhorrorromancewesternmusicalpositive sentimentnegative sentimentfilm craftgenre

PCA, Model A (text8). The two axes keep 11.7% and 7.8% of the variance. Silhouette score +0.066.

goodgreatwonderfulbrilliantcharmingbeautifulfunnydelightfulbadterribleawfuldullboringstupidtediousblandcinematographyscreenplaydirectioneditingperformancesscriptactingsoundtrackcomedydramathrillerdocumentaryhorrorromancewesternmusicalpositive sentimentnegative sentimentfilm craftgenre

t-SNE, Model A (text8). Perplexity 10. Silhouette score +0.063.

A flat picture throws most of the information away. The first two PCA axes keep between 16% and 37% of the variance, depending on the model. So the last two score columns skip the projection and measure the same thing on the full 300-dimensional vectors, using cosine distance. The last of those asks only whether positive and negative words form separate groups.

Model2-D, PCA2-D, t-SNE300-D, all groups300-D, positive vs negative
A (SST)+0.036+0.051+0.063+0.076
A (text8)+0.066+0.063+0.054+0.035
B (SST)+0.035-0.033+0.075+0.059
B (text8)+0.072-0.107+0.039+0.004
C (SST)-0.004+0.110+0.142+0.126
C (text8)+0.050+0.063+0.060+0.035
G+0.120+0.121+0.141+0.211
G-ft+0.164+0.187+0.147+0.195
The GoogleNews models separate the groups best. Under PCA, G-ft and G score +0.164 and +0.120, while A, B and C trained on SST score between -0.004 and +0.036. t-SNE is less stable: the same model can score quite differently under the two methods, as B (text8) does (+0.072 against -0.107).
text8 teaches similarity, not sentiment. The text8 models score close to G on WordSim-353 (0.694 for A against 0.700 for G), yet they separate positive from negative words worse than any other model, at +0.004 to +0.035, against +0.076 for A trained on SST and +0.211 for G. Wikipedia uses good and bad in the same kinds of sentence, so the vectors for praise and criticism end up close together. That fits the drop in sentiment accuracy described under More training text.
Where the sentiment score misleads

Good at sentiment, useless at similarity

This analysis is included because sentiment accuracy alone makes the embeddings look more capable than they are.

The three benchmarks compare cosine similarity with human ratings using Spearman correlation. Coverage is the fraction of pairs where both words are in the vocabulary. It is part of the result: the SST-trained models cover only about half of WordSim-353 because many benchmark words never occur in the movie reviews.

ModelWordSim-353SimLex-999MEN-3kCoverageSST-2 accuracy
A (SST)+0.029+0.045+0.11850.1%0.761
A (text8)+0.694+0.341+0.64594.3%0.722
B (SST)-0.036+0.069-0.01050.1%0.729
B (text8)+0.666+0.259+0.60694.3%0.724
C (SST)+0.005+0.033+0.09650.1%0.744
C (text8)+0.644+0.332+0.56794.3%0.715
G+0.700+0.442+0.771100.0%0.798
G-ft+0.585+0.405+0.70250.1%0.792
Trained on SST, A, B and C show no measurable lexical similarity structure. B is slightly negative on two benchmarks, which is effectively no correlation. Yet those same three models score 0.729 to 0.761 on sentiment, within 0.069 of GoogleNews. Mean-pooled logistic regression needs only that sentiment-bearing words point in consistent directions; it does not need tiger to sit near cat. Downstream accuracy by itself would make these embeddings look nearly as good as GoogleNews, which the intrinsic tests clearly contradict. The text8 rows are the same three models trained on more text, and they score close to GoogleNews; More training text covers them.

This also clarifies what the warm start contributes. G-ft scores 0.585 on WordSim-353 against 0.029 for A, trained from scratch on the identical corpus with identical settings. The pretrained initialization does more than add 0.031 accuracy. It supplies all of Model G-ft's measurable lexical structure.

More training text

More text fixes similarity, not sentiment

The sections above blame the size of the training data for the failures of A, B and C on similarity and analogies. To test that directly, I retrained all three on text8, the first 100 MB of cleaned English Wikipedia (mattmahoney.net/dc/text8.zip). It has 17,005,207 tokens, 27 times the SST phrase corpus. Every hyperparameter matches the SST runs. One implementation detail differs: C's text8 run uses sparse Adam, which updates only the rows a batch touches, because dense Adam is too slow at this vocabulary size (see Cost to run). C (text8) also trained on a PACE ICE A100 GPU, so its training time is not comparable with the laptop times. The sentiment test is also unchanged: the classifier is still fit on the SST phrase labels and scored on the same SST-2 test sentences, so only the word vectors differ.

ModelTokensWordSimAnalogiesking − man + womanSST-2 acc.OOVTrain time
A (SST)634k+0.0290.001scorpion0.7616.6%11.8 s
A (text8)17M+0.6940.372queen0.72216.9%889.3 s
B (SST)634k-0.0360.002sugar0.7296.6%3.6 s
B (text8)17M+0.6660.314queen0.72416.9%176.6 s
C (SST)634k+0.0050.002middle-aged0.7446.6%160.2 s
C (text8)17M+0.6440.353queen0.71516.9%658.7 s (A100)
G100B+0.7000.756queen0.79827.1%-
More text fixes the structure. WordSim-353 rises from +0.029 to +0.694 for A, from -0.036 to +0.666 for B and from +0.005 to +0.644 for C, close to GoogleNews at +0.700. Analogy accuracy goes from almost zero to 0.372, 0.314 and 0.353, and all three now answer queen for king - man + woman. The analogy tables and neighbour lists include the text8 models as extra rows.
Sentiment accuracy does not improve. A falls from 0.761 to 0.722, B moves from 0.729 to 0.724, and C falls from 0.744 to 0.715. The text8 vocabulary misses 16.9% of the SST-2 test tokens, against 6.6% for the SST models. 64% of the missing tokens are punctuation, which carries no sentiment. The rest matters more. text8 has no apostrophes, so n't is missing, and it occurs in 129 of the 1,821 test sentences. Review vocabulary is missing too: refreshingly, soggy, mesmerizing, sappy, clunker, heartwarming are all in the SST vocabulary but not in text8's, and several of them are strong sentiment words. Wikipedia teaches general word meaning. SST teaches the words reviewers use to praise and criticize, and the sentiment test rewards that.

Testing the handout's claims

Page 1 of the handout makes two claims about the architectures that the two corpora can test. CBOW trains several times faster. On text8, B took 177 seconds against A's 889, a 5.0× difference (963,171 against 191,213 words per second). The claim holds. Skip-gram does better on small data, and CBOW needs large data. On SST, skip-gram leads CBOW by 0.015 in sentiment accuracy averaged over three seeds, and neither has any similarity structure. On text8, the accuracy gap disappears (+0.002 for B), while A still leads on WordSim-353 by 0.028 and on analogies by 0.058. CBOW closes the gap as the data grows, which fits the claim, but skip-gram stays ahead on the finer-grained tests. The text8 figures are single runs. On SST, the hyperparameter sweep puts the seed-to-seed standard deviation of accuracy at 0.002 to 0.007, so the text8 accuracy gap is within noise. The WordSim-353 and analogy gaps are far larger than that.

Error analysis

Why every model struggles with the same sentences

ModelErrors / 1,821NegationContrastOther
G36810264202
G-ft3799664219
A (SST)43510172262
C (SST)46611479273
B (SST)49312062311
Negation and contrast account for 37% to 45% of each main model's errors, against 32% of the test set. They are over-represented for every model, and most of all for GoogleNews (45%), so better embeddings do not solve it. Mean pooling removes word order, including the information needed to determine the scope of a negation.

Model G-ft, confidently right and confidently wrong

Two test examples Model G-ft, the fine-tuned one, gets right and two it gets wrong. All four come from the same 1,821-sentence test split. Within each outcome, these are the highest-confidence cases.

OutcomeGoldPredictedConfidenceSentence
correctpositivepositive0.999an intelligent , moving and invigorating film .
correctpositivepositive0.998a smart , sweet and playful romantic comedy .
incorrectnegativepositive0.952it 's a great deal of sizzle and very little steak .
incorrectnegativepositive0.938a well acted and well intentioned snoozer .

Confident failures across models

ModelSentenceGoldPredictedConfidenceCategory
A (SST)not a bad journey at all .posneg0.958negation
A (SST)an ultra-low-budget indie debut that smacks more of good intentions than talent .negpos0.917other
A (SST)a well acted and well intentioned snoozer .negpos0.898other
G-ftnever -lrb- sinks -rrb- into exploitation .posneg0.990negation
G-ftthere is n't a weak or careless performance amongst them .posneg0.973negation
G-ftdespite the long running time , the pace never feels slack -- there 's no scene …posneg0.955negation

The failures follow directly from the representation. not a bad journey at all averages not and bad into negative territory. there is n't a weak or careless performance amongst them pools two strongly negative adjectives without preserving the negation that reverses them. In a great deal of sizzle and very little steak, the negative meaning comes from the contrast between clauses, which averaging also removes.

Mechanism

How the two architectures work

A Word2Vec model comes down to two matrices and a dot product. It has no hidden layer or nonlinearity. W_in stores one row per word and becomes the final embedding table. W_out is used for the training objective, then discarded.

one-hotV = 14,309W_incoded = 300W_outcontext -5...context -1context +1... context +510 negativesper positivesampled fromunigram^0.75encoder output, keptno nonlinearity

A and C use skip-gram with negative sampling. A centre word predicts each surrounding word independently. Each observed pair is scored against 10 negative samples from the unigram distribution raised to 0.75, avoiding a softmax over all 14,309 words.

context -5...context -1context +1... context +5meanof C rowsW_in, sharedcoded = 300W_outHuffman treelog2(V) ~ 14 nodeshierarchical softmaxencoder output, kept

B uses CBOW with hierarchical softmax. The context rows are averaged into one vector, which predicts the centre word. A binary tree over the vocabulary reduces one prediction from V decisions to log2(V).

The encoder, code, and decoder analogy only goes so far. The terms come from autoencoders, which reconstruct their own input. Word2Vec predicts a neighbouring word and throws away its output matrix after training. In the mapping above, the one-hot word is the input, W_in is the encoder, its 300-dimensional row is the code, W_out is the decoder, and the predicted context word is the output.
Corpus

Training and test data

The sentiment data comes from SST-2, but its two releases cannot be swapped freely. GLUE replaces every test label with -1 because the split is reserved for its leaderboard. I use the labelled SetFit test split instead. src/data.py raises an error unless both classes appear in the loaded test labels.

SplitSourceRowsTokensMean lengthPositiveUsed for
trainstanfordnlp/sst2 (phrase level)67,348633,7209.455.8%embedding training and classifier fitting
devSetFit/sst287217,04619.550.9%held out, unused
testSetFit/sst21,82135,02319.249.9%every number on this page
14,309 vocabulary at min_count=2, phrase-level train
7,140 vocabulary at min_count=2, sentence-level train
6.6% test-token OOV rate, SST-trained vocabulary
27.1% test-token OOV rate, GoogleNews-300 vocabulary

What the rows look like

Five training examples and four test examples, two positive and two negative. Each example was selected by a stated rule rather than picked by eye.

Five training examples

Selected forTextGold label
positive, plainthat loves its characters and communicates something rather beautiful about human naturepositive
negative, plainremains utterly satisfied to remain the same throughoutnegative
negationfor those moviegoers who complain that ` they do n't make movies like they used to …negative
contrastive clausethe plot is nothing but boilerplate clichés from start to finish ,negative
film-domain vocabulary, good action , good acting , good dialogue , good pace , good cinematography .positive

Four test examples

Selected forTextGold label
positivethis is one of polanski 's best films .positive
positivefinally , a genre movie that delivers -- in a couple of genres , no less .positive
negativearnold 's jump from little screen to big will leave frowns on more than a few faces .negative
negativeno movement , no yuks , not much of anything .negative
The leakage check found one duplicate. The phrase rows are labelled treebank subtrees taken only from the training split. An exact string check still found the fragment what 's next ? in both training and test data. I removed it. There was no overlap with dev, and the training count fell from 67,349 to 67,348 rows.
Most of the phrase-level text repeats the sentences. 97.5% of the phrase rows (65,640 of 67,348) appear word for word inside the 6,920 training sentences, because each row is a subtree of one of them. The phrase corpus has 4.75× the tokens of the sentence release, but most of those tokens are the same text cut into overlapping pieces. The repetition also doubles the vocabulary. At min_count=2 it grows from 7,140 to 14,309 words, and 7,132 of the 7,184 added words occur only once in the sentences. They pass the cutoff because the fragments of that one sentence repeat them. Vector arithmetic shows what this does to the embeddings.

Second training corpus: text8

A, B and C were also trained on text8, the first 100 MB of an English Wikipedia dump cleaned by Matt Mahoney (mattmahoney.net/dc/text8.zip). It has 17,005,207 tokens and a vocabulary of 135,335 words at min_count=2. The text contains only lowercase letters and spaces. Punctuation is removed and numbers are spelled out digit by digit, so 1989 becomes one nine eight nine. text8 is used only to train word vectors; the classifier and every test stay on SST. The file is downloaded by src/run_text8.py rather than stored in the repository.

The pretrained baseline is GoogleNews-300, a skip-gram model with negative sampling trained on roughly 100 billion news tokens. It comes from code.google.com/archive/p/word2vec, is loaded through gensim-data, and is capped at the 500,000 most frequent words. I did not use GloVe as the pretrained baseline because it factorizes a co-occurrence matrix rather than training Word2Vec. That would change the question being tested.

The pretrained model

FieldValue
ModelGoogleNews-vectors-negative300
ReferenceMikolov, Sutskever, Chen, Corrado and Dean (2013), Distributed Representations of Words and Phrases and their Compositionality, NIPS. arxiv.org/abs/1310.4546
Distribution URLcode.google.com/archive/p/word2vec, fetched through gensim-data as word2vec-google-news-300
Training corpusGoogle News, roughly 100 billion tokens. The corpus was not released, so there is no public corpus link.
ArchitectureSkip-gram with negative sampling, 300 dimensions, 3M words and phrases
Test set used hereGoogle analogy set (questions-words.txt), 19,544 questions, shipped in data/eval/
Accuracy, measured here0.7561 across 15,434 attempted questions (79.0% coverage, with the candidate pool limited to the top 50,000 words)
Accuracy, as publishedNot quoted. The Google Code archive does not expose figures in a form that can be fetched, and its linked papers report results for the models studied in those papers, not this released file. I report the measured result above with its exact conditions instead of attaching an unverified published number to the model.
Configuration

Hyperparameters

A and B intentionally change two things together: the architecture and the loss. All other settings stay fixed, so the comparison is between the two standard Word2Vec configurations. The sweep below separates the two changes.

A (SST)B (SST)C (SST)GG-ftA2
Architecture (sg)101111
Hierarchical softmax010000
Negatives1001051010
Dimensions300300300300300300
Window555555
Min count222522
Epochs101010not reported88
Initial LR0.0250.0250.0010.0250.0050.005
Subsample threshold0.0010.0010.0011e-050.0010.001

Sweep

Each setting below was trained 3 times on the SST phrase corpus, with seeds 42, 43, 44. The tables give the mean and standard deviation across seeds. One factor changes at a time, and everything else stays at A's or B's settings. Training time is the mean wall clock on the M2 Air.

Architecture and loss

SettingSST-2 accuracyWordSim-353MEN-3kTraining time
skip-gram + NS0.752 ± 0.006+0.008 ± 0.018+0.122 ± 0.00815.2 ± 0.9 s
skip-gram + HS0.752 ± 0.006+0.043 ± 0.014+0.042 ± 0.00613.7 ± 0.5 s
CBOW + NS0.735 ± 0.005-0.005 ± 0.019+0.128 ± 0.0045.8 ± 0.1 s
CBOW + HS0.737 ± 0.005-0.002 ± 0.018-0.000 ± 0.0046.3 ± 0.6 s

Context window

SettingSST-2 accuracyWordSim-353MEN-3kTraining time
A, window 20.738 ± 0.002+0.028 ± 0.008+0.130 ± 0.00211.2 ± 0.4 s
A, window 50.752 ± 0.006+0.008 ± 0.018+0.122 ± 0.00815.2 ± 0.9 s
A, window 100.755 ± 0.005+0.029 ± 0.020+0.095 ± 0.00226.1 ± 1.7 s
B, window 20.715 ± 0.002-0.006 ± 0.013+0.010 ± 0.0104.8 ± 0.1 s
B, window 50.737 ± 0.005-0.002 ± 0.018-0.000 ± 0.0046.3 ± 0.6 s
B, window 100.741 ± 0.007-0.014 ± 0.010+0.005 ± 0.0087.1 ± 1.4 s

Vector dimensions

SettingSST-2 accuracyWordSim-353MEN-3kTraining time
A, 50 dims0.746 ± 0.006+0.007 ± 0.006+0.104 ± 0.0096.6 ± 0.3 s
A, 100 dims0.751 ± 0.007+0.018 ± 0.019+0.106 ± 0.0098.8 ± 0.2 s
A, 300 dims0.752 ± 0.006+0.008 ± 0.018+0.122 ± 0.00815.2 ± 0.9 s
B, 50 dims0.681 ± 0.004-0.013 ± 0.012+0.012 ± 0.0072.8 ± 0.3 s
B, 100 dims0.689 ± 0.003+0.020 ± 0.026+0.005 ± 0.0033.2 ± 0.1 s
B, 300 dims0.737 ± 0.005-0.002 ± 0.018-0.000 ± 0.0046.3 ± 0.6 s

Negative samples

SettingSST-2 accuracyWordSim-353MEN-3kTraining time
A, 5 negatives0.761 ± 0.004+0.008 ± 0.022+0.121 ± 0.00418.5 ± 4.7 s
A, 10 negatives0.752 ± 0.006+0.008 ± 0.018+0.122 ± 0.00815.2 ± 0.9 s
A, 20 negatives0.760 ± 0.004+0.018 ± 0.003+0.125 ± 0.00938.4 ± 6.7 s
Architecture matters and the loss does not. Skip-gram averages 0.752 with negative sampling and 0.752 with hierarchical softmax. CBOW averages 0.735 and 0.737. Changing the loss moves accuracy by at most 0.002, while changing the architecture moves it by about 0.015. The gap between A and B comes from skip-gram against CBOW.
Single runs overstate the A-B gap. The headline reports A at 0.761 and B at 0.729, a gap of 0.032. Over three seeds the same settings average 0.752 and 0.737, a gap of 0.015. Each headline figure is one run, and A's landed above its mean while B's landed below.

Wider windows help both models. Going from a window of 2 to 10 raises A from 0.738 to 0.755 and B from 0.715 to 0.741. For skip-gram it also more than doubles training time, from 11.2 to 26.1 seconds, because every extra context word is another prediction. CBOW needs more dimensions than skip-gram. B gains 0.056 going from 50 to 300 dimensions, while A gains only 0.006. The number of negatives shows no clear trend. 5, 10 and 20 negatives give 0.761, 0.752 and 0.760. The middle setting is the lowest, which fits no trend, and with three seeds a gap that small cannot be separated from seed noise. 20 negatives take 38 seconds.

No setting produces similarity structure. WordSim-353 stays between -0.014 and +0.043 across all 14 distinct settings. On SST, the hyperparameters shift sentiment accuracy by a few points, but none of them fixes similarity. Only more data did, as More training text shows.

Training times drift as the fanless M2 Air warms up. The 10-negative settings were the first runs of the sweep, on a cool machine, and came out faster than the 5-negative runs even though they do more work. Small time differences between settings are therefore not reliable.

Model G-ft

What fine-tuning means for Word2Vec

Fine-tuning Word2Vec is much simpler than fine-tuning a transformer. There are no frozen layers or adapters. I initialize W_in with published vectors, then continue ordinary skip-gram training on movie reviews at a low learning rate.

12,032 rows seeded from GoogleNews
2,277 rows Google's vocabulary lacks
0.961 mean cosine before and after training
+0.031 accuracy over training from scratch

Moved most

kidman0.553
philippe0.599
schwarzenegger0.608
holm0.647
griffiths0.663
sheridan0.669
evelyn0.677
clooney0.700

Did not move at all

oozes1.000
priggish1.000
plod1.000
worshipful1.000
drudgery1.000
abysmally1.000
poo1.000
execrable1.000
The drift ranking mostly tracks corpus frequency. The words that move most are actor surnames, which are common in reviews but rare in news. At the other end, rare sentiment adjectives remain at cosine 1.000 because one or two SST appearances do not produce enough gradient to move them. The full ranking gives a clearer account of fine-tuning than one hand-picked word would.
  1. Build the vocabulary from our corpus first intersect_word2vec_format only overwrites rows that already exist. It does not import Google's three million words, so G-ft keeps the vocabulary built from SST.
  2. Set vectors_lockf to ones explicitly gensim 4.x does not create it. Without it the import either fails or silently freezes every row, leaving the imported vectors unchanged.
  3. Keep the learning rate low: alpha 0.005, 8 epochs Google never published their output matrix, so W_out starts random. Early updates must learn a new decoder as well as adjust the imported vectors. A standard learning rate distorts those vectors during the first epoch.
  4. Measure drift rather than assuming it Cosine between each word's warm-start vector and its final vector. The mean is 0.9609 and the median is 0.9731, confirming that the low learning rate preserved most of the pretrained structure.
Model C

Writing skip-gram with negative sampling

Writing the training loop makes each part of Word2Vec concrete. src/sgns.py implements the vocabulary and counts, subsampling of frequent words, the unigram^0.75 noise table, a dynamic window, the two embedding matrices, and linear learning-rate decay. to_keyedvectors wraps W_in in a gensim object, which lets C use the same evaluation code as every other model.

A, gensimC, from scratch
SST-2 accuracy0.76110.7441
Wall clock11.8 s160.2 s
Words per second537,88739,571
Loss, first to last epochnot exposed4.2288 to 2.8681
The first version did not learn. Its loss stayed at exactly 1.3863 for all ten epochs, and accuracy was 0.4992, or chance on a balanced split. A loss of 1.3863 is 2·ln 2, what we get when every logit is zero. The problem was the loss reduction, not the sampler. Calling BCEWithLogitsLoss once on the positives and once on the negatives averages over B entries and over B·k entries, so at a batch of 8,192 the effective per-pair learning rate was about 3e-6. The measured gradient norm on W_in was exactly 0.0. I fixed it by writing Mikolov's equation 4 directly, summing across the k negatives, and averaging only across the batch.

Two component checks made the failure quick to locate. Subsampling retains 17.6% of occurrences of the. The noise table maps the from a raw frequency of 4.30% down to a sampled 1.31% while lifting execrable from effectively zero to 2e-5. Both results match the expected effect of the 0.75 exponent.

Systems measurements

What these models cost to run

Query latency measures brute-force most_similar, which runs a dense matrix-vector product over the full vocabulary. The mean, p50 and p95 per query all come from the same 200 calls, cycling through the five query words after one untimed warm-up call each. Memory is the RSS increase in an isolated subprocess, compared with the size of the matrices the model retains.

ModelVocabMean msp50 msp95 msKeepsPredicted MBMeasured MBRatio
A (SST)14,3090.690.382.29234.335.01.02
B (SST)14,3091.270.454.50234.348.01.40
C (SST)14,3090.740.422.74117.217.81.03
G500,00029.9019.2372.321600.0195.60.33
G-ft14,3091.150.494.06234.334.81.01
Brute-force query time grows with vocabulary size. G has 35 times as many words as the SST models and is 51 times slower at p50. An approximate index adds little value at 14,309 words. At 500,000, a query already takes 19 ms, and the full three-million-word Google model would take roughly 115 ms.

The 2 · V · d · 4 prediction holds within 3% for A, G-ft and C. B comes in at 1.40 because hierarchical softmax also keeps Huffman-tree code and point arrays that the formula omits.

G's memory result does not match the prediction. 500,000 by 300 float32 is 600 MB and the measured increase is only 195.6 MB. macOS memory compression is a possible explanation because it can compress inactive anonymous pages, causing ru_maxrss to fall below the allocation. A Linux run should land near 600 MB if that explanation is correct. Until then, the gap remains unresolved.

A separate benchmark determined how C trains. On this vocabulary, MPS is slower than CPU (64.3 against 44.6 ms per batch) and sparse gradients are slower than dense gradients (52.6 against 44.6). The model is small enough for GPU launch and data transfer overhead to dominate. Sparse updates also offer little benefit. A batch of 8,192 pairs with 10 negatives touches roughly 90,000 index slots across 14,309 rows, so almost every row updates on each step.

On text8, both results reverse. The vocabulary grows to 135,335 rows, and a dense Adam step has to update every one of them. A benchmark of a single training step at that size gave 1,300 ms per batch for dense updates on the CPU, against 207 ms for sparse updates on the CPU and 196 ms for dense updates on MPS. C's text8 run therefore uses sparse Adam. On the laptop it measured about 0.15 seconds per step, which puts the full 10-epoch run at roughly four hours, so it moved to a PACE ICE A100 GPU. There it trained in 659 seconds, 258,169 words per second. The GPU changed the speed and not the result: the laptop run, stopped after two epochs, had reached the same loss, 2.8402, at the end of epoch 2.
Reproduction

Installation, running and measurement

Four setup and measurement problems took most of the debugging time. In each case, the visible error pointed away from the actual cause. The symptoms and fixes are recorded here.

  1. gensim has no wheels for Python 3.14 The machine's default interpreter is 3.14. Installing gensim there starts a Cython source build and fails late in the process. The virtual environment therefore uses Homebrew Python 3.11.
  2. import gensim fails on a fresh unpinned install gensim 4.3.x imports scipy.linalg.triu, which scipy 1.13 removed. The ImportError points at gensim even though the incompatible scipy version is the cause. requirements.txt pins scipy<1.13 and numpy<2.
  3. The SST-2 split with hidden labels The GLUE copy sets every test label to -1. An accuracy calculation against those placeholders can still produce a plausible number. src/data.py prevents that by requiring two classes in the test labels.
  4. Whole-process peak RSS is not a model measurement The first memory table compared process peak RSS against the matrix prediction and returned 297.8 MB for a model with 34.3 MB of matrices. Process peak RSS also includes the interpreter, gensim, numpy, torch, and the resident corpus. src/measure_mem.py now measures an RSS delta in an isolated subprocess per model.

After pip install -r requirements.txt, these commands produce the results the page is built from, in order:

  1. python -m src.data corpus statistics
  2. python -m src.main trains A and B on SST and loads G
  3. python -m src.run_e trains C on SST, 160 seconds on CPU
  4. python -m src.finetune trains G-ft from the GoogleNews vectors, then the A2 control with the same schedule and random initialization
  5. python -m src.run_eval, python -m src.measure_mem, python -m src.run_analogies, python -m src.testcases similarity, latency, memory, errors, analogies and the worked test examples
  6. python -m src.examples the example tables for training data and for G-ft's right and wrong calls, read back from the data and from G-ft's mined errors
  7. python -m src.run_text8 downloads text8 and retrains A and B on it, about 18 minutes
  8. python -m src.run_e --text8 trains C on text8. On a GPU cluster, sbatch slurm/c_text8.sbatch runs the same command; it saves a checkpoint after every epoch, so an interrupted job resumes where it stopped.
  9. python -m src.visualize PCA and t-SNE projections and the cluster scores
  10. python -m src.sweep the hyperparameter sweep
  11. python -m src.build_report rebuilds both report variants

src/build_report.py reads every reported figure from results/*.json when it builds the page. Re-running a model and rebuilding therefore updates the prose and tables together. gensim training with four workers is not deterministic, so rerunning a model shifts its scores by roughly the seed spread measured in the sweep.