Repository navigation
feat(tasks): describe the answer's form with TaskContext.response - #210
Merged
Merged
Conversation
TaskContext gains response: Choice (optionally with implied probabilities or reported confidence), Scale for ratings, and Text for written answers. Each response phrases its directive and parses replies; answers arrive on addressed behavior:<kind> channels that each part declares, so compatibility is decided before compute. BrainScoreModel gains reply and likelihood parts, and a ridge readout gives ratings for feature models. task_type values keep their meaning on plain behavior.
Contributor
Author
|
Folded into #209, which now carries these commits. |
KartikP
added a commit
that referenced
this pull request
Nov 4, 2026
…ive input forms (#209) The experiment API for UMI v2 in core: a study reads like a lab protocol (describe the task, bring in a subject, attach recorders, run once), and every subject sees each stimulus in its own native form. This folds in what was #210. Paired with KartikP/brainscore-unified#54; the two are pinned to each other (`umi-v2.yml` here, `install/peer-revisions.json` there). ## What changes **1. Behavior and activity from one presentation.** A `BrainScoreModel` session may ask for `behavior` and `neural:<region>` together. The regions are recorded during the same forward pass that produced the answer, and each stimulus is shown once. - **Rule 4.5:** the session's `task_context` decides what the subject does; the requested outputs decide what is measured. - `task_type='passive'` means plain viewing. - A session that asks only for `neural:*` under a real task records during that task. - A task configured by an earlier session no longer captures a later neural-only session. That used to return the behavioral assembly. - **`TaskContext.readout_fit`:** `'intact'` fits a readout with every active change undone, then tests the changed network with it. The default `'current'` is unchanged. - **Labelling:** recordings made during a task carry `attrs['recording']`. Plain neural sessions are unchanged. **2. The form of the answer: `TaskContext.response`** (`brainscore_core.responses`): | Response | Channels | | --- | --- | | `Choice(labels)` | `behavior:choice` | | `Choice(..., probabilities=True)` | `+ behavior:probabilities` (implied) | | `Choice(..., confidence=Scale(...))` | `+ behavior:confidence` (reported) | | `Scale(low, high, step)` | `behavior:rating` | | `Text()` | `behavior:text` | - **Reported or implied:** a reported answer is a parsed reply; an implied one is read from a readout or from likelihoods. Each result says which in `attrs['measure']`. - **Benchmarks own wording and parsing:** each response builds its own directive and parses replies. An unparseable reply is recorded as invalid. - **New parts:** `reply(prompt)` and `likelihood(prompt, candidates)`. `RatingRegressor` gives ratings for readout models. - **Compatibility from declarations:** each part declares which `behavior:<kind>` channels it can give, so mismatches are N/A before any compute. - **Existing `task_type` values** keep their path on plain `behavior`. **3. Native input forms and ordered trials.** - **Rule 5.9:** `vision` is addressable as `vision:still` (declared by a `vision`-keyed extractor) and `vision:clip` (declared by a `video`-keyed one). A subject with both takes each stimulus in its own form, so a clip goes to the clip extractor. - **Order:** a trial is shown as a `Prompt` in this order: the instruction, the stimulus parts in `TaskContext.present` order, then the response's directive. A stimulus with several parts and no declared order fails before any compute. - **Clips keep their time steps** when recorded during a task. **Rule 9.3 reason:** `TaskContext` is a session setting. Separating the task from the form of its answer needed `task_type` to become optional, which adding a field alone couldn't do. Every existing call keeps its meaning. Out of scope, each raising a clear error before any compute: - recording during likelihood scoring; - recording with probabilities alongside a generated answer; - composite regions, streaming and more than one time bin during a task. ## Verification - **Unit tests:** - `test_joint_behavior_neural.py` (10 tests): one presentation, prompt-dependent activity, `readout_fit`, neural-only sessions under a task, plain sessions unchanged. - `test_task_responses.py` (12 tests): parsing, confidence, ratings from a reply and a readout, text, likelihood and readout probabilities, N/A cases, trial order. - `test_visual_forms.py` (6 tests): form declarations, dispatch by form, clip time steps during a task. - **Full core suite:** 1172 passed. The 8 failures fail the same way on unchanged v2 in my environment: plugin and conda tests break on a space in the checkout path, and one test needs a database. - **Real weights:** Qwen3.6-27B on 4×A10G. - **One presentation:** 8 images × 2 conditions made 16 `generate` calls. - **Recording during the answer:** IT matched passive viewing to within bf16 rounding. - **Likelihood choice:** 8/8 correct, with probability 0.97 to 0.99 on the right colour. - **Reported confidence:** valid, and recorded during the answer. - **Rating:** circles 7, squares 1. - **Text:** faithful descriptions. - **Clips:** - stills come back as `(presentation, neuroid)`, and clips as `(presentation, time_bin, neuroid)`: three 1-second bins for a 3-second clip at 2 fps; - a moving ball changes the activity across time (0.132), and a static clip does not change it at all (0.000); - "Which way does the ball move?", asked with the text before the video: 8/8 correct by reply, and 8/8 by likelihood with probability 0.994 to 0.996 on the right direction; - IT recorded during the answer keeps its time bins and is identical to passive viewing (max abs diff 0.0); the language recording is one vector per clip.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #209: merge that first, then retarget this PR to
unified-model-interface-v2.TaskContext.task_typemixed what a subject is asked to do with the form its answer takes. Ratings, reported confidence and written answers had no place in it. This addsTaskContext.response, built from the types in the newbrainscore_core.responses:task_type='passive'Choice(labels)behavior:choiceChoice(labels, probabilities=True)+ behavior:probabilitiesChoice(labels, confidence=Scale(1, 5, 1))+ behavior:confidenceScale(low, high, step)behavior:ratingText()behavior:textReported or implied. A reported answer is the model's reply, parsed. An implied measure is read from the model's internals: a readout, or a language model's likelihoods, the same kind of link Futrell2018 uses when it reads reading times from surprisal. Every result says which in
attrs['measure'].Prompt wording and parsing belong to the benchmark (rule 11.3). Each response builds its own directive and parses replies, and a benchmark can subclass it to change either. An unparseable reply is recorded as invalid, with
answer_valid,raw_answerandparse_error(rule 6.2).New
BrainScoreModelparts:reply(stimulus_row, prompt) -> str: the raw reply.likelihood(stimulus_row, prompt, candidates) -> {candidate: log p}: scores each candidate reply without generating.RatingRegressor, a ridge readout, gives ratings for feature models.Paths:
prefer_pathaccepts'generation','likelihood','readout'or'auto', tried in that order. One benchmark can still take both kinds of model for choices, ratings and implied probabilities. Reported confidence and written answers need a model that replies.Compatibility.
behavioris now an addressable catalog family. Each part declares whichbehavior:<kind>channels it can give:answer: choice;reply: choice, rating, confidence, text;likelihood: choice, probabilities.So, for example, a written-answer benchmark is N/A for a readout-only model before any compute (rule 5.3).
Existing calls are unchanged.
task_type='label'/'classification'/'probabilities'keep their path on plainbehavior, so existing scores don't move. Two tests that pinout_channelsnow also list the addressed channels that readout models truthfully declare.Rule 9.3 reason.
TaskContextis a session setting in the contract. Adding a field alone couldn't separate the task from the form of the answer without also makingtask_typeoptional. Embodied tasks stay on their own path.Recording during these tasks uses #209's single presentation:
Recording during likelihood scoring, or together with probabilities alongside a generated answer, needs extra forward passes. Both raise a clear error before any compute.
Verification
tests/test_task_responses.py(10 tests) covers:behaviorrequests;TaskContextvalidation.chat_parts(feat(experiments): record every kind of answer; chat reply and likelihood parts KartikP/brainscore-unified#55):