lmcinnes / lmcinnes/umap

Slow fit for small datasets using UMAP or densMAP

Open
#766 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
8.3k
Forks
871
Avg merge
1d 13h
Merged PRs (30d)
5

Description

Is it normal for UMAP or densMAP to take 5-40 s to fit a small dataset of precomputed distances (e.g. 10x10 distance matrix)? My "test" scripts for a separate project which include a couple of UMAP calls end up taking about as much time as if I used a 10000 x 10000 distance matrix.

Reproducible script:

"""Time various calls to UMAP."""
import umap
import numpy as np
from time import time
from scipy.spatial.distance import pdist, squareform


class Timer(object):
    """
    Simple timer class.

    https://stackoverflow.com/a/5849861/13697228
    Usage
    -----
    with Timer("description"):
        # do stuff
    """

    def __init__(self, name=None):
        """Record name."""
        self.name = name

    def __enter__(self):
        """Enter the timer."""
        self.tstart = time()

    def __exit__(self, type, value, traceback):
        """Exit the timer."""
        if self.name:
            print(
                "[%s]" % self.name,
            )
        print(("Elapsed: {}\n").format(round((time() - self.tstart), 5)))


X = np.random.rand(10, 5)

eucl_dm = squareform(pdist(X))

# fmt: off
# Earth Mover's Distance matrix (not related to X)
emd_dm = np.array([[ 0.        , 27.        , 27.        ,  6.86666667, 15.        ,
        19.65      , 10.8       , 15.6       ,  9.71428571, 19.6       ],
       [27.        ,  0.        , 14.5       , 26.66666667, 20.6       ,
        20.25      , 35.        , 19.        , 19.42857143, 25.        ],
       [27.        , 14.5       ,  0.        , 23.33333333, 14.2       ,
        11.25      , 24.        , 18.6       , 18.42857143, 25.        ],
       [ 6.86666667, 26.66666667, 23.33333333,  0.        , 12.66666667,
        18.41666667, 13.        , 13.26666667,  9.57142857, 16.33333333],
       [15.        , 20.6       , 14.2       , 12.66666667,  0.        ,
         8.15      , 18.8       , 19.4       , 16.42857143, 24.6       ],
       [19.65      , 20.25      , 11.25      , 18.41666667,  8.15      ,
         0.        , 19.75      , 23.85      , 17.53571429, 29.25      ],
       [10.8       , 35.        , 24.        , 13.        , 18.8       ,
        19.75      ,  0.        , 24.6       , 17.71428571, 29.        ],
       [15.6       , 19.        , 18.6       , 13.26666667, 19.4       ,
        23.85      , 24.6       ,  0.        ,  9.02857143,  6.8       ],
       [ 9.71428571, 19.42857143, 18.42857143,  9.57142857, 16.42857143,
        17.53571429, 17.71428571,  9.02857143,  0.        , 13.42857143],
       [19.6       , 25.        , 25.        , 16.33333333, 24.6       ,
        29.25      , 29.        ,  6.8       , 13.42857143,  0.        ]]) #
# fmt: on

with Timer("Euclidean UMAP"):
    umap.UMAP().fit(X)

with Timer("Precomputed Euclidean UMAP"):
    umap.UMAP(metric="precomputed").fit(eucl_dm)

with Timer("Precomputed EMD UMAP"):
    umap.UMAP(metric="precomputed").fit(emd_dm)

with Timer("Precomputed EMD densMAP"):
    umap_trans = umap.UMAP(
        densmap=True,
        output_dens=True,
        dens_lambda=1.0,
        min_dist=0.01,
        n_components=2,
        metric="precomputed",
    ).fit(emd_dm)

with Timer("Precomputed EMD densMAP"):
    std_trans = umap.UMAP(
        densmap=True,
        output_dens=True,
        dens_lambda=1.0,
        metric="precomputed",
    ).fit(emd_dm)

with Timer("Precomputed EMD UMAP"):
    std_trans = umap.UMAP(metric="precomputed").fit(emd_dm)

Output:

C:\Users\sterg\Anaconda3\envs\elm2d-crabnet\lib\site-packages\umap\umap_.py:2213: UserWarning: n_neighbors is larger than the dataset size; truncating to X.shape[0] - 1
  warn(
[Euclidean UMAP]
Elapsed: 14.15193

C:\Users\sterg\Anaconda3\envs\elm2d-crabnet\lib\site-packages\umap\umap_.py:1735: UserWarning: using precomputed metric; transform will be unavailable for new data and inverse_transform will be unavailable for all data
  warn(
C:\Users\sterg\Anaconda3\envs\elm2d-crabnet\lib\site-packages\umap\umap_.py:2213: UserWarning: n_neighbors is larger than the dataset size; truncating to X.shape[0] - 1
  warn(
[Precomputed Euclidean UMAP]
Elapsed: 8.00694

C:\Users\sterg\Anaconda3\envs\elm2d-crabnet\lib\site-packages\umap\umap_.py:1735: UserWarning: using precomputed metric; transform will be unavailable for new data and inverse_transform will be unavailable for all data
  warn(
C:\Users\sterg\Anaconda3\envs\elm2d-crabnet\lib\site-packages\umap\umap_.py:2213: UserWarning: n_neighbors is larger than the dataset size; truncating to X.shape[0] - 1
  warn(
[Precomputed EMD UMAP]
Elapsed: 4.76215

C:\Users\sterg\Anaconda3\envs\elm2d-crabnet\lib\site-packages\umap\umap_.py:1735: UserWarning: using precomputed metric; transform will be unavailable for new data and inverse_transform will be unavailable for all data
  warn(
C:\Users\sterg\Anaconda3\envs\elm2d-crabnet\lib\site-packages\umap\umap_.py:2213: UserWarning: n_neighbors is larger than the dataset size; truncating to X.shape[0] - 1
  warn(
[Precomputed EMD densMAP]
Elapsed: 28.30055

C:\Users\sterg\Anaconda3\envs\elm2d-crabnet\lib\site-packages\umap\umap_.py:1735: UserWarning: using precomputed metric; transform will be unavailable for new data and inverse_transform will be unavailable for all data
  warn(
C:\Users\sterg\Anaconda3\envs\elm2d-crabnet\lib\site-packages\umap\umap_.py:2213: UserWarning: n_neighbors is larger than the dataset size; truncating to X.shape[0] - 1
  warn(
[Precomputed EMD densMAP]
Elapsed: 11.316

C:\Users\sterg\Anaconda3\envs\elm2d-crabnet\lib\site-packages\umap\umap_.py:1735: UserWarning: using precomputed metric; transform will be unavailable for new data and inverse_transform will be unavailable for all data
  warn(
C:\Users\sterg\Anaconda3\envs\elm2d-crabnet\lib\site-packages\umap\umap_.py:2213: UserWarning: n_neighbors is larger than the dataset size; truncating to X.shape[0] - 1
  warn(
[Precomputed EMD UMAP]
Elapsed: 5.19057

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the reproducible script in the issue and comparing the reported timings for Euclidean, precomputed, and densMAP fits on the 10x10 inputs. Trace the UMAP().fit entry point to identify the source of the small-dataset overhead, then verify that any performance change preserves the shown precomputed-distance and densMAP behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
machine-learning, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.