Does it make sense to log-transform the dependent when using Gradient Boosted Trees?

When we are modeling non-negative data with linear regression we often log-transform the dependent, thereby ensuring that all the predictions made by the model will be positive as well.

Does it make sense to do the same when using Gradient Boosted Trees (GBTs)? For Random Forests the output is an average of the training labels in the "winning" leaf of each tree, thereby ensuring that the prediction will be non-negative.

Is it safe to make the same assumption for GBTs? I haven't been able to follow the derivation from the original paper (section 4.3).

• It surprises me that you can get negative values from GBTs; my understanding of this was that you couldn't predict outside the range of the observed (all positive) data. – zbicyclist Feb 21 '17 at 15:38
• You can definitely get negative predictions from a GBT even if all the training data is positive. – Matthew Drury Feb 21 '17 at 15:54
• @MatthewDrury is there some way to show this mathematically? Even a toy example would work. – Bar Feb 22 '17 at 10:54
• A single tree, or ensemble of trees, trained only by splitting the observed data alone would not be able to predict outside the range of the data (e.g. CART or random forests). However, boosting creates trees fitted to the residuals of previous model predictions. – Paul Feb 22 '17 at 19:34
• @Bar I got a chance to write an answer for you. I hope this helps. – Matthew Drury Feb 24 '17 at 4:38

As I mentioned in the comments:

You can definitely get negative predictions from a GBT even if all the training data is positive.

Here's a simple example of this in practice:

x = np.array([0, 1, 2, 3]).reshape(-1, 1)
y = np.array([0, 100, 0, 100]).reshape(-1, 1)

gbr.fit(x, y)


Of course, the parameters here are extreme so as to make the point in the simplest possible situation (no one should use a learning rate of 1.0 in practice). The predictions from this model produce negative values at the second stage:

for i, preds in enumerate(gbr.staged_predict(x)):
print("Preds at stage {}: {}".format(i, preds))


which produces the following output:

Preds at stage 0: [  0.          66.66666667  66.66666667  66.66666667]
Preds at stage 1: [ -11.11111111   55.55555556   55.55555556  100.       ]


To understand what happened, we need the gradient from the first stage:

for i, preds in enumerate(gbr.staged_predict(x)):

Gradient at stage 0: [  0.          33.33333333 -66.66666667  33.33333333]

Recall the a regression tree is fit to the gradient at stage zero, and the average residual is calculated in each of their terminal regions of this true to form the update step of the booster. This tree resulted in two terminal regions, the first consisting of the first three observations, and the second of only the last observation. The average gradient (residual) in this first region is 11.111..., which is then subtracted from the prediction of 0.0 for the first observation, resulting in -11.111....