Skip to content

Score a classifier against a fixed set of occurrences, and show where it does well - #1491

Closed
mohamedelabbas1996 wants to merge 6 commits into
RolnickLab:mainfrom
mohamedelabbas1996:feat/evaluate-classifiers
Closed

mohamedelabbas1996 wants to merge 6 commits into
RolnickLab:mainfrom
mohamedelabbas1996:feat/evaluate-classifiers

Conversation

@mohamedelabbas1996

Copy link
Copy Markdown
Contributor

Built on #1490. GitHub cannot base a pull request on a branch in a fork, so the
commits from #1490 (occurrence sets) appear here too. Review only the last commit —
feat(ml): score an algorithm against a fixed set of occurrences. Once #1490 merges this
rebases and the diff shrinks to that commit alone.

Summary

A project runs more than one classifier over time and has no way to say whether a newer one
is better. Accuracy quoted from different data is not a comparison, so scoring here is
always against an occurrence set: the same occurrences, every time, for every model.

A run compares an algorithm's stored predictions with what people identified. No images are
opened and no model runs, so it is a pass over rows and returns at once.

List of Changes

# Change (effect) How (implementation)
1 A model can be scored on a fixed set evaluate_algorithm job type; ami/ml/evaluation.py compares stored classifications with identifications
2 Only what people identified counts Filtered on a non-withdrawn Identification, not on determination — that falls back to the model's own prediction, which would have a model agreeing with itself
3 A species is matched by taxon, not by label text A category map label is the name the model was trained under; comparing it as text silently drops species whose stored name differs
4 The score says what it was measured over species_in_set and species_predictable in the result and the job log: 1.00 over six of sixty species reads the same as 1.00 over all of them
5 Scores are stored, not recomputed AlgorithmEvaluation (one per algorithm and set) and TaxonEvaluation (one per species); a re-run replaces rather than appends
6 An algorithm page lists how it has scored evaluations on AlgorithmSerializer
7 A species page shows how each model did on it algorithm_performance on TaxonSerializer
8 A taxa list names its best model best_model on TaxaListSerializer, annotated in the viewset so a page costs one query
9 Only ML data managers can start a scoring run run_evaluate_algorithm_job, with an object-level backfill for existing projects

Related Issues

Built on #1490 (occurrence sets), which this scores against. Split out of #1407, where this
began.

Detailed Description

  • Ranking by the per-species average, not the plain share. Trap data is long-tailed, so a
    model that only handles the common species would otherwise win every list.
  • Scoping. Algorithms and taxa are shared across the platform; evaluation sets are not.
    Every read is scoped to the project, or to what the user can see when the route carries no
    project id — the algorithm detail route does not.
  • ami/ml/migrations/0029 collides by number with Store feature vectors from any model for every detection, indexed for the ways they are read #1462. Whichever lands second gets
    renumbered; nothing else about them overlaps.
  • No interface yet. The numbers are on the API; the screens that read them are not in
    this PR.

Testing

ami.ml.test_evaluation — 30 tests: scoring, the self-scoring and label-matching guards,
coverage reporting, cross-project exposure, the per-species and best-model reads, and a
pinned query count for a page of taxa lists.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CnKz4AS4iFrYj1GrgkZQbq

mohamedelabbas1996 and others added 6 commits October 8, 2026 14:19
A project needs the same occurrences again later: comparing two classifiers only
means something if both saw the same rows, and a review pass wants the list it
started from. A filter answers differently as data arrives, so the membership is
stored rather than described.

An OccurrenceSet holds its occurrences and the projects it belongs to. A set with
no project is global and is offered everywhere, following how TaxaList already
treats a list with no project. Membership is decided when the set is created and
nothing adds to or removes from one afterwards, because anything recorded against
a set was measured on exactly those occurrences.

Creating, renaming and deleting are gated on new project permissions, held by the
roles that already curate a project's data. A global set has no single project to
check against, so it cannot be edited through the API at all.

The occurrence list takes an occurrence_set filter, and the sets are offered as
choices the same way capture sets are.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CnKz4AS4iFrYj1GrgkZQbq
Adds the occurrence set to the occurrence filter panel, picked from the set
choices endpoint the same way a capture set is. A set is only useful if you can
look at what is in it, and this is where someone reviewing one starts.

The field is carried over from other views like the existing filters, so arriving
with a set already chosen shows it in the panel where it can be cleared.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CnKz4AS4iFrYj1GrgkZQbq
Someone filtering the occurrence list to the rows they care about had no way to
keep that selection. Selecting occurrences now offers saving them as a set, next
to the identification actions already there.

The action does not change any occurrence, so it is not behind update rights on
them; the endpoint gates it on the project's own permission instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CnKz4AS4iFrYj1GrgkZQbq
Registering a filter in the shared list is not enough for it to appear: the
occurrences page renders one FilterControl per field it offers, and the set was
missing from that list, so the filter existed everywhere except on screen.

It sits under More filters beside the capture set, and that section now opens on
arrival when a set is already applied, as it does for the other filters there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CnKz4AS4iFrYj1GrgkZQbq
The selection bar holds identification actions and is hidden from anyone without
update rights on the occurrences, so saving a set — which changes none of them —
was unavailable to a reader who could still create one. It also sat there as an
unlabelled icon among three others.

It now sits beside Export as a labelled button, and appears only while something
is selected, since that is the only time it does anything.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CnKz4AS4iFrYj1GrgkZQbq
A project runs more than one classifier over time and has no way to say whether a
newer one is better. Accuracy quoted from different data is not a comparison, so
scoring here is always against an occurrence set: the same occurrences, every
time, for every model.

A run compares an algorithm's stored predictions with what people identified.
No images are opened and no model runs, so it is a pass over rows and returns at
once. Only occurrences a person identified are counted: determination falls back
to the model's own prediction when nobody has identified one, and scoring those
would have the model agreeing with itself.

The species a model can be asked about are resolved to taxa rather than compared
as label text, because a category map label is the name the model was trained
under and need not be the name the taxon is stored under here. The result also
carries what it was measured over, since an accuracy over six of a model's sixty
species reads the same as one over all of them.

The scores surface in three places: an algorithm's own list of evaluations, a
species page showing how each scored model did on it, and the best model for a
taxa list, ranked on the per-species average rather than the plain share because
trap data is long-tailed.

Stacked on the occurrence sets branch, which the scoring runs against.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CnKz4AS4iFrYj1GrgkZQbq
@netlify

netlify Bot commented Oct 8, 2026

Copy link
Copy Markdown

👷 Deploy request for antenna-ssec pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit 28c0994

@netlify

netlify Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for antenna-preview ready!

Name Link
🔨 Latest commit 28c0994
🔍 Latest deploy log https://app.netlify.com/projects/antenna-preview/deploys/6ac7ef12948f0700090db58b
😎 Deploy Preview https://deploy-preview-1491--antenna-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 52 (🔴 down 13 from production)
Accessibility: 81 (🔴 down 8 from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 92 (no change from production)
PWA: 80 (no change from production)
View the detailed breakdown and full score reports
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mohamedelabbas1996

Copy link
Copy Markdown
Contributor Author

Reopening from a branch in this repository so it can be based on the occurrence sets branch directly. Replaced by the PR linked below.

@mohamedelabbas1996
mohamedelabbas1996 deleted the feat/evaluate-classifiers branch October 8, 2026 19:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant