10 AI Code Examples in Python You Can Run Offline

Run ten small Python examples for machine learning interviews, with checked outputs, edge cases, and practice links for regression, retrieval, SQL, and more.

Topics: Machine learning, Python, AI engineering, Interview preparation

Author: PracHub

Published: 10/16/2026

10 AI Code Examples in Python You Can Run Offline

October 16, 2026
11 min read
10 AI Code Examples in Python You Can Run Offline

Quick Overview

Run ten small Python examples for machine learning interviews, with checked outputs, edge cases, and practice links for regression, retrieval, SQL, and more.

Machine learningPythonAI engineeringInterview preparation
Machine Learning EngineerFree

A useful AI code example should run, expose one idea clearly, and make its limits visible. These ten Python examples cover preprocessing, regression, text classification, probabilities, vision metrics, retrieval, query execution, recommendations, and a reinforcement-learning update.

Each Python block is independent. Save a block as example.py and run python3 example.py. They use the Python standard library, including sqlite3, and small invented fixtures. No model download, API credential, network request, or GPU is required. The examples demonstrate components and numerical behavior; their tiny datasets do not establish production accuracy.

Pick an example by the behavior you want to inspect

ExampleWhat you can verifyMain limitation
Train-only centeringTest data does not determine the fitted meanOne feature and a fixed split
Linear regressionA fitted line predicts a known pointSynthetic, noise-free observations
Naive BayesSmoothed token counts produce a labelTiny vocabulary and simple tokenization
Threshold evaluationPrecision and recall change with a cutoffSix labeled observations
SoftmaxLarge finite logits produce finite probabilitiesProbabilities are not automatically calibrated
Box overlapIntersection over union follows the geometryNo detector or image loading
RetrievalMatching passages are ranked with source IDsLexical matching, no generation
Approved SQLOnly a known query and validated values executeNarrow template-based workflow
RecommendationSimilar users contribute unseen-item scoresSparse toy history and no ranking evaluation
Q-learning updateTerminal states remove the bootstrap termOne update, not a trained control agent

1. Fit preprocessing on the training split

This deliberately simple transform subtracts a mean learned from training values. The held-out value should not move that mean.

from statistics import mean

train = [10.0, 20.0, 30.0]
test = [1000.0]
center = mean(train)

train_centered = [value - center for value in train]
test_centered = [value - center for value in test]

print(train_centered)  # [-10.0, 0.0, 10.0]
print(test_centered)   # [980.0]

Recomputing the mean from train + test would leak information from the evaluation split. The scikit-learn common-pitfalls guide explains the same boundary for learned preprocessing and pipelines.

A fixed split is enough to inspect this arithmetic, but real splits must reflect the prediction setting. For time-dependent or repeated-user data, decide how to prevent future information or related observations from crossing the boundary. An empty training set also needs an explicit policy; this example lets mean() raise an error.

2. Fit a one-feature linear regression

Here is ordinary least squares for one feature and an intercept. The three invented observations lie on the line y = 1 + 2x.

from statistics import mean

def fit_line(xs, ys):
    if len(xs) != len(ys) or len(xs) < 2:
        raise ValueError("Need at least two paired observations")
    x_mean, y_mean = mean(xs), mean(ys)
    denominator = sum((x - x_mean) ** 2 for x in xs)
    if denominator == 0:
        raise ValueError("The feature must vary")
    slope = sum(
        (x - x_mean) * (y - y_mean) for x, y in zip(xs, ys)
    ) / denominator
    return y_mean - slope * x_mean, slope

intercept, slope = fit_line([0.0, 1.0, 2.0], [1.0, 3.0, 5.0])
print(round(intercept + slope * 3.0, 1))  # 7.0

The LinearRegression reference describes the least-squares objective. This small implementation assumes ordinary finite numeric inputs and a single feature. It is not a substitute for a numerically robust multi-feature solver.

Predicting one point on a perfect line checks the implementation, not generalization. Add noise, hold out observations, and compare against a baseline before discussing model quality. Practice the broader fit/predict contract in MSE loss and linear regression from scratch.

3. Train a tiny Naive Bayes text classifier

This example learns token counts from four invented messages. It uses add-one smoothing and sums log probabilities. It abstains when none of the input words appeared during training.

from collections import Counter
from math import log

training = [
    ("positive", "good bright"), ("positive", "good helpful"),
    ("negative", "bad slow"), ("negative", "bad broken"),
]
counts = {label: Counter() for label, _ in training}
class_docs = Counter()
for label, text in training:
    counts[label].update(text.lower().split())
    class_docs[label] += 1
vocabulary = set().union(*(set(c) for c in counts.values()))

def classify(text):
    words = [w for w in text.lower().split() if w in vocabulary]
    if not words:
        return None
    scores = {}
    for label in sorted(counts):
        total = sum(counts[label].values()) + len(vocabulary)
        scores[label] = log(class_docs[label] / len(training)) + sum(
            log((counts[label][word] + 1) / total) for word in words
        )
    return max(scores, key=scores.get)

print(classify("good helpful"))  # positive
print(classify("unknown words"))  # None

The Naive Bayes documentation explains multinomial text features and smoothing. Here, punctuation is not normalized and token order is discarded. Negation, sarcasm, spelling variants, and domain shift can defeat this tiny model.

Use a held-out labeled set to evaluate changes. For a deeper discussion of smoothing and unseen words, continue with PracHub's Naive Bayes interview questions.

4. Inspect a classification threshold with a confusion matrix

A score and a decision are different things. This function makes the threshold explicit and reports precision and recall, returning None when a denominator is zero.

def evaluate(labels, scores, threshold):
    if len(labels) != len(scores) or not labels:
        raise ValueError("Need nonempty paired labels and scores")
    if any(label not in (0, 1) for label in labels):
        raise ValueError("Labels must be binary")
    predicted = [score >= threshold for score in scores]
    tp = sum(y == 1 and p for y, p in zip(labels, predicted))
    fp = sum(y == 0 and p for y, p in zip(labels, predicted))
    fn = sum(y == 1 and not p for y, p in zip(labels, predicted))
    precision = tp / (tp + fp) if tp + fp else None
    recall = tp / (tp + fn) if tp + fn else None
    return tp, fp, fn, precision, recall

labels = [1, 0, 1, 0, 1, 0]
scores = [0.9, 0.8, 0.7, 0.4, 0.3, 0.1]
print(evaluate(labels, scores, 0.5))  # (2, 1, 1, 2/3, 2/3)
print(evaluate(labels, scores, 1.0))  # (0, 0, 3, None, 0.0)

The first line prints floating-point approximations for the two fractions. Inspect false positives and false negatives against the product's costs. Select the threshold using appropriate validation data, then report final performance on a separate test set. Repeatedly tuning against that test set would undermine the comparison.

The function assumes finite scores. If scores come from an external service, validate that contract before using them. A six-row arithmetic fixture cannot establish that a threshold will remain suitable after the data distribution changes.

5. Compute softmax without exponentiating huge logits

Subtracting the maximum logit preserves the softmax result while avoiding overflow from large positive inputs.

from math import exp, isfinite

def softmax(logits):
    if not logits or not all(isfinite(x) for x in logits):
        raise ValueError("Need finite logits")
    largest = max(logits)
    weights = [exp(x - largest) for x in logits]
    total = sum(weights)
    return [weight / total for weight in weights]

print([round(p, 6) for p in softmax([1000.0, 1001.0])])
# [0.268941, 0.731059]

Check that the result sums to approximately one and is unchanged when the same finite constant is added to every input. The PyTorch Softmax reference defines the normalization; training losses may use a combined or log-space formulation instead.

A high probability does not prove calibration or correctness on unfamiliar data. To extend this component into a training exercise, use softmax cross-entropy forward and backward passes.

6. Calculate bounding-box overlap for a vision pipeline

Intersection over union, or IoU, compares two boxes by dividing their intersection area by their union area. This example uses finite axis-aligned coordinates in (x1, y1, x2, y2) order.

def iou(a, b):
    for box in (a, b):
        if box[2] < box[0] or box[3] < box[1]:
            raise ValueError("Reversed box coordinates")
    width = max(0, min(a[2], b[2]) - max(a[0], b[0]))
    height = max(0, min(a[3], b[3]) - max(a[1], b[1]))
    intersection = width * height
    area_a = (a[2] - a[0]) * (a[3] - a[1])
    area_b = (b[2] - b[0]) * (b[3] - b[1])
    union = area_a + area_b - intersection
    return intersection / union if union else 0.0

print(round(iou((0, 0, 2, 2), (1, 1, 3, 3)), 6))  # 0.142857
print(iou((0, 0, 1, 1), (2, 2, 3, 3)))  # 0.0

The Torchvision box_iou reference documents this coordinate format and metric. State whether coordinates use continuous extents or an inclusive pixel convention; the arithmetic above uses continuous extents.

This is a geometry component, not an object detector. It does not load an image, find objects, apply non-maximum suppression, or evaluate a detector's precision and recall.

7. Retrieve passages before discussing RAG answers

A small lexical retriever makes the ranking step inspectable. Cosine similarity normalizes the dot product by vector length, as described in the scikit-learn cosine-similarity reference.

import re
from collections import Counter
from math import sqrt

def tokens(text):
    return Counter(re.findall(r"[a-z]+", text.lower()))

def cosine(a, b):
    denominator = sqrt(sum(v * v for v in a.values())) * sqrt(
        sum(v * v for v in b.values())
    )
    numerator = sum(v * b.get(k, 0) for k, v in a.items())
    return numerator / denominator if denominator else 0.0

documents = {
    "a": "reset password", "b": "retry failed requests",
    "c": "retry network errors",
}
query = tokens("retry network")
ranked = sorted(
    documents,
    key=lambda key: (-cosine(query, tokens(documents[key])), key),
)
hits = [key for key in ranked if cosine(query, tokens(documents[key])) > 0][:2]
context = [{"source": key, "text": documents[key]} for key in hits]
print([item["source"] for item in context])  # ['c', 'b']

The original retrieval-augmented generation paper combines retrieval with generation. This snippet stops at retrieval and context assembly. It does not call a language model or establish that a generated answer will be supported by its sources.

Before sending context to any generator, filter documents using the caller's permissions. Treat retrieved text as untrusted data, not permission to execute tools. Evaluate retrieval separately from answer quality; a fluent response can hide a retrieval miss. Practice the broader design in a retrieval-augmented generation pipeline question.

8. Put an approval boundary before SQL execution

For a narrow workflow, a model can propose a known query identifier and values. Application code chooses the SQL from a fixed registry. This example uses a simulated proposal and a disposable in-memory database; it does not generate or execute free-form model-written SQL.

import sqlite3

def approved_query(connection, proposal):
    if type(proposal) is not dict or set(proposal) != {"query_id", "active"}:
        raise ValueError("Unexpected proposal schema")
    if proposal["query_id"] != "users_by_status":
        raise ValueError("Unknown query")
    active = proposal["active"]
    if type(active) is not int or active not in (0, 1):
        raise ValueError("Invalid status")
    sql = "SELECT name FROM users WHERE active = ? ORDER BY name LIMIT ?"
    return connection.execute(sql, (active, 10)).fetchall()

connection = sqlite3.connect(":memory:")
try:
    connection.execute("CREATE TABLE users(name TEXT, active INTEGER)")
    connection.executemany(
        "INSERT INTO users VALUES (?, ?)", [("Ada", 1), ("Lin", 0)]
    )
    print(approved_query(connection, {"query_id": "users_by_status", "active": 1}))
    # [('Ada',)]
finally:
    connection.close()

Python's SQLite documentation explains placeholder binding. Parameters bind values; they do not supply table names or authorization. Real applications still need caller-specific permissions, limited database privileges, resource limits, and evaluation of whether the selected query answers the intended question.

An approved query can be semantically wrong for a user's request. Keep that failure separate from syntax validity and injection prevention. Do not replace the registry check with a rule that merely looks for SELECT at the start of arbitrary text.

9. Recommend unseen items from similar users

This is a small neighbor-voting baseline. It uses cosine similarity over a user's observed rating vector and accumulates similarity-weighted ratings for unseen items.

from collections import defaultdict
from math import sqrt

ratings = {
    "target": {"A": 5, "B": 1},
    "neighbor1": {"A": 5, "C": 4},
    "neighbor2": {"B": 5, "D": 4},
}

def recommend(user):
    target = ratings[user]
    scores = defaultdict(float)
    for other, values in ratings.items():
        if other == user:
            continue
        denominator = sqrt(sum(v * v for v in target.values())) * sqrt(
            sum(v * v for v in values.values())
        )
        numerator = sum(v * values.get(k, 0) for k, v in target.items())
        similarity = numerator / denominator if denominator else 0.0
        if similarity <= 0:
            continue
        for item, rating in values.items():
            if item not in target:
                scores[item] += similarity * rating
    return sorted(scores, key=lambda item: (-scores[item], item))

print(recommend("target"))  # ['C', 'D']
ratings["new"] = {}
print(recommend("new"))  # []

These are ranking scores, not normalized predicted ratings or calibrated relevance probabilities. The example does not implement matrix factorization or a distributed recommender. A new user with no ratings has no similarity signal here and receives an empty result; choose an explicit fallback before serving a real product.

Evaluate candidate generation and ranking on appropriately separated interactions. Do not claim recommendation quality from two neighbors agreeing with an invented fixture.

10. Mask terminal states in a Q-learning update

A true terminal transition has no future value to bootstrap. The following arithmetic example updates one state-action value twice so the difference is visible.

q = {"state": [0.0, 0.0], "next": [2.0, 4.0]}

def update(state, action, reward, next_state, terminated, alpha=0.5, gamma=0.9):
    future = 0.0 if terminated else max(q[next_state])
    target = reward + gamma * future
    q[state][action] += alpha * (target - q[state][action])
    return q[state][action]

print(f"{update('state', 0, 1.0, 'next', False):.2f}")  # 2.30
print(f"{update('state', 0, 1.0, None, True):.2f}")     # 1.65

The Gymnasium training tutorial describes the temporal-difference update and distinguishes termination from truncation. An external time limit is not automatically the same as a terminal state; define the learning task and bootstrap policy accordingly.

This snippet is an update rule, not a trained CartPole or Blackjack agent. A full experiment also needs an environment, exploration, episode handling, repeated training, and separate evaluation. Continue with Q-learning targets and terminal states.

Use the examples as evidence, not benchmarks

For each block, first predict the output, then run it and change one assumption. Try a constant regression feature, unknown classifier words, no positive predictions, a zero-area box, an empty retrieval query, a malformed SQL proposal, a cold-start user, and a terminal transition with no next state.

Keep correctness checks separate from quality evaluation. A passing arithmetic fixture shows that the illustrated behavior occurred. Production claims need representative data, independent evaluation, permissions, resource bounds, and monitoring. When examples grow into file-backed datasets, Python file-size handling is a useful starting point for reasoning about byte counts and streaming boundaries.

Keep practicing


Comments (0)