Mini-batch k-means with exponential-moving-average centroid updates
(Bessa et al., 2026, arXiv:2611.09501). Batch size and step size come from
fitted scaling laws, so the only inputs are the data, k, and the epoch
budget. In a few epochs it typically reaches a lower within-cluster sum of
squares than 25 Lloyd iterations.
Arguments
- x
Numeric matrix or data frame, one row per point.
- k
Number of clusters, or a
k x dmatrix of initial centers.- nepochs
Epoch budget; fractions allowed (e.g.
1.5).- batch_law
"auto"(quality law whend <= 16, else throughput),"throughput", or"quality"(batch about 8 times smaller).- batch_size, alpha0
Optional overrides. Giving
batch_sizealone keeps the step law foralpha0.- spherical
Cluster unit vectors by cosine similarity. Rows of
xmust already be unit-norm; centers are returned as unit vectors.- unused_threshold
Revival threshold tau: clusters with mass below
tau * mean(mass)are revived.NULLgives 0.1, or 0 (off) whend <= 2.- max_points_per_centroid
If set and
nrow(x) > max_points_per_centroid * k, train on a random subset of that many rows (asfaiss.Kmeans).- precision
"double", or"single"for float32 distance computation (about 2x faster, half the memory). Centroids and statistics are always accumulated in double precision.- final_assign
If
TRUE(default), assign every row ofxto the final centers, socluster,withinssandsizedescribe the returned centers (asstats::kmeans()). IfFALSE,clusterholds labels from the last training pass, and rows left out bymax_points_per_centroidareNA.- seed
Integer seed for initialization, shuffling and revival. Does not touch R's random number generator.
- n_threads
Threads for assignment. Defaults to
getOption("emeansR.threads"), else physical cores minus one.- verbose
Print the recipe and per-epoch objective.
Value
An object of class c("emeans", "kmeans") with components
cluster, centers, totss, withinss, tot.withinss, betweenss,
size, iter (epochs), ifault (0), plus obj (moving-pass objective
per epoch), iteration_stats, recipe, n_splits, spherical and
precision.
Details
Each epoch visits the rows in a fresh random order in batches of
batch_size. Every batch is assigned to the nearest centroids, and each
touched centroid moves towards its batch mean with weight
alpha * n_j / N_j, where the mass N_j is itself an exponential moving
average and alpha follows a cosine schedule from alpha0 to
alpha0 / 10. Every five updates, clusters with mass below
unused_threshold * mean(mass) are revived by splitting large clusters
(picked with probability proportional to mass). The returned centers are
the exact means of the points assigned during the last epoch.
