diff --git a/notebooks/Parametric_UMAP/08.0-torch-parametric-umap.ipynb b/notebooks/Parametric_UMAP/08.0-torch-parametric-umap.ipynb new file mode 100644 index 00000000..58fecf2f --- /dev/null +++ b/notebooks/Parametric_UMAP/08.0-torch-parametric-umap.ipynb @@ -0,0 +1,334 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Some examples of using the torch version of Parametric UMAP\n", + "#\n", + "# Borrows ideas/code from:\n", + "# * https://github.com/lmcinnes/umap/issues/580\n", + "# * https://colab.research.google.com/drive/1CYxt0GD-Y2zPMOnJIXJWsAhr0LdqI0R6\n", + "#\n", + "# Uses the MNIST dataset to demonstrate how to use the code in two examples:\n", + "# * Vanilla parametric UMAP using the default arguments\n", + "# * Parametric Umap with a custom encoder, and a decoder for inversion. " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# Load the MNIST dataset (requires torchvision)\n", + "# Store dataset in variable X, where X[i] is\n", + "# the flattened pixel values for digit i (784=28^2 components).\n", + "\n", + "import torch\n", + "import torchvision\n", + "from torchvision.transforms import transforms\n", + "\n", + "download_dir = '/tmp'\n", + "\n", + "train_dataset = torchvision.datasets.MNIST(root=f'{download_dir}/.data',\n", + " train=True,\n", + " transform=transforms.ToTensor(),\n", + " download=True)\n", + "train_label = torch.tensor([example[1] for example in train_dataset])\n", + "train_tensor = torch.stack([example[0] for example in train_dataset])[:, 0][:, None, ...]\n", + "labels = [str(example[1]) for example in train_dataset]\n", + "X = train_tensor\n", + "X = X.reshape(-1,28*28)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "application/json": { + "ascii": false, + "bar_format": null, + "colour": null, + "elapsed": 0.061779022216796875, + "initial": 0, + "n": 0, + "ncols": null, + "nrows": null, + "postfix": null, + "prefix": "", + "rate": null, + "total": 7103, + "unit": "it", + "unit_divisor": 1000, + "unit_scale": false + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "8dd3148a55c64684a0d4f490f9e38a1b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/7103 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Visualise the results (requires matplotlib)\n", + "\n", + "import matplotlib.pyplot as plt\n", + "\n", + "with plt.style.context('seaborn-paper'):\n", + " fig,ax = plt.subplots(figsize=(8,8))\n", + " int_labels = [int(y) for y in labels]\n", + " scatter = ax.scatter(*U.T,\n", + " c=int_labels,\n", + " s=0.5,\n", + " cmap=plt.cm.tab10)\n", + " legend = ax.legend(*scatter.legend_elements(),\n", + " loc=\"lower left\", title=\"Classes\")\n", + " ax.add_artist(legend)\n", + " plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# We can define our own Encoder, in this example we use\n", + "# a CNN to encode the MNIST digits.\n", + "\n", + "# We can also define a decoder to perform inversion and map from the 2D\n", + "# UMAP projection back to a digit. To do this, we use a simple CNN as a decoder.\n", + "\n", + "import torch.nn as nn\n", + "\n", + "class ConvEncoder(nn.Module):\n", + " '''Simple CNN to encode 28*28 images into 2d vectors'''\n", + " def __init__(self):\n", + " super().__init__()\n", + "\n", + " self.encoder = nn.Sequential(\n", + " nn.Conv2d(\n", + " in_channels=1, out_channels=64, kernel_size=3, stride=2, padding=0,\n", + " ),\n", + " nn.PReLU(),\n", + " nn.Conv2d(\n", + " in_channels=64, out_channels=128, kernel_size=3, stride=2, padding=0,\n", + " ),\n", + " nn.Flatten(),\n", + " nn.Linear(128*6*6, 128),\n", + " nn.PReLU(),\n", + " nn.Linear(128, 64),\n", + " nn.PReLU(),\n", + " nn.Linear(64, 2)\n", + " )\n", + " def forward(self, X):\n", + " return self.encoder(X)\n", + " \n", + "class ConvDecoder(nn.Module):\n", + " ''' Simple CNN to decode 2d vectors into 28*28 images'''\n", + " def __init__(self):\n", + " super().__init__()\n", + "\n", + " self.decoder = nn.Sequential(\n", + " nn.Linear(2, 32),\n", + " nn.PReLU(),\n", + " nn.Linear(32, 128),\n", + " nn.PReLU(),\n", + " nn.Linear(128, 64*7*7),\n", + " nn.Unflatten(dim=-1,unflattened_size=(64,7,7)),\n", + " nn.ConvTranspose2d(64, 32, \n", + " kernel_size=3, \n", + " stride=2, \n", + " padding=1, \n", + " output_padding=1),\n", + " nn.PReLU(),\n", + " nn.ConvTranspose2d(32, 1, \n", + " kernel_size=3, \n", + " stride=2, \n", + " padding=1, \n", + " output_padding=1),\n", + " nn.Sigmoid()\n", + " )\n", + " def forward(self, X):\n", + " return self.decoder(X)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "application/json": { + "ascii": false, + "bar_format": null, + "colour": null, + "elapsed": 0.031854867935180664, + "initial": 0, + "n": 0, + "ncols": null, + "nrows": null, + "postfix": null, + "prefix": "", + "rate": null, + "total": 7103, + "unit": "it", + "unit_divisor": 1000, + "unit_scale": false + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "647e954df93e4de9ac768be8915204a5", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/7103 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Visualise the results (requires matplotlib)\n", + "\n", + "import matplotlib.pyplot as plt\n", + "\n", + "with plt.style.context('seaborn-paper'):\n", + " fig,ax = plt.subplots(figsize=(8,8))\n", + " int_labels = [int(y) for y in labels]\n", + " scatter = ax.scatter(*U.T,\n", + " c=int_labels,\n", + " s=0.5,\n", + " cmap=plt.cm.tab10)\n", + " legend = ax.legend(*scatter.legend_elements(),\n", + " loc=\"lower left\", title=\"Classes\")\n", + " ax.add_artist(legend)\n", + " plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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qa+vh9vt7vM5AMwnW+qvtaojmjl3lkvdvrBmpASDQVy8eBEulwsOhyN0YUCv19ulixRGE7seu+nHrZRCfX09MjMzERPTt5/e/3E+1dfXG/289oTpr6meMPnYu5pL/e6MLCYmRrwuU3JysnH/CVax67GbfNx9fSZ23R/nk+MP39Ex+XntKR67ecfelVxiswcRERmNhYyIiIzW7wuZ0+nEc889Z8ur3tr12O163JFm5+eVxx7dx97vmj2IiIjC0e/PyIiIiDrCQkZEREZjISMiIqOxkBERkdH6dSF76aWXkJOTgwEDBmD8+PF47733+npKltu3bx/mzJmDzMxMOBwOvP766222K6VQWFiIzMxMJCYmIjc3F8ePH++byVqsqKgIEydOhMvlQlpaGubOnYtTp061uU80H39vskMuAfbNJ7vnUr8tZNu3b8eyZcuwYsUKHDlyBNOmTUN+fj7Onz/f11OzVENDA8aMGYP169eL29esWYO1a9di/fr1OHToEDweD2bNmhVaQ89kZWVlWLx4MQ4ePIiSkhIEAgHk5eWhoaEhdJ9oPv7eYpdcAuybT7bPJdVP3XXXXerxxx9vE7v99tvVM88800czijwAaufOnaGfg8Gg8ng86oUXXgjFmpqalNvtVj/+8Y/7YIaRVV1drQCosrIypZT9jj9S7JhLStk7n+yWS/3yjKy5uRnl5eXIy8trE8/Ly8OBAwf6aFa9r6KiAlVVVW2eB6fTiRkzZkTl8+Dz+QAAKSkpAOx3/JHAXPqCnV5PdsulflnIampq0NraivT09Dbx9PR0VFVV9dGset/1Y7XD86CUwvLlyzF16lSMHj0agL2OP1KYS1+wy+vJjrnU71a//2PXV+u+TinVLmYHdngelixZgqNHj2L//v3tttnh+CONz+EXov25sGMu9cszstTUVMTGxrZ7p1BdXd3uHUU083g8ABD1z8PSpUuxa9cu7N27t80lfOxy/JHEXPqCHV5Pds2lflnIEhISMH78eJSUlLSJl5SUYMqUKX00q96Xk5MDj8fT5nlobm5GWVlZVDwPSiksWbIEO3bswLvvvoucnJw226P9+HsDc+kL0fx6sn0u9VWXSWe2bdum4uPj1csvv6xOnDihli1bppKSktTZs2f7emqWqq+vV0eOHFFHjhxRANTatWvVkSNH1Llz55RSSr3wwgvK7XarHTt2qGPHjqmHHnpIZWRkqLq6uj6eec898cQTyu12q9LSUlVZWRm6Xbt2LXSfaD7+3mKXXFLKvvlk91zqt4VMKaU2bNigsrOzVUJCgho3blyolTSa7N27VwFod1u4cKFS6vO22eeee055PB7ldDrV9OnT1bFjx/p20haRjhuAKi4uDt0nmo+/N9khl5Sybz7ZPZd4GRciIjJav/wbGRERUVexkBERkdFYyIiIyGgsZEREZDQWMiIiMhoLGRERGY2FjIiIjMZCRkRERmMhIyIio7GQERGR0VjIiIjIaCxkRERkNBYyIiIyGgsZEREZjYWMiIiMxkJGRERGYyEjIiKjsZAREZHRWMiIiMhoLGRERGQ0FjIiIjIaCxkRERmNhYyIiIzGQkZEREZjISMiIqOxkBERkdFYyIiIyGgsZEREZDQWMiIiMhoLGRERGY2FjIiIjMZCRkRERmMhIyIio7GQERGR0VjIiIjIaCxkRERkNBYyIiIyGgsZEREZjYWMiIiMxkJGRERGi4vUwC+99BJefPFFVFZWYtSoUVi3bh2mTZvW6eOCwSAuXrwIl8sFh8MRqekRRYxSCvX19cjMzERMTM/fK3Y3lwDmE5mty7mkImDbtm0qPj5e/eQnP1EnTpxQ3/ve91RSUpI6d+5cp4/1er0KAG+8GX/zer19mkvMJ96i5dZZLjmUUgoWmzRpEsaNG4eNGzeGYnfccQfmzp2LoqKiDh/r8/kwePBgTMW3EYd4q6dGFHEBtGA/3kRtbS3cbnePxupJLgHMJzJbV3PJ8o8Wm5ubUV5ejmeeeaZNPC8vDwcOHGh3f7/fD7/fH/q5vr7+DxOLR5yDiUcG+sNbw55+lBduLgHMJ4oyXcwly5s9ampq0NraivT09Dbx9PR0VFVVtbt/UVER3G536JaVlWX1lIiMFG4uAcwnsqeIdS1+uYIqpcSqWlBQAJ/PF7p5vd5ITYnISF3NJYD5RPZk+UeLqampiI2NbfeOsbq6ut07SwBwOp1wOp1WT4PIeOHmEsB8Inuy/IwsISEB48ePR0lJSZt4SUkJpkyZYvXuiKIWc4moayLyPbLly5fjO9/5DiZMmIC7774bmzZtwvnz5/H4449HYndEUYu5RNS5iBSyBQsW4PLly3j++edRWVmJ0aNH480330R2dnYkdkcUtZhLRJ2LyPfIeqKurg5utxu5uJftwmSkgGpBKd6Az+dDcnJyn86F+UQm62ouca1FIiIyGgsZEREZjYWMiIiMxkJGRERGYyEjIiKjsZAREZHRWMiIiMhoLGRERGQ0FjIiIjIaCxkRERmNhYyIiIzGQkZEREZjISMiIqOxkBERkdEicj0y6h+qn5SvIhx06h/z66fWi/FYh/yeZ/ZHf6Id69KO8K6ZlfGfJ7XbWq9cCWssIgBwxCd0sE3+9edwDZIfkCzHW9L1lxcJJGn20SpfPcsR1A6FmOZWMR5X2yTf/0qddqzW6hoxrlrlfSCoifcTPCMjIiKjsZAREZHRWMiIiMhoLGRERGQ0FjIiIjKa5YWssLAQDoejzc3j8Vi9G6Kox1wi6pqItN+PGjUK77zzTujn2NjYSOzGdmKHDRPjZ/72VjH+4XfXifF4h/7/Q9f9G1Ry++3/3LZLOxYK9Jskt931qHbbiO/as/2eudQ1jjj5V1lMUqL+MUMGi/G6r8lvFuqy5efeP0Q/r8BAuc0+6JQzLTbVrx0rGHSIceVzi3H3iRTtWBnvJsn7+OS8vA9//26/j0ghi4uL4ztHIgswl4g6F5G/kZ0+fRqZmZnIycnBgw8+iE8++SQSuyGKeswlos5ZfkY2adIkbNmyBSNHjsSlS5fwwx/+EFOmTMHx48cxdOjQdvf3+/3w+784na6r038bnchOws0lgPlE9mT5GVl+fj7mz5+PO++8E9/85jexe/duAMDmzZvF+xcVFcHtdoduWVlZVk+JyEjh5hLAfCJ7inj7fVJSEu68806cPn1a3F5QUACfzxe6eb3eSE+JyEid5RLAfCJ7iviiwX6/HydPnsS0adPE7U6nE05nB6vY2kzTn96l3faTDf9HjOfEDdA8wqwOt1m36xcNPtt70+i3OsslwAb5FCO/pmNcLjEeuONG7VC/nyF37mGCTwxnDakV43Ex+pV+b0ySu21jIHcz3jrwknasq61yng+JaxDj/9/IcdqxTmdlivFbtsuLLMec/Fg7VrBJXrS4N1l+RvbUU0+hrKwMFRUV+OCDD3D//fejrq4OCxcutHpXRFGNuUTUNZafkV24cAEPPfQQampqMGzYMEyePBkHDx5EdnZ4l/QgsjvmElHXWF7Itm3bZvWQRLbEXCLqGq61SERERmMhIyIio0W8a5Fkuu7EFeuKtY/RdydapyIgdyDNefUpMf7bRest2/ffpb+j3Tbv+38vxm948QP5Af380uzUAU1nIgDEDpXXD2wcJ//d8Ny39b/iZk/5tRj/apL8lYVhcfVi/GKLfrHFFiUfSyvkdRM70qrk845hcfKX3udkHNOOdSz3MzH+fvNoMX5L1WDtWMEqTaelkjszO+T48vPigKbBsw2ekRERkdFYyIiIyGgsZEREZDQWMiIiMhoLGRERGY2FjIiIjMb2+z5y/+o9YnxmYuQX4LymmrXb/qJQbrO/ZdcpMX570mLtWC/P2STGvz6gRYx39PWCI3/zb2L83v+YLcZbP/1UOxb1D444+ddPzMCB2scEb5Kvln3uYXnh3mfu2qUd6+uJ8kK43sBgMf57TZv9B74c7T6S4vxi3Nsgj3WlKVE71oC4gBg/65KvTXd7UpV2rBsT5fb7X46UFyBuGKe/HNCAd+SFkZVfPvYOOb58bhXD9nsiIop+LGRERGQ0FjIiIjIaCxkRERmNhYyIiIzGrkULdNRl9btVXxXjDyX/i+YR+s493YK+W65MFuO/elK+1HlMs35B3ZTD74tx3SNuXXZQO9Y/7H9UjO/90Uvax4Tr9I9uEOM3P8yuxf5O17UYuPNm7WPOPOgU4y/e/ZoYnzTgonasS60JYrzEN0qMv3X2DjHuP+vS7sMRkBcHjrumWTS4gw695sFyZ+b5NHkh5Yab5OMDgD9N/Y0YX3pnmRh/aeKfaMe6+egwMR7wXtA+RuvLi32rri3+zTMyIiIyGgsZEREZjYWMiIiMxkJGRERGYyEjIiKjhd21uG/fPrz44osoLy9HZWUldu7ciblz54a2K6WwcuVKbNq0CVeuXMGkSZOwYcMGjBoldwJFg9Mrx2i3ffTAes0WfXeizv0/+nsxnrH2gBh3QO5M6sYFyLsl6UJjxPdxc3pNxPcRKXbJJUe83D3nyB4uxn8/Xd8F/Df3vCnG/yxJXu/vvLw8IQBg8+Wvi/H/OTRWjA8+Lv+6HFKl76xLqJMnENMsdyA6AnIcAFpc8WLcd4u8PuMhv34NyG8NPS7GxySeE+PxX5OfXwBoGS6v9ejoTtdiN4V9RtbQ0IAxY8Zg/Xr5F/SaNWuwdu1arF+/HocOHYLH48GsWbNQX1/f48kSRRPmEpE1wj4jy8/PR35+vrhNKYV169ZhxYoVmDdvHgBg8+bNSE9Px9atW/HYY4/1bLZEUYS5RGQNS/9GVlFRgaqqKuTl5YViTqcTM2bMwIED8sdffr8fdXV1bW5EdtedXAKYT2RPlhayqqrPr3+Tnp7eJp6enh7a9mVFRUVwu92hW1aW/ro3RHbRnVwCmE9kTxHpWnQ42i6/opRqF7uuoKAAPp8vdPN6vZGYEpGRwsklgPlE9mTpWosez+dXb62qqkJGRkYoXl1d3e6d5XVOpxNOp7x+GpFddSeXAOYT2ZOlhSwnJwcejwclJSUYO/bzFtbm5maUlZVh9erVVu6qT8SOvEWMP5m/x7J9bKmTF8EFgKzXPhHjHXQYR73b3JfE+Mc52drHBCrkFuP+xLhcionVb0oeJMZrx6aK8RGzP9aONWfQb8V4EHLxLmm4TTvWz9+dIMazy+SMcn4qd4vGVfu0+0CLPJa6pvlqitK338drFllOvJgmxgMDh2jH+nic/JjcgWfE+PQb5N89AHAsVf760QDda+LLCwP/sXafNji69H2hsAvZ1atXcebMFwdbUVGBDz/8ECkpKbjxxhuxbNkyrFq1CiNGjMCIESOwatUqDBw4EA8//HC4uyKKaswlImuEXcgOHz6MmTNnhn5evnw5AGDhwoV45ZVX8PTTT6OxsRFPPvlk6Eucb7/9Nlwu/aUOiOyIuURkjbALWW5uLpTSn+s5HA4UFhaisLCwJ/MiinrMJSJrcK1FIiIyGgsZEREZzdKuxWgRO9gtxm/+T3kRzKVDToe9j50N8uXJt/+vb2kf46iUFwHur2Ir5C/ufvujuWL8zdtfD3sf/5JxUIzflb9U+5i0l/p/16JpYhLkBW0BQGV5xHjVFPlj1Rezfq4dyx0jf4duf5O8CPfqfX+iHevG9+TuuYG/uyw/oFbuWgw2NWn3oZqb5Q2t8r5VsIOPmuPlX9cxVXJ34CCv/HsMAM40DBPjn7rlBYhvG6j/Ev6vho4T44ma/6sOGjO7jWdkRERkNBYyIiIyGgsZEREZjYWMiIiMxkJGRERGY9eioPG/Bovx/5O5w7J9FPxqnhi/9cARy/bR11ovVYvxmv+eIsYrn9GsPwcgI1bupqJ+Ikb/nrhuZLIYnzBO7vb9Sry+C7BW09W34tR9Yjxnh75FbsBvzovx4Ge12sdIVKClg41dWCiwq/vpYDeSax79/8nIQXJuxjjk5yveoV/RNahpWFWazswOffn56uLzxzMyIiIyGgsZEREZjYWMiIiMxkJGRERGYyEjIiKjsWtR4IyN/DWXR/xIc+XYiO+576W9dECMH1ymvzr2fUmfRWo6ZAFHUpJ2W81X5TX3HkuV1w6tDeo7Df/TJ1/V2f+mfMXjoSflzkQAaNV0J2q7EC3sQOwOR6y8pqJjgHx17Ibh+udxeIKcTwMccqfhx03y8wsAiTWa/Tg050mqG92MneAZGRERGY2FjIiIjMZCRkRERmMhIyIio7GQERGR0cIuZPv27cOcOXOQmZkJh8OB119/vc32RYsWweFwtLlNnjzZqvkSRQ3mEpE1wm6/b2howJgxY/DII49g/vz54n1mz56N4uLi0M8JCQndn2GExHztK9ptucMOWbafH9Z8VYzHXpYvmx75xn/qL6IllwBAZQzVbksZLy9QOy3xrBi/puQ2cwD4dW2WGHd/IrfMq3o5z4AOFrXt4zZ7LSW3ubemDZbvnubXDvWtpFNifLBm8efT9fr2+6SzV+X9B61vs9cJu5Dl5+cjPz+/w/s4nU54PJ5uT4rIDphLRNaIyN/ISktLkZaWhpEjR+LRRx9FdbX8joyIOsZcIuqc5St75Ofn44EHHkB2djYqKirw7LPP4p577kF5eTmczvbfQPf7/fD7vzgFrqurs3pKREYKN5cA5hPZk+WFbMGCBaF/jx49GhMmTEB2djZ2796NefPaX0yyqKgIK1eutHoaRMYLN5cA5hPZU8Tb7zMyMpCdnY3Tp+WrwRYUFMDn84VuXq830lMiMlJnuQQwn8ieIr5o8OXLl+H1epGRkSFudzqd2o9JIqlq6mDttuUpH1m2n1f3ThPjt35y0LJ99FcOzf/ruWfGi/FpA/Z3MFqiGP27SrkdPfPNC9qRTO0M7SyXgL7Lp5aUgdptMz2HxXi8vJYwqgKDtGMduyAvLH3LpWtiPNjYpB1L1wXYpxyaJwVA7LBUMX75NnnB5twRv9WO5YqR93OqRX7t/O7tW7Rj3XRe/n3Zez2L3ShkV69exZkzZ0I/V1RU4MMPP0RKSgpSUlJQWFiI+fPnIyMjA2fPnsUPfvADpKam4r777rN04kSmYy4RWSPsQnb48GHMnDkz9PPy5csBAAsXLsTGjRtx7NgxbNmyBbW1tcjIyMDMmTOxfft2uFwu62ZNFAWYS0TWCLuQ5ebmQnXwhcE9e/b0aEJEdsFcIrIG11okIiKjsZAREZHRIt61SPalxt4mxo/+9b9pHiF3JnbklC9d3nBWf4l7sl5goH59xJsG1IQ11mm/fkkuVTlAjDtam+UHxOl/xakWXf+qdd2Mjrh4MR6TKB8HOug4vTrhRjE++W/lrtB/SCvTjuWKkdfs3PqZ3AWc9Qt5PUUACNbpt/UWnpEREZHRWMiIiMhoLGRERGQ0FjIiIjIaCxkRERmNhYyIiIzG9nvqEt0CwLoWewCIf+HTSE0n5ONL8kKqt0C/aDBFgH6BEhyqyxHjN8XLr4+agH4JrtYkuTW+8QZ54dyBLcO1Y8X65ZZ9R0OjGA/W1YvxmOQOlgxzym3uzdlDxfilCfqvoIyc+zsxvi5Dbr8H5OcEAN66JufzL/57ohjPPnVSO1ZroEW7rbfwjIyIiIzGQkZEREZjISMiIqOxkBERkdFYyIiIyGjsWoywe+4+JsYvZsndVAFv5LvtdB2IAPC7F78mxlWSfOHy383+dyumBADY1TBEu+2ZnX8hxkc8f1SM98OL2Ee1BJ9m0V4ABy7cJMYnJ38sxuMd8msNAFKH14rxSxPk7tVB6SnasQbUyq+S+Kvy/mNa5Ps3eOTORABoyJTPFervkJ+vJyfrr0E3z/UbMV7T6hDjP2uQu0UB4B/fnCfGb3tDXuC5tbZWOxY6uKZeb+EZGRERGY2FjIiIjMZCRkRERmMhIyIio7GQERGR0cLqWiwqKsKOHTvw0UcfITExEVOmTMHq1atx221frLenlMLKlSuxadMmXLlyBZMmTcKGDRswatQoyyffE4mf6vvajjfLl0AflRB+k+dLw/eJ8Sf/v+li/PRKea2z7vjOv/yPGB8ce037mD9LOmDZ/sP1D7+5V7vt5u+/L8ZN7U6MplwCgHjvZe02deRGMV6WOVKMTx18RjvWt7OOi/F9zlvFeOWVZO1YtYFYMR7wyV2IgzOvivGUJLnTDwAe8PxWjA+KbRLjs5Pk9RQBoEXTHPj3v58txo9svVM71m0/rxLjwfO/lx/QW52Jji93YDo6XMfzurDOyMrKyrB48WIcPHgQJSUlCAQCyMvLQ0NDQ+g+a9aswdq1a7F+/XocOnQIHo8Hs2bNQn29vOAmkR0xl4isE9YpxltvvdXm5+LiYqSlpaG8vBzTp0+HUgrr1q3DihUrMG/e599T2Lx5M9LT07F161Y89thj1s2cyGDMJSLr9OhvZD6fDwCQkvL5lw4rKipQVVWFvLy80H2cTidmzJiBAwfkj6z8fj/q6ura3IjsxopcAphPZE/dLmRKKSxfvhxTp07F6NGjAQBVVZ9/7pqent7mvunp6aFtX1ZUVAS32x26ZWVldXdKREayKpcA5hPZU7cL2ZIlS3D06FG89tpr7bY5vvQHO6VUu9h1BQUF8Pl8oZvX6+3ulIiMZFUuAcwnsqdurbW4dOlS7Nq1C/v27cPw4V+sGejxeAB8/m4yIyMjFK+urm73zvI6p9MJZwdr/xFFMytzCWA+kT2FVciUUli6dCl27tyJ0tJS5OS0XZQyJycHHo8HJSUlGDt2LACgubkZZWVlWL16tXWztoBr+0HttmcekxfU/J/bdlm2f11bPn6iiRtoX5Pcxvw3L8uNCjn//pF2LP0ysmaKplwCgNbqT7Xbst6WF4N+78bbxPjEr5/TjjUwRl5s9y+HfyDGB2frv2pSHxwgxs/55QWIU+PlblF/MF67jykDT4vxy8EkMV7SIH+NAACKz00R44FX08R45s/1+RT0yX87VQH5q0e95stt/l1s+w+rkC1evBhbt27FG2+8AZfLFfqs3u12IzExEQ6HA8uWLcOqVaswYsQIjBgxAqtWrcLAgQPx8MMPh7MroqjGXCKyTliFbOPGjQCA3NzcNvHi4mIsWrQIAPD000+jsbERTz75ZOhLnG+//TZcLpclEyaKBswlIuuE/dFiZxwOBwoLC1FYWNjdORFFPeYSkXW41iIRERmNhYyIiIzWrfb7aBf7iFzf1+2WFzldNkS/0Kdpdl9zi/GN53PF+IV35AVhASB7k9w1NfyyvDJFtHUm2olqlrsJASDm5FkxfsOer4jxf0ueoR3r6zdViPF7h1aK8WGx+pVNbtCsRhvvkF+Jx6/dIMbLLuk7Dd9x3i7GK+vkxYxjd8kdngCQelRetNjxkbyQcrDJrx2rz7sTLcYzMiIiMhoLGRERGY2FjIiIjMZCRkRERmMhIyIio7FrURA4J68Y/ouFk8V4yqsNYhwAvpusuXR4L/j7qklivOzlu7SPGfyx3H2WsOewGB+OC9qx2IVoIx18wTt4Ve62cx+U88x5JUOMA8CR0XeK8dLb7xDjQ4b7tGO1tMaK8YZ6eQ3GgSfkuPtj/Su9oVF+XoY2yF2DCR/q10dEq7yf1gbNepIqqB8ryvCMjIiIjMZCRkRERmMhIyIio7GQERGR0VjIiIjIaCxkRERkNLbfh0GVy4tz/vQOj/YxP4V+W+TJLb5pkBftJYoITWt+oPKSGI+vuawdKvOYvNhu5hA5HnQP1M8rKLenO/xyO7ujRV6A2NHSwQK8zS36bdKUOlh8WYU5VkdfiYg2PCMjIiKjsZAREZHRWMiIiMhoLGRERGS0sApZUVERJk6cCJfLhbS0NMydOxenTp1qc59FixbB4XC0uU2eLK9RSGRXzCUi64TVtVhWVobFixdj4sSJCAQCWLFiBfLy8nDixAkkJSWF7jd79mwUFxeHfk5ISLBuxkRRgLkEICgvgqv8+kV4Wy9/Jm/49FMrZvT5/h2OMB/Qje5A3T5s1GlopbAK2VtvvdXm5+LiYqSlpaG8vBzTp08PxZ1OJzyevmw7J+rfmEtE1unR38h8vs8vkZCSktImXlpairS0NIwcORKPPvooqqure7IboqjHXCLqPodS3TuXVUrh3nvvxZUrV/Dee++F4tu3b8egQYOQnZ2NiooKPPvsswgEAigvL4fT6Ww3jt/vh9/vD/1cV1eHrKws5OJexDniuzM1oj4VUC0oxRvw+XxITpa/qPvHrMolwAb5FCNfQ0z3MWW38KPFfqOrudTtlT2WLFmCo0ePYv/+/W3iCxYsCP179OjRmDBhArKzs7F7927Mmzev3ThFRUVYuXJld6dBZDyrcglgPpE9deujxaVLl2LXrl3Yu3cvhg8f3uF9MzIykJ2djdOnT4vbCwoK4PP5QjevV75qLFE0sjKXAOYT2VNYZ2RKKSxduhQ7d+5EaWkpcnJyOn3M5cuX4fV6kZEhX77c6XRqPyYhilaRyCXABvlk5UeIOr3x8R4/QrRUWGdkixcvxquvvoqtW7fC5XKhqqoKVVVVaGxsBABcvXoVTz31FN5//32cPXsWpaWlmDNnDlJTU3HfffdF5ACITMRcIrJOWGdkGzduBADk5ua2iRcXF2PRokWIjY3FsWPHsGXLFtTW1iIjIwMzZ87E9u3b4XK5LJs0kemYS0TWCfujxY4kJiZiz549PZoQkR0wl4isw7UWiYjIaCxkRERkNBYyIiIyWre/EE1E1Ot6Y2UPMg7PyIiIyGgsZEREZDQWMiIiMhoLGRERGa3fNXtc/6JoAC0AlyMjAwXQAqDzLz33hqjLJxXUxNnsEY26mkv9rpDV19cDAPbjzT6eCVHP1NfXw+129/kcgCjKJ00do+jWWS51+8KakRIMBnHx4kW4XC44HI7QhQG9Xm+XLlIYTex67KYft1IK9fX1yMzMRExM3356/8f5VF9fb/Tz2hOmv6Z6wuRj72ou9bszspiYGPG6TMnJycb9J1jFrsdu8nH39ZnYdX+cT44/XJXY5Oe1p3js5h17V3KJzR5ERGQ0FjIiIjJavy9kTqcTzz33XHRf9VbDrsdu1+OONDs/rzz26D72ftfsQUREFI5+f0ZGRETUERYyIiIyGgsZEREZjYWMiIiM1q8L2UsvvYScnBwMGDAA48ePx3vvvdfXU7Lcvn37MGfOHGRmZsLhcOD1119vs10phcLCQmRmZiIxMRG5ubk4fvx430zWYkVFRZg4cSJcLhfS0tIwd+5cnDp1qs19ovn4e5Mdcgmwbz7ZPZf6bSHbvn07li1bhhUrVuDIkSOYNm0a8vPzcf78+b6emqUaGhowZswYrF+/Xty+Zs0arF27FuvXr8ehQ4fg8Xgwa9as0Bp6JisrK8PixYtx8OBBlJSUIBAIIC8vDw0NDaH7RPPx9xa75BJg33yyfS6pfuquu+5Sjz/+eJvY7bffrp555pk+mlHkAVA7d+4M/RwMBpXH41EvvPBCKNbU1KTcbrf68Y9/3AczjKzq6moFQJWVlSml7Hf8kWLHXFLK3vlkt1zql2dkzc3NKC8vR15eXpt4Xl4eDhw40Eez6n0VFRWoqqpq8zw4nU7MmDEjKp8Hn88HAEhJSQFgv+OPBObSF+z0erJbLvXLQlZTU4PW1lakp6e3iaenp6OqqqqPZtX7rh+rHZ4HpRSWL1+OqVOnYvTo0QDsdfyRwlz6gl1eT3bMpX63+v0fu75a93VKqXYxO7DD87BkyRIcPXoU+/fvb7fNDscfaXwOvxDtz4Udc6lfnpGlpqYiNja23TuF6urqdu8oopnH4wGAqH8eli5dil27dmHv3r1tLuFjl+OPJObSF+zwerJrLvXLQpaQkIDx48ejpKSkTbykpARTpkzpo1n1vpycHHg8njbPQ3NzM8rKyqLieVBKYcmSJdixYwfeffdd5OTktNke7cffG5hLX4jm15Ptc6mvukw6s23bNhUfH69efvlldeLECbVs2TKVlJSkzp4929dTs1R9fb06cuSIOnLkiAKg1q5dq44cOaLOnTunlFLqhRdeUG63W+3YsUMdO3ZMPfTQQyojI0PV1dX18cx77oknnlBut1uVlpaqysrK0O3atWuh+0Tz8fcWu+SSUvbNJ7vnUr8tZEoptWHDBpWdna0SEhLUuHHjQq2k0WTv3r0KQLvbwoULlVKft80+99xzyuPxKKfTqaZPn66OHTvWt5O2iHTcAFRxcXHoPtF8/L3JDrmklH3zye65xMu4EBGR0frl38iIiIi6ioWMiIiMxkJGRERGYyEjov+/vTogAQAAABD0/3U7Aj0hrIkMgDWRAbAmMgDWRAbAmsgAWBMZAGsiA2BNZACsBQt1QNPkGKrpAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# See how our decoder performs by inverting the encoding of training samples\n", + "from matplotlib import pyplot as plt \n", + "import numpy as np \n", + "\n", + "V = pumap.inverse_transform(U)\n", + "fig,axs = plt.subplots(5,2,figsize=(5,15))\n", + "axs=axs.flatten()\n", + "for i in range(5):\n", + " j = np.random.randint(len(X))\n", + " axs[2*i].imshow(X[j].reshape((28,28)))\n", + " axs[2*i+1].imshow(V[j].reshape((28,28)))\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pytorch", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/setup.py b/setup.py index 9e2a9354..7bba2acb 100644 --- a/setup.py +++ b/setup.py @@ -64,6 +64,7 @@ def readme(): "scikit-image", ], "parametric_umap": ["tensorflow >= 2.1"], + "torch_umap": ["torch"], "tbb": ["tbb >= 2019.0"], }, "ext_modules": [], diff --git a/umap/torch.py b/umap/torch.py new file mode 100644 index 00000000..1211a7b7 --- /dev/null +++ b/umap/torch.py @@ -0,0 +1,487 @@ +""" +Pytorch implimentation of ParametricUMAP +Borrows ideas/code from: + * https://github.com/lmcinnes/umap/issues/580 + * https://colab.research.google.com/drive/1CYxt0GD-Y2zPMOnJIXJWsAhr0LdqI0R6 +""" + +import numpy as np +from pynndescent import NNDescent +from sklearn.utils import check_random_state +from tqdm.auto import tqdm as tq +from warnings import warn + +from .umap_ import fuzzy_simplicial_set, find_ab_params + +try: + import torch + from torch.utils.data import Dataset, DataLoader + +except ImportError: + warn( + """The umap.torch package requires PyTorch to be installed. + You can install PyTorch at https://pytorch.org/ + + + """ + ) + raise ImportError("umap.torch requires torch") from None + + +def convert_distance_to_probability(distances, a=1.0, b=1.0): + return -torch.log1p(a * distances ** (2 * b)) + + +def compute_cross_entropy( + probabilities_graph, probabilities_distance, repulsion_strength=1.0 +): + # cross entropy + attraction_term = -probabilities_graph * torch.nn.functional.logsigmoid( + probabilities_distance + ) + repellant_term = ( + -(1.0 - probabilities_graph) + * ( + torch.nn.functional.logsigmoid(probabilities_distance) + - probabilities_distance + ) + * repulsion_strength + ) + + # balance the expected losses between attraction and repulsion + CE = attraction_term + repellant_term + return attraction_term, repellant_term, CE + + +def umap_loss(embedding_to, embedding_from, _a, _b, batch_size, negative_sample_rate=5): + # get negative samples by randomly shuffling the batch + embedding_neg_to = embedding_to.repeat(negative_sample_rate, 1) + repeat_neg = embedding_from.repeat(negative_sample_rate, 1) + embedding_neg_from = repeat_neg[torch.randperm(repeat_neg.shape[0])] + distance_embedding = torch.cat( + ( + (embedding_to - embedding_from).norm(dim=1), + (embedding_neg_to - embedding_neg_from).norm(dim=1), + ), + dim=0, + ) + + # convert probabilities to distances + probabilities_distance = convert_distance_to_probability(distance_embedding, _a, _b) + # set true probabilities based on negative sampling + probabilities_graph = torch.cat( + (torch.ones(batch_size), torch.zeros(batch_size * negative_sample_rate)), + dim=0, + ) + + # compute cross entropy + (attraction_loss, repellant_loss, ce_loss) = compute_cross_entropy( + probabilities_graph.cuda(), + probabilities_distance.cuda(), + ) + loss = torch.mean(ce_loss) + return loss + + +def get_umap_graph( + X, n_neighbors=10, metric="cosine", random_state=None, verbose=False +): + random_state = check_random_state(None) if random_state is None else random_state + # number of trees in random projection forest + n_trees = 5 + int(round((X.shape[0]) ** 0.5 / 20.0)) + # max number of nearest neighbor iters to perform + n_iters = max(5, int(round(np.log2(X.shape[0])))) + # distance metric + + # get nearest neighbors + nnd = NNDescent( + X.reshape((len(X), np.product(np.shape(X)[1:]))), + n_neighbors=n_neighbors, + metric=metric, + n_trees=n_trees, + n_iters=n_iters, + max_candidates=60, + verbose=verbose, + ) + # get indices and distances + knn_indices, knn_dists = nnd.neighbor_graph + + # get indices and distances + knn_indices, knn_dists = nnd.neighbor_graph + # build fuzzy_simplicial_set + umap_graph, sigmas, rhos = fuzzy_simplicial_set( + X=X, + n_neighbors=n_neighbors, + metric=metric, + random_state=random_state, + knn_indices=knn_indices, + knn_dists=knn_dists, + ) + + return umap_graph + + +def get_graph_elements(graph_, n_epochs): + + graph = graph_.tocoo() + # eliminate duplicate entries by summing them together + graph.sum_duplicates() + # number of vertices in dataset + n_vertices = graph.shape[1] + # get the number of epochs based on the size of the dataset + if n_epochs is None: + # For smaller datasets we can use more epochs + if graph.shape[0] <= 10_000: + n_epochs = 50 + else: + n_epochs = 20 + + # remove elements with very low probability + graph.data[graph.data < (graph.data.max() / float(n_epochs))] = 0.0 + graph.eliminate_zeros() + # get epochs per sample based upon edge probability + epochs_per_sample = n_epochs * graph.data + + head = graph.row + tail = graph.col + weight = graph.data + + return graph, epochs_per_sample, head, tail, weight, n_vertices + + +class UMAPDataset(Dataset): + """A dataset containing positive edges from the umap graph. + If data is provided, returns the data vectors, otherwise returns the indices + """ + + def __init__(self, graph_, data=None, n_epochs=None): + graph, epochs_per_sample, head, tail, weight, n_vertices = get_graph_elements( + graph_, n_epochs + ) + + self.edges_to_ix, self.edges_from_ix = ( + np.repeat(head, epochs_per_sample.astype("int")), + np.repeat(tail, epochs_per_sample.astype("int")), + ) + + if data is not None: + self.data = torch.Tensor(data) + else: + self.data = None + + def __len__(self): + return int(self.edges_to_ix.shape[0]) + + def __getitem__(self, index): + edges_to_ix = self.edges_to_ix[index] + edges_from_ix = self.edges_from_ix[index] + + if self.data is not None: + edges_to_exp = self.data[edges_to_ix] + edges_from_exp = self.data[edges_from_ix] + return edges_to_exp, edges_from_exp + else: + return edges_to_ix, edges_from_ix + + +class Encoder(torch.nn.Module): + """ + Default encoder for ParametricUmap class + """ + + def __init__( + self, + input_channels, + output_channels, + hidden_channels=128, + activation=torch.nn.LeakyReLU, + ): + super().__init__() + self.encoder = torch.nn.Sequential( + torch.nn.Linear(input_channels, hidden_channels), + activation(), + torch.nn.Linear(hidden_channels, hidden_channels), + activation(), + torch.nn.Linear(hidden_channels, output_channels), + ) + + def forward(self, X): + return self.encoder(X) + + +class ParametricUMAP: + def __init__( + self, + n_components=2, + n_neighbors=15, + metric="euclidean", + n_training_epochs=1, + n_epochs=None, + negative_sample_rate=5, + lr=1e-3, + min_dist=0.1, + encoder=None, + decoder=None, + beta=1, + batch_size=1024, + num_workers=4, + random_state=None, + device=None, + verbose=True, + ): + """ + Parametric UMAP implimentation in PyTorch + + Parameters + ---------- + n_components: int (optional, default 2) + The dimension of the space to embed into. This defaults to 2 to + provide easy visualization, but can reasonably be set to any + integer value in the range 2 to 100. + + n_neighbors: float (optional, default 15) + The size of local neighborhood (in terms of number of neighboring + sample points) used for manifold approximation. Larger values + result in more global views of the manifold, while smaller + values result in more local data being preserved. In general + values should be in the range 2 to 100. + + metric: string or function (optional, default 'euclidean') + The metric to use to compute distances in high dimensional space. + If a string is passed it must match a valid predefined metric. If + a general metric is required a function that takes two 1d arrays and + returns a float can be provided. For performance purposes it is + required that this be a numba jit'd function. Valid string metrics + include: + + * euclidean + * manhattan + * chebyshev + * minkowski + * canberra + * braycurtis + * mahalanobis + * wminkowski + * seuclidean + * cosine + * correlation + * haversine + * hamming + * jaccard + * dice + * russelrao + * kulsinski + * ll_dirichlet + * hellinger + * rogerstanimoto + * sokalmichener + * sokalsneath + * yule + + TODO: The torch implimentation currently does not support additional + arguments that should be passed to the metric (e.g. minkowski, + mahalanobis etc.) + + n_training_epochs: int (optional, default 1) + The number of training epochs to be used in optimizing the + low dimensional embedding. Corresponds to the number of times + we optimisze over the training dataloader. + + n_epochs: int (optional, default None) + The number of epochs used in constructing the UMAP dataset. + The highest probability edge in the umap graph will appear in + the train dataset n_epochs times. edges with lower probability + are represented proportionally. A larger value will result in + a larger, but more accurate dataset. + Defaults to 50 for small datasets, 20 for large. + + negative_sample_rate: int (optional, default 5) + The number of negative samples to select per positive sample + in the optimization process. Increasing this value will result + in greater repulsive force being applied, greater optimization + cost, but slightly more accuracy. + + lr: float (optional, default 1e-3) + The learning rate for the embedding optimization. + Passed to the torch optimizer. + + min_dist: float (optional, default 0.1) + The effective minimum distance between embedded points. Smaller values + will result in a more clustered/clumped embedding where nearby points + on the manifold are drawn closer together, while larger values will + result on a more even dispersal of points. The value should be set + relative to the ``spread`` value, which determines the scale at which + embedded points will be spread out. + + encoder: torch.nn.Module (optional, default None) + An encoder which takes items from your data and maps them to + vectors of size n_components. Defaults to a standard multi-layer + encoder model (3 linear layers with LeakyReLU activation). + + decoder: torch.nn.Module (optional, default None) + A decoder for inverting vectors of shape n_components, returning + vectors shaped like the input data. Default is none, meaning that + we do not train a decoder. + + beta: float (optional, default 1) + The contribution of the decoder loss to the total loss. Total loss + is given by umap_loss + beta * decoder_loss. Increasing/decreasing + this will prioritise decoder loss over umap loss and vice versa. + + batch_size: int (optional, default 1024) + Training batch size. 1024 is a sensible default for medium-large datasets. + + num_workers: int (optional, default 4) + Number of workers used to manage the training dataloader. + Defaults to 4, but performance may be boosted by increasing this for + large datasets on machines with many cores. + + random_state: int or instance of RandomState (optional, default None) + controls the random_state which is used in creating the umap graph. + Setting this seed does not guarantee reproducability since it is + not passed through to the torch modules. + + device: str, 'cpu' or 'cuda' (optional, default None) + Controls the device on which we train the umap model. Set to 'cpu' + for cpu training, or 'cuda' for gpu training. Default behaviour is + to search for the active device via torch.cuda.is_available(). + + verbose: bool (optional, default True) + Controls whether we have progress bars during training. + + """ + self.n_components = n_components + self.encoder = encoder + self.decoder = decoder + self.n_neighbors = n_neighbors + self.min_dist = min_dist + self.beta = beta + self.metric = metric + self.lr = lr + self.n_training_epochs = n_training_epochs + self.n_epochs = n_epochs + self.negative_sample_rate = negative_sample_rate + self.batch_size = batch_size + self.num_workers = num_workers + self.random_state = random_state + self._a, self._b = find_ab_params(1.0, self.min_dist) + + if device is None: + self.device = "cuda" if torch.cuda.is_available() else "cpu" + else: + self.device = device + + self.verbose = verbose + + def fit(self, X): + + if type(X) is np.ndarray: + X = torch.from_numpy(X).float() + + assert isinstance(X, torch.Tensor) + + if self.encoder is None: + self.encoder = Encoder( + input_channels=X.shape[-1], output_channels=self.n_components + ) + + # Move encoder/decoder to correct device + self.encoder.to(self.device) + if self.decoder is not None: + self.decoder.to(self.device) + + graph = get_umap_graph( + X, + n_neighbors=self.n_neighbors, + metric=self.metric, + random_state=self.random_state, + ) + + dataset = UMAPDataset(graph, data=X, n_epochs=self.n_epochs) + + dataloader = DataLoader( + dataset=dataset, + batch_size=self.batch_size, + num_workers=self.num_workers, + shuffle=True, + ) + + # Don't forget to add decoder to optimizer if it is present + if self.decoder is None: + optimizer = torch.optim.AdamW(self.encoder.parameters(), lr=self.lr) + else: + optimizer = torch.optim.AdamW( + (*self.encoder.parameters(), *self.decoder.parameters()), lr=self.lr + ) + + # Use tqdm for nice loading bars if verbose flag set + # otherwise run silently + if self.verbose: + + def wrapper(loader): + return tq(enumerate(loader), total=len(loader), leave=False) + + else: + + def wrapper(loader): + return enumerate(loader) + + for epoch in range(self.n_training_epochs): + + for ib, batch in (batch_pbar := wrapper(dataloader)): + + total_loss = 0 + + edges_to_exp, edges_from_exp = batch + edges_to_exp = edges_to_exp.to(self.device) + edges_from_exp = edges_from_exp.to(self.device) + + embedding_to = self.encoder(edges_to_exp) + embedding_from = self.encoder(edges_from_exp) + + encoder_loss = umap_loss( + embedding_to, + embedding_from, + self._a, + self._b, + edges_to_exp.shape[0], + negative_sample_rate=self.negative_sample_rate, + ) + + total_loss += encoder_loss + + if self.decoder is not None: + recon = self.decoder(embedding_to) + recon_loss = torch.nn.functional.mse_loss(recon, edges_to_exp) + total_loss += self.beta * recon_loss + + total_loss.backward() + optimizer.step() + optimizer.zero_grad() + + if self.verbose: + desc = f"Batch: {ib} Training loss: {total_loss.item():5.3f}" + if self.decoder is not None: + desc += f" | Umap loss: {encoder_loss.item():5.3f}" + desc += f" | Reconstruction loss: {recon_loss.item():5.3f}" + batch_pbar.set_description(desc) + + def fit_transform(self, X): + self.fit(X) + return self.transform(X) + + @torch.no_grad() + def transform(self, X): + if type(X) is np.ndarray: + X = torch.from_numpy(X).float() + self.embedding_ = self.encoder(X.to(self.device)).detach().cpu().numpy() + return self.embedding_ + + @torch.no_grad() + def inverse_transform(self, Z): + assert ( + self.decoder is not None + ), "No inverse_transform available, decoder is None." + if type(Z) is np.ndarray: + Z = torch.from_numpy(Z).float() + return self.decoder(Z.to(self.device)).detach().cpu().numpy()