Train
Supervised training with Optuna-backed hyperparameter search, feature selection,
sample weight calculation, threshold optimisation, and structured reports.
OptunaTrainer is the primary entry point for both classification and regression.
Configuration & State
bitbullet.train.core.config.TrainConfig
dataclass
Configuration for model training.
Provides comprehensive control over the training process including hyperparameter optimization, feature selection, cross-validation, and evaluation strategies.
Example
config = TrainConfig(
name="fraud_detector",
model_type="lgbm",
task="binary_classification",
n_trials=100,
cv_folds=5,
optimization_metric="roc_auc",
feature_selection="importance",
threshold_optimization_method="youden"
)
__post_init__()
Validate configuration after initialization.
to_dict()
Convert config to dictionary.
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary representation of config |
bitbullet.train.core.state.TrainState
dataclass
Stores the complete state of a training run.
Captures everything needed to understand, reproduce, and analyze a training session, including SHAP-based explanations.
Attributes:
| Name | Type | Description |
|---|---|---|
model |
Optional[BaseModel]
|
The trained model instance |
best_params |
Dict[str, Any]
|
Best hyperparameters found during optimization |
best_score |
float
|
Best CV score achieved |
cv_scores |
List[float]
|
Cross-validation scores for each fold |
cv_predictions |
Optional[DataFrame]
|
Out-of-fold predictions (if saved) |
fold_metadata |
List[Dict[str, Any]]
|
Auditable splitter boundaries for each CV fold |
optimal_threshold |
Optional[float]
|
Optimal classification threshold |
feature_importance |
Optional[DataFrame]
|
Feature importance scores (tree-based) |
feature_names |
List[str]
|
List of features used |
n_features_selected |
int
|
Number of features after selection |
shap_values |
Optional[ndarray]
|
SHAP values matrix for model explanations |
shap_expected_value |
Optional[Union[float, ndarray]]
|
Base value(s) for SHAP explanations |
shap_feature_importance |
Optional[DataFrame]
|
Feature importance from SHAP analysis |
shap_background_data |
Optional[DataFrame]
|
Background dataset used for SHAP |
shap_interaction_values |
Optional[ndarray]
|
SHAP interaction values (if calculated) |
training_time_seconds |
float
|
Total training time |
optimization_time_seconds |
float
|
Time spent on hyperparameter optimization |
study |
Optional[Any]
|
Optuna study object (if using Optuna) |
training_history |
Dict[str, List[float]]
|
Training metrics over time |
fold_metrics |
List[Dict[str, float]]
|
Metrics for each CV fold |
started_at |
Optional[datetime]
|
Training start timestamp |
completed_at |
Optional[datetime]
|
Training completion timestamp |
cv_score_mean
property
Get mean CV score.
cv_score_std
property
Get CV score standard deviation.
is_complete
property
Check if training is complete.
summary()
Generate a human-readable summary.
Returns:
| Type | Description |
|---|---|
str
|
Formatted summary string |
Base Trainer
bitbullet.train.core.base.BaseTrainer
Bases: ABC
Abstract base class for all model trainers.
Implements the template method pattern, providing a consistent training workflow while allowing subclasses to customize specific steps like hyperparameter optimization.
The training workflow: 1. Validate input data 2. Feature selection (optional) 3. Sample weight calculation (optional) 4. Hyperparameter optimization 5. Train final model with best parameters 6. Threshold optimization (classification only) 7. Generate reports
Example
from bitbullet.train.trainers.optuna_trainer import OptunaTrainer
from bitbullet.train.core.config import TrainConfig
config = TrainConfig(
name="my_model",
model_type="lgbm",
n_trials=50
)
trainer = OptunaTrainer(config)
model = trainer.fit(X_train, y_train, X_val, y_val)
best_params
property
Get the best hyperparameters.
best_score
property
Get the best CV score.
config
property
Get the training configuration.
cv_scores
property
Get CV scores for all folds.
feature_importance
property
Get feature importance DataFrame.
optimal_threshold
property
Get the optimal classification threshold.
state
property
Get the training state.
__init__(config, prepare_final_features=None)
Initialize trainer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TrainConfig
|
Training configuration |
required |
prepare_final_features
|
Optional[Callable[[], PreparedFeatureProvider[tuple[DataFrame, Optional[DataFrame]]]]]
|
Optional runtime preparer called after hyperparameter optimization. It returns a provider for the final training features and optional validation features. The provider is closed after all final-model consumers finish. |
None
|
fit(X, y, X_val=None, y_val=None, sample_weight=None)
Train a model using the complete pipeline.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
Union[DataFrame, ndarray]
|
Training features, or the row-identity carrier used by runtime feature providers. |
required |
y
|
Union[Series, ndarray]
|
Training targets. |
required |
X_val
|
Optional[Union[DataFrame, ndarray]]
|
Validation features or matching row-identity carrier. |
None
|
y_val
|
Optional[Union[Series, ndarray]]
|
Validation targets. |
None
|
sample_weight
|
Optional[Union[Series, ndarray]]
|
Optional row-aligned sample weights. |
None
|
Returns:
| Type | Description |
|---|---|
BaseModel
|
Trained model. |
Any prepared final feature provider is released before this method returns and is also released when training raises.
Trainers
Fold-fitted preprocessing
OptunaTrainer(..., fold_features=build_fold) accepts an optional runtime-only
callback. It receives positional training and validation indices relative to
the X/y supplied to fit, and returns two pandas feature frames. Fit a fresh
preprocessor and any selector using only the training membership, then transform
validation with that fitted preprocessor. Return the exact original row indices
in their validated order and matching, unique columns. Folds may have different
feature widths, for example because training-only category vocabularies differ.
The callback is called for every fold and trial. It is not a serialized inference
component and is not used for the final fit: that fit uses the caller-supplied
X and the configured final feature selection. The caller must retain the final
fitted preprocessor separately for inference. Do not build validation features
with a preprocessor fitted on the whole outer training partition. Omitting this
callback preserves the existing trainer behaviour; it does not automatically
make an externally preprocessed matrix fold-local.
For deterministic preprocessing that should be reused across trials, use
prepare_fold_features instead. The preparer receives each validated fold's
row positions exactly once and returns a PreparedFeatureProvider. Its load
callback supplies the fixed training and validation frames whenever a trial
uses that fold. Its close callback releases caller-owned resources as soon as
the search finishes, including exceptional exits. A provider's loader must be
safe for concurrent reads when n_jobs permits parallel trials. The loaded
frames are treated as immutable. fold_features and prepare_fold_features
cannot be combined.
prepare_final_features defers construction of the final training matrix until
hyperparameter search has finished and every prepared fold provider has been
closed. It returns a provider whose loader supplies (X_train, X_val_or_none).
The frames must preserve the row identity supplied to fit, while their columns
may reflect caller-owned preprocessing. Final feature selection, fitting,
threshold analysis, importance, explanations, and reports complete before this
provider is closed. Search with deferred final features requires a fold-local
factory or preparer, because fitting trials against a different eager matrix
would change the model-selection question. Omitting both new hooks preserves
the existing eager training path.
bitbullet.train.core.features.PreparedFeatureProvider
Bases: Generic[PreparedValue]
Load a prepared feature value and release its caller-owned resources.
load may be called more than once. When a trainer runs trials in
parallel, it may also be called concurrently, so callers choosing a lazy
loader must make those reads safe. close is idempotent and the trainer
calls it once the value can no longer be requested, including error paths.
close()
Release the provider exactly once.
fixed(value)
classmethod
Create a provider for an already prepared in-memory value.
load()
Return the prepared value while this provider is active.
TrainConfig(protected_features=["structural_input"]) requires those inputs in
the final and fold matrices and retains them through feature selection. The
maximum selected feature count must accommodate every protected input. This is
explicit caller configuration, not automatic feature inference.
bitbullet.train.trainers.optuna_trainer.OptunaTrainer
Bases: BaseTrainer
Trainer using Optuna for hyperparameter optimization.
Features: - Optuna TPE sampler for efficient search - Stratified K-Fold cross-validation - Early stopping with best iteration tracking - Automatic handling of sample weights - Comprehensive metric tracking
Example
from bitbullet.train.trainers.optuna_trainer import OptunaTrainer
from bitbullet.train.core.config import TrainConfig
config = TrainConfig(
name="fraud_detector",
model_type="lgbm",
task="binary_classification",
n_trials=100,
cv_folds=5,
optimize_threshold=True
)
trainer = OptunaTrainer(config)
model = trainer.fit(X_train, y_train, X_val, y_val)
# Access results
print(f"Best CV score: {trainer.best_score:.4f}")
print(f"Best params: {trainer.best_params}")
print(f"Optimal threshold: {trainer.optimal_threshold:.3f}")
# Feature importance
print(trainer.feature_importance.head(10))
__init__(config, progress_callback=None, cv_splitter=None, fold_features=None, prepare_fold_features=None, prepare_final_features=None)
Initialize Optuna trainer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
TrainConfig
|
Training configuration |
required |
progress_callback
|
Optional[Callable]
|
Optional callback function called after each trial. Receives (trial_number: int, score: float, params: dict) |
None
|
cv_splitter
|
Optional[Any]
|
Optional sklearn-compatible splitter. When supplied,
it replaces the strategy constructed from |
None
|
fold_features
|
Optional[Callable[[ndarray, ndarray], tuple[DataFrame, DataFrame]]]
|
Optional runtime factory receiving validated train and validation row positions. It must fit preprocessing and any feature selection using only those training rows, returning aligned train/validation feature frames. It is called afresh for each fold/trial and is never used for the final fit, which uses the X explicitly supplied to fit(). This lets callers keep raw covariates outside the trainer without leaking transforms fitted on the complete training partition into inner CV. |
None
|
prepare_fold_features
|
Optional[Callable[[ndarray, ndarray], PreparedFeatureProvider[tuple[DataFrame, DataFrame]]]]
|
Optional runtime preparer called once for
each validated fold before the search starts. It returns a
provider that supplies the same training/validation features
to every trial and is closed as soon as search finishes. This
is mutually exclusive with |
None
|
prepare_final_features
|
Optional[Callable[[], PreparedFeatureProvider[tuple[DataFrame, Optional[DataFrame]]]]]
|
Optional runtime preparer called only after
search finishes. It returns a provider for the final training
features and optional external-validation features. The row
membership must match the inputs supplied to |
None
|
Optimizers
bitbullet.train.optimizers.optuna_optimizer.OptunaOptimizer
Optuna hyperparameter optimizer.
Wraps Optuna's study API with sensible defaults and enhanced functionality for ML model optimization.
Example
optimizer = OptunaOptimizer(
direction="maximize",
sampler="tpe",
random_state=42
)
def objective(trial):
# Define hyperparameter search space
params = {
'n_estimators': trial.suggest_int('n_estimators', 50, 500),
'max_depth': trial.suggest_int('max_depth', 3, 10),
'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3, log=True)
}
# Train model and return score
model = LGBMClassifier(**params)
score = cross_val_score(model, X, y, cv=5).mean()
return score
study = optimizer.optimize(objective, n_trials=100)
best_params = study.best_params
__init__(direction='maximize', sampler='tpe', random_state=42, study_name=None, storage=None, search_space=None)
Initialize Optuna optimizer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
direction
|
str
|
'maximize' or 'minimize' |
'maximize'
|
sampler
|
str
|
'tpe', 'random', or 'cmaes' |
'tpe'
|
random_state
|
int
|
Random seed for reproducibility |
42
|
study_name
|
Optional[str]
|
Name for the study (for persistence) |
None
|
storage
|
Optional[str]
|
Storage URL for study persistence (e.g., 'sqlite:///optuna.db') |
None
|
extract_best_params(study)
staticmethod
Extract best parameters from completed study.
Includes special handling for iteration-based parameters (e.g., best_iteration from early stopping).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
study
|
Study
|
Completed Optuna study |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary of best parameters |
Example
study = optimizer.optimize(objective, n_trials=100)
best_params = OptunaOptimizer.extract_best_params(study)
# Train final model
model = LGBMClassifier(**best_params)
model.fit(X, y)
get_trial_history(study)
staticmethod
Get history of all trials.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
study
|
Study
|
Completed Optuna study |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, list]
|
Dictionary with trial history |
Example
history = OptunaOptimizer.get_trial_history(study)
import matplotlib.pyplot as plt
plt.plot(history['trial_numbers'], history['values'])
plt.xlabel('Trial')
plt.ylabel('Score')
plt.show()
optimize(objective, n_trials=100, timeout=None, n_jobs=1, show_progress_bar=True, callbacks=None)
Run hyperparameter optimization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
objective
|
Callable
|
Objective function that takes a trial and returns a score |
required |
n_trials
|
int
|
Number of trials to run |
100
|
timeout
|
Optional[float]
|
Time limit in seconds (optional) |
None
|
n_jobs
|
int
|
Number of parallel jobs (-1 for all CPUs) |
1
|
show_progress_bar
|
bool
|
Whether to show progress bar |
True
|
callbacks
|
Optional[list]
|
List of callback functions |
None
|
Returns:
| Type | Description |
|---|---|
Study
|
Completed Optuna study |
Example
def objective(trial):
params = trial.suggest_int('n_estimators', 50, 500)
# Train and evaluate model
return score
study = optimizer.optimize(
objective=objective,
n_trials=100,
n_jobs=-1,
show_progress_bar=True
)
print(f"Best score: {study.best_value}")
print(f"Best params: {study.best_params}")
print_study_summary(study)
staticmethod
Print a summary of the optimization study.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
study
|
Study
|
Completed Optuna study |
required |
Feature Selection
bitbullet.train.feature_selection.selector_factory.FeatureSelector
Factory for creating feature selectors.
create(method, **kwargs)
staticmethod
Create a feature selector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
Selection method - 'importance': Tree-based feature importance (fast) - 'correlation': Remove correlated features - 'mutual_info': Mutual information with target - 'shap': SHAP-based importance (most accurate, slower) - 'rfe': Recursive Feature Elimination (thorough, slow) - 'statistical': Mutual information with correlation redundancy filtering |
required |
**kwargs
|
Method-specific parameters |
{}
|
Returns:
| Type | Description |
|---|---|
BaseFeatureSelector
|
Feature selector instance |
Example
# Tree importance (fast, good baseline)
selector = FeatureSelector.create('importance', top_k=50)
selected = selector.select(X, y)
# SHAP (most accurate, recommended for production)
selector = FeatureSelector.create('shap', top_k=50, model_type='lgbm')
selected = selector.select(X, y)
# Correlation-based (removes redundant features)
selector = FeatureSelector.create('correlation', threshold=0.95)
selected = selector.select(X, y)
# RFE (thorough, slow)
selector = FeatureSelector.create('rfe', n_features=50)
selected = selector.select(X, y)
# Statistical selector (model-free redundancy filtering)
selector = FeatureSelector.create('statistical', top_k=50, threshold=0.8)
selected = selector.select(X, y)
Sample Weights
bitbullet.train.utils.sample_weights.SampleWeightCalculator
Factory for creating sample weight calculators.
create(strategy, **kwargs)
staticmethod
Create a sample weight calculator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
strategy
|
str
|
Strategy name ('exponential_decay', 'class_balance', 'custom') |
required |
**kwargs
|
Strategy-specific parameters |
{}
|
Returns:
| Type | Description |
|---|---|
BaseSampleWeightCalculator
|
Sample weight calculator instance |
Example
# Exponential decay for time-series
calculator = SampleWeightCalculator.create(
'exponential_decay',
time_column='days_diff',
threshold_days=180,
decay_rate=0.01
)
weights = calculator.calculate(X, y)
# Class balancing
calculator = SampleWeightCalculator.create('class_balance')
weights = calculator.calculate(X, y)
Threshold Optimisation
bitbullet.train.evaluation.threshold_optimizer.ThresholdOptimizer
Optimizes classification thresholds.
Supports multiple optimization methods: - Youden's Index (TPR - FPR) - F1 Score maximization - Custom metric optimization
Example
optimizer = ThresholdOptimizer(method="youden")
optimal_threshold, metrics = optimizer.optimize(y_true, y_pred_proba)
print(f"Optimal threshold: {optimal_threshold:.3f}")
print(f"Metrics at threshold: {metrics}")
__init__(method='youden')
Initialize threshold optimizer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
Optimization method ('youden', 'f1', 'custom') |
'youden'
|
optimize(y_true, y_pred_proba)
Find optimal threshold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
True labels |
required |
y_pred_proba
|
ndarray
|
Predicted probabilities |
required |
Returns:
| Type | Description |
|---|---|
Tuple[float, Dict[str, float]]
|
Tuple of (optimal_threshold, metrics_dict) |
Reports
bitbullet.train.reports.training_report.TrainingReport
dataclass
Container for training report data.
__str__()
Generate human-readable report string.
to_dict()
Convert report to dictionary for serialization.
to_json(path)
Save report to JSON file.
bitbullet.train.reports.training_report.TrainingReportGenerator
Generate comprehensive training reports.
from_trainer_state(trainer_state, config, study=None)
staticmethod
Generate report from trainer state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trainer_state
|
Trainer state object |
required | |
config
|
Training configuration |
required | |
study
|
Optional[Study]
|
Optuna study (optional) |
None
|
Returns:
| Type | Description |
|---|---|
TrainingReport
|
TrainingReport instance |
generate_optuna_importance_report(study, top_n=10)
staticmethod
Generate hyperparameter importance report.
Analyzes which hyperparameters had the most impact on performance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
study
|
Study
|
Completed Optuna study |
required |
top_n
|
int
|
Number of top parameters to show |
10
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with parameter importance |
generate_trial_history(study)
staticmethod
Generate trial history DataFrame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
study
|
Study
|
Completed Optuna study |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with trial history |
print_training_summary(report)
staticmethod
Print training summary to console.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report
|
TrainingReport
|
TrainingReport instance |
required |
save_full_report(report, study, output_dir)
staticmethod
Save complete training report with all artifacts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report
|
TrainingReport
|
TrainingReport instance |
required |
study
|
Optional[Study]
|
Optuna study (optional) |
required |
output_dir
|
str
|
Directory to save reports |
required |
bitbullet.train.reports.feature_report.FeatureReport
dataclass
Container for feature analysis report data.
__str__()
Generate human-readable report string.
to_dict()
Convert report to dictionary for serialization.
bitbullet.train.reports.feature_report.FeatureReportGenerator
Generate comprehensive feature analysis reports.
Inspired by the assess_features method from rfi codebase but enhanced with additional analysis capabilities.
assess_feature_stability(importance_dfs, top_k=20)
staticmethod
Assess stability of feature importance across multiple runs.
Useful for understanding which features are consistently important across different CV folds or training runs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
importance_dfs
|
List[DataFrame]
|
List of importance DataFrames from different runs |
required |
top_k
|
int
|
Number of top features to analyze |
20
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with stability metrics |
from_model(model, feature_names=None, X=None, correlation_threshold=0.95)
staticmethod
Generate feature report from a trained model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Trained model with feature_importances_ attribute |
required | |
feature_names
|
Optional[List[str]]
|
List of feature names |
None
|
X
|
Optional[DataFrame]
|
Training data (for correlation analysis) |
None
|
correlation_threshold
|
float
|
Threshold for high correlation detection |
0.95
|
Returns:
| Type | Description |
|---|---|
FeatureReport
|
FeatureReport instance |
from_selection_results(importance_df, selected_features, original_features, selection_method, X=None, correlation_threshold=0.95)
staticmethod
Generate feature report from feature selection results.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
importance_df
|
DataFrame
|
DataFrame with feature importance |
required |
selected_features
|
List[str]
|
List of selected features |
required |
original_features
|
List[str]
|
List of original features |
required |
selection_method
|
str
|
Method used for selection |
required |
X
|
Optional[DataFrame]
|
Training data (for correlation analysis) |
None
|
correlation_threshold
|
float
|
Threshold for high correlation detection |
0.95
|
Returns:
| Type | Description |
|---|---|
FeatureReport
|
FeatureReport instance |
generate_feature_summary_stats(importance_df)
staticmethod
Generate summary statistics for feature importance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
importance_df
|
DataFrame
|
DataFrame with feature importance |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, float]
|
Dictionary with summary statistics |
save_feature_report(report, output_dir, include_csv=True)
staticmethod
Save feature report to files.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report
|
FeatureReport
|
FeatureReport instance |
required |
output_dir
|
str
|
Directory to save reports |
required |
include_csv
|
bool
|
Whether to save CSV files |
True
|