diff --git a/notebooks/ivf_flat_example.ipynb b/notebooks/ivf_flat_example.ipynb index 4fd6dde1cd..6e9ff937f4 100644 --- a/notebooks/ivf_flat_example.ipynb +++ b/notebooks/ivf_flat_example.ipynb @@ -15,12 +15,13 @@ "source": [ "## Introduction\n", "\n", - "This notebook demonstrates how to run approximate nearest neighbor search using RAFT IVF-Flat algorithm." + "This notebook demonstrates how to run approximate nearest neighbor search using RAFT IVF-Flat algorithm.\n", + "It builds and searches an index using a dataset from the ann-benchmarks million-scale datasets, saves/loads the index to disk, and explores important parameters for fine-tuning the search performance and accuracy of the index." ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 15, "id": "fe73ada7-7b7f-4005-9440-85428194311b", "metadata": {}, "outputs": [], @@ -30,11 +31,9 @@ "import numpy as np\n", "from pylibraft.common import DeviceResources\n", "from pylibraft.neighbors import ivf_flat\n", - "import time\n", "import matplotlib.pyplot as plt\n", - "import h5py\n", "import tempfile\n", - "import urllib.request" + "from utils import BenchmarkTimer, calc_recall, load_dataset" ] }, { @@ -80,7 +79,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Mon Sep 18 03:01:31 2023 \n", + "Thu Sep 21 02:30:53 2023 \n", "+---------------------------------------------------------------------------------------+\n", "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", "|-----------------------------------------+----------------------+----------------------+\n", @@ -88,9 +87,9 @@ "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", "| | | MIG M. |\n", "|=========================================+======================+======================|\n", - "| 0 NVIDIA A10 On | 00000000:81:00.0 Off | 0 |\n", - "| 0% 37C P0 56W / 150W | 1264MiB / 23028MiB | 0% Default |\n", - "| | | N/A |\n", + "| 0 NVIDIA H100 PCIe On | 00000000:41:00.0 Off | 0 |\n", + "| N/A 35C P0 69W / 350W | 1487MiB / 81559MiB | 0% Default |\n", + "| | | Disabled |\n", "+-----------------------------------------+----------------------+----------------------+\n", " \n", "+---------------------------------------------------------------------------------------+\n", @@ -98,7 +97,7 @@ "| GPU GI CI PID Type Process name GPU Memory |\n", "| ID ID Usage |\n", "|=======================================================================================|\n", - "| 0 N/A N/A 12573 C /opt/conda/envs/rapids/bin/python 1252MiB |\n", + "| 0 N/A N/A 3940 C /opt/conda/envs/rapids/bin/python 1474MiB |\n", "+---------------------------------------------------------------------------------------+\n" ] } @@ -108,61 +107,6 @@ "!nvidia-smi" ] }, - { - "cell_type": "markdown", - "id": "104ef64f-7d98-4450-b04b-fcf498099b4b", - "metadata": {}, - "source": [ - "### Utility functions" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "496fc8a6-139f-4b88-a2f4-a34357fd1712", - "metadata": {}, - "outputs": [], - "source": [ - "def calc_recall(ann_idx, true_nn_idx):\n", - " k = ann_idx.shape[1]\n", - " if k > true_nn_idx.shape[1]:\n", - " raise RuntimeError(\n", - " \"Incompatible shapes {} vs {}\".format(ann_idx.shape, true_nn_idx.shape)\n", - " )\n", - " \n", - " n = 0\n", - " for i in range(ann_idx.shape[0]):\n", - " n += cp.intersect1d(ann_idx[i, :], true_nn_idx[i, :k]).size\n", - " recall = n / ann_idx.size\n", - " return recall\n", - "\n", - "class BenchmarkTimer:\n", - " \"\"\"Provides a context manager that runs a code block `reps` times\n", - " and records results to the instance variable `timings`. Use like:\n", - " .. code-block:: python\n", - " timer = BenchmarkTimer(rep=5)\n", - " for _ in timer.benchmark_runs():\n", - " ... do something ...\n", - " print(np.min(timer.timings))\n", - "\n", - " This class is borrowed from the rapids/cuml benchmark suite\n", - " \"\"\"\n", - "\n", - " def __init__(self, reps=1, warmup=0):\n", - " self.warmup = warmup\n", - " self.reps = reps\n", - " self.timings = []\n", - "\n", - " def benchmark_runs(self):\n", - " for r in range(self.reps + self.warmup):\n", - " t0 = time.time()\n", - " yield r\n", - " t1 = time.time()\n", - " self.timings.append(t1 - t0)\n", - " if r >= self.warmup:\n", - " self.timings.append(t1 - t0)" - ] - }, { "cell_type": "markdown", "id": "88a654cc-6389-4526-a3e6-826de5606a09", @@ -177,7 +121,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 16, "id": "5f529ad6-b0bd-495c-bf7c-43f10fb6aa14", "metadata": {}, "outputs": [ @@ -185,31 +129,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "The index and data will be saved in /tmp/raft_ivf_flat_example\n" + "The index and data will be saved in /tmp/raft_example\n" ] } ], "source": [ - "#DATASET_URL = \"http://ann-benchmarks.com/glove-100-angular.hdf5\"\n", - "DATASET_URL = \"http://ann-benchmarks.com/sift-128-euclidean.hdf5\"\n", - "DATASET_FILENAME = DATASET_URL.split('/')[-1]\n", - "\n", - "# We'll need to load store some data in this tutorial\n", - "WORK_FOLDER = os.path.join(tempfile.gettempdir(), 'raft_ivf_flat_example')\n", - "\n", - "if not os.path.exists(WORK_FOLDER):\n", - " os.makedirs(WORK_FOLDER)\n", - "print(\"The index and data will be saved in\", WORK_FOLDER)\n", - "\n", - "## download the dataset\n", - "dataset_path = os.path.join(WORK_FOLDER, DATASET_FILENAME)\n", - "if not os.path.exists(dataset_path):\n", - " urllib.request.urlretrieve(DATASET_URL, dataset_path)" + "WORK_FOLDER = os.path.join(tempfile.gettempdir(), \"raft_example\")\n", + "f = load_dataset(\"http://ann-benchmarks.com/sift-128-euclidean.hdf5\", work_folder=WORK_FOLDER)" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "3d68a7db-bcf4-449c-96c3-1e8ab146c84d", "metadata": {}, "outputs": [ @@ -223,8 +154,6 @@ } ], "source": [ - "f = h5py.File(dataset_path, \"r\")\n", - "\n", "metric = f.attrs['distance']\n", "\n", "dataset = cp.array(f['train'])\n", @@ -243,7 +172,8 @@ "id": "9f463c50-d1d3-49be-bcfe-952602efa603", "metadata": {}, "source": [ - "## Build index" + "## Build index\n", + "We set [IndexParams](https://docs.rapids.ai/api/raft/nightly/pylibraft_api/neighbors/#pylibraft.neighbors.ivf_flat.IndexParams) and build the index. The index parameters will be discussed in more detail in later sections of this notebook." ] }, { @@ -256,8 +186,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 189 ms, sys: 32.3 ms, total: 222 ms\n", - "Wall time: 226 ms\n" + "CPU times: user 120 ms, sys: 5.33 ms, total: 125 ms\n", + "Wall time: 124 ms\n" ] } ], @@ -313,7 +243,7 @@ "id": "89ba2eaa-4c85-4e1c-b07c-920394e55dce", "metadata": {}, "source": [ - "It is recommended to reuse devece recosources across multiple invacations of search. " + "It is recommended to reuse [device recosources](https://docs.rapids.ai/api/raft/nightly/pylibraft_api/common/#pylibraft.common.DeviceResources) across multiple invocations of search, since constructing these can be time consuming. We will reuse the resources by passing the same handle to each RAFT API call." ] }, { @@ -326,9 +256,17 @@ "handle = DeviceResources()" ] }, + { + "cell_type": "markdown", + "id": "a6365229-18fd-468f-af30-e24b950cbd6e", + "metadata": {}, + "source": [ + "After setting [SearchParams](https://docs.rapids.ai/api/raft/nightly/pylibraft_api/neighbors/#pylibraft.neighbors.ivf_flat.SearchParams) we search for for `k=10` neighbors." + ] + }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "id": "595454e1-7240-4b43-9a73-963d5670b00c", "metadata": {}, "outputs": [ @@ -336,8 +274,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 165 ms, sys: 141 ms, total: 306 ms\n", - "Wall time: 303 ms\n" + "CPU times: user 171 ms, sys: 52.6 ms, total: 224 ms\n", + "Wall time: 236 ms\n" ] } ], @@ -350,6 +288,7 @@ "# Search 10 nearest neighbors.\n", "distances, indices = ivf_flat.search(search_params, index, cp.asarray(queries[:n_queries,:]), k=10, handle=handle)\n", " \n", + "# RAFT calls are asyncronous (when handle arg is provided), we need to sync before accessing the results.\n", "handle.sync()\n", "distances, neighbors = cp.asnumpy(distances), cp.asnumpy(indices)" ] @@ -359,22 +298,22 @@ "id": "43d20ca7-7b9e-4046-bb52-640a2744db75", "metadata": {}, "source": [ - "The returnad arrays have shappe {n_queries x 10] and store the distance values and the indices of the searched vectors." + "The returned arrays have shape {n_queries x 10] and store the distance values and the indices of the searched vectors. We check how accurate the search is. The accuracy of the search is quantified as `recall`, which is a value between 0 and 1 and tells us what fraction of the returned neighbors are actual k nearest neighbors. " ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 11, "id": "8cd9cd20-ca00-4a35-a0a0-86636521b31a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "0.974" + "0.97406" ] }, - "execution_count": 12, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -389,12 +328,12 @@ "metadata": {}, "source": [ "## Save and load the index\n", - "You can serialize the index to file, and load it later." + "You can serialize the index to file using [save](https://docs.rapids.ai/api/raft/nightly/pylibraft_api/neighbors/#pylibraft.neighbors.ivf_flat.save), and [load](https://docs.rapids.ai/api/raft/nightly/pylibraft_api/neighbors/#pylibraft.neighbors.ivf_flat.load) it later." ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 17, "id": "bf94e45c-e7fb-4aa3-a611-ddaee7ac41ae", "metadata": {}, "outputs": [], @@ -405,7 +344,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 18, "id": "1622d9be-be41-4d25-be99-d348c5e54957", "metadata": {}, "outputs": [], @@ -426,7 +365,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 19, "id": "ace0c31f-af75-4352-a438-123a9a03612c", "metadata": {}, "outputs": [ @@ -436,44 +375,44 @@ "text": [ "\n", "Benchmarking search with n_probes = 10\n", - "recall 0.86509\n", - "Average search time: 0.067 +/- 0.0464 s\n", - "Queries per second (QPS): 148962\n", + "recall 0.86625\n", + "Average search time: 0.026 +/- 0.000259 s\n", + "Queries per second (QPS): 384968\n", "\n", "Benchmarking search with n_probes = 20\n", - "recall 0.94818\n", - "Average search time: 0.133 +/- 0.0932 s\n", - "Queries per second (QPS): 75407\n", + "recall 0.94705\n", + "Average search time: 0.050 +/- 5.43e-05 s\n", + "Queries per second (QPS): 198880\n", "\n", "Benchmarking search with n_probes = 30\n", - "recall 0.974\n", - "Average search time: 0.198 +/- 0.14 s\n", - "Queries per second (QPS): 50476\n", + "recall 0.97406\n", + "Average search time: 0.075 +/- 8.59e-05 s\n", + "Queries per second (QPS): 133954\n", "\n", "Benchmarking search with n_probes = 50\n", - "recall 0.99152\n", - "Average search time: 0.328 +/- 0.232 s\n", - "Queries per second (QPS): 30450\n", + "recall 0.99169\n", + "Average search time: 0.123 +/- 4.78e-05 s\n", + "Queries per second (QPS): 80997\n", "\n", "Benchmarking search with n_probes = 100\n", - "recall 0.99827\n", - "Average search time: 0.652 +/- 0.46 s\n", - "Queries per second (QPS): 15330\n", + "recall 0.99844\n", + "Average search time: 0.244 +/- 0.000249 s\n", + "Queries per second (QPS): 40934\n", "\n", "Benchmarking search with n_probes = 200\n", - "recall 0.99926\n", - "Average search time: 1.266 +/- 0.894 s\n", - "Queries per second (QPS): 7901\n", + "recall 0.99932\n", + "Average search time: 0.468 +/- 0.000367 s\n", + "Queries per second (QPS): 21382\n", "\n", "Benchmarking search with n_probes = 500\n", "recall 0.99933\n", - "Average search time: 2.881 +/- 2.04 s\n", - "Queries per second (QPS): 3471\n", + "Average search time: 1.039 +/- 0.000209 s\n", + "Queries per second (QPS): 9625\n", "\n", "Benchmarking search with n_probes = 1024\n", - "recall 0.99933\n", - "Average search time: 2.258 +/- 1.6 s\n", - "Queries per second (QPS): 4429\n" + "recall 0.99935\n", + "Average search time: 0.701 +/- 0.00579 s\n", + "Queries per second (QPS): 14273\n" ] } ], @@ -493,6 +432,7 @@ " k=10,\n", " handle=handle,\n", " )\n", + " handle.sync()\n", " \n", " recall[i] = calc_recall(cp.asnumpy(neighbors), gt_neighbors)\n", " print(\"recall\", recall[i])\n", @@ -515,21 +455,10 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "id": "e1ac370f-91c8-4054-95c7-a749df5f16d2", "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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W1uXycKEAEFPuLBkcEtcHfV7DlwdbVgyuCVdfdzK5HmFEA+nqPzo6+vLli1byux5K2N+FKIsdfVxK/Tz183RmufqamhUswqXb2Tx8oooFu05g0ZW5q+QStWzfy87OztatGhhyFjMnm7OvokORxVC5Pnf//5HaGgo06dP1zv+wQcfsGLFCmbMmGHw59RoNKxevZohQ4Zgaflf6r158yZTp07l+eefx83NjXPnzvH+++/j5OSkG0br6OjIsGHDmDBhAjVq1KB69epMnDgRX19f3erTXl5edO3aleHDh7N06VIA3njjDXr27ImnpycAgYGBeHt7ExwczJw5c7h27RoTJ05k+PDhZTrctKuPGwHerizb+w+zdp2ivlMVdod0Mtmh+0JUZkXlxylTpjBp0iQWLFhg8OeszPlRiLKU//kbnXCNyzcycba3oUVte3bt/IUpXdowfftpvjt8kXnhp4k+d42FA5pT47GCI1WE+SpRz/4nn3xS4sUCDh48yPbt2x8pKHOl1vXsyzB+IUxZcnIyr7zySoHjgwYNIi0trUyec/fu3SQmJuqtEQCgVCo5fvw4ffr0oXHjxgwZMoTGjRsTGRmpt6f1ggUL6Nu3L/3796d9+/bY2dmxbds2lMr/FuBZv349vr6+BAYGEhgYSNOmTVm7dq3ec23fvh0bGxvat29P//796du3L3Pnzi2TOuvV00JB6wZ5wwRzNBpp6AthoorKj4MHDy52obpHUdnzoxBlSWmhwL9hDfo0r4V/wxq6z187K0vm9W/GnBeaYqOyYP+ZVLp/tp/ohGtGjlgYUol69uPj46lTpw4vvvgivXv3plWrVrp9UXNycoiPj+fAgQOsW7eOpKQkvv766zIN2lTl3u3ZV8lFrBAm7emnn2b//v00atRI73hERESZLTAaGBiIVltwiJytrS27du164ONtbGxYtGgRixYtKrJM9erVWbduXbHnqVOnDj///PODAy4DVe3y5qGl3zavLXOEqEyKyo8HDhzgqaeeKpPnlPwohPG82MqDZh5VGbX+MH9fvslLy6MICWjMyE4NsZA2jdkrUWP/66+/5tixY3zxxRe8/PLLpKeno1Qqsba21vX4t2jRgjfeeIMhQ4YUulBJZaBboE969oUwab179+bdd98lJiaGtm3bAhAVFcXmzZt57rnn2LZtm24oae/evY0ZaoXiaJvX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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "fig = plt.figure(figsize=(12,3))\n", "ax = fig.add_subplot(131)\n", @@ -567,19 +496,10 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "id": "addbfff3-7773-4290-9608-5489edf4886d", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 536 ms, sys: 15.3 ms, total: 551 ms\n", - "Wall time: 545 ms\n" - ] - } - ], + "outputs": [], "source": [ "%%time\n", "build_params = ivf_flat.IndexParams(\n", @@ -603,19 +523,10 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "id": "8a0149ad-de38-4195-97a5-ce5d5d877036", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 598 ms, sys: 392 ms, total: 990 ms\n", - "Wall time: 985 ms\n" - ] - } - ], + "outputs": [], "source": [ "%%time\n", "n_queries=10000\n", @@ -631,21 +542,10 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "id": "eedc3ec4-06af-42c5-8cdf-490a5c2bc49a", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.9884" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "calc_recall(neighbors, gt_neighbors)" ] @@ -661,19 +561,10 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "id": "5a54d190-64d4-4cd4-a497-365cbffda871", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 76.6 ms, sys: 27 µs, total: 76.6 ms\n", - "Wall time: 76.2 ms\n" - ] - } - ], + "outputs": [], "source": [ "%%time\n", "build_params = ivf_flat.IndexParams( \n", @@ -695,21 +586,10 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "id": "4cc992e8-a5e5-4508-b790-0e934160b660", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.98798" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "search_params = ivf_flat.SearchParams(n_probes=10)\n", "\n", @@ -735,19 +615,10 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "id": "7ebcf970-94ed-4825-9885-277bd984b90c", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Index before adding vectors Index(type=IVF-FLAT, metric=sqeuclidean, size=0, dim=128, n_lists=1024, adaptive_centers=False)\n", - "Index after adding vectors Index(type=IVF-FLAT, metric=sqeuclidean, size=1000000, dim=128, n_lists=1024, adaptive_centers=False)\n" - ] - } - ], + "outputs": [], "source": [ "# subsample the dataset\n", "n_train = 10000\n", diff --git a/notebooks/tutorial_ivf_pq.ipynb b/notebooks/tutorial_ivf_pq.ipynb index 6aa8cd6495..397e39bfba 100644 --- a/notebooks/tutorial_ivf_pq.ipynb +++ b/notebooks/tutorial_ivf_pq.ipynb @@ -79,6 +79,7 @@ "from pylibraft.common import DeviceResources\n", "from pylibraft.neighbors import ivf_pq, refine\n", "from adjustText import adjust_text\n", + "from utils import calc_recall, load_dataset\n", "\n", "%matplotlib inline" ] @@ -194,15 +195,18 @@ "cell_type": "code", "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The index and data will be saved in /tmp/raft_example\n" + ] + } + ], "source": [ "DATASET_URL = \"http://ann-benchmarks.com/sift-128-euclidean.hdf5\"\n", - "DATASET_FILENAME = DATASET_URL.split('/')[-1]\n", - "\n", - "## download the dataset\n", - "dataset_path = os.path.join(WORK_FOLDER, DATASET_FILENAME)\n", - "if not os.path.exists(dataset_path):\n", - " urllib.request.urlretrieve(DATASET_URL, dataset_path)" + "f = load_dataset(DATASET_URL)" ] }, { @@ -227,8 +231,6 @@ } ], "source": [ - "f = h5py.File(dataset_path, \"r\")\n", - "\n", "metric = f.attrs['distance']\n", "\n", "dataset = cp.array(f['train'])\n", @@ -456,28 +458,6 @@ } ], "source": [ - "## Check the quality of the prediction (recall)\n", - "def calc_recall(found_indices, ground_truth):\n", - " found_indices = cp.asarray(found_indices)\n", - " bs, k = found_indices.shape\n", - " if bs != ground_truth.shape[0]:\n", - " raise RuntimeError(\n", - " \"Batch sizes do not match {} vs {}\".format(\n", - " bs, ground_truth.shape[0])\n", - " )\n", - " if k > ground_truth.shape[1]:\n", - " raise RuntimeError(\n", - " \"Not enough indices in the ground truth ({} > {})\".format(\n", - " k, ground_truth.shape[1])\n", - " )\n", - " n = 0\n", - " # Go over the batch\n", - " for i in range(bs):\n", - " # Note, ivf-pq does not guarantee the ordered input, hence the use of intersect1d\n", - " n += cp.intersect1d(found_indices[i, :k], ground_truth[i, :k]).size\n", - " recall = n / found_indices.size\n", - " return recall\n", - "\n", "recall_first_try = calc_recall(neighbors, gt_neighbors)\n", "print(f\"Got recall = {recall_first_try} with the default parameters (k = {k}).\")" ] diff --git a/notebooks/utils.py b/notebooks/utils.py new file mode 100644 index 0000000000..1295ca1693 --- /dev/null +++ b/notebooks/utils.py @@ -0,0 +1,103 @@ +# +# Copyright (c) 2023, NVIDIA CORPORATION. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import cupy as cp +import h5py +import os +import tempfile +import time +import urllib + +## Check the quality of the prediction (recall) +def calc_recall(found_indices, ground_truth): + found_indices = cp.asarray(found_indices) + bs, k = found_indices.shape + if bs != ground_truth.shape[0]: + raise RuntimeError( + "Batch sizes do not match {} vs {}".format( + bs, ground_truth.shape[0] + ) + ) + if k > ground_truth.shape[1]: + raise RuntimeError( + "Not enough indices in the ground truth ({} > {})".format( + k, ground_truth.shape[1] + ) + ) + n = 0 + # Go over the batch + for i in range(bs): + # Note, ivf-pq does not guarantee the ordered input, hence the use of intersect1d + n += cp.intersect1d(found_indices[i, :k], ground_truth[i, :k]).size + recall = n / found_indices.size + return recall + + +class BenchmarkTimer: + """Provides a context manager that runs a code block `reps` times + and records results to the instance variable `timings`. Use like: + .. code-block:: python + timer = BenchmarkTimer(rep=5) + for _ in timer.benchmark_runs(): + ... do something ... + print(np.min(timer.timings)) + + This class is borrowed from the rapids/cuml benchmark suite + """ + + def __init__(self, reps=1, warmup=0): + self.warmup = warmup + self.reps = reps + self.timings = [] + + def benchmark_runs(self): + for r in range(self.reps + self.warmup): + t0 = time.time() + yield r + t1 = time.time() + self.timings.append(t1 - t0) + if r >= self.warmup: + self.timings.append(t1 - t0) + + +def load_dataset(dataset_url, work_folder=None): + """Download dataset from url. It is expeted that the dataset contains a hdf5 file in ann-benchmarks format + + Parameters + ---------- + dataset_url address of hdf5 file + work_folder name of the local folder to store the dataset + + """ + dataset_url = "http://ann-benchmarks.com/sift-128-euclidean.hdf5" + dataset_filename = dataset_url.split("/")[-1] + + # We'll need to load store some data in this tutorial + if work_folder is None: + work_folder = os.path.join(tempfile.gettempdir(), "raft_example") + + if not os.path.exists(work_folder): + os.makedirs(work_folder) + print("The index and data will be saved in", work_folder) + + ## download the dataset + dataset_path = os.path.join(work_folder, dataset_filename) + if not os.path.exists(dataset_path): + urllib.request.urlretrieve(dataset_url, dataset_path) + + f = h5py.File(dataset_path, "r") + + return f