veritiana-ai's picture
Upload folder using huggingface_hub
811d51e verified
Raw
History Blame Contribute Delete
9.13 kB
#!/usr/bin/env python3
"""Reproduce Veritiana AI Meter classifier v3.1.0 from training-balanced.jsonl."""
from __future__ import annotations
import argparse, hashlib, json, struct, sys, time
from collections import Counter
from pathlib import Path
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.model_selection import GroupShuffleSplit
from features import INPUT_SIZE, build_features
TASK_LABELS = ["general_chat","writing","translation","summarization","research","coding","mathematics","document_analysis","high_stakes"]
COMPLEXITY_LABELS = ["low","medium","high"]
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def _varint(number: int) -> bytes:
number = int(number)
if number < 0: number = (1 << 64) + number
output = bytearray()
while True:
byte = number & 0x7F; number >>= 7
output.append(byte | (0x80 if number else 0))
if not number: return bytes(output)
def _key(field: int, wire: int) -> bytes: return _varint((field << 3) | wire)
def _fv(field: int, value: int) -> bytes: return _key(field,0)+_varint(value)
def _fb(field: int, value: bytes) -> bytes: return _key(field,2)+_varint(len(value))+value
def _fs(field: int, value: str) -> bytes: return _fb(field,value.encode())
def _msg(field: int, value: bytes) -> bytes: return _fb(field,value)
def _packed(field: int, values) -> bytes: return _fb(field,b''.join(_varint(v) for v in values))
def _tensor(name: str, array: np.ndarray) -> bytes:
array=np.ascontiguousarray(array.astype(np.float32))
return _packed(1,array.shape)+_fv(2,1)+_fs(8,name)+_fb(9,array.tobytes(order='C'))
def _dim(value): return _fs(2,value) if isinstance(value,str) else _fv(1,value)
def _shape(dims): return b''.join(_msg(1,_dim(value)) for value in dims)
def _tensor_type(element,dims): return _fv(1,element)+_msg(2,_shape(dims))
def _type_proto(element,dims): return _msg(1,_tensor_type(element,dims))
def _value_info(name,dims): return _fs(1,name)+_msg(2,_type_proto(1,dims))
def _attr_int(name,value): return _fs(1,name)+_fv(3,value)+_fv(20,2)
def _node(inputs,outputs,operation,name='',attributes=None):
value=b''.join(_fs(1,item) for item in inputs)+b''.join(_fs(2,item) for item in outputs)
if name: value += _fs(3,name)
value += _fs(4,operation)
for attribute in attributes or []: value += _msg(5,attribute)
return value
def _graph(nodes,initializers,inputs,outputs,name):
return (b''.join(_msg(1,node) for node in nodes)+_fs(2,name)+
b''.join(_msg(5,item) for item in initializers)+
b''.join(_msg(11,item) for item in inputs)+
b''.join(_msg(12,item) for item in outputs))
def _opset(version): return _fv(2,version)
def _kv(key,value): return _fs(1,key)+_fs(2,value)
def export_onnx(task_model, complexity_model, destination: Path, version: str) -> None:
initializers=[
_tensor('task_weights',task_model.coef_.astype(np.float32).T.copy()),
_tensor('task_bias',task_model.intercept_.astype(np.float32).copy()),
_tensor('complexity_weights',complexity_model.coef_.astype(np.float32).T.copy()),
_tensor('complexity_bias',complexity_model.intercept_.astype(np.float32).copy()),
]
nodes=[
_node(['features','task_weights'],['task_mm'],'MatMul','TaskMatMul'),
_node(['task_mm','task_bias'],['task_logits'],'Add','TaskAdd'),
_node(['task_logits'],['task_probabilities'],'Softmax','TaskSoftmax',[_attr_int('axis',1)]),
_node(['features','complexity_weights'],['complexity_mm'],'MatMul','ComplexityMatMul'),
_node(['complexity_mm','complexity_bias'],['complexity_logits'],'Add','ComplexityAdd'),
_node(['complexity_logits'],['complexity_probabilities'],'Softmax','ComplexitySoftmax',[_attr_int('axis',1)]),
]
graph=_graph(nodes,initializers,[_value_info('features',['batch',1544])],
[_value_info('task_probabilities',['batch',9]),_value_info('complexity_probabilities',['batch',3])],
'VeritianaAIMeterClassifier')
metadata={
'model':'Veritiana Multilingual Intent Classifier','version':version,'input_size':'1544',
'task_labels':json.dumps(TASK_LABELS),'complexity_labels':json.dumps(COMPLEXITY_LABELS),
'training_data':'OASST1 + CoEdIT + MBPP+ + IFEval; filtered, weak-labeled, balanced',
'feature_contract':'AI Meter v1.4.9 compatible'
}
payload=(_fv(1,8)+_fs(2,'veritiana-ai-meter-model-trainer')+_fs(3,version)+
_msg(7,graph)+_msg(8,_opset(13))+b''.join(_msg(14,_kv(k,v)) for k,v in metadata.items()))
destination.write_bytes(payload)
def main() -> int:
parser=argparse.ArgumentParser()
parser.add_argument('dataset',type=Path,help='Prepared training-balanced.jsonl')
parser.add_argument('--output-dir',type=Path,default=Path('output'))
parser.add_argument('--version',default='3.1.0-multisource-balanced')
parser.add_argument('--test-size',type=float,default=0.20)
parser.add_argument('--seed',type=int,default=42)
parser.add_argument('--c',type=float,default=4.0)
args=parser.parse_args()
rows=[json.loads(line) for line in args.dataset.read_text(encoding='utf-8').splitlines() if line.strip()]
if not rows: raise ValueError('Empty dataset')
task_map={label:index for index,label in enumerate(TASK_LABELS)}
complexity_map={label:index for index,label in enumerate(COMPLEXITY_LABELS)}
X=np.vstack([build_features(row['text']) for row in rows]).astype(np.float32)
task_y=np.asarray([task_map[row['task']] for row in rows],dtype=np.int64)
complexity_y=np.asarray([complexity_map[row['complexity']] for row in rows],dtype=np.int64)
groups=[row.get('derived_from') or row.get('normalized_hash') or row.get('id') for row in rows]
splitter=GroupShuffleSplit(n_splits=1,test_size=args.test_size,random_state=args.seed)
train_index,test_index=next(splitter.split(X,task_y,groups=groups))
def fit(target):
model=LogisticRegression(C=args.c,max_iter=3000,solver='lbfgs',class_weight='balanced',random_state=args.seed)
model.fit(X[train_index],target[train_index]); return model
started=time.perf_counter(); task_model=fit(task_y); complexity_model=fit(complexity_y); seconds=time.perf_counter()-started
task_prediction=task_model.predict(X[test_index]); complexity_prediction=complexity_model.predict(X[test_index])
task_report=classification_report(task_y[test_index],task_prediction,labels=list(range(9)),target_names=TASK_LABELS,output_dict=True,zero_division=0)
complexity_report=classification_report(complexity_y[test_index],complexity_prediction,labels=list(range(3)),target_names=COMPLEXITY_LABELS,output_dict=True,zero_division=0)
evaluation={
'summary':{'task_accuracy':task_report['accuracy'],'task_macro_f1':task_report['macro avg']['f1-score'],
'complexity_accuracy':complexity_report['accuracy'],'complexity_macro_f1':complexity_report['macro avg']['f1-score'],
'training_seconds':seconds},
'task_report':task_report,'complexity_report':complexity_report,
'task_confusion_matrix':confusion_matrix(task_y[test_index],task_prediction,labels=list(range(9))).tolist(),
'complexity_confusion_matrix':confusion_matrix(complexity_y[test_index],complexity_prediction,labels=list(range(3))).tolist()
}
args.output_dir.mkdir(parents=True,exist_ok=True)
model_path=args.output_dir/'veritiana-classifier-v2.onnx'; export_onnx(task_model,complexity_model,model_path,args.version)
(args.output_dir/'evaluation.json').write_text(json.dumps(evaluation,ensure_ascii=False,indent=2),encoding='utf-8')
meta={
'model':'Veritiana Multilingual Intent Classifier','version':args.version,'engine':'ONNX Runtime Web',
'architecture':'1544 hashed lexical/character/numeric features; dual multinomial logistic heads',
'input':{'name':'features','dtype':'float32','shape':['batch',1544]},
'outputs':[{'name':'task_probabilities','dtype':'float32','shape':['batch',9]},
{'name':'complexity_probabilities','dtype':'float32','shape':['batch',3]}],
'task_labels':TASK_LABELS,'complexity_labels':COMPLEXITY_LABELS,
'training_data':{'dataset_sha256':sha256(args.dataset),'balanced_rows':len(rows),'train_rows':len(train_index),'test_rows':len(test_index),
'label_method_counts':dict(Counter(row.get('label_method','unknown') for row in rows))},
'validation':evaluation['summary'],'onnx_sha256':sha256(model_path),
'limitations':'Validation uses weak labels and deterministic augmentations; it is not an independent human-ground-truth benchmark.'
}
(args.output_dir/'classifier-meta.json').write_text(json.dumps(meta,ensure_ascii=False,indent=2),encoding='utf-8')
print(json.dumps({'model':str(model_path),'onnx_sha256':sha256(model_path),'evaluation':evaluation['summary']},indent=2))
return 0
if __name__=='__main__': raise SystemExit(main())