| name | lightgbm |
| description | [Applies to: **/*.py] This guide provides opinionated, actionable best practices for using LightGBM in production-grade Python ML pipelines, focusing on performance, reproducibility, and maintainability. |
| source | cursor_mdc |
lightgbm Best Practices
LightGBM is our go-to for high-performance tabular modeling. These rules ensure our LightGBM implementations are fast, reliable, and maintainable.
1. Code Structure & Reproducibility
Always encapsulate model creation and ensure runs are repeatable.
1.1. Use Scikit-learn API & Model Builder Functions
Always use LGBMClassifier or LGBMRegressor for sklearn compatibility. Wrap model instantiation in a function for clean, version-controlled hyperparameter management.
❌ BAD: Inline model creation with hardcoded parameters
import lightgbm as lgb
model = lgb.LGBMClassifier(n_estimators=100, learning_rate=0.1, max_depth=7)
model.fit(X_train, y_train)
✅ GOOD: Function-based model creation with type hints and external config
import lightgbm as lgb
from typing import Dict, Any
import yaml
def build_lgbm_classifier(params: Dict[str, Any]) -> lgb.LGBMClassifier:
"""Builds a LightGBM Classifier with specified hyperparameters."""
return lgb.LGBMClassifier(**params)
import yaml
from models.lgbm_model import build_lgbm_classifier
with open("config/lgbm_params.yaml", "r") as f:
params = yaml.safe_load(f)['classifier_v1']
model = build_lgbm_classifier(params)
model.fit(X_train, y_train)
1.2. Set Reproducibility Flags
Ensure random_state (or seed) and deterministic=True are always set for consistent results.
❌ BAD: Non-deterministic training
model = lgb.LGBMClassifier(n_estimators=100)
✅ GOOD: Fully reproducible training
model = lgb.LGBMClassifier(n_estimators=100, random_state=42, deterministic=True)
2. Data Handling
Leverage LightGBM's native capabilities for efficiency.
2.1. Native Categorical Feature Handling
Feed raw categorical columns directly. LightGBM handles them efficiently, saving memory and speeding up training.
❌ BAD: Manual one-hot or label encoding
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder(handle_unknown='ignore', sparse_output=False)
X_train_encoded = encoder.fit_transform(X_train[categorical_cols])
model.fit(X_train_encoded, y_train)
✅ GOOD: Use categorical_feature parameter
categorical_feature_names = ['feature_A', 'feature_B']
categorical_feature_indices = [X_train.columns.get_loc(col) for col in categorical_feature_names]
model = lgb.LGBMClassifier(categorical_feature=categorical_feature_indices, **params)
model.fit(X_train, y_train)
2.2. Missing Values
LightGBM handles missing values natively. While it can split on them, consider imputing sensible defaults (e.g., mean, target encoding) for improved stability in some cases.
✅ GOOD: Let LightGBM handle, or impute strategically
model = lgb.LGBMClassifier(**params)
model.fit(X_train_with_nans, y_train)
from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy='mean')
X_train_imputed = imputer.fit_transform(X_train_with_nans)
model.fit(X_train_imputed, y_train)
3. Hyperparameter Tuning & Training
Systematic tuning and robust training practices are non-negotiable.
3.1. Automated Hyperparameter Tuning
Always use automated tools like Optuna or FLAML for systematic tuning. Prioritize learning_rate, num_leaves, max_depth, and min_data_in_leaf.
✅ GOOD: Optuna for efficient hyperparameter search
import optuna
import lightgbm as lgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
def objective(trial):
params = {
'objective': 'binary',
'metric': 'auc',
'n_estimators': trial.suggest_int('n_estimators', 100, 1000),
'learning_rate': trial.suggest_loguniform('learning_rate', 0.01, 0.1),
'num_leaves': trial.suggest_int('num_leaves', 2, 256),
'max_depth': trial.suggest_int('max_depth', 3, 12),
'min_child_samples': trial.suggest_int('min_child_samples', 20, 100),
'subsample': trial.suggest_uniform('subsample', 0.6, 1.0),
'colsample_bytree': trial.suggest_uniform('colsample_bytree', 0.6, 1.0),
'random_state': 42,
'n_jobs': -1,
}
model = lgb.LGBMClassifier(**params)
model.fit(X_train, y_train,
eval_set=[(X_val, y_val)],
eval_metric=,
callbacks=[lgb.early_stopping(, verbose=)])
y_pred = model.predict_proba(X_val)[:, ]
roc_auc_score(y_val, y_pred)
study = optuna.create_study(direction=)
study.optimize(objective, n_trials=)
()
()
3.2. num_leaves vs max_depth
Always ensure num_leaves < 2^max_depth to prevent the leaf-wise tree from over-growing and overfitting.
❌ BAD: num_leaves too large for max_depth
model = lgb.LGBMClassifier(num_leaves=127, max_depth=6)
✅ GOOD: Balanced num_leaves and max_depth
model = lgb.LGBMClassifier(num_leaves=31, max_depth=6)
3.3. Early Stopping
Always use a validation set with early stopping to prevent overfitting and optimize training time.
❌ BAD: Training for a fixed, potentially excessive number of estimators
model = lgb.LGBMClassifier(n_estimators=1000)
model.fit(X_train, y_train)
✅ GOOD: Early stopping with a validation set
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
model = lgb.LGBMClassifier(n_estimators=1000, learning_rate=0.05, random_state=42)
model.fit(X_train, y_train,
eval_set=[(X_val, y_val)],
eval_metric='auc',
callbacks=[lgb.early_stopping(stopping_rounds=50, verbose=True)])
3.4. Callbacks for Monitoring
Use log_evaluation and custom callbacks for transparent and debuggable training.
✅ GOOD: Log evaluation during training
model.fit(X_train, y_train,
eval_set=[(X_val, y_val)],
eval_metric='auc',
callbacks=[lgb.early_stopping(50), lgb.log_evaluation(period=10)])
3.5. Advanced Sampling
Enable GOSS (boosting_type='goss') and Exclusive Feature Bundling (EFB) for large datasets to reduce data size and speed up training without significant accuracy loss.
✅ GOOD: Leverage GOSS for efficiency
model = lgb.LGBMClassifier(boosting_type='goss', **params)
model.fit(X_train, y_train)
4. Performance & Scalability
Optimize for speed and memory.
4.1. num_threads & GPU
Set n_jobs=-1 (or num_threads) to utilize all available CPU cores. For very large datasets, use a GPU-enabled LightGBM build.
✅ GOOD: Maximize CPU utilization
model = lgb.LGBMClassifier(n_jobs=-1, **params)
4.2. Distributed Training
For scaling beyond a single node, select the appropriate parallel algorithm (tree_learner) based on data vs. feature size.
✅ GOOD: Configure for distributed training (e.g., Dask)
import lightgbm.dask as lgb_dask
from distributed import Client, LocalCluster
cluster = LocalCluster(n_workers=4, threads_per_worker=2)
client = Client(cluster)
dask_model = lgb_dask.DaskLGBMClassifier(n_estimators=100, client=client, **params)
dask_model.fit(dask_X_train, dask_y_train,
eval_set=[(dask_X_val, dask_y_val)],
eval_metric='auc',
callbacks=[lgb.early_stopping(50)])
client.close()
cluster.close()
For Spark, use SynapseML's LightGBMClassifier.
5. Common Pitfalls (❌ BAD vs ✅ GOOD)
Avoid these common mistakes.
5.1. Imbalanced Datasets
Always address class imbalance in classification tasks.
❌ BAD: Ignoring imbalanced classes
model = lgb.LGBMClassifier(objective='binary', random_state=42)
model.fit(X_train_imbalanced, y_train_imbalanced)
✅ GOOD: Use is_unbalance=True or class_weight='balanced'
model = lgb.LGBMClassifier(objective='binary', is_unbalance=True, random_state=42)
model.fit(X_train_imbalanced, y_train_imbalanced)
model = lgb.LGBMClassifier(objective='binary', class_weight='balanced', random_state=42)
model.fit(X_train_imbalanced, y_train_imbalanced)
6. Testing & Deployment Readiness
Ensure models are robust and ready for production.
6.1. Cross-Validation
Always use cross-validation for robust model evaluation and hyperparameter tuning.
✅ GOOD: Scikit-learn cross_val_score or GridSearchCV
from sklearn.model_selection import cross_val_score
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=1000, n_features=10, random_state=42)
model = lgb.LGBMClassifier(random_state=42)
scores = cross_val_score(model, X, y, cv=5, scoring='roc_auc', n_jobs=-1)
print(f"CV AUC scores: {scores.mean():.4f} +/- {scores.std():.4f}")
6.2. Virtual Environments & Packaging
Always use virtual environments (e.g., venv, conda) and manage dependencies with pip-tools or poetry. Package your application for deployment using standard Python tools.
✅ GOOD: Isolated environment and explicit dependencies
python -m venv .venv
source .venv/bin/activate
pip install pip-tools
pip-compile requirements.in
pip install -r requirements.txt