Artemis

Data science platform for Artemis II 13-month calendar image selection using CLIP embeddings, statistical modeling, and multi-objective optimization.

64 lessons

Project page

Tags

Lessons

Lesson 012: Bayesian Beta-Binomial Smoothing

The Artemis vote system shows 50 random images per ballot and asks voters to pick 5 favorites. With 500 ballots across 12,217 images, most images are shown only 1-2 times. A raw selection rate of "1 out of 1 shown = 100%" is meaningless — it tells you nothing about whether the image is actually pref...

Artemis 2026-05-24 algorithms

Lesson 013: Elo Rating for Image Comparison

The Artemis pairwise voting mode shows two images side by side and asks "which is better?" This produces binary outcomes (winner / loser) for specific pairs, not absolute ratings. We need to convert these relative comparisons into a single continuous strength score per image that can be combined wit...

Artemis 2026-05-24 algorithms

Lesson 014: Bradley-Terry-Luce and When to Skip It

We have pairwise comparison data (image A beats image B) and want the best possible strength estimates. Bradley-Terry-Luce (BTL) is the textbook model for this — it's more principled than Elo. But with 2,000 comparisons across 12,217 images, we chose to skip BTL entirely. This lesson explains what B...

Artemis 2026-05-24 algorithms

Lesson 015: Borda Count for Ranked Voting

The Artemis category voting mode asks voters to rank their top 3 images within a category. We need to convert these partial rankings into numeric scores that can be aggregated across voters and combined with batch and pairwise preference signals.

Artemis 2026-05-24 algorithms

Lesson 016: Krippendorff's Alpha for Sparse Agreement

We want to measure whether voters agree on which images are good. With 100 voters and 12,217 images, the voter-image matrix is >98% missing — most voters never saw most images. Standard agreement metrics require complete matrices. We need a reliability measure that handles extreme sparsity.

Artemis 2026-05-24 algorithms

Lesson 017: Composite Scoring with Heterogeneous Signals

We have three different types of preference data — batch selection rates, Elo ratings from pairwise comparisons, and Borda scores from category rankings. Each covers a different subset of images, uses a different scale, and captures a different aspect of preference. Most images have data from only o...

Artemis 2026-05-24 algorithms

Lesson 018: Run-ID Partitioned Scoring

The scoring pipeline will be re-run as new vote data arrives, as scoring methods are tuned, or as bugs are fixed. Each run produces a full set of scores for all 12,217 images. If each run overwrites the previous scores, we lose the ability to compare methods, audit changes, or roll back to a known-g...

Artemis 2026-05-24 algorithms

Lesson 019: NULL as Honest Missing Data

Several columns in the scoring output have no meaningful value for most images. Only ~200 images have Elo scores (from 2,000 pairwise votes). Only ~150 images have Borda scores (from 250 category rankings). The BTL model wasn't run at all. Fleiss' kappa and Kendall's W can't be computed with incompl...

Artemis 2026-05-24 implementation

Lesson 022: Heuristic Month-Fit Scoring Without Text Metadata

When images lack text metadata (titles, descriptions, captions), month or season suitability can still be approximated from visual features alone — color temperature, brightness, contrast, and content flags. The signal is coarse (3-4 seasonal buckets, not 13 distinct months) but sufficient to preven...

Artemis 2026-05-24 algorithms

Lesson 024: Hungarian Algorithm for Optimal Assignment

When you need to assign N items to N slots where each item-slot pair has a fitness score, the Hungarian algorithm gives the provably optimal assignment in O(N^3) time. For small N (≤50), it runs in microseconds and eliminates the need for greedy heuristics, manual tuning, or iterative search. Use sc...

Artemis 2026-05-24 algorithms

Lesson 025: Multiple Selection Methods as Baselines

When building an optimizer, always generate multiple candidate solutions using different methods — including at least one naive baseline. The baseline proves the optimizer adds value. The alternatives expose the trade-off frontier. Without baselines, you can't distinguish "good optimization" from "e...

Artemis 2026-05-24 implementation

Lesson 026: Formalizing De Facto Dependencies

A dependency that's imported in production code but missing from the package manifest is a time bomb. It works on the developer's machine (where the package was installed for something else) and fails on fresh installs, CI, or new team members. Audit imports against declared dependencies whenever ad...

Artemis 2026-05-24 implementation

Lesson 028: Chi-Squared Tests for Bias Detection at Small Scale

We planted known biases in synthetic vote data — 10% of voters had position bias (preferring earlier-displayed images), 20% had visual-drama bias (preferring dramatic images). We need statistical tests that can detect these biases with only 100 voters and 500 ballots, without requiring heavy statist...

Artemis 2026-05-24 algorithms

Lesson 029: Ground-Truth Recovery as Optimizer Validation

We have a calendar optimizer that selects 13 images from 12,217 using a weighted objective function (popularity, diversity, month-fit, cover-fit, redundancy penalty). The optimizer reports an objective score, but a high score doesn't prove the optimizer is selecting the *right* images — it could be...

Artemis 2026-05-24 testing

Lesson 030: Reliability Delta as Noise Measurement

We know that 20% of synthetic voters are intentionally noisy (10% position-biased, 10% random). We compute Krippendorff's alpha on all voters and get a moderate value (~0.52). But how much of the low agreement is caused by these noisy voters vs. genuine preference diversity among neutral voters? We...

Artemis 2026-05-24 implementation

Lesson 031: Read-Only DB Connections for Web Layers

When an embedded database (DuckDB, SQLite) serves both a batch pipeline and an interactive web app, the web layer should open the database in read-only mode. This avoids writer-lock conflicts entirely and makes the architecture self-documenting: the web app *cannot* mutate the warehouse, by construc...

Artemis 2026-05-24 data-engineering

Lesson 032: Startup Cache for Interactive Scoring

When an interactive web app needs sub-100ms responses from a scoring function that depends on large lookup tables, load those tables into memory at startup rather than querying the database per request. The cache size is bounded (you know exactly what's in the warehouse), startup cost is a one-time...

Artemis 2026-05-24 algorithms

Lesson 035: Design System Portability via Tokens

A design system built on CSS custom properties (design tokens) can be shared across completely independent frontends — static HTML pages, vanilla JS SPAs, embedded widgets — by copying two files. The tokens provide visual consistency without requiring a shared component library, a build system, or a...

Artemis 2026-05-24 architecture

Lesson 036: Linter Rules vs. Framework Idioms

When a linter rule flags code that follows a framework's official pattern, suppress the rule per-line with `noqa` rather than restructuring the code. Linter rules encode general best practices; framework idioms encode domain-specific patterns that intentionally violate those practices. Restructuring...

Artemis 2026-05-24 implementation

Lesson 042: Lift as the Primary Bias Detection Metric

Block-aware statistics need a metric that answers: "does this voting block select images with attribute X more than expected?" Raw selection counts don't work because blocks have different sizes. Rate differences (block rate - global rate) are hard to interpret when base rates vary widely. The metri...

Artemis 2026-05-24 implementation

Lesson 045: Embedding-Based Deduplication for Image Collection Curation

When working with a large image collection from an automated source, assume near-duplicates dominate the pool until proven otherwise. Embedding cosine similarity with connected-component grouping reduces a collection to its unique members in minutes, but the threshold choice dramatically affects the...

Artemis 2026-05-24 algorithms
ai

Lesson 048: Greedy Max-Min Diversity Selection

To select k items that maximally represent the diversity within a group, iteratively pick the item most distant from all already-selected items. This greedy max-min approach is O(n×k), produces near-optimal diversity in practice, and avoids the NP-hard max-dispersion problem entirely.

Artemis 2026-05-24 algorithms

Lesson 049: Drag-and-Drop as the Simplest Viable Interaction

When the user's mental model is "put this thing in that slot," drag-and-drop is less code and more intuitive than alternatives like dropdowns, search dialogs, or multi-step wizards. The key is spatial co-visibility: the source pool and target slots must be on screen simultaneously so the user can se...

Artemis 2026-05-24 frontend
ui

Lesson 050: Connected Components for Transitive Deduplication

When deduplicating by pairwise similarity, use graph connected components to group items — not naive pair-based merging. Pairwise similarity is not transitive in theory (A~B and B~C doesn't guarantee A~C), but for near-duplicates in practice, transitivity holds and connected components correctly gro...

Artemis 2026-05-24 algorithms

Lesson 053: Audit-First Design

Before writing any code for a new feature, produce a written audit of the existing codebase: what exists, what can be reused, where new code slots in. The audit document prevents reimplementing existing functionality and identifies the exact extension points — saving more time than it costs to write...

Artemis 2026-05-24 process

Lesson 054: Phased Autonomous Execution Plans

Breaking large projects into numbered, independently shippable phases — each with explicit entry criteria, exit criteria, and a commit checkpoint — transforms ambitious multi-session work from a coordination problem into a queue of self-contained tasks. The plan file is both the work instruction and...

Artemis 2026-05-24 process

Lesson 057: Test-Gated Commits at Scale

Gate every commit on a passing test suite, not on "the feature looks done." With 1,500+ tests across a project, the suite catches regressions that visual inspection misses — wrong column names, broken imports, type mismatches, off-by-one errors. The test suite is the contract for "this commit is saf...

Artemis 2026-05-24 testing

Lesson 058: DuckDB Cursor-Per-Request for Concurrent Web Handlers

When serving DuckDB through a multi-threaded web framework (FastAPI/uvicorn), never share a single connection object across concurrent request handlers. Instead, call `conn.cursor()` to create a per-request cursor. DuckDB's Python driver does not support concurrent queries on the same connection fro...

Artemis 2026-05-24 data-engineering

Lesson 061: Centralize Project Metadata to Prevent Count Drift

When the same project-level number (image count, cluster count, lesson count) appears in multiple frontend modules, centralize it in a single metadata object. Better still, fetch live counts from the API at render time and use the centralized constant only as a fallback. Hardcoded numbers scattered...

Artemis 2026-05-24 implementation

Lesson 062: A Guided Reviewer Path for Portfolio Projects

Add a numbered "review this project in N minutes" path to the homepage of any portfolio project or case study. Without explicit guidance, reviewers wander randomly through pages and miss the strongest parts of the work. A curated path ensures every reviewer sees the same narrative arc, regardless of...

Artemis 2026-05-24 implementation