Fig. 1B: the conceptual cartoon — from a society in space to a society in time
The paper's key move drawn as a two-panel partner to the programme grid (now Fig. 1A): on the left, the usual picture — contemporaries exchanging messages (multi-agent systems, one moment on the clock); on the right, the same ecosystem seen along its time axis — a pedigree in which a rare capability (gold dot) is lost under single-parent inheritance, reassembled by merging complementary parents, and re-supplied by grounding from a reality that can say no (the globe). Friendly-robot glyphs, colour-coded capability dots; build.py now stacks multiple PDFs per figure; the Introduction cites 1A and 1B in panel order. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BkRLcc18rwT2Lysu6PbG7v
This commit is contained in:
parent
9c0210ba93
commit
b417a66c23
13 changed files with 136 additions and 12 deletions
|
|
@ -16,11 +16,12 @@ The generations are coupled through data as well as through weights. Successive
|
|||
|
||||
Training each generation of a model on the previous generation's output degrades it (\emph{model collapse}): rare capabilities vanish first, and the lineage drifts toward its own most common behaviour (21). That degradation is, mathematically, \emph{genetic drift}, the loss of rare variants that any finite population suffers when each generation is a finite sample of the last --- the same sampling accident by which rare surnames vanish from small villages and rare alleles (gene variants) drift out of island populations with no selection against them. The identification has been made repeatedly and independently: for sequential inference chains before deep learning (22), for language-model text ecosystems (23), as a closed-form first-extinction law placing collapse onset at the Wright--Fisher first-extinction time (24), and in quantitative-genetic form for self-consuming diffusion models (25). A diagnosis reached so often, from such different starting points, marks population genetics as the natural mathematics of the setting, though only as its entry point: population genetics is not, at heart, a theory of decay; it is a theory of the mechanisms that maintain and build populations despite decay (immigration, recombination, selection, population structure) and of where those mechanisms reach their limits. This paper develops that fuller structure for model populations: the arc from drift through its remedies to its limit, reproductive isolation --- the point at which diverged lineages can no longer produce working offspring, biology's boundary between species --- carried as one framework from closed forms to trained networks to language models.
|
||||
|
||||
An operator of a model population faces recurring decisions for which there is no principled guidance: how much verified real data does retraining need before a lineage decays; will combining two particular models compose their abilities or damage them; can incompatibility be detected before paying for a failed merge; and when should specialists be kept separate rather than consolidated? In practice these are settled by convention and by trial-and-error search. They are also, recognisably, machine learning's oldest problem at a new scale: \emph{continual learning}, the struggle to acquire new abilities without losing old ones (26, 27), transposed from a single network to a population whose members inherit from one another. Population genetics, I will argue, prices these decisions. Table 1 summarises the correspondences on which the argument runs, and Fig. 1 maps the experimental programme built on them: the same abstractions tested at three tiers --- an exact population-genetic simulator, trained neural networks, and language models --- with the sections that follow climbing that ladder.
|
||||
An operator of a model population faces recurring decisions for which there is no principled guidance: how much verified real data does retraining need before a lineage decays; will combining two particular models compose their abilities or damage them; can incompatibility be detected before paying for a failed merge; and when should specialists be kept separate rather than consolidated? In practice these are settled by convention and by trial-and-error search. They are also, recognisably, machine learning's oldest problem at a new scale: \emph{continual learning}, the struggle to acquire new abilities without losing old ones (26, 27), transposed from a single network to a population whose members inherit from one another. Population genetics, I will argue, prices these decisions. Table 1 summarises the correspondences on which the argument runs. Fig. 1A maps the experimental programme built on them: the same abstractions tested at three tiers --- an exact population-genetic simulator, trained neural networks, and language models --- with the sections that follow climbing that ladder. Fig. 1B draws the shift of perspective the whole transfer rests on: the model ecosystem read not as a society in space, contemporaries exchanging messages, but as a society in time, generations coupled by inheritance, recombination, and immigration.
|
||||
|
||||
\begin{figure*}[p]\centering % fig1
|
||||
\includegraphics[width=\textwidth]{figs/fig1.pdf}
|
||||
\caption{The experimental programme. Each population-genetic abstraction (Table 1) is tested at up to three tiers, ordered left to right by increasing realism: an exact Wright--Fisher simulator over knowledge distributions (closed forms; bitwise-reproducible), trained neural networks measured against exact oracles (recurrent, feedforward, and variational-autoencoder generators on a synthetic mode universe, and a convolutional VAE on MNIST), and language models (LoRA specialists on Qwen bases at 0.5B and 7B, scored by an exact-match verifier). Colour separates the two categories: the population-genetic theory tier in blue, the two AI-model tiers in oranges. The same abstractions are carried across all three. Rows are the framework's mechanisms, each defined at the left margin; filled cells name the experiments run at each tier, and each carries, in its corner, the figure or table where that result is reported, so this figure doubles as a map of the paper. Each claim is tested at the cheapest tier that can falsify it, and a costlier tier is entered only where it adds a discriminating test rather than a replication: grounding at language-model scale is established in prior work (21, 30) and is not re-run; epistasis and the society skip the middle tier, whose distinctive value (exact oracles) does not bear on those operator-level questions; and the society at language-model scale is the integrative experiment this paper specifies but does not run --- its stated gap.}\label{fig1}
|
||||
\includegraphics[width=\textwidth]{figs/fig1a.pdf}\\[6pt]
|
||||
\includegraphics[width=\textwidth]{figs/fig1b.pdf}
|
||||
\caption{(A) The experimental programme. Each population-genetic abstraction (Table 1) is tested at up to three tiers, ordered left to right by increasing realism: an exact Wright--Fisher simulator over knowledge distributions (closed forms; bitwise-reproducible), trained neural networks measured against exact oracles (recurrent, feedforward, and variational-autoencoder generators on a synthetic mode universe, and a convolutional VAE on MNIST), and language models (LoRA specialists on Qwen bases at 0.5B and 7B, scored by an exact-match verifier). Colour separates the two categories: the population-genetic theory tier in blue, the two AI-model tiers in oranges. The same abstractions are carried across all three. Rows are the framework's mechanisms, each defined at the left margin; filled cells name the experiments run at each tier, and each carries, in its corner, the figure or table where that result is reported, so this figure doubles as a map of the paper. Each claim is tested at the cheapest tier that can falsify it, and a costlier tier is entered only where it adds a discriminating test rather than a replication: grounding at language-model scale is established in prior work (21, 30) and is not re-run; epistasis and the society skip the middle tier, whose distinctive value (exact oracles) does not bear on those operator-level questions; and the society at language-model scale is the integrative experiment this paper specifies but does not run --- its stated gap. (B) The conceptual basis of the transfer. A population of models is usually pictured as a society in space: contemporaries exchanging messages. The couplings this paper studies run instead between generations --- training on model output (inheritance), weight-space merging (recombination), verified real data entering each generation (immigration from reality) --- a society in time, the object population genetics was built to describe. Dots mark capabilities: the rare one (gold) is lost under single-parent inheritance, reassembled by merging complementary parents, and re-supplied by grounding.}\label{fig1}
|
||||
\end{figure*}
|
||||
|
||||
\section*{The minimal model, and where its exactness ends}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ OUT = HERE / "body.tex"
|
|||
|
||||
# figure name -> (single publication PDF from make_figs.py, caption)
|
||||
FIGURES: dict[str, tuple[list[str], str]] = {
|
||||
"fig1": (["paper/pnas/figs/fig1.pdf"],
|
||||
"The experimental programme. Each population-genetic abstraction (Table 1) is tested at up "
|
||||
"fig1": (["paper/pnas/figs/fig1a.pdf", "paper/pnas/figs/fig1b.pdf"],
|
||||
"(A) The experimental programme. Each population-genetic abstraction (Table 1) is tested at up "
|
||||
"to three tiers, ordered left to right by increasing realism: an exact "
|
||||
"Wright--Fisher simulator over knowledge distributions (closed forms; bitwise-reproducible), "
|
||||
"trained neural networks measured against exact oracles (recurrent, feedforward, and "
|
||||
|
|
@ -38,7 +38,14 @@ FIGURES: dict[str, tuple[list[str], str]] = {
|
|||
"(21, 30) and is not re-run; epistasis and the society skip the middle tier, whose "
|
||||
"distinctive value (exact oracles) does not bear on those operator-level questions; and the "
|
||||
"society at language-model scale is the integrative experiment this paper specifies but does "
|
||||
"not run --- its stated gap."),
|
||||
"not run --- its stated gap. (B) The conceptual basis of the transfer. A population of models "
|
||||
"is usually pictured as a society in space: contemporaries exchanging messages. The couplings "
|
||||
"this paper studies run instead between generations --- training on model output "
|
||||
"(inheritance), weight-space merging (recombination), verified real data entering each "
|
||||
"generation (immigration from reality) --- a society in time, the object population genetics "
|
||||
"was built to describe. Dots mark capabilities: the rare one (gold) is lost under "
|
||||
"single-parent inheritance, reassembled by merging complementary parents, and re-supplied by "
|
||||
"grounding."),
|
||||
"fig2": (["paper/pnas/figs/fig2.pdf"],
|
||||
"Grounding is immigration. (A) Stationary diversity against the grounding fraction in the "
|
||||
"minimal inheritance model: simulation (points, 95\\% CI) matches the exact immigration--drift "
|
||||
|
|
@ -133,9 +140,10 @@ def inline(s: str) -> str:
|
|||
|
||||
def figure_env(name: str) -> str:
|
||||
pdfs, caption = FIGURES[name]
|
||||
src = ROOT / pdfs[0]
|
||||
incl = "\\\\[6pt]\n".join(f"\\includegraphics[width=\\textwidth]{{figs/{(ROOT / p).name}}}"
|
||||
for p in pdfs)
|
||||
lines = [f"\\begin{{figure*}}[p]\\centering % {name}",
|
||||
f"\\includegraphics[width=\\textwidth]{{figs/{src.name}}}",
|
||||
incl,
|
||||
f"\\caption{{{caption}}}\\label{{{name}}}",
|
||||
"\\end{figure*}"]
|
||||
return "\n".join(lines)
|
||||
|
|
|
|||
Binary file not shown.
BIN
paper/pnas/figs/fig1b.pdf
Normal file
BIN
paper/pnas/figs/fig1b.pdf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -92,9 +92,12 @@ convention and by trial-and-error search. They are also, recognisably, machine l
|
|||
problem at a new scale: *continual learning*, the struggle to acquire new abilities without losing old
|
||||
ones (26, 27), transposed from a single network to a population whose members inherit from one
|
||||
another. Population genetics, I will argue, prices these decisions. Table 1 summarises the
|
||||
correspondences on which the argument runs, and Fig. 1 maps the experimental programme built on
|
||||
correspondences on which the argument runs. Fig. 1A maps the experimental programme built on
|
||||
them: the same abstractions tested at three tiers — an exact population-genetic simulator,
|
||||
trained neural networks, and language models — with the sections that follow climbing that ladder.
|
||||
Fig. 1B draws the shift of perspective the whole transfer rests on: the model ecosystem read not as
|
||||
a society in space, contemporaries exchanging messages, but as a society in time, generations
|
||||
coupled by inheritance, recombination, and immigration.
|
||||
|
||||
*(FIG:fig1)*
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -43,7 +43,7 @@ def save(fig, name):
|
|||
|
||||
|
||||
# ---------------------------------------------------------------- fig 1: experimental programme
|
||||
def fig1():
|
||||
def fig1a():
|
||||
from matplotlib.patches import FancyBboxPatch
|
||||
|
||||
TIERS = [
|
||||
|
|
@ -92,6 +92,7 @@ def fig1():
|
|||
ax.set_axis_off()
|
||||
ax.set_xlim(0, 1)
|
||||
ax.set_ylim(0, 1)
|
||||
ax.text(0.0, 0.995, "A", fontsize=13, fontweight="bold", va="top")
|
||||
x0, gap, sep = 0.205, 0.008, 0.02 # sep: extra gutter between theory and the AI pair
|
||||
cw = (1.0 - x0 - sep) / 3
|
||||
xs = [x0, x0 + cw + sep, x0 + 2 * cw + sep]
|
||||
|
|
@ -145,7 +146,118 @@ def fig1():
|
|||
if tags[j2]: # where the result lives (the ToC role)
|
||||
ax.text(x + cw - gap - 0.005, y0 + 0.006, tags[j2], ha="right", va="bottom",
|
||||
fontsize=5.6, style="italic", color=edge)
|
||||
save(fig, "fig1")
|
||||
save(fig, "fig1a")
|
||||
|
||||
# ------------------------------------------------------- fig 1B: society in space -> in time
|
||||
BLUE, GREEN, GOLD = "#4292c6", "#41ab5d", "#d4a017"
|
||||
|
||||
|
||||
def _robot(ax, x, y, fc="#e8eef5", dots=(), lost=(), w=0.52, h=0.52):
|
||||
"""A friendly robot head with capability dots beneath (gold = the rare skill)."""
|
||||
from matplotlib.patches import Arc, Circle, FancyBboxPatch
|
||||
|
||||
ax.add_patch(FancyBboxPatch((x - w / 2, y - h / 2), w, h, boxstyle="round,pad=0.03",
|
||||
fc=fc, ec="#445", lw=1.1))
|
||||
for dx in (-w / 4.5, w / 4.5):
|
||||
ax.add_patch(Circle((x + dx, y + h / 7), w / 9, fc="white", ec="#445", lw=0.7))
|
||||
ax.add_patch(Circle((x + dx, y + h / 7), w / 24, fc="#334", ec="none"))
|
||||
ax.add_patch(Arc((x, y - h / 7), w / 2.4, h / 3.2, theta1=195, theta2=345, ec="#445", lw=1.0))
|
||||
ax.plot([x, x], [y + h / 2 + 0.03, y + h / 2 + 0.10], color="#445", lw=1.1)
|
||||
ax.add_patch(Circle((x, y + h / 2 + 0.135), 0.04, fc="#445", ec="none"))
|
||||
marks = [(c, False) for c in dots] + [(c, True) for c in lost]
|
||||
n = len(marks)
|
||||
for i, (c, is_lost) in enumerate(marks):
|
||||
cx = x + (i - (n - 1) / 2) * 0.19
|
||||
cy = y - h / 2 - 0.17
|
||||
if is_lost:
|
||||
ax.add_patch(Circle((cx, cy), 0.07, fc="white", ec=c, lw=0.9, ls=(0, (2, 2))))
|
||||
ax.text(cx, cy - 0.005, "\u00d7", ha="center", va="center", fontsize=6, color=c)
|
||||
else:
|
||||
ax.add_patch(Circle((cx, cy), 0.07, fc=c, ec="none"))
|
||||
|
||||
|
||||
def fig1b():
|
||||
from matplotlib.patches import Circle, FancyArrowPatch
|
||||
|
||||
W, H = 11.4, 4.1
|
||||
fig, ax = plt.subplots(figsize=(W, H))
|
||||
ax.set_xlim(0, W)
|
||||
ax.set_ylim(0, H)
|
||||
ax.set_aspect("equal")
|
||||
ax.set_axis_off()
|
||||
ax.text(0.05, H - 0.05, "B", fontsize=13, fontweight="bold", va="top")
|
||||
|
||||
def arrow(p, q, color="#666", lw=1.2, style="-|>", shrink=2.0, ls="-"):
|
||||
ax.add_patch(FancyArrowPatch(p, q, arrowstyle=style, mutation_scale=9, color=color,
|
||||
lw=lw, linestyle=ls, shrinkA=shrink, shrinkB=shrink))
|
||||
|
||||
# ---------------- left: a society in space (contemporaries exchanging messages)
|
||||
cx, cy, r = 2.45, 2.45, 0.95
|
||||
dotsets = [(BLUE, GOLD), (GREEN,), (BLUE, GREEN), (GOLD, GREEN), (BLUE,)]
|
||||
pos = []
|
||||
for i, ds in enumerate(dotsets):
|
||||
a = np.pi / 2 + i * 2 * np.pi / 5
|
||||
x, y = cx + r * np.cos(a) * 1.4, cy + r * np.sin(a) * 0.95
|
||||
pos.append((x, y))
|
||||
_robot(ax, x, y, dots=ds)
|
||||
for i, j2 in [(0, 2), (1, 3), (2, 4), (0, 3), (1, 4)]:
|
||||
arrow(pos[i], pos[j2], color="#99a", lw=0.9, style="<|-|>", shrink=26, ls=(0, (4, 2)))
|
||||
ax.text(pos[0][0] + 0.60, pos[0][1] + 0.30, "\u2026", fontsize=9, ha="center",
|
||||
bbox=dict(boxstyle="round,pad=0.22", fc="white", ec="#99a", lw=0.8))
|
||||
clk = (0.55, 3.72)
|
||||
ax.add_patch(Circle(clk, 0.21, fc="white", ec="#445", lw=1.1))
|
||||
ax.plot([clk[0], clk[0]], [clk[1], clk[1] + 0.13], color="#445", lw=1.0)
|
||||
ax.plot([clk[0], clk[0] + 0.10], [clk[1], clk[1]], color="#445", lw=1.0)
|
||||
ax.text(clk[0], clk[1] - 0.34, "one moment", ha="center", fontsize=6, style="italic",
|
||||
color="#555")
|
||||
ax.text(2.45, 0.48, "a society in space", ha="center", fontsize=9.5, fontweight="bold")
|
||||
ax.text(2.45, 0.22, "contemporaries exchanging messages \u2014 multi-agent systems, agent economies",
|
||||
ha="center", fontsize=6.6, style="italic", color="#555")
|
||||
|
||||
# ---------------- middle: the shift of perspective
|
||||
arrow((4.62, 2.30), (5.90, 2.30), color="#445", lw=2.0, style="-|>")
|
||||
ax.text(5.26, 2.44, "the same ecosystem,\nseen along its time axis", ha="center",
|
||||
va="bottom", fontsize=7, style="italic", color="#334", linespacing=1.3)
|
||||
|
||||
# ---------------- right: a society in time (a pedigree)
|
||||
axx = 6.55
|
||||
arrow((axx, 3.90), (axx, 0.90), color="#445", lw=1.3)
|
||||
for gy, lab in [(3.45, "gen 0"), (2.20, "gen 1"), (1.05, "gen 2")]:
|
||||
ax.text(axx - 0.12, gy, lab, ha="right", va="center", fontsize=6.5, color="#445")
|
||||
|
||||
p1, p2 = (7.55, 3.45), (9.25, 3.45)
|
||||
c1, c2 = (7.15, 2.20), (8.65, 2.20)
|
||||
g2 = (8.65, 1.05)
|
||||
_robot(ax, *p1, dots=(BLUE, GOLD))
|
||||
_robot(ax, *p2, dots=(GREEN, BLUE))
|
||||
_robot(ax, *c1, dots=(BLUE,), lost=(GOLD,))
|
||||
_robot(ax, *c2, dots=(BLUE, GREEN, GOLD))
|
||||
_robot(ax, *g2, dots=(BLUE, GREEN, GOLD))
|
||||
arrow((7.42, 2.94), (7.20, 2.66), color="#666")
|
||||
ax.text(7.02, 2.86, "inherit\n(train on output)", ha="right", fontsize=6, style="italic",
|
||||
color="#555", linespacing=1.2)
|
||||
arrow((7.72, 2.94), (8.48, 2.66), color="#666")
|
||||
arrow((9.15, 2.94), (8.84, 2.66), color="#666")
|
||||
ax.text(8.68, 2.92, "merge (sex)", ha="center", fontsize=6, style="italic", color="#555")
|
||||
ax.text(7.15, 1.50, "rare skill lost", ha="center", fontsize=5.8, style="italic",
|
||||
color="#a33")
|
||||
arrow((8.65, 1.64), (8.65, 1.46), color="#666")
|
||||
globe = (10.55, 1.60)
|
||||
ax.add_patch(Circle(globe, 0.30, fc="#eaf4fb", ec="#2c7fb8", lw=1.2))
|
||||
from matplotlib.patches import Arc as _Arc
|
||||
ax.add_patch(_Arc(globe, 0.30, 0.60, theta1=90, theta2=270, ec="#2c7fb8", lw=0.8))
|
||||
ax.add_patch(_Arc(globe, 0.30, 0.60, theta1=270, theta2=90, ec="#2c7fb8", lw=0.8))
|
||||
ax.plot([globe[0] - 0.30, globe[0] + 0.30], [globe[1], globe[1]], color="#2c7fb8", lw=0.8)
|
||||
ax.text(globe[0], globe[1] - 0.42, "reality\n(verifier)", ha="center", va="top", fontsize=6,
|
||||
color="#2c7fb8", linespacing=1.2)
|
||||
arrow((10.26, 1.45), (9.02, 1.15), color="#2c7fb8", lw=1.2)
|
||||
ax.text(9.72, 1.52, "ground\n(immigrate)", ha="center", fontsize=6, style="italic",
|
||||
color="#2c7fb8", linespacing=1.2)
|
||||
ax.text(8.55, 0.48, "a society in time", ha="center", fontsize=9.5, fontweight="bold")
|
||||
ax.text(8.55, 0.22, "generations coupled by inheritance, recombination, and grounding \u2014 "
|
||||
"where population genetics applies",
|
||||
ha="center", fontsize=6.6, style="italic", color="#555")
|
||||
save(fig, "fig1b")
|
||||
|
||||
# ---------------------------------------------------------------- fig 2: grounding + MNIST
|
||||
def fig2():
|
||||
|
|
@ -488,5 +600,5 @@ def fig3():
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for f in (fig1, fig2, fig3, fig4, fig5, fig6, fig7):
|
||||
for f in (fig1a, fig1b, fig2, fig3, fig4, fig5, fig6, fig7):
|
||||
f()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue