RAG explained in 50 lines of Python (with a local model)

RAG explained in 50 lines of Python (with a local model)

Retrieval-augmented generation in 49 lines: embed documents, retrieve the closest ones, and put them in the prompt. Without RAG, a local model invented a vacation policy; with RAG it answered correctly, and one rule stopped it guessing.

TL;DR — RAG (retrieval-augmented generation) means: find the pieces of your documents that match the question, paste them into the prompt, and ask the model to answer from them. Asked about a fictional company without RAG, a small local model invented answers (“10 vacation days”); with RAG, it answered correctly (“28 days”). When the answer wasn’t in the documents, it still invented one until we told it to say “I don’t know”.

LSTM input shape explained: (batch, timesteps, features)

LSTM input shape explained: (batch, timesteps, features)

What each of the three LSTM input dimensions means, how to build them from a time series with sliding windows, and the PyTorch batch_first default that silently mixes your samples. Tested with PyTorch 2.14 and Keras 3.15.

TL;DR — An LSTM expects a 3D input: (batch, timesteps, features). Batch is how many sequences you feed at once, timesteps is how many steps each sequence has, and features is how many values you have per step. In PyTorch, pass batch_first=True. Without it, nn.LSTM reads the first dimension as time, and a wrong order runs without any error.

Train, validation and test sets: what overfitting looks like in numbers

Train, validation and test sets: what overfitting looks like in numbers

Why you need three data splits, measured on a real dataset: a fully grown decision tree scored 1.000 on training data and 0.912 on validation, and picking the best model on one small validation split overstated the test score.

TL;DR — Train on the training set, choose settings with the validation set (or cross-validation), and use the test set once, at the very end. Overfitting is when a model learns its training data too well to generalize: in our test, a decision tree reached 1.000 training accuracy while validation accuracy fell to 0.912.

Histogram vs KDE: how do you choose the bandwidth?

Histogram vs KDE: how do you choose the bandwidth?

A KDE is a smooth histogram, and its bandwidth works like the bin width. Tested with SciPy: what Scott’s rule gets right, how a KDE leaks below zero, and when a histogram shows more.

TL;DR — Start with the default (Scott’s rule, bw_method="scott" in SciPy), then check it against a histogram. A bandwidth that is too small invents peaks; one that is too large merges real ones. Be careful with data that has a hard limit such as zero, and with narrow spikes: a default KDE handles both poorly.

What does temperature do in an LLM? Temperature, top-k and top-p explained

What does temperature do in an LLM? Temperature, top-k and top-p explained

Temperature reshapes the next-token probabilities; top-k and top-p cut off unlikely tokens. Tested on a small open model: at temperature 2.0 the output turned into noise, and top-k fixed it while top-p did not.

TL;DR — An LLM picks each next token by sampling from a probability distribution. Temperature sharpens (below 1) or flattens (above 1) that distribution. Top-k keeps only the k most likely tokens; top-p keeps the smallest set whose probabilities add up to p. Low temperature gives safe, repetitive text; high temperature gives variety, and past a point, noise.

pandas vs Polars vs DuckDB in 2026: a real benchmark on 10 million rows

pandas vs Polars vs DuckDB in 2026: a real benchmark on 10 million rows

pandas 3.0.6, Polars 1.44.2 and DuckDB 1.5.5 on the same 10M-row dataset: read, group by, filter and join, plus a single-thread run that shows how much of the gap is just parallelism.

TL;DR — On 10 million rows, Polars and DuckDB were 2× to 60× faster than pandas at group by, filter and join. Much of that gap comes from using all CPU cores: limited to one thread, Polars was only about 1.4× to 1.6× faster than pandas. DuckDB’s join stood out, staying fast even on a single thread.

Why you should always build a baseline model first

Why you should always build a baseline model first

Before training a real model, measure the simplest possible one. On a price-like series, “tomorrow = today” beat a random forest by more than 3×. Tested with scikit-learn, with the reason the forest failed.

TL;DR — A baseline is the simplest prediction you could make without machine learning, such as “tomorrow equals today” or “always predict the average”. Measure it first. If your model can’t beat it clearly, the model isn’t learning anything useful. In our test, the naive baseline scored an error of 0.79; a random forest scored 2.55.

Why fit preprocessing on training data only? A data leakage demo

Why fit preprocessing on training data only? A data leakage demo

Fitting a scaler or a feature selector on all your data before cross-validation leaks test information. On pure noise, leaky feature selection scored 0.80 instead of 0.50. Tested with scikit-learn 1.9.1.

TL;DR — Any step that learns from data (a scaler, an imputer, a feature selector) must be fitted on the training data only. Otherwise your test score includes information the model won’t have in production. The simplest fix is to put every step in a scikit-learn Pipeline and cross-validate the whole pipeline.

What are embeddings? Semantic search with cosine similarity in Python

What are embeddings? Semantic search with cosine similarity in Python

An embedding turns text into a vector so that similar meanings land close together. A 30-line example with a free local model finds “My laptop gets very hot” for “Why does my notebook overheat?”, with no shared words.

TL;DR — An embedding model turns a piece of text into a list of numbers (a vector) so that texts with similar meaning get similar vectors. Comparing vectors with cosine similarity lets you search by meaning instead of by matching words. In our test, keyword search ranked a sourdough sentence first for a laptop question; embedding search found the right answers.

How to show a progress bar in GridSearchCV (scikit-learn 1.9 callbacks)

How to show a progress bar in GridSearchCV (scikit-learn 1.9 callbacks)

scikit-learn 1.9 added experimental callbacks: ProgressBar for GridSearchCV and Pipeline, and ScoringMonitor for per-iteration training scores. Tested on 1.9.1, including the missing rich dependency.

TL;DR — Since scikit-learn 1.9, you can call grid_search.set_callbacks(ProgressBar()) before fit() to get live progress bars for every candidate and fold. Install rich first (pip install rich), or you get an ImportError. The feature is experimental and works only on a handful of estimators so far.

Pagination