-
Notifications
You must be signed in to change notification settings - Fork 514
Review and add GPU-accelerated notebook samples #243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Alexander (bvonodiripsa)
wants to merge
9
commits into
microsoft:main
Choose a base branch
from
bvonodiripsa:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 5 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
d281844
Add GPU-accelerated notebook samples
bvonodiripsa 8b0c943
Add README with notebook descriptions
bvonodiripsa 125cef1
Add NVIDIA RAPIDS attribution for all notebooks
bvonodiripsa 0361bb7
Update notebooks: add titles, fix paths, improve content
bvonodiripsa 35ee3a8
Add setup instructions and notes to README
bvonodiripsa 05c7634
Address PR feedback: simplify and consolidate notebooks
bvonodiripsa 9f50137
Add auto-download for HAR dataset in cuml notebook
bvonodiripsa b492939
Add PyOD GPU anomaly detection demo notebook
bvonodiripsa c9a2c23
Fix PyOD notebook: cuml.accel never actually accelerated anything
alex File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
57 changes: 57 additions & 0 deletions
57
docs-samples/data-science/gpu-accelerated-samples/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| # GPU-Accelerated Samples | ||
|
|
||
| Jupyter notebooks demonstrating GPU acceleration using NVIDIA RAPIDS libraries on Microsoft Fabric and Azure VMs with NVIDIA GPUs. | ||
|
|
||
| > Based on examples from [NVIDIA RAPIDS](https://github.com/rapidsai) (Apache 2.0 License, Copyright NVIDIA Corporation). Modified and tested for Azure GPU VM environment. | ||
|
|
||
| ## Requirements | ||
|
|
||
| - NVIDIA GPU (Tesla T4 or better) | ||
| - CUDA 12.x | ||
| - Python 3.12+ | ||
| - RAPIDS 25.x (cuDF, cuML, cuCIM) | ||
| - Conda environment recommended | ||
|
|
||
| ## Notebooks | ||
|
|
||
| | Notebook | Description | | ||
| |----------|-------------| | ||
| | [cudf-pandas-accelerator-demo.ipynb](cudf-pandas-accelerator-demo.ipynb) | Drop-in GPU acceleration for pandas with `cudf.pandas` — no code changes needed | | ||
| | [cudf-pandas-stock-analysis.ipynb](cudf-pandas-stock-analysis.ipynb) | Stock market data analysis using GPU-accelerated pandas (read, merge, resample, plot) | | ||
| | [cuml-scikit-learn-accelerator-demo.ipynb](cuml-scikit-learn-accelerator-demo.ipynb) | Drop-in GPU acceleration for scikit-learn (PCA, UMAP, KNN, HDBSCAN on activity recognition data) | | ||
| | [gpu-vs-cpu-compute-benchmark.ipynb](gpu-vs-cpu-compute-benchmark.ipynb) | Side-by-side GPU vs CPU benchmark for common compute operations | | ||
| | [rapids-dataframe-gpu-vs-cpu.ipynb](rapids-dataframe-gpu-vs-cpu.ipynb) | RAPIDS cuDF DataFrame operations compared to pandas on larger datasets | | ||
| | [multi-gpu-embedding-and-knn-search.ipynb](multi-gpu-embedding-and-knn-search.ipynb) | Multi-GPU text embedding generation and KNN similarity search using Ray + RAPIDS | | ||
|
|
||
| ## cuCIM Medical Imaging | ||
|
|
||
| GPU-accelerated image processing for digital pathology and microscopy using [cuCIM](https://github.com/rapidsai/cucim). | ||
|
|
||
| > Based on examples from [rapidsai/cucim](https://github.com/rapidsai/cucim) (Apache 2.0 License, Copyright NVIDIA Corporation). Modified for Azure GPU VM environment. | ||
|
|
||
| | Notebook | Description | | ||
| |----------|-------------| | ||
| | [01-whole-slide-image-reading.ipynb](cucim_medical_imaging/01-whole-slide-image-reading.ipynb) | Reading and displaying multi-resolution whole-slide pathology images | | ||
| | [02-image-cache-performance.ipynb](cucim_medical_imaging/02-image-cache-performance.ipynb) | Tile caching strategies for faster repeated region access | | ||
| | [03-gabor-texture-classification.ipynb](cucim_medical_imaging/03-gabor-texture-classification.ipynb) | GPU vs CPU Gabor filter bank for texture classification | | ||
| | [04-random-walker-segmentation.ipynb](cucim_medical_imaging/04-random-walker-segmentation.ipynb) | GPU-accelerated random walker image segmentation | | ||
| | [05-vesselness-filter.ipynb](cucim_medical_imaging/05-vesselness-filter.ipynb) | GPU-accelerated vessel detection using Frangi/Sato filters | | ||
|
|
||
| ### Setup for cuCIM notebooks | ||
|
|
||
| **Notebooks 01 and 02** require a whole-slide image file. Before running them, create the `input/` folder and download a sample image: | ||
|
|
||
| ```bash | ||
| cd cucim_medical_imaging | ||
| mkdir -p input | ||
| # Download a sample Aperio SVS whole-slide image (~170MB) | ||
| wget -O input/image.tif https://openslide.cs.cmu.edu/download/openslide-testdata/Aperio/CMU-1.svs | ||
| ``` | ||
|
|
||
| **Notebooks 03, 04, and 05** are self-contained — they use synthetic data from scikit-image and do not require any additional files. | ||
|
|
||
| ### Notes | ||
|
|
||
| - **multi-gpu-embedding-and-knn-search.ipynb** requires an Azure OpenAI API key and endpoint. Update the `api_key` and `azure_endpoint` values in Step 1 before running. | ||
| - **cuml-scikit-learn-accelerator-demo.ipynb** downloads the UCI HAR dataset to `/tmp/HAR_data/` on first run (requires internet access). | ||
| - **cudf-pandas-stock-analysis.ipynb** downloads stock price data from Yahoo Finance (requires internet access). | ||
314 changes: 314 additions & 0 deletions
314
...-science/gpu-accelerated-samples/cucim_medical_imaging/01-whole-slide-image-reading.ipynb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,314 @@ | ||
| { | ||
| "cells": [ | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "eb420212", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "# Basic Usage\n", | ||
| "\n", | ||
| "This notebook shows basic usage of cuCIM with a whole-slide pathology image." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "39389384", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Prerequisites" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "93157b57", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "# !conda install -c conda-forge pillow" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "6378c4a3", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Read image" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "c02bf93e", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "from cucim import CuImage\n", | ||
| "\n", | ||
| "img = CuImage(\"input/image.tif\")\n", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better to auto download the data from a blob storage to local path. |
||
| "print(f\"Image loaded: {img.path}\")\n", | ||
| "print(f\"Dimensions (XY): {img.size('XY')}\")" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "2b371170", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### See metadata" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "4ff52186", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import json\n", | ||
| "\n", | ||
| "metadata = img.metadata\n", | ||
| "print(json.dumps(metadata, indent=2)[:2000])" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "5ffeeeed", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### Read region\n", | ||
| "\n", | ||
| "Let's read the whole slide at the lowest resolution and save as a thumbnail." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "ec613ae8", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "from PIL import Image\n", | ||
| "\n", | ||
| "resolutions = img.resolutions\n", | ||
| "level_dimensions = resolutions[\"level_dimensions\"]\n", | ||
| "level_count = resolutions[\"level_count\"]\n", | ||
| "\n", | ||
| "print(f\"Number of levels: {level_count}\")\n", | ||
| "print(f\"Level dimensions: {level_dimensions}\")\n", | ||
| "\n", | ||
| "# Read the whole slide at lowest resolution\n", | ||
| "region = img.read_region(\n", | ||
| " location=[0, 0], size=level_dimensions[level_count - 1], level=level_count - 1\n", | ||
| ")\n", | ||
| "\n", | ||
| "# Save and display\n", | ||
| "region.save(\"input/thumbnail.ppm\")\n", | ||
| "Image.open(\"input/thumbnail.ppm\")" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "2f6da447", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "Now let's read a 512x512 region at full resolution (level 0)." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "daacf8cf", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "from PIL import Image\n", | ||
| "\n", | ||
| "region = img.read_region([10000, 20000], [512, 512], 0)\n", | ||
| "region.save(\"input/test.ppm\")\n", | ||
| "Image.open(\"input/test.ppm\")" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "6c367337", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### `__array_interface__` support\n", | ||
| "\n", | ||
| "A NumPy array has the `__array_interface__` property. Here is a simple example:" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "e09b53d6", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import numpy as np\n", | ||
| "\n", | ||
| "np_arr = np.array([1, 2, 3])\n", | ||
| "print(np_arr.__array_interface__)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "f7a3f798", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "As you can see from the above result, a NumPy array has `__array_interface__` property which describes the memory layout.\n", | ||
| "\n", | ||
| "**cuCIM also supports `__array_interface__`**, so you can directly convert a cuCIM region to a NumPy array and display it with PIL:" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "0804834e", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import numpy as np\n", | ||
| "from PIL import Image\n", | ||
| "\n", | ||
| "region = img.read_region([10000, 20000], [512, 512], 0)\n", | ||
| "np_img_arr = np.asarray(region)\n", | ||
| "\n", | ||
| "print(f\"Array shape: {np_img_arr.shape}, dtype: {np_img_arr.dtype}\")\n", | ||
| "arr_iface = np_img_arr.__array_interface__\n", | ||
| "print(f\"__array_interface__ shape: {arr_iface['shape']}\")\n", | ||
| "\n", | ||
| "Image.fromarray(np_img_arr)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "0b702b4e", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### `__cuda_array_interface__` support\n", | ||
| "\n", | ||
| "A CuPy array has `__cuda_array_interface__` property. Here is a simple example:" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "0990d4d4", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import cupy as cp\n", | ||
| "\n", | ||
| "cp_arr = cp.array([1, 2, 3])\n", | ||
| "print(cp_arr.__cuda_array_interface__)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "a130d04f", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "A Python object that has `__cuda_array_interface__` property is considered as a CUDA array-like object and can be converted to CuPy array through `cp.asarray(obj)` method.\n", | ||
| "\n", | ||
| "**`__cuda_array_interface__` is also supported in CuImage** if you specify `device='cuda'` in `read_region()`. The following code loads image data directly to GPU memory and visualizes it in the Jupyter notebook:" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "5a43d245", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import cupy as cp\n", | ||
| "from cucim import CuImage\n", | ||
| "from PIL import Image\n", | ||
| "\n", | ||
| "img = CuImage(\"input/image.tif\")\n", | ||
| "resolutions = img.resolutions\n", | ||
| "level_dimensions = resolutions[\"level_dimensions\"]\n", | ||
| "level_count = resolutions[\"level_count\"]\n", | ||
| "\n", | ||
| "# Read region directly to GPU memory\n", | ||
| "region_gpu = img.read_region(\n", | ||
| " [0, 0], level_dimensions[level_count - 1], level_count - 1, device=\"cuda\"\n", | ||
| ")\n", | ||
| "\n", | ||
| "print(f\"Device: {region_gpu.device}\")\n", | ||
| "cuda_iface = region_gpu.__cuda_array_interface__\n", | ||
| "print(f\"__cuda_array_interface__ shape: {cuda_iface['shape']}\")\n", | ||
| "\n", | ||
| "# Convert to CuPy array, then to CPU for display\n", | ||
| "cupy_arr = cp.asarray(region_gpu)\n", | ||
| "print(f\"CuPy array shape: {cupy_arr.shape}\")\n", | ||
| "Image.fromarray(cupy_arr.get())" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "id": "499f2e79", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### Associated images\n", | ||
| "\n", | ||
| "Some image formats such as Philips TIFF and Aperio SVS have associated images (Macro or Label images) in addition to the multi-resolution images.\n", | ||
| "\n", | ||
| "Let's check what associated images are available:" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": null, | ||
| "id": "b9714d55", | ||
| "metadata": {}, | ||
| "outputs": [], | ||
| "source": [ | ||
| "import numpy as np\n", | ||
| "from PIL import Image\n", | ||
| "\n", | ||
| "print(f\"Associated images: {img.associated_images}\")\n", | ||
| "\n", | ||
| "for name in img.associated_images:\n", | ||
| " assoc_img = img.associated_image(name)\n", | ||
| " np_arr = np.asarray(assoc_img)\n", | ||
| " print(f\" '{name}': shape={np_arr.shape}\")\n", | ||
| "\n", | ||
| "# Display the macro image if available\n", | ||
| "if 'macro' in img.associated_images:\n", | ||
| " macro_image = img.associated_image('macro')\n", | ||
| " np_img_arr = np.asarray(macro_image)\n", | ||
| " print(f\"\\nDisplaying macro image: {np_img_arr.shape}\")\n", | ||
| " display(Image.fromarray(np_img_arr))\n", | ||
| "\n", | ||
| "# Check for label image\n", | ||
| "if 'label' in img.associated_images:\n", | ||
| " print(\"Label image exists\")\n", | ||
| "else:\n", | ||
| " print(\"\\nThere is no associated image named 'label'!\")" | ||
| ] | ||
| } | ||
| ], | ||
| "metadata": { | ||
| "kernelspec": { | ||
| "display_name": "Python 3 (ipykernel)", | ||
| "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.13.11" | ||
| } | ||
| }, | ||
| "nbformat": 4, | ||
| "nbformat_minor": 5 | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these notebooks seem to be a little bit heavy as sample notebooks, I'd suggest we combine them into one notebook and keep core components in the notebooks.