TensorTonic writing

Why bother learning machine learning from scratch?

Learning machine learning from scratch matters more as writing code gets easier, because assumptions, evaluation, and failure still require human judgment.

Pratham Grover8 min read

Every few weeks, someone asks why TensorTonic encourages people to learn machine learning from scratch by implementing ideas that already have excellent library support. Why write logistic regression when scikit-learn has maintained a better version for years, or build attention with tensor operations when PyTorch ships an optimized kernel? It is a reasonable question, especially now that a coding model can generate either implementation in seconds.

When the goal is to ship a model, I use the library because it will almost always be faster, safer, and better tested than something written for an exercise. Copying NumPy versions of algorithms does not make somebody an ML engineer, but a carefully chosen implementation can be useful when it exposes a decision that the tool normally keeps out of view.

Machine learning has an awkward property because the code can be correct while the system is wrong. A training loop may run normally, the loss may fall, and the validation score may improve even while the model learns from a leaked feature, optimizes a metric that has little to do with the real outcome, or exploits a pattern that disappears outside the dataset. Since the software has carried out its instructions correctly, it never raises an exception to tell you that the experiment itself is flawed.

What the abstraction hides

Abstractions are valuable precisely because they let us spend time on the problem instead of recreating the machinery underneath it. I would rather call a reliable optimizer than rewrite one, just as I would rather use a framework kernel than debug my own CUDA every time I train a model.

The tradeoff is that every abstraction decides what you get to see. A classifier may expose only a few parameters and a prediction method, while the interface hides consequential choices about representation, objective, optimization, regularization, and numerical behaviour. Those choices remain quiet while the defaults suit the problem, but the same interface offers very little help once one of its assumptions stops holding.

A small from-scratch implementation pays off when it gives you a concrete picture of how data becomes a prediction, how the loss assigns responsibility for an error, and how that signal changes the parameters. With that picture in mind, unstable training becomes a question about specific values you can inspect, while a suspiciously good evaluation makes you examine how information moved through the experiment instead of treating the final score as an answer.

That understanding scales beyond classical models as long as the implementation remains focused. Implementing attention once makes masks, tensor shapes, and normalization concrete; training a small language model connects context length, tokenization, batch construction, and next-token evaluation; writing a simple kernel reveals how memory movement can make a mathematically smaller operation slower in practice.

AI makes the distinction sharper

Coding assistants have made implementation dramatically cheaper, to the point where I can describe a model and receive a plausible training loop before I have decided whether the experiment itself is sensible. The speed is genuinely useful, although it moves more of the work into specifying the right system and evaluating what comes back.

Somebody still has to decide whether the loss matches the task, whether the train and test split leaks information, whether the tensor dimensions mean what the comments claim, and whether a result is evidence or noise. Generated code can look especially convincing in the places where a subtle mistake is hardest to notice, so reviewing it requires a mental model of the computation that exists independently of the generated answer.

As code becomes easier to produce, foundational work becomes more valuable because the market will have no shortage of people who can generate an implementation. Knowing what to ask for, recognizing when the output is conceptually wrong, and designing a test that settles the question will remain much harder to automate.

How far down should you go?

My rule is to understand one layer below the abstraction you expect to use. A data scientist relying on scikit-learn should understand how the model, objective, regularization, and evaluation fit together, while an engineer training neural networks in PyTorch should also understand backpropagation, initialization, optimization, masking, and numerical stability. Someone working on inference will need to follow the problem further into batching, caches, precision, kernels, and hardware.

Going deeper is worthwhile when your work repeatedly crosses that boundary, although rebuilding every dependency rarely teaches enough to justify the time. The most useful implementations are usually small, deliberate, and disposable because they isolate one mechanism, make its behaviour visible, and give you something concrete to test before you return to the production library with a better idea of what it is doing for you.

Memorizing the implementation is unnecessary; what matters is whether you can reconstruct the reasoning, explain which assumptions matter, predict what changes when one of them is removed, and inspect a failure without changing five unrelated settings at once. That ability is much closer to real engineering than recalling an API or reproducing a familiar notebook.

What we are trying to preserve

TensorTonic exists because reading a clear explanation can feel almost identical to understanding it, even though the gap becomes visible as soon as you turn the idea into working code and encounter the cases your first mental model missed. A passed implementation remains imperfect evidence of understanding, but it tells us more than recognition alone.

I want the platform to preserve that productive friction without asking people to rebuild the world: read enough to understand the question, implement the part that carries the idea, run it, and let the failures reveal what was still vague before using the best libraries available for the real work.

Learning machine learning from scratch remains useful for the same reason we still derive results, reproduce papers, and inspect systems below their public interface: it gives us a practical way to distinguish code that merely looks familiar from an idea we can actually reason about.

Written by Pratham Grover, founder of TensorTonic.