Voltline's picture
Release vimeml-tiny-zh-v1 (step 844730): FP32 bundle, INT8 Core ML packages, source snapshot
81cffe7 verified
Raw History Blame Contribute Delete
7.17 kB
"""Preregistered V4.4 analysis (docs/zh-CN/training-v44-plan.md) on completed FP32 scores.
Reads only the `scores.jsonl` files of V4.2 epoch 20 and the six V4.4 arms; no model is
run. Reports per track the exact, notation-lenient and pool-conditional Top-1 of every
model, the seed noise band (V4.2 + s1-s3), all six seed pairs, each treatment against
V4.2 under ime-metrics-v2 statistics, and the registered decision for chat0, chat10 and
crop60 on Standard. Training-side LM/chat BPC histories are added from the run records.
"""
import argparse
import hashlib
import json
import statistics as stats
import sys
from itertools import combinations
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / 'src'))
from vimeml.training.data import write_json
from vimeml.benchmarks.notation import error_class
from vimeml.benchmarks import statistics
TRACKS = ['standard-development', 'legacy-regression', 'ajimee', 'historical-development']
PRIMARY = 'standard-development'
GUARDED = ['standard-development', 'legacy-regression', 'ajimee']
SMOKE, SMOKE_DROP = 'historical-development', 3
SEEDS = ['v42-e20', 's1', 's2', 's3']
TREATMENTS = ['chat0', 'chat10', 'crop60']
SCORES = ROOT / 'outputs/evaluation-v44-20261105'
MODELS = {'v42-e20': ROOT / 'outputs/evaluation-v42-20261020',
**{arm: SCORES / arm for arm in SEEDS[1:] + TREATMENTS}}
RECORDS = ROOT / 'outputs/training-v44-20261105/remote'
V42_HISTORY = ROOT / 'outputs/training-v42-20261020/remote/records/epoch-validation-history.json'
OUTCOMES = {'exact': ('correct',), 'notation_lenient': ('correct', 'notation')}
ALPHA = .05
def read_rows(path):
with open(path, encoding='utf-8') as handle:
return [json.loads(line) for line in handle]
def holm(pvalues):
"""Holm step-down adjusted p-values, in input order."""
order = sorted(range(len(pvalues)), key=lambda i: pvalues[i])
adjusted, running = [0.] * len(pvalues), 0.
for rank, i in enumerate(order):
running = max(running, min(1., (len(pvalues) - rank) * pvalues[i]))
adjusted[i] = running
return adjusted
def band(values):
return {'values': values, 'mean': stats.mean(values), 'min': min(values), 'max': max(values),
'range': max(values) - min(values), 'sd': stats.stdev(values)}
def history(path):
rows = json.loads(Path(path).read_text(encoding='utf-8'))
return [{'epoch': r['epoch'], 'lm_bpc': r['lm']['bpc'], 'chat_bpc': r['chat']['bpc']} for r in rows]
def main():
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('--output', type=Path, required=True, help='Separate empty output directory')
args = parser.parse_args()
out = args.output.resolve()
if out.exists() and any(out.iterdir()):
parser.error('Use an empty output directory')
for arm in SEEDS[1:] + TREATMENTS:
if json.loads((SCORES / arm / 'summary.json').read_text(encoding='utf-8'))['status'] != 'complete':
parser.error(f'Score {arm} first')
summary = {'plan': 'docs/zh-CN/training-v44-plan.md', 'models': {k: str(v.relative_to(ROOT)) for k, v in MODELS.items()},
'score_sha256': {}, 'blind_read': False, 'reference_read': False, 'tracks': {}}
for track in TRACKS:
rows = {name: read_rows(folder / track / 'scores.jsonl') for name, folder in MODELS.items()}
base = rows['v42-e20']
for name, data in rows.items():
summary['score_sha256'][f'{name}/{track}'] = hashlib.sha256(
(MODELS[name] / track / 'scores.jsonl').read_bytes()).hexdigest()
assert [r['id'] for r in data] == [r['id'] for r in base], (track, name)
assert all(r['answers'] == b['answers'] and r['orders']['azookey'] == b['orders']['azookey']
for r, b in zip(data, base)), (track, name)
reachable = sum(any(a in {c['text'] for c in r['candidates']} for a in r['answers']) for r in base)
labels = statistics.clusters(base, template=None)
classes = {name: [error_class(r, r['orders']['lm_context_sum'][0])[0] for r in data]
for name, data in rows.items()}
entry = {'cases': len(base), 'pool_reachable': reachable, 'author_clusters': len(set(labels)),
'models': {}, 'seed_band': {}, 'seed_pairs': {}, 'vs_v42': {}}
for name, items in classes.items():
exact = items.count('correct')
entry['models'][name] = {'exact': exact, 'notation_lenient': exact + items.count('notation'),
'pool_conditional': exact / reachable,
'errors': {c: items.count(c) for c in sorted(set(items) - {'correct'})}}
for metric in OUTCOMES:
entry['seed_band'][metric] = band([entry['models'][m][metric] for m in SEEDS])
def compare(a, b):
return {metric: statistics.paired([c in ok for c in classes[a]], [c in ok for c in classes[b]], labels)
for metric, ok in OUTCOMES.items()}
for a, b in combinations(SEEDS, 2):
entry['seed_pairs'][f'{a}->{b}'] = compare(a, b)
for arm in SEEDS[1:] + TREATMENTS:
entry['vs_v42'][arm] = compare('v42-e20', arm)
summary['tracks'][track] = entry
print(track, json.dumps({k: (v['exact'], v['notation_lenient']) for k, v in entry['models'].items()}), flush=True)
primary = summary['tracks'][PRIMARY]
raw = [primary['vs_v42'][arm]['exact']['cluster_sign_flip_p'] for arm in TREATMENTS]
decision = {}
for arm, p, adjusted in zip(TREATMENTS, raw, holm(raw)):
exact = primary['models'][arm]['exact']
seed = primary['seed_band']['exact']
guards = {track: summary['tracks'][track]['vs_v42'][arm]['notation_lenient'] for track in GUARDED}
regressions = [t for t, g in guards.items() if g['net'] < 0 and g['cluster_sign_flip_p'] < ALPHA]
outside = exact < seed['min'] or exact > seed['max']
smoke = summary['tracks'][SMOKE]
drop = smoke['models']['v42-e20']['exact'] - smoke['models'][arm]['exact']
decision[arm] = {'standard_exact': exact, 'net_vs_v42': primary['vs_v42'][arm]['exact']['net'],
'outside_seed_range': outside, 'cluster_p': p, 'holm_p': adjusted,
'notation_lenient_regressions': regressions, 'smoke_flag': drop > SMOKE_DROP,
'effective': outside and adjusted < ALPHA and not regressions}
summary['decision'] = decision
summary['bpc'] = {'v42-e20': history(V42_HISTORY),
**{arm: history(RECORDS / arm / 'records/epoch-validation-history.json')
for arm in SEEDS[1:] + TREATMENTS}}
final = {arm: rows[-1] for arm, rows in summary['bpc'].items()}
summary['bpc_seed_band'] = {key: band([final[m][key] for m in SEEDS]) for key in ('lm_bpc', 'chat_bpc')}
print(json.dumps(decision, indent=1), flush=True)
out.mkdir(parents=True, exist_ok=True)
write_json(out / 'summary.json', summary)
if __name__ == '__main__':
main()