← Back to index
AI·Jul 2, 2026·14 min read

Demos: the million-persona model that called Korea's local elections

We built a synthetic electorate — one million personas, one sovereign Korean LLM, a two-dimensional ideology space — and locked its forecast for the June 2026 nationwide local elections before the vote. It called 15 of 16 governor races and 192 of 223 mayoral races, with zero opinion polls anywhere in the pipeline. The method, written out with the actual math — plus the one structural flaw that almost sank it, and the fix.

A two-dimensional map of a synthetic Korean electorate

On June 3rd, 2026, Korea held its 9th nationwide simultaneous local elections — sixteen metropolitan governorships, two hundred twenty-three mayoral and county races, decided in one day. A week earlier, on May 27th at 10:00 UTC, our four-person team committed a set of directional predictions to an append-only directory in a public git repository (commit 070dc22), flagged the two races we considered most likely to be wrong, and stopped touching the model.

When the returns came in: 15 of 16 governor races correct (94%), and 192 of 223 mayoral races correct (86%). The model never saw an opinion poll. Its entire input was a synthetic population, verified results of past elections, and one Korean-built 11.5B-parameter language model running on a single GPU.

The system is called Demos — δῆμος, the people. This post is the method, written out with the math, because the interesting parts don't survive translation into prose alone. It is also, in the second half, an honest account of the one structural flaw our own backtests exposed — the model was blind to time — and the layer that fixed it.

First, the people

Everything starts from NVIDIA's Nemotron-Personas-Korea dataset: one million synthetic Korean personas, statistically calibrated to census and administrative distributions (KOSIS, court name registries, health-insurance demographics), resolved down to the 시·군·구 — the municipal district. Each persona is a small biography: age, occupation, family, region, and a paragraph of texture. No real individual appears anywhere; there is no PII to leak because there are no persons.

There is also, by design, no politics. The dataset carries no partisanship field, no ideology score, nothing to aggregate into a vote. That absence is the entire technical problem:

Given a synthetic citizen with no political labels, infer a distribution over how they would vote — and be able to show your work.

Don't ask the model who they'd vote for

The obvious approach — hand the persona to an LLM and ask "progressive or conservative?" — fails in a characteristic way: mode collapse. The model develops house opinions about entire demographic strata and assigns them wholesale. A classifier is exactly the wrong shape for this job.

So we never ask that question. Instead the LLM (KT Mi:dm 2.0, one of Korea's sovereign models — we restricted ourselves to Korean-built LLMs only, and treated the constraint as a feature) is used as a feature extractor. For each persona it scores seven narrow behavioral signals, each grounded in Korean political-science literature: economic anxiety, institutional trust, regional identity, generational consciousness, anti-establishment sentiment, and the salience of progressive and conservative issue clusters. Each signal comes back as a score sks_k, a confidence ckc_k, and a Korean-language evidence string quoting the persona.

Low-confidence extractions shouldn't move anything, so every signal is shrunk toward ignorance before it's used:

s~k=cksk+(1ck)12.\tilde{s}_k = c_k\, s_k + (1 - c_k)\cdot \tfrac{1}{2} .

A deterministic mapper — pure function, explicit weight matrix, no LLM — turns the seven shrunk signals into a four-way political posterior

π=(πprog, πcent, πcons, πnone),π=1,\pi = (\pi_{\text{prog}},\ \pi_{\text{cent}},\ \pi_{\text{cons}},\ \pi_{\text{none}}), \qquad \textstyle\sum \pi = 1,

plus an establishment-affinity score. The posterior is blended with a regional prior, because a persona is not a free-floating individual — a 60-year-old shopkeeper in Daegu and one in Gwangju start from very different baselines:

πi=0.35br(i)+0.65f(s~i),\pi_i = 0.35\, b_{r(i)} + 0.65\, f(\tilde{\mathbf{s}}_i),

where brb_r is the province-level lean baseline. Below the province, a second correction patches in district-level structure: for each 시·군·구 we compute a lean offset from verified past results (recent elections, plus the 2025 presidential sub-region margins where available),

Δdistrict=clamp(0.00833w, ±0.50),w=0.60Δrecent+0.40εΔpres,\Delta_{\text{district}} = \mathrm{clamp}\big(0.00833 \cdot w,\ \pm 0.50\big), \qquad w = 0.60\,\Delta_{\text{recent}} + 0.40\,\varepsilon\,\Delta_{\text{pres}},

and add it to the persona's posterior. Two hundred twenty-nine district overrides, every one of them traceable to a specific verified election result.

The extraction ran on a single NVIDIA H100 at a sustained 13–14 LLM calls per second with 128 workers; a full 16-race metropolitan run took about 72 minutes. Every posterior is cached, keyed by (persona, model, version) — 293,610 of them by election week — so re-runs are CPU-only and cheap. That cache matters for what comes later.

An electorate in two dimensions

Each persona now becomes a point on a map. The x-axis is ideology, the y-axis is establishment affinity:

xi=πconsπprog[1,+1],yi[0,1],x_i = \pi_{\text{cons}} - \pi_{\text{prog}} \in [-1, +1], \qquad y_i \in [0, 1],

with per-persona Gaussian jitter (σx=0.15\sigma_x = 0.15, σy=0.10\sigma_y = 0.10, seeded — every run reproducible) so ten thousand personas don't stack into a lattice. Candidates live on the same map, placed by profile: party position, plus documented biography.

A persona's distance to each candidate is then adjusted by three multiplicative layers, each with a deliberately narrow range:

dij=zicj2Aj(ri)regional affinity[0.78,1]Djincumbency×0.96Hjpersonal vote[0.92,1].d_{ij} = \big\lVert z_i - c_j \big\rVert_2 \cdot \underbrace{A_j(r_i)}_{\text{regional affinity} \in [0.78,\,1]} \cdot \underbrace{D_j}_{\text{incumbency} \times 0.96} \cdot \underbrace{H_j}_{\text{personal vote} \in [0.92,\,1]} .

Regional affinity is the big one — in Korean elections, where a candidate is from matters enormously, and the literature says so. Incumbency and personal vote are small nudges, bounded so they can tilt a close race but never overturn the spatial structure.

Non-voters are modeled, not ignored. A persona abstains when it is both politically disengaged and far from everyone on offer:

πnone0.40andminjdij>0.60    abstain.\pi_{\text{none}} \geq 0.40 \quad\text{and}\quad \min_j d_{ij} > 0.60 \;\Longrightarrow\; \text{abstain}.

Everyone else votes by a sharp softmax over squared adjusted distance:

P(ij)=exp ⁣(dij2/T)kexp ⁣(dik2/T),T=0.10.P(i \to j) = \frac{\exp\!\big(-d_{ij}^2 / T\big)}{\sum_k \exp\!\big(-d_{ik}^2 / T\big)}, \qquad T = 0.10 .

T=0.10T = 0.10 makes proximity nearly decisive — this is a vote, not a preference survey. Race-level shares aggregate the sampled votes, blend in a candidate recognition floor (you cannot receive votes from people who have never heard of you), and apply a bounded government-alignment shift scaled by a per-province climate elasticity ε\varepsilon — 1.00 in the swingy capital region, falling to 0.50 in the most politically inertial provinces:

vj=renorm(max(0, 0.80vjraw+0.20recogj+gjεr)).v_j = \mathrm{renorm}\Big( \max\big(0,\ 0.80\, v_j^{\text{raw}} + 0.20\, \mathrm{recog}_j + g_j\,\varepsilon_r \big) \Big).

Every hyperparameter above — all 91 of them — is frozen in a versioned manifest before any prediction is generated. Sample sizes are population-proportional, N=clamp(pop/1000, 1500, 15000)N = \mathrm{clamp}(\text{pop}/1000,\ 1500,\ 15000), and cutting the sample moved shares by at most 1.28pp in our sensitivity checks. The demographic means are robust; that's what a million-persona calibrated population buys you.

Why it should have failed

Here is the part a promotional writeup would skip.

Before predicting anything real, we backtested. On the 2024 National Assembly election — in-sample, since its results helped calibrate the district priors — the spatial model called 153 of 174 constituencies (87.9%), mean absolute error 8.9pp. Encouraging. Then we ran it against the 2022 local elections, fully held out, and got a much more educational answer: 70.6% on governorships, 63.3% on mayoral races — and in the capital region's mayoral tier, a collapse to roughly 30%.

The diagnosis was clean, and worse than any single number: the model was time-invariant. Demographics and regional structure change on the scale of decades, so a model built only from them predicts every election the same way. 2022 was a conservative wave year; our synthetic electorate, anchored to long-run structure, could not feel it. It didn't fail randomly — it failed precisely where the political weather diverged from the demographic climate, which in a wave year is everywhere that matters.

A structure-only model of elections is a floor, not a forecast. We wrote that sentence in the failure report and then had to earn the fix.

The fix: anchor to the weather

The tempting fix is opinion polls. We refused them — not out of purism, but because "persona simulation that secretly regresses onto polls" is epistemically empty. If the system works, it must work from verifiable public record. So the canonical forecast uses zero polls; they were quarantined into a held-out evaluation set and two clearly-labeled comparison variants that were never scored as the model.

Instead, the final layer anchors the simulated electorate to the most recent verified elections. For each district we build a margin target from the 2024 general election, the swing observed in the 2025 presidential election, and a honeymoon term for the new administration:

m=m2024+0.5Δ2025+H,H=+10pp,m^{*} = m_{2024} + 0.5\,\Delta_{2025} + H, \qquad H = +10\text{pp},

with HH calibrated on the 2022 cycle — the previous time a fresh administration faced local elections — not chosen to taste. Then, rather than editing any race's output directly, we solve for a single global shift Δx\Delta x of the electorate along the ideology axis such that the realized simulation reproduces the target margin:

find Δx s.t.   margin(Δx)=m,\text{find } \Delta x \text{ s.t. } \; \mathrm{margin}\big(\Delta x\big) = m^{*},

by bisection — the margin is monotone in Δx\Delta x, so this is honest root finding, not tuning. The personas, their posteriors, and the whole spatial mechanism stay frozen; the only thing that moves is where the climate puts the crowd. The uncorrected board is preserved in the repository alongside the canonical one, so anyone can score both and measure exactly what the climate layer contributed.

(One more disclosure, because it belongs here: we also built a news-exposure layer over a 113,800-article corpus to model climate shifts continuously. It wasn't validated in time, so it was excluded from the final forecast — and we published a signed note saying it was excluded, dated before the election. Unused machinery you stay quiet about is how post-hoc stories start.)

Receipts before results

Forecasting elections is the rare AI evaluation with a ground truth that arrives on a known date and cannot be argued with. That only means something if you make yourself falsifiable in advance, so the discipline was as much of the build as the math:

June 3rd

The canonical forecast — persona layer frozen, climate anchor on, zero polls — scored:

Governors: 1516=93.8%,Mayors: 192223=86.1%,overall 207239=86.6%.\textbf{Governors: } \frac{15}{16} = 93.8\%, \qquad \textbf{Mayors: } \frac{192}{223} = 86.1\%, \qquad \text{overall } \frac{207}{239} = 86.6\%.

Two comparisons put that in place. First, against our own held-out baseline: the structure-only model managed 63.3% on the equivalent mayoral tier in 2022. If that were still the true hit rate, the chance of getting 192 or more of 223 right is

z  =  192223×0.633223×0.633×0.367    7.1,z \;=\; \frac{192 - 223 \times 0.633}{\sqrt{223 \times 0.633 \times 0.367}} \;\approx\; 7.1,

about seven standard deviations — with the honest caveat that race outcomes correlate through the national climate, so treat this as an illustration of effect size, not a literal p-value. The improvement is the climate layer, and it is not noise.

Second, against the polls we never used: scored on the final pre-election polls available for 148 mayoral races, the model agreed with the poll leader only 67% of the time. Scored on the actual returns, it was right 86% of the time. A meaningful share of our "disagreements with the polls" were the polls being wrong. We would not have dared write that sentence before June 3rd; the whole point of pre-registration is that now we're allowed to.

And the limits, stated with the same confidence as the wins: one governor race wrong, thirty-one mayoral races wrong. The spatial model resolves the main two-party axis, so races fought off that axis — one-party-dominant regions where the real contest was major party versus splinter or independent — carry direction-only confidence, and we labeled them as such in advance. The fourteen concurrent National Assembly by-elections were out of scope for this version. The model is a floor plus a weather report, and there are kinds of weather it has not yet been tested against.

What I keep coming back to

What's next

Demos was always more than an election model — the forecast is the falsifiable validation of a persona engine whose real job is simulating citizen reactions to policy before the policy ships, with provenance attached to every output. The election was the one day the simulation could be graded against reality at national scale, in public, with the answer key held by thirty-five million voters. It passed.

The repository — pre-registration files, hyperparameter manifests, the uncorrected boards next to the canonical ones, the disclosure notes — is the argument. We built it so that the only way to believe us is to check, and the only thing you'll find when you check is that you didn't have to trust us at any step.

That was the design goal all along.

Credits

Demos is joint work. Shoutout to my co-authors — Yongsung Kim, Seokjin Hong, and Seungjae Lee — who carried this from a whiteboard sketch to a system that could be graded by thirty-five million voters. Every number in this post is theirs as much as mine.

← All writingTop ↑