Optimization
Optimization selects the COBRA hyperparameters that minimize the cross-validation objective.
In the current source, optimization is responsible for parameters such as:
rather than for fitting the original base estimators.
The optimizer subsystem lives under:
kfc_procedure/cobra/core/optimizers/
├── base.py
├── _utils.py
├── search/
│ ├── base.py
│ └── search.py
└── gradient/
├── base.py
├── gd.py
├── momentum.py
└── adam.py
The currently registered optimizers are:
and are resolved through:
Where optimization fits
The COBRA training flow is approximately:
flowchart LR
P["Calibration representation"]
D["Distance matrix"]
C["CV folds"]
T["Candidate parameters"]
A["Adapter"]
K["Kernel"]
G["Aggregator"]
L["Loss"]
O["Optimizer"]
P --> D
D --> A
T --> A
A --> K --> G --> L
C --> G
L --> O
O --> T
The objective being minimized is the mean cross-validation loss:
where \(\theta\) may be:
for a one-parameter bandwidth model, or:
for two-parameter MixCOBRA.
Optimization is a separate layer
The source keeps several concerns separate:
Distance
defines geometry
Kernel adapter
inserts tunable parameters
Kernel
converts adapted distances to weights
Aggregator
produces predictions
Loss
evaluates predictions
Optimizer
searches for parameters minimizing the loss
Changing the optimizer does not change the distance, kernel, or loss formulas themselves.
BaseOptimizer
All optimizers inherit:
Its constructor is:
It stores:
and:
Unified optimization interface
Every optimizer implements:
where:
The base class also implements:
as:
So these are equivalent:
and:
OptimizerFactory
Create an optimizer with:
from kfc_procedure.cobra.core.optimizers import (
OptimizerFactory,
)
optimizer = OptimizerFactory.create(
"grid",
param_grid={
"bandwidth": [
0.1,
1.0,
10.0,
]
},
)
Registered categories
The current factory registrations attach categories.
grid is registered with:
while:
are registered with:
This lets higher-level estimators check whether a configured optimizer belongs to the appropriate family.
Inspect optimizers
Current registered names should include:
Inspect by category
returns the search family.
returns the gradient family.
Two optimization families
The source divides optimization into:
-
Search optimization
Evaluate a discrete set of candidate parameter combinations.
Current implementation:
-
Gradient optimization
Iteratively update continuous parameters using numerical or supplied gradients.
Current implementations:
Grid search
The registered search optimizer is:
under the name:
It inherits:
and performs exhaustive evaluation over a supplied parameter grid.
Constructor
where param_grid is a dictionary such as:
Candidate generation
GridSearchOptimizer.candidates() does:
keys = list(
self.param_grid.keys()
)
values = list(
self.param_grid.values()
)
grid = list(
product(
*values
)
)
then converts every combination to a float array.
So the candidate ordering follows:
over the dictionary's insertion-ordered values.
Parameter names are not passed to the objective
The generated candidate is only the numeric vector:
The objective therefore sees something like:
rather than:
Parameter meaning is determined by grid insertion order and the objective's interpretation of vector positions.
One-dimensional grid example
optimizer = GridSearchOptimizer(
param_grid={
"bandwidth": [
0.1,
0.5,
1.0,
]
},
show_process=False,
)
The generated candidates are conceptually:
Two-dimensional grid example
The source evaluates all Cartesian-product pairs:
Search risk reduction
BaseSearchOptimizer supports objective functions returning either:
or:
through:
The constructor parameter is:
by default.
Supported risk strategies
The current source supports:
through:
strategies = {
"mean": np.mean,
"sum": np.sum,
"max": np.max,
"min": np.min,
"median": np.median,
"l2": np.linalg.norm,
}
Scalar objective
If:
the score is returned directly as:
So the risk strategy has no effect on normal scalar COBRA CV objectives.
Best-candidate selection
After evaluating all candidates, the search optimizer computes:
and finds all matching indices.
If there is one minimum:
If several candidates have exactly the same minimum risk:
Tie behavior
This means the source does not select the first tied grid candidate.
It selects the middle index among the tied candidate positions.
For tied indices:
it selects:
For tied indices:
it selects:
because:
Grid result structure
BaseSearchOptimizer.optimize() returns:
{
"x": ...,
"score": ...,
"risk": ...,
"best_index": ...,
"history": ...,
"scores": ...,
"risks": ...,
}
The key used by the main COBRA estimators is primarily:
Search history
Every evaluated candidate contributes:
to the history.
So grid-search history contains every candidate, including candidates evaluated after the eventual optimum was already encountered.
init_param in search optimizers
BaseSearchOptimizer.optimize() accepts:
for API compatibility but does not use it.
The candidate set comes entirely from:
Gradient optimizers
The gradient family is built on:
with three update rules:
The detailed gradient algorithms are covered in Gradient Optimization.
This page focuses on how that family plugs into the general optimizer system.
Gradient optimizer defaults
BaseGradientOptimizer.__init__() currently defines:
learning_rate=0.01
max_iter=300
tol=1e-7
speed="constant"
gradient_method="central"
eps=1e-7
n_tries=5
init_range=(1e-4, 3.0)
show_process=True
Additional unknown keyword arguments are accepted through:
and stored in the base optimizer's:
dictionary.
Numerical gradients
Unless an analytical gradient function is explicitly supplied to
optimize(), the gradient optimizer calls:
from:
The default method is:
finite differences.
Supported gradient estimators
The numerical-gradient dispatcher contains:
These are implementation details of the gradient optimization family rather
than separate OptimizerFactory optimizers.
Gradient initialization
If:
the gradient base class performs a small initialization search.
It creates:
using:
and:
Then for parameter dimension dim:
The candidate with the minimum objective value is used as the initial parameter vector.
Important initialization geometry
For dimensions greater than one, this initialization searches only diagonal vectors:
It does not search all combinations of independent coordinate values.
For example, with two dimensions it tests:
rather than every:
combination.
Initial parameter used by COBRA estimators
The main estimator methods normally provide explicit initial parameters for gradient optimization.
GradientCOBRA uses:
MixCOBRA uses:
in one-parameter mode and:
in two-parameter mode.
So the gradient base class's automatic initialization search is normally bypassed in those paths.
Gradient result structure
BaseGradientOptimizer.optimize() returns:
This matches the keys expected by GradientCOBRA and MixCOBRA.
Unlike the search result, it does not return:
Optimization history conversion
The high-level estimators convert optimizer histories through:
This helper creates:
and, if an x column exists, expands vector coordinates into named columns.
Example
For a history entry:
and:
the resulting DataFrame contains a:
column and drops:
GradientCOBRA optimization
The relevant constructor parameters are:
optimizer="grid"
optimizer_params=None
opt_method="grid"
bandwidth_list=None
learning_rate=0.1
max_iter=300
The distinction between:
and:
is important.
opt_method
For GradientCOBRA:
selects the optimization family:
while:
selects the concrete registered optimizer.
GradientCOBRA grid path
With the defaults:
the estimator creates candidate bandwidths:
unless:
was supplied.
Custom bandwidth list
The supplied array becomes the grid directly.
The source does not sort, deduplicate, or validate positivity of this list before grid construction.
GradientCOBRA category validation
For:
the source requires:
If not, it raises a ValueError listing the registered search optimizers.
For:
it requires category:
GradientCOBRA optimizer parameters
For grid mode, the source updates the optimizer parameter dictionary with:
The current GridSearchOptimizer does not use random_state; because its base
classes accept extra **kwargs, the value is simply stored in optimizer
configuration.
Gradient mode parameters
For gradient mode:
So constructor-level:
override same-named values that may have been placed in:
before the update.
Other optimizer parameters remain configurable through:
Kernel compatibility fallback
Before resolving the optimizer family, GradientCOBRA checks:
So a non-gradient-compatible kernel automatically changes the effective method from:
to:
Effective method is recorded
GradientCOBRA stores:
where method is the effective value after any fallback.
So this model records the actual method used.
GradientCOBRA result
After optimization:
The stored summary is:
self.optimization_outputs_ = {
"method": method,
"optimizer": optimizer,
"bandwidth": self.bandwidth_,
"score": result["score"],
"history": history_df,
"evaluations": len(
result["history"]
),
}
Inspect GradientCOBRA optimization
The history is a pandas DataFrame:
MixCOBRA optimization
The current MixCOBRA constructor exposes:
optimizer="grid"
optimizer_params=None
alpha_list=None
beta_list=None
opt_method="grid"
learning_rate=0.01
max_iter=300
one_parameter=False
Its optimization target depends on:
MixCOBRA candidate arrays
If explicit lists are absent:
and:
Two-parameter MixCOBRA
With the default:
grid search uses:
This creates the full Cartesian product.
If both candidate arrays contain:
values, the exhaustive grid contains:
candidate parameter pairs.
Each candidate then evaluates the cross-validation objective.
Grid size
max_iter controls the number of values per parameter in the default
two-parameter MixCOBRA grid.
It does not mean only max_iter total objective evaluations.
One-parameter MixCOBRA
With:
the optimizer is constructed with:
even though the one-parameter adapter itself represents a parameter named:
The one-dimensional objective simply reads:
so the dictionary key does not affect the numeric optimization.
Naming inconsistency
Later in the grid execution branch, the source creates a local variable:
and passes it as the second positional argument to:
But for search optimizers this second argument is interpreted as:
and is ignored.
The actual candidate grid was already stored when the optimizer was constructed.
So this local param_grid does not reconfigure the search optimizer.
MixCOBRA category validation
Like GradientCOBRA:
requires a registered:
optimizer.
requires a registered:
optimizer.
MixCOBRA kernel fallback
If:
but:
the local variable:
is changed to:
The appropriate optimizer category is then validated using this effective method.
MixCOBRA reporting caveat
Although MixCOBRA may change the local:
from gradient to grid, its stored output uses:
rather than the effective local variable.
Therefore:
can report:
even when a non-gradient kernel caused the source to execute the grid path.
Current source inconsistency
GradientCOBRA records the effective optimization method.
MixCOBRA records the originally configured self.opt_method.
MixCOBRA optimizer construction
The source calls:
The current built-in optimizer constructors accept extra **kwargs, so the
seed can be stored even when the optimizer does not use randomness directly.
MixCOBRA optimization outputs
The stored summary is:
{
"method": self.opt_method,
"score": result["score"],
"history": history_df,
"evaluations": len(
result["history"]
),
"params": result["x"],
}
The fitted parameter vector is therefore available as:
Parameter names in history
MixCOBRA always calls:
In one-parameter mode, x has only one column.
The helper loops over the actual coordinate count, so only:
is created; the extra name:
is unused.
CombinedClassifier optimization
CombinedClassifier exposes:
but it does not expose:
as a public constructor parameter.
The source optimization method is structurally simpler than GradientCOBRA and MixCOBRA.
CombinedClassifier default grid
Candidate bandwidths are:
when provided, otherwise:
CombinedClassifier optimizer construction
The source builds:
then updates:
{
"param_grid": {
"bandwidth": bandwidth_candidates,
},
"max_iter": self.max_iter,
"random_state": self.random_state,
}
and creates:
No category check in CombinedClassifier
Unlike GradientCOBRA and MixCOBRA, the current classifier does not call:
inside _optimize_hyperparameters().
So the source does not explicitly enforce that:
belongs to the search family.
The default:
does, but other registered optimizers can be constructed if their constructor accepts the supplied parameters.
CombinedClassifier reporting
After optimization:
is read from:
and:
is stored as:
{
"method": "grid",
"optimizer": self.optimizer,
"bandwidth": self.bandwidth_,
"score": result["score"],
"history": history_df,
}
Reporting caveat for non-grid optimizer names
Because the stored method is hard-coded to:
the output does not dynamically describe the optimizer's actual algorithm.
For the supported default:
this is consistent.
If a different registered optimizer is forced through the current API,
optimization_outputs_["method"] still says:
Search complexity
For a one-dimensional candidate list with:
values, grid search performs:
objective evaluations.
For a two-dimensional Cartesian grid with:
and:
values, it performs:
Cross-validation cost
Each objective evaluation itself loops over the stored CV folds.
So approximate objective work is:
For two-parameter MixCOBRA with default:
this means:
candidate evaluations and approximately:
fold-level aggregation/loss evaluations.
This count describes the loop structure; individual fold work also depends on calibration size.
max_iter has different meanings
The parameter:
is reused by multiple optimization modes.
For one-dimensional default grid search:
For two-dimensional MixCOBRA grid search:
For gradient optimization:
So max_iter=300 does not imply the same computational workload in each mode.
Custom search grid
GradientCOBRA:
MixCOBRA:
model = MixCOBRARegressor(
alpha_list=np.array([
0.1,
0.5,
1.0,
]),
beta_list=np.array([
0.1,
0.5,
1.0,
]),
)
CombinedClassifier:
Candidate validation
The high-level source generally converts supplied candidate lists with:
but does not perform shared validation for:
The optimizer evaluates whatever numeric candidates are present, subject to downstream operations.
Empty grid behavior
GridSearchOptimizer creates:
and later computes:
If the parameter grid produces no candidates, the current implementation has no explicit friendly empty-grid validation.
A lower-level NumPy error can occur during best-candidate selection.
Parameter bounds
Neither GridSearchOptimizer nor the gradient base optimizer defines general
hard parameter bounds.
Grid search is implicitly bounded by the candidate list.
Gradient optimization can move outside the initialization range and outside the typical positive bandwidth region because no projection or clipping step is implemented in the base optimizer.
Negative gradient parameters
The gradient update rules operate directly on real-valued vectors.
There is no general source-level constraint such as:
Thus gradient optimization can in principle propose negative values.
The objective, adapter, and kernel then receive those values as implemented.
No projection step
The current gradient optimizer base does not project parameters back into the positive grid-search interval.
Positivity is enforced by neither BaseGradientOptimizer nor the standard
update rules.
Search determinism
GridSearchOptimizer itself contains no random candidate generation.
Given:
its candidate order and selection logic are deterministic.
A random_state may still affect the surrounding COBRA estimator through:
but not the grid enumeration itself.
Progress display
Both search and gradient base classes use:
when available.
If tqdm cannot be imported, the source defines:
so optimization still runs without progress bars.
Disable search progress
Pass:
to a high-level estimator.
For example:
The parameter reaches GridSearchOptimizer through **kwargs.
Inspect the resolved optimizer
After fitting:
The base representation is:
because BaseOptimizer.__repr__() returns:
Why some visible optimizer parameters are absent from config
Subclass constructors often consume arguments before calling the base class.
For example, BaseGradientOptimizer consumes:
and only forwards remaining **kwargs into:
Therefore:
is not a complete snapshot of every optimizer attribute.
Inspect explicit attributes as well.
Inspect grid history
outputs = model.optimization_outputs_
history = outputs[
"history"
]
print(
history.head()
)
print(
history.tail()
)
For GradientCOBRA and CombinedClassifier, useful columns include:
for grid search.
Find the best history row
For scalar grid objectives:
The optimizer's tie rule may select the middle tied candidate rather than the
first row returned by a basic idxmin(), so exact tie reproduction should use
the optimizer's selection logic.
Inspect MixCOBRA history
In two-parameter grid mode, it normally contains:
Direct grid optimizer example
import numpy as np
from kfc_procedure.cobra.core.optimizers import (
GridSearchOptimizer,
)
def objective(
params,
):
x = float(
params[0]
)
return (
x - 2.0
) ** 2
optimizer = GridSearchOptimizer(
param_grid={
"x": np.array([
0.0,
1.0,
2.0,
3.0,
])
},
show_process=False,
)
result = optimizer.optimize(
objective
)
print(
result["x"]
)
print(
result["score"]
)
Vector-valued objective example
The search base can reduce a vector score:
Then:
uses the mean of the two returned values as the selection risk.
The main COBRA objectives currently return scalar losses, so this feature is more relevant to custom optimizer use.
Custom optimizer
A custom optimizer can subclass:
and implement:
Example:
import numpy as np
from kfc_procedure.cobra.core.optimizers import (
BaseOptimizer,
OptimizerFactory,
)
@OptimizerFactory.register(
"fixed_one",
categories={
"search"
},
)
class FixedOneOptimizer(
BaseOptimizer
):
def optimize(
self,
objective,
init_param=None,
**kwargs,
):
x = np.array([
1.0
])
score = objective(
x
)
return {
"x": x,
"score": score,
"history": [
{
"iter": 0,
"x": x.copy(),
"score": score,
}
],
}
Result-contract requirement
A custom optimizer intended for use by the current high-level COBRA estimators should return at least:
because those keys are accessed directly after optimization.
GradientCOBRA also calls:
and converts history to a DataFrame.
Category requirement
For GradientCOBRA or MixCOBRA:
expects the registered optimizer to support category:
and:
expects category:
A custom registered optimizer without the matching category will be rejected before construction in those two estimators.
Custom search optimizer
If the new method works on candidate sets, subclassing:
lets you reuse:
and implement only:
plus any specialized constructor behavior.
Custom gradient optimizer
For a new update rule, subclass:
and implement:
while reusing the numerical-gradient and iteration machinery.
See:
for that extension path.
Optimization and kernels
GradientCOBRA and MixCOBRA make a source-level distinction based on:
If a configured kernel does not support gradient optimization, requested gradient mode is replaced with grid mode.
Current non-gradient kernels include:
Current gradient-compatible kernels include:
Optimization and adapters
The optimizer does not directly modify kernel objects in the main COBRA paths.
Instead it changes adapter parameters.
GradientCOBRA:
Two-parameter MixCOBRA:
Then the adapted distance matrix is passed to the kernel.
Bandwidth direction
For the one-parameter adapter:
With the default RBF kernel:
so:
Therefore larger optimized bandwidth values make RBF similarity decay faster.
This is the package's implemented bandwidth direction.
Grid vs gradient
The source provides different optimization mechanisms rather than one being a drop-in implementation detail.
| Property | Grid | Gradient family |
|---|---|---|
| candidates | explicit discrete set | iterative continuous updates |
| gradient required | No | estimated or supplied |
| built-in optimizer | grid |
gd, momentum, adam |
| parameter positivity | candidate-list dependent | not constrained |
| exhaustive within supplied grid | Yes | No |
| history | all candidates | iterative path |
| tie handling | middle tied candidate | best score encountered |
| compact/non-smooth kernel support | Yes | automatically falls back in Gradient/MixCOBRA |
Choosing via GradientCOBRA
Grid:
Gradient descent:
Momentum:
Adam:
Configure optimizer internals
model = GradientCOBRA(
optimizer="adam",
opt_method="grad",
learning_rate=0.05,
optimizer_params={
"tol": 1e-6,
"gradient_method": "central",
"show_process": False,
"beta1": 0.9,
"beta2": 0.999,
},
)
The high-level source adds its own:
after copying optimizer_params.
MixCOBRA example
model = MixCOBRARegressor(
optimizer="grid",
opt_method="grid",
alpha_list=np.array([
0.1,
0.5,
1.0,
]),
beta_list=np.array([
0.1,
0.5,
1.0,
]),
)
For gradient mode:
CombinedClassifier example
The source-supported default path is:
Because the class has no opt_method switch and reports its method as grid,
this is the clearest current public optimization path.
Reproducibility
Grid enumeration itself is deterministic.
Gradient optimizers using:
gradient methods are deterministic for a deterministic objective.
However:
uses:
with the global NumPy random generator.
The current spsa_gradient() function does not accept or use the estimator's
random_state.
So SPSA-specific reproducibility is not controlled by the normal COBRA seed.
Optimization history caveat at early stopping
In BaseGradientOptimizer.optimize(), the source checks:
before appending the current iteration to:
Therefore the terminating step is not added to history when stopping occurs through the gradient-norm condition.
The best score can still be updated before the break.
Initial gradient evaluation
Before entering the loop, the gradient optimizer computes:
and:
These evaluations are also not represented as ordinary history entries.
Thus:
is not the total number of objective calls made by a gradient optimizer.
Objective-call cost of finite differences
For parameter dimension:
central differences require approximately:
objective calls per gradient evaluation.
The optimizer also separately evaluates the objective score.
So gradient mode can call the cross-validation objective many times per iteration even though the stored history records only one row per completed iteration.
Debugging optimization
Inspect configured values
print(
model.optimizer
)
print(
getattr(
model,
"opt_method",
None,
)
)
print(
model.optimizer_params
)
Inspect resolved optimizer
Inspect selected parameters
GradientCOBRA:
MixCOBRA:
CombinedClassifier:
Inspect the final score
This is the objective value associated with the optimizer's selected parameter vector.
Inspect evaluation history
Check effective method
For GradientCOBRA:
reflects the effective method.
For MixCOBRA, remember that the current source records the originally configured:
even after a kernel-driven fallback.
Current implementation summary
| Optimizer | Registry | Category | Core behavior |
|---|---|---|---|
GridSearchOptimizer |
grid |
search, derivative-free | exhaustive Cartesian grid |
GradientDescentOptimizer |
gd |
optimizer, gradient | vanilla gradient step |
MomentumOptimizer |
momentum |
optimizer, gradient | velocity update |
AdamOptimizer |
adam |
optimizer, gradient | adaptive first/second moments |
High-level estimator optimization
| Estimator | Default optimizer | Family selector | Tuned parameters |
|---|---|---|---|
GradientCOBRA |
grid |
opt_method |
bandwidth |
MixCOBRARegressor |
grid |
opt_method |
bandwidth-like one parameter or alpha, beta |
CombinedClassifier |
grid |
no public opt_method |
bandwidth |
Important current-source caveats
| Area | Current behavior |
|---|---|
| search optimizer implementations | only grid |
| gradient implementations | GD, Momentum, Adam |
| grid candidate validation | minimal |
| grid tie selection | middle tied candidate |
| gradient bounds/projection | none |
| default gradient method | central finite difference |
| SPSA seed | not linked to model random_state |
| GradientCOBRA kernel fallback reporting | effective method recorded |
| MixCOBRA kernel fallback reporting | configured method recorded |
| CombinedClassifier category validation | absent |
| CombinedClassifier method output | hard-coded "grid" |
| two-parameter Mix grid size | Cartesian product |
| gradient history evaluation count | undercounts objective calls |
| custom optimizer result keys needed | x, score, history |
Quick reference
| Goal | Current source configuration |
|---|---|
| exhaustive bandwidth search | optimizer="grid", opt_method="grid" |
| vanilla gradient updates | optimizer="gd", opt_method="grad" |
| momentum updates | optimizer="momentum", opt_method="grad" |
| Adam updates | optimizer="adam", opt_method="grad" |
| custom grid values | bandwidth_list, alpha_list, beta_list |
| disable progress bar | optimizer_params={"show_process": False} |
| inspect selected result | optimization_outputs_ |
| inspect optimizer object | optimizer_ |
Mental model
COBRA optimization searches the parameterization of the aggregation geometry, not the original estimator coefficients.
For a one-parameter model:
For two-parameter MixCOBRA:
The current source supports exhaustive grid search as the default and three gradient update rules for continuous optimization.