Lesson 4 · Intro to Machine Learning

Your First Model

Hands-on — train, evaluate, and iterate on a real machine learning model using scikit-learn.

AI Blog TeamAugust 15, 20264 min read

Let's Build Something Real

Theory is necessary. Practice is what makes it stick. In this lesson, we'll train a real classifier to predict whether a tumour is malignant or benign — a classic ML benchmark from the UCI Machine Learning Repository, built into scikit-learn.

The Dataset

We'll use the Breast Cancer Wisconsin dataset: 569 samples, 30 features, 2 classes.

from sklearn.datasets import load_breast_cancer
import pandas as pd

data = load_breast_cancer()

X = pd.DataFrame(data.data, columns=data.feature_names)
y = pd.Series(data.target, name="diagnosis")  # 0=malignant, 1=benign

print(f"Samples: {len(X)}")
print(f"Features: {X.shape[1]}")
print(f"Class balance: {y.value_counts().to_dict()}")
# Samples: 569
# Features: 30
# Class balance: {1: 357, 0: 212}

Class imbalance (357 benign vs 212 malignant) is worth noting. Accuracy alone can be misleading — a model that always predicts "benign" would be 62.7% accurate but completely useless. We'll use precision, recall, and F1 score.

Step 1: Split the Data

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
    X, y,
    test_size=0.2,      # 20% for testing
    random_state=42,    # reproducibility
    stratify=y,         # maintain class balance in both splits
)

print(f"Training samples: {len(X_train)}")
print(f"Test samples:     {len(X_test)}")

Step 2: Preprocess — Scale the Features

Many ML algorithms (especially those using distances or gradients) are sensitive to feature scales. The 30 features in our dataset have very different ranges — mean radius is typically 6–28, while mean area is 143–2501.

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)  # Fit only on training data!
X_test_scaled = scaler.transform(X_test)         # Apply same scaling to test

Always fit the scaler ontraining data only, then transform both sets. Fitting on test data (or all data) leaks information about the test set into training — a common and subtle mistake.

Step 3: Train a Model

Let's start simple with Logistic Regression, then compare to a more powerful approach.

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix

# Train
lr_model = LogisticRegression(max_iter=1000, random_state=42)
lr_model.fit(X_train_scaled, y_train)

# Evaluate
lr_preds = lr_model.predict(X_test_scaled)
print(classification_report(y_test, lr_preds, target_names=["Malignant", "Benign"]))

Typical output:

              precision    recall  f1-score   support
   Malignant       0.97      0.93      0.95        43
      Benign       0.96      0.99      0.97        71
    accuracy                           0.96       114

96% accuracy with just logistic regression! But we can do better.

Step 4: Try a More Powerful Model

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
import numpy as np

rf_model = RandomForestClassifier(n_estimators=100, random_state=42)
rf_model.fit(X_train_scaled, y_train)

# 5-fold cross-validation for a more reliable estimate
cv_scores = cross_val_score(rf_model, X_train_scaled, y_train, cv=5, scoring="f1")
print(f"CV F1: {cv_scores.mean():.3f} ± {cv_scores.std():.3f}")
# CV F1: 0.973 ± 0.012

rf_preds = rf_model.predict(X_test_scaled)
print(classification_report(y_test, rf_preds, target_names=["Malignant", "Benign"]))

Step 5: Understand What the Model Learned

One of the advantages of Random Forests is interpretability — we can see which features mattered most.

import matplotlib.pyplot as plt

importances = pd.Series(
    rf_model.feature_importances_,
    index=data.feature_names
).sort_values(ascending=False)

print("Top 5 most important features:")
print(importances.head())
# worst concave points       0.147
# worst perimeter            0.132
# worst radius               0.115
# mean concave points        0.103
# worst area                 0.089

Step 6: Model Comparison

ModelAccuracyF1 (Malignant)F1 (Benign)
Logistic Regression96.5%0.950.97
Random Forest97.4%0.960.98
SVM (RBF kernel)97.4%0.970.98

For this problem, all three models perform similarly. In practice, try multiple models and pick based on the metric that matters most for your use case.

For medical diagnosis,recall on the malignant class (catching all actual cancers) is more important than overall accuracy. Missing a malignant case is far worse than a false alarm. Always choose metrics that reflect your real-world costs.

Step 7: Save and Load Your Model

import joblib

# Save
joblib.dump(rf_model, "breast_cancer_classifier.pkl")
joblib.dump(scaler, "scaler.pkl")

# Load and use
loaded_model = joblib.load("breast_cancer_classifier.pkl")
loaded_scaler = joblib.load("scaler.pkl")

new_patient = X_test.iloc[[0]]
scaled = loaded_scaler.transform(new_patient)
diagnosis = loaded_model.predict(scaled)
print("Benign" if diagnosis[0] == 1 else "Malignant")

🎉 Course Complete!

You've now covered the complete ML workflow:

  1. Understand the problem — what are we predicting? What type of task?
  2. Prepare the data — split, scale, handle imbalance
  3. Train a model — start simple, then go complex
  4. Evaluate correctly — use the right metrics for the problem
  5. Interpret the model — understand what it learned
  6. Deploy — save and load for production use

The next step is to apply this workflow to your own data. Real ML work is mostly data wrangling and iteration — the modelling part often takes less than 20% of the time. Welcome to the field.

16px