Skip to content

Commit b466047

Browse files
implemented suggestions
1 parent e6ea8a3 commit b466047

3 files changed

Lines changed: 43 additions & 68 deletions

File tree

pygbm/grower.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
import numpy as np
99
from time import time
1010

11-
from .splitting import (SplittingContext, split_indices_parallel,
12-
split_indices_single_thread, find_node_split,
11+
from .splitting import (SplittingContext, find_node_split,
1312
find_node_split_subtraction)
1413
from .predictor import TreePredictor, PREDICTOR_RECORD_DTYPE
1514

@@ -189,7 +188,6 @@ def __init__(self, X_binned, gradients, hessians, max_leaf_nodes=None,
189188
self.X_binned = X_binned
190189
self.min_gain_to_split = min_gain_to_split
191190
self.shrinkage = shrinkage
192-
self.parallel_splitting = parallel_splitting
193191
self.splittable_nodes = []
194192
self.finalized_leaves = []
195193
self.total_find_split_time = 0. # time spent finding the best splits
@@ -339,9 +337,9 @@ def split_next(self):
339337
node = heappop(self.splittable_nodes)
340338

341339
tic = time()
342-
split_indices = split_indices_parallel if self.parallel_splitting else split_indices_single_thread
343-
(sample_indices_left, sample_indices_right) = split_indices(
344-
self.splitting_context, node.split_info, node.sample_indices)
340+
(sample_indices_left, sample_indices_right) = \
341+
self.splitting_context.split_indices(node.split_info,
342+
node.sample_indices)
345343
toc = time()
346344
node.apply_split_time = toc - tic
347345
self.total_apply_split_time += node.apply_split_time

pygbm/splitting.py

Lines changed: 33 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -171,35 +171,39 @@ def __init__(self, X_binned, max_bins, n_bins_per_feature,
171171
self.right_indices_buffer = np.empty_like(self.partition)
172172
self.left_indices_buffer = np.empty_like(self.partition)
173173

174+
def split_indices(self, split_info, sample_indices):
175+
"""Split samples into left and right arrays.
176+
177+
Parameters
178+
----------
179+
split_info : SplitInfo
180+
The SplitInfo of the node to split
181+
sample_indices : array of int
182+
The indices of the samples at the node to split. This is a view on
183+
context.partition, and it is modified inplace by placing the indices
184+
of the left child at the beginning, and the indices of the right child
185+
at the end.
186+
187+
Returns
188+
-------
189+
left_indices : array of int
190+
The indices of the samples in the left child. This is a view on
191+
context.partition.
192+
right_indices : array of int
193+
The indices of the samples in the right child. This is a view on
194+
context.partition.
195+
"""
196+
if self.parallel_splitting:
197+
return _split_indices_parallel(self, split_info, sample_indices)
198+
else:
199+
return _split_indices_single_threaded(self, split_info, sample_indices)
200+
174201

175202
@njit(parallel=True,
176203
locals={'sample_idx': uint32,
177204
'left_count': uint32,
178205
'right_count': uint32})
179-
def split_indices_parallel(context, split_info, sample_indices):
180-
"""Split samples into left and right arrays.
181-
182-
Parameters
183-
----------
184-
context : SplittingContext
185-
The splitting context
186-
split_ingo : SplitInfo
187-
The SplitInfo of the node to split
188-
sample_indices : array of int
189-
The indices of the samples at the node to split. This is a view on
190-
context.partition, and it is modified inplace by placing the indices
191-
of the left child at the beginning, and the indices of the right child
192-
at the end.
193-
194-
Returns
195-
-------
196-
left_indices : array of int
197-
The indices of the samples in the left child. This is a view on
198-
context.partition.
199-
right_indices : array of int
200-
The indices of the samples in the right child. This is a view on
201-
context.partition.
202-
"""
206+
def _split_indices_parallel(context, split_info, sample_indices):
203207
# This is a multi-threaded implementation inspired by lightgbm.
204208
# Here is a quick break down. Let's suppose we want to split a node with
205209
# 24 samples named from a to x. context.partition looks like this (the *
@@ -309,47 +313,20 @@ def split_indices_parallel(context, split_info, sample_indices):
309313
sample_indices[right_child_position:])
310314

311315
@njit(parallel=False)
312-
def split_indices_single_thread(context, split_info, sample_indices):
313-
"""Split samples into left and right arrays.
314-
315-
This implementation requires less memory than the parallel version.
316-
317-
Parameters
318-
----------
319-
context : SplittingContext
320-
The splitting context
321-
split_ingo : SplitInfo
322-
The SplitInfo of the node to split
323-
sample_indices : array of int
324-
The indices of the samples at the node to split. This is a view on
325-
context.partition, and it is modified inplace by placing the indices
326-
of the left child at the beginning, and the indices of the right child
327-
at the end.
328-
329-
Returns
330-
-------
331-
left_indices : array of int
332-
The indices of the samples in the left child. This is a view on
333-
context.partition.
334-
right_indices : array of int
335-
The indices of the samples in the right child. This is a view on
336-
context.partition.
337-
"""
338-
X_binned = context.X_binned.T[split_info.feature_idx]
316+
def _split_indices_single_threaded(context, split_info, sample_indices):
317+
binned_feature = context.X_binned.T[split_info.feature_idx]
339318
n_samples = sample_indices.shape[0]
340-
341319
# approach from left with i
342320
i = 0
343321
# approach from right with j
344322
j = n_samples - 1
345-
X = X_binned
346323
pivot = split_info.bin_idx
347324
while i != j:
348325
# continue until we find an element that should be on right
349-
while X[sample_indices[i]] <= pivot and i < n_samples:
326+
while binned_feature[sample_indices[i]] <= pivot and i < n_samples:
350327
i += 1
351328
# same, but now an element that should be on the left
352-
while X[sample_indices[j]] > pivot and j >= 0:
329+
while binned_feature[sample_indices[j]] > pivot and j >= 0:
353330
j -= 1
354331
if i >= j: # j can become smaller than j!
355332
break
@@ -358,8 +335,7 @@ def split_indices_single_thread(context, split_info, sample_indices):
358335
sample_indices[i], sample_indices[j] = sample_indices[j], sample_indices[i]
359336
i += 1
360337
j -= 1
361-
return (sample_indices[:i],
362-
sample_indices[i:])
338+
return (sample_indices[:i], sample_indices[i:])
363339

364340

365341
@njit(parallel=True)

tests/test_splitting.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pygbm.splitting import _find_histogram_split
77
from pygbm.splitting import (SplittingContext, find_node_split,
88
find_node_split_subtraction,
9-
split_indices_parallel, split_indices_single_thread)
9+
_split_indices_parallel, _split_indices_single_threaded)
1010

1111

1212
@pytest.mark.parametrize('n_bins', [3, 32, 256])
@@ -271,8 +271,8 @@ def test_split_indices():
271271
assert si_root.feature_idx == 1
272272
assert si_root.bin_idx == 3
273273

274-
samples_left, samples_right = split_indices_parallel(
275-
context, si_root, context.partition.view())
274+
samples_left, samples_right = context.split_indices(
275+
si_root, context.partition.view())
276276
assert set(samples_left) == set([0, 1, 3, 4, 5, 6, 8])
277277
assert set(samples_right) == set([2, 7, 9])
278278

@@ -288,8 +288,9 @@ def test_split_indices():
288288
assert samples_left.shape[0] == si_root.n_samples_left
289289
assert samples_right.shape[0] == si_root.n_samples_right
290290

291-
samples_left_single_thread, samples_right_single_thread = split_indices_single_thread(
292-
context, si_root, context.partition.view())
291+
# test if the single thread version gives the same result
292+
samples_left_single_thread, samples_right_single_thread = \
293+
_split_indices_single_threaded(context, si_root, context.partition.view())
293294

294295
assert samples_left.tolist() == samples_left_single_thread.tolist()
295296
assert samples_right.tolist() == samples_right_single_thread.tolist()

0 commit comments

Comments
 (0)