Claude
Skills
Sign in
Back

debug:scikit-learn

Included with Lifetime
$97 forever

Debug Scikit-learn issues systematically. Use when encountering model errors like NotFittedError, shape mismatches between train and test data, NaN/infinity value errors, pipeline configuration issues, convergence warnings from optimizers, cross-validation failures due to class imbalance, data leakage causing suspiciously high scores, or preprocessing errors with ColumnTransformer and feature alignment.

Code Review

What this skill does


# Scikit-learn Debugging Guide

This guide provides a systematic approach to debugging Scikit-learn machine learning code. Follow these phases to identify and resolve issues efficiently.

## Common Error Patterns

### 1. ValueError: Shapes Not Aligned
```python
# Error: ValueError: shapes (100,5) and (4,) not aligned
# Cause: Feature count mismatch between train and test data

# Debug steps:
print(f"X_train shape: {X_train.shape}")
print(f"X_test shape: {X_test.shape}")
print(f"Feature names train: {X_train.columns.tolist() if hasattr(X_train, 'columns') else 'N/A'}")
print(f"Feature names test: {X_test.columns.tolist() if hasattr(X_test, 'columns') else 'N/A'}")

# Common fixes:
# 1. Ensure same preprocessing on train and test
# 2. Use Pipeline to encapsulate all transformations
# 3. Check for columns dropped during one-hot encoding
```

### 2. NotFittedError
```python
# Error: NotFittedError: This StandardScaler instance is not fitted yet
# Cause: Calling transform() or predict() before fit()

from sklearn.utils.validation import check_is_fitted

# Check if model is fitted:
try:
    check_is_fitted(model)
    print("Model is fitted")
except Exception as e:
    print(f"Model not fitted: {e}")

# Debug fitted attributes:
print(f"Model attributes: {[a for a in dir(model) if a.endswith('_') and not a.startswith('_')]}")

# Common fixes:
# 1. Call fit() before transform() or predict()
# 2. Use fit_transform() for training data
# 3. Ensure Pipeline is fitted before prediction
```

### 3. NaN Values in Input
```python
# Error: ValueError: Input contains NaN, infinity or a value too large
# Cause: Missing or infinite values in data

import numpy as np
import pandas as pd

# Diagnose NaN issues:
def diagnose_nan_issues(X, name="X"):
    if isinstance(X, pd.DataFrame):
        nan_counts = X.isna().sum()
        print(f"{name} NaN counts per column:\n{nan_counts[nan_counts > 0]}")
        print(f"{name} total NaN: {X.isna().sum().sum()}")
    else:
        print(f"{name} contains NaN: {np.isnan(X).any()}")
        print(f"{name} contains inf: {np.isinf(X).any()}")
        print(f"{name} NaN count: {np.isnan(X).sum()}")

diagnose_nan_issues(X_train, "X_train")
diagnose_nan_issues(X_test, "X_test")

# Common fixes:
from sklearn.impute import SimpleImputer

# Option 1: Remove rows with NaN
X_clean = X[~np.isnan(X).any(axis=1)]

# Option 2: Impute missing values
imputer = SimpleImputer(strategy='median')
X_imputed = imputer.fit_transform(X_train)

# Option 3: Replace infinity
X_train = np.clip(X_train, -1e10, 1e10)
```

### 4. Feature Mismatch Train/Test
```python
# Error: ValueError: X has 10 features, but model expects 12 features
# Cause: Different preprocessing on train vs test

# Debug feature alignment:
def debug_feature_mismatch(X_train, X_test, model=None):
    print(f"Train features: {X_train.shape[1]}")
    print(f"Test features: {X_test.shape[1]}")

    if model and hasattr(model, 'n_features_in_'):
        print(f"Model expects: {model.n_features_in_} features")

    if hasattr(X_train, 'columns') and hasattr(X_test, 'columns'):
        train_cols = set(X_train.columns)
        test_cols = set(X_test.columns)
        print(f"In train but not test: {train_cols - test_cols}")
        print(f"In test but not train: {test_cols - train_cols}")

# Fix: Use ColumnTransformer with remainder='passthrough' or 'drop'
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler

preprocessor = ColumnTransformer(
    transformers=[
        ('num', StandardScaler(), numerical_cols),
        ('cat', OneHotEncoder(handle_unknown='ignore'), categorical_cols)
    ],
    remainder='drop'  # Explicitly handle unknown columns
)
```

### 5. Cross-Validation Issues
```python
# Error: ValueError: Cannot have number of splits n_splits=5 greater than samples
# Cause: Too few samples for specified fold count

from sklearn.model_selection import cross_val_score, StratifiedKFold

# Debug cross-validation setup:
def debug_cv_setup(X, y, cv=5):
    print(f"Total samples: {len(X)}")
    print(f"CV folds requested: {cv}")
    print(f"Min samples per fold: {len(X) // cv}")

    if hasattr(y, 'value_counts'):
        print(f"Class distribution:\n{y.value_counts()}")
    else:
        unique, counts = np.unique(y, return_counts=True)
        print(f"Class distribution: {dict(zip(unique, counts))}")

# Fix: Use appropriate CV strategy
# For small datasets:
from sklearn.model_selection import LeaveOneOut, RepeatedStratifiedKFold

# For imbalanced data:
cv = StratifiedKFold(n_splits=min(5, y.value_counts().min()))

# For time series:
from sklearn.model_selection import TimeSeriesSplit
cv = TimeSeriesSplit(n_splits=5)
```

### 6. Pipeline Configuration Errors
```python
# Error: TypeError: All estimators should implement fit and transform
# Cause: Final estimator in Pipeline doesn't have transform method

from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer

# Debug pipeline structure:
def debug_pipeline(pipe):
    print("Pipeline steps:")
    for name, step in pipe.named_steps.items():
        has_fit = hasattr(step, 'fit')
        has_transform = hasattr(step, 'transform')
        has_predict = hasattr(step, 'predict')
        print(f"  {name}: fit={has_fit}, transform={has_transform}, predict={has_predict}")
        print(f"    Type: {type(step).__name__}")
        if hasattr(step, 'get_params'):
            params = step.get_params()
            print(f"    Params: {params}")

debug_pipeline(my_pipeline)

# Common fixes:
# 1. Only the last step can be a predictor (no transform)
# 2. Intermediate steps must have fit_transform or fit + transform
# 3. Use 'passthrough' for no-op steps
```

### 7. Convergence Warnings
```python
# Warning: ConvergenceWarning: lbfgs failed to converge
# Cause: Optimization didn't reach convergence criteria

from sklearn.linear_model import LogisticRegression
from sklearn.exceptions import ConvergenceWarning
import warnings

# Capture and analyze warnings:
with warnings.catch_warnings(record=True) as w:
    warnings.simplefilter("always")
    model.fit(X_train, y_train)

    for warning in w:
        if issubclass(warning.category, ConvergenceWarning):
            print(f"Convergence issue: {warning.message}")

# Common fixes:
# 1. Increase max_iter
model = LogisticRegression(max_iter=1000)

# 2. Scale features
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 3. Try different solver
model = LogisticRegression(solver='saga', max_iter=1000)

# 4. Adjust tolerance
model = LogisticRegression(tol=1e-3)
```

### 8. Data Leakage Detection
```python
# Symptom: Suspiciously high cross-validation scores (>0.99)
# Cause: Information from test set leaking into training

# Debug data leakage:
def check_for_leakage(X, y, model, cv=5):
    from sklearn.model_selection import cross_val_score

    scores = cross_val_score(model, X, y, cv=cv)
    print(f"CV scores: {scores}")
    print(f"Mean: {scores.mean():.4f} (+/- {scores.std() * 2:.4f})")

    if scores.mean() > 0.99:
        print("WARNING: Suspiciously high scores - check for data leakage!")
        print("Common causes:")
        print("  - Target variable encoded in features")
        print("  - Future information in time series")
        print("  - Preprocessing before train-test split")

    return scores

# Fix: Use Pipeline to prevent leakage
from sklearn.pipeline import Pipeline

# WRONG - leakage:
# scaler = StandardScaler()
# X_scaled = scaler.fit_transform(X)  # Fitted on ALL data
# X_train, X_test = train_test_split(X_scaled, ...)

# CORRECT - no leakage:
pipe = Pipeline([
    ('scaler', StandardScaler()),
    ('model', LogisticRegression())
])
# fit_transform only sees training data in each CV fold
scores = cross_val_score(pipe, X, y, cv=5)
```

## Debugging Tools

### Model Inspection
```python
# Get all model parameters
print(model.get_params())

# Get only non-d

Related in Code Review