The previous articles covered AI concepts, the machine learning workflow, model training and evaluation, and neural network basics. This article runs a small end-to-end practice project: a binary classification task with Python and scikit-learn.
The example uses the breast cancer dataset built into scikit-learn, so no external data file is required. The goal is not to chase the highest score. The goal is to walk through loading data, splitting data, standardizing features, training, predicting, and evaluating.
Note: this dataset is used here only for machine learning practice. It should not be used for medical decisions or real diagnosis. The article focuses on the classification workflow, not medical conclusions.
1. Prepare the Environment
Create a virtual environment and install the dependency:
python3 -m venv .venv
source .venv/bin/activate
pip install scikit-learn
This example uses only scikit-learn, not a deep learning framework. That keeps the focus on the basic machine learning workflow.
2. Complete Code
The following script can be run directly:
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
def main():
dataset = load_breast_cancer()
X = dataset.data
y = dataset.target
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y,
)
model = Pipeline(
steps=[
("scaler", StandardScaler()),
("classifier", LogisticRegression(max_iter=500)),
]
)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print("Accuracy:", accuracy_score(y_test, y_pred))
print("Confusion matrix:")
print(confusion_matrix(y_test, y_pred))
print("Classification report:")
print(classification_report(y_test, y_pred, target_names=dataset.target_names))
if __name__ == "__main__":
main()
Save it as ai_classification_demo.py and run:
python ai_classification_demo.py
If dependency import feels slow the first time, confirm that the virtual environment is active and run python -c "import sklearn; print(sklearn.__version__)" to check that scikit-learn is installed.
3. What the Dataset Contains
load_breast_cancer() returns a binary classification dataset. Each sample contains numeric features, and the label indicates which class the sample belongs to.
In the script:
Xis the feature matrix, with one row per sampleyis the label array, with one label per sampledataset.target_namescontains the class names
The dataset is already prepared as numeric features, which makes it useful for practicing classification basics.
4. Why Split Training and Test Data?
The script uses train_test_split():
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y,
)
test_size=0.2 means 20% of the data is reserved for testing. stratify=y tries to preserve the class ratio after the split, which is useful for classification.
If you evaluate only on training data, the model may have memorized training examples instead of learning a pattern that generalizes.
5. Why Use Pipeline?
The code uses Pipeline instead of manually standardizing first and training later:
model = Pipeline(
steps=[
("scaler", StandardScaler()),
("classifier", LogisticRegression(max_iter=500)),
]
)
This has two benefits:
- Standardization and classification stay in one reproducible workflow
- The test set uses scaling parameters learned only from the training set, which avoids data leakage
Data leakage is a common beginner mistake. If you standardize the full dataset before splitting, information from the test set has already influenced training.
6. Why Logistic Regression?
Logistic regression is a classic baseline for classification. It is fast, stable, and easier to explain than many more complex models.
This example does not start with a neural network because running the full workflow is more important at this stage. Once every line in this script is clear, replacing the classifier with a random forest, support vector machine, or neural network becomes more meaningful.
7. How to Read the Evaluation
The script prints three kinds of results:
Accuracy: the overall proportion of correct predictionsconfusion_matrix: which classes were predicted incorrectlyclassification_report: precision, recall, F1-score, and related metrics
Even if accuracy is high, do not stop there. Check the confusion matrix to see which class causes mistakes, then compare precision and recall to the requirements of the problem.
A real rerun: reconstructing 98.25% from 114 predictions
The complete script above was rerun on 2026-09-07 with Python 3.13.9, scikit-learn 1.7.2, NumPy 2.3.5 and SciPy 1.17.1. This is reproduction of a fixed teaching example, not a new independent test. Its actual output was:
Accuracy: 0.9824561403508771
Confusion matrix:
[[41 1]
[ 1 71]]
Classification report:
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
The dataset contains 569 samples and 30 features; 455 rows enter training and 114 enter testing. The matrix uses [0=malignant, 1=benign], with true labels in rows and predictions in columns. There are 41+71=112 correct predictions, so accuracy is 112/114, not an average of the rounded report entries. See the scikit-learn dataset description for the original data and label definitions.
| Original index | True class | Predicted class | Model P(class 0) |
|---|---|---|---|
| 541 | 1, benign | 0, malignant | 0.634138 |
| 73 | 0, malignant | 1, benign | 0.091272 |
The second error shows that mistakes need not sit close to the 0.5 decision boundary. This experiment did not evaluate probability calibration: these outputs are not personal disease risks. Nor do the two rows establish which feature caused a mistake; that would require a separate investigation, not a medical interpretation of one probability.
A DummyClassifier(strategy="most_frequent") fitted to the same training rows scores 72/114=63.16% while detecting none of class 0. Logistic regression’s class-0 recall here is 41/42=97.62%. That demonstrates more than majority-class guessing on this split, not suitability for diagnosis.
Download the reproduction package and enter the extracted ml-basics-lab directory. Its run_lab.py retains the fixed model and split, adding original row indices, probabilities, a dataset hash and the majority baseline. Inspect the complete 114-row prediction CSV and metrics and split JSON, not just a screenshot.
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
python run_lab.py --output results
python -m unittest -v test_lab
The reference environment and script SHA-256 are in reference/environment.json. After dependency installation the script requires no external data request. Floating-point probabilities can vary slightly across platforms. If the test size differs from 114 or class support differs from 42/72, check the data and split settings rather than attributing it to rounding.
8. What to Try Next
After the script runs, try a few small experiments:
These are development experiments, not permission to pick the best score on the same test set. Compare models or preprocessing inside the original training partition using cross-validation. The test errors are now public; once they guide a model change, those rows are development evidence. Choosing another random seed does not make previously examined data independent again.
- Change
test_sizeto 0.3 and see whether results stay stable - Remove
StandardScalerand compare the metrics - Replace
LogisticRegressionwithRandomForestClassifier - Print
dataset.feature_namesand read what each feature means - Find the indexes of wrong predictions and inspect those samples
The key to learning AI foundations is to make each example explainable. In this practice project, you did not just run a classifier. You walked through a complete machine learning workflow.
9. Practice Run Audit Table
After running the script, use the table below to turn a one-time execution into a reproducible learning record.
| Audit item | What to capture | Why it matters | What to try next |
|---|---|---|---|
| Environment | Python version, scikit-learn version, and command used | Different library versions can change defaults and warnings | Re-run after upgrading dependencies and compare output |
| Dataset shape | Sample count, feature count, class names, and class balance | Metrics are easier to interpret when the class distribution is known | Print feature names and inspect several rows |
| Pipeline behavior | Whether scaling is inside the pipeline and fitted only on training data | This prevents leakage from the test set into preprocessing | Remove the scaler and compare convergence and metrics |
| Error pattern | Confusion matrix, wrong sample indexes, and class-level recall | Wrong examples explain model limitations better than accuracy alone | Compare logistic regression with a tree-based baseline |
10. Add This Practice to Your Notes
After running the code, record these details:
- How many samples, features, and classes the dataset contains
- How many samples are in the training and test sets
- The accuracy, precision, recall, and F1-score
- Which type of mistake appears more often in the confusion matrix
- What changes when you remove standardization or switch models
These notes are more useful than saving only one accuracy value because they help you explain the experiment, not just preserve the result.
11. Series Review
This article turns the previous concepts into code. To revisit the foundations, start again from the AI Basics Learning Roadmap, or return to the Blog page for the full series.