Repository navigation
feat(clustering): add optional k-means sampling and assignment of new vectors - #202
Merged
Merged
Conversation
… vectors Add max_points_per_centroid to RaBitQKMeansParameters and QGKMeansParameters. It defaults to 0, which trains on all vectors as before; Faiss uses 256. When positive and n exceeds k * max_points_per_centroid, initialization and Lloyd iterations use a seeded random sample drawn from a stream independent of centroid initialization, and the final centroids then assign all n points with the configured final_assignment. assignments and distances keep shape (n,); obj and iteration_stats describe the sample, distances and final_obj all points. Training that does not sample is unchanged bit for bit. Sampling copies the sample, so it raises peak memory. Add assign(n, x, assignments, distances) const to both C++ classes and assign(x) -> (uint32 (m,), float32 (m,)) in Python. It uses the parameters of the last successful fit and no labels or search hints from training, so each vector's result is independent of the batch: - RaBitQKMeans stores the center its training points were encoded around and encodes new vectors around it. - QGKMeans caches a graph over the centroids, rebuilt only when the centroids or graph settings change, and searches it with per-call scratch and no query-sum buffer. assign validates x and the public centroids like training input and raises "<Name> has not been trained" before a successful train(). A sampled fit's final pass is the same assignment, so assign reproduces its labels.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds optional training-set sampling to
RaBitQKMeansandQGKMeans, and anassignmethod that maps new vectors to trained centroids.Sampling (
max_points_per_centroid, default0)0trains on all vectors, as before. Faiss uses 256.n > k * max_points_per_centroid, centroid initialization and the Lloyd iterations usek * max_points_per_centroidvectors drawn without replacement.seedselects them through a random stream separate from initialization.nvectors with the configuredfinal_assignment.assignmentsanddistanceskeep shape(n,), soIvfIndex.build(data, centroids, assignments)and similar calls are unchanged.objanditeration_statsdescribe the sample;distancesandfinal_objcover all points. The docs say the two are not directly comparable.assignassign(n, x, assignments, distances) constwrites to caller-owned buffers.assign(x)returns(uint32 (m,), float32 (m,)).final_assignmentand the other parameters of the last successful fit.RaBitQKMeansstores the center its training points were encoded around, and encodes new vectors around it.QGKMeanscaches a graph over the centroids. It is rebuilt only when the centroids or graph settings change, and searched read-only with per-call scratch.assignreproduces its labels. It also reproduces training labels in Exact mode and forRaBitQKMeans. Unsampled approximateQGKMeanstraining uses search hints, so its labels can differ (documented).xand the publiccentroidsare validated like training input.train(), it raisesRuntimeError/std::runtime_errorwith"<Name> has not been trained", including when C++ callers wrotecentroidsdirectly.Compatibility
assignmethod, and amax_points_per_centroidfield added at the end of both parameter structs, so positional aggregate initialization keeps its meaning.cpattribute; the type stubs and docstrings are updated.Testing
All checks ran on this commit (rebased on
cc2e4f2), on Linux x86-64, GCC 14.3,RABITQ_ENABLE_NATIVE_OPTIMIZATION=OFF:CTest (
--no-tests=error): 468/468 passed. One test (ThreadCount.BoundParallelForUsesRuntimePlacementAndPropagatesErrors) skipped becauseOMP_PROC_BINDwas not set.Static analysis and formatting: full
./scripts/check-tidy.shpassed, includingkmeans_bindings.cpp. Focused clang-tidy on the changed test files passed../scripts/check-format.shand./scripts/check-python.shpassed.Python (after reinstalling into the project environment and confirming the imported extension was the new build):
RABITQ_REQUIRE_CPP_EXAMPLES=1: 18 passed.Docs:
mkdocs build --strictpassed.Compatibility: the same program was built against the
mainheaders and this branch's headers and compared over 112 configurations: both classes, both final modes, L2 and spherical, QG bits 0/4/8, 1 and 4 threads, retraining on own centroids, andn = k * 256exactly. Outputs are bit-identical single-threaded. With 4 threads everything exceptiteration_stats[].shiftmatches;shiftalready varies run to run onmainbecause it comes from an OpenMP+reduction.New tests (
kmeans_sampling_test.cpp,test_kmeans_sampling.py), for both classes and both final modes:n = k*mandk*m + 1;npoints are assigned;assignerrors and validation, including invalid centroids;assignreproduces training labels in the cases listed above;Mutating the threshold (both off-by-one directions), removing centroid validation, encoding around the batch mean, or disabling cache invalidation each makes the corresponding tests fail.
Not run: macOS, Windows and ARM builds, which CI covers. Performance is not claimed in this PR; a benchmark of training time and downstream IVF recall with and without sampling is planned for the next release.