Model and Modality PlaybooksNovember 10, 2026Flatkey Team

Machine Learning with Python: Your First Model and When to Use a GPU

A practical CPU-based tutorial with scikit-learn, error evaluation, and an infrastructure decision based on real workload—without requiring a GPU to get started.

Machine Learning with Python: Your First Model and When to Use a GPU

To get started with machine learning with Python, choose a small task, separate the training and test data, and compare a simple model with a basic baseline. You do not need to rent a GPU for your first exercise. In this guide, we will build a complete classifier on a CPU, interpret its errors, and decide when it makes sense to change the infrastructure.

The goal is to finish with an experiment that you can repeat and explain. Getting a single prediction right is less useful than knowing which data the model saw, how it was evaluated, and where it fails.

What to learn first in machine learning with Python

A supervised learning project has inputs, called features, and a known answer, called the target. In this article's example, the features are flower measurements and the target is the species. The model learns a relationship using the training set and then tries to classify examples that were kept separate.

Before choosing a library, write down the question you want to answer:

Task Response type Possible first experiment
Classify an observation A category Logistic regression or a decision tree
Estimate a quantity A number Linear regression and error analysis
Find groups without labels Clusters A clustering method and inspection of the groups
Work with images or language at scale More complex representations and predictions An appropriate deep learning library, after defining the evaluation

Do not choose a neural network just because it seems more advanced. If a simple solution solves the task within the required cost and timeframe, it is an important baseline for any later alternative.

Set up an isolated environment

You need a Python installation compatible with scikit-learn and access to a terminal. Create a working folder and a virtual environment to avoid conflicts with other projects:

python -m venv .venv

On macOS or Linux, activate it with source .venv/bin/activate. In Windows PowerShell, use .venv\Scripts\Activate.ps1, according to local terminal policies. Then install the library and record the versions:

python -m pip install scikit-learn
python -m pip freeze > requirements.txt

Use python3 instead of python if that is the name of the executable on your computer. The dependency file helps recreate the environment; also keep the Python version, the code, and the experiment configuration.

A first complete model: flower classification

The scikit-learn Iris dataset contains 150 observations, four numerical features, and three classes. It is small and suitable for learning the workflow, but it does not demonstrate that a system is ready for real-world data.

Save the code below as exemplo_iris.py and run python exemplo_iris.py. It uses 120 observations for training and 30 for testing. The stratified split maintains class representation; the fixed seed makes it possible to repeat the same split.

import platform
from time import perf_counter
import sklearn
from sklearn.datasets import load_iris
from sklearn.dummy import DummyClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, f1_score, confusion_matrix
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.20, random_state=42, stratify=y
)
models = {
    "baseline": DummyClassifier(strategy="most_frequent"),
    "logistica": make_pipeline(
        StandardScaler(), LogisticRegression(max_iter=1000)
    ),
}
for name, model in models.items():
    start = perf_counter()
    model.fit(X_train, y_train)
    train_seconds = perf_counter() - start
    pred = model.predict(X_test)
    print(name, "acuracia=", round(accuracy_score(y_test, pred), 3),
          "f1_macro=", round(f1_score(
              y_test, pred, average="macro", zero_division=0
          ), 3))
    print("matriz de confusao:", confusion_matrix(y_test, pred).tolist())
    print("tempo de treino (s):", round(train_seconds, 6))
print("Python:", platform.python_version(),
      "scikit-learn:", sklearn.__version__)

The DummyClassifier provides a comparison that does not learn a relationship between the features and the class. With the strategy used here, it predicts the most frequent class in the training data. If a more elaborate model does not improve on such a baseline, investigate the data, objective, and evaluation before spending more resources. See the strategies in the DummyClassifier documentation.

Logistic regression receives the data through a Pipeline: first standardization, then the classifier. The transformations are fitted on the training data and reapplied to the test data. This prevents calculating the scale using information that should be reserved for evaluation, a common form of data leakage. The scikit-learn best practices documentation explains this precaution.

How to interpret the output without misleading yourself

When running this example on CPU, with Python 3.14.4 and scikit-learn 1.9.1, we obtained the results below. They are results from this exercise and this data split, not a promise for another dataset.

Model Test accuracy Macro F1
Most frequent class baseline 0.333 0.167
Standardization + logistic regression 0.933 0.933

The matrix for the second model was [[10, 0, 0], [0, 9, 1], [0, 1, 9]]: two of the 30 test flowers were confused between the last two classes. This is more informative than simply saying that the model got about 93% correct.

Accuracy indicates the proportion of correct predictions in the evaluated set. Macro F1 calculates the F1 score for each class and averages them without giving greater weight to the most frequent class. The confusion matrix shows which classes were confused. Use all three views to understand the behavior, rather than looking only for the largest number.

The printed time is only the training time for that run on your computer. On such a small dataset, it varies with the machine and measurement noise. Do not use this number to promise cloud savings or compare CPU and GPU.

The Iris result should not become an expectation of accuracy for your product either. With your own data, the distribution, label quality, and cost of errors may be completely different. A rare failure can matter more than dozens of easy successes.

Before changing the algorithm, answer:

  1. Did the model outperform the basic baseline using exactly the same test set?
  2. Which class accounts for the errors, and what is their impact on the task?
  3. Is there information in the inputs that would only be available after the prediction?
  4. Does the test represent new examples, or does it contain duplicates and observations related to the training data?
  5. Does performance hold across other valid data splits?

From the exercise to a real dataset

A random split is convenient for Iris. It may be unsuitable if you predict future events, have multiple rows per customer, or find copies of the same observation. In those cases, consider a temporal or group-based split, according to how the model will be used. The random_state parameter does not fix a conceptually wrong split. The train_test_split reference describes the controls available for this simple split.

Set aside validation data to choose configurations and keep a separate final test set. If you repeatedly consult the test set to decide the next adjustment, it starts influencing your choices. For a small project, an experiment spreadsheet already helps:

Record What to note
Data Source, date, size, columns, and exclusion rule
Split Temporal, group-based, or random; seed when applicable
Model Algorithm, parameters, and dependency versions
Evaluation Main metric, errors by class, and comparison with the baseline
Operation Time, memory, artifact size, and measured cost
Decision What improved and which problem remains open

When replacing Iris with your own table, validate types, missing fields, and categories before training. Keep transformations together with the model so that prediction uses the same data preparation. A column whose unit has changed can cause problems even if the code continues to run.

When a GPU helps — and when it does not

The example in this guide runs on CPU. Adding a graphics card to the machine does not automatically make all scikit-learn code use it. The project documents GPU support for a limited set of estimators and configurations, including through specific inputs and options. Check the exact path for the chosen algorithm.

A GPU is worth an experiment when there is a compatible implementation and a workload that can take advantage of it, such as certain neural network training tasks or batch processing. Even then, the gain must offset data transfer, environment setup, and infrastructure cost.

Observed situation Recommended next action
Little data and fast CPU training Prioritize data quality and evaluation
Reading or preparation consumes most of the time Measure storage, loading, and transformations
Algorithm without a GPU-compatible path Review the library or stay with CPU
GPU-compatible training takes up a large part of the cycle Run a controlled test on the candidate configuration
Large model or high inference volume Measure memory, latency, and concurrency with the real workload

Compare the total time to a useful result, not just the execution of one operation. Include installation, transfer, training, validation, storage, and idle periods. An alternative that is faster per epoch may be worse if it requires more attempts to reach the required quality.

When the project reaches this stage, Flatkey Cloud GPU lets you evaluate NVIDIA GPU rental options. Confirm the configuration, region, availability, and terms for the intended workload. Bring the dataset size, library, required memory, and test objective; this tutorial does not require renting a GPU to complete.

Before putting the model into use

Define the accepted input format, the behavior for invalid fields, and how to monitor errors after publication. Keep the model version and data preparation together. Test a prediction with representative data outside the notebook before thinking about scale.

If the next step is to serve a language model with your own infrastructure, the vLLM deployment guide covers another type of workload. Do not confuse a small tabular classifier with operating an LLM: memory, evaluation, and server requirements are different.

Frequently asked questions

Do I need a GPU to learn machine learning with Python?

No. The example shown can run on a CPU. Choose larger infrastructure when there is a measured bottleneck and an implementation that can take advantage of it.

Why use a baseline before a more complex model?

It shows what can be achieved with a simple rule. This makes it possible to assess whether the model learned something useful and whether the added complexity is justified.

Does the same seed guarantee the same result in every environment?

It fixes the random choices controlled by that parameter. Also record versions and the environment; implementation and execution differences can affect results. Do not confuse the repeatability of a split with a universal performance guarantee.

What is the next project after Iris?

Choose a small task with data you understand, a verifiable target, and a success criterion. Repeat the process: baseline, correct split, model, error analysis, and measurement. This habit is the foundation for progressing in machine learning with Python without increasing costs before necessary.