This is a multicore modification of Barnes-Hut t-SNE by L. Van der Maaten with python and Torch CFFI-based wrappers. This code also works faster than sklearn.TSNE on 1 core.
Difference of this fork with Multicore t-SNE repository of DmitryUlyanov:
- Refactored code with explanation of the implementation in the comments.
- More metrics available:
- Euclidean distance
- Squared euclidean distance
- Angular distance
- Cosine distance (not a real metric)
- Precomputed distance marix
- Possibility to freeze some specific point or set a lower learning rate for them.
Barnes-Hut t-SNE is done in two steps.
-
First step: an efficient data structure for nearest neighbours search is built and used to compute probabilities. This can be done in parallel for each point in the dataset, this is why we can expect a good speed-up by using more cores.
-
Second step: the embedding is optimized using gradient descent. This part is essentially consecutive so we can only optimize within iteration. In fact some parts can be parallelized effectively, but not all of them a parallelized for now. That is why second step speed-up will not be that significant as first step sepeed-up but there is still room for improvement.
So when can you benefit from parallelization?
It is almost true, that the second step computation time is constant of D
and
depends mostly on N
. The first part's time depends on D
a lot,
so for small D
time(Step 1) << time(Step 2)
,
for large D
time(Step 1) >> time(Step 2)
.
As we are only good at parallelizing step 1 we will benefit most when D
is large enough (MNIST's D = 784
is large, D = 10
even for N=1000000
is not so much).
Interestingly, that this code beats other implementations. We compare to sklearn
(Barnes-Hut of course), L. Van der Maaten's bhtsne, py_bh_tsne repo (cython wrapper for bhtsne with QuadTree). perplexity = 30, theta=0.5
for every run. In fact py_bh_tsne repo works at the same speed as this code when using more optimization flags for compiler.
This is a benchmark for 70000x784
MNIST data:
Method | Step 1 (sec) | Step 2 (sec) |
---|---|---|
MulticoreTSNE(n_jobs=1) | 912 | 350 |
bhtsne | 4257 | 1233 |
py_bh_tsne | 1232 | 367 |
sklearn(0.18) | ~5400 | ~20920 |
I did my best to find what is wrong with sklearn numbers, but it is the best benchmark I could do (you can find test script in python/tests
folder).
This table shows a relative to 1 core speed-up when using n
cores.
n_jobs | Step 1 | Step 2 |
---|---|---|
1 | 1x | 1x |
2 | 1.54x | 1.05x |
4 | 2.6x | 1.2x |
8 | 5.6x | 1.65x |
Python and torch wrappers are available.
cmake >= v3.8
as we need C++17 support- C++ compiler, such as
gcc
orllvm-clang
. On macOS, you can get both via homebrew. Python 2.7
or3.6
To install the package, please do:
git clone https://github.com/DmitryUlyanov/Multicore-TSNE.git
cd Multicore-TSNE/
pip install .
You can use it as a near drop-in replacement for sklearn.manifold.TSNE.
from MulticoreTSNE import MulticoreTSNE as TSNE
tsne = TSNE(n_jobs=4)
Y = tsne.fit_transform(X)
Please refer to sklearn TSNE manual for parameters explanation.
This implementation n_components=2
,
which is the most common case (use Barnes-Hut t-SNE or sklearn otherwise). Also note that some parameters are there just for the sake of compatibility with sklearn and are otherwise ignored. See MulticoreTSNE
class docstring for more info.
You can test it on MNIST dataset with the following command:
python MulticoreTSNE/examples/test.py <n_jobs>
To make the computation log visible in jupyter please install wurlitzer
(pip install wurlitzer
) and execute this line in any cell beforehand:
%load_ext wurlitzer
Memory leakages are possible if you interrupt the process. Should be OK if you let it run until the end.
Inherited from original repo's license.
- Allow other types than double
- Improve step 2 performance (possible)
Please cite this repository if it was useful for your research:
@misc{Sanakoyeu2018,
author = {Sanakoyeu, Artsiom},
title = {Multicore-TSNE},
year = {2018},
publisher = {GitHub},
journal = {GitHub repository},
howpublished = {\url{https://github.com/asanakoy/Multicore-TSNE}},
}
@misc{Ulyanov2016,
author = {Ulyanov, Dmitry},
title = {Multicore-TSNE},
year = {2016},
publisher = {GitHub},
journal = {GitHub repository},
howpublished = {\url{https://github.com/DmitryUlyanov/Multicore-TSNE}},
}
Of course, do not forget to cite L. Van der Maaten's paper