GloVe & Adam Optimizer (2014) › The Solution: Seeing the Big Picture

Michael BrenndoerferMay 26, 20258 min read
History of Language AIAll chapters
Part 4 of 9Deep Learning Arrives
Chapter 44GloVe & Adam Optimizer (2014) › The Solution: Seeing the Big Picture
Reading section 3 of 7 · The Solution: Seeing the Big Picture · 8 minSection 3 of 7 · 8 min

Book contents

Section 3 of 7 · 8 min read

GloVe & Adam Optimizer (2014) › The Solution: Seeing the Big PictureOne section · Sections in this chapter3 / 7 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

GloVe: Learning from the Entire Corpus at Once

The Stanford team had a deceptively simple idea: instead of learning word meanings indirectly through millions of local predictions, what if you could directly model the global statistics of how words appear together? Their approach, which they called GloVe (Global Vectors for Word Representation), worked by first building a giant table counting every time each pair of words appeared near each other in the entire corpus, then learning word embeddings that captured the patterns in that table.

The key insight concerned ratios: a word's meaning depends on how often other words appear nearby relative to one another. Consider "ice" and "steam" again. Both relate to water, so they might share some contexts. But now look at a third word: "solid." If you count across millions of documents, you'll find that "ice" appears near "solid" quite frequently, while "steam" rarely does. The ratio between these frequencies.say, 100 appearances for ice-solid versus 10 for steam-solid.tells you something fundamental about what makes ice and steam different.

Apply this logic across thousands of words, and you get a signature for each word based on its co-occurrence ratios with every other word. "Ice" has high ratios with "frozen," "solid," and "cold" but low ratios with "vapor," "boil," and "gas." "Steam" shows the opposite pattern. These ratio patterns encode meaning in a way that's more stable and informative than raw co-occurrence counts or local context windows.

Pennington, Socher, and Manning formalized this by first building what they called a co-occurrence matrix.a massive table where entry (i,j) counted how many times word i appeared within a certain window of word j across the entire corpus. For a vocabulary of 50,000 words, this meant a table with 2.5 billion entries. Most were zeros (how often do "aardvark" and "xylophone" appear together?), but the non-zero entries captured exactly which words co-occurred throughout the corpus.

Then came the clever part. Instead of using this giant table directly, GloVe learned compact word vectors whose relationships approximated the relationships in the co-occurrence matrix. Specifically, the model learned vectors such that when you multiply the vector for word i with the vector for word j, you get something close to the logarithm of how often they appeared together.

The mathematical objective looked like this:

J=∑i,j=1Vf(Xij)(wi⋅w~j+bi+b~j−log⁡Xij)2J = \sum_{i,j=1}^{V} f(X_{ij}) (w_i \cdot \tilde{w}_j + b_i + \tilde{b}_j - \log X_{ij})^2

Breaking this down in plain language: for every pair of words in the vocabulary, GloVe tries to make the dot product of their vectors (plus some adjustment terms) equal to the logarithm of how often they appeared together. The f(Xij)f(X_{ij}) part is a weighting function that we'll get to in a moment.

Why use the logarithm? Because word relationships scale multiplicatively, not additively. If "ice" appears with "solid" 100 times and "steam" appears with "solid" 10 times, the meaningful relationship is the 10:1 ratio, not the difference of 90. Taking logarithms converts multiplicative relationships into additive ones, which makes the math work better.

The weighting function f(Xij)f(X_{ij}) solved a critical problem: not all co-occurrence counts are equally informative. Consider the words "the" and "of".they appear together constantly in English text, but this frequency doesn't tell you much about meaning. Meanwhile, rare co-occurrences might happen only once or twice by chance. The really informative co-occurrences are in the middle.word pairs that appear together more often than random chance but aren't just statistical noise from ultra-high-frequency words.

GloVe used a simple weighting function that capped the influence of very frequent co-occurrences:

f(x)={(x/xmax)αif x<xmax1otherwisef(x) = \begin{cases} (x/x_{max})^\alpha & \text{if } x < x_{max} \\ 1 & \text{otherwise} \end{cases}

Typically, xmaxx_{max} was set to 100 and α\alpha to 0.75. This meant that co-occurrences happening fewer than 100 times received weights proportional to their frequency, while anything more frequent was capped at the same weight. This focused learning on the moderate-frequency co-occurrences that best captured semantic relationships.

GloVe differed from word2vec by explicitly using global statistics. Word2vec slid windows across text, creating millions of local training examples. GloVe built the entire co-occurrence matrix first.capturing every co-occurrence in the corpus.then learned embeddings that explained that global structure. For rare words that might appear only a few times, GloVe could still learn meaningful representations because it could see exactly which other words they co-occurred with across the entire corpus.

After training, GloVe produced two sets of vectors for each word: word vectors and context vectors. The final embedding for each word usually combined both by adding them together. The resulting embeddings had similar properties to word2vec.including the remarkable ability to solve analogies through vector arithmetic.but often worked better on tasks where global statistical patterns mattered.

Adam: Adaptive Learning Without the Tuning

Kingma and Ba's solution to the optimization problem was beautifully elegant: give each parameter its own adaptive learning rate that automatically adjusts based on recent history. Instead of manually tuning one learning rate for the entire network, let the optimizer figure out what each parameter needs.

Adam (short for Adaptive Moment Estimation) tracked two pieces of information for every parameter in the network. First, it tracked the average direction the gradient had been pointing recently.this gave it momentum to accelerate in consistent directions. Second, it tracked the typical magnitude of recent gradients.this told it whether a parameter needed large or small updates.

Here's the intuition. Imagine you're adjusting a parameter that has been receiving large, consistent gradient signals pointing in one direction. Adam would build up momentum in that direction (from the first piece of information) and give it relatively smaller updates to avoid overshooting (from the second piece). Now imagine a different parameter that only occasionally receives small gradient signals. Adam would give it relatively larger updates when those rare signals arrive, making the most of the limited information.

The algorithm worked by maintaining two running averages for each parameter. The first moment estimate mtm_t tracked where gradients had been pointing:

mt=β1mt−1+(1−β1)gtm_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t

This is an exponentially weighted moving average, where gtg_t is the current gradient and β1\beta_1 (typically 0.9) controls how much weight to give recent gradients versus the running average. Think of this as building up velocity in the direction gradients consistently point.

The second moment estimate vtv_t tracked the typical magnitude of gradients:

vt=β2vt−1+(1−β2)gt2v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2

Here β2\beta_2 (typically 0.999) controls the averaging, and we square the gradient to capture magnitude regardless of sign. This tells us how "noisy" or "stable" the gradients have been.parameters with large vtv_t values have been seeing big gradients, while small vtv_t values indicate gentler, smaller gradients.

There's a subtle issue: both these estimates start at zero, which means they're initially biased toward zero. Adam fixed this with bias correction terms:

m^t=mt1−β1t\hat{m}_t = \frac{m_t}{1 - \beta_1^t} v^t=vt1−β2t\hat{v}_t = \frac{v_t}{1 - \beta_2^t}

These corrections account for the zero initialization and ensure the estimates are accurate even in early training steps.

Finally, Adam updated each parameter using:

θt+1=θt−ηv^t+ϵm^t\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \hat{m}_t

Let's unpack this. The numerator m^t\hat{m}_t provides direction (where to move) with momentum. The denominator v^t\sqrt{\hat{v}_t} scales the update based on typical gradient magnitude.parameters with large gradients get divided by a large number, reducing their update size. The base learning rate η\eta (typically 0.001) controls overall step size, and ϵ\epsilon (a tiny number like 10−810^{-8}) prevents division by zero.

The brilliance was in the combination. Parameters with consistent, large gradients would build up momentum (large m^t\hat{m}_t) but also get divided by a large scaling factor (large v^t\sqrt{\hat{v}_t}), preventing overshooting. Parameters with small or sparse gradients would accumulate less momentum but also get divided by a smaller scaling factor, allowing them to make meaningful progress even with limited gradient information.

Best of all, the default hyperparameter values worked remarkably well across different tasks. Set β1=0.9\beta_1 = 0.9, β2=0.999\beta_2 = 0.999, and η=0.001\eta = 0.001, and you'd likely have a working optimizer. No more spending days trying different learning rates and momentum values. No more watching training runs fail halfway through because the learning rate was slightly wrong. Adam just... worked.

Why Adaptive Learning Rates Matter

Think about the different types of parameters in a neural network. Some are in early layers, processing raw input data.these often need gentle, careful adjustments to learn stable patterns. Others are in later layers, combining high-level features.these might benefit from larger, bolder updates. Some parameters see gradient signals on every training example, while others only get updated occasionally when specific patterns appear in the data.

Traditional optimizers treated all these parameters identically, using the same learning rate for everything. This forced an awkward compromise: set the rate low enough to keep the sensitive parameters stable, which meant the less sensitive ones learned painfully slowly. Or set it high enough for fast learning, which risked destabilizing the sensitive parameters.

Adam solved this by automatically giving each parameter the learning rate it needed. Parameters that consistently saw large gradients got smaller effective rates (to avoid overshooting). Parameters with sparse or small gradients got larger effective rates (to make progress from limited information). No manual tuning required.the optimizer figured it out from the gradient patterns.