lightgbm
LightGBM is a gradient boosting framework built around histogram-based, leaf-wise tree growth: a C++ core with a Python package exposing both a native API (lgb.Dataset, lgb.train) and scikit-learn estimators (LGBMClassifier, LGBMRegressor, LGBMRanker). On tabular data it usually trains faster and uses less memory than other boosting libraries at comparable accuracy, handles categorical features without one-hot encoding, and supports distributed and GPU training. It moved from the Microsoft org to lightgbm-org on GitHub in March 2026 under the same maintainers.
The speed choice for gradient boosting on CPU and the default for large tabular and ranking workloads, with maintenance continuing under a community org after leaving Microsoft. Pick xgboost if an easy GPU wheel matters more than raw CPU throughput.
Use it if
- You train on large tabular datasets where CPU speed is the bottleneck: leaf-wise histogram growth is typically the fastest of the major boosting libraries
- Your features include categoricals you do not want to encode: pandas category columns are handled natively and usually beat one-hot encoding on both speed and accuracy
- You need learning-to-rank: LGBMRanker with the lambdarank objective is a production standard for search and recommendation ranking
- Memory is tight: histogram binning keeps the training footprint well below equivalent exact-split implementations
- Your data is images, audio or free text: gradient boosting needs engineered tabular features and does not learn representations, so use deep learning there
- Your dataset is small: leaf-wise growth overfits small data quickly and demands babysitting of num_leaves and min_data_in_leaf, while sklearn's HistGradientBoosting gets close with zero extra dependencies
- You want GPU training from a pip install: the standard wheel is CPU-only, and CUDA or OpenCL builds mean compiling from source or special install flags, unlike xgboost whose wheel ships GPU support
- You learned the library before 4.0 and cannot budget migration time: early_stopping_rounds and verbose were removed from fit() in favor of callbacks, so most older tutorials and internal snippets are now wrong
Setup reality
The pip wheel is compact and CPU-only; that is pleasant until you want GPU, which requires building with CMake plus OpenCL or CUDA, or pip install with --config-settings flags, and it is the single biggest source of setup questions. On macOS you need libomp from Homebrew, and mismatched libomp copies across ML libraries have caused segfaults. API churn to know: 4.x removed early_stopping_rounds and verbose from fit(), so you pass callbacks=[lgb.early_stopping(50), lgb.log_evaluation(100)] instead, and sklearn-wrapper models warn if prediction-time feature names differ from training. The GitHub org also changed to lightgbm-org in March 2026; old Microsoft links redirect.
Patterns
Train a classifier with the sklearn APItrain-classifier
from lightgbm import LGBMClassifier
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
model = LGBMClassifier(n_estimators=500, learning_rate=0.05, num_leaves=31)
model.fit(X_train, y_train)
print(model.score(X_test, y_test))num_leaves is the main capacity knob, not max_depth; the default 31 is sane, and doubling it without raising min_data_in_leaf is the classic overfit.
Early stopping with callbacks (4.x style)early-stopping
import lightgbm as lgb
model = lgb.LGBMClassifier(n_estimators=2000, learning_rate=0.05)
model.fit(
X_train, y_train,
eval_set=[(X_val, y_val)],
eval_metric='auc',
callbacks=[lgb.early_stopping(50), lgb.log_evaluation(100)],
)
print(model.best_iteration_)Passing early_stopping_rounds or verbose directly to fit() was removed in 4.0; callbacks are the only supported route now.
Native API with lgb.Datasetnative-train-api
import lightgbm as lgb
train_set = lgb.Dataset(X_train, label=y_train)
val_set = lgb.Dataset(X_val, label=y_val, reference=train_set)
booster = lgb.train(
{'objective': 'binary', 'learning_rate': 0.05, 'num_leaves': 31},
train_set,
num_boost_round=2000,
valid_sets=[val_set],
callbacks=[lgb.early_stopping(50)],
)
preds = booster.predict(X_test)Validation Datasets should pass reference=train_set so they share bin edges; predict returns probabilities for binary objectives, threshold them yourself.
Use categorical columns without one-hot encodingcategorical-features
import pandas as pd
X = df.copy()
for col in ['city', 'plan']:
X[col] = X[col].astype('category')
model = LGBMClassifier()
model.fit(X, y) # category dtype columns are auto-detectedAuto-detection needs pandas category dtype; plain object columns raise. Category codes must match between train and predict, so persist the dtype categories.
Cross-validate boosting roundscross-validation
import lightgbm as lgb
cv = lgb.cv(
{'objective': 'binary', 'learning_rate': 0.05, 'metric': 'auc'},
lgb.Dataset(X, label=y),
num_boost_round=1000,
nfold=5,
callbacks=[lgb.early_stopping(50)],
)
print(len(cv['valid auc-mean']), cv['valid auc-mean'][-1])lgb.cv returns a dict of metric lists; the list length is the surviving round count to reuse when fitting the final model.
Feature importance by gainfeature-importance
import pandas as pd
imp = pd.Series(
model.booster_.feature_importance(importance_type='gain'),
index=model.booster_.feature_name(),
)
print(imp.sort_values(ascending=False).head(10))The sklearn attribute feature_importances_ uses split counts by default, which favors high-cardinality features; gain is usually what you actually want, and SHAP is better still for decisions.
Save and load a model safelysave-load-model
model.booster_.save_model('model.txt')
import lightgbm as lgb
booster = lgb.Booster(model_file='model.txt')
preds = booster.predict(X_new)The text model format survives library upgrades; pickling the sklearn wrapper works within a version but is fragile across them.
Handle class imbalanceimbalanced-classes
neg, pos = (y_train == 0).sum(), (y_train == 1).sum()
model = LGBMClassifier(
scale_pos_weight=neg / pos, # or is_unbalance=True
)
model.fit(X_train, y_train, eval_set=[(X_val, y_val)], eval_metric='average_precision')Use scale_pos_weight or is_unbalance, never both. Reweighting skews predicted probabilities, so recalibrate if you need real probabilities rather than a ranking.
Predict quantiles instead of the meanquantile-regression
from lightgbm import LGBMRegressor
p90 = LGBMRegressor(objective='quantile', alpha=0.9, n_estimators=500)
p90.fit(X_train, y_train)
p10 = LGBMRegressor(objective='quantile', alpha=0.1, n_estimators=500)
p10.fit(X_train, y_train)
# p10/p90 predictions form a rough 80% intervalOne model per quantile; nothing forces the quantile curves not to cross, so sanity-check intervals before shipping them.
Learning-to-rank with LGBMRankerranking-lambdarank
from lightgbm import LGBMRanker
# group = number of rows per query, in row order
ranker = LGBMRanker(objective='lambdarank', n_estimators=500)
ranker.fit(
X_train, y_train,
group=train_group_sizes,
eval_set=[(X_val, y_val)],
eval_group=[val_group_sizes],
eval_at=[10],
)Rows must be sorted so each query's documents are contiguous and group sizes sum to the row count; labels are relevance grades (0, 1, 2, ...), not clicks.
The parameters that actually mattertune-key-parameters
model = LGBMClassifier(
n_estimators=2000, # large, rely on early stopping
learning_rate=0.05,
num_leaves=63, # capacity; pair with min_data_in_leaf
min_data_in_leaf=50, # overfitting brake
feature_fraction=0.8, # column subsampling
bagging_fraction=0.8,
bagging_freq=1,
)num_leaves, learning_rate and min_data_in_leaf carry most of the signal; for serious tuning use Optuna's LightGBM tuner, which the docs point to.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| xgboost | PyPI | Comparable accuracy with CUDA support included in the standard wheel and a larger deployment tooling ecosystem |
| catboost | PyPI | High-cardinality categorical features and strong defaults with minimal tuning effort |
| scikit-learn | PyPI | HistGradientBoosting, itself inspired by LightGBM, covers everyday cases with no extra dependency |