Data Sparsity

By Updated

Observe how "sparse" data (inputs composed mostly of zeros) propagates. Because multiplying by zero yields zero, pathways originating from dormant inputs become entirely inactive. Combining this with ReLU activations forces massive sections of the network into a dormant state, drastically reducing the number of required computations!

50%
Percentage of input values that will be strictly 0.

Efficiency Metrics

State
IDLE
Click 'Next Sample' or 'Stream Data'.
Active Inputs
--
Active Neurons
--
Compute Skipped (FLOPs)
--
Out of 0 total mults

Visual Legend

Dormant (0.0) Active (>0.0)
Drag to Pan | Scroll to Zoom

Overview

Observe how "sparse" data (inputs composed mostly of zeros) propagates.

In this guide
  1. What sparsity means, and where it comes from
  2. Why it hurts training
  3. Two other problems with the same name
  4. Why sparse features are awkward
  5. Embeddings: the standard fix
  6. Mostly zeros, and what that costs
  7. Guided tests
  8. Embeddings, the standard fix
  9. Where this goes wrong
  10. What to remember
  11. Handling it in code
  12. Which nets cope well
  13. Sparse labels, briefly
  14. Questions people ask
  15. Recap in one screen

Data Sparsity: A Practical Guide

When most feature values are zero, dense storage and dense arithmetic both stop making sense. Sparsity is normal in real data, and handling it well is mostly about not materialising the zeros.

What sparsity means, and where it comes from

A feature vec is sparse when most of its entries are zero. This is not an edge case. It is the default in a few of the most common data types:

  • One-hot encoded groups. A country field with 195 vals turns 195 columns of which just one is 1. That is 99.5% zeros by construction.
  • Bag-of-words text. A vocabulary of 50,000 words against a 200-word document gives at most 0.4% non-zero.
  • Recommender matrices. A user has rated a few dozen of a million items. These are routinely above 99.9% zero.
  • Mix and count features where most combinations simply never occur.

The density of a grid is the fraction of entries that are non-zero. A bag-of-words grid at 0.4% density wastes 99.6% of its memory on zeros and 99.6% of its arithmetic on multiplying by zero. Sparse storage formats (CSR, CSC, COO) store just the non-zero vals and their indices, cutting memory by two to three orders of magnitude. Sparse grid steps skip the zero entries fully. It is why scikit-learn's linear nets accept scipy sparse matrices and train much faster on them than on dense NumPy arrays. In deep learning, embeddings solve the same problem differently: instead of multiplying a one-hot vec by a weight grid (which selects one row), an embedding layer looks up that row right by index, avoiding the multiplication altogether. Adaptive solvers like Adam and Adagrad handle the gradient side of sparsity by giving each param its own good learning rate based on how often it has been updated. So rare features learn at a pace proportional to their information rather than their rate. This page builds a sparse dataset, checks dense and sparse storage sizes and training times. And shows how embeddings replace the one-hot multiplication.

Why it hurts training

Sparsity causes three split problems. And they are worth keeping apart:

Memory. Storing a 100,000×50,000 grid densely at 4 bytes per float is 20 GB, nearly all of it zeros. Sparse formats such as CSR store just the non-zeros plus their indices, often cutting this by two or three orders of magnitude.Wasted math. A dense grid multiply does a multiply-add for each item. And multiplying by zero adds nothing. Most of the arithmetic gives no information.Uneven gradients. This is the subtle one. A weight connected to a feature that is non-zero in 0.1% of samples gets a gradient in just 0.1% of steps. Rare features therefore learn roughly a thousand times more slowly than common ones under a single global learning rate. Which is precisely the problem Adagrad and Adam solve, by giving each param its own good rate based on how often it has got a gradient. That is why adaptive solvers are the standard choice on sparse data. The real fix for memory is to use scipy's sparse grid formats (CSR, CSC, COO) or their equivalents in your framework. They store just the non-zero vals and their indices, cutting memory by orders of magnitude. For math, sparse grid-vec products skip the zero entries fully. It is why scikit-learn's linear nets accept sparse input natively and train much faster on it than on the dense the same. Embeddings are the deep-learning solution: instead of feeding a one-hot vec of 50,000 dimensions into a dense layer (which would multiply by 50,000 weights), look up the single non-zero index in a learned embedding table and use those few hundred vals right. This page creates a sparse dataset, checks dense and sparse storage sizes. And shows how an embedding layer replaces the one-hot multiplication.

Two other problems with the same name

"Sparse" describes two cases that need fully other replies. And conflating them causes a lot of confusion.

Sparse features — most vals in the input are zero. One-hot encoded groups, bag-of-words text, user-item mix matrices. A vocabulary of 50,000 words means a document vec where 49,900 entries are zero.

Sparse labels — you have plenty of data and very few annotations. This is a labelling problem, addressed by semi-supervised learning, active learning or transfer learning.

Both are common; just the first is about form. And the rest of this is mostly about that one.

Why sparse features are awkward

Memory. A million users by 50,000 items stored densely is 200GB of floats. Stored sparsely — just the non-zero spots and vals. It may be a few hundred megabytes. Sparse formats are not an optimisation here. They are the gap between possible and impossible.

Thin stats. A feature that is non-zero in 20 of a million rows gives the net almost nothing to learn from. So its weight is mostly noise.

Distances stop working. Two documents that share no words have identical Euclidean distance no matter what else they hold. Cosine similarity, which checks way, is the standard option for just this reason.

Gradients are sparse too. A weight connected to a feature that is zero in this batch gets no gradient. So it updates just in the batches where its feature appears. Rare features therefore learn very slowly. Which is precisely the problem adaptive solvers were designed for. Adam and AdaGrad give params with infrequent gradients more good steps.

Embeddings: the standard fix

Instead of a 50,000-dim sparse vec, learn a dense vec of 64 to 256 counts per group, trained jointly with the rest of the net.

 One-hotEmbedding
WidthOne column per categoryA chosen width, e.g. 64
Similar categoriesEquidistant — no notion of similarityNearby in the space
Rare categoriesAlmost no data per columnShare structure with others
MemorySparse, wideDense, compact

The second row is the key one. One-hot coding makes each group just as other from each other. An embedding can place "London" near "Manchester" and far from "banana" since the training signal pushes them there. That is why embeddings are the default for high-cardinality class-based features in deep nets. And why they underpin each recommendation system.

A common width heuristic is min(50, (cardinality + 1) // 2). Or the fourth root of the cardinality. Both are starting points to be tuned rather than rules.

Mostly zeros, and what that costs

Sparse data breaks dense assumptions in three set ways -- memory, gradient flow, and distance. Each one is measured here, along with the standard fix.

example_01.pyNumPy
Output

Building on the same setup, the next block shows the left cases.

example_02.pyNumPy
Output

The next block covers the left steps.

example_03.pyNumPy
Output

Guided tests

  1. Start dense. Set Input Sparsity Level to 0 and press Stream Data. Each input carries signal and each weight gets a gradient on each sample.
  2. Add realistic sparsity. Set Input Sparsity Level to 70 and stream again. Most inputs are now zero. And the connections behind them go quiet. A weight whose input is zero gets no gradient at all that step.
  3. Push it to the recommender regime. Set Input Sparsity Level to 95 and press Next Sample repeatedly. Watch how rarely any given input activates. Those weights update on a handful of samples in a hundred. It is just why they lag.
  4. Check which weights move. At high sparsity, note that the weights attached to often non-zero inputs converge quickly while the rest barely move. A global learning rate cannot serve both.

Embeddings, the standard fix

For high-cardinality class-based data the usual answer is not to keep the one-hot vec at all. An embedding layer maps each group to a short dense vec. Say 50 dimensions instead of 195 columns. Learned in training.

In math this is identical to multiplying the one-hot vec by a weight grid. But the build is a row lookup instead of a grid multiply. So it skips the zeros fully. It also solves a modelling problem: one-hot groups are all equidistant, whereas learned embeddings place close groups near each other. So information is shared between tied vals rather than each being learned in isolation.

Where this goes wrong

  • Densifying a sparse grid. Calling .toarray() on a large sparse grid is the classic out-of-memory error. Keep it sparse through the whole pipeline.
  • Plain SGD on sparse features. Rare features learn far too slowly. Use Adam or Adagrad. It adapt per-param learning rates to update rate.
  • One-hot coding a high-cardinality column. Thousands of near-empty columns. Use an embedding, target coding or hashing instead.
  • Mean-imputing a structural zero. In sparse data zero often means “absent”, not “missing”. Replacing it with a column mean invents signal that was never there.
  • Centring sparse data. Subtracting the mean makes each zero non-zero and destroys the sparsity outright. Scale without centring.

What to remember

Sparse data is the norm for one-hot groups, text and recommenders. And it costs memory, wasted arithmetic. And — least obviously. Badly uneven learning rates, since a weight just updates when its input is non-zero. Keep sparse matrices in sparse formats, replace one-hot encodings of high-cardinality fields with learned embeddings. And use an adaptive solver so rare features are not left thousands of steps behind common ones.

Handling it in code

For classical nets, keep the grid sparse end to end:

from scipy import sparse
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression

X = TfidfVectorizer(min_df=5).fit_transform(docs)     # sparse matrix
print(X.shape, X.nnz / (X.shape[0] * X.shape[1]))     # density, often < 1%

LogisticRegression().fit(X, y)                        # accepts sparse input

Never call .toarray() on a large sparse grid. That is the line that exhausts memory. min_df=5 is also doing real work: dropping terms that appear in fewer than five documents removes most of the vocabulary and almost none of the signal.

For neural networks, use an embedding layer and pass integer indices rather than one-hot vecs:

emb = nn.Embedding(num_embeddings=50_000, embedding_dim=64)
out = emb(token_ids)          # (batch, seq) -> (batch, seq, 64)

nn.Embedding is a lookup, not a grid multiplication. So it never materialises the one-hot vec at all. nn.EmbeddingBag sums or averages a few embeddings in one call. It is the fast way to represent a bag of features.

Which nets cope well

ModelWith sparse features
Linear / logistic regressionExcellent — sparse-aware and fast
Naive BayesExcellent — designed for high-dimensional counts
Gradient boosting (LightGBM, XGBoost)Good — handles sparsity natively
Neural networks with embeddingsExcellent, and the standard for high cardinality
KNN, k-meansPoor without cosine distance and dimension reduction
Dense neural networks on one-hot inputPoor — wasteful and slow to learn

Two more options worth knowing. Feature hashing maps groups into a fixed count of buckets, accepting collisions in exchange for bounded memory and no vocabulary to keep. Useful when new groups appear constantly. And truncated SVD reduces a sparse grid to a few hundred dense parts. It is what makes clustering and nearest-neighbour search on text feasible.

Sparse labels, briefly

The other meaning deserves its own short answer, since the tricks are other:

  • Transfer learning — start from a pretrained net. So a few hundred labels suffice.
  • Self-supervised pretraining on your unlabelled data, then fine-tune on the labelled subset.
  • Active learning — let the net choose which cases to send for labelling, prioritising the ones it is least sure about.
  • Pseudo-labelling — label the confident predictions on unlabelled data and train on them, carefully, since errors compound.
  • Weak supervision — mix noisy rules and heuristics into probabilistic labels.

The first two are where the uses is: pretraining plus a small labelled set now beats a large labelled set trained from scratch on most tasks.

Questions people ask

When is data "sparse"? Informally, when a large bulk of entries are zero. Below about 10% density, sparse storage and sparse-aware nets start to matter.

Should I use one-hot or an embedding? One-hot below about 10 groups. Embeddings above roughly 50, especially in a network.

Does PCA work on sparse data? Not right. Centring destroys sparsity. Use TruncatedSVD, which does not centre.

Which distance for sparse vecs? Cosine, almost always. Euclidean distance is dominated by the shared zeros.

Do trees handle sparsity? LightGBM and XGBoost handle it natively and well, with treating missing and zero appropriately.

Why do rare features learn slowly? They appear in few batches. So their weights get few gradients. Adaptive solvers partly compensate by giving them more good steps.

Recap in one screen

  • "Sparse" means either mostly-zero features or mostly-missing labels. Other problems, other fixes.
  • Sparse features cost memory, give thin per-feature stats, break Euclidean distance and slow learning for rare vals.
  • Keep sparse matrices sparse. Never densify a large one.
  • Embeddings replace wide one-hot encodings with compact dense vecs that can express similarity.
  • For sparse labels, transfer learning and self-supervised pretraining give the most uses.

Recall check

0 of 4

Say the answer out loud before you reveal it — recalling it is what makes it stick, and rereading it is not.

  1. What is meant by “Transfer learning” here?

  2. What is meant by “Self-supervised pretraining” here?

  3. What is meant by “Active learning” here?

  4. What is meant by “Pseudo-labelling” here?

Cheat sheet

Data Sparsity

Feed mostly-zero inputs through a network and watch entire pathways go dormant, because anything multiplied by zero stays zero.

DEEP LEARNING · vizlearn.in/deep_learning/data_sparsity.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.