The model is the easy half

Third-year Applied Data Science & AI at Breda University of Applied Sciences, on the Data Scientist and Data Engineer tracks. Machine learning, data engineering, and the infrastructure that puts models to work.

Filipp Lotsmanov
BrabantHack 2026 DEMCON Deep Tech, won HERE Technologies hackathon challenge track NPEC Netherlands Plant Eco-phenotyping Centre Airflow → Azure ML champion-gated retraining BUas GPA 8.5 / 10 Now calibration & selective prediction for HTR

Every number names what it was measured on.

Eight figures — held-out sets, hidden leaderboards, one cross-validation, one memory ceiling.

0.8371F1Root tissue segmentation, U-Net20,512 held-out test patches, split at source-image level
0.7199IoUSame model, same run20,512 held-out test patches
10.7%sMAPERoot-length estimationKaggle private leaderboard
0.626IoU · winning entryOff-screen pedestrian localisationHidden leaderboard test set
0.646mmMean pipette positioning error50 targets on an Opentrons OT-2
5.2MB peak heapStreaming ETL, memory flat in file size2.3 GB / 27.6M records, insert-bound
0.8162macro F1 · 0.9202 accuracySeven-class emotion classification62,862 held-out samples, DeBERTa-v3-base
0.81F1 · 5-fold CVErroneous map turn restrictionsStratified cross-validation, HERE probe traces

autonomous-root-inoculation

Root length, read off the segmentation.

sMAPE on the Kaggle private leaderboard, drawn on the full 0–40% scale. Lower is better.

0% 10 20 30 40% 10.7%

Each root is segmented, skeletonised and measured along its skeleton. sMAPE is the average percentage gap between that length and the leaderboard’s ground truth.

go-etl-pipeline

Memory that ignores the file.

Bytes read against bytes resident. Three orders of magnitude apart, so the axis is logarithmic.

1 MB 10 MB 100 MB 1 GB 10 GB 5.2 MB 2.3 GB heap holds flat while the file streams past

Log scale, because on a linear one 5.2 MB is a third of a pixel beside 2.3 GB. The CSV is consumed row by row, so the heap never learns how large the file is.

Six projects, end to end.

Built for external clients or to hackathon deadlines; two are solo work.

Schematic — not model output. Plate imagery is NPEC's; this draws the three stages the pipeline runs.

01 / 08 · Team capstone, five people

Two gates decide what reaches production.

github.com/filipp-lotsmanov/root-inoculation-mlops

NPEC · Airflow · Azure ML · FastAPI · U-Net (resnet34)

0.8371 F1, 0.7199 IoU on 20,512 held-out test patches — orchestrated by six Airflow DAGs, with an 85% coverage floor gating every build.

How it works

Researchers correct predictions in the UI. A daily DAG merges the corrections into training data while keeping the test set frozen, retrains, and puts the candidate through two independent gates.

Scope
Airflow orchestration and the Azure ML job layer, the feedback flywheel, the champion–challenger promotion gate, and most of the cv-pipeline package — inference, training loop, validation, weights handling and CLI. One inference path serves all three delivery forms, so the CLI, the HTTP API and the Azure ML scoring script cannot drift apart.
Limitation
The university-provisioned Azure and on-premise environments are decommissioned; the local Compose stack is the reproducible path.
  1. 01Correctionsresearchers flag predictions in the UI
  2. 02Retrainmerged in, test set frozen
  3. 03F1 gateclears the threshold on held-out
  4. 04Registrythe candidate is versioned
  5. 05Champion gatemust beat what is already serving
  6. 06Trafficonly now does it serve

Registered, never promotedclears 03, loses 05

Passing an offline threshold is not the same as being better than production. A candidate can enter the registry and never take traffic.
The researcher UI’s metrics tab: five landmarks detected, mask confidence 0.980, model version unet-v1, and per-landmark confidence between 0.997 and 1.000, with the drift-alert threshold marked on the confidence scale.

The same thresholds the backend monitors on. 0.50 is the model’s decision midpoint; confidence held under 0.60 for an hour raises a drift alert. The interface shows researchers the same confidence the alert is computed from.

An Opentrons OT-2 pipetting robot with a petri dish loaded.

The Opentrons OT-2 the pipeline targets.

02 / 08 · Individual

Segmentation that drives a pipette to 0.646 mm.

github.com/filipp-lotsmanov/autonomous-root-inoculation

U-Net · skeletonisation · PID · Opentrons OT-2

10.7% sMAPE on the Kaggle private leaderboard; 0.646 mm mean positioning across 50 targets.

How it works

Segmentation finds root tips, skeletonisation measures length, an affine transform maps pixels to robot coordinates, and a PID controller drives the pipette. A PPO agent was trained alongside it as an alternative controller.

Development
An earlier version also located the dish and separated overlapping roots on the fly. Every plate in this dataset is photographed the same way, so those steps added error rather than removing it; taking them out brought sMAPE from 37.6% to 10.7%.
Limitation
PID and PPO were measured on different simulators, so their error figures are not directly comparable.
Prediction panel: side Left at 100% confidence, direction Abstain at 50%, and a box drawn left of the road scene, outside the image.
Screen recording of the Shadow Detection web app.

Predicted box spans x −186.3 to −114.0 px — entirely left of zero, because the person is not in the picture.

03 / 08 · Team of three BrabantHack 2026 winner

Find the person who isn't in the frame.

github.com/filipp-lotsmanov/shadow-detection

DEMCON Deep Tech · with Oleksii Krasnoshtanov and Danil Sysenko

Geometric-feature ablation

0 0.25 0.5 0.75 1.0 ≈0.57 ≈0.62 +0.05 IoU from 19 geometric features

Both points sit on the full 0–1 IoU scale rather than a zoomed one, so the difference reads at its true size.

IoU 0.626 on the hidden leaderboard — the winning submission.

How it works

Only the shadow reaches the image. The x-distribution is bimodal, so the target is decomposed: a classifier picks the side, a regressor learns offsets from that edge, and 19 geometric features fuse with the ResNet-50 embedding.

Scope
The three-head architecture, the 19 features, flip-aware augmentation and the TTA inference path.
Limitation
The direction head abstains on every input, and training data is entirely synthetic.

04 / 08 · Group project, external client

Nine stages from a Russian video to an emotion timeline.

github.com/filipp-lotsmanov/nlp-emotion-classification

Whisper · NLLB · XLM-R · DeBERTa-v3 · DistilRoBERTa · Docker

A Russian-language YouTube URL goes in; a seven-class emotion timeline comes out. Whisper transcribes the audio, NLLB translates it, and an ensemble scores each segment — a Russian model on the original text, DistilRoBERTa and a fine-tuned DeBERTa-v3-base on the translation — behind a REST API, a web interface and a Docker deployment.

DeBERTa-v3-base emotion head · 62,862 held-out samples

0 0.5 1.0 macro F1 · 0.8162 accuracy · 0.9202
  • 419,180training samples, DeBERTa-v3-base fine-tuned
  • 62,862held-out samples behind both figures

Macro F1 leads because the seven classes are far from evenly sized, and it weights each class equally instead of rewarding the largest one. Accuracy is shown for reference.

The app’s timeline view: coloured emotion bands across a 50-minute clip, a final label distribution led by Neutral at 78.9%, and a per-model table comparing rubert-tiny2, DistilRoBERTa and DeBERTa.

The web interface on a 50-minute clip — 185 segments, 78.9% of them neutral, with each ensemble model’s label counts listed separately.

My stages were transcription and scene detection, plus a transformer encoder–decoder written from scratch.

How it works
The nine stages
Download, scene detection, audio normalisation, Whisper transcription, semantic segmentation, NLLB translation, valence–arousal regression, seven-class emotion classification — the last two in both languages — then the timeline plot and a consolidated CSV.
Scope
The transcription and scene-detection stages, and a transformer encoder–decoder built from scratch rather than fine-tuned.
Revival
Bringing the archive back to a runnable state meant pinning the upstream valence–arousal checkpoint by digest and retraining the DeBERTa-v3-base head from the committed corpus.
Limitation
The arousal head of the valence–arousal regressor scores AUC 0.5734 on the project’s own classes, so the significance gate that depends on it is the pipeline’s weakest stage. The English models also read translated text, so their predictions carry any translation error with them.

05 / 08 · Team of three · HERE Technologies challenge

Which turn restrictions on the map are wrong?

XGBoost · GeoPandas · Shapely · GPS probe traces

A map says you cannot turn left here. Sometimes the map is wrong. Given GPS traces of cars that actually drove the junction, a classifier separates genuine restrictions from erroneous ones, with features built in the Belgian national grid so distances come out in metres rather than degrees.

0.81 F1 under stratified 5-fold cross-validation.

How it works

The source was five proprietary HERE datasets. To work on them safely the team generated zero-match synthetic replicas — same shape and statistics, no row traceable to a real one.

The interesting failure
Some synthetic training sets had to be discarded: labels had leaked into route-derived violation features, so a model could score well by reading the answer instead of the road.
Limitation
0.81 F1 is cross-validated, not measured on a held-out set the model never influenced, and the source data cannot be published.

06 / 08 · Personal

2.3 GB through a 4 MB heap, and an answer for every missing row.

github.com/filipp-lotsmanov/go-etl-pipeline

Go · channels · worker pool · PostgreSQL

A staged pipeline wired by channels, reading row by row so memory is independent of file size. Every row is attributed to the stage that consumed it, and a run whose ledger does not balance exits non-zero rather than reporting success.

Where 500,000 rows went · one run, to scale

read parse validate dedupe
  • 321,979inserted · 64.40%
  • 138,001duplicates skipped · 27.60%
  • 26,642failed validation · 5.33%
  • 13,378unparseable · 2.68%

Every row is attributed to the stage that consumed it. The four outcomes sum to exactly 500,000, and the validation slice balances too: 13,420 bad distance + 6,557 negative fare + 6,665 bad passenger count = 26,642. A run whose ledger does not balance exits non-zero.

Terminal output: heap-in-use lines between 2.79 and 4.75 MB, then the load-complete ledger, exit code 0.

A 500,000-row test run; the 2.3 GB benchmark above is a separate run. Heap holds between 2.79 and 4.75 MB throughout. Reading the throughput field precisely: records_per_sec=13525 counts inserted rows (321,979 ÷ 23.806 s); rows read per second was 21,003.

Flat 2.8–5.2 MB heap across a 2.3 GB, 27.6M-record file at roughly 41,000 records/sec, with 4.76× enrichment throughput from a goroutine worker pool on 14 cores.

How it works
Scope
The staged pipeline, the row ledger, a goroutine worker pool sized by GOMAXPROCS, and live-Postgres tests that gate the Docker build in CI.
Limitation
4.76× is the shipped figure on 14 cores. The higher 5.7× in the benchmarks needs a source constant raised, so reproducing it means rebuilding.
The sign-language app predicting the letter B at 96.8 per cent confidence from a live webcam frame.

B at 96.8%, live in the browser.

07 / 08 · Team of three

Two models, one socket, real time in the browser.

github.com/filipp-lotsmanov/sign-language

MediaPipe · FastAPI · WebSocket · ResidualMLP + BiLSTM

Dual-model routing over one socket, with 21 landmarks extracted client-side per frame.

How it works

MediaPipe extracts 21 hand landmarks client-side; frames stream to FastAPI over WebSocket. Static and dynamic letters dispatch to different models: a ResidualMLP over 25 static classes — the 24 still letters plus a nonsense class, so the app can refuse input that is not a letter rather than guessing — and a BiLSTM over 30-frame sequences for J and Z.

Limitation
Augmentation happens before the split, so near-duplicate frames leak across train, validation and test.

21 landmarks, extracted client-side, then routed by letter type.

63-dim vector · one frameResidualMLP25 static classes — 24 letters and a nonsense class
30-frame sequenceBidirectional LSTMJ and Z — the two that move
Two of the twenty-six letters move. Those two get a sequence model; the other twenty-four get a single-frame one, alongside a twenty-fifth class for input that is not a letter at all.
A green GitHub Actions run of the CV build: the steps asserting font binaries, failing on box warnings and verifying PDF invariants all pass.

CI verifies the PDF still parses for an ATS.

08 / 08 · Personal

A CV that verifies itself before it ships.

github.com/filipp-lotsmanov/resume

LaTeX · latexmk · GitHub Actions · pdfplumber

How it works

CI asserts the Charter Type 1 binaries are installed, fails on any overfull box, then re-extracts the PDF with pdfplumber and asserts it still parses into the sections an ATS looks for.

Limitation
Charter's 2.77 pt interword gap sits under pdfplumber's 3.0 pt default, so some parsers may merge adjacent words.

Stack

Grouped by what it is used for.

Languages

PythonGoSQL

Deep learning

PyTorchTensorFlowHugging Face TransformersKerastorchvisionStable Baselines3

Computer vision

OpenCVMediaPipeU-Netalbumentationsscikit-imagescipy.ndimage

Classical ML

scikit-learnXGBooststatsmodelspandasNumPy

NLP & speech

SentencePiece/BPENLLBfaster-whisperLangChainFAISS

Serving & optimisation

FastAPIWebSocketTorchScriptquantisationmixed-precisionOllamaNVIDIA Jetson

Orchestration & MLOps

AirflowAzure MLMLflowClearMLHydraPrometheus

Infrastructure

DockerComposeKubernetesAzureGitHub ActionsPortainerPostgreSQLAlembic

Geospatial & BI

GeoPandasShapelyPower BI

Tooling

uvruffpytestVitestSphinxLaTeX

Currently

  • Calibration and selective prediction on handwritten text recognition.

    When a vision-language model is wrong about a historical manuscript, does it know?

  • A retrieval-augmented generation system, deployed on AWS.

    The retrieval pipeline, the generation step, and the cloud infrastructure that serves them.

  • BSc Applied Data Science & Artificial Intelligence.

    Breda University of Applied Sciences · 2024 – July 2028 · GPA 8.5 / 10

  • Also learning

    Go for online data streaming and processing, and DVC for data and model versioning.