What is Machine Learning? A Complete Beginner’s Guide

CODE REVIEW: overhyped_model_v1.py

Reviewer: Senior Embedded Systems Engineer (Staff Level, 32 Years Experience)
Date: October 24, 2023
Subject: Stop wasting my cycles on this statistical alchemy.


EXECUTIVE SUMMARY: A Monument to Inefficiency

I was asked to review this repository, and frankly, I’d rather be debugging a race condition in a 1992 interrupt handler written in hand-optimized assembly. What I found in overhyped_model_v1.py is not “Artificial Intelligence.” It is a bloated, fragile, and mathematically illiterate attempt to perform high-dimensional curve fitting using the most inefficient tools ever devised by man.

You are using Python 3.11.4, a language that treats memory management like a suggestion rather than a law of physics. You have pulled in NumPy 1.26.0, pandas 2.1.1, and scikit-learn 1.3.2 to solve a problem that could be handled by a simple lookup table or a few lines of C. Your script consumes 1.2GB of RAM before it even performs a single calculation. In my world, we launch satellites with 128KB.

This code is a “black box” because you don’t understand the math, not because the math is complex. You’ve replaced logic with “training,” and you’ve replaced efficiency with “layers.” This review will be painful, but perhaps it will prevent you from committing another crime against the CPU.


H2: The Dependency Bloat (The “Hello World” of Resource Waste)

Let’s start with your imports. You’ve managed to create a dependency graph that looks like a bowl of spaghetti.

# The Junior's Code
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neural_network import MLPClassifier
import pickle

Senior Reviewer Comments:

Look at this. To perform what is essentially a series of matrix multiplications, you’ve imported half of the PyData ecosystem. Do you have any idea what happens when you run import pandas as pd?

On my machine, running Python 3.11.4, here is the terminal output of your “simple” script just trying to load its dependencies:

$ /usr/bin/time -v python3 overhyped_model_v1.py
    Command being timed: "python3 overhyped_model_v1.py"
    User time (seconds): 2.45
    System time (seconds): 0.82
    Percent of CPU this job got: 98%
    Maximum resident set size (kbytes): 452032
    Minor (reclaiming a frame) page faults: 112043
    Voluntary context switches: 452
    Involuntary context switches: 120

452MB of RAM just to start the script. You haven’t even touched a data point yet. You are using pandas (version 2.1.1) to load a CSV file. A CSV file is a text file with commas. I can write a parser for that in 20 lines of C that uses 4KB of stack space. Instead, you load a library that brings in its own memory manager and a thousand helper functions you will never use.

And scikit-learn 1.3.2? You’re using an MLPClassifier. You’ve pulled in an entire library for a Multi-Layer Perceptron when all you’re doing is an iterative optimization of a non-convex loss function. You don’t even know what that means, do you? You think it’s “learning.” It’s not learning. It’s math.


H2: Data Ingestion or: How to Choke a CPU with CSVs

# The Junior's Code
def load_and_preprocess(file_path):
    data = pd.read_csv(file_path)
    X = data.drop('target', axis=1)
    y = data['target']

    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)

    return train_test_split(X_scaled, y, test_size=0.2)

Senior Reviewer Comments:

This function is a disaster. You are loading the entire dataset into memory at once. What happens when the dataset is 50GB? Your script will trigger the OOM (Out of Memory) killer faster than you can say “Big Data.”

$ python3 overhyped_model_v1.py
[1]    84201 killed     python3 overhyped_model_v1.py
# Kernel log:
# [92834.12] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,task=python3,pid=84201,uid=1000
# [92834.12] Out of memory: Killed process 84201 (python3) total-vm:16777216kB, anon-rss:14520320kB, file-rss:0kB, shmem-rss:0kB

You are using StandardScaler. Let’s talk about what is actually happening here. You are calculating the mean and standard deviation of your input vectors in a high-dimensional manifold. You are then subtracting the mean and dividing by the standard deviation for every single element.

In C, I would do this in a single pass using Welford’s online algorithm to keep memory usage constant. You, however, are creating multiple copies of the data in RAM. X is a copy, X_scaled is another copy, and train_test_split creates even more copies. You are treating RAM like it’s an infinite resource. It isn’t. Every byte you waste is a byte that can’t be used for actual computation.


H2: The Architecture of Ignorance (What is Machine Learning?)

Now we get to the “AI” part. You’ve defined your model like this:

# The Junior's Code
model = MLPClassifier(hidden_layer_sizes=(100, 50), 
                      activation='relu', 
                      solver='adam', 
                      max_iter=500)

Senior Reviewer Comments:

You call this a “Neural Network.” I call it a series of nested loops and dot products that you don’t understand.

To answer the question you clearly haven’t asked: what is machine learning?

Machine learning is not “intelligence.” It is the process of using a computer to perform a numerical optimization of a parameterized function until the output matches a set of labels within an acceptable margin of error. In your case, you are trying to find a set of weights ($W$) and biases ($b$) such that $f(X, W, b) \approx y$.

Your MLPClassifier is just a collection of linear transformations followed by non-linear “activation functions” (like ReLU, which is just max(0, x)—an if statement for people who like to waste GPU cycles).

By choosing hidden_layer_sizes=(100, 50), you have created a system with thousands of parameters. For a classification problem with 10 input features, this is like using a sledgehammer to crack a nut. You are trying to fit a curve to data points. If you had any sense, you’d start with a linear regression or a simple decision tree. But no, you want “Deep Learning” because it sounds better in a LinkedIn bio.

You are using the adam solver. That’s an adaptive moment estimation—a fancy way of saying you’re doing gradient descent but you’re too lazy to tune the learning rate yourself. You’re letting the computer guess how to move down the gradient of a non-convex loss function. If you hit a local minimum, your model is useless, and you won’t even know why.


H2: The Training Loop: A Non-Convex Nightmare

# The Junior's Code
print("Training model...")
model.fit(X_train, y_train)
print(f"Training complete. Iterations: {model.n_iter_}")

Senior Reviewer Comments:

“Training.” You make it sound so organic. Let’s look at the raw reality of what your CPU is doing during model.fit().

It is performing backpropagation. This involves calculating the partial derivative of the loss function with respect to every single weight in your 100×50 network. Because you are using Python 3.11.4, every one of those calculations is wrapped in an object. Every float is a PyObject.

While a C program would be piping these numbers directly into the AVX-512 registers of the CPU, your Python script is bouncing around the heap, checking the Global Interpreter Lock (GIL), and praying that the garbage collector doesn’t decide to wake up.

Here is what your “Training” looks like on a system monitor:

PID    USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
84201  junior    20   0 18.4G  1.2G  45200 R  99.9   7.2   5:12.43 python3

99% CPU usage for five minutes to solve a problem that a closed-form solution (like Ordinary Least Squares) could solve in 10 milliseconds. You are burning electricity to avoid thinking.

And let’s talk about the “non-convex loss function.” Because your “network” is deep (two layers is “deep” for someone with your attention span), the error surface is full of pits and valleys. You aren’t finding the “truth”; you’re finding a spot where the math stops changing. That’s not intelligence; that’s exhaustion.


H2: Overfitting: The Result of Algorithmic Laziness

# The Junior's Code
train_acc = model.score(X_train, y_train)
test_acc = model.score(X_test, y_test)

print(f"Training Accuracy: {train_acc:.4f}")
print(f"Test Accuracy: {test_acc:.4f}")

Senior Reviewer Comments:

I ran your code. Here is the output:
Training Accuracy: 0.9998
Test Accuracy: 0.7241

Congratulations. You haven’t built a model; you’ve built a very expensive, very slow lookup table. This is what we call overfitting.

Overfitting is a symptom of developer laziness. You gave the model so many parameters (the 100×50 hidden layers) that it simply memorized the training data. It didn’t find the underlying pattern in the high-dimensional manifold; it just drew a circle around every point in the training set.

In the embedded world, if my sensor logic only works in the lab and fails in the field, people die. In your world, you just call it “a need for more data.” No. You need fewer parameters. You need regularization. You need to understand that what is happening is that your model has “high variance.” It’s sensitive to the noise in your data, not the signal.

You’re using scikit-learn 1.3.2, which has L2 regularization (weight decay) turned on by default with alpha=0.0001. Clearly, that’s not enough to save you from your own architectural hubris. You’ve created a digital version of a student who memorizes the answers to the practice test but fails the actual exam because they don’t understand the subject.


H2: Inference and the Death of Real-Time Performance

# The Junior's Code
def predict_realtime(sample):
    # sample is a list of features
    sample_scaled = scaler.transform([sample])
    prediction = model.predict(sample_scaled)
    return prediction[0]

Senior Reviewer Comments:

“Real-time.” You keep using that word. I do not think it means what you think it means.

In my world, “real-time” means a deterministic response within a fixed number of clock cycles. In your world, “real-time” means “whenever Python feels like it.”

Let’s look at the latency of your predict_realtime function. To predict a single label, you have to:
1. Convert a Python list to a NumPy array (allocation!).
2. Pass it through the StandardScaler (more math, more allocations).
3. Perform multiple matrix multiplications in the MLP.
4. Apply the activation functions.
5. Return a NumPy array and extract the first element.

I profiled this. The latency is approximately 15ms per prediction. 15 milliseconds! In 15ms, a modern CPU can execute 45 million instructions. You are using 45 million instructions to do a few hundred multiplications.

If I put this code in an anti-lock braking system, the car would be in the next county before the brakes applied. This is the fundamental problem with modern “AI” development: you’ve become so accustomed to fast hardware that you’ve forgotten how to write fast software. You treat the CPU as a magic box that turns your slow Python into “intelligence.”

And what happens if the input is slightly outside the range of your training data? Your model will confidently give a wrong answer. It has no bounds checking. It has no safety. It’s a black box that spits out a float and you treat it like the gospel truth.


FINAL VERDICT: Back to Basics

Your overhyped_model_v1.py is a case study in everything wrong with modern software engineering. You have:
1. Ignored memory constraints.
2. Used massive dependencies for trivial tasks.
3. Substituted mathematical understanding with “training.”
4. Produced a model that is too slow for real-world use and too bloated for embedded systems.

What is the solution?

First, delete your venv folder. It’s 2GB of garbage you don’t need.
Second, open a textbook on linear algebra. Learn what a dot product actually is. Learn what a Jacobian matrix is.
Third, rewrite this entire thing in C. Use fixed-point arithmetic if you want to impress me.

If you can’t explain the math behind your model using only a pencil and paper, you have no business writing code that implements it. You are not an engineer; you are a script kiddie playing with statistical fire.

Go back to school. Learn how pointers work. Learn how the stack and the heap differ. Learn why a PyObject is the most expensive way to store a 4-byte integer. Until then, stay away from my production servers. You’re a liability to the uptime.

Grade: F-
Note: The CPU fan on my laptop is still spinning from running your script. I’m sending you the bill for the electricity.


Appendix: Raw Pip Error Log from Junior’s Environment

(Just to show how fragile this “modern” stack is)

$ pip install -r requirements.txt
Collecting pandas==2.1.1 (from -r requirements.txt (line 1))
  Using cached pandas-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (12.3 MB)
Collecting numpy==1.26.0 (from -r requirements.txt (line 2))
  Using cached numpy-1.26.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (18.2 MB)
Collecting scikit-learn==1.3.2 (from -r requirements.txt (line 3))
  Using cached scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (10.8 MB)
Installing collected packages: numpy, pandas, scikit-learn
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
some-other-useless-package 0.1.2 requires numpy<1.24.0,>=1.21.0, but you have numpy 1.26.0 which is incompatible.
Successfully installed numpy-1.26.0 pandas-2.1.1 scikit-learn-1.3.2

Even your package manager hates your choices. Fix it.

Related Articles

Explore more insights and best practices:

Leave a Comment