deepbox/ml
Manifold & Projection
Manifold learning (TSNE, Isomap, MDS, SpectralEmbedding) and Gaussian random projection.
- Embedding
Overview
fitTransform(X)returns the embedding of the fitted rows.nComponentsdefaults to 2.TSNEruns the exact O(n^2) algorithm by default and refuses more thanmaxExactSamples(2000) rows. Setmethod: "approximate"for larger inputs.perplexitydefaults to 30 and must be below the number of samples. PassrandomStatefor a repeatable embedding.Isomapbuilds its neighbor graph fromnNeighbors: 5by default.GaussianRandomProjectionreduces dimensions with a random matrix.johnsonLindenstraussMinDim(nSamples, eps)gives the number of components that keeps pairwise distances withineps.
TSNEOptions
typeConstructor options of TSNE.
readonly nComponents?: numberreadonly perplexity?: numberreadonly learningRate?: number | "auto"readonly nIter?: numberreadonly earlyExaggeration?: numberreadonly earlyExaggerationIter?: numberreadonly randomState?: numberreadonly minGradNorm?: numberreadonly method?: "exact" | "approximate""exact" uses full pairwise interactions; "approximate" uses sampling for large datasets.
readonly maxExactSamples?: numberMaximum samples allowed in exact mode before requiring approximate.
readonly approximateNeighbors?: numberNumber of nearest neighbors per point in approximate mode.
readonly negativeSamples?: numberNumber of negative samples per point in approximate mode.
Isomap
Isomap: nonlinear dimensionality reduction through geodesic distances.
1. Connects every sample to its nNeighbors nearest neighbors (the graph is made symmetric; separate components are joined through their closest points and a warning is issued). 2. Computes all-pairs shortest-path distances on that graph (Dijkstra). 3. Embeds the geodesic distances with classical MDS.
Unlike t-SNE, Isomap can project new samples with transform.
import { Isomap } from 'deepbox/ml';import { tensor } from 'deepbox/ndarray'; const X = tensor([[0, 0], [1, 0.1], [2, 0.3], [3, 0.2], [4, 0.1], [5, 0]]);const iso = new Isomap({ nComponents: 1, nNeighbors: 2 });const embedding = iso.fitTransform(X); // shape [6, 1]const projected = iso.transform(tensor([[2.5, 0.25]]));Members (9)
constructor(options?: { readonly nComponents?: number; readonly nNeighbors?: number; })embedding: TensorEmbedding of the training data, shape (n_samples, nComponents).
distMatrix: TensorGeodesic (shortest-path) distances between the training samples, shape (n_samples, n_samples).
nFeaturesIn: numberNumber of features seen during
fit.fit(X: Tensor): thisLearn the embedding of the training data.
fitTransform(X: Tensor): TensorFit the model and return the embedding of the training data.
transform(X: Tensor): TensorProject new samples into the learned embedding.
getParams(): Record<string, unknown>setParams(params: Record<string, unknown>): thisUpdate hyperparameters.
MDS
Classical (Torgerson) multidimensional scaling.
Computes the pairwise Euclidean distances (or takes a precomputed distance matrix), double-centres the squared distances and embeds the points with the leading eigenvectors. This is a closed-form, deterministic solution and not the iterative SMACOF stress minimisation that scikit-learn's MDS runs; for Euclidean input it equals PCA of the data.
import { MDS } from 'deepbox/ml';import { tensor } from 'deepbox/ndarray'; const X = tensor([[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 1]]);const embedding = new MDS({ nComponents: 2 }).fitTransform(X); // From a precomputed distance matrixconst D = tensor([[0, 3, 4], [3, 0, 5], [4, 5, 0]]);const layout = new MDS({ nComponents: 2, dissimilarity: 'precomputed' }).fitTransform(D);Members (7)
constructor(options?: { readonly nComponents?: number; readonly dissimilarity?: "euclidean" | "precomputed"; })embedding: TensorEmbedding of the fitted data, shape (n_samples, nComponents).
nFeaturesIn: numberNumber of columns of the matrix passed to
fit.fit(X: Tensor): thisCompute the embedding.
fitTransform(X: Tensor): TensorFit the model and return the embedding.
getParams(): Record<string, unknown>setParams(params: Record<string, unknown>): thisUpdate hyperparameters.
SpectralEmbedding
Laplacian eigenmaps with a fully connected RBF affinity graph.
The affinity is W_ij = exp(-gamma * ||x_i - x_j||^2) with a zero diagonal. With the degree matrix D, the embedding consists of the eigenvectors of the symmetric normalized affinity D^-1/2 W D^-1/2 that belong to its largest eigenvalues (the smallest eigenvalues of the normalized Laplacian), without the trivial first one, mapped back with D^-1/2. This is the construction of scikit-learn's SpectralEmbedding(affinity="rbf").
import { SpectralEmbedding } from 'deepbox/ml';import { tensor } from 'deepbox/ndarray'; const X = tensor([[0, 0], [0, 1], [1, 0], [5, 5], [5, 6], [6, 5]]);const se = new SpectralEmbedding({ nComponents: 2, gamma: 0.5 });const embedding = se.fitTransform(X);Members (7)
constructor(options?: { readonly nComponents?: number; readonly gamma?: number; })embedding: TensorEmbedding of the fitted data, shape (n_samples, nComponents).
nFeaturesIn: numberNumber of features seen during
fit.fit(X: Tensor): thisCompute the embedding.
fitTransform(X: Tensor): TensorFit the model and return the embedding.
getParams(): Record<string, unknown>setParams(params: Record<string, unknown>): thisUpdate hyperparameters.
TSNE
t-Distributed Stochastic Neighbor Embedding (t-SNE).
A nonlinear dimensionality reduction technique for embedding high-dimensional data into a low-dimensional space (typically 2D or 3D) for visualization.
Algorithm: Exact t-SNE with an optional sparse approximation - Computes pairwise affinities in high-dimensional space using a Gaussian kernel whose bandwidth is calibrated per point to the requested perplexity - Computes pairwise affinities in low-dimensional space using a Student-t distribution - Minimizes the KL divergence between the two distributions by gradient descent with momentum and adaptive per-coordinate gains
Scalability Note: Exact t-SNE is O(n^2) in time and memory. With method: "approximate" the input affinities are restricted to the approximateNeighbors true nearest neighbors of every point and the repulsive forces are estimated from negativeSamples random points per sample and iteration, so every iteration costs O(n * (neighbors + negatives)). Finding the neighbors is still an O(n^2 d) brute-force pass.
t-SNE is non-parametric: it embeds exactly the samples it was fitted on and cannot project new data.
import { TSNE } from 'deepbox/ml';import { tensor } from 'deepbox/ndarray'; const X = tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [2, 1, 0]]); const tsne = new TSNE({ nComponents: 2, perplexity: 2, randomState: 0 });const embedding = tsne.fitTransform(X);Members (9)
constructor(options?: TSNEOptions)embedding: TensorEmbedding of the fitted data, shape (n_samples, n_components).
embeddingResult: TensordeprecatedGet the embedding.
klDivergence: number | undefinedKL divergence between the input and embedding affinities after the last fit.
fitTransform(X: Tensor): TensorFit the t-SNE model and return the embedding.
fit(X: Tensor): thisFit the model (same as fitTransform for t-SNE).
transform(X?: Tensor): TensorReturn the fitted embedding.
getParams(): Record<string, unknown>Get hyperparameters for this estimator.
setParams(params: Record<string, unknown>): thisUpdate hyperparameters.
GaussianRandomProjection
Gaussian Random Projection.
Reduces dimensionality by projecting data onto a random Gaussian matrix. Each component of the projection matrix is drawn from N(0, 1/n_components).
Based on the Johnson-Lindenstrauss lemma: random projections approximately preserve pairwise distances. With nComponents: "auto" the target dimension is the Johnson-Lindenstrauss bound for the number of training samples and eps.
Without randomState the matrix is drawn from the global generator, so setSeed() makes it reproducible; with randomState it comes from a private stream.
import { GaussianRandomProjection } from 'deepbox/ml'; const proj = new GaussianRandomProjection({ nComponents: 50, randomState: 0 });proj.fit(X_train);const X_reduced = proj.transform(X_test);Members (8)
constructor(options?: { readonly nComponents?: number | "auto"; readonly eps?: number; readonly randomState?: number | null; readonly seed?: number | null; })components: TensorProjection matrix of shape (n_components, n_features).
fit(X: Tensor, _y?: Tensor): GaussianRandomProjectionDraw the random projection matrix.
transform(X: Tensor): TensorProject
Xonto the random components.fitTransform(X: Tensor, y?: Tensor): TensorFit and project in one call.
getParams(): Record<string, unknown>setParams(params: Record<string, unknown>): GaussianRandomProjectionSet hyperparameters.
clone(): GaussianRandomProjectionCreate an unfitted copy with the same hyperparameters.
johnsonLindenstraussMinDim
function johnsonLindenstraussMinDim(nSamples: number, eps?: number): number;Smallest number of random-projection components that keeps pairwise distances of nSamples points within a factor 1 +/- eps, according to the Johnson-Lindenstrauss lemma (the bound used by scikit-learn's johnson_lindenstrauss_min_dim).
Parameters
nSamplesnumber- Number of points (>= 1)
epsoptionalnumber- Allowed distortion, in the open interval (0, 1)
Returns
numberMinimum safe number of components
Throws
InvalidParameterErrorIfnSamplesis not a positive integer orepsis outside (0, 1)
johnsonLindenstraussMinDim(1e6, 0.5); // 663import { GaussianRandomProjection, Isomap, MDS, TSNE, johnsonLindenstraussMinDim,} from "deepbox/ml";import { tensor } from "deepbox/ndarray"; const X = tensor([ [1, 0, 0], [0.9, 0.1, 0], [0, 1, 0], [0, 0.9, 0.1], [0, 0, 1], [0.1, 0, 0.9],]); console.log(johnsonLindenstraussMinDim(1000, 0.2));console.log(new GaussianRandomProjection({ nComponents: 2, randomState: 0 }).fitTransform(X).shape);console.log(new Isomap({ nComponents: 2, nNeighbors: 3 }).fitTransform(X).shape);console.log(new MDS({ nComponents: 2 }).fitTransform(X).shape);console.log(new TSNE({ nComponents: 2, perplexity: 2, randomState: 0 }).fitTransform(X).shape);Found a mistake? Open an issue.