fev-4b / tests /test_training_tools.py
murabcd's picture
feat: add compiled serving shared training and benchmarks
5adf069 verified
Raw History Blame Contribute Delete
5.47 kB
"""Leakage, label and measurement checks independent of CUDA."""
import copy
import json
import tempfile
import unittest
from pathlib import Path
from fev4b.data import split_records, require_disjoint, target_index
from fev4b.metrics import rows_for, summarize
from fev4b.calibrate import fit_temperature, rescale
from fev4b.benchmark import evaluate
from fev4b.adaptation import base_identity, verify_adaptation
from fev4b.integrity import file_sha256
from tools.compare_runs import compare
def record(i, group=None):
return {'id': str(i), 'group': group or str(i), 'state': str(i), 'questions': {
'q': {'type': 'choice', 'criteria': {'yes': None, 'no': None}, 'label': 'yes'}}}
class TrainingToolsTests(unittest.TestCase):
def test_overlay_rejects_other_base_or_modified_payload(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
(root/'adapter').mkdir()
config = {'r': 8, 'lora_alpha': 16}
(root/'adapter/adapter_config.json').write_text(json.dumps(config))
manifest = {'files': {'checkpoint/model.safetensors': {'bytes': 4, 'sha256': 'base'},
'adapter/adapter_model.safetensors': {'bytes': 4, 'sha256': 'shared'}}}
(root/'manifest.json').write_text(json.dumps(manifest))
overlay = root/'overlay'
overlay.mkdir()
(overlay/'adapter_config.json').write_text(json.dumps(config))
(overlay/'adapter_model.safetensors').write_bytes(b'test')
meta = {'format': 'fev-adaptation-v1', 'baseIdentity': base_identity(root), 'temperature': 1.5,
'files': {name: file_sha256(overlay/name) for name in
('adapter_config.json', 'adapter_model.safetensors')}}
(overlay/'adaptation.json').write_text(json.dumps(meta))
self.assertEqual(verify_adaptation(root, overlay)['temperature'], 1.5)
(overlay/'adapter_model.safetensors').write_bytes(b'changed')
with self.assertRaises(ValueError):
verify_adaptation(root, overlay)
(overlay/'adapter_model.safetensors').write_bytes(b'test')
manifest['files']['checkpoint/model.safetensors']['sha256'] = 'other'
(root/'manifest.json').write_text(json.dumps(manifest))
with self.assertRaises(ValueError):
verify_adaptation(root, overlay)
def test_comparison_rejects_different_labels_and_preserves_paired_counts(self):
a = rows_for(record(0), {'answers': {'q': {'type': 'choice', 'probabilities': {'yes': .9, 'no': .1}}}})
b = rows_for(record(0), {'answers': {'q': {'type': 'choice', 'probabilities': {'yes': .1, 'no': .9}}}})
result = compare({'a': a, 'b': b})
self.assertEqual(result['pairedOutcomes']['a vs b']['aOnlyCorrect'], 1)
b[0]['target'] = 1
with self.assertRaises(ValueError):
compare({'a': a, 'b': b})
def test_group_bridge_does_not_leak(self):
records = [record(i) for i in range(30)]
duplicate = record(31, group='0')
duplicate['state'] = '1'
records.append(duplicate)
split = split_records(records)
require_disjoint(*split.values())
partition = next(rows for rows in split.values() if any(r['id'] == '0' for r in rows))
self.assertTrue({'0', '1', '31'} <= {r['id'] for r in partition})
with self.assertRaises(ValueError):
require_disjoint([record(0)], [record(1, group='0')])
def test_label_types_are_strict(self):
with self.assertRaises(ValueError):
target_index({'type': 'noul', 'label': 1})
with self.assertRaises(ValueError):
target_index({'type': 'score', 'criteria': ['low', 'high'], 'label': True})
def test_errors_remain_in_denominator_and_confidence_is_top_probability(self):
response = {'answers': {'q': {'type': 'choice', 'probabilities': {'yes': .9, 'no': .1}}}}
rows = rows_for(record(0), response) + rows_for(record(1), error='Timeout')
report = summarize(rows)
self.assertEqual(report['accuracyAllQuestions'], .5)
self.assertEqual(report['coverageByFixedThreshold'][3]['coverage'], .5)
response['answers']['q']['probabilities'] = {'yes': 2., 'no': -1.}
with self.assertRaises(ValueError):
rows_for(record(0), response)
def test_temperature_does_not_change_argmax(self):
values = [.05, .15, .8]
for factor in (.2, 1., 5.):
self.assertEqual(max(range(3), key=rescale(values, factor).__getitem__), 2)
rows = [{'error': None, 'probabilities': [.99, .01], 'target': 1} for _ in range(3)]
_, report = fit_temperature(rows)
self.assertLessEqual(report['afterNll'], report['beforeNll'])
def test_benchmark_keeps_failed_records(self):
def predict(request):
if request['state'] == '1':
raise RuntimeError('failed')
return {'model': 'fake', 'answers': {'q': {'type': 'choice', 'probabilities': {'yes': .8, 'no': .2}}}}
with tempfile.TemporaryDirectory() as directory:
report = evaluate([record(0), record(1)], predict, Path(directory)/'run', provenance={})
self.assertEqual(report['overall']['questions'], 2)
self.assertEqual(report['overall']['errors'], 1)
if __name__ == '__main__':
unittest.main()