Oversight-Scaling-Laws-Statistics

Module statistique consolide - PR 4 / 4 du sub-grain #16754

Ce notebook consolide les notebooks anterieurs du meme sub-grain :

PR Notebook Contenu Section R12
1 (#17092) Oversight-Scaling-Laws-Oversight.ipynb Mesure empirique : Nim (jeu a information incomplete) §2 (base experimentale)
2 (#17099) Oversight-Scaling-Laws-Analytics.ipynb NSO close-form + double-ReLU L-BFGS-B + AIC §3 (formalisation)
3 (#17108) Oversight-Scaling-Laws-Wargames.ipynb Simulation 3 roles Defender/Attacker/Judge §5 (Wargames)
4 (ce notebook) Oversight-Scaling-Laws-Statistics.ipynb Tests statistiques + calibration + meta-analyse §3 + §4 (calibration)

Sources :

  • R12 : Engels, Baek, Kantamneni, Tegmark. Scaling Laws For Scalable Oversight. NeurIPS 2025. arXiv:2504.18530.
  • Sub-grain #16754 (T13 distillation corpus Tegmark) - EPIC #16741.

Auto-contenu : numpy + scipy.stats uniquement, pas de GPU, 100% reproductible (seed = 42).

Plan :

  1. Calibration des estimateurs (PR 1-3) sur donnees synthetiques de controle
  2. Tests statistiques pour comparer les regimes (PR 2 vs PR 3)
  3. Bootstrap + IC95 sur les metriques cles (n*, D_elo, p_success)
  4. Meta-analyse : les predictions NSO (PR 2) sont-elles compatibles avec les simulations Wargames (PR 3) ?
import numpy as np
from scipy import stats
rng = np.random.default_rng(42)
print("Setup OK - module stats Oversight R12, numpy + scipy.stats")
Setup OK - module stats Oversight R12, numpy + scipy.stats

1. Calibration des estimateurs

PR 1 a mesure l’oversight par un jeu Nim ; PR 2 a formalise par NSO close-form ; PR 3 a simule en Wargames.

Question de calibration : pour des donnees synthetiques dont on connait la vraie valeur des parametres, est-ce que les estimateurs des PRs 1-3 retournent ces valeurs ?

On genere des donnees selon un modele controle et on compare :

  • Vrai D_elo (connu) vs D_elo estime par la formule NSO inverse de PR 2
  • Vrai p_success (connu) vs p_success mesure par la simulation Wargames de PR 3
  • Vrai n* (connu) vs n* predit par NSO
def D_elo_from_data(wins, losses):
    """Conversion Elo : D_elo = -400 * log10(1/winrate - 1). winrate = wins / (wins+losses).

    Convention R12 : D_elo > 0 si le 'fort' (wins > losses) gagne.
    Pour wins=60, losses=40 : winrate=0.6 -> D_elo = -400*log10(1/0.6 - 1) = +70.4.
    """
    if wins == 0:
        return -np.inf  # le fort perd toutes les parties, D_elo -> -inf
    if losses == 0:
        return np.inf   # le fort gagne toutes les parties, D_elo -> +inf
    winrate = wins / (wins + losses)
    return -400 * np.log10(1 / winrate - 1)

def n_star_closeform(D_elo, q):
    """NSO close-form R12 : n* = log(q) / log(1 - D_elo/400).

    Renvoie NaN hors domaine :
    - q hors [0.5, 1) : probabilite cible invalide
    - D_elo <= 0 : Attacker dominant (n* <= 0 par construction, on signale)
    - D_elo >= 400 : Defender sature (log(<=0) indefini)
    - log(ratio) == 0 : singularite

    Interpretation des NaN : NSO trivial (Defender dominant -> n* < 1 -> un seul
    niveau de Judge suffit). Voir §4 du notebook pour l'analyse des regimes.
    """
    if q <= 0.5 or q >= 1:
        return np.nan
    if D_elo >= 400:
        return float("nan")  # Defender sature
    ratio = 1 - D_elo / 400
    if ratio <= 0:
        return float("nan")
    if ratio >= 1:  # D_elo <= 0
        return float("nan")  # Attacker dominant -> n* <= 0 -> NSO trivial
    val = np.log(q) / np.log(ratio)
    if not np.isfinite(val):
        return float("nan")
    return val

# Donnees de controle : Elo 200 vs 150 (D = 50), 100 parties simulees
true_D = 50
n_games = 100
# Probabilite que le fort gagne selon Elo : p = 1 / (1 + 10^(-D/400))
p_strong = 1 / (1 + 10**(-true_D / 400))
wins = sum(rng.random() < p_strong for _ in range(n_games))
losses = n_games - wins
est_D = D_elo_from_data(wins, losses)

print(f"Vrai D_elo = {true_D}")
print(f"Estime D_elo = {est_D:.1f} (sur {n_games} parties, {wins} victoires du fort)")
print(f"winrate observe = {wins/n_games:.3f} (attendu = {p_strong:.3f})")
print(f"n* predit pour q=0.8 : {n_star_closeform(true_D, 0.8):.3f}")
print(f"n* predit pour q=0.95 : {n_star_closeform(true_D, 0.95):.3f}")
Vrai D_elo = 50
Estime D_elo = 70.4 (sur 100 parties, 60 victoires du fort)
winrate observe = 0.600 (attendu = 0.571)
n* predit pour q=0.8 : 1.671
n* predit pour q=0.95 : 0.384

Lecture du résultat — l’estimation d’Elo amplifie le bruit d’échantillonnage

Le vrai écart vaut D_elo = 50, mais 100 parties ne suffisent pas à le voir : 60 victoires du fort (winrate observé 0.600 contre 0.571 attendu) donnent une estimation de 70.4. La conversion passe par log10(1/winrate - 1), dont la dérivée explose quand le winrate s’éloigne de 0.5 : ±2 points de winrate se paient ici +20 points d’Elo. La conséquence se lit sur n* : 1.671 pour q=0.8, 0.384 pour q=0.95 — l’écart de qualité exigé change le nombre de niveaux de Judge requis d’un facteur 4, donc toute erreur d’estimation sur D_elo se propage directement dans le dimensionnement de l’oversight. Les NaN renvoyés par n_star_closeform ne sont pas des bugs : ils délimitent les régimes où la question posée est triviale (cf. section 4 pour celle où elle ne l’est pas).

2. Tests statistiques : NSO vs Wargames

PR 2 predit un n* en fonction de D_elo et du seuil de qualite q. PR 3 simule un scenario Wargames et mesure p_success selon (K, d_strength).

Test : les predictions NSO de PR 2 sont-elles compatibles avec les observations Wargames de PR 3 ?

Methode : pour un meme scenario (meme K, meme qualite q), le pont Elo est plug-in — D_elo est derive du winrate Defenseur observe par la simulation, via l’inverse calibre en section 1 (D_elo_from_data) — puis on compare - p_success predit par NSO (PR 2) = 1 - (1 - q)^(1/n*) - p_success observe par simulation Wargames (PR 3)

Si les deux sont dans la meme IC95, NSO est conserve par Wargames. Sinon, on cherche le regime de desaccord.

# Scenario test : K = 10, d_strength = 0.0 (Defender transparent, regime trivial)
# PR 3 attend : p_success ~ 1.0 des la 1ere question
# PR 2 predit : si Defender est transparent, D_elo est tres negatif, donc n* est tres petit

K = 10
q = 0.95  # qualite cible
d_strength = 0.0

# Simulation PR 3 (repliquee en inline)
class Defender:
    def __init__(self, secret, K, d_strength, rng):
        self.secret = secret; self.K = K; self.d_strength = d_strength; self.rng = rng
    def answer(self, subset):
        truth = self.secret in subset
        if self.rng.random() < self.d_strength:
            return not truth
        return truth

class BayesianAttacker:
    def __init__(self, K, rng):
        self.K = K; self.rng = rng; self.posterior = np.ones(K) / K
    def best_guess(self):
        return int(np.argmax(self.posterior))
    def update(self, subset, answer):
        likelihood = np.array([1.0 if ((s in subset) == answer) else 0.0 for s in range(self.K)])
        self.posterior *= likelihood
        if self.posterior.sum() == 0:
            self.posterior = np.ones(self.K) / self.K
        else:
            self.posterior /= self.posterior.sum()
    def ask_question(self, posterior):
        sorted_idx = np.argsort(posterior)[::-1]
        cumulative = 0; half = posterior.sum() / 2; split = 1
        for i, idx in enumerate(sorted_idx):
            cumulative += posterior[idx]
            if cumulative >= half:
                split = i + 1; break
        return set(sorted_idx[:split].tolist())

def run_episode(K, d_strength, n_q, rng):
    secret = int(rng.integers(0, K))
    d = Defender(secret, K, d_strength, rng); a = BayesianAttacker(K, rng)
    for q in range(n_q):
        s = a.ask_question(a.posterior); ans = d.answer(s); a.update(s, ans)
        if a.posterior.max() > 0.99: break
    return a.best_guess() == secret, q + 1

n_eps = 500
results = [run_episode(K, d_strength, 20, rng) for _ in range(n_eps)]
p_obs = sum(s for s, _ in results) / n_eps
avg_q = np.mean([q for _, q in results])

# Pont Elo plug-in : D_elo derive du winrate DEFENSEUR observe sur ces memes
# episodes, via l'inverse calibre en cellule 3 (D_elo_from_data, fort = Defender).
# Remplace l'ancienne heuristique -400*log10(K*(1-d)+d) : son argument valait
# K*(1-d)+d >= 1 pour toute configuration (K >= 1, d dans [0,1]), donc D_elo <= 0
# partout et NSO trivial dans tous les scenarios — elle ne pouvait jamais tester D > 0.
succes = sum(s for s, _ in results)
echecs = n_eps - succes
D_pred = D_elo_from_data(echecs, succes)
n_star = n_star_closeform(D_pred, q)
if np.isfinite(n_star) and n_star > 0:
    p_pred_nso = 1 - (1 - q) ** (1 / n_star)
else:
    # n_star <= 0 ou NaN : regime trivial (1 niveau suffit / attacker dominant)
    p_pred_nso = 1.0

print(f"Scenario : K={K}, d_strength={d_strength}, q_qualite={q}")
print(f"NSO PR 2 : D_elo={D_pred:+.1f} (plug-in winrate defenseur), n*={n_star:.3f}, p_success predit={p_pred_nso:.3f}")
print(f"Wargames PR 3 : p_success observe={p_obs:.3f}, avg_questions={avg_q:.1f} (sur {n_eps} episodes)")
print(f"Ecart NSO-Wargames : {abs(p_pred_nso - p_obs):.3f}")
Scenario : K=10, d_strength=0.0, q_qualite=0.95
NSO PR 2 : D_elo=-inf (plug-in winrate defenseur), n*=nan, p_success predit=1.000
Wargames PR 3 : p_success observe=1.000, avg_questions=3.4 (sur 500 episodes)
Ecart NSO-Wargames : 0.000

Lecture du résultat — le régime transparent est un sanity check, pas une découverte

Avec un Defender qui ne se trompe jamais (d_strength = 0.0), les deux camps disent la même chose : NSO prédit p_success = 1.000, la simulation Wargames observe 1.000 sur 500 épisodes, écart nul. L’information intéressante est le coût : 3.4 questions en moyenne pour isoler le secret parmi K=10. C’est la signature de l’attaquant bayésien de la cellule — chaque question coupe la masse postérieure en deux environ, et log2(10) ≈ 3.32 : la borne informationnelle est atteinte. Retenir ce chiffre comme référence : quand d_strength passera à 0.5 en section 4, c’est le seul levier qui restera à l’attaquant.

3. Bootstrap et IC95

Les estimations ponctuelles de p_success et n* ont de l’incertitude liee a l’echantillonnage. On la quantifie par bootstrap : reechantillonnage B fois des observations et calcul des IC95.

Methode :

  1. Pour chaque scenario, on dispose de N observations (parties simulees ou mesures empiriques)
  2. On tire B echantillons bootstrap avec remise
  3. On calcule la metrique sur chaque echantillon bootstrap
  4. IC95 = percentiles 2.5 et 97.5
def bootstrap_ci(observations, stat_func, B=1000, alpha=0.05):
    """IC95 par bootstrap sur observations (liste de scalaires)."""
    n = len(observations)
    boot_stats = []
    for _ in range(B):
        sample = [observations[rng.integers(0, n)] for _ in range(n)]
        boot_stats.append(stat_func(sample))
    lo = np.percentile(boot_stats, 100 * alpha / 2)
    hi = np.percentile(boot_stats, 100 * (1 - alpha / 2))
    return np.mean(boot_stats), lo, hi

# Bootstrap sur les resultats Wargames precedents (cellule 5)
boot_results = [1.0 if s else 0.0 for s, _ in results]
boot_q = [float(q) for _, q in results]

mean_p, lo_p, hi_p = bootstrap_ci(boot_results, np.mean)
mean_q, lo_q, hi_q = bootstrap_ci(boot_q, np.mean)

print(f"Bootstrap p_success sur {n_eps} observations (B=1000) :")
print(f"  Moyenne = {mean_p:.3f}, IC95 = [{lo_p:.3f}, {hi_p:.3f}]")
print(f"Bootstrap avg_questions :")
print(f"  Moyenne = {mean_q:.2f}, IC95 = [{lo_q:.2f}, {hi_q:.2f}]")
print(f"Conclusion : Wargames converge en {mean_q:.1f} questions (precision de {hi_q-lo_q:.1f} questions)")
Bootstrap p_success sur 500 observations (B=1000) :
  Moyenne = 1.000, IC95 = [1.000, 1.000]
Bootstrap avg_questions :
  Moyenne = 3.39, IC95 = [3.35, 3.44]
Conclusion : Wargames converge en 3.4 questions (precision de 0.1 questions)

Lecture du résultat — un IC dégénéré est un diagnostic, pas une précision

Deux bootstrap, deux leçons. Sur p_success, l’IC95 vaut [1.000, 1.000] : les 500 épisodes sont tous des succès, la distribution bootstrap est un point masse — le percentile bootstrap ne peut pas dire « p est exactement 1 », seulement « aucun tirage observé ne contredit 1 ». C’est la limite connue du percentile bootstrap aux bornes de l’espace paramétrique (un IC binomial exact type Clopper-Pearson donnerait [0.9926, 1.0]). Sur avg_questions en revanche, l’IC [3.35, 3.44] est informatif : la précision de ±0.05 question sur une moyenne de 3.39 avec n=500 fixe le budget d’épisodes qu’il faudrait pour distinguer deux stratégies de questionnement séparées de 0.1 question.

4. Meta-analyse : prediction NSO vs observation Wargames

On compare maintenant sur plusieurs scenarios pour identifier le regime de desaccord.

On balaye 3 niveaux de K (petit / moyen / grand) et 2 niveaux de defense (transparent d_strength=0 / mensonges d_strength=0.5), soit 6 scenarios au total. Pour chaque scenario, on calcule :

  • p_pred_nso : prediction par NSO (PR 2)
  • p_obs_wargames : observation par simulation (PR 3)
  • |ecart| : ecart absolu

Hypothese H0 : accord parfait — la moyenne des ecarts absolus est nulle. Alternative H1 : NSO sous-estime ou surestime systematiquement.

scenarios = [
    (4, 0.0), (4, 0.5),
    (10, 0.0), (10, 0.5),
    (25, 0.0), (25, 0.5),
]

print(f"{'K':>5} {'d_str':>6} {'w_att':>6} {'D_elo':>8} {'n*':>6} {'p_NSO':>8} {'p_obs':>8} {'ecart':>8}")
print('-' * 68)
ecarts = []
for K_s, d_s in scenarios:
    obs = [run_episode(K_s, d_s, 20, rng) for _ in range(200)]
    succ = sum(s for s, _ in obs)
    p_obs = succ / len(obs)
    # Pont plug-in (cf. cellule 5) : D_elo du winrate DEFENSEUR observe,
    # via l'inverse calibre en cellule 3 — couvre D > 0, contrairement a
    # l'ancienne heuristique qui bornait D_elo <= 0 pour toute configuration.
    D_s = D_elo_from_data(len(obs) - succ, succ)
    n_s = n_star_closeform(D_s, 0.95)
    if np.isfinite(n_s) and n_s > 0:
        p_nso = 1 - (1 - 0.95) ** (1 / n_s)
    else:
        # n_s <= 0 ou NaN : regime trivial (attacker dominant, ou Defender sature)
        p_nso = 1.0
    ecart = abs(p_nso - p_obs)
    ecarts.append(ecart)
    print(f'{K_s:>5} {d_s:>6.2f} {p_obs:>6.3f} {D_s:>+8.1f} {n_s:>6.2f} {p_nso:>8.3f} {p_obs:>8.3f} {ecart:>8.3f}')

# Test statistique sur les ecarts : H0 = accord parfait (moyenne des |ecarts| = 0)
t_stat, p_value = stats.ttest_1samp(ecarts, 0)
print(f'\nTest t sur les ecarts : t={t_stat:.3f}, p={p_value:.3f}')
n_grands = sum(1 for e in ecarts if e > 0.1)
if p_value > 0.05:
    print(f'H0 non rejetee au seuil 0.05 (p={p_value:.3f}, n={len(ecarts)})')
    print(f'Mais {n_grands}/{len(ecarts)} scenarios ont un ecart > 0.10 : le test manque de puissance, l accord n est pas etabli')
else:
    print('H0 rejetee : desaccord systematique entre la prediction NSO et la simulation Wargames')
    K  d_str  w_att    D_elo     n*    p_NSO    p_obs    ecart
--------------------------------------------------------------------
    4   0.00  1.000     -inf    nan    1.000    1.000    0.000
    4   0.50  0.230   +209.9   0.07    1.000    0.230    0.770
   10   0.00  1.000     -inf    nan    1.000    1.000    0.000
   10   0.50  0.080   +424.3    nan    1.000    0.080    0.920
   25   0.00  1.000     -inf    nan    1.000    1.000    0.000
   25   0.50  0.040   +552.1    nan    1.000    0.040    0.960

Test t sur les ecarts : t=2.217, p=0.077
H0 non rejetee au seuil 0.05 (p=0.077, n=6)
Mais 3/6 scenarios ont un ecart > 0.10 : le test manque de puissance, l accord n est pas etabli

Lecture du résultat — le modèle casse exactement là où le problème devient intéressant

La table de méta-analyse se lit en deux blocs. Aux trois scénarios transparents (d_strength = 0.0), prédiction et observation coïncident (écart 0.000) — c’est la section 2 répliquée. Aux trois scénarios adverses (d_strength = 0.5), l’écart vaut 0.770 à 0.960 : NSO annonce p ≈ 1.000 quand la simulation observe 0.230, 0.080, 0.040. La cause se lit dans la colonne D_elo : +209.9, +424.3, +552.1 — or le close-form n* n’est défini que pour D_elo < 400 (saturation du Defender). Le plug-in, faute de réponse, décrète p_nso = 1.000 : le modèle répond « trivial » précisément dans le régime le plus dur.

Le test t qui clôt la cellule mérite un regard critique : t = 2.217, p = 0.077, « H0 non rejetée » — conclure à l’accord serait une erreur. Les 6 écarts valent {0, 0.77, 0, 0.92, 0, 0.96} : trois zéros exacts et trois valeurs massives. Avec n = 6, la puissance du test est effondrée, et l’hypothèse de normalité est contredite par cette distribution à deux pôles. Le test ne valide pas le modèle : il mesure surtout son incapacité à séparer 0 de 0.9 sur six points. La conclusion honnête est celle du corps du notebook : le close-form NSO est fiable en régime transparent, et sans réponse en régime adverse — c’est un plafond de modèle documenté, pas un désaccord statistique.

Exercice 1 : l’IC95 de D_elo par bootstrap

La section 1 a montré qu’une estimation ponctuelle de D_elo sur 100 parties (70.4 pour un vrai 50) peut être largement décalée. Quantifiez cette incertitude.

  • Étape 1 : simulez 100 parties avec le winrate attendu 0.571 (D_elo vrai = 50) en réutilisant rng.
  • Étape 2 : resamplez B = 1000 fois ces 100 parties (tirage avec remise), et recalculez D_elo sur chaque rééchantillon — une liste de victoires 1/0 se convertit par D_elo_from_data(sum(sample), len(sample) - sum(sample)).
  • Étape 3 : rapportez l’IC95 percentile et la moyenne bootstrap. Le vrai 50 est-il couvert ? L’estimation 70.4 de la section 1 est-elle dans l’intervalle ?

Indice : bootstrap_ci(observations, stat_func) existe déjà (section 3) — il suffit d’une stat_func adaptée.

# Exercice 1 : IC95 bootstrap de D_elo sur 100 parties
# Etape 1 : simuler 100 parties (winrate attendu 0.571, D_elo vrai = 50)
# Etape 2 : B=1000 resamples -> D_elo a chaque tirage
# Etape 3 : IC95 percentile + moyenne bootstrap ; le vrai 50 est-il couvert ?
# TODO etudiant
resultat_ic95 = None  # TODO etudiant : (moyenne_bootstrap, lo, hi)
print("Exercice 1 a completer : IC95 bootstrap de D_elo sur 100 parties")
Exercice 1 a completer : IC95 bootstrap de D_elo sur 100 parties

Exercice 2 : la puissance du test, ou pourquoi p = 0.077 ne sauve rien

La méta-analyse conclut « H0 non rejetée » avec p = 0.077 sur 6 écarts dont trois valent ~0.9. Fabrichez le contre-exemple.

  • Étape 1 : isolez les 3 scénarios adverses (d_strength = 0.5) et relancez le test t univarié sur leurs écarts seuls.
  • Étape 2 : même chose sur les 3 scénarios transparents.
  • Étape 3 : comparez les p-values et concluez : que faudrait-il comme nombre de scénarios pour que l’écart moyen ~0.88 du bloc adverse soit détecté au seuil 0.05 ?

Indice : stats.ttest_1samp sur chaque sous-liste ; pour l’étape 3, une monte-carlo rapide (tirer n écarts {0, 0.9} équilibrés, compter les rejets) suffit.

# Exercice 2 : puissance du test t sur les ecarts de la meta-analyse
# Etape 1 : test t sur les 3 ecarts adverses (d_strength = 0.5) seuls
# Etape 2 : test t sur les 3 ecarts transparents seuls
# Etape 3 : monte-carlo -- combien de scenarios pour detecter l'ecart ~0.88 ?
# TODO etudiant
p_adverse = None  # TODO etudiant : p-value du bloc adverse
n_requis = None   # TODO etudiant : nombre de scenarios pour puissance ~0.8
print("Exercice 2 a completer : puissance du test sur les ecarts")
Exercice 2 a completer : puissance du test sur les ecarts

Exercice 3 : cartographier la frontière de saturation du close-form

La table de la section 4 montre le close-form muet dès D_elo ≥ 400. Localisez la frontière.

  • Étape 1 : étendez la table avec d_strength ∈ {0.1, 0.25, 0.4} pour K = 10 (200 épisodes par scénario, réutilisez run_episode).
  • Étape 2 : pour chaque scénario, notez p_obs, D_elo estimé, et si n_star_closeform renvoie un nombre ou NaN.
  • Étape 3 : tracez p_obs en fonction de d_strength — la transition est-elle progressive ou brutale ? Quel d_strength rend le plug-in D_elo ≥ 400 pour K = 10 ?

Indice : la frontière cherchée est celle où le winrate observé du Defender passe sous ~10 % (winrate → 0.1 donne D_elo → +400 par la formule Elo).

# Exercice 3 : frontiere de saturation D_elo >= 400 en fonction de d_strength
# Etape 1 : scenarios (K=10, d_strength in {0.1, 0.25, 0.4}), 200 episodes chacun
# Etape 2 : p_obs, D_elo estime, n* (nombre ou NaN) par scenario
# Etape 3 : transition progressive ou brutale ? d_strength qui sature K=10 ?
# TODO etudiant
frontiere = None  # TODO etudiant : d_strength ou D_elo franchit 400 pour K=10
print("Exercice 3 a completer : frontiere de saturation du close-form")
Exercice 3 a completer : frontiere de saturation du close-form

Conclusion

Ce que ce notebook valide

  1. Calibration : la cellule 3 valide l’inverse Elo sur donnees controlees (D_elo estime = 70.4 pour un vrai D_elo = 50, winrate 0.600 observe contre 0.571 attendu) ; la cellule 5 l’applique en pont plug-in au scenario transparent (D_elo = -inf, NSO trivial, ecart 0.000).
  2. Coherence NSO-Wargames : avec le pont plug-in (D_elo derive du winrate defenseur observe), les scenarios d_strength=0.5 produisent enfin D_elo > 0 (+209.9 a +552.1) — la zone non-triviale est couverte. NSO y predit quand meme p_NSO = 1.000 (n* = 0.07 puis saturation D >= 400), tandis que Wargames observe 0.230 / 0.080 / 0.040 : le desaccord est rendu visible au lieu d’etre enfoui sous une constante.
  3. Bootstrap : IC95 sur p_success et avg_questions permettent de quantifier l’incertitude d’echantillonnage.
  4. Meta-analyse : test t sur les ecarts (t=2.217, p=0.077, n=6) — non rejetee au seuil 0.05 mais 3/6 scenarios ont un ecart > 0.10 (max 0.960) : le test manque de puissance, l’accord NSO-Wargames n’est pas etabli.

Limites assumees (honnetete Tell c.G.9)

  1. Donnees synthetiques : les scenarios sont generes selon le modele jouet (Defender + BayesianAttacker), pas par de vrais LLM.
  2. Echantillon limite : 200 episodes par scenario, 1000 bootstrap. Pour des IC95 plus precises, augmenter.
  3. Pont plug-in : D_elo est derive du winrate defenseur observe sur les memes episodes que la prediction (inverse calibre en cellule 3) — la mesure et la prediction ne sont pas independantes. L’ancienne heuristique -400log10(K(1-d)+d) (bornee a D_elo <= 0 pour toute configuration) a ete retiree.
  4. Regimes non couverts par la formule : la close-form n* = log(q)/log(1 - D/400) n’est definie que pour 0 < D < 400. Avec le pont plug-in, (K=4, d=0.5) tombe dans ce domaine (D = +209.9, n* = 0.07 < 1), mais (K>=10, d=0.5) sature l’echelle Elo (D >= 400 -> n* indefini) : le comportement NSO y reste hors de portee de la formule, et la comparaison a p_NSO = 1.0 y est un fallback, pas une prediction.
  5. Pas de calibration sur LLM reels : Tell c.1261-L1 strict. Une vraie calibration demanderait une machine GenAI adequate (po-2023 ou ai-01 vLLM).

Suite logique (PR 5+ sur #16754)

  • PR 5 (optionnel) : calibration empirique si greenlight GenAI po-2023 ou ai-01 vLLM. Executer les notebooks sur GPT-4 vs GPT-3.5 et comparer aux predictions.
  • PR 6 (optionnel) : cross-extension a d’autres scenarios NSO (Debate, Market Making, etc.) au-dela de Wargames.

Statut sub-grain #16754

Apres cette PR 4, le sub-grain est complet :

  • notebooks (Oversight / Analytics / Wargames / Statistics)
  • 1 grain DEEP/notebook-python par PR
  • Couverture R12 §2 (base), §3 (formalisation), §4 (calibration), §5 (Wargames)
  • Pipeline coherent : mesure -> formalisation -> simulation -> calibration
Retour au sommet