Embedding clustering
July 28, 2026 ยท View on GitHub
Protify can cluster existing embedding vectors or per-token embedding matrices without loading a protein language model. The workflow reads local embedding artifacts or a Python vector-database adapter, converts every record to a fixed-width vector, optionally standardizes the features, runs one or more clustering algorithms, and saves assignments, metrics, transformed vectors, configuration, and diagnostic plots.
The implementation is in visualization/clustering.py.
Command-line workflow
Cluster a Protify .pth embedding dictionary or SQLite embedding database:
python -m src.protify.visualization.clustering \
--input embeddings/ESM2-650_False_mean.pth \
--output_dir plots/clustering/esm2_650 \
--algorithms kmeans agglomerative dbscan optics \
--n_clusters 8 \
--standardize \
--reductions pca tsne
If --output_dir is omitted, artifacts go to plots/clustering/<input-stem>/.
For per-token matrix embeddings, one or more pooling operations can be concatenated:
python -m src.protify.visualization.clustering \
--input embeddings/ESM2-650_True.pth \
--pooling_types mean var max \
--algorithms all \
--n_clusters 12
Supported pooling operations are mean, max, norm, median, std, var, and cls. Existing one-dimensional vectors are used unchanged. parti is not available because saved embedding artifacts do not contain the attention matrices it requires.
Input sources
The loader accepts:
.ptor.pth: a mapping from record IDs or sequences to(d,)vectors or(l, d)token matrices..npy: an(n, d)vector matrix or(n, l, d)stack of token matrices..npz: anembeddingsarray and optionalidsarray. A single non-ID array is also accepted..csvor.tsv: one record per row, with numeric feature columns and an optional ID column. Use--csv_id_columnwhen it cannot be inferred..db,.sqlite, or.sqlite3: Protify'sembeddings(sequence, embedding)SQLite schema.- Direct NumPy arrays, Torch tensors, mappings, or a Python
VectorSourceadapter.
Modern Protify SQLite blobs carry their tensor shape. Legacy raw-float blobs require --legacy_embedding_dim so the feature width is explicit.
FastPLMs result databases and third-party services such as FAISS, Chroma, Qdrant, Pinecone, or Weaviate use different schemas and are not opened implicitly. Connect them through the small adapter protocol instead:
from protify.visualization.clustering import (
ClusteringConfig,
run_clustering_workflow,
)
class MyVectorDatabase:
def iter_vectors(self):
for record in query_my_database():
yield record.id, record.embedding
result = run_clustering_workflow(
MyVectorDatabase(),
ClusteringConfig(
output_dir="plots/clustering/my_database",
algorithms=("kmeans", "dbscan", "hdbscan"),
n_clusters=8,
standardize=True,
),
)
The adapter may yield either vectors or per-token matrices.
Algorithms
The basic default set avoids algorithms with known quadratic worst-case behavior:
kmeansbirchhdbscan
Additional selectable algorithms are:
mini_batch_kmeansagglomerativespectralgaussian_mixturedbscanoptics
Use --algorithms all to run every algorithm. Fixed-cluster algorithms use --n_clusters. DBSCAN uses --eps and --min_samples; OPTICS uses --min_samples; HDBSCAN uses both --min_samples and --min_cluster_size. Stochastic algorithms use --seed.
Agglomerative, spectral, and DBSCAN clustering are guarded above 10,000 records by default because their worst-case runtime or memory can be quadratic. Use --quadratic_algorithm_limit to make an explicit higher-cost choice. Internal validity metrics use a deterministic sample of at most 5,000 records, while reductions and scatter diagnostics use at most 3,000. These limits are configurable with --metric_sample_size and --plot_sample_size; assignments and cluster-size counts always cover the complete input.
Standardization is opt-in. --standardize fits a StandardScaler after matrix pooling and before every clustering algorithm and dimensionality reduction.
Outputs
Every run writes:
assignments.tsv: record IDs and the cluster label from each successful algorithm. Density methods retain-1for noise.metrics.tsv: cluster count, noise count and fraction, silhouette score, Calinski-Harabasz score, and Davies-Bouldin score.vectors.npz: the exact fixed-width vectors used for clustering, after optional standardization.standardizer.npz: fitted feature means and scales when standardization is enabled.config.json: the resolved run configuration and source.errors.json: any algorithm or optional dimensionality-reduction step that was skipped, with its error.manifest.json: current-run artifact inventory, resolved estimator parameters, source file metadata, and relevant library versions.
Plots are PNG files saved at 300 dpi by default:
- PCA, t-SNE, or optional UMAP cluster scatter plots.
- Cluster-size bars.
- Silhouette distributions when at least two non-noise clusters exist.
- Pairwise cluster-centroid distance heatmaps.
- Cross-algorithm metric comparison and PCA overview.
- KMeans elbow and silhouette-by-k diagnostics.
- DBSCAN nearest-neighbor distance diagnostics.
- OPTICS reachability and agglomerative dendrograms when selected.
Validity scores exclude records labeled as noise; metrics.tsv and the comparison plot report clustered coverage so sparse density-clustering solutions remain visible. UMAP is loaded lazily and requires the optional umap-learn package. If it is requested but unavailable, the rest of the run completes and the reason is recorded in errors.json.