Thomas Piacentini
← Workshop

Cheat sheet

Classical Machine Learning

Original text · English

Fit, validate, tune and fix regression, classification and clustering models with scikit-learn.

Fit a model

TaskCode
L2 regularized regressionRidge(alpha=1.0)
Classifier for rare positivesLogisticRegression(class_weight="balanced")
Random forestRandomForestClassifier(n_estimators=100, random_state=42)
Kernel SVMSVC(kernel="rbf", C=1.0, gamma="scale")
Linear SVM for large dataLinearSVC(C=1.0, max_iter=10000)
k nearest neighboursKNeighborsClassifier(n_neighbors=5, metric="euclidean")
K-Means clustersKMeans(n_clusters=3, random_state=42).fit(data)
Density clusters and noiseDBSCAN(eps=1.5, min_samples=5).fit(data)
Sources

Metrics

MetricFormula and use
Accuracy(TP + TN) / (TP + TN + FP + FN). Misleading on imbalanced classes
PrecisionTP / (TP + FP). Use when false positives are costly
RecallTP / (TP + FN). Use when false negatives are costly
F12 \cdot P \cdot R / (P + R). Harmonic mean of both
AUC-ROC0.5 is random, 1.0 is perfect. Threshold independent
R-squared1 - SS_{\text{res}} / SS_{\text{tot}}. Below 0 is worse than the mean
Sources

Key formulas

MSE
\text{MSE} = \frac{1}{n} \sum (y_{\text{predicted}} - y_{\text{actual}})^2
Normal equation
w = (X^T X)^{-1} X^T y. Cost is O(n^3) in features.
Ridge
\text{Cost} = \text{MSE} + \lambda \sum w_i^2
Log loss
-\frac{1}{n} \sum \left( y \log(p) + (1-y) \log(1-p) \right), p = \sigma(wx + b)
Gini
\text{Gini}(S) = 1 - \sum_k p_k^2. 0 is a pure node.
Soft margin SVM
\min \frac{1}{2} \lVert w \rVert^2 + C \sum_i \max(0, 1 - y_i (w^T x_i + b))
RBF kernel
K(x, z) = \exp(-\gamma \lVert x - z \rVert^2)
Error split
\text{Bias}^2 + \text{Variance} + \text{Irreducible Noise}
Sources

Leak-free preprocessing

Leaky: the scaler sees the test rows

X_scaled = scaler.fit_transform(X)

Correct: fit on train, reuse on test

X_train_scaled = scaler.fit_transform(X_train)learn mean and std
X_test_scaled = scaler.transform(X_test)train statistics only

Best: one Pipeline object for train and serve

pipe = Pipeline([
("scaler", StandardScaler()),
("model", LogisticRegression()),
])
pipe.fit(X_train, y_train)fit_transform, then fit
predictions = pipe.predict(X_test)transform, then predict
scores = cross_val_score(full_pipe, df, y, cv=5, scoring="accuracy")refit per fold
Sources

Validate without leakage

  1. Split hold out a test set. Touch it exactly once, at the end.
  2. Folds split the rest into K folds. Stratify for class ratios.
  3. Fit the scaler calls fit_transform on the K-1 training folds.
  4. Train the model calls fit on the scaled training folds.
  5. Score the scaler calls transform on the validation fold, then the model predicts.
  6. Average repeat for each fold and average the K scores.
  7. Report score the final model on the test set.
Sources

Questions

Why not MSE for logistic regression?
With a sigmoid it is non-convex. Log loss is convex.
Why scale before an SVM?
The margin depends on \lVert w \rVert.
Why does L1 select features?
It pushes some weights exactly to zero.
Will more data fix high bias?
No. Change the model.
Why class weights over oversampling?
Same in expectation, no duplicate rows.
Sources

Tips

  • Sweep the learning rate first. Fix the rest at defaults.
  • Search learning rate and regularization on a log scale.
  • Set n_estimators high and let early stopping decide.
  • Use nested cross-validation for the final estimate.
  • Add a was_this_missing column before you impute.
  • Pick the threshold on the validation set, not the test set.
Sources

ColumnTransformer pipeline

selectselectDataFramenumeric and categoricalNumeric columnsage, income, scoreCategorical columnscity, planSimpleImputermedian, thenStandardScalerSimpleImputermost_frequent, thenOneHotEncoderColumnTransformerjoins both outputsModelGradientBoostingClassifier, predict
Nodes and connections
NodeRoleKind
DataFramenumeric and categorical
Numeric columnsage, income, score
Categorical columnscity, plan
SimpleImputermedian, then StandardScaler
SimpleImputermost_frequent, then OneHotEncoder
ColumnTransformerjoins both outputs
ModelGradientBoostingClassifier, predict
FromToConnection
DataFrameNumeric columnsselect
DataFrameCategorical columnsselect
Numeric columnsSimpleImputer
Categorical columnsSimpleImputer
SimpleImputerColumnTransformer
SimpleImputerColumnTransformer
ColumnTransformerModel
Sources

Tune hyperparameters

param_dist = {
"learning_rate": loguniform(1e-4, 0.5),log scale for rates
"max_depth": randint(2, 10),integer range
"n_estimators": randint(50, 500),
}
search = RandomizedSearchCV(
GradientBoostingRegressor(), param_dist,
n_iter=100, cv=5,100 samples, 5 folds each
scoring="neg_mean_squared_error",negated, higher is better
random_state=42, n_jobs=-1,all CPU cores
)
search.fit(X_train, y_train)
print(search.best_params_, -search.best_score_)negate back to MSE
Sources

Troubleshooting

SymptomCauseFix
High train and test errorhigh biasmore features, complex model, less regularization
Low train error, high test errorhigh variancemore data, regularization, simpler model
Test error rises, train error fallsoverfitting in progressearly stopping
CV score higher than productionscaler fit on full dataPipeline with cross_val_score
Crash on a new categoryencoder never saw the valueOneHotEncoder(handle_unknown="ignore")
99% accuracy, no positives foundclass imbalanceclass weights, threshold tuning
SVM or KNN scores lowunscaled featuresStandardScaler() first
Best value at the search edgerange too narrowwiden the search space
Sources

Starting values

SettingDefaultNote
Decision threshold0.5lower it for rare positives
CV folds K510 is also standard
Features per split, forestsqrt(n_features)n_features / 3 for regression
Trees in a forest100n_estimators in the lesson code
Boosting learning_rate0.1typical 0.01 to 0.3
Boosting max_depth3lesson GradientBoosting class
Naive Bayes alpha1.0Laplace smoothing
Out of bag share36.8%free validation set
Sources

Abbreviations

AUC-ROC
Area Under the Receiver Operating Characteristic curve.
SMOTE
synthetic minority samples by k-NN interpolation.
GMM
Gaussian Mixture Model. Soft clusters by Expectation-Maximization.
RFE
Recursive Feature Elimination. Train, rank, prune, repeat.
Sources
Original source
---
id: classical-ml
title: Classical Machine Learning
code: CM
subtitle: Fit, validate, tune and fix regression, classification and clustering models with scikit-learn.
hue: 120
sources: phases/02-ml-fundamentals/02-linear-regression, phases/02-ml-fundamentals/03-logistic-regression, phases/02-ml-fundamentals/04-decision-trees, phases/02-ml-fundamentals/05-support-vector-machines, phases/02-ml-fundamentals/06-knn-and-distances, phases/02-ml-fundamentals/07-unsupervised-learning, phases/02-ml-fundamentals/08-feature-engineering, phases/02-ml-fundamentals/09-model-evaluation, phases/02-ml-fundamentals/10-bias-variance, phases/02-ml-fundamentals/11-ensemble-methods, phases/02-ml-fundamentals/12-hyperparameter-tuning, phases/02-ml-fundamentals/13-ml-pipelines, phases/02-ml-fundamentals/14-naive-bayes, phases/02-ml-fundamentals/17-imbalanced-data, phases/02-ml-fundamentals/18-feature-selection
source_hash: e9148bde41cb124d
---

## Fit a model
type: task-table
col: 1
from: phases/02-ml-fundamentals/02-linear-regression/docs/en.md, phases/02-ml-fundamentals/17-imbalanced-data/docs/en.md, phases/02-ml-fundamentals/04-decision-trees/docs/en.md, phases/02-ml-fundamentals/05-support-vector-machines/docs/en.md, phases/02-ml-fundamentals/06-knn-and-distances/docs/en.md, phases/02-ml-fundamentals/07-unsupervised-learning/docs/en.md

| Task | Code |
|---|---|
| L2 regularized regression | `Ridge(alpha=1.0)` |
| Classifier for rare positives | `LogisticRegression(class_weight="balanced")` |
| Random forest | `RandomForestClassifier(n_estimators=100, random_state=42)` |
| Kernel SVM | `SVC(kernel="rbf", C=1.0, gamma="scale")` |
| Linear SVM for large data | `LinearSVC(C=1.0, max_iter=10000)` |
| k nearest neighbours | `KNeighborsClassifier(n_neighbors=5, metric="euclidean")` |
| K-Means clusters | `KMeans(n_clusters=3, random_state=42).fit(data)` |
| Density clusters and noise | `DBSCAN(eps=1.5, min_samples=5).fit(data)` |

## Metrics
type: command-table
col: 2
from: phases/02-ml-fundamentals/09-model-evaluation/docs/en.md, phases/02-ml-fundamentals/17-imbalanced-data/docs/en.md

| Metric | Formula and use |
|---|---|
| Accuracy | $(TP + TN) / (TP + TN + FP + FN)$. Misleading on imbalanced classes |
| Precision | $TP / (TP + FP)$. Use when false positives are costly |
| Recall | $TP / (TP + FN)$. Use when false negatives are costly |
| F1 | $2 \cdot P \cdot R / (P + R)$. Harmonic mean of both |
| AUC-ROC | 0.5 is random, 1.0 is perfect. Threshold independent |
| R-squared | $1 - SS_{\text{res}} / SS_{\text{tot}}$. Below 0 is worse than the mean |

## Key formulas
type: concepts
col: 3
from: phases/02-ml-fundamentals/02-linear-regression/docs/en.md, phases/02-ml-fundamentals/03-logistic-regression/docs/en.md, phases/02-ml-fundamentals/04-decision-trees/docs/en.md, phases/02-ml-fundamentals/05-support-vector-machines/docs/en.md, phases/02-ml-fundamentals/10-bias-variance/docs/en.md

- **MSE**: $\text{MSE} = \frac{1}{n} \sum (y_{\text{predicted}} - y_{\text{actual}})^2$
- **Normal equation**: $w = (X^T X)^{-1} X^T y$. Cost is $O(n^3)$ in features.
- **Ridge**: $\text{Cost} = \text{MSE} + \lambda \sum w_i^2$
- **Log loss**: $-\frac{1}{n} \sum \left( y \log(p) + (1-y) \log(1-p) \right)$, $p = \sigma(wx + b)$
- **Gini**: $\text{Gini}(S) = 1 - \sum_k p_k^2$. 0 is a pure node.
- **Soft margin SVM**: $\min \frac{1}{2} \lVert w \rVert^2 + C \sum_i \max(0, 1 - y_i (w^T x_i + b))$
- **RBF kernel**: $K(x, z) = \exp(-\gamma \lVert x - z \rVert^2)$
- **Error split**: $\text{Bias}^2 + \text{Variance} + \text{Irreducible Noise}$

## Leak-free preprocessing
type: annotated-code
col: 2
lang: python
from: phases/02-ml-fundamentals/13-ml-pipelines/docs/en.md, phases/02-ml-fundamentals/13-ml-pipelines/code/pipeline.py

```python
# Leaky: the scaler sees the test rows
X_scaled = scaler.fit_transform(X)
# Correct: fit on train, reuse on test
X_train_scaled = scaler.fit_transform(X_train)   # learn mean and std
X_test_scaled = scaler.transform(X_test)         # train statistics only
# Best: one Pipeline object for train and serve
pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("model", LogisticRegression()),
])
pipe.fit(X_train, y_train)                       # fit_transform, then fit
predictions = pipe.predict(X_test)               # transform, then predict
scores = cross_val_score(full_pipe, df, y, cv=5, scoring="accuracy")  # refit per fold
```

## Validate without leakage
type: flow
col: 4
from: phases/02-ml-fundamentals/09-model-evaluation/docs/en.md, phases/02-ml-fundamentals/13-ml-pipelines/docs/en.md

1. **Split**: hold out a test set. Touch it exactly once, at the end.
2. **Folds**: split the rest into $K$ folds. Stratify for class ratios.
3. **Fit**: the scaler calls `fit_transform` on the $K-1$ training folds.
4. **Train**: the model calls `fit` on the scaled training folds.
5. **Score**: the scaler calls `transform` on the validation fold, then the model predicts.
6. **Average**: repeat for each fold and average the $K$ scores.
7. **Report**: score the final model on the test set.

## Questions
type: qa
col: 4
from: phases/02-ml-fundamentals/03-logistic-regression/docs/en.md, phases/02-ml-fundamentals/05-support-vector-machines/docs/en.md, phases/02-ml-fundamentals/10-bias-variance/docs/en.md, phases/02-ml-fundamentals/17-imbalanced-data/docs/en.md, phases/02-ml-fundamentals/09-model-evaluation/docs/en.md

- **Why not MSE for logistic regression?** With a sigmoid it is non-convex. Log loss is convex.
- **Why scale before an SVM?** The margin depends on $\lVert w \rVert$.
- **Why does L1 select features?** It pushes some weights exactly to zero.
- **Will more data fix high bias?** No. Change the model.
- **Why class weights over oversampling?** Same in expectation, no duplicate rows.

## Tips
type: tips
col: 1
from: phases/02-ml-fundamentals/12-hyperparameter-tuning/docs/en.md, phases/02-ml-fundamentals/09-model-evaluation/docs/en.md, phases/02-ml-fundamentals/08-feature-engineering/docs/en.md, phases/02-ml-fundamentals/17-imbalanced-data/docs/en.md

- Sweep the learning rate first. Fix the rest at defaults.
- Search learning rate and regularization on a log scale.
- Set n_estimators high and let early stopping decide.
- Use nested cross-validation for the final estimate.
- Add a was_this_missing column before you impute.
- Pick the threshold on the validation set, not the test set.

## ColumnTransformer pipeline
type: architecture
col: 1
from: phases/02-ml-fundamentals/13-ml-pipelines/docs/en.md, phases/02-ml-fundamentals/13-ml-pipelines/code/pipeline.py

```arch
node | raw | 1,1 | DataFrame | numeric and categorical
node | num | 1,2 | Numeric columns | age, income, score
node | cat | 2,2 | Categorical columns | city, plan
node | nimp | 1,3 | SimpleImputer | median, then StandardScaler
node | cimp | 2,3 | SimpleImputer | most_frequent, then OneHotEncoder
node | ct | 1,4 | ColumnTransformer | joins both outputs
node | model | 2,4 | Model | GradientBoostingClassifier, predict
edge | raw | num | select
edge | raw | cat | select
edge | num | nimp |
edge | cat | cimp |
edge | nimp | ct |
edge | cimp | ct |
edge | ct | model |
```

## Tune hyperparameters
type: annotated-code
col: 1
lang: python
from: phases/02-ml-fundamentals/12-hyperparameter-tuning/docs/en.md

```python
param_dist = {
    "learning_rate": loguniform(1e-4, 0.5),      # log scale for rates
    "max_depth": randint(2, 10),                 # integer range
    "n_estimators": randint(50, 500),
}
search = RandomizedSearchCV(
    GradientBoostingRegressor(), param_dist,
    n_iter=100, cv=5,                            # 100 samples, 5 folds each
    scoring="neg_mean_squared_error",            # negated, higher is better
    random_state=42, n_jobs=-1,                  # all CPU cores
)
search.fit(X_train, y_train)
print(search.best_params_, -search.best_score_)  # negate back to MSE
```

## Troubleshooting
type: troubleshooting
col: 3
from: phases/02-ml-fundamentals/10-bias-variance/docs/en.md, phases/02-ml-fundamentals/13-ml-pipelines/docs/en.md, phases/02-ml-fundamentals/17-imbalanced-data/docs/en.md, phases/02-ml-fundamentals/05-support-vector-machines/docs/en.md, phases/02-ml-fundamentals/12-hyperparameter-tuning/docs/en.md, phases/02-ml-fundamentals/06-knn-and-distances/docs/en.md

| Symptom | Cause | Fix |
|---|---|---|
| High train and test error | high bias | more features, complex model, less regularization |
| Low train error, high test error | high variance | more data, regularization, simpler model |
| Test error rises, train error falls | overfitting in progress | early stopping |
| CV score higher than production | scaler fit on full data | Pipeline with cross_val_score |
| Crash on a new category | encoder never saw the value | `OneHotEncoder(handle_unknown="ignore")` |
| 99% accuracy, no positives found | class imbalance | class weights, threshold tuning |
| SVM or KNN scores low | unscaled features | `StandardScaler()` first |
| Best value at the search edge | range too narrow | widen the search space |

## Starting values
type: defaults
col: 3
from: phases/02-ml-fundamentals/17-imbalanced-data/docs/en.md, phases/02-ml-fundamentals/09-model-evaluation/docs/en.md, phases/02-ml-fundamentals/11-ensemble-methods/docs/en.md, phases/02-ml-fundamentals/04-decision-trees/docs/en.md, phases/02-ml-fundamentals/14-naive-bayes/docs/en.md

| Setting | Default | Note |
|---|---|---|
| Decision threshold | 0.5 | lower it for rare positives |
| CV folds K | 5 | 10 is also standard |
| Features per split, forest | sqrt(n_features) | n_features / 3 for regression |
| Trees in a forest | 100 | n_estimators in the lesson code |
| Boosting learning_rate | 0.1 | typical 0.01 to 0.3 |
| Boosting max_depth | 3 | lesson GradientBoosting class |
| Naive Bayes alpha | 1.0 | Laplace smoothing |
| Out of bag share | 36.8% | free validation set |

## Abbreviations
type: glossary
col: 3
span: 2
from: phases/02-ml-fundamentals/09-model-evaluation/docs/en.md, phases/02-ml-fundamentals/17-imbalanced-data/docs/en.md, phases/02-ml-fundamentals/07-unsupervised-learning/docs/en.md, phases/02-ml-fundamentals/18-feature-selection/docs/en.md

- **AUC-ROC**: Area Under the Receiver Operating Characteristic curve.
- **SMOTE**: synthetic minority samples by k-NN interpolation.
- **GMM**: Gaussian Mixture Model. Soft clusters by Expectation-Maximization.
- **RFE**: Recursive Feature Elimination. Train, rank, prune, repeat.
Code and artifacts 1
What you take away

Saved in your notebook on this device.

Sources and limits

Reading checkpoints are self-reported. Test results are recorded separately and apply to the executed cases.

AI Engineering from Scratch · 3f9715c · MIT

Download this file ↓