1
1
"""
2
2
Testing for the gradient boosting loss functions and initial estimators.
3
3
"""
4
-
4
+ from itertools import product
5
5
import numpy as np
6
- from numpy .testing import assert_almost_equal
7
6
from numpy .testing import assert_allclose
8
7
import pytest
8
+ from pytest import approx
9
9
10
10
from sklearn .utils import check_random_state
11
11
from sklearn .ensemble ._gb_losses import RegressionLossFunction
@@ -25,35 +25,37 @@ def test_binomial_deviance():
25
25
bd = BinomialDeviance (2 )
26
26
27
27
# pred has the same BD for y in {0, 1}
28
- assert (bd (np .array ([0.0 ]), np .array ([0.0 ])) ==
29
- bd (np .array ([1.0 ]), np .array ([0.0 ])))
30
-
31
- assert_almost_equal ( bd (np .array ([1.0 , 1.0 , 1.0 ]),
32
- np .array ([100.0 , 100.0 , 100.0 ])),
33
- 0.0 )
34
- assert_almost_equal ( bd ( np . array ([ 1.0 , 0.0 , 0.0 ]),
35
- np . array ([ 100.0 , - 100.0 , - 100.0 ])), 0 )
36
-
37
- # check if same results as alternative definition of deviance (from ESLII)
38
- def alt_dev ( y , pred ):
39
- return np . mean ( np . logaddexp ( 0.0 , - 2.0 * ( 2.0 * y - 1 ) * pred ))
40
-
41
- test_data = [( np . array ([ 1.0 , 1.0 , 1.0 ]), np . array ([ 100.0 , 100.0 , 100.0 ])),
42
- ( np .array ([ 0.0 , 0.0 , 0.0 ]), np .array ([ 100.0 , 100.0 , 100.0 ])),
43
- ( np . array ([ 0.0 , 0.0 , 0.0 ]),
44
- np . array ([ - 100.0 , - 100.0 , - 100.0 ])),
45
- ( np .array ([1.0 , 1.0 , 1.0 ] ),
46
- np .array ([- 100.0 , - 100.0 , - 100.0 ]))]
28
+ assert (bd (np .array ([0. ]), np .array ([0. ])) ==
29
+ bd (np .array ([1. ]), np .array ([0. ])))
30
+
31
+ assert bd (np .array ([1. , 1 , 1 ]), np . array ([ 100. , 100 , 100 ])) == approx ( 0 )
32
+ assert bd ( np . array ([ 1. , 0 , 0 ]), np .array ([100. , - 100 , - 100 ])) == approx ( 0 )
33
+
34
+ # check if same results as alternative definition of deviance, from ESLII
35
+ # Eq. (10.18): -loglike = log(1 + exp(-2*z*f) )
36
+ # Note:
37
+ # - We use y = {0, 1}, ESL (10.18) uses z in {-1, 1}, hence y=2*y-1
38
+ # - ESL 2*f = pred_raw, hence the factor 2 of ESL disappears.
39
+ # - Deviance = -2*loglike + .., hence a factor of 2 in front.
40
+ def alt_dev ( y , raw_pred ):
41
+ z = 2 * y - 1
42
+ return 2 * np .mean ( np . log ( 1 + np .exp ( - z * raw_pred )))
43
+
44
+ test_data = product (
45
+ ( np . array ([ 0. , 0 , 0 ]), np .array ([1. , 1 , 1 ]) ),
46
+ ( np .array ([- 5. , - 5 , - 5 ]), np . array ([ 3. , 3 , 3 ])))
47
47
48
48
for datum in test_data :
49
- assert_almost_equal ( bd (* datum ), alt_dev (* datum ))
49
+ assert bd (* datum ) == approx ( alt_dev (* datum ))
50
50
51
- # check the gradient against the
52
- def alt_ng (y , pred ):
53
- return (2 * y - 1 ) / (1 + np .exp (2 * (2 * y - 1 ) * pred ))
51
+ # check the negative gradient against altenative formula from ESLII
52
+ # Note: negative_gradient is half the negative gradient.
53
+ def alt_ng (y , raw_pred ):
54
+ z = 2 * y - 1
55
+ return z / (1 + np .exp (z * raw_pred ))
54
56
55
57
for datum in test_data :
56
- assert_almost_equal ( bd .negative_gradient (* datum ), alt_ng (* datum ))
58
+ assert bd .negative_gradient (* datum ) == approx ( alt_ng (* datum ))
57
59
58
60
59
61
def test_sample_weight_smoke ():
@@ -65,7 +67,7 @@ def test_sample_weight_smoke():
65
67
loss = LeastSquaresError ()
66
68
loss_wo_sw = loss (y , pred )
67
69
loss_w_sw = loss (y , pred , np .ones (pred .shape [0 ], dtype = np .float32 ))
68
- assert_almost_equal ( loss_wo_sw , loss_w_sw )
70
+ assert loss_wo_sw == approx ( loss_w_sw )
69
71
70
72
71
73
def test_sample_weight_init_estimators ():
@@ -164,13 +166,13 @@ def test_multinomial_deviance(n_classes, n_samples):
164
166
loss_wo_sw = loss (y_true , y_pred )
165
167
assert loss_wo_sw > 0
166
168
loss_w_sw = loss (y_true , y_pred , sample_weight = sample_weight )
167
- assert loss_wo_sw == pytest . approx (loss_w_sw )
169
+ assert loss_wo_sw == approx (loss_w_sw )
168
170
169
171
# Multinomial deviance uses weighted average loss rather than
170
172
# weighted sum loss, so we make sure that the value remains the same
171
173
# when we device the weight by 2.
172
174
loss_w_sw = loss (y_true , y_pred , sample_weight = 0.5 * sample_weight )
173
- assert loss_wo_sw == pytest . approx (loss_w_sw )
175
+ assert loss_wo_sw == approx (loss_w_sw )
174
176
175
177
176
178
def test_mdl_computation_weighted ():
@@ -180,8 +182,7 @@ def test_mdl_computation_weighted():
180
182
expected_loss = 1.0909323
181
183
# MultinomialDeviance loss computation with weights.
182
184
loss = MultinomialDeviance (3 )
183
- assert (loss (y_true , raw_predictions , weights )
184
- == pytest .approx (expected_loss ))
185
+ assert loss (y_true , raw_predictions , weights ) == approx (expected_loss )
185
186
186
187
187
188
@pytest .mark .parametrize ('n' , [0 , 1 , 2 ])
@@ -241,23 +242,23 @@ def test_init_raw_predictions_values():
241
242
init_estimator = loss .init_estimator ().fit (X , y )
242
243
raw_predictions = loss .get_init_raw_predictions (y , init_estimator )
243
244
# Make sure baseline prediction is the mean of all targets
244
- assert_almost_equal (raw_predictions , y .mean ())
245
+ assert_allclose (raw_predictions , y .mean ())
245
246
246
247
# Least absolute and huber loss
247
248
for Loss in (LeastAbsoluteError , HuberLossFunction ):
248
249
loss = Loss ()
249
250
init_estimator = loss .init_estimator ().fit (X , y )
250
251
raw_predictions = loss .get_init_raw_predictions (y , init_estimator )
251
252
# Make sure baseline prediction is the median of all targets
252
- assert_almost_equal (raw_predictions , np .median (y ))
253
+ assert_allclose (raw_predictions , np .median (y ))
253
254
254
255
# Quantile loss
255
256
for alpha in (.1 , .5 , .9 ):
256
257
loss = QuantileLossFunction (alpha = alpha )
257
258
init_estimator = loss .init_estimator ().fit (X , y )
258
259
raw_predictions = loss .get_init_raw_predictions (y , init_estimator )
259
260
# Make sure baseline prediction is the alpha-quantile of all targets
260
- assert_almost_equal (raw_predictions , np .percentile (y , alpha * 100 ))
261
+ assert_allclose (raw_predictions , np .percentile (y , alpha * 100 ))
261
262
262
263
y = rng .randint (0 , 2 , size = n_samples )
263
264
@@ -271,14 +272,14 @@ def test_init_raw_predictions_values():
271
272
# So we want raw_prediction = link_function(p) = log(p / (1 - p))
272
273
raw_predictions = loss .get_init_raw_predictions (y , init_estimator )
273
274
p = y .mean ()
274
- assert_almost_equal (raw_predictions , np .log (p / (1 - p )))
275
+ assert_allclose (raw_predictions , np .log (p / (1 - p )))
275
276
276
277
# Exponential loss
277
278
loss = ExponentialLoss (n_classes = 2 )
278
279
init_estimator = loss .init_estimator ().fit (X , y )
279
280
raw_predictions = loss .get_init_raw_predictions (y , init_estimator )
280
281
p = y .mean ()
281
- assert_almost_equal (raw_predictions , .5 * np .log (p / (1 - p )))
282
+ assert_allclose (raw_predictions , .5 * np .log (p / (1 - p )))
282
283
283
284
# Multinomial deviance loss
284
285
for n_classes in range (3 , 5 ):
@@ -288,7 +289,7 @@ def test_init_raw_predictions_values():
288
289
raw_predictions = loss .get_init_raw_predictions (y , init_estimator )
289
290
for k in range (n_classes ):
290
291
p = (y == k ).mean ()
291
- assert_almost_equal (raw_predictions [:, k ], np .log (p ))
292
+ assert_allclose (raw_predictions [:, k ], np .log (p ))
292
293
293
294
294
295
@pytest .mark .parametrize ('seed' , range (5 ))
@@ -304,9 +305,9 @@ def test_lad_equals_quantile_50(seed):
304
305
305
306
lad_loss = lad (y_true , raw_predictions )
306
307
ql_loss = ql (y_true , raw_predictions )
307
- assert_almost_equal ( lad_loss , 2 * ql_loss )
308
+ assert lad_loss == approx ( 2 * ql_loss )
308
309
309
310
weights = np .linspace (0 , 1 , n_samples ) ** 2
310
311
lad_weighted_loss = lad (y_true , raw_predictions , sample_weight = weights )
311
312
ql_weighted_loss = ql (y_true , raw_predictions , sample_weight = weights )
312
- assert_almost_equal ( lad_weighted_loss , 2 * ql_weighted_loss )
313
+ assert lad_weighted_loss == approx ( 2 * ql_weighted_loss )
0 commit comments