Hierarchical Softmax › Path Probability: The Mathematics
Language AI HandbookAll chapters Hierarchical Softmax › Path Probability: The MathematicsOne section · Sections in this chapter5 / 16 ↓
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
With the tree structure defined, we can now write the probability formula precisely. The key insight is that the probability of a word equals the product of probabilities of all correct binary decisions along its path from root to leaf.
Binary Decisions with Learned Vectors
Each internal node has a learned vector of the same dimension as the word embeddings. To make the left-right decision at node , we compare this node vector with the current context embedding using a dot product, then pass the result through the sigmoid function:
where:
- : the sigmoid function, mapping any real number to
- : the learned vector at internal node
- : the context embedding (center word's input embedding)
This output gives the probability of going left. The probability of going right is . These two always sum to 1, which is what ensures the tree defines a valid probability distribution.
Why the sigmoid function specifically? The sigmoid maps real numbers to the interval , making it a natural candidate for probabilities. More importantly, the symmetry property means we can encode both the "go left" and "go right" probabilities using a single dot product and a sign flip. This symmetry is exploited compactly in the path probability formula.
The Path Probability Formula
The probability of word given context embedding is the product of probabilities at each decision point along the root-to-leaf path:
where:
- : the total number of nodes along the path from root to word (including root and leaf)
- : the -th node along the path to , with being the root
- : the direction code at step , equal to if the next node is the left child and if it is the right child
- : the learned vector at the -th internal node
- : the context embedding
The direction encoding is the key mechanism. When (go left), we compute , which is high when the dot product is positive. When (go right), we compute , which is the complementary probability. This compact encoding lets a single formula handle both directions without needing two separate parameters per node.
For hierarchical softmax to be valid, all leaf probabilities must sum to 1. This holds because at each internal node, the probabilities of its two children are and , which sum to 1. By induction starting from the root, the probability mass is conserved at every level and distributed exactly across all leaves.
Why Probabilities Can Be Small
For a vocabulary of 100,000 words, a Huffman tree assigns paths of roughly 15 to 20 nodes. The final probability is a product of 15 to 20 sigmoid values, each between 0 and 1. Even if each step has probability 0.8, the product of 17 steps is . This is expected behavior: 100,000 words share probability mass, so individual word probabilities must be small. The model is not failing; it is correctly distributing probability across a large vocabulary.
Working in log space helps numerically. Instead of multiplying probabilities (which can underflow to zero for long paths), we sum log-probabilities. This is numerically stable and is the natural form for the loss function we will derive below.
import numpy as np
def sigmoid(x):
"""Numerically stable sigmoid."""
return np.where(x >= 0, 1 / (1 + np.exp(-x)), np.exp(x) / (1 + np.exp(x)))
def compute_path_probability(h, path_vectors, path_directions):
"""
Compute P(w | h) as the product of binary decisions along the path.
Args:
h: Context embedding (embedding_dim,)
path_vectors: Internal node vectors along the path
path_directions: +1 for left, -1 for right at each node
Returns:
Word probability and per-step probabilities
"""
step_probs = []
for v, d in zip(path_vectors, path_directions):
step_prob = sigmoid(d * np.dot(v, h))
step_probs.append(float(step_prob))
word_prob = float(np.prod(step_probs))
return word_prob, step_probs
np.random.seed(42)
dim = 50
h = np.random.randn(dim) * 0.3
# Simulate a path of length 4 (small vocabulary example)
path_vectors = [np.random.randn(dim) * 0.3 for _ in range(4)]
path_directions = [1, -1, -1, 1] # left, right, right, left
word_prob, step_probs = compute_path_probability(
h, path_vectors, path_directions
)Path Probability Step-by-Step:
-----------------------------------------------------------------
Step Direction Dot Product Step Prob Cumulative
-----------------------------------------------------------------
1 left 0.3782 0.5934 0.593443
2 right -0.4852 0.6190 0.367330
3 right 0.3082 0.4235 0.155581
4 left 1.3432 0.7930 0.123377
-----------------------------------------------------------------
Final P(word | h) = 0.123377
For comparison: 1 / vocab_size (8 words) = 0.125000The output shows how each binary decision multiplies into the running probability. The final value is close to for a perfectly balanced 8-leaf tree, which is the expected word probability when the model assigns equal mass to all words. As training proceeds, the model adjusts node vectors so that these probabilities increase for observed word pairs and decrease for unobserved ones.