Skip to content

Prediction parity between LightGBM (Python) and leaves (Go) #94

@YuminosukeSato

Description

@YuminosukeSato

Summary

Under the same model and input, predictions from LightGBM's Python predict can differ from those produced by leaves. Root causes were specification mismatches and missing features in leaves:

  • RF averaging mismatch: LightGBM averages by the total number of trees when average_output is set, while leaves averaged by the number of trees actually used.
  • Post-transform coverage: Logistic/softmax/exponential post-transform was not applied for RF models and for JSON-loaded models, even when requested.
  • Transform name gap: Exponential name was missing from TransformType.Name() which could lead to unknown.

Expected behavior

When Python's predict(X, raw_score=..., num_iteration=...) has a logical counterpart in leaves using
LGEnsembleFromFile(path, useTransformation) / LGEnsembleFromJSON(r, useTransformation) and Predict...(nEstimators), the numbers should match.

Actual behavior

  • With RF models, using only a subset of trees (e.g., via num_iteration) caused a mismatch because of different averaging rules.
  • Even with loadTransformation=true, RF and JSON paths did not apply logistic/softmax/exp transforms, so probabilities did not match Python.

Reproduction (example)

  1. Python
clf = lgb.Booster(model_file='model.txt')
py = clf.predict(X, raw_score=False, num_iteration=k)
  1. Go (before)
model, _ := leaves.LGEnsembleFromFile("model.txt", true)
_ = model.PredictDense(vals, rows, cols, pred, k, 0)

→ RF probabilities differ because the older implementation divided by used trees, not total trees.

For JSON models, LGEnsembleFromJSON did not apply the transform and returned raw scores.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions