Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions conda/environments/all_cuda-129_arch-aarch64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ dependencies:
- libopenblas<=0.3.30
- libraft==26.6.*,>=0.0.0a0
- librmm==26.6.*,>=0.0.0a0
- lightgbm
- matplotlib-base
- nbsphinx
- ninja
Expand Down Expand Up @@ -75,6 +76,7 @@ dependencies:
- scikit-learn>=1.5
- scipy>=1.14.0
- seaborn
- shap
- skl2onnx
- sphinx
- sphinx-copybutton
Expand Down
2 changes: 2 additions & 0 deletions conda/environments/all_cuda-129_arch-x86_64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencies:
- libcuvs==26.6.*,>=0.0.0a0
- libraft==26.6.*,>=0.0.0a0
- librmm==26.6.*,>=0.0.0a0
- lightgbm
- matplotlib-base
- nbsphinx
- ninja
Expand Down Expand Up @@ -74,6 +75,7 @@ dependencies:
- scikit-learn>=1.5
- scipy>=1.14.0
- seaborn
- shap
- skl2onnx
- sphinx
- sphinx-copybutton
Expand Down
2 changes: 2 additions & 0 deletions conda/environments/all_cuda-131_arch-aarch64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ dependencies:
- libopenblas<=0.3.30
- libraft==26.6.*,>=0.0.0a0
- librmm==26.6.*,>=0.0.0a0
- lightgbm
- matplotlib-base
- nbsphinx
- ninja
Expand Down Expand Up @@ -75,6 +76,7 @@ dependencies:
- scikit-learn>=1.5
- scipy>=1.14.0
- seaborn
- shap
- skl2onnx
- sphinx
- sphinx-copybutton
Expand Down
2 changes: 2 additions & 0 deletions conda/environments/all_cuda-131_arch-x86_64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencies:
- libcuvs==26.6.*,>=0.0.0a0
- libraft==26.6.*,>=0.0.0a0
- librmm==26.6.*,>=0.0.0a0
- lightgbm
- matplotlib-base
- nbsphinx
- ninja
Expand Down Expand Up @@ -74,6 +75,7 @@ dependencies:
- scikit-learn>=1.5
- scipy>=1.14.0
- seaborn
- shap
- skl2onnx
- sphinx
- sphinx-copybutton
Expand Down
2 changes: 2 additions & 0 deletions dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,8 @@ dependencies:
- statsmodels
- umap-learn>=0.5.7,<0.5.12
- pynndescent
- lightgbm
- shap
- output_types: conda
packages:
- cuvs==26.6.*,>=0.0.0a0
Expand Down
23 changes: 15 additions & 8 deletions python/cuml/cuml/explainer/base.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ from cuml.explainer.common import (
output_list_shap_values,
)
from cuml.internals.base import get_handle
from cuml.internals.input_utils import input_to_cupy_array, input_to_host_array
from cuml.internals.validation import check_array

from libc.stdint cimport uintptr_t
from libcpp cimport bool
Expand Down Expand Up @@ -129,9 +129,10 @@ class SHAPBase():
raise ValueError("dtype must be either np.float32 or np.float64.")
self.dtype = dtype

self.background, self.nrows, self.ncols, _ = \
input_to_cupy_array(background, order=self.order,
convert_to_dtype=self.dtype)
self.background = check_array(
background, order=self.order, dtype=self.dtype, ensure_all_finite=False
)
self.nrows, self.ncols = self.background.shape

self.random_state = random_state

Expand Down Expand Up @@ -204,9 +205,13 @@ class SHAPBase():
"""
self._reset_timers()

X = input_to_cupy_array(X,
order=self.order,
convert_to_dtype=self.dtype)[0]
X = check_array(
X,
order=self.order,
dtype=self.dtype,
ensure_2d=False,
ensure_all_finite=False,
)

if X.ndim == 1:
X = X.reshape((1, self.ncols))
Expand Down Expand Up @@ -294,7 +299,9 @@ class SHAPBase():
out = Explanation(
values=shap_values,
base_values=base_values,
data=input_to_host_array(X).array,
data=check_array(
X, mem_type="host", ensure_2d=False, ensure_all_finite=False
),
feature_names=self.feature_names,
main_effects=main_effect_values
)
Expand Down
10 changes: 5 additions & 5 deletions python/cuml/cuml/explainer/common.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#
# SPDX-FileCopyrightText: Copyright (c) 2020-2025, NVIDIA CORPORATION.
# SPDX-FileCopyrightText: Copyright (c) 2020-2026, NVIDIA CORPORATION.
# SPDX-License-Identifier: Apache-2.0
#
import cupy as cp

from cuml.internals.input_utils import input_to_cupy_array
from cuml.internals.validation import check_array


def get_tag_from_model_func(func, tag, default=None):
Expand Down Expand Up @@ -39,17 +39,17 @@ def model_func_call(X, model_func, gpu_model=False):
Returns the results as CuPy arrays.
"""
if gpu_model:
y = input_to_cupy_array(X=model_func(X), order="K").array
y = model_func(X)
else:
try:
y = input_to_cupy_array(model_func(cp.asnumpy(X))).array
y = model_func(cp.asnumpy(X))
except TypeError:
raise TypeError(
"Explainer can only explain models that can "
"take GPU data or NumPy arrays as input."
)

return y
return check_array(y, ensure_2d=False, ensure_all_finite=False)


def get_cai_ptr(X):
Expand Down
7 changes: 4 additions & 3 deletions python/cuml/cuml/explainer/kernel_shap.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import numpy as np
from cuml.explainer.base import SHAPBase
from cuml.explainer.common import get_cai_ptr, model_func_call
from cuml.internals import get_handle
from cuml.internals.input_utils import input_to_cupy_array
from cuml.internals.validation import check_array
from cuml.linear_model import Lasso, LinearRegression

from libc.stdint cimport uint64_t, uintptr_t
Expand Down Expand Up @@ -279,8 +279,9 @@ class KernelExplainer(SHAPBase):
self.randind,
self.dtype)

row, _, _, _ = \
input_to_cupy_array(row, order=self.order)
row = check_array(
row, order=self.order, ensure_2d=False, ensure_all_finite=False
)

handle = get_handle()
cdef handle_t* handle_ = <handle_t*><size_t>handle.getHandle()
Expand Down
65 changes: 27 additions & 38 deletions python/cuml/cuml/explainer/sampling.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
# SPDX-FileCopyrightText: Copyright (c) 2021-2025, NVIDIA CORPORATION.
# SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION.
# SPDX-License-Identifier: Apache-2.0
#
import cudf
import cupy as cp
from scipy.sparse import issparse
import pandas as pd

import cuml
from cuml import KMeans
from cuml.internals.input_utils import (
determine_array_type,
get_supported_input_type,
)
from cuml.internals.array import CumlArray
from cuml.internals.validation import check_array
from cuml.preprocessing import SimpleImputer


Expand Down Expand Up @@ -43,58 +42,48 @@ def kmeans_sampling(X, k, round_values=True, detailed=False, random_state=0):
labels : Cluster labels of the data points in the original dataset,
shape (n_samples, 1)
"""
output_dtype = get_supported_input_type(X)
_output_dtype_str = determine_array_type(X)

if output_dtype is None:
raise TypeError(
f"Type of input {type(X)} is not supported. Supported \
dtypes: cuDF DataFrame, cuDF Series, cupy, numba,\
numpy, pandas DataFrame, pandas Series"
)

if "DataFrame" in str(output_dtype):
group_names = X.columns
X = cp.array(X.values, copy=False)
if "Series" in str(output_dtype):
group_names = X.name
X = cp.array(X.values.reshape(-1, 1), copy=False)
if isinstance(X, (cudf.DataFrame, pd.DataFrame)):
group_names = [str(c) for c in X.columns]
elif isinstance(X, (cudf.Series, pd.Series)):
group_names = [str(X.name)]
elif len(X.shape) == 2:
group_names = [str(i) for i in range(X.shape[1])]
else:
# it's either numpy, cupy or numba
X = cp.array(X, copy=False)
try:
# more than one column
group_names = [str(i) for i in range(X.shape[1])]
except IndexError:
# one column
X = X.reshape(-1, 1)
group_names = ["0"]
group_names = ["0"]

X, index = check_array(
X, ensure_2d=False, ensure_all_finite=False, return_index=True
)
Comment thread
jcrist marked this conversation as resolved.
if X.ndim == 1:
X = X.reshape(-1, 1)

# in case there are any missing values in data impute them
imp = SimpleImputer(
missing_values=cp.nan, strategy="mean", output_type=_output_dtype_str
missing_values=cp.nan, strategy="mean", output_type="cupy"
)
X = imp.fit_transform(X)

kmeans = KMeans(
n_clusters=k,
random_state=random_state,
output_type=_output_dtype_str,
output_type="cupy",
n_init="auto",
).fit(X)

if round_values:
for i in range(k):
for j in range(X.shape[1]):
xj = (
X[:, j].toarray().flatten() if issparse(X) else X[:, j]
) # sparse support courtesy of @PrimozGodec
xj = X[:, j]
ind = cp.argmin(cp.abs(xj - kmeans.cluster_centers_[i, j]))
kmeans.cluster_centers_[i, j] = X[ind, j]
summary = kmeans.cluster_centers_
labels = kmeans.labels_

if detailed:
return summary, group_names, labels
return (
CumlArray(data=summary),
group_names,
CumlArray(labels, index=index),
)
else:
return summary
return CumlArray(data=summary)
Loading
Loading