ald-tao-gpu / training_config.py
TiGa-RCE's picture
Default training and frozen evaluation to 60 Hz decisions (batch count 1)
e70da5c verified
Raw History Blame
4.6 kB
"""Settings contract shared by the CPU and leased GPU dispatch paths."""
import copy
import math
DEFAULTS={'mode':'train','clean_min_laps':2,'population':4,'generations':1,'ticks':60,'mutation':.01,'seed':42,
'mutation_end':.01,'schedule':'fixed','selection':'best','parents':1,'elite':1,
'crossover':'none','decay':0,'adaptive_mutation':False,'control_hz':60,'workers':1,
'collision':False,'idle':False,'freeze':{'layers':[],'inputs':[],'outputs':[]},
'rewards':[{'metric':'total_score','type':'default','weight':100}]}
METRICS={'total_score','total_speed','total_abs_steering','total_actual_distance',
'total_drifting_amount','total_momentum_change_frequency','total_throttle_change_frequency',
'total_braking_frequency','total_distance_from_wall','total_distance_from_center',
'network_novelty','collision_count','best_lap_performance','total_racing_line_efficiency'}
def validate_freeze(value):
"""Check the settings envelope; the uploaded network supplies exact bounds."""
if not isinstance(value,dict) or set(value)!={'layers','inputs','outputs'}:
raise ValueError('Invalid freeze selection')
result={}
for key,low,high in (('layers',1,10),('inputs',0,19),('outputs',0,4)):
items=value[key]
if (not isinstance(items,list) or len(items)>high-low+1 or
any(type(item) is not int or not low<=item<=high for item in items) or
items!=sorted(set(items))):
raise ValueError('Invalid frozen '+key)
result[key]=items[:]
return result
def validate_config(value=None, *, work_limit=4_000_000):
if value is None:return copy.deepcopy(DEFAULTS)
if not isinstance(value,dict) or set(value)-set(DEFAULTS):raise ValueError('Unexpected training settings')
c={**copy.deepcopy(DEFAULTS),**value}
bounds={'population':(1,1024,True),'generations':(1,2000,True),'ticks':(1,18000,True),
'mutation':(0,4.2,False),'mutation_end':(0,4.2,False),'seed':(0,2147483647,True),
'clean_min_laps':(1,100,True),'parents':(1,1024,True),'elite':(0,1024,True),'decay':(0,1,False),'workers':(1,4,True)}
for key,(low,high,integer) in bounds.items():
v=c[key]
if type(v) not in (int,float) or not math.isfinite(v) or not low<=v<=high or (integer and type(v) is not int):raise ValueError('Invalid '+key)
for key,choices in {'mode':('train','evaluate'),'schedule':('fixed','linear','geometric'),'selection':('best','tournament','roulette'),
'crossover':('none','single_point','uniform'),'control_hz':(60,30,15)}.items():
if c[key] not in choices or (key=='control_hz' and type(c[key]) is not int):raise ValueError('Invalid '+key)
for key in ('adaptive_mutation','collision','idle'):
if type(c[key]) is not bool:raise ValueError('Invalid '+key)
c['freeze']=validate_freeze(c['freeze'])
if c['mode']=='train' and (c['parents']>c['population'] or c['elite']>c['population']):raise ValueError('Parents/elites exceed population')
if c['mode']=='train' and (c['mutation']<=0 or (c['schedule']!='fixed' and c['mutation_end']<=0)):raise ValueError('This training runner requires positive mutation rates, including when continuing an uploaded network')
# Echo the rate actually passed to the native CLI, not an unused form value.
if c['schedule']=='fixed':c['mutation_end']=c['mutation']
rewards=c['rewards']
if not isinstance(rewards,list) or not 1<=len(rewards)<=len(METRICS):raise ValueError('Use one or more distinct supported rewards')
seen=set();clean=[]
for r in rewards:
if not isinstance(r,dict) or set(r)!= {'metric','type','weight'}:raise ValueError('Unexpected reward fields')
if not isinstance(r['metric'],str) or r['metric'] not in METRICS or r['type'] not in ('default','average') or r['metric'] in seen or type(r['weight']) is not int or abs(r['weight'])>100:raise ValueError('Invalid reward')
seen.add(r['metric']);clean.append(dict(r))
if not any(r['weight'] for r in clean):raise ValueError('A reward weight must be nonzero')
c['rewards']=clean
if c['mode']=='evaluate':
c.update(population=1,generations=1,mutation=0,mutation_end=0,decay=0,adaptive_mutation=False,parents=1,elite=0,crossover='none',selection='best',schedule='fixed',freeze={'layers':[],'inputs':[],'outputs':[]})
if c['population']*c['generations']>32_768:raise ValueError('Run exceeds 32,768 car-generation analysis rows')
if c['population']*c['generations']*c['ticks']>work_limit:raise ValueError('Run exceeds backend vehicle-tick limit')
return c