Inspect Fitted KFC Models
After KFCRegressor or KFCClassifier has been fitted, the three trained
stages are available directly from the estimator:
These attributes expose the fitted clustering models, local predictive models, and final combiner.
This page shows how to inspect each stage and how to reconstruct the intermediate predictions that move through the KFC pipeline.
Fit a model first
For regression:
from kfc_procedure import KFCRegressor
model = KFCRegressor(
divergences=["euclidean"],
local_model="ridge",
combiner="gradientcobra",
random_state=42,
)
model.fit(
X_train,
y_train,
)
For classification:
from kfc_procedure import KFCClassifier
model = KFCClassifier(
divergences=["euclidean"],
local_model="logistic_regression",
combiner="combined_classifier",
random_state=42,
)
model.fit(
X_train,
y_train,
)
After fit(), KFC checks and uses the fitted attributes:
during prediction.
Pipeline inspection map
flowchart LR
X["New X"]
K["model.kstep_"]
C["cluster assignments"]
F["model.fstep_"]
P["prediction matrix"]
S["model.cstep_.strategy_"]
Y["final prediction"]
X --> K --> C --> F --> P --> S --> Y
A useful debugging pattern is therefore:
clusters = model.kstep_.predict(X_test)
P = model.fstep_.predict(
X_test,
clusters,
)
y_pred = model.cstep_.predict(P)
This should reproduce:
for the same fitted model.
Inspect the K-Step
The fitted clustering stage is:
Its main fitted attributes are:
kstep_.models_
models_ is a dictionary containing one fitted BregmanKMeans instance per
divergence.
For example:
when those four divergence strings were configured.
Inspect the model types:
for divergence, clustering_model in model.kstep_.models_.items():
print(
divergence,
type(clustering_model).__name__,
)
Typical output:
Inspect cluster centers
Each fitted BregmanKMeans stores:
Example:
Its shape is:
You can inspect the dimensions:
Inspect training cluster labels
The underlying clustering model stores:
Example:
These labels correspond to the internal K-Step training subset used during
fit().
kstep_.clusters_
K-Step also stores all training assignments in:
This is a dictionary keyed by divergence.
You can inspect cluster counts:
import numpy as np
for divergence, labels in model.kstep_.clusters_.items():
unique, counts = np.unique(
labels,
return_counts=True,
)
print(
divergence,
dict(
zip(
unique,
counts,
)
),
)
This is especially useful when diagnosing very small clusters.
Inspect cluster distortion
Each BregmanKMeans stores:
In the current implementation, this is the best run's average Bregman distortion.
Note
Do not assume that inertia_ values from different divergence families
are directly comparable as though they were measured on the same metric
scale.
Inspect iteration count
The selected clustering run stores:
Example:
Inspect all main BregmanKMeans state
for divergence, km in model.kstep_.models_.items():
print(
"\nDivergence:",
divergence,
)
print(
"centers:",
km.cluster_centers_.shape,
)
print(
"labels:",
km.labels_.shape,
)
print(
"distortion:",
km.inertia_,
)
print(
"iterations:",
km.n_iter_,
)
Predict and inspect new cluster assignments
For new observations:
The result is a dictionary:
Inspect it:
Every value has shape:
Inspect distances to centroids
The underlying BregmanKMeans provides:
For example:
With three clusters:
Each row contains the divergence from one observation to each fitted centroid.
The predicted cluster is the minimum-distance centroid.
manual_labels = np.argmin(
D,
axis=1,
)
automatic_labels = km.predict(
X_test
)
print(
np.array_equal(
manual_labels,
automatic_labels,
)
)
Inspect the F-Step
The fitted local-model stage is:
Its main fitted attribute is:
Structure of fstep_.models_
The current source stores a nested dictionary:
Example:
{
"euclidean": {
"m0": {
"divergence": "euclidean",
"cluster": 0,
"model": ...
},
"m1": {
"divergence": "euclidean",
"cluster": 1,
"model": ...
}
}
}
Inspect the top-level keys:
List all fitted local models
for divergence, models in model.fstep_.models_.items():
print(
"\nDivergence:",
divergence,
)
for model_name, metadata in models.items():
print(
model_name,
"cluster=",
metadata["cluster"],
"type=",
type(
metadata["model"]
).__name__,
)
Count local models
Total count:
total = sum(
len(models)
for models in model.fstep_.models_.values()
)
print(
"Total local models:",
total,
)
With \(M\) divergences and \(K\) populated clusters per divergence, this will usually be approximately:
Get one local model
For example:
metadata = model.fstep_.models_[
"euclidean"
]["m0"]
local_model = metadata["model"]
print(
local_model
)
Inspect its cluster ID:
and divergence:
Inspect local-model parameters
For scikit-learn-backed local models, the wrapper exposes:
Example:
This is useful for confirming that options in:
were actually forwarded to the underlying estimator.
Inspect the wrapped scikit-learn model
When the local estimator is represented by the package's scikit-learn adapter, it stores the underlying estimator on:
Example:
wrapped = model.fstep_.models_[
"euclidean"
]["m0"]["model"]
print(
type(
wrapped.model
).__name__
)
Then normal scikit-learn fitted attributes may be available.
For example, for linear or logistic regression:
when the underlying estimator defines coef_.
Note
The exact fitted attributes depend on the selected local estimator.
KFC itself does not normalize them into one common interface.
Reconstruct the F-Step prediction matrix
The C-Step does not see the original feature matrix.
It sees the prediction matrix created by F-Step.
To inspect it:
clusters = model.kstep_.predict(
X_test
)
P_test = model.fstep_.predict(
X_test,
clusters,
)
print(
P_test.shape
)
print(
P_test[:5]
)
If four divergences were configured:
Regression example
The matrix might look like:
Each column corresponds to one divergence-specific local-model family.
Classification example
The C-Step combines each row into one final class.
Verify the full prediction path
You can manually reconstruct model.predict().
clusters = model.kstep_.predict(
X_test
)
P = model.fstep_.predict(
X_test,
clusters,
)
manual_prediction = model.cstep_.predict(
P
)
normal_prediction = model.predict(
X_test
)
For numeric regression output:
For classification:
Expected:
Check the F-Step matrix for missing values
The F-Step initializes each divergence prediction vector with:
and fills entries for matching fitted cluster models.
For numeric regression predictions:
Normally:
If True, inspect:
and confirm that every prediction-time cluster ID has a corresponding stored local model.
Inspect the C-Step
The fitted aggregation stage is:
Its fitted strategy is:
Inspect its type:
The exact fitted attributes depend on the combiner.
Inspect mean
MeanCombiner is stateless.
There are no learned coefficients to inspect.
The combiner simply computes the row-wise mean at prediction time.
Inspect weighted_mean
WeightedMeanCombiner stores a scikit-learn linear regression instance on:
Example:
The coefficients correspond to the F-Step prediction columns.
If the F-Step columns correspond to:
then the coefficient vector follows that same column order.
Note
The current weighted_mean implementation uses ordinary linear
regression.
The coefficients are not constrained to be non-negative and are not constrained to sum to one.
Inspect stacking_regressor
The fitted clone of the regression meta-model is:
Example:
If it is linear regression:
Inspect stacking_classifier
Similarly:
For the default logistic regression meta-model:
Inspect gradientcobra
The KFC regression wrapper stores the fitted GradientCOBRA object on:
Example:
Useful fitted attributes can include:
cobra.bandwidth_
cobra.distance_matrix_
cobra.cv_folds_
cobra.optimization_outputs_
cobra.global_mean_
depending on the fitted source version and configuration.
Example:
Optimization history:
Inspect mixcobra
The KFC regression wrapper stores:
which is a fitted MixCOBRARegressor.
Example:
Depending on mode, useful attributes can include:
and the normalization constants used internally.
Because KFC passes the F-Step matrix using:
interpret these attributes in the context of the wrapper's precomputed prediction-space use.
Inspect combined_classifier
The classification COBRA wrapper also stores:
Example:
cobra = model.cstep_.strategy_.cobra
print(
cobra.bandwidth_
)
print(
cobra.classes_
)
print(
cobra.global_majority_class_
)
print(
cobra.optimization_outputs_
)
Its aggregation prediction-space representation is stored as:
and the pairwise aggregation distance matrix is:
when fitted.
Inspect majority vote
MajorityVoteCombiner is stateless.
There are no learned parameters.
You can inspect its input directly:
and compare it with:
This is often the most useful way to understand majority-vote decisions.
Inspect a single prediction end to end
For one sample:
1. Cluster assignments
Example:
2. F-Step predictions
Example regression output:
or classification:
3. Final C-Step output
This three-stage inspection is the simplest way to explain one KFC prediction.
Inspect one divergence path
Suppose you want to trace only the Euclidean path.
x = X_test[:1]
euclidean_km = model.kstep_.models_[
"euclidean"
]
cluster_id = euclidean_km.predict(
x
)[0]
print(
"cluster:",
cluster_id,
)
Find the corresponding local model:
local_models = model.fstep_.models_[
"euclidean"
]
selected = None
for metadata in local_models.values():
if metadata["cluster"] == cluster_id:
selected = metadata["model"]
break
Then:
That value should match the Euclidean column of the F-Step prediction matrix.
Map F-Step columns to divergence names
The F-Step prediction columns follow the insertion order of:
You can recover the names with:
Then:
and inspect one sample:
This is especially useful when interpreting C-Step coefficients.
Build a small inspection table
For regression:
import pandas as pd
clusters = model.kstep_.predict(
X_test
)
P = model.fstep_.predict(
X_test,
clusters,
)
columns = list(
model.fstep_.models_.keys()
)
table = pd.DataFrame(
P,
columns=columns,
)
table["final_prediction"] = model.cstep_.predict(
P
)
print(
table.head()
)
This produces a table conceptually like:
Add cluster assignments to the table
Then:
Now the table shows both routing and prediction behavior.
Classification inspection table
For classification:
clusters = model.kstep_.predict(
X_test
)
P = model.fstep_.predict(
X_test,
clusters,
)
columns = list(
model.fstep_.models_.keys()
)
table = pd.DataFrame(
P,
columns=[
f"{name}_prediction"
for name in columns
],
)
table["final_prediction"] = model.cstep_.predict(
P
)
print(
table.head()
)
What KFC does not retain at the top level
The current KFCProcedure.fit() creates local variables:
but does not store them on the top-level estimator.
So after fitting, you cannot directly access:
because those attributes are not created by the current source.
Important
If you need the exact internal split for auditing or diagnostics, reproduce
it externally using the same train_test_split arguments, or modify the
implementation to retain those arrays.
Reproduce the internal split
For regression:
from sklearn.model_selection import train_test_split
X_k, X_l, y_k, y_l = train_test_split(
X_train,
y_train,
test_size=0.5,
random_state=model.random_state,
stratify=None,
)
For classification:
X_k, X_l, y_k, y_l = train_test_split(
X_train,
y_train,
test_size=0.5,
random_state=model.random_state,
stratify=y_train,
)
With the same input ordering, seed, and scikit-learn environment, this follows
the same splitting call used by KFCProcedure.fit().
Reconstruct the C-Step training matrix
Once the internal split is reproduced:
Then:
This is the kind of matrix used to train:
during the original fit.
Note
P_l itself is not stored by KFCProcedure.
Check whether the estimator is fitted
KFC's top-level predict() calls:
You can do the same:
from sklearn.utils.validation import check_is_fitted
check_is_fitted(
model,
[
"kstep_",
"fstep_",
"cstep_",
],
)
If no exception is raised, all three top-level fitted stages exist.
Inspect constructor configuration
Because KFCProcedure inherits from scikit-learn BaseEstimator, normal
estimator parameter inspection is available:
This reports constructor configuration such as:
divergences
local_model
combiner
divergences_params
local_model_params
combiner_params
n_clusters
max_iter
tol
verbose
random_state
This is different from fitted state.
Use:
for configuration and the trailing-underscore attributes for learned state.
Inspect KFC logging configuration
The estimator stores:
The logger is created during construction from the supplied verbose value.
This is configuration state, not fitted state, but it can help when reproducing debugging behavior.
Regression inspection example
import numpy as np
import pandas as pd
from kfc_procedure import KFCRegressor
model = KFCRegressor(
divergences=["euclidean"],
local_model="ridge",
combiner="gradientcobra",
random_state=42,
)
model.fit(
X_train,
y_train,
)
# K-Step
for name, km in model.kstep_.models_.items():
print(
name,
km.cluster_centers_.shape,
km.inertia_,
km.n_iter_,
)
# F-Step
for name, local_models in model.fstep_.models_.items():
print(
name,
len(local_models),
)
# Intermediate predictions
clusters = model.kstep_.predict(
X_test
)
P = model.fstep_.predict(
X_test,
clusters,
)
# C-Step
strategy = model.cstep_.strategy_
print(
type(strategy).__name__
)
# Final prediction
y_pred = model.cstep_.predict(
P
)
# Inspection table
table = pd.DataFrame(
P,
columns=list(
model.fstep_.models_.keys()
),
)
table["prediction"] = y_pred
print(
table.head()
)
Classification inspection example
import pandas as pd
from kfc_procedure import KFCClassifier
model = KFCClassifier(
divergences=["euclidean"],
local_model="logistic_regression",
combiner="combined_classifier",
random_state=42,
)
model.fit(
X_train,
y_train,
)
clusters = model.kstep_.predict(
X_test
)
P = model.fstep_.predict(
X_test,
clusters,
)
final = model.cstep_.predict(
P
)
table = pd.DataFrame(
P,
columns=[
f"{name}_prediction"
for name
in model.fstep_.models_.keys()
],
)
table["final_prediction"] = final
print(
table.head()
)
Probability inspection limitation
The top-level classifier source defines:
through inheritance from KFCProcedure.
That implementation attempts to call:
but the current FStep implementation does not define predict_proba().
Therefore end-to-end probability inspection is not currently available through:
in this source version.
The combined_classifier C-Step wrapper itself does expose a probability
method on its wrapped classifier, but the top-level KFC probability pipeline
fails earlier at F-Step.
Useful inspection checklist
When a fitted KFC model behaves unexpectedly, inspect the pipeline in this order:
1. K-Step models_
↓
2. cluster counts
↓
3. cluster centers and inertia_
↓
4. F-Step models_
↓
5. local model types and parameters
↓
6. prediction matrix P
↓
7. C-Step strategy_
↓
8. combiner-specific fitted attributes
↓
9. final prediction
This follows the same direction as the actual prediction pipeline.
Quick reference
| Object | Useful fitted state |
|---|---|
model.kstep_ |
models_, clusters_ |
BregmanKMeans |
labels_, cluster_centers_, inertia_, n_iter_ |
model.fstep_ |
models_ |
| local-model metadata | divergence, cluster, model |
model.cstep_ |
strategy_ |
| weighted mean | strategy_.model.coef_ |
| stacking | strategy_.meta_model_ |
| GradientCOBRA wrapper | strategy_.cobra |
| MixCOBRA wrapper | strategy_.cobra |
| CombinedClassifier wrapper | strategy_.cobra |
Mental model
Inspect KFC in the same order that data flows through it: clusters, local predictions, then consensus.