EduFlow

Section 1 · What Machine Learning Is

Models in scikit-learn: fit, predict, score

18 min3 min readPython exercise

Lesson at a glance

Use any scikit-learn model through the same three calls (fit, predict, score), read what a fitted model learned, and spot memorisation by comparing training and test scores.

  1. 1Three calls
  2. 2What a fitted model knows
  3. 3The training score lies
  4. 4Same contract, different assumptions

No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.

scikit-learn ships dozens of models, from straight lines to forests of trees, and you use every one of them the same way. Learn three method calls and you can try any of them on the churn data in a few lines. That consistency is the main reason scikit-learn is the default library for classic machine learning.

Three calls

A model in scikit-learn is an estimator: a Python object you create with its settings, train with fit, and use with predict and score.

Python
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

churn = pd.read_csv("churn.csv")
features = ["tenure_days", "orders", "total_spent", "avg_order_value",
            "days_since_last_order", "used_coupon", "support_tickets", "mobile_share"]
X, y = churn[features], churn["churned"]
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, random_state=42, stratify=y)

model = DecisionTreeClassifier(max_depth=3, random_state=42)  # 1. create
model.fit(X_train, y_train)                                    # 2. learn
print(model.predict(X_test)[:10])                              # 3. use
print("train accuracy:", round(model.score(X_train, y_train), 3))
print("test accuracy: ", round(model.score(X_test, y_test), 3))

What each call does:

  • The constructor takes hyperparameters: choices you make before training. max_depth=3 says the tree may ask at most three questions in a row. random_state=42 makes tie-breaking repeatable.
  • fit(X_train, y_train) looks at the examples and learns the model’s parameters. For a tree, those are the questions and thresholds (“is days_since_last_order above 236.5?”). It returns the model itself, so model = DecisionTreeClassifier().fit(X, y) also works.
  • predict(X) returns one prediction per row: here an array of 0s and 1s.
  • score(X, y) predicts and compares with the true answers. For classifiers it returns accuracy, the share of correct predictions. For regressors it returns R², which you’ll meet in section 2.
An estimator is created with hyperparameters, fit on training data to learn parameters stored in attributes ending with an underscore, and then used to predict on new data or score against known answers. Model(max_depth=3) hyperparameters fit Fitted model classes_, tree_ … predict(X) score(X, y)
The life of an estimator

What a fitted model knows

Everything a model learns during fit is stored in attributes whose names end with an underscore. They don’t exist before fit; asking for one raises an AttributeError, and calling predict on an unfitted model raises NotFittedError.

Python
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

churn = pd.read_csv("churn.csv")
features = ["tenure_days", "orders", "total_spent", "avg_order_value",
            "days_since_last_order", "used_coupon", "support_tickets", "mobile_share"]
X_train, X_test, y_train, y_test = train_test_split(
    churn[features], churn["churned"], test_size=0.25, random_state=42,
    stratify=churn["churned"])
model = DecisionTreeClassifier(max_depth=3, random_state=42).fit(X_train, y_train)

print(model.classes_)            # the labels it saw: [0 1]
print(model.n_features_in_)      # 8
print(model.feature_names_in_[:3])

new_customer = pd.DataFrame([{
    "tenure_days": 400, "orders": 2, "total_spent": 180.0, "avg_order_value": 90.0,
    "days_since_last_order": 150, "used_coupon": 0, "support_tickets": 1,
    "mobile_share": 0.5}])
print(model.predict(new_customer))        # [1]: likely to churn
print(model.predict_proba(new_customer))  # [[0.36 0.64]]

predict_proba gives the model’s estimated probability for each class, in the order of classes_. This customer gets 64% for churn. You’ll lean on probabilities heavily in section 3, because they let you choose how cautious to be.

The training score lies

Now remove the depth limit and watch what happens. An unlimited tree keeps asking questions until every training customer sits in a leaf with only their own class.

Python
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier

churn = pd.read_csv("churn.csv")
features = ["tenure_days", "orders", "total_spent", "avg_order_value",
            "days_since_last_order", "used_coupon", "support_tickets", "mobile_share"]
X_train, X_test, y_train, y_test = train_test_split(
    churn[features], churn["churned"], test_size=0.25, random_state=42,
    stratify=churn["churned"])

for model in [DecisionTreeClassifier(max_depth=3, random_state=42),
              DecisionTreeClassifier(random_state=42),
              KNeighborsClassifier(n_neighbors=5)]:
    model.fit(X_train, y_train)
    print(f"{model!r:55} train {model.score(X_train, y_train):.2f}  test {model.score(X_test, y_test):.2f}")

The unlimited tree is perfect on the 298 customers it studied and worse than the shallow tree on the 100 it didn’t. It memorised quirks of individual customers. This gap between training and test performance is overfitting, and you’ll spend much of section 2 learning to control it.

Notice also the third model. k-nearest neighbours works nothing like a tree (it predicts by looking at the five most similar training customers), yet the code is identical. Swapping models costs one line.

Same contract, different assumptions

The shared API hides very different ideas about the world. A tree assumes churn can be described by a handful of yes/no questions with sharp cut-offs. k-nearest neighbours assumes customers who look alike behave alike, where “look alike” means close together when every column is treated as a distance. Neither assumption is right; each is useful in some situations and wrong in others. That’s why you try several models on the same split rather than arguing about which one ought to win.

Regression models keep the same contract. DecisionTreeRegressor or LinearRegression take a numeric y, predict returns numbers instead of classes, and score returns R² instead of accuracy. Everything you learn about one estimator carries over to the rest.

Two details save confusion later. Calling fit a second time throws away what the model learned before and starts from scratch; it doesn’t add to the old knowledge. And the settings you passed to the constructor stay readable as plain attributes (model.max_depth), so you can always check what a fitted model was told to do.

You now have a model at 73% test accuracy. Is that good? You can’t say yet, because you haven’t asked what a model with no intelligence at all would score. That’s the next lesson.

Key takeaways

  • Every scikit-learn estimator is created with its settings, learns in fit(X, y), and is used with predict(X) and score(X, y).
  • Hyperparameters such as max_depth are set by you before training; parameters such as split thresholds are learned during fit.
  • Fitted attributes end with an underscore (classes_, feature_names_in_) and only exist after fit.
  • score returns accuracy for classifiers and R² for regressors.
  • A large gap between training and test scores means the model memorised its training rows.

Further reading

Check your understanding

4 questions

Question 1 of 4

What does this print?

Python
from sklearn.tree import DecisionTreeClassifier
model = DecisionTreeClassifier(max_depth=3)
print(model.classes_)
Question 2 of 4

A tree scores 1.00 on the training set and 0.72 on the test set. A depth-3 tree scores 0.78 and 0.73. Which is the better model for Cartwheel?

Question 3 of 4

Which value is a hyperparameter rather than a learned parameter?

Question 4 of 4

You fitted a model on a DataFrame with columns in one order, then call predict on a DataFrame with the same columns in a different order. What happens?

Practice

Python exercise

Fit two trees and compare

Fit two decision trees on the churn training data and see memorisation in numbers.

  1. Fit shallow (max_depth=3) and deep (no depth limit), both with random_state=42.
  2. Set shallow_test to the shallow tree’s test accuracy, and deep_train / deep_test to the deep tree’s training and test accuracy.
  3. Set new_pred to the shallow tree’s prediction (0 or 1) for new_customer.

Data files: churn.csv

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

churn = pd.read_csv("churn.csv")
features = ["tenure_days", "orders", "total_spent", "avg_order_value",
            "days_since_last_order", "used_coupon", "support_tickets", "mobile_share"]
X_train, X_test, y_train, y_test = train_test_split(
    churn[features], churn["churned"], test_size=0.25, random_state=42,
    stratify=churn["churned"])

new_customer = pd.DataFrame([{
    "tenure_days": 120, "orders": 6, "total_spent": 940.0, "avg_order_value": 156.7,
    "days_since_last_order": 12, "used_coupon": 1, "support_tickets": 0,
    "mobile_share": 0.8}])

# 1. A tree limited to depth 3, and a tree with no depth limit (both random_state=42)
shallow = DecisionTreeClassifier(max_depth=3, random_state=42)
deep = DecisionTreeClassifier(random_state=42)

# 2. Fit both on the training data, then fill in the scores
shallow_test = None
deep_train = None
deep_test = None

# 3. The shallow tree's prediction (0 or 1) for new_customer
new_pred = None
Loading the editor…

Ctrl/⌘ + Enter to run · Runs in a sandbox in your browser. Nothing leaves your device.

(no output)

Checks

  • Both trees are fitted with the right depth settings
  • shallow_test is the shallow tree’s test accuracy
  • deep_train and deep_test are the deep tree’s scores
  • new_pred is the shallow tree’s prediction for new_customer