Session API - Stateful Workflows
The Session API provides a stateful interface for multi-step machine learning workflows. Unlike the functional nirs4all.run() API, sessions maintain state across multiple operations, making them useful for iterative experimentation and model deployment workflows.
Important
Sessions follow the package default engine, native. Native session state uses
the bounded portable Core Archive V2 path and does not instantiate a
PipelineRunner. That runner exists only for direct-Python rollback selected
with engine="legacy". Convert a legacy .n4a archive before using it on the
default path, or select the rollback engine explicitly. See
Native Capability Preflight.
When to Use Sessions
Choose the Session API when you need:
Multi-step workflows: Train → predict → retrain → save in one coherent object
Interactive experimentation: Keep a trained model in memory for quick predictions
Production pipelines: Consistent configuration across training and prediction phases
Model persistence: Save/load trained models with their complete state
Resource efficiency: Reuse session state and workspace context across operations
For one-off experiments or quick analysis, use the simpler nirs4all.run() functional API.
Basic Usage
Creating a Session
A session is created with a pipeline definition and optional configuration:
import nirs4all
from sklearn.preprocessing import MinMaxScaler
from sklearn.cross_decomposition import PLSRegression
from sklearn.model_selection import ShuffleSplit
# Define your pipeline
# Create a session
session = nirs4all.Session(
pipeline=[
MinMaxScaler(),
ShuffleSplit(n_splits=3, test_size=0.2, random_state=42),
{"model": PLSRegression(n_components=10)},
],
name="WheatProteinModel",
verbose=1,
save_artifacts=True
)
print(f"Session: {session.name}")
print(f"Status: {session.status}") # 'initialized'
print(f"Pipeline steps: {len(session.pipeline)}")
Training with a Session
Once created, train the session on a dataset:
# Train the pipeline
result = session.run(dataset="sample_data/regression")
print(f"Best RMSE: {result.best_rmse:.4f}")
print(f"Best R²: {result.best_r2:.4f}")
print(f"Session status: {session.status}") # 'trained'
print(f"Is trained: {session.is_trained}") # True
The run() method returns a standard RunResult object with all predictions and metrics.
Making Predictions
After training, predict on new data:
import numpy as np
# Generate new data (in practice, load from file)
X_new = np.random.randn(20, 100)
# Predict using the trained session
predictions = session.predict(X_new)
print(f"Predictions shape: {predictions.shape}")
print(f"Model name: {predictions.model_name}")
print(f"First 5 predictions: {predictions.values[:5]}")
The session automatically:
Selects the best model from training
Applies all preprocessing steps in the correct order
Performs ensemble averaging across CV folds
Returns predictions in a convenient
PredictResultobject
Saving and Loading Sessions
Save the trained session to a .n4a bundle:
from pathlib import Path
# Save session
save_path = Path("exports/wheat_model.n4a")
save_path.parent.mkdir(parents=True, exist_ok=True)
session.save(save_path)
print(f"Session saved: {save_path}")
print(f"File size: {save_path.stat().st_size / 1024:.1f} KB")
Load a saved session for later use:
import nirs4all
# Load session from bundle
loaded_session = nirs4all.load_session("exports/wheat_model.n4a")
print(f"Loaded: {loaded_session.name}")
print(f"Status: {loaded_session.status}") # 'trained'
print(f"Ready for prediction: {loaded_session.is_trained}") # True
# Predict immediately
predictions = loaded_session.predict(X_new)
Session Features
Stateful Pipeline Management
Sessions maintain the complete state of your pipeline:
# Check session state at any time
print(f"Status: {session.status}") # 'initialized', 'trained', or 'error'
print(f"Is trained: {session.is_trained}") # Boolean
print(f"Pipeline: {session.pipeline}") # List of steps
print(f"Workspace: {session.workspace_path}") # Path to workspace
After training, the session holds:
Fitted preprocessing artifacts (scalers, transformers)
Trained model weights for each CV fold
Training metrics and prediction results
Workspace references for artifact loading
Automatic Workspace Context
Sessions automatically manage workspace resources:
# Specify workspace location
session = nirs4all.Session(
pipeline=pipeline,
workspace_path="my_workspace/",
name="Production_Model_v1"
)
# All artifacts saved to workspace
result = session.run(dataset)
# Session knows where to find artifacts
predictions = session.predict(X_new)
If no workspace is specified, a default workspace/ directory is created.
Training History
Sessions track their run history:
# Initial training
result1 = session.run("sample_data/regression")
# Retrain on new data
result2 = session.retrain("sample_data/new_batch", mode="transfer")
# Check history
print(f"Total runs: {len(session.history)}")
for i, entry in enumerate(session.history):
print(f"Run {i+1}: {entry['dataset']} -> RMSE {entry['best_score']:.4f}")
Each history entry records:
Dataset used
Best score achieved
Number of predictions generated
Retrain mode (if applicable)
Context Manager Pattern
Use sessions as context managers for automatic resource cleanup:
# Session auto-closes when block exits
with nirs4all.session(pipeline=pipeline, name="ContextDemo", verbose=1) as sess:
result = sess.run("sample_data/regression")
predictions = sess.predict(X_new)
print(f"Best score: {result.best_score:.4f}")
# Session resources released here
# Also works for resource sharing (no pipeline)
with nirs4all.session(verbose=0, save_artifacts=False) as shared:
# Multiple runs share the same runner
r1 = nirs4all.run(pipeline1, dataset1, session=shared)
r2 = nirs4all.run(pipeline2, dataset2, session=shared)
Complete Example
Here’s a full workflow showing session creation through deployment:
import nirs4all
import numpy as np
from sklearn.preprocessing import MinMaxScaler, StandardScaler
from sklearn.cross_decomposition import PLSRegression
from sklearn.model_selection import ShuffleSplit
from nirs4all.operators.transforms import StandardNormalVariate as SNV
# 1. Define pipeline
# 2. Create and train session
session = nirs4all.Session(
pipeline=[
MinMaxScaler(), # X scaling
ShuffleSplit(n_splits=5, test_size=0.2), # 5-fold CV
SNV(), # SNV preprocessing
{"y_processing": StandardScaler()}, # Target scaling
{"model": PLSRegression(n_components=15)}, # PLS model
],
name="WheatProtein_Production_v2",
verbose=1,
workspace_path="production_workspace/",
random_state=42
)
print("Training session...")
result = session.run(dataset="data/wheat_samples/")
# 3. Validate results
print(f"\nTraining complete:")
print(f" Predictions: {result.num_predictions}")
print(f" Best RMSE: {result.best_rmse:.4f}")
print(f" Best R²: {result.best_r2:.4f}")
print(f" Status: {session.status}")
# 4. Test predictions
X_validation = np.load("data/validation_set.npy")
validation_preds = session.predict(X_validation)
print(f"\nValidation predictions:")
print(f" Shape: {validation_preds.shape}")
print(f" Mean: {validation_preds.values.mean():.2f}")
print(f" Std: {validation_preds.values.std():.2f}")
# 5. Save for deployment
save_path = "exports/wheat_protein_v2.n4a"
session.save(save_path)
print(f"\nModel exported: {save_path}")
# 6. Simulate deployment: load and predict
deployed_session = nirs4all.load_session(save_path)
X_new_sample = np.random.randn(1, 100) # New sample from sensor
prediction = deployed_session.predict(X_new_sample)
print(f"\nDeployment test:")
print(f" Input shape: {X_new_sample.shape}")
print(f" Prediction: {prediction.values[0]:.2f}")
Advanced Usage
Retraining
Update a trained session with new data:
# Initial training
session = nirs4all.Session(pipeline=pipeline, name="Adaptive")
result = session.run("data/initial_batch/")
# New data arrives
new_result = session.retrain(
dataset="data/new_batch/",
mode="transfer" # or "full", "finetune"
)
print(f"Initial RMSE: {result.best_rmse:.4f}")
print(f"Retrained RMSE: {new_result.best_rmse:.4f}")
Retrain modes:
"full": Complete retraining from scratch"transfer": Transfer learning (keep preprocessing, retrain model)"finetune": Fine-tuning (update model weights incrementally)
Multiple Sessions
Manage multiple models in parallel:
# Create sessions for different targets
protein_session = nirs4all.Session(pipeline=protein_pipeline, name="Protein")
moisture_session = nirs4all.Session(pipeline=moisture_pipeline, name="Moisture")
# Train in parallel
protein_result = protein_session.run("data/wheat/")
moisture_result = moisture_session.run("data/wheat/")
# Predict with both
protein_pred = protein_session.predict(X_new)
moisture_pred = moisture_session.predict(X_new)
print(f"Protein: {protein_pred.values[0]:.2f}%")
print(f"Moisture: {moisture_pred.values[0]:.2f}%")
Resource Sharing Mode
Use a session without a pipeline to share resources across multiple nirs4all.run() calls:
# Context manager for shared runner
with nirs4all.session(verbose=1, workspace_path="shared_workspace/") as shared:
# Try different pipelines with shared configuration
r1 = nirs4all.run(pipeline=[MinMaxScaler(), PLSRegression(5)],
dataset="data/", session=shared)
r2 = nirs4all.run(pipeline=[MinMaxScaler(), PLSRegression(10)],
dataset="data/", session=shared)
r3 = nirs4all.run(pipeline=[MinMaxScaler(), PLSRegression(15)],
dataset="data/", session=shared)
# Compare results
print(f"PLS(5): RMSE = {r1.best_rmse:.4f}")
print(f"PLS(10): RMSE = {r2.best_rmse:.4f}")
print(f"PLS(15): RMSE = {r3.best_rmse:.4f}")
This shares session context. A PipelineRunner is created only if the caller
selects engine="legacy" explicitly.
API Reference
Creating Sessions
nirs4all.Session(pipeline, name, **kwargs)
Create a stateful session for pipeline management.
Parameters:
pipeline(list): Pipeline definition (list of steps)name(str): Session name for identificationverbose(int): Verbosity level (0-3). Default: 1save_artifacts(bool): Save artifacts to workspace. Default: Trueworkspace_path(str|Path): Workspace directory pathrandom_state(int): Random seed for reproducibilityplots_visible(bool): Show plots during training
Returns: Session object
nirs4all.session(pipeline, name, **kwargs)
Context manager for creating a session (same parameters as above).
Returns: Context manager yielding Session
nirs4all.load_session(path)
Load a session from a saved .n4a bundle.
Parameters:
path(str|Path): Path to.n4abundle file
Returns: Session object ready for prediction when its bundle is supported by the selected engine. Default-native prediction requires Core Archive V2 state.
Session Methods
session.run(dataset, **kwargs)
Train the session’s pipeline on a dataset.
Parameters:
dataset(str|Path|tuple|dict): Dataset to train onPath to data folder:
"sample_data/regression"NumPy arrays:
(X, y)or{"X": X, "y": y}
plots_visible(bool): Show plots during training
Returns: RunResult with predictions and metrics
session.predict(dataset, **kwargs)
Make predictions using the trained pipeline.
Parameters:
dataset(str|Path|array): Data to predict onPath to data folder
NumPy array X
Dict with ‘X’ key
Returns: PredictResult with predictions
Raises: ValueError if the session is not trained; native prediction of a
legacy archive raises a structured RtError with capability
legacy_archive_conversion_required
session.retrain(dataset, mode="full", **kwargs)
Retrain the pipeline on new data.
Parameters:
dataset(str|Path|tuple|dict): New datasetmode(str): Retrain mode - “full”, “transfer”, or “finetune”
Returns: RunResult from retraining
Raises: ValueError if session not trained
session.save(path)
Save the trained session to a .n4a bundle file.
Parameters:
path(str|Path): Output path (e.g.,"models/my_model.n4a")
Returns: Path to saved bundle
Raises: ValueError if session not trained
Session Properties
session.name → str
Session name
session.pipeline → list
Pipeline definition (list of steps)
session.status → str
Current status:
"initialized","trained", or"error"
session.is_trained → bool
Whether the pipeline has been trained or loaded from bundle
session.history → list[dict]
List of run history entries with dataset, scores, and metadata
session.workspace_path → Path
Path to the workspace directory
session.runner → PipelineRunner
Legacy-only rollback surface. Access is refused for native session state; it is created lazily only after explicit
engine="legacy"selection.
Session Lifecycle
┌─────────────┐
│ initialized │ ← nirs4all.Session(pipeline=...) or nirs4all.load_session()
└──────┬──────┘
│ session.run(dataset)
▼
┌─────────────┐
│ trained │ ← Ready for predict(), retrain(), save()
└──────┬──────┘
│ session.save() or session.retrain()
▼
┌─────────────┐
│ saved/ │ ← Persistent bundle file
│ updated │ Can load and return to trained state
└─────────────┘
Methods available by state:
State |
Available Methods |
|---|---|
|
|
|
|
|
|
|
None (must recreate session) |
Session vs Functional API
Feature |
Session API |
Functional API ( |
|---|---|---|
State management |
✅ Stateful |
❌ Stateless |
Multi-step workflows |
✅ Native |
❌ Manual |
Model persistence |
✅ |
⚠️ |
Prediction |
✅ |
⚠️ |
Retraining |
✅ |
⚠️ |
Resource sharing |
✅ Context manager |
❌ None |
Code verbosity |
Lower for workflows |
Lower for single runs |
Best for |
Production, iterative work |
Quick experiments, one-off analysis |
Recommendation:
Use Session API for production pipelines, model deployment, and iterative experimentation
Use Functional API for quick analysis, one-off experiments, and Jupyter notebooks
Common Patterns
Production Pipeline
# config.py
# train.py
import nirs4all
from config import PRODUCTION_PIPELINE
session = nirs4all.Session(
pipeline=[
MinMaxScaler(),
ShuffleSplit(n_splits=10, test_size=0.15, random_state=42),
SNV(),
{"model": PLSRegression(n_components=20)},
],
name="Production_Wheat_v3",
workspace_path="/data/production_workspace/",
random_state=42,
verbose=2
)
result = session.run(dataset="/data/wheat_samples/")
print(f"Training complete: RMSE = {result.best_rmse:.4f}")
session.save("/models/wheat_v3.n4a")
print("Model deployed")
# predict.py (deployed system)
import nirs4all
import numpy as np
session = nirs4all.load_session("/models/wheat_v3.n4a")
def predict_sample(spectra: np.ndarray) -> float:
"""Predict protein content from NIR spectra."""
result = session.predict(spectra)
return result.values[0]
Iterative Experimentation
# Quick iteration on model hyperparameters
session = nirs4all.Session(pipeline=base_pipeline, name="Experiment")
# Try different component counts
for n_comp in [5, 10, 15, 20]:
session.pipeline[-1] = {"model": PLSRegression(n_components=n_comp)}
result = session.run("data/train/")
print(f"n_components={n_comp}: RMSE={result.best_rmse:.4f}")
Multi-Model Deployment
# Load multiple models for ensemble prediction
models = {
"pls": nirs4all.load_session("models/pls_model.n4a"),
"rf": nirs4all.load_session("models/rf_model.n4a"),
"nicon": nirs4all.load_session("models/nicon_model.n4a"),
}
# Ensemble prediction
X_new = load_sample()
predictions = {name: sess.predict(X_new).values[0]
for name, sess in models.items()}
ensemble_pred = np.mean(list(predictions.values()))
print(f"Ensemble prediction: {ensemble_pred:.2f}")
See Also
Making Predictions - Prediction fundamentals
Export and Deployment - Bundle format and export options
Advanced Predictions - Retraining and transfer learning
Predictions API Reference - PredictResult API reference
See also
Related Examples:
D01: Session Workflow - Complete session API workflow guide
U03: Workspace Management - Session context and workspace structure
U01: Save, Load, Predict - Basic prediction workflow