release(camera-ai): publish 0.4.12 catalog portability fix
This commit is contained in:
+262
-262
@@ -1,262 +1,262 @@
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from libc.stdlib cimport free
|
||||
from libc.string cimport memset
|
||||
|
||||
import numpy as np
|
||||
from scipy.sparse import issparse
|
||||
|
||||
from ..utils._typedefs cimport float32_t, float64_t, intp_t, int32_t, uint8_t
|
||||
# Note: _tree uses cimport numpy, cnp.import_array, so we need to include
|
||||
# numpy headers in the build configuration of this extension
|
||||
from ..tree._tree cimport Node
|
||||
from ..tree._tree cimport Tree
|
||||
from ..tree._utils cimport safe_realloc
|
||||
|
||||
|
||||
# no namespace lookup for numpy dtype and array creation
|
||||
from numpy import zeros as np_zeros
|
||||
|
||||
|
||||
# constant to mark tree leafs
|
||||
cdef intp_t TREE_LEAF = -1
|
||||
|
||||
cdef void _predict_regression_tree_inplace_fast_dense(
|
||||
const float32_t[:, ::1] X,
|
||||
Node* root_node,
|
||||
double *value,
|
||||
double scale,
|
||||
Py_ssize_t k,
|
||||
float64_t[:, :] out
|
||||
) noexcept nogil:
|
||||
"""Predicts output for regression tree and stores it in ``out[i, k]``.
|
||||
|
||||
This function operates directly on the data arrays of the tree
|
||||
data structures. This is 5x faster than the variant above because
|
||||
it allows us to avoid buffer validation.
|
||||
|
||||
The function assumes that the ndarray that wraps ``X`` is
|
||||
c-continuous.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : float32_t 2d memory view
|
||||
The memory view on the data ndarray of the input ``X``.
|
||||
Assumes that the array is c-continuous.
|
||||
root_node : tree Node pointer
|
||||
Pointer to the main node array of the :class:``sklearn.tree.Tree``.
|
||||
value : np.float64_t pointer
|
||||
The pointer to the data array of the ``value`` array attribute
|
||||
of the :class:``sklearn.tree.Tree``.
|
||||
scale : double
|
||||
A constant to scale the predictions.
|
||||
k : int
|
||||
The index of the tree output to be predicted. Must satisfy
|
||||
0 <= ``k`` < ``K``.
|
||||
out : memory view on array of type np.float64_t
|
||||
The data array where the predictions are stored.
|
||||
``out`` is assumed to be a two-dimensional array of
|
||||
shape ``(n_samples, K)``.
|
||||
"""
|
||||
cdef intp_t n_samples = X.shape[0]
|
||||
cdef Py_ssize_t i
|
||||
cdef Node *node
|
||||
for i in range(n_samples):
|
||||
node = root_node
|
||||
# While node not a leaf
|
||||
while node.left_child != TREE_LEAF:
|
||||
if X[i, node.feature] <= node.threshold:
|
||||
node = root_node + node.left_child
|
||||
else:
|
||||
node = root_node + node.right_child
|
||||
out[i, k] += scale * value[node - root_node]
|
||||
|
||||
|
||||
def _predict_regression_tree_stages_sparse(
|
||||
object[:, :] estimators,
|
||||
object X,
|
||||
double scale,
|
||||
float64_t[:, :] out
|
||||
):
|
||||
"""Predicts output for regression tree inplace and adds scaled value to ``out[i, k]``.
|
||||
|
||||
The function assumes that the ndarray that wraps ``X`` is csr_matrix.
|
||||
"""
|
||||
cdef const float32_t[::1] X_data = X.data
|
||||
cdef const int32_t[::1] X_indices = X.indices
|
||||
cdef const int32_t[::1] X_indptr = X.indptr
|
||||
|
||||
cdef intp_t n_samples = X.shape[0]
|
||||
cdef intp_t n_features = X.shape[1]
|
||||
cdef intp_t n_stages = estimators.shape[0]
|
||||
cdef intp_t n_outputs = estimators.shape[1]
|
||||
|
||||
# Indices and temporary variables
|
||||
cdef intp_t sample_i
|
||||
cdef intp_t feature_i
|
||||
cdef intp_t stage_i
|
||||
cdef intp_t output_i
|
||||
cdef Node *root_node = NULL
|
||||
cdef Node *node = NULL
|
||||
cdef double *value = NULL
|
||||
|
||||
cdef Tree tree
|
||||
cdef Node** nodes = NULL
|
||||
cdef double** values = NULL
|
||||
safe_realloc(&nodes, n_stages * n_outputs)
|
||||
safe_realloc(&values, n_stages * n_outputs)
|
||||
for stage_i in range(n_stages):
|
||||
for output_i in range(n_outputs):
|
||||
tree = estimators[stage_i, output_i].tree_
|
||||
nodes[stage_i * n_outputs + output_i] = tree.nodes
|
||||
values[stage_i * n_outputs + output_i] = tree.value
|
||||
|
||||
# Initialize auxiliary data-structure
|
||||
cdef float32_t feature_value = 0.
|
||||
cdef float32_t* X_sample = NULL
|
||||
|
||||
# feature_to_sample as a data structure records the last seen sample
|
||||
# for each feature; functionally, it is an efficient way to identify
|
||||
# which features are nonzero in the present sample.
|
||||
cdef intp_t* feature_to_sample = NULL
|
||||
|
||||
safe_realloc(&X_sample, n_features)
|
||||
safe_realloc(&feature_to_sample, n_features)
|
||||
|
||||
memset(feature_to_sample, -1, n_features * sizeof(intp_t))
|
||||
|
||||
# Cycle through all samples
|
||||
for sample_i in range(n_samples):
|
||||
for feature_i in range(X_indptr[sample_i], X_indptr[sample_i + 1]):
|
||||
feature_to_sample[X_indices[feature_i]] = sample_i
|
||||
X_sample[X_indices[feature_i]] = X_data[feature_i]
|
||||
|
||||
# Cycle through all stages
|
||||
for stage_i in range(n_stages):
|
||||
# Cycle through all trees
|
||||
for output_i in range(n_outputs):
|
||||
root_node = nodes[stage_i * n_outputs + output_i]
|
||||
value = values[stage_i * n_outputs + output_i]
|
||||
node = root_node
|
||||
|
||||
# While node not a leaf
|
||||
while node.left_child != TREE_LEAF:
|
||||
# ... and node.right_child != TREE_LEAF:
|
||||
if feature_to_sample[node.feature] == sample_i:
|
||||
feature_value = X_sample[node.feature]
|
||||
else:
|
||||
feature_value = 0.
|
||||
|
||||
if feature_value <= node.threshold:
|
||||
node = root_node + node.left_child
|
||||
else:
|
||||
node = root_node + node.right_child
|
||||
out[sample_i, output_i] += scale * value[node - root_node]
|
||||
|
||||
# Free auxiliary arrays
|
||||
free(X_sample)
|
||||
free(feature_to_sample)
|
||||
free(nodes)
|
||||
free(values)
|
||||
|
||||
|
||||
def predict_stages(
|
||||
object[:, :] estimators,
|
||||
object X,
|
||||
double scale,
|
||||
float64_t[:, :] out
|
||||
):
|
||||
"""Add predictions of ``estimators`` to ``out``.
|
||||
|
||||
Each estimator is scaled by ``scale`` before its prediction
|
||||
is added to ``out``.
|
||||
"""
|
||||
cdef Py_ssize_t i
|
||||
cdef Py_ssize_t k
|
||||
cdef Py_ssize_t n_estimators = estimators.shape[0]
|
||||
cdef Py_ssize_t K = estimators.shape[1]
|
||||
cdef Tree tree
|
||||
|
||||
if issparse(X):
|
||||
if X.format != 'csr':
|
||||
raise ValueError("When X is a sparse matrix, a CSR format is"
|
||||
" expected, got {!r}".format(type(X)))
|
||||
_predict_regression_tree_stages_sparse(
|
||||
estimators=estimators, X=X, scale=scale, out=out
|
||||
)
|
||||
else:
|
||||
if not isinstance(X, np.ndarray) or np.isfortran(X):
|
||||
raise ValueError(f"X should be C-ordered np.ndarray, got {type(X)}")
|
||||
|
||||
for i in range(n_estimators):
|
||||
for k in range(K):
|
||||
tree = estimators[i, k].tree_
|
||||
|
||||
# avoid buffer validation by casting to ndarray
|
||||
# and get data pointer
|
||||
# need brackets because of casting operator priority
|
||||
_predict_regression_tree_inplace_fast_dense(
|
||||
X=X,
|
||||
root_node=tree.nodes,
|
||||
value=tree.value,
|
||||
scale=scale,
|
||||
k=k,
|
||||
out=out
|
||||
)
|
||||
# out[:, k] += scale * tree.predict(X).ravel()
|
||||
|
||||
|
||||
def predict_stage(
|
||||
object[:, :] estimators,
|
||||
int stage,
|
||||
object X,
|
||||
double scale,
|
||||
float64_t[:, :] out
|
||||
):
|
||||
"""Add predictions of ``estimators[stage]`` to ``out``.
|
||||
|
||||
Each estimator in the stage is scaled by ``scale`` before
|
||||
its prediction is added to ``out``.
|
||||
"""
|
||||
return predict_stages(
|
||||
estimators=estimators[stage:stage + 1], X=X, scale=scale, out=out
|
||||
)
|
||||
|
||||
|
||||
def _random_sample_mask(
|
||||
intp_t n_total_samples,
|
||||
intp_t n_total_in_bag,
|
||||
random_state
|
||||
):
|
||||
"""Create a random sample mask where ``n_total_in_bag`` elements are set.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n_total_samples : int
|
||||
The length of the resulting mask.
|
||||
|
||||
n_total_in_bag : int
|
||||
The number of elements in the sample mask which are set to 1.
|
||||
|
||||
random_state : RandomState
|
||||
A numpy ``RandomState`` object.
|
||||
|
||||
Returns
|
||||
-------
|
||||
sample_mask : np.ndarray, shape=[n_total_samples]
|
||||
An ndarray where ``n_total_in_bag`` elements are set to ``True``
|
||||
the others are ``False``.
|
||||
"""
|
||||
cdef float64_t[::1] rand = random_state.uniform(size=n_total_samples)
|
||||
cdef uint8_t[::1] sample_mask = np_zeros((n_total_samples,), dtype=bool)
|
||||
|
||||
cdef intp_t n_bagged = 0
|
||||
cdef intp_t i = 0
|
||||
|
||||
for i in range(n_total_samples):
|
||||
if rand[i] * (n_total_samples - i) < (n_total_in_bag - n_bagged):
|
||||
sample_mask[i] = 1
|
||||
n_bagged += 1
|
||||
|
||||
return sample_mask.base
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from libc.stdlib cimport free
|
||||
from libc.string cimport memset
|
||||
|
||||
import numpy as np
|
||||
from scipy.sparse import issparse
|
||||
|
||||
from ..utils._typedefs cimport float32_t, float64_t, intp_t, int32_t, uint8_t
|
||||
# Note: _tree uses cimport numpy, cnp.import_array, so we need to include
|
||||
# numpy headers in the build configuration of this extension
|
||||
from ..tree._tree cimport Node
|
||||
from ..tree._tree cimport Tree
|
||||
from ..tree._utils cimport safe_realloc
|
||||
|
||||
|
||||
# no namespace lookup for numpy dtype and array creation
|
||||
from numpy import zeros as np_zeros
|
||||
|
||||
|
||||
# constant to mark tree leafs
|
||||
cdef intp_t TREE_LEAF = -1
|
||||
|
||||
cdef void _predict_regression_tree_inplace_fast_dense(
|
||||
const float32_t[:, ::1] X,
|
||||
Node* root_node,
|
||||
double *value,
|
||||
double scale,
|
||||
Py_ssize_t k,
|
||||
float64_t[:, :] out
|
||||
) noexcept nogil:
|
||||
"""Predicts output for regression tree and stores it in ``out[i, k]``.
|
||||
|
||||
This function operates directly on the data arrays of the tree
|
||||
data structures. This is 5x faster than the variant above because
|
||||
it allows us to avoid buffer validation.
|
||||
|
||||
The function assumes that the ndarray that wraps ``X`` is
|
||||
c-continuous.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
X : float32_t 2d memory view
|
||||
The memory view on the data ndarray of the input ``X``.
|
||||
Assumes that the array is c-continuous.
|
||||
root_node : tree Node pointer
|
||||
Pointer to the main node array of the :class:``sklearn.tree.Tree``.
|
||||
value : np.float64_t pointer
|
||||
The pointer to the data array of the ``value`` array attribute
|
||||
of the :class:``sklearn.tree.Tree``.
|
||||
scale : double
|
||||
A constant to scale the predictions.
|
||||
k : int
|
||||
The index of the tree output to be predicted. Must satisfy
|
||||
0 <= ``k`` < ``K``.
|
||||
out : memory view on array of type np.float64_t
|
||||
The data array where the predictions are stored.
|
||||
``out`` is assumed to be a two-dimensional array of
|
||||
shape ``(n_samples, K)``.
|
||||
"""
|
||||
cdef intp_t n_samples = X.shape[0]
|
||||
cdef Py_ssize_t i
|
||||
cdef Node *node
|
||||
for i in range(n_samples):
|
||||
node = root_node
|
||||
# While node not a leaf
|
||||
while node.left_child != TREE_LEAF:
|
||||
if X[i, node.feature] <= node.threshold:
|
||||
node = root_node + node.left_child
|
||||
else:
|
||||
node = root_node + node.right_child
|
||||
out[i, k] += scale * value[node - root_node]
|
||||
|
||||
|
||||
def _predict_regression_tree_stages_sparse(
|
||||
object[:, :] estimators,
|
||||
object X,
|
||||
double scale,
|
||||
float64_t[:, :] out
|
||||
):
|
||||
"""Predicts output for regression tree inplace and adds scaled value to ``out[i, k]``.
|
||||
|
||||
The function assumes that the ndarray that wraps ``X`` is csr_matrix.
|
||||
"""
|
||||
cdef const float32_t[::1] X_data = X.data
|
||||
cdef const int32_t[::1] X_indices = X.indices
|
||||
cdef const int32_t[::1] X_indptr = X.indptr
|
||||
|
||||
cdef intp_t n_samples = X.shape[0]
|
||||
cdef intp_t n_features = X.shape[1]
|
||||
cdef intp_t n_stages = estimators.shape[0]
|
||||
cdef intp_t n_outputs = estimators.shape[1]
|
||||
|
||||
# Indices and temporary variables
|
||||
cdef intp_t sample_i
|
||||
cdef intp_t feature_i
|
||||
cdef intp_t stage_i
|
||||
cdef intp_t output_i
|
||||
cdef Node *root_node = NULL
|
||||
cdef Node *node = NULL
|
||||
cdef double *value = NULL
|
||||
|
||||
cdef Tree tree
|
||||
cdef Node** nodes = NULL
|
||||
cdef double** values = NULL
|
||||
safe_realloc(&nodes, n_stages * n_outputs)
|
||||
safe_realloc(&values, n_stages * n_outputs)
|
||||
for stage_i in range(n_stages):
|
||||
for output_i in range(n_outputs):
|
||||
tree = estimators[stage_i, output_i].tree_
|
||||
nodes[stage_i * n_outputs + output_i] = tree.nodes
|
||||
values[stage_i * n_outputs + output_i] = tree.value
|
||||
|
||||
# Initialize auxiliary data-structure
|
||||
cdef float32_t feature_value = 0.
|
||||
cdef float32_t* X_sample = NULL
|
||||
|
||||
# feature_to_sample as a data structure records the last seen sample
|
||||
# for each feature; functionally, it is an efficient way to identify
|
||||
# which features are nonzero in the present sample.
|
||||
cdef intp_t* feature_to_sample = NULL
|
||||
|
||||
safe_realloc(&X_sample, n_features)
|
||||
safe_realloc(&feature_to_sample, n_features)
|
||||
|
||||
memset(feature_to_sample, -1, n_features * sizeof(intp_t))
|
||||
|
||||
# Cycle through all samples
|
||||
for sample_i in range(n_samples):
|
||||
for feature_i in range(X_indptr[sample_i], X_indptr[sample_i + 1]):
|
||||
feature_to_sample[X_indices[feature_i]] = sample_i
|
||||
X_sample[X_indices[feature_i]] = X_data[feature_i]
|
||||
|
||||
# Cycle through all stages
|
||||
for stage_i in range(n_stages):
|
||||
# Cycle through all trees
|
||||
for output_i in range(n_outputs):
|
||||
root_node = nodes[stage_i * n_outputs + output_i]
|
||||
value = values[stage_i * n_outputs + output_i]
|
||||
node = root_node
|
||||
|
||||
# While node not a leaf
|
||||
while node.left_child != TREE_LEAF:
|
||||
# ... and node.right_child != TREE_LEAF:
|
||||
if feature_to_sample[node.feature] == sample_i:
|
||||
feature_value = X_sample[node.feature]
|
||||
else:
|
||||
feature_value = 0.
|
||||
|
||||
if feature_value <= node.threshold:
|
||||
node = root_node + node.left_child
|
||||
else:
|
||||
node = root_node + node.right_child
|
||||
out[sample_i, output_i] += scale * value[node - root_node]
|
||||
|
||||
# Free auxiliary arrays
|
||||
free(X_sample)
|
||||
free(feature_to_sample)
|
||||
free(nodes)
|
||||
free(values)
|
||||
|
||||
|
||||
def predict_stages(
|
||||
object[:, :] estimators,
|
||||
object X,
|
||||
double scale,
|
||||
float64_t[:, :] out
|
||||
):
|
||||
"""Add predictions of ``estimators`` to ``out``.
|
||||
|
||||
Each estimator is scaled by ``scale`` before its prediction
|
||||
is added to ``out``.
|
||||
"""
|
||||
cdef Py_ssize_t i
|
||||
cdef Py_ssize_t k
|
||||
cdef Py_ssize_t n_estimators = estimators.shape[0]
|
||||
cdef Py_ssize_t K = estimators.shape[1]
|
||||
cdef Tree tree
|
||||
|
||||
if issparse(X):
|
||||
if X.format != 'csr':
|
||||
raise ValueError("When X is a sparse matrix, a CSR format is"
|
||||
" expected, got {!r}".format(type(X)))
|
||||
_predict_regression_tree_stages_sparse(
|
||||
estimators=estimators, X=X, scale=scale, out=out
|
||||
)
|
||||
else:
|
||||
if not isinstance(X, np.ndarray) or np.isfortran(X):
|
||||
raise ValueError(f"X should be C-ordered np.ndarray, got {type(X)}")
|
||||
|
||||
for i in range(n_estimators):
|
||||
for k in range(K):
|
||||
tree = estimators[i, k].tree_
|
||||
|
||||
# avoid buffer validation by casting to ndarray
|
||||
# and get data pointer
|
||||
# need brackets because of casting operator priority
|
||||
_predict_regression_tree_inplace_fast_dense(
|
||||
X=X,
|
||||
root_node=tree.nodes,
|
||||
value=tree.value,
|
||||
scale=scale,
|
||||
k=k,
|
||||
out=out
|
||||
)
|
||||
# out[:, k] += scale * tree.predict(X).ravel()
|
||||
|
||||
|
||||
def predict_stage(
|
||||
object[:, :] estimators,
|
||||
int stage,
|
||||
object X,
|
||||
double scale,
|
||||
float64_t[:, :] out
|
||||
):
|
||||
"""Add predictions of ``estimators[stage]`` to ``out``.
|
||||
|
||||
Each estimator in the stage is scaled by ``scale`` before
|
||||
its prediction is added to ``out``.
|
||||
"""
|
||||
return predict_stages(
|
||||
estimators=estimators[stage:stage + 1], X=X, scale=scale, out=out
|
||||
)
|
||||
|
||||
|
||||
def _random_sample_mask(
|
||||
intp_t n_total_samples,
|
||||
intp_t n_total_in_bag,
|
||||
random_state
|
||||
):
|
||||
"""Create a random sample mask where ``n_total_in_bag`` elements are set.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n_total_samples : int
|
||||
The length of the resulting mask.
|
||||
|
||||
n_total_in_bag : int
|
||||
The number of elements in the sample mask which are set to 1.
|
||||
|
||||
random_state : RandomState
|
||||
A numpy ``RandomState`` object.
|
||||
|
||||
Returns
|
||||
-------
|
||||
sample_mask : np.ndarray, shape=[n_total_samples]
|
||||
An ndarray where ``n_total_in_bag`` elements are set to ``True``
|
||||
the others are ``False``.
|
||||
"""
|
||||
cdef float64_t[::1] rand = random_state.uniform(size=n_total_samples)
|
||||
cdef uint8_t[::1] sample_mask = np_zeros((n_total_samples,), dtype=bool)
|
||||
|
||||
cdef intp_t n_bagged = 0
|
||||
cdef intp_t i = 0
|
||||
|
||||
for i in range(n_total_samples):
|
||||
if rand[i] * (n_total_samples - i) < (n_total_in_bag - n_bagged):
|
||||
sample_mask[i] = 1
|
||||
n_bagged += 1
|
||||
|
||||
return sample_mask.base
|
||||
|
||||
+85
-85
@@ -1,85 +1,85 @@
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from cython.parallel import prange
|
||||
from libc.math cimport isnan
|
||||
|
||||
from .common cimport X_DTYPE_C, X_BINNED_DTYPE_C
|
||||
from ...utils._typedefs cimport uint8_t
|
||||
|
||||
|
||||
def _map_to_bins(const X_DTYPE_C [:, :] data,
|
||||
list binning_thresholds,
|
||||
const uint8_t[::1] is_categorical,
|
||||
const uint8_t missing_values_bin_idx,
|
||||
int n_threads,
|
||||
X_BINNED_DTYPE_C [::1, :] binned):
|
||||
"""Bin continuous and categorical values to discrete integer-coded levels.
|
||||
|
||||
A given value x is mapped into bin value i iff
|
||||
thresholds[i - 1] < x <= thresholds[i]
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data : ndarray, shape (n_samples, n_features)
|
||||
The data to bin.
|
||||
binning_thresholds : list of arrays
|
||||
For each feature, stores the increasing numeric values that are
|
||||
used to separate the bins.
|
||||
is_categorical : ndarray of uint8_t of shape (n_features,)
|
||||
Indicates categorical features.
|
||||
n_threads : int
|
||||
Number of OpenMP threads to use.
|
||||
binned : ndarray, shape (n_samples, n_features)
|
||||
Output array, must be fortran aligned.
|
||||
"""
|
||||
cdef:
|
||||
int feature_idx
|
||||
|
||||
for feature_idx in range(data.shape[1]):
|
||||
_map_col_to_bins(
|
||||
data[:, feature_idx],
|
||||
binning_thresholds[feature_idx],
|
||||
is_categorical[feature_idx],
|
||||
missing_values_bin_idx,
|
||||
n_threads,
|
||||
binned[:, feature_idx]
|
||||
)
|
||||
|
||||
|
||||
cdef void _map_col_to_bins(
|
||||
const X_DTYPE_C [:] data,
|
||||
const X_DTYPE_C [:] binning_thresholds,
|
||||
const uint8_t is_categorical,
|
||||
const uint8_t missing_values_bin_idx,
|
||||
int n_threads,
|
||||
X_BINNED_DTYPE_C [:] binned
|
||||
):
|
||||
"""Binary search to find the bin index for each value in the data."""
|
||||
cdef:
|
||||
int i
|
||||
int left
|
||||
int right
|
||||
int middle
|
||||
|
||||
for i in prange(data.shape[0], schedule='static', nogil=True,
|
||||
num_threads=n_threads):
|
||||
if (
|
||||
isnan(data[i]) or
|
||||
# To follow LightGBM's conventions, negative values for
|
||||
# categorical features are considered as missing values.
|
||||
(is_categorical and data[i] < 0)
|
||||
):
|
||||
binned[i] = missing_values_bin_idx
|
||||
else:
|
||||
# for known values, use binary search
|
||||
left, right = 0, binning_thresholds.shape[0]
|
||||
while left < right:
|
||||
# equal to (right + left - 1) // 2 but avoids overflow
|
||||
middle = left + (right - left - 1) // 2
|
||||
if data[i] <= binning_thresholds[middle]:
|
||||
right = middle
|
||||
else:
|
||||
left = middle + 1
|
||||
|
||||
binned[i] = left
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from cython.parallel import prange
|
||||
from libc.math cimport isnan
|
||||
|
||||
from .common cimport X_DTYPE_C, X_BINNED_DTYPE_C
|
||||
from ...utils._typedefs cimport uint8_t
|
||||
|
||||
|
||||
def _map_to_bins(const X_DTYPE_C [:, :] data,
|
||||
list binning_thresholds,
|
||||
const uint8_t[::1] is_categorical,
|
||||
const uint8_t missing_values_bin_idx,
|
||||
int n_threads,
|
||||
X_BINNED_DTYPE_C [::1, :] binned):
|
||||
"""Bin continuous and categorical values to discrete integer-coded levels.
|
||||
|
||||
A given value x is mapped into bin value i iff
|
||||
thresholds[i - 1] < x <= thresholds[i]
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data : ndarray, shape (n_samples, n_features)
|
||||
The data to bin.
|
||||
binning_thresholds : list of arrays
|
||||
For each feature, stores the increasing numeric values that are
|
||||
used to separate the bins.
|
||||
is_categorical : ndarray of uint8_t of shape (n_features,)
|
||||
Indicates categorical features.
|
||||
n_threads : int
|
||||
Number of OpenMP threads to use.
|
||||
binned : ndarray, shape (n_samples, n_features)
|
||||
Output array, must be fortran aligned.
|
||||
"""
|
||||
cdef:
|
||||
int feature_idx
|
||||
|
||||
for feature_idx in range(data.shape[1]):
|
||||
_map_col_to_bins(
|
||||
data[:, feature_idx],
|
||||
binning_thresholds[feature_idx],
|
||||
is_categorical[feature_idx],
|
||||
missing_values_bin_idx,
|
||||
n_threads,
|
||||
binned[:, feature_idx]
|
||||
)
|
||||
|
||||
|
||||
cdef void _map_col_to_bins(
|
||||
const X_DTYPE_C [:] data,
|
||||
const X_DTYPE_C [:] binning_thresholds,
|
||||
const uint8_t is_categorical,
|
||||
const uint8_t missing_values_bin_idx,
|
||||
int n_threads,
|
||||
X_BINNED_DTYPE_C [:] binned
|
||||
):
|
||||
"""Binary search to find the bin index for each value in the data."""
|
||||
cdef:
|
||||
int i
|
||||
int left
|
||||
int right
|
||||
int middle
|
||||
|
||||
for i in prange(data.shape[0], schedule='static', nogil=True,
|
||||
num_threads=n_threads):
|
||||
if (
|
||||
isnan(data[i]) or
|
||||
# To follow LightGBM's conventions, negative values for
|
||||
# categorical features are considered as missing values.
|
||||
(is_categorical and data[i] < 0)
|
||||
):
|
||||
binned[i] = missing_values_bin_idx
|
||||
else:
|
||||
# for known values, use binary search
|
||||
left, right = 0, binning_thresholds.shape[0]
|
||||
while left < right:
|
||||
# equal to (right + left - 1) // 2 but avoids overflow
|
||||
middle = left + (right - left - 1) // 2
|
||||
if data[i] <= binning_thresholds[middle]:
|
||||
right = middle
|
||||
else:
|
||||
left = middle + 1
|
||||
|
||||
binned[i] = left
|
||||
|
||||
+20
-20
@@ -1,20 +1,20 @@
|
||||
from .common cimport X_BINNED_DTYPE_C
|
||||
from .common cimport BITSET_DTYPE_C
|
||||
from .common cimport BITSET_INNER_DTYPE_C
|
||||
from .common cimport X_DTYPE_C
|
||||
from ...utils._typedefs cimport uint8_t
|
||||
|
||||
|
||||
cdef void init_bitset(BITSET_DTYPE_C bitset) noexcept nogil
|
||||
|
||||
cdef void set_bitset(BITSET_DTYPE_C bitset, X_BINNED_DTYPE_C val) noexcept nogil
|
||||
|
||||
cdef uint8_t in_bitset(BITSET_DTYPE_C bitset, X_BINNED_DTYPE_C val) noexcept nogil
|
||||
|
||||
cpdef uint8_t in_bitset_memoryview(const BITSET_INNER_DTYPE_C[:] bitset,
|
||||
X_BINNED_DTYPE_C val) noexcept nogil
|
||||
|
||||
cdef uint8_t in_bitset_2d_memoryview(
|
||||
const BITSET_INNER_DTYPE_C[:, :] bitset,
|
||||
X_BINNED_DTYPE_C val,
|
||||
unsigned int row) noexcept nogil
|
||||
from .common cimport X_BINNED_DTYPE_C
|
||||
from .common cimport BITSET_DTYPE_C
|
||||
from .common cimport BITSET_INNER_DTYPE_C
|
||||
from .common cimport X_DTYPE_C
|
||||
from ...utils._typedefs cimport uint8_t
|
||||
|
||||
|
||||
cdef void init_bitset(BITSET_DTYPE_C bitset) noexcept nogil
|
||||
|
||||
cdef void set_bitset(BITSET_DTYPE_C bitset, X_BINNED_DTYPE_C val) noexcept nogil
|
||||
|
||||
cdef uint8_t in_bitset(BITSET_DTYPE_C bitset, X_BINNED_DTYPE_C val) noexcept nogil
|
||||
|
||||
cpdef uint8_t in_bitset_memoryview(const BITSET_INNER_DTYPE_C[:] bitset,
|
||||
X_BINNED_DTYPE_C val) noexcept nogil
|
||||
|
||||
cdef uint8_t in_bitset_2d_memoryview(
|
||||
const BITSET_INNER_DTYPE_C[:, :] bitset,
|
||||
X_BINNED_DTYPE_C val,
|
||||
unsigned int row) noexcept nogil
|
||||
|
||||
+65
-65
@@ -1,65 +1,65 @@
|
||||
from .common cimport BITSET_INNER_DTYPE_C
|
||||
from .common cimport BITSET_DTYPE_C
|
||||
from .common cimport X_DTYPE_C
|
||||
from .common cimport X_BINNED_DTYPE_C
|
||||
from ...utils._typedefs cimport uint8_t
|
||||
|
||||
|
||||
# A bitset is a data structure used to represent sets of integers in [0, n]. We
|
||||
# use them to represent sets of features indices (e.g. features that go to the
|
||||
# left child, or features that are categorical). For familiarity with bitsets
|
||||
# and bitwise operations:
|
||||
# https://en.wikipedia.org/wiki/Bit_array
|
||||
# https://en.wikipedia.org/wiki/Bitwise_operation
|
||||
|
||||
|
||||
cdef inline void init_bitset(BITSET_DTYPE_C bitset) noexcept nogil: # OUT
|
||||
cdef:
|
||||
unsigned int i
|
||||
|
||||
for i in range(8):
|
||||
bitset[i] = 0
|
||||
|
||||
|
||||
cdef inline void set_bitset(BITSET_DTYPE_C bitset, # OUT
|
||||
X_BINNED_DTYPE_C val) noexcept nogil:
|
||||
bitset[val // 32] |= (1 << (val % 32))
|
||||
|
||||
|
||||
cdef inline uint8_t in_bitset(BITSET_DTYPE_C bitset,
|
||||
X_BINNED_DTYPE_C val) noexcept nogil:
|
||||
return (bitset[val // 32] >> (val % 32)) & 1
|
||||
|
||||
|
||||
cpdef inline uint8_t in_bitset_memoryview(const BITSET_INNER_DTYPE_C[:] bitset,
|
||||
X_BINNED_DTYPE_C val) noexcept nogil:
|
||||
return (bitset[val // 32] >> (val % 32)) & 1
|
||||
|
||||
|
||||
cdef inline uint8_t in_bitset_2d_memoryview(const BITSET_INNER_DTYPE_C[:, :] bitset,
|
||||
X_BINNED_DTYPE_C val,
|
||||
unsigned int row) noexcept nogil:
|
||||
# Same as above but works on 2d memory views to avoid the creation of 1d
|
||||
# memory views. See https://github.com/scikit-learn/scikit-learn/issues/17299
|
||||
return (bitset[row, val // 32] >> (val % 32)) & 1
|
||||
|
||||
|
||||
cpdef inline void set_bitset_memoryview(BITSET_INNER_DTYPE_C[:] bitset, # OUT
|
||||
X_BINNED_DTYPE_C val):
|
||||
bitset[val // 32] |= (1 << (val % 32))
|
||||
|
||||
|
||||
def set_raw_bitset_from_binned_bitset(BITSET_INNER_DTYPE_C[:] raw_bitset, # OUT
|
||||
BITSET_INNER_DTYPE_C[:] binned_bitset,
|
||||
X_DTYPE_C[:] categories):
|
||||
"""Set the raw_bitset from the values of the binned bitset
|
||||
|
||||
categories is a mapping from binned category value to raw category value.
|
||||
"""
|
||||
cdef:
|
||||
int binned_cat_value
|
||||
X_DTYPE_C raw_cat_value
|
||||
|
||||
for binned_cat_value, raw_cat_value in enumerate(categories):
|
||||
if in_bitset_memoryview(binned_bitset, binned_cat_value):
|
||||
set_bitset_memoryview(raw_bitset, <X_BINNED_DTYPE_C>raw_cat_value)
|
||||
from .common cimport BITSET_INNER_DTYPE_C
|
||||
from .common cimport BITSET_DTYPE_C
|
||||
from .common cimport X_DTYPE_C
|
||||
from .common cimport X_BINNED_DTYPE_C
|
||||
from ...utils._typedefs cimport uint8_t
|
||||
|
||||
|
||||
# A bitset is a data structure used to represent sets of integers in [0, n]. We
|
||||
# use them to represent sets of features indices (e.g. features that go to the
|
||||
# left child, or features that are categorical). For familiarity with bitsets
|
||||
# and bitwise operations:
|
||||
# https://en.wikipedia.org/wiki/Bit_array
|
||||
# https://en.wikipedia.org/wiki/Bitwise_operation
|
||||
|
||||
|
||||
cdef inline void init_bitset(BITSET_DTYPE_C bitset) noexcept nogil: # OUT
|
||||
cdef:
|
||||
unsigned int i
|
||||
|
||||
for i in range(8):
|
||||
bitset[i] = 0
|
||||
|
||||
|
||||
cdef inline void set_bitset(BITSET_DTYPE_C bitset, # OUT
|
||||
X_BINNED_DTYPE_C val) noexcept nogil:
|
||||
bitset[val // 32] |= (1 << (val % 32))
|
||||
|
||||
|
||||
cdef inline uint8_t in_bitset(BITSET_DTYPE_C bitset,
|
||||
X_BINNED_DTYPE_C val) noexcept nogil:
|
||||
return (bitset[val // 32] >> (val % 32)) & 1
|
||||
|
||||
|
||||
cpdef inline uint8_t in_bitset_memoryview(const BITSET_INNER_DTYPE_C[:] bitset,
|
||||
X_BINNED_DTYPE_C val) noexcept nogil:
|
||||
return (bitset[val // 32] >> (val % 32)) & 1
|
||||
|
||||
|
||||
cdef inline uint8_t in_bitset_2d_memoryview(const BITSET_INNER_DTYPE_C[:, :] bitset,
|
||||
X_BINNED_DTYPE_C val,
|
||||
unsigned int row) noexcept nogil:
|
||||
# Same as above but works on 2d memory views to avoid the creation of 1d
|
||||
# memory views. See https://github.com/scikit-learn/scikit-learn/issues/17299
|
||||
return (bitset[row, val // 32] >> (val % 32)) & 1
|
||||
|
||||
|
||||
cpdef inline void set_bitset_memoryview(BITSET_INNER_DTYPE_C[:] bitset, # OUT
|
||||
X_BINNED_DTYPE_C val):
|
||||
bitset[val // 32] |= (1 << (val % 32))
|
||||
|
||||
|
||||
def set_raw_bitset_from_binned_bitset(BITSET_INNER_DTYPE_C[:] raw_bitset, # OUT
|
||||
BITSET_INNER_DTYPE_C[:] binned_bitset,
|
||||
X_DTYPE_C[:] categories):
|
||||
"""Set the raw_bitset from the values of the binned bitset
|
||||
|
||||
categories is a mapping from binned category value to raw category value.
|
||||
"""
|
||||
cdef:
|
||||
int binned_cat_value
|
||||
X_DTYPE_C raw_cat_value
|
||||
|
||||
for binned_cat_value, raw_cat_value in enumerate(categories):
|
||||
if in_bitset_memoryview(binned_bitset, binned_cat_value):
|
||||
set_bitset_memoryview(raw_bitset, <X_BINNED_DTYPE_C>raw_cat_value)
|
||||
|
||||
+59
-59
@@ -1,59 +1,59 @@
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from cython.parallel import prange
|
||||
import numpy as np
|
||||
|
||||
from .common import Y_DTYPE
|
||||
from .common cimport Y_DTYPE_C
|
||||
|
||||
|
||||
def _update_raw_predictions(
|
||||
Y_DTYPE_C [::1] raw_predictions, # OUT
|
||||
grower,
|
||||
n_threads,
|
||||
):
|
||||
"""Update raw_predictions with the predictions of the newest tree.
|
||||
|
||||
This is equivalent to (and much faster than):
|
||||
raw_predictions += last_estimator.predict(X_train)
|
||||
|
||||
It's only possible for data X_train that is used to train the trees (it
|
||||
isn't usable for e.g. X_val).
|
||||
"""
|
||||
cdef:
|
||||
unsigned int [::1] starts # start of each leaf in partition
|
||||
unsigned int [::1] stops # end of each leaf in partition
|
||||
Y_DTYPE_C [::1] values # value of each leaf
|
||||
const unsigned int [::1] partition = grower.splitter.partition
|
||||
list leaves
|
||||
|
||||
leaves = grower.finalized_leaves
|
||||
starts = np.array([leaf.partition_start for leaf in leaves],
|
||||
dtype=np.uint32)
|
||||
stops = np.array([leaf.partition_stop for leaf in leaves],
|
||||
dtype=np.uint32)
|
||||
values = np.array([leaf.value for leaf in leaves], dtype=Y_DTYPE)
|
||||
|
||||
_update_raw_predictions_helper(raw_predictions, starts, stops, partition,
|
||||
values, n_threads)
|
||||
|
||||
|
||||
cdef inline void _update_raw_predictions_helper(
|
||||
Y_DTYPE_C [::1] raw_predictions, # OUT
|
||||
const unsigned int [::1] starts,
|
||||
const unsigned int [::1] stops,
|
||||
const unsigned int [::1] partition,
|
||||
const Y_DTYPE_C [::1] values,
|
||||
int n_threads,
|
||||
):
|
||||
|
||||
cdef:
|
||||
unsigned int position
|
||||
int leaf_idx
|
||||
int n_leaves = starts.shape[0]
|
||||
|
||||
for leaf_idx in prange(n_leaves, schedule='static', nogil=True,
|
||||
num_threads=n_threads):
|
||||
for position in range(starts[leaf_idx], stops[leaf_idx]):
|
||||
raw_predictions[partition[position]] += values[leaf_idx]
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from cython.parallel import prange
|
||||
import numpy as np
|
||||
|
||||
from .common import Y_DTYPE
|
||||
from .common cimport Y_DTYPE_C
|
||||
|
||||
|
||||
def _update_raw_predictions(
|
||||
Y_DTYPE_C [::1] raw_predictions, # OUT
|
||||
grower,
|
||||
n_threads,
|
||||
):
|
||||
"""Update raw_predictions with the predictions of the newest tree.
|
||||
|
||||
This is equivalent to (and much faster than):
|
||||
raw_predictions += last_estimator.predict(X_train)
|
||||
|
||||
It's only possible for data X_train that is used to train the trees (it
|
||||
isn't usable for e.g. X_val).
|
||||
"""
|
||||
cdef:
|
||||
unsigned int [::1] starts # start of each leaf in partition
|
||||
unsigned int [::1] stops # end of each leaf in partition
|
||||
Y_DTYPE_C [::1] values # value of each leaf
|
||||
const unsigned int [::1] partition = grower.splitter.partition
|
||||
list leaves
|
||||
|
||||
leaves = grower.finalized_leaves
|
||||
starts = np.array([leaf.partition_start for leaf in leaves],
|
||||
dtype=np.uint32)
|
||||
stops = np.array([leaf.partition_stop for leaf in leaves],
|
||||
dtype=np.uint32)
|
||||
values = np.array([leaf.value for leaf in leaves], dtype=Y_DTYPE)
|
||||
|
||||
_update_raw_predictions_helper(raw_predictions, starts, stops, partition,
|
||||
values, n_threads)
|
||||
|
||||
|
||||
cdef inline void _update_raw_predictions_helper(
|
||||
Y_DTYPE_C [::1] raw_predictions, # OUT
|
||||
const unsigned int [::1] starts,
|
||||
const unsigned int [::1] stops,
|
||||
const unsigned int [::1] partition,
|
||||
const Y_DTYPE_C [::1] values,
|
||||
int n_threads,
|
||||
):
|
||||
|
||||
cdef:
|
||||
unsigned int position
|
||||
int leaf_idx
|
||||
int n_leaves = starts.shape[0]
|
||||
|
||||
for leaf_idx in prange(n_leaves, schedule='static', nogil=True,
|
||||
num_threads=n_threads):
|
||||
for position in range(starts[leaf_idx], stops[leaf_idx]):
|
||||
raw_predictions[partition[position]] += values[leaf_idx]
|
||||
|
||||
+256
-256
@@ -1,256 +1,256 @@
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from cython.parallel import prange
|
||||
from libc.math cimport isnan
|
||||
import numpy as np
|
||||
|
||||
from ...utils._typedefs cimport intp_t, uint8_t
|
||||
from .common cimport X_DTYPE_C
|
||||
from .common cimport Y_DTYPE_C
|
||||
from .common import Y_DTYPE
|
||||
from .common cimport X_BINNED_DTYPE_C
|
||||
from .common cimport BITSET_INNER_DTYPE_C
|
||||
from .common cimport node_struct
|
||||
from ._bitset cimport in_bitset_2d_memoryview
|
||||
|
||||
|
||||
def _predict_from_raw_data( # raw data = non-binned data
|
||||
const node_struct [:] nodes,
|
||||
const X_DTYPE_C [:, :] numeric_data,
|
||||
const BITSET_INNER_DTYPE_C [:, ::1] raw_left_cat_bitsets,
|
||||
const BITSET_INNER_DTYPE_C [:, ::1] known_cat_bitsets,
|
||||
const unsigned int [::1] f_idx_map,
|
||||
int n_threads,
|
||||
Y_DTYPE_C [:] out):
|
||||
|
||||
cdef:
|
||||
int i
|
||||
|
||||
for i in prange(numeric_data.shape[0], schedule='static', nogil=True,
|
||||
num_threads=n_threads):
|
||||
out[i] = _predict_one_from_raw_data(
|
||||
nodes, numeric_data, raw_left_cat_bitsets,
|
||||
known_cat_bitsets,
|
||||
f_idx_map, i)
|
||||
|
||||
|
||||
cdef inline Y_DTYPE_C _predict_one_from_raw_data(
|
||||
const node_struct [:] nodes,
|
||||
const X_DTYPE_C [:, :] numeric_data,
|
||||
const BITSET_INNER_DTYPE_C [:, ::1] raw_left_cat_bitsets,
|
||||
const BITSET_INNER_DTYPE_C [:, ::1] known_cat_bitsets,
|
||||
const unsigned int [::1] f_idx_map,
|
||||
const int row) noexcept nogil:
|
||||
# Need to pass the whole array and the row index, else prange won't work.
|
||||
# See issue Cython #2798
|
||||
|
||||
cdef:
|
||||
node_struct node = nodes[0]
|
||||
unsigned int node_idx = 0
|
||||
X_DTYPE_C data_val
|
||||
|
||||
while True:
|
||||
if node.is_leaf:
|
||||
return node.value
|
||||
|
||||
data_val = numeric_data[row, node.feature_idx]
|
||||
|
||||
if isnan(data_val):
|
||||
if node.missing_go_to_left:
|
||||
node_idx = node.left
|
||||
else:
|
||||
node_idx = node.right
|
||||
elif node.is_categorical:
|
||||
if data_val < 0:
|
||||
# data_val is not in the accepted range, so it is treated as missing value
|
||||
node_idx = node.left if node.missing_go_to_left else node.right
|
||||
elif in_bitset_2d_memoryview(
|
||||
raw_left_cat_bitsets,
|
||||
<X_BINNED_DTYPE_C>data_val,
|
||||
node.bitset_idx):
|
||||
node_idx = node.left
|
||||
elif in_bitset_2d_memoryview(
|
||||
known_cat_bitsets,
|
||||
<X_BINNED_DTYPE_C>data_val,
|
||||
f_idx_map[node.feature_idx]):
|
||||
node_idx = node.right
|
||||
else:
|
||||
# Treat unknown categories as missing.
|
||||
node_idx = node.left if node.missing_go_to_left else node.right
|
||||
else:
|
||||
if data_val <= node.num_threshold:
|
||||
node_idx = node.left
|
||||
else:
|
||||
node_idx = node.right
|
||||
node = nodes[node_idx]
|
||||
|
||||
|
||||
def _predict_from_binned_data(
|
||||
node_struct [:] nodes,
|
||||
const X_BINNED_DTYPE_C [:, :] binned_data,
|
||||
BITSET_INNER_DTYPE_C [:, :] binned_left_cat_bitsets,
|
||||
const uint8_t missing_values_bin_idx,
|
||||
int n_threads,
|
||||
Y_DTYPE_C [:] out):
|
||||
|
||||
cdef:
|
||||
int i
|
||||
|
||||
for i in prange(binned_data.shape[0], schedule='static', nogil=True,
|
||||
num_threads=n_threads):
|
||||
out[i] = _predict_one_from_binned_data(nodes,
|
||||
binned_data,
|
||||
binned_left_cat_bitsets, i,
|
||||
missing_values_bin_idx)
|
||||
|
||||
|
||||
cdef inline Y_DTYPE_C _predict_one_from_binned_data(
|
||||
node_struct [:] nodes,
|
||||
const X_BINNED_DTYPE_C [:, :] binned_data,
|
||||
const BITSET_INNER_DTYPE_C [:, :] binned_left_cat_bitsets,
|
||||
const int row,
|
||||
const uint8_t missing_values_bin_idx) noexcept nogil:
|
||||
# Need to pass the whole array and the row index, else prange won't work.
|
||||
# See issue Cython #2798
|
||||
|
||||
cdef:
|
||||
node_struct node = nodes[0]
|
||||
unsigned int node_idx = 0
|
||||
X_BINNED_DTYPE_C data_val
|
||||
|
||||
while True:
|
||||
if node.is_leaf:
|
||||
return node.value
|
||||
|
||||
data_val = binned_data[row, node.feature_idx]
|
||||
|
||||
if data_val == missing_values_bin_idx:
|
||||
if node.missing_go_to_left:
|
||||
node_idx = node.left
|
||||
else:
|
||||
node_idx = node.right
|
||||
elif node.is_categorical:
|
||||
if in_bitset_2d_memoryview(
|
||||
binned_left_cat_bitsets,
|
||||
data_val,
|
||||
node.bitset_idx):
|
||||
node_idx = node.left
|
||||
else:
|
||||
node_idx = node.right
|
||||
else:
|
||||
if data_val <= node.bin_threshold:
|
||||
node_idx = node.left
|
||||
else:
|
||||
node_idx = node.right
|
||||
node = nodes[node_idx]
|
||||
|
||||
|
||||
def _compute_partial_dependence(
|
||||
node_struct [:] nodes,
|
||||
const X_DTYPE_C [:, ::1] X,
|
||||
const intp_t [:] target_features,
|
||||
Y_DTYPE_C [:] out
|
||||
):
|
||||
"""Partial dependence of the response on the ``target_features`` set.
|
||||
|
||||
For each sample in ``X`` a tree traversal is performed.
|
||||
Each traversal starts from the root with weight 1.0.
|
||||
|
||||
At each non-leaf node that splits on a target feature, either
|
||||
the left child or the right child is visited based on the feature
|
||||
value of the current sample, and the weight is not modified.
|
||||
At each non-leaf node that splits on a complementary feature,
|
||||
both children are visited and the weight is multiplied by the fraction
|
||||
of training samples which went to each child.
|
||||
|
||||
At each leaf, the value of the node is multiplied by the current
|
||||
weight (weights sum to 1 for all visited terminal nodes).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nodes : view on array of PREDICTOR_RECORD_DTYPE, shape (n_nodes)
|
||||
The array representing the predictor tree.
|
||||
X : view on 2d ndarray, shape (n_samples, n_target_features)
|
||||
The grid points on which the partial dependence should be
|
||||
evaluated.
|
||||
target_features : view on 1d ndarray of intp_t, shape (n_target_features)
|
||||
The set of target features for which the partial dependence
|
||||
should be evaluated.
|
||||
out : view on 1d ndarray, shape (n_samples)
|
||||
The value of the partial dependence function on each grid
|
||||
point.
|
||||
"""
|
||||
|
||||
cdef:
|
||||
unsigned int current_node_idx
|
||||
unsigned int [:] node_idx_stack = np.zeros(shape=nodes.shape[0],
|
||||
dtype=np.uint32)
|
||||
Y_DTYPE_C [::1] weight_stack = np.zeros(shape=nodes.shape[0],
|
||||
dtype=Y_DTYPE)
|
||||
node_struct * current_node # pointer to avoid copying attributes
|
||||
|
||||
unsigned int sample_idx
|
||||
intp_t feature_idx
|
||||
unsigned stack_size
|
||||
Y_DTYPE_C left_sample_frac
|
||||
Y_DTYPE_C current_weight
|
||||
Y_DTYPE_C total_weight # used for sanity check only
|
||||
bint is_target_feature
|
||||
|
||||
for sample_idx in range(X.shape[0]):
|
||||
# init stacks for current sample
|
||||
stack_size = 1
|
||||
node_idx_stack[0] = 0 # root node
|
||||
weight_stack[0] = 1 # all the samples are in the root node
|
||||
total_weight = 0
|
||||
|
||||
while stack_size > 0:
|
||||
|
||||
# pop the stack
|
||||
stack_size -= 1
|
||||
current_node_idx = node_idx_stack[stack_size]
|
||||
current_node = &nodes[current_node_idx]
|
||||
|
||||
if current_node.is_leaf:
|
||||
out[sample_idx] += (weight_stack[stack_size] *
|
||||
current_node.value)
|
||||
total_weight += weight_stack[stack_size]
|
||||
else:
|
||||
# determine if the split feature is a target feature
|
||||
is_target_feature = False
|
||||
for feature_idx in range(target_features.shape[0]):
|
||||
if target_features[feature_idx] == current_node.feature_idx:
|
||||
is_target_feature = True
|
||||
break
|
||||
|
||||
if is_target_feature:
|
||||
# In this case, we push left or right child on stack
|
||||
if X[sample_idx, feature_idx] <= current_node.num_threshold:
|
||||
node_idx_stack[stack_size] = current_node.left
|
||||
else:
|
||||
node_idx_stack[stack_size] = current_node.right
|
||||
stack_size += 1
|
||||
else:
|
||||
# In this case, we push both children onto the stack,
|
||||
# and give a weight proportional to the number of
|
||||
# samples going through each branch.
|
||||
|
||||
# push left child
|
||||
node_idx_stack[stack_size] = current_node.left
|
||||
left_sample_frac = (
|
||||
<Y_DTYPE_C> nodes[current_node.left].count /
|
||||
current_node.count)
|
||||
current_weight = weight_stack[stack_size]
|
||||
weight_stack[stack_size] = current_weight * left_sample_frac
|
||||
stack_size += 1
|
||||
|
||||
# push right child
|
||||
node_idx_stack[stack_size] = current_node.right
|
||||
weight_stack[stack_size] = (
|
||||
current_weight * (1 - left_sample_frac))
|
||||
stack_size += 1
|
||||
|
||||
# Sanity check. Should never happen.
|
||||
if not (0.999 < total_weight < 1.001):
|
||||
raise ValueError("Total weight should be 1.0 but was %.9f" %total_weight)
|
||||
# Authors: The scikit-learn developers
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from cython.parallel import prange
|
||||
from libc.math cimport isnan
|
||||
import numpy as np
|
||||
|
||||
from ...utils._typedefs cimport intp_t, uint8_t
|
||||
from .common cimport X_DTYPE_C
|
||||
from .common cimport Y_DTYPE_C
|
||||
from .common import Y_DTYPE
|
||||
from .common cimport X_BINNED_DTYPE_C
|
||||
from .common cimport BITSET_INNER_DTYPE_C
|
||||
from .common cimport node_struct
|
||||
from ._bitset cimport in_bitset_2d_memoryview
|
||||
|
||||
|
||||
def _predict_from_raw_data( # raw data = non-binned data
|
||||
const node_struct [:] nodes,
|
||||
const X_DTYPE_C [:, :] numeric_data,
|
||||
const BITSET_INNER_DTYPE_C [:, ::1] raw_left_cat_bitsets,
|
||||
const BITSET_INNER_DTYPE_C [:, ::1] known_cat_bitsets,
|
||||
const unsigned int [::1] f_idx_map,
|
||||
int n_threads,
|
||||
Y_DTYPE_C [:] out):
|
||||
|
||||
cdef:
|
||||
int i
|
||||
|
||||
for i in prange(numeric_data.shape[0], schedule='static', nogil=True,
|
||||
num_threads=n_threads):
|
||||
out[i] = _predict_one_from_raw_data(
|
||||
nodes, numeric_data, raw_left_cat_bitsets,
|
||||
known_cat_bitsets,
|
||||
f_idx_map, i)
|
||||
|
||||
|
||||
cdef inline Y_DTYPE_C _predict_one_from_raw_data(
|
||||
const node_struct [:] nodes,
|
||||
const X_DTYPE_C [:, :] numeric_data,
|
||||
const BITSET_INNER_DTYPE_C [:, ::1] raw_left_cat_bitsets,
|
||||
const BITSET_INNER_DTYPE_C [:, ::1] known_cat_bitsets,
|
||||
const unsigned int [::1] f_idx_map,
|
||||
const int row) noexcept nogil:
|
||||
# Need to pass the whole array and the row index, else prange won't work.
|
||||
# See issue Cython #2798
|
||||
|
||||
cdef:
|
||||
node_struct node = nodes[0]
|
||||
unsigned int node_idx = 0
|
||||
X_DTYPE_C data_val
|
||||
|
||||
while True:
|
||||
if node.is_leaf:
|
||||
return node.value
|
||||
|
||||
data_val = numeric_data[row, node.feature_idx]
|
||||
|
||||
if isnan(data_val):
|
||||
if node.missing_go_to_left:
|
||||
node_idx = node.left
|
||||
else:
|
||||
node_idx = node.right
|
||||
elif node.is_categorical:
|
||||
if data_val < 0:
|
||||
# data_val is not in the accepted range, so it is treated as missing value
|
||||
node_idx = node.left if node.missing_go_to_left else node.right
|
||||
elif in_bitset_2d_memoryview(
|
||||
raw_left_cat_bitsets,
|
||||
<X_BINNED_DTYPE_C>data_val,
|
||||
node.bitset_idx):
|
||||
node_idx = node.left
|
||||
elif in_bitset_2d_memoryview(
|
||||
known_cat_bitsets,
|
||||
<X_BINNED_DTYPE_C>data_val,
|
||||
f_idx_map[node.feature_idx]):
|
||||
node_idx = node.right
|
||||
else:
|
||||
# Treat unknown categories as missing.
|
||||
node_idx = node.left if node.missing_go_to_left else node.right
|
||||
else:
|
||||
if data_val <= node.num_threshold:
|
||||
node_idx = node.left
|
||||
else:
|
||||
node_idx = node.right
|
||||
node = nodes[node_idx]
|
||||
|
||||
|
||||
def _predict_from_binned_data(
|
||||
node_struct [:] nodes,
|
||||
const X_BINNED_DTYPE_C [:, :] binned_data,
|
||||
BITSET_INNER_DTYPE_C [:, :] binned_left_cat_bitsets,
|
||||
const uint8_t missing_values_bin_idx,
|
||||
int n_threads,
|
||||
Y_DTYPE_C [:] out):
|
||||
|
||||
cdef:
|
||||
int i
|
||||
|
||||
for i in prange(binned_data.shape[0], schedule='static', nogil=True,
|
||||
num_threads=n_threads):
|
||||
out[i] = _predict_one_from_binned_data(nodes,
|
||||
binned_data,
|
||||
binned_left_cat_bitsets, i,
|
||||
missing_values_bin_idx)
|
||||
|
||||
|
||||
cdef inline Y_DTYPE_C _predict_one_from_binned_data(
|
||||
node_struct [:] nodes,
|
||||
const X_BINNED_DTYPE_C [:, :] binned_data,
|
||||
const BITSET_INNER_DTYPE_C [:, :] binned_left_cat_bitsets,
|
||||
const int row,
|
||||
const uint8_t missing_values_bin_idx) noexcept nogil:
|
||||
# Need to pass the whole array and the row index, else prange won't work.
|
||||
# See issue Cython #2798
|
||||
|
||||
cdef:
|
||||
node_struct node = nodes[0]
|
||||
unsigned int node_idx = 0
|
||||
X_BINNED_DTYPE_C data_val
|
||||
|
||||
while True:
|
||||
if node.is_leaf:
|
||||
return node.value
|
||||
|
||||
data_val = binned_data[row, node.feature_idx]
|
||||
|
||||
if data_val == missing_values_bin_idx:
|
||||
if node.missing_go_to_left:
|
||||
node_idx = node.left
|
||||
else:
|
||||
node_idx = node.right
|
||||
elif node.is_categorical:
|
||||
if in_bitset_2d_memoryview(
|
||||
binned_left_cat_bitsets,
|
||||
data_val,
|
||||
node.bitset_idx):
|
||||
node_idx = node.left
|
||||
else:
|
||||
node_idx = node.right
|
||||
else:
|
||||
if data_val <= node.bin_threshold:
|
||||
node_idx = node.left
|
||||
else:
|
||||
node_idx = node.right
|
||||
node = nodes[node_idx]
|
||||
|
||||
|
||||
def _compute_partial_dependence(
|
||||
node_struct [:] nodes,
|
||||
const X_DTYPE_C [:, ::1] X,
|
||||
const intp_t [:] target_features,
|
||||
Y_DTYPE_C [:] out
|
||||
):
|
||||
"""Partial dependence of the response on the ``target_features`` set.
|
||||
|
||||
For each sample in ``X`` a tree traversal is performed.
|
||||
Each traversal starts from the root with weight 1.0.
|
||||
|
||||
At each non-leaf node that splits on a target feature, either
|
||||
the left child or the right child is visited based on the feature
|
||||
value of the current sample, and the weight is not modified.
|
||||
At each non-leaf node that splits on a complementary feature,
|
||||
both children are visited and the weight is multiplied by the fraction
|
||||
of training samples which went to each child.
|
||||
|
||||
At each leaf, the value of the node is multiplied by the current
|
||||
weight (weights sum to 1 for all visited terminal nodes).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nodes : view on array of PREDICTOR_RECORD_DTYPE, shape (n_nodes)
|
||||
The array representing the predictor tree.
|
||||
X : view on 2d ndarray, shape (n_samples, n_target_features)
|
||||
The grid points on which the partial dependence should be
|
||||
evaluated.
|
||||
target_features : view on 1d ndarray of intp_t, shape (n_target_features)
|
||||
The set of target features for which the partial dependence
|
||||
should be evaluated.
|
||||
out : view on 1d ndarray, shape (n_samples)
|
||||
The value of the partial dependence function on each grid
|
||||
point.
|
||||
"""
|
||||
|
||||
cdef:
|
||||
unsigned int current_node_idx
|
||||
unsigned int [:] node_idx_stack = np.zeros(shape=nodes.shape[0],
|
||||
dtype=np.uint32)
|
||||
Y_DTYPE_C [::1] weight_stack = np.zeros(shape=nodes.shape[0],
|
||||
dtype=Y_DTYPE)
|
||||
node_struct * current_node # pointer to avoid copying attributes
|
||||
|
||||
unsigned int sample_idx
|
||||
intp_t feature_idx
|
||||
unsigned stack_size
|
||||
Y_DTYPE_C left_sample_frac
|
||||
Y_DTYPE_C current_weight
|
||||
Y_DTYPE_C total_weight # used for sanity check only
|
||||
bint is_target_feature
|
||||
|
||||
for sample_idx in range(X.shape[0]):
|
||||
# init stacks for current sample
|
||||
stack_size = 1
|
||||
node_idx_stack[0] = 0 # root node
|
||||
weight_stack[0] = 1 # all the samples are in the root node
|
||||
total_weight = 0
|
||||
|
||||
while stack_size > 0:
|
||||
|
||||
# pop the stack
|
||||
stack_size -= 1
|
||||
current_node_idx = node_idx_stack[stack_size]
|
||||
current_node = &nodes[current_node_idx]
|
||||
|
||||
if current_node.is_leaf:
|
||||
out[sample_idx] += (weight_stack[stack_size] *
|
||||
current_node.value)
|
||||
total_weight += weight_stack[stack_size]
|
||||
else:
|
||||
# determine if the split feature is a target feature
|
||||
is_target_feature = False
|
||||
for feature_idx in range(target_features.shape[0]):
|
||||
if target_features[feature_idx] == current_node.feature_idx:
|
||||
is_target_feature = True
|
||||
break
|
||||
|
||||
if is_target_feature:
|
||||
# In this case, we push left or right child on stack
|
||||
if X[sample_idx, feature_idx] <= current_node.num_threshold:
|
||||
node_idx_stack[stack_size] = current_node.left
|
||||
else:
|
||||
node_idx_stack[stack_size] = current_node.right
|
||||
stack_size += 1
|
||||
else:
|
||||
# In this case, we push both children onto the stack,
|
||||
# and give a weight proportional to the number of
|
||||
# samples going through each branch.
|
||||
|
||||
# push left child
|
||||
node_idx_stack[stack_size] = current_node.left
|
||||
left_sample_frac = (
|
||||
<Y_DTYPE_C> nodes[current_node.left].count /
|
||||
current_node.count)
|
||||
current_weight = weight_stack[stack_size]
|
||||
weight_stack[stack_size] = current_weight * left_sample_frac
|
||||
stack_size += 1
|
||||
|
||||
# push right child
|
||||
node_idx_stack[stack_size] = current_node.right
|
||||
weight_stack[stack_size] = (
|
||||
current_weight * (1 - left_sample_frac))
|
||||
stack_size += 1
|
||||
|
||||
# Sanity check. Should never happen.
|
||||
if not (0.999 < total_weight < 1.001):
|
||||
raise ValueError("Total weight should be 1.0 but was %.9f" %total_weight)
|
||||
|
||||
+43
-43
@@ -1,43 +1,43 @@
|
||||
from ...utils._typedefs cimport float32_t, float64_t, intp_t, uint8_t, uint32_t
|
||||
|
||||
|
||||
ctypedef float64_t X_DTYPE_C
|
||||
ctypedef uint8_t X_BINNED_DTYPE_C
|
||||
ctypedef float64_t Y_DTYPE_C
|
||||
ctypedef float32_t G_H_DTYPE_C
|
||||
ctypedef uint32_t BITSET_INNER_DTYPE_C
|
||||
ctypedef BITSET_INNER_DTYPE_C[8] BITSET_DTYPE_C
|
||||
|
||||
|
||||
cdef packed struct hist_struct:
|
||||
# Same as histogram dtype but we need a struct to declare views. It needs
|
||||
# to be packed since by default numpy dtypes aren't aligned
|
||||
Y_DTYPE_C sum_gradients
|
||||
Y_DTYPE_C sum_hessians
|
||||
unsigned int count
|
||||
|
||||
|
||||
cdef packed struct node_struct:
|
||||
# Equivalent struct to PREDICTOR_RECORD_DTYPE to use in memory views. It
|
||||
# needs to be packed since by default numpy dtypes aren't aligned
|
||||
Y_DTYPE_C value
|
||||
unsigned int count
|
||||
intp_t feature_idx
|
||||
X_DTYPE_C num_threshold
|
||||
uint8_t missing_go_to_left
|
||||
unsigned int left
|
||||
unsigned int right
|
||||
Y_DTYPE_C gain
|
||||
unsigned int depth
|
||||
uint8_t is_leaf
|
||||
X_BINNED_DTYPE_C bin_threshold
|
||||
uint8_t is_categorical
|
||||
# The index of the corresponding bitsets in the Predictor's bitset arrays.
|
||||
# Only used if is_categorical is True
|
||||
unsigned int bitset_idx
|
||||
|
||||
|
||||
cpdef enum MonotonicConstraint:
|
||||
NO_CST = 0
|
||||
POS = 1
|
||||
NEG = -1
|
||||
from ...utils._typedefs cimport float32_t, float64_t, intp_t, uint8_t, uint32_t
|
||||
|
||||
|
||||
ctypedef float64_t X_DTYPE_C
|
||||
ctypedef uint8_t X_BINNED_DTYPE_C
|
||||
ctypedef float64_t Y_DTYPE_C
|
||||
ctypedef float32_t G_H_DTYPE_C
|
||||
ctypedef uint32_t BITSET_INNER_DTYPE_C
|
||||
ctypedef BITSET_INNER_DTYPE_C[8] BITSET_DTYPE_C
|
||||
|
||||
|
||||
cdef packed struct hist_struct:
|
||||
# Same as histogram dtype but we need a struct to declare views. It needs
|
||||
# to be packed since by default numpy dtypes aren't aligned
|
||||
Y_DTYPE_C sum_gradients
|
||||
Y_DTYPE_C sum_hessians
|
||||
unsigned int count
|
||||
|
||||
|
||||
cdef packed struct node_struct:
|
||||
# Equivalent struct to PREDICTOR_RECORD_DTYPE to use in memory views. It
|
||||
# needs to be packed since by default numpy dtypes aren't aligned
|
||||
Y_DTYPE_C value
|
||||
unsigned int count
|
||||
intp_t feature_idx
|
||||
X_DTYPE_C num_threshold
|
||||
uint8_t missing_go_to_left
|
||||
unsigned int left
|
||||
unsigned int right
|
||||
Y_DTYPE_C gain
|
||||
unsigned int depth
|
||||
uint8_t is_leaf
|
||||
X_BINNED_DTYPE_C bin_threshold
|
||||
uint8_t is_categorical
|
||||
# The index of the corresponding bitsets in the Predictor's bitset arrays.
|
||||
# Only used if is_categorical is True
|
||||
unsigned int bitset_idx
|
||||
|
||||
|
||||
cpdef enum MonotonicConstraint:
|
||||
NO_CST = 0
|
||||
POS = 1
|
||||
NEG = -1
|
||||
|
||||
+44
-44
@@ -1,44 +1,44 @@
|
||||
import numpy as np
|
||||
|
||||
# Y_DYTPE is the dtype to which the targets y are converted to. This is also
|
||||
# dtype for leaf values, gains, and sums of gradients / hessians. The gradients
|
||||
# and hessians arrays are stored as floats to avoid using too much memory.
|
||||
Y_DTYPE = np.float64
|
||||
X_DTYPE = np.float64
|
||||
X_BINNED_DTYPE = np.uint8 # hence max_bins == 256
|
||||
# dtype for gradients and hessians arrays
|
||||
G_H_DTYPE = np.float32
|
||||
X_BITSET_INNER_DTYPE = np.uint32
|
||||
|
||||
# Note that we use Y_DTYPE=float64 to avoid issues with floating point precision when
|
||||
# summing gradients and hessians (both float32). Those are difficult to protect via
|
||||
# tools like (Kahan-) Neumaier summation as in CPython, see
|
||||
# https://github.com/python/cpython/issues/100425, or pairwise summation as numpy, see
|
||||
# https://github.com/numpy/numpy/pull/3685, due to the way histograms are summed
|
||||
# (number of additions per bin is not known in advance). See also comment in
|
||||
# _subtract_histograms.
|
||||
HISTOGRAM_DTYPE = np.dtype([
|
||||
('sum_gradients', Y_DTYPE), # sum of sample gradients in bin
|
||||
('sum_hessians', Y_DTYPE), # sum of sample hessians in bin
|
||||
('count', np.uint32), # number of samples in bin
|
||||
])
|
||||
|
||||
PREDICTOR_RECORD_DTYPE = np.dtype([
|
||||
('value', Y_DTYPE),
|
||||
('count', np.uint32),
|
||||
('feature_idx', np.intp),
|
||||
('num_threshold', X_DTYPE),
|
||||
('missing_go_to_left', np.uint8),
|
||||
('left', np.uint32),
|
||||
('right', np.uint32),
|
||||
('gain', Y_DTYPE),
|
||||
('depth', np.uint32),
|
||||
('is_leaf', np.uint8),
|
||||
('bin_threshold', X_BINNED_DTYPE),
|
||||
('is_categorical', np.uint8),
|
||||
# The index of the corresponding bitsets in the Predictor's bitset arrays.
|
||||
# Only used if is_categorical is True
|
||||
('bitset_idx', np.uint32)
|
||||
])
|
||||
|
||||
ALMOST_INF = 1e300 # see LightGBM AvoidInf()
|
||||
import numpy as np
|
||||
|
||||
# Y_DYTPE is the dtype to which the targets y are converted to. This is also
|
||||
# dtype for leaf values, gains, and sums of gradients / hessians. The gradients
|
||||
# and hessians arrays are stored as floats to avoid using too much memory.
|
||||
Y_DTYPE = np.float64
|
||||
X_DTYPE = np.float64
|
||||
X_BINNED_DTYPE = np.uint8 # hence max_bins == 256
|
||||
# dtype for gradients and hessians arrays
|
||||
G_H_DTYPE = np.float32
|
||||
X_BITSET_INNER_DTYPE = np.uint32
|
||||
|
||||
# Note that we use Y_DTYPE=float64 to avoid issues with floating point precision when
|
||||
# summing gradients and hessians (both float32). Those are difficult to protect via
|
||||
# tools like (Kahan-) Neumaier summation as in CPython, see
|
||||
# https://github.com/python/cpython/issues/100425, or pairwise summation as numpy, see
|
||||
# https://github.com/numpy/numpy/pull/3685, due to the way histograms are summed
|
||||
# (number of additions per bin is not known in advance). See also comment in
|
||||
# _subtract_histograms.
|
||||
HISTOGRAM_DTYPE = np.dtype([
|
||||
('sum_gradients', Y_DTYPE), # sum of sample gradients in bin
|
||||
('sum_hessians', Y_DTYPE), # sum of sample hessians in bin
|
||||
('count', np.uint32), # number of samples in bin
|
||||
])
|
||||
|
||||
PREDICTOR_RECORD_DTYPE = np.dtype([
|
||||
('value', Y_DTYPE),
|
||||
('count', np.uint32),
|
||||
('feature_idx', np.intp),
|
||||
('num_threshold', X_DTYPE),
|
||||
('missing_go_to_left', np.uint8),
|
||||
('left', np.uint32),
|
||||
('right', np.uint32),
|
||||
('gain', Y_DTYPE),
|
||||
('depth', np.uint32),
|
||||
('is_leaf', np.uint8),
|
||||
('bin_threshold', X_BINNED_DTYPE),
|
||||
('is_categorical', np.uint8),
|
||||
# The index of the corresponding bitsets in the Predictor's bitset arrays.
|
||||
# Only used if is_categorical is True
|
||||
('bitset_idx', np.uint32)
|
||||
])
|
||||
|
||||
ALMOST_INF = 1e300 # see LightGBM AvoidInf()
|
||||
|
||||
+520
-520
File diff suppressed because it is too large
Load Diff
+20
-20
@@ -1,20 +1,20 @@
|
||||
hist_gradient_boosting_extension_metadata = {
|
||||
'_gradient_boosting': {'sources': [cython_gen.process('_gradient_boosting.pyx')],
|
||||
'dependencies': [openmp_dep]},
|
||||
'histogram': {'sources': [cython_gen.process('histogram.pyx')], 'dependencies': [openmp_dep]},
|
||||
'splitting': {'sources': [cython_gen.process('splitting.pyx')], 'dependencies': [openmp_dep]},
|
||||
'_binning': {'sources': [cython_gen.process('_binning.pyx')], 'dependencies': [openmp_dep]},
|
||||
'_predictor': {'sources': [cython_gen.process('_predictor.pyx')], 'dependencies': [openmp_dep]},
|
||||
'_bitset': {'sources': [cython_gen.process('_bitset.pyx')]},
|
||||
'common': {'sources': [cython_gen.process('common.pyx')]},
|
||||
}
|
||||
|
||||
foreach ext_name, ext_dict : hist_gradient_boosting_extension_metadata
|
||||
py.extension_module(
|
||||
ext_name,
|
||||
ext_dict.get('sources'),
|
||||
dependencies: ext_dict.get('dependencies', []),
|
||||
subdir: 'sklearn/ensemble/_hist_gradient_boosting',
|
||||
install: true
|
||||
)
|
||||
endforeach
|
||||
hist_gradient_boosting_extension_metadata = {
|
||||
'_gradient_boosting': {'sources': [cython_gen.process('_gradient_boosting.pyx')],
|
||||
'dependencies': [openmp_dep]},
|
||||
'histogram': {'sources': [cython_gen.process('histogram.pyx')], 'dependencies': [openmp_dep]},
|
||||
'splitting': {'sources': [cython_gen.process('splitting.pyx')], 'dependencies': [openmp_dep]},
|
||||
'_binning': {'sources': [cython_gen.process('_binning.pyx')], 'dependencies': [openmp_dep]},
|
||||
'_predictor': {'sources': [cython_gen.process('_predictor.pyx')], 'dependencies': [openmp_dep]},
|
||||
'_bitset': {'sources': [cython_gen.process('_bitset.pyx')]},
|
||||
'common': {'sources': [cython_gen.process('common.pyx')]},
|
||||
}
|
||||
|
||||
foreach ext_name, ext_dict : hist_gradient_boosting_extension_metadata
|
||||
py.extension_module(
|
||||
ext_name,
|
||||
ext_dict.get('sources'),
|
||||
dependencies: ext_dict.get('dependencies', []),
|
||||
subdir: 'sklearn/ensemble/_hist_gradient_boosting',
|
||||
install: true
|
||||
)
|
||||
endforeach
|
||||
|
||||
+1201
-1201
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
py.extension_module(
|
||||
'_gradient_boosting',
|
||||
[cython_gen.process('_gradient_boosting.pyx')] + utils_cython_tree,
|
||||
dependencies: [np_dep],
|
||||
subdir: 'sklearn/ensemble',
|
||||
install: true
|
||||
)
|
||||
|
||||
subdir('_hist_gradient_boosting')
|
||||
py.extension_module(
|
||||
'_gradient_boosting',
|
||||
[cython_gen.process('_gradient_boosting.pyx')] + utils_cython_tree,
|
||||
dependencies: [np_dep],
|
||||
subdir: 'sklearn/ensemble',
|
||||
install: true
|
||||
)
|
||||
|
||||
subdir('_hist_gradient_boosting')
|
||||
|
||||
Reference in New Issue
Block a user