Data Science & Machine Learning
Hands-onBeginner 35 min· Machine learning · 8 of 20

Classification: predicting categories

Build your first classifier with scikit-learn: split the data, set a baseline, train logistic regression and KNN, read a confusion matrix and use probabilities. You will see why scaling and a baseline matter.

What you will build: a model that looks at 30 measurements of a tumour sample and predicts whether it is malignant or benign. You will use scikit-learn's built-in breast cancer dataset, so there is nothing to download. Along the way you learn the steps every classification project follows.

Prerequisites: you can run Python in Google Colab or Kaggle (see *Free notebooks: Google Colab and Kaggle*), you know what a pandas DataFrame is, and you have read *How machine learning learns* and *Regression: predicting numbers*. Colab already has scikit-learn installed.

What is classification?

Classification is supervised learning where the answer is a category, not a number. Is this email spam or not spam? Will this customer churn, yes or no? Which of ten digits is in this image? You give the model many examples with the right answer attached (the label), and it learns a rule that works on new examples.

When there are two categories it is binary classification. With three or more it is multiclass classification. The code is almost the same for both.

Classification examples

QuestionTypeTypical label
Is this UPI transaction fraud?Binary0 = normal, 1 = fraud
Will this customer leave next month?Binary0 = stays, 1 = churns
Which language is this text written in?Multiclass"hindi", "tamil", "english"
Is the review positive, neutral or negative?Multiclass"pos", "neu", "neg"

Regression and classification use the same workflow. What changes is the target and how you judge the result: a classifier is scored on how often it is right, and on which kinds of mistakes it makes (see *Metrics: accuracy, precision, recall, F1 and ROC*).

The plan

  1. 1Load the data and check the class balance.
  2. 2Split into train and test sets, then build a baseline.
  3. 3Train logistic regression inside a pipeline with scaling.
  4. 4Read the confusion matrix and the probabilities.
  5. 5Try a second model (KNN) and compare.

Step 1: load and look at the data

scikit-learn's load_breast_cancer gives us 569 samples. Each has 30 numeric measurements (radius, texture, smoothness and so on) and a label: 0 for malignant, 1 for benign. Note the order: 0 is malignant. We will keep that in mind when reading results.

Load the data
import pandas as pd
from sklearn.datasets import load_breast_cancer

data = load_breast_cancer(as_frame=True)
X = data.data      # 30 numeric measurements per tumour
y = data.target    # 0 = malignant, 1 = benign

print(X.shape)
print(data.target_names)
print(y.value_counts())
print(X.iloc[:3, :4])

Prints the shape, the class names, how many samples are in each class and the first rows.

Output
(569, 30)
['malignant' 'benign']
target
1    357
0    212
Name: count, dtype: int64
   mean radius  mean texture  mean perimeter  mean area
0        17.99         10.38           122.8     1001.0
1        20.57         17.77           132.9     1326.0
2        19.69         21.25           130.0     1203.0

Always check the class balance first. Here 357 of 569 samples (about 63%) are benign. The classes are not equal, which matters for the next step.

Step 2: split, and set a baseline

We hold back 20% of the data as a test set that the model never sees during training. stratify=y keeps the share of each class the same in both parts, which is important when one class is rarer. random_state=42 makes the split repeatable. The full reasoning is in *Train, validation, test and overfitting*.

Then we build a baseline: a dummy model that always predicts the most common class. Any real model must beat it, otherwise it has learned nothing.

Split and baseline
from sklearn.model_selection import train_test_split
from sklearn.dummy import DummyClassifier
from sklearn.metrics import accuracy_score

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

baseline = DummyClassifier(strategy="most_frequent")
baseline.fit(X_train, y_train)
print("Baseline accuracy:", round(accuracy_score(y_test, baseline.predict(X_test)), 3))

The dummy model gets roughly 63% by always saying "benign".

Output
Baseline accuracy: 0.632

Step 3: train logistic regression

Logistic regression is the standard first classifier. Despite the name it is used for classification. It computes a weighted sum of the features, like linear regression, and then squeezes the result into a probability between 0 and 1 using an S-shaped curve. If the probability is above 0.5 it predicts class 1, otherwise class 0.

Logistic regression works best when features are on a similar scale, so we put a StandardScaler (subtract the mean, divide by the standard deviation) in a pipeline with the model. A pipeline runs the steps in order and, importantly, learns the scaling from the training data only.

Train and evaluate
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import confusion_matrix, classification_report

logreg = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000))
logreg.fit(X_train, y_train)

pred = logreg.predict(X_test)
print("Accuracy:", round(accuracy_score(y_test, pred), 3))
print(confusion_matrix(y_test, pred))
print(classification_report(y_test, pred, target_names=data.target_names))

fit() learns from the training data; predict() gives labels for the test data.

Output
Accuracy: 0.982
[[41  1]
 [ 1 71]]
              precision    recall  f1-score   support

   malignant       0.98      0.98      0.98        42
      benign       0.99      0.99      0.99        72

    accuracy                           0.98       114
   macro avg       0.98      0.98      0.98       114
weighted avg       0.98      0.98      0.98       114

Accuracy is 98.2%, far above the 63.2% baseline. The confusion matrix shows where the errors are. Rows are the true classes and columns are the predicted ones, in the order 0 then 1:

- 41 malignant tumours were correctly called malignant. - 1 malignant tumour was wrongly called benign (the dangerous mistake). - 1 benign tumour was wrongly called malignant. - 71 benign tumours were correctly called benign.

Exact numbers can differ slightly with other library versions.

Step 4: probabilities, not just labels

Most classifiers can say how confident they are. predict_proba returns one column per class, in the order of model.classes_ (here 0 then 1). predict simply picks the class with the higher probability.

Look at the probabilities
print(logreg.predict_proba(X_test.iloc[:3]).round(3))
print("Predicted:", logreg.predict(X_test.iloc[:3]))
print("Actual:   ", y_test.iloc[:3].values)
Output
[[1.    0.   ]
 [0.    1.   ]
 [0.994 0.006]]
Predicted: [0 1 0]
Actual:    [0 1 0]

Probabilities are useful because you can choose your own cut-off instead of 0.5. If missing a malignant tumour is much worse than a false alarm, you would flag anything above, say, 0.2 probability of malignant. This is explained in the metrics lesson.

Step 5: try a different model

K-nearest neighbours (KNN) predicts by looking at the k most similar training examples and taking a vote. It needs no training in the usual sense: it just remembers the data. Because it measures distances, features with big numbers (like area, in the thousands) would drown out features with small numbers (like smoothness, below 1). Scaling fixes that. Run both versions to see the effect.

KNN with and without scaling
from sklearn.neighbors import KNeighborsClassifier

knn_raw = KNeighborsClassifier(n_neighbors=5)
knn_raw.fit(X_train, y_train)
print("KNN without scaling:", round(knn_raw.score(X_test, y_test), 3))

knn_scaled = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5))
knn_scaled.fit(X_train, y_train)
print("KNN with scaling:   ", round(knn_scaled.score(X_test, y_test), 3))

Uses X_train, y_train, X_test, y_test, make_pipeline and StandardScaler from the earlier cells, so run this in the same notebook.

Output
KNN without scaling: 0.912
KNN with scaling:    0.956

When to scale

Scaling matters for logistic regression, KNN, SVMs and neural networks. It does not matter for decision trees and random forests, which only compare values within one column.

A common mistake

Do not fit the scaler on the whole dataset before splitting. The scaler would learn the mean of the test rows, which leaks information. Putting it inside a pipeline avoids this automatically.

Choosing a model

A quick guide

ModelGood atWatch out for
Logistic regressionFast, simple, explainable, a strong first modelOnly draws straight boundaries between classes
K-nearest neighboursEasy to understand; no training stepSlow on big data; needs scaling
Decision tree / random forestHandles mixed data and curved boundariesSingle trees overfit (see the trees lesson)
Gradient boostingOften the best on tables of dataMore settings to tune
Prompt · Ask AI to explain your confusion matrix
I trained a binary classifier in scikit-learn. Here is my confusion matrix (rows = true class, columns = predicted class, order 0 then 1):

[confusion matrix]

Class 1 means [what class 1 means, e.g. customer churned]. Explain in plain English what each of the four numbers means, which mistake is worse for [your use case], and what I could try to reduce it. Assume I am a beginner.

What to expect: A good answer names true/false positives and negatives for your real use case, and suggests options such as changing the threshold, class weights or a different metric.

How this comes up in interviews

Interviewers often ask: "Why is accuracy not enough?", "What does logistic regression do?" (it outputs a probability using a sigmoid; the decision threshold is a choice), "Why scale features?" and "What is a baseline?". Be ready to explain the confusion matrix in words and to say which mistake costs more in a given problem.

Try it: Change the cut-off

Using the fitted logreg pipeline, get the probability of the benign class with logreg.predict_proba(X_test)[:, 1]. Then predict malignant (class 0) whenever the probability of benign is below 0.9, instead of below 0.5. Print the confusion matrix again. What happened to the number of malignant tumours that were missed, and what did you pay for it?

Try it: Try a third model

Train DecisionTreeClassifier(max_depth=3, random_state=42) and RandomForestClassifier(random_state=42) on the same split and compare their test accuracy with logistic regression. Do these need scaling?

Key takeaways

  1. Classification predicts a category; binary has two classes, multiclass has more.
  2. The workflow is: look at the data and class balance, split with stratify, build a baseline, train, evaluate on the test set.
  3. A baseline that always predicts the majority class tells you how much a real model adds.
  4. The confusion matrix shows which kinds of mistakes happen; accuracy alone hides this.
  5. predict_proba gives confidence, so you can choose your own threshold.
  6. Scale features for logistic regression and KNN, and keep the scaler inside a pipeline to avoid leakage.

Check yourself

Finishing the quiz marks the lesson as done.

1. In a dataset 95% of emails are not spam. A model that always predicts "not spam" gets 95% accuracy. What does this show?

2. Why do we pass stratify=y to train_test_split in a classification problem?

3. What does predict_proba return for a binary classifier?

4. Which model is most affected if you forget to scale the features?

5. Where should StandardScaler be fitted?

Saved on this device. Create a free account to keep it on every device.

Practise this with Sam

A real mock interview, out loud, with an honest scorecard. Free to start.

Start free