Accuracy is not a nuisance to be maximised. It determines which parameters are identifiable at all, and the answer is different for the two things a GRT study usually reports.
| Per-dimension accuracy | Overall (4AFC) | Independence | Separability |
|---|---|---|---|
| 60–65% | ~39% | 83% | 58% |
| 70–75% | ~52% | 80% | 73% |
| 75–80% | ~60% | 78% | 79% |
| 80–85% | ~68% | 75% | 82% |
| 95–100% | ~94% | 68% | 74% |
Classification accuracy for each construct, measured on 72,000 simulated observers. Independence is best identified around 60–65% per dimension; separability around 80–85%. At the accuracy that best serves independence, separability is recovered barely above chance.
So there is no single right difficulty. If you need both, the crossing point — about 75–80% per dimension, or 60% overall — is the compromise, and it is where the familiar advice to run near 60% correct comes from. Note which scale that number is on: 60% overall is about 77% per dimension, and running at 60% per dimension would be a poor design for separability.
-
Decide what you are measuring
Pick the target before collecting anything: independence → aim low, separability → aim high, both → aim at the crossing point. The tolerance is tighter for small experiments: with 200+ trials per stimulus the correlation error is nearly flat below 75% accuracy, but under 20 trials it climbs across the whole range.
-
Run a start-up block and measure where you landed
Accuracy read off the matrix, not from the fitted parameters — it is the thing you can see during a pilot.
import numpy as np # Mean of the two marginal accuracies. # Canonical order is A1B1, A1B2, A2B1, A2B2 -- B varies fastest, so dimension A # is correct when stimulus and response share a half, and B when they share parity. def per_dimension_accuracy(counts): c = np.asarray(counts, float) n = c.sum() a = sum(c[s, r] for s in range(4) for r in range(4) if s // 2 == r // 2) b = sum(c[s, r] for s in range(4) for r in range(4) if s % 2 == r % 2) return 0.5 * (a / n + b / n) acc = per_dimension_accuracy(startup_counts) print(f"landed at {acc:.0%} per dimension")# Mean of the two marginal accuracies. # Canonical order is A1B1, A1B2, A2B1, A2B2 -- B varies fastest. per_dimension_accuracy <- function(counts) { n <- sum(counts); a <- 0; b <- 0 for (s in 1:4) for (r in 1:4) { if ((s - 1) %/% 2 == (r - 1) %/% 2) a <- a + counts[s, r] if ((s - 1) %% 2 == (r - 1) %% 2) b <- b + counts[s, r] } 0.5 * (a / n + b / n) } acc <- per_dimension_accuracy(startup_counts) message(sprintf("landed at %.0f%% per dimension", 100 * acc)) -
Adjust the separation and repeat
A plain proportional step is enough. The point is not a clever algorithm; it is that you now know which way to move and when to stop.
TARGET, TOL, GAIN = 0.77, 0.03, 0.6 # aim at the crossing point separation = initial_separation for block in range(6): counts = run_block(separation, n_trials=48) acc = per_dimension_accuracy(counts) if abs(acc - TARGET) <= TOL: break # too accurate -> move the levels closer together, and vice versa separation *= 1 - GAIN * (acc - TARGET) / TARGETTARGET <- 0.77; TOL <- 0.03; GAIN <- 0.6 # aim at the crossing point separation <- initial_separation for (block in 1:6) { counts <- run_block(separation, n_trials = 48) acc <- per_dimension_accuracy(counts) if (abs(acc - TARGET) <= TOL) break # too accurate -> move the levels closer together, and vice versa separation <- separation * (1 - GAIN * (acc - TARGET) / TARGET) } -
Use blocks long enough to be worth acting on
Accuracy measured from a short block is noisy, and steering on noise is worse than not steering. At 5–20 trials per stimulus the standard deviation of an accuracy estimate is about 0.047 — roughly a third of the width of the band you are aiming at. At 50–200 it falls to 0.015.
Blocks of 40–50 trials are a reasonable floor. Below that, expect to chase sampling error rather than the participant.
What this does and does not do
It places the observer where the question is answerable, and it assumes nothing about learning: the representation is taken as fixed, and only the sampling location is being chosen. That is a weaker and safer assumption than an adaptive training curriculum, which would additionally require the representation to move predictably in response to practice.
What it cannot do is manufacture information. If a correlation is weak and the trial budget is small, no placement recovers it — the model will correctly report a wide interval, and that is the answer.
Back to all tutorials, or see the live task, where the accuracy readout tells you in real time which side of the band you are on.