Python for Humans · Draft v0.1 · Oct 29, 2026 · Draft position: chapter 23 · First short project draft.

Train Your First Model

You should already know
The notebook workflow from Explore Real Data; tables, functions and missing values.
You'll build
A trained classifier, a baseline comparison and a confusion matrix on held-out passengers.
You'll be able to
Separate features from labels, split data before fitting and evaluate predictions on unseen rows.
You need
Your local setup, any plain-text editor, uv and internet access for installing packages. Python 3.14 is selected for these examples.

Until now, you supplied the rule: count a word, compare a rank or filter a magnitude. A machine-learning classifier learns a rule from examples with known answers.

We'll use historical Titanic passenger records to predict the survived label from four fields. This is a small, local model trained on your computer. It doesn't need an AI account, a paid API or a GPU.

Prepare the Experiment

mkdir titanic-model
cd titanic-model
uv init --bare --vcs none --no-workspace --python 3.14
uv python pin 3.14
uv add jupyterlab pandas matplotlib scikit-learn

Save titanic.ipynb, titanic.csv and the source metadata together in the project folder. Run uv run jupyter lab and open the notebook with its Python 3 kernel.

Run cells one at a time. The notebook contains the complete experiment and explanations; add your own predictions as Markdown cells before changing it.

Inputs and Known Answers

The notebook chooses its fields explicitly:

features = ['age', 'fare', 'pclass', 'sex']
X = data[features]
y = data['survived']

A feature is an input used for a prediction. The label, or target, is the answer associated with a training example. Here, 0 means the passenger died and 1 means they survived. X and y are conventional names for those inputs and answers.

The CSV includes other columns. One, alive, directly encodes the answer. Including it would let the model read the answer instead of learning a useful predictive pattern. This is data leakage. Explicit field selection helps us avoid it.

These four fields reflect patterns in a particular historical sample. A prediction doesn't establish what caused survival or determine what would happen to an individual.

Keep Some Answers for Checking

The notebook splits the rows:

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

Three quarters become training examples; one quarter is held out for checking. stratify=y keeps the label proportions similar. random_state makes this particular random split reproducible.

First, a DummyClassifier always predicts the training set's majority class. Its accuracy is our baseline. A model that seems impressive needs comparison with a rule this simple.

Split before learning how to fill missing values or encode categories. Otherwise information from the held-out data can influence training indirectly.

Fit a Pipeline

The notebook constructs preprocessing and model steps, then connects them:

model = make_pipeline(prepare, LogisticRegression(max_iter=1000))
model.fit(X_train, y_train)
predicted = model.predict(X_test)

.fit() learns from training examples. .predict() supplies predicted labels for input rows without receiving their answers.

prepare uses a ColumnTransformer to give numeric and category columns different treatment. Numeric gaps are filled with training medians and values are scaled. Category gaps are filled with the most frequent training category, then one-hot encoded into numeric indicator columns. Unknown categories can be handled when making later predictions.

The pipeline keeps these learned transformations with the classifier. New rows get the same processing without recomputing it from test answers. Read the preprocessing cell a step at a time; it is configuration of library tools, not a new counting algorithm you need to memorize.

Logistic regression is a classification model despite its name. This first project uses it through scikit-learn's interface. The mathematics can be a later direction.

Evaluate What It Got Wrong

Accuracy is the fraction of held-out labels predicted correctly. Compare it with the baseline, then inspect the confusion matrix: a table separating correct predictions from the two kinds of mistake.

The notebook also predicts two invented passengers. They aren't extra test evidence because we don't have known outcomes for them. Their purpose is to show how new rows use the trained pipeline.

Your numbers describe this dataset and split. Predictive performance on another passenger sample is a separate question.

Your Turn

Inspect several incorrect predictions in X_test. What information might the selected columns leave out? Write the question before proposing more features.

Try removing sex. Compare the predictions and discuss how historical social patterns enter a model through its data. A changed prediction isn't proof that changing a person's attribute would cause a different historical outcome.

Repeatedly choosing changes based on this same test set makes it less independent. A larger experiment should use validation data for model choices and reserve a fresh final test. Don't keep adjusting until a test number merely looks good.

Restart the kernel and run all cells. The source, split and complete fitting steps should be sufficient to reproduce the experiment.

What I've Learned

Explain the jobs of training data, a baseline and held-out data. Give one example of information that would leak the answer into this model.

The scikit-learn getting-started guide develops the estimator and pipeline ideas. You have trained a model; the next project applies a related workflow to words.