# # Collective Knowledge: crowdbenchmarking using ARM's workload automation and CK # # See CK LICENSE.txt for licensing details # See CK COPYRIGHT.txt for copyright details # # Developer: Grigori Fursin, Grigori.Fursin@cTuning.org, http://fursin.net # cfg={} # Will be updated by CK (meta description of this module) work={} # Will be updated by CK (temporal data) ck=None # Will be updated by CK (initialized CK kernel) # Local settings compiler_choices='#choices#compiler_flags#' line='================================================================' fsummary='summary.json' fclassification='classification.json' fgraph='tmp-reactions-graph.json' ffstat='ck-stat-flat-characteristics.json' form_name='wa_web_form' onchange='document.'+form_name+'.submit();' hextra='' #hextra+='
\n' #hextra+=' [ Community-driven AI R&D powered by CK ], ' #hextra+=' [ CGO\'17 test of time award for our interdisiplinary R&D ], ' #hextra+=' [ Android app to crowd-optimize DNN engines and models ], ' #hextra+=' [ CK-Caffe2 GitHub / CK-Caffe GitHub ], ' #hextra+=' [ CK-TensorFlow GitHub ], ' #hextra+=' [ Wikipedia, \n' #hextra+='paper 1, \n' #hextra+='Paper 2, \n' #hextra+='YouTube CK intro ] \n' #hextra+='
\n' #hextra+='
\n' selector=[{'name':'Scenario', 'key':'crowd_uid', 'module_uoa':'65477d547a49dd2c', 'module_key':'##dict#title'}, {'name':'DNN engine', 'key':'engine'}, {'name':'Model', 'key':'model'}, {'name':'Platform', 'key':'plat_name','new_line':'no'}, {'name':'OS', 'key':'os_name'}, {'name':'CPU', 'key':'cpu_name', 'new_line':'no'}, {'name':'CPU ABI', 'key':'cpu_abi'}, {'name':'GPU', 'key':'gpu_name'}] abis=[{'abi':'arm64-v8a', 'os_uoa':'android21-arm64'}, {'abi':'armeabi-v7a', 'os_uoa':'android21-arm-v7a'}] libxopenme='librtlxopenme.so' ############################################################################## # Initialize module def init(i): """ Input: {} Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 } """ return {'return':0} ############################################################################## # show results def show(i): """ Input: { (crowd_module_uoa) - if rendered from experiment crowdsourcing (crowd_key) - add extra name to Web keys to avoid overlapping with original crowdsourcing HTML (crowd_on_change) - reuse onchange doc from original crowdsourcing HTML (highlight_behavior_uid) - highlight specific result (behavior)! (highlight_by_user) - highlight all results from a given user (all) - if 'yes', view all results (minimal) - if 'yes', use in interactive papers } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 } """ import os import copy import time st='' minimal=i.get('minimal','')=='yes' cmuoa=i.get('crowd_module_uoa','') ckey=i.get('crowd_key','') debug=(i.get('debug','')=='yes') # debug=True conc=i.get('crowd_on_change','') if conc=='': conc=onchange hi_uid=i.get('highlight_behavior_uid','') hi_user=i.get('highlight_by_user','') # h='
\n' h='
\n' h+='\n\n\n\n' h+=hextra # h+='
\n' h+='
\n' # Check host URL prefix and default module/action rx=ck.access({'action':'form_url_prefix', 'module_uoa':'wfe', 'host':i.get('host',''), 'port':i.get('port',''), 'template':i.get('template','')}) if rx['return']>0: return rx url0=rx['url'] template=rx['template'] url=url0 action=i.get('action','') muoa=i.get('module_uoa','') st='' url+='action=index&module_uoa=wfe&native_action='+action+'&'+'native_module_uoa='+muoa url1=url # List entries dt=time.time() ii={'action':'search', 'module_uoa':work['self_module_uid'], 'add_meta':'yes'} if cmuoa!='': ii['module_uoa']=cmuoa r=ck.access(ii) if r['return']>0: return r if debug: h+='\n

Debug time (CK query): '+str(time.time()-dt)+' sec.

\n' lst=r['lst'] # Find unique variables dt=time.time() choices={} mchoices={} # cache of UID -> alias choices wchoices={} cache_meta={} for q in lst: d=q['meta'] meta=d.get('meta',{}) # Process some derivatives scenario=meta.get('crowd_uid','') kscenario=cfg['module_deps']['experiment.scenario.mobile']+':'+scenario if kscenario not in cache_meta: r=ck.access({'action':'load', 'module_uoa':cfg['module_deps']['experiment.scenario.mobile'], 'data_uoa':scenario}) if r['return']>0: return r xd=r['dict'] # Find model size for q in xd.get('files',[]): if q.get('model_weights','')=='yes': xd['model_weights_size']=int(q.get('file_size',0)/1E6) break cache_meta[kscenario]=xd else: xd=cache_meta[kscenario] if meta.get('engine','')=='': meta['engine']=xd.get('engine','') if meta.get('model','')=='': meta['model']=xd.get('model','') meta['engine_meta']=xd.get('engine_meta',{}) # Process selector meta for kk in selector: kx=kk['key'] k=ckey+kx if k not in choices: choices[k]=[] wchoices[k]=[{'name':'','value':''}] v=meta.get(kx,'') if v!='': if v not in choices[k]: choices[k].append(v) muoa=kk.get('module_uoa','') vv=v if muoa!='': if k not in mchoices: mchoices[k]={} vv=mchoices[k].get(v,'') if vv=='': r=ck.access({'action':'load', 'module_uoa':muoa, 'data_uoa':v}) if r['return']==0: mk=kk.get('module_key','') if mk=='': mk='##data_name' rx=ck.get_by_flat_key({'dict':r, 'key':mk}) if rx['return']>0: return rx vv=rx['value'] if vv=='' or vv==None: vv=v mchoices[k][v]=vv wchoices[k].append({'name':vv, 'value':v}) if debug: h+='\n

Debug time (CK find unique vars): '+str(time.time()-dt)+' sec.

\n' # Prepare query div *************************************************************** dt=time.time() if cmuoa=='': # Start form + URL (even when viewing entry) r=ck.access({'action':'start_form', 'module_uoa':cfg['module_deps']['wfe'], 'url':url1, 'name':form_name}) if r['return']>0: return r h+=r['html'] for kk in selector: k=ckey+kk['key'] n=kk['name'] nl=kk.get('new_line','') if nl=='yes': h+='
\n

\n' v='' if i.get(k,'')!='': v=i[k] kk['value']=v # Show hardware ii={'action':'create_selector', 'module_uoa':cfg['module_deps']['wfe'], 'data':wchoices.get(k,[]), 'name':k, 'onchange':conc, 'skip_sort':'no', 'selected_value':v, 'style':'margin:5px;'} r=ck.access(ii) if r['return']>0: return r h+=''+n.replace(' ',' ')+': '+r['html'].strip()+'\n' if debug: h+='\n

Debug time (prepare selector): '+str(time.time()-dt)+' sec.

\n' # Check hidden if hi_uid!='': h+='\n' if hi_user!='': h+='\n' h+='

' # Prune list ****************************************************************** dt=time.time() plst=[] for q in lst: d=q['meta'] meta=d.get('meta',{}) # Check selector skip=False for kk in selector: k=kk['key'] n=kk['name'] v=kk.get('value','') if v!='' and meta.get(k,'')!=v: skip=True break # FGG added later - should be correct if not skip: # Process raw results arr=d.get('all_raw_results',[]) for g in arr: nn=copy.deepcopy(q) ih=g.get('image_height',0) iw=g.get('image_width',0) it=0 if ih!=0 and iw!=0: it=ih*iw key=str(ih)+' x '+str(iw) prd=g.get('prediction','') if prd!='': j1=prd.find('\n') if j1>0: j2=prd.find('\n',j1+1) if j2>0: prd=prd[j1:j2] # Check timing - currently temporal ugly hack t=g.get('time',[]) tmin=0 tmax=0 if len(t)>0: tmin=min(t)/1E3 tmax=max(t)/1E3 nn['extra']={'key':key, 'raw_results':g, 'time_min':tmin, 'time_max':tmax, 'prediction':prd} # Check xOpenME timing xopenme=g.get('xopenme',{}) xxopenme={} if type(xopenme)==dict: for xk in xopenme: t=xopenme[xk] tmin=0 tmax=0 if len(t)>0: tmin=min(t) tmax=max(t) xxopenme['xopenme_'+xk+'_min']=tmin xxopenme['xopenme_'+xk+'_max']=tmax nn['extra'].update(xxopenme) plst.append(nn) if debug: h+='\n

Debug time (prune entries by user selection): '+str(time.time()-dt)+' sec.

\n' # Add extra selectors h+='

\n' wchoices9a=[{'name':'throughput', 'value':'throughput'}, {'name':'latency', 'value':'latency'}] key9a='sort_by' k=ckey+key9a v9a='' if i.get(k,'')!='': v9a=i[k] else: v9a=wchoices9a[0]['value'] # Prepare selector ii={'action':'create_selector', 'module_uoa':cfg['module_deps']['wfe'], 'data':wchoices9a, 'name':k, 'onchange':conc, 'skip_sort':'yes', 'selected_value':v9a} r=ck.access(ii) if r['return']>0: return r h+='Sort by: '+r['html'].strip()+'\n' wchoices9b=[{'name':'throughput vs top 1 accuracy', 'value':'throughput_top1'}, {'name':'throughput vs top 5 accuracy', 'value':'throughput_top5'}, {'name':'latency vs top 1 accuracy', 'value':'latency_top1'}, {'name':'latency vs top 5 accuracy', 'value':'latency_top5'}, {'name':'recognition time vs device cost vs model size', 'value':'time_cost'}] key9b='which_graph' k=ckey+key9b v9b='' if i.get(k,'')!='': v9b=i[k] else: v9b=wchoices9b[0]['value'] # Prepare selector ii={'action':'create_selector', 'module_uoa':cfg['module_deps']['wfe'], 'data':wchoices9b, 'name':k, 'onchange':conc, 'skip_sort':'yes', 'selected_value':v9b} r=ck.access(ii) if r['return']>0: return r h+='  Plot: '+r['html'].strip()+'\n' h+='
\n' h+='

\n' # Sort first before prunning dt=time.time() if v9a=='throughput': splst=sorted(plst, key=lambda x: x.get('extra',{}).get('xopenme_execution_time_kernel_2_min',0)) else: splst=sorted(plst, key=lambda x: x.get('extra',{}).get('time_min',0)) if debug: h+='\n

Debug time (sorting table): '+str(time.time()-dt)+' sec.

\n' # Demo graph bgraph={'0':[]} # Just for graph demo if hi_uid!='' or hi_user!='': bgraph['1']=[] # execution time vs cost bgraph2={'0':[]} igraph2={'0':[]} # execution time vs cost bgraph3={'0':[]} igraph3={'0':[]} f_bgraph3={} f_igraph3={} # Check if too many lplst=len(plst) min_view=False view_all=i.get('all','') if view_all!='': h+='\n' # Advertisement h+='

\n' # hhh+=' \n' # hhh+='
\n' h+=' These are raw benchmarking/optimization results shared by volunteers from their Android devices\n' h+=' to test our collaborative approach to unify and crowdsource learning, benchmarking, optimization and co-design of efficient SW/HW stack for emerging workloads\n' h+=' across diverse devices, models and data sets. See ACM ReQuEST tournaments for more details!\n' h+='
\n' h+='
\n' if lplst==0: h+='No results found!' return {'return':0, 'html':h, 'style':st} elif lplst>100 and view_all!='yes': h+='Too many species ('+str(lplst)+') - see 150 top performing species or prune list further!

' # min_view=True # Removing entries but leave user selection splst1=[] iq=0 for q in splst: rres=q['extra'].get('raw_results',{}) buid=rres.get('behavior_uid','') user=rres.get('user','') if iq<150 or (hi_uid!='' and buid==hi_uid) or (hi_user!='' and hi_user==user): splst1.append(q) iq+=1 splst=splst1 # del splst[150:] # return {'return':0, 'html':h, 'style':st} # Long term TBD: here we should move table prepation either to numpy or django or both ... # This is just for a quick proof-of-concept (FGG) # Prepare table # bgc='afffaf' bgc='dfffdf' bg=' style="background-color:#'+bgc+';"' bg1=' style="background-color:#bfffbf;"' bg2=' style="background-color:#afffaf;"' bg9=' style="background-color:#afffaf;color:#7f0000;"' h+='\n' ha='align="center" valign="top"' hb='align="left" valign="top"' h+=' \n' h+=' \n' h+=' \n' h+=' \n' if not min_view: h+=' \n' h+=' \n' x='' if v9a=='latency': x=' style="color:#7f0000"' h+=' \n' if not min_view: h+=' \n' h+=' \n' x='' if v9a=='throughput': x=' style="color:#7f0000"' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' if not min_view: h+=' \n' h+=' \n' h+=' \n' if not min_view: h+=' \n' h+=' \n' h+=' \n' h+=' \n' if not min_view: h+=' \n' h+=' \n' # Dictionary to hold target meta tm={} ix=0 # Trick - want to put graph first hhh=h h='' dt=time.time() for q in splst: ix+=1 duid=q['data_uid'] path=q['path'] d=q['meta'] meta=d.get('meta',{}) extra=q['extra'] img=extra.get('img',{}) rres=extra.get('raw_results',{}) mp=rres.get('mispredictions',[]) key=extra.get('key','') buid=rres.get('behavior_uid','') pred=extra.get('prediction','').replace(' ',' ') user=rres.get('user','') tmin=extra.get('time_min',0) tmax=extra.get('time_max',0) scenario=meta.get('crowd_uid','') plat_name=meta.get('plat_name','') cpu_name=meta.get('cpu_name','') cpu_abi=meta.get('cpu_abi','') os_name=meta.get('os_name','') gpu_name=meta.get('gpu_name','') gpgpu_name=meta.get('gpgpu_name','') plat_uid=meta.get('platform_uid','') cpu_uid=meta.get('cpu_uid','') os_uid=meta.get('os_uid','') gpu_uid=meta.get('gpu_uid','') gpgpu_uid=meta.get('gpgpu_uid','') te=d.get('characteristics',{}).get('run',{}) bgx=bg bgx1=bg1 bgx2=bg2 bgx9=bg9 if (hi_uid!='' and buid==hi_uid) or (hi_user!='' and hi_user==user): bgx=' style="background-color:#ffcf7f"' bgx1=' style="background-color:#ffbf5f"' bgx2=' style="background-color:#ffaf2f"' bgx9=' style="background-color:#ffaf2f;color:#7f0000;"' # Starting raw h+=' \n' h+=' \n' x=plat_name if plat_uid!='': x=''+x+'' h+=' \n' # Output scenario xx=mchoices.get(ckey+'crowd_uid',{}).get(scenario,'') kscenario=cfg['module_deps']['experiment.scenario.mobile']+':'+scenario xd=cache_meta[kscenario] xx=xd.get('title','') model_weights_size=int(xd.get('model_weights_size',0))+1 fbsize=xd.get('features',{}).get('fixed_batch_size','') h+=' \n' # Versions if not min_view: ver='' dver=meta.get('engine_meta',{}).get(cpu_abi,{}) ver+='main: '+str(dver.get('program_version',''))+'\n' dps=dver.get('deps_versions',{}) for dx in dps: ver+=dx+': '+str(dps[dx].get('version',''))+'\n' ver=ver.replace("\'","'").replace("'","\\'").replace('\"','"').replace('"',"\\'").replace('\n','\\n') if ver!='': ver='' h+=' \n' # Model weight size h+=' \n' # Check relative time xx=''+('%.3f'%tmin)+' / '+('%.3f'%tmax) if tmin==0: xx+='
bug?
\n' # TBD: need to check how time is normalized ... # elif fbsize!='': # xx+='

batch size='+str(fbsize)+'' if (hi_uid!='' and buid==hi_uid) or (hi_user!='' and hi_user==user): bgraph['0'].append([ix,None]) bgraph['1'].append([ix,tmin]) else: bgraph['0'].append([ix,tmin]) if hi_uid!='' or hi_user!='': bgraph['1'].append([ix,None]) x=bgx9 if v9a=='latency' else bgx1 h+=' \n' # Finer grain timing ttmin=tmin ttdelta=tmax-tmin if not min_view: for ixo in range(0,3): tmin=extra.get('xopenme_execution_time_kernel_'+str(ixo)+'_min',0) tmax=extra.get('xopenme_execution_time_kernel_'+str(ixo)+'_max',0) if tmin<0: tmin=0 # detected bug xx=''+('%.3f'%tmin)+' / '+('%.3f'%tmax) if tmin==0 and ixo!=1: xx+='
possible bug detected - check further
\n' x=bgx9 if (v9a=='throughput' and ixo==2) else bgx1 if v9b.startswith('throughput') and ixo==2: ttmin=tmin ttdelta=tmax-tmin h+=' \n' # Accuracy x=pred j=x.find('-') if j>0: x=x[:j-1].strip() h+=' \n' # Get info about platform hd={} if plat_uid!='': rh=ck.access({'action':'load', 'module_uoa':cfg['module_deps']['platform'], 'data_uoa':plat_uid}) if rh['return']==0: hd=rh['dict'] # Energy TBD x='-' if len(hd)>0: power=hd.get('features',{}).get('power_consumption',{}) if len(power)>0: pmin=power.get('min','') pmax=power.get('max','') x=str(pmin)+' / '+str(pmax) h+=' \n' # Memory usage TBD h+=' \n' # Accuracy (take from model info) acc=xd.get('features',{}).get('accuracy','') acc5=xd.get('features',{}).get('accuracy_top5','') x=str(acc) if acc5!='': x+=' / '+str(acc5) if x=='': x='-' h+=' \n' # Will be used to optimize model topology and parameters x='default' xfiles=xd.get('files',[]) for xf in xfiles: if xf.get('filename','')=='deploy.prototxt': xx1=xf.get('from_data_uoa','') if xx1=='': xx1=scenario xx2=xf.get('path','') x='deploy.prototxt\n' break h+=' \n' # Cost (take from platform meta) hc='-' last_cost=0 if len(hd)>0: costs=hd.get('features',{}).get('cost',[]) hc='' if len(costs)>0: last_cost=int(costs[0].get('price','0')) for c in costs: if hc!='': hc+='
\n' hc+=''+str(c.get('price',''))+' '+c.get('currency','')+ ' - '+c.get('desc','')+' ('+c.get('date','')+')' h+=' \n' # TBD: all other costs h+=' \n' # Mispredictions and unexpected behavior if not min_view: x='' for q in mp: ca=q.get('correct_answer','') mi=q.get('mispredicted_image','') mr=q.get('misprediction_results','') if mr!='': j1=mr.find('\n') if j1>0: j2=mr.find('\n',j1+1) if j2>0: mr=mr[j1:j2] xx=ca if mi!='': y=work['self_module_uid'] if cmuoa!='': y=cmuoa url=url0+'action=pull&common_action=yes&cid='+y+':'+duid+'&filename='+mi if ca=='': ca='unknown' xx=''+ca+'' if x!='': x+='
\n' x+=''+mr+'
'+xx+'
\n' if tmin==0: x+='
Bug detected
\n' h+=' \n' # All images h+=' \n' # Extra info about platform x=cpu_name if cpu_uid!='': x=''+x+'' h+=' \n' # CPU ABI if not min_view: h+=' \n' x=gpu_name if gpu_uid!='': x=''+x+'' h+=' \n' x=os_name if os_uid!='': x=''+x+'' h+=' \n' # Data x=work['self_module_uid'] if cmuoa!='': x=cmuoa raw_data_url=url0+'wcid='+x+':'+duid h+=' \n' # User if not min_view: h+=' \n' h+=' \n' tdelta=tmax-tmin # show best specie! # if tdelta>0.1: tdelta=0.1 # to avoid messy graph - just show that there is an issue with variation ... if tmin>0: # to skip bugs # check accuracy xcol='#0000bf' size5=2 if acc5!='' and acc5>0: top5=(float(acc5)-0.8)*1000+50 size5=(float(acc5)-0.75)*40+2 col=hex(int(top5))[2:] xcol='#'+col+col+'ff' # mem usage ## xx=(int(model_weights_size/20))*5.35+127 # x=hex(int((xx)))[2:] # if len(x)<2: x='0'+x # mcol='#0000'+x divide=80 if i.get(ckey+'engine')=='TFLite CPU': divide=3 sizem=int(1+(model_weights_size/divide))+3 bgraph2['0'].append([model_weights_size,tmin,tmin+tdelta]) igraph2['0'].append({'size':3,'color':xcol}) features={} X=0 Y=0 YD=0 YS=0 to_add=False if v9b=='time_cost': if last_cost>0: to_add=True if debug: sizem=int(1+(model_weights_size/80))*6 X=last_cost Y=tmin YD=tdelta YS=sizem else: if acc>0 and ttmin>0: to_add=True if debug: sizem=int(1+(model_weights_size/80))*6 X=ttmin if v9b.endswith('top1'): Y=acc else: Y=acc5 YD=ttdelta YS=sizem if Y=='': Y=0 YD=0 if to_add: # FGG: must improve to make a constant threshold afterwards (after knowing Ymin and Ymax) if (type(Y)==float or type(Y)==int) and Y>0 and YD>0 and YD>float(Y)*0.05: YD=float(Y)*0.05 bgraph3['0'].append([X,Y,Y+YD]) # igraph3['0'].append({'size':YS, 'color':xcol, 'features':meta, 'url':'', 'url_ext':raw_data_url}) igraph3['0'].append({'size':YS, 'color':xcol, 'features':meta, 'anchor':buid}) # For frontier # rx=ck.gen_uid({}) # if rx['return']>0: return rx # puid=rx['data_uid'] # f_bgraph3[puid]={"X":X, "Y":Y} # f_igraph3[puid]={'size':YS, 'tdelta':YD, 'features':meta, 'url':'', 'url_ext':raw_data_url} h+='
#PlatformCrowd scenarioVersionsModel weight sizeTotal time (min/max sec.)

* LATENCY *

Init network time (min/max sec.)Image preparation (min/max sec.)Classification time (min/max sec.)

* THROUGHPUT *

Prediction probabilityPower consumption (W)
min / max
Memory usage (MB)Model accuracy on ImageNetModel topology and parametersHW costsAll usage costs (preparation, training, inference, errors, etc)Mispredictions and unexpected behaviorImage featuresCPUCPU ABIGPUOSData UID / Behavior UIDUser
'+str(ix)+''+x+''+xx+''+ver+''+str(model_weights_size)+' MB'+xx+''+xx+''+x+''+x+'-'+x+''+x+''+hc+''+x+''+key.replace(' ',' ')+''+x+''+cpu_abi+''+x+''+x+''+duid+' '+buid+''+user+'
\n' h+='
\n' if debug: h+='\n

Debug time (preparing html of a table): '+str(time.time()-dt)+' sec.

\n' if cmuoa=='': h+='\n' # 1st graph if len(bgraph['0'])>0: dt=time.time() ii={'action':'plot', 'module_uoa':cfg['module_deps']['graph'], "table":bgraph, "ymin":0, "ignore_point_if_none":"yes", "plot_type":"d3_2d_bars", "display_y_error_bar":"no", "title":"Powered by Collective Knowledge", "x_ticks_period":50, "axis_x_desc":"Experiment", "axis_y_desc":"DNN image classification time (s)", "plot_grid":"yes", "d3_div":"ck_interactive", "image_width":"900", "image_height":"400", "wfe_url":url0} # r=ck.access(ii) # if r['return']==0: # x=r.get('html','') # if x!='': # st+=r.get('style','') # # h+='
\n' # h+='

\n' # h+='
\n' # h+='
\n' # h+=x+'\n' # h+='
\n' # h+='
\n' # h+='
\n' # 2nd graph if len(bgraph2['0'])>0: ii={'action':'plot', 'module_uoa':cfg['module_deps']['graph'], "table":bgraph2, "table_info":igraph2, "xmin":-10, "ymin":0, "ignore_point_if_none":"yes", "plot_type":"d3_2d_scatter", "display_y_error_bar2":"no", "title":"Powered by Collective Knowledge", "x_ticks_period":50, # "axis_x_desc":"Weigths size (MB)", # "axis_y_desc":"DNN image classification time (s)", "plot_grid":"yes", "d3_div":"ck_interactive2", "image_width":"900", "image_height":"400", "wfe_url":url0} # r=ck.access(ii) # if r['return']==0: # x=r.get('html','') # if x!='': # st+=r.get('style','') # # h+='
\n' # h+='
\n' # h+='
\n' # h+='\n' # h+='
\n' # h+=x+'\n' # h+='
\n' # h+='
\n' # h+='
\n' # 3nd graph graph_html='' graph_style='' if len(bgraph3['0'])>0: # Fontier r=ck.access({'action':'filter', 'module_uoa':cfg['module_deps']['math.frontier'], 'points':f_bgraph3}) if r['return']>0: return r points=r['points'] bgraph3['1']=[] igraph3['1']=[] if v9b=='time_cost': xdesc='Device price, $' ydesc='image classification time, sec.' elif v9b=='throughput_top1' or v9b=='': xdesc='throughput (image classification), sec.' ydesc='Top1 accuracy' elif v9b=='throughput_top5': xdesc='throughput (image classification), sec.' ydesc='Top5 accuracy' elif v9b=='latency_top1': xdesc='latency (image classification), sec.' ydesc='Top1 accuracy' elif v9b=='latency_top5': xdesc='latency (image classification), sec.' ydesc='Top5 accuracy' for q in points: q1=f_bgraph3[q] q2=f_igraph3[q] bgraph3['1'].append([q1['X'],q1['Y'],q1['Y']+q2['tdelta']]) del(q2['tdelta']) igraph3['1'].append(q2) ii={'action':'plot', 'module_uoa':cfg['module_deps']['graph'], "table":bgraph3, "table_info":igraph3, "ignore_point_if_none":"yes", "plot_type":"d3_2d_scatter", "display_y_error_bar2":"yes", "title":"Powered by Collective Knowledge", "x_ticks_period":50, "axis_x_desc":xdesc, "axis_y_desc":ydesc, "xmin":-0.01, "ymin":0, # "ymax":25, "point_style":{"1":{"color":"#dc3912", "connect_lines":"no"}}, "plot_grid":"no", "d3_div":"ck_interactive3", "image_width":"900", "image_height":"400", "wfe_url":url0} # Temporal hack if debug: ii['out_to_file']='/tmp/dnn-co-design.pdf' ii['customize_dots']='yes' ii['plot_type']='mpl_2d_scatter' r=ck.access(ii) if r['return']==0: graph_html=r.get('html','') if graph_html!='': graph_style=r.get('style','') st+=graph_style hhh+='
\n' hhh+='
\n' hhh+='
\n' # hhh+='Device cost (X axis, €) vs image classification time (Y axis, s.) vs model size (dot size) vs model TOP5 ImageNet accuracy (darker colors for lower accuracy).\n' hhh+='Dot size ~ model size ; dot color ~ model TOP5 ImageNet accuracy (darker colors for lower accuracy).\n' # hhh+='Red dots - surviving species for a given realistic AI scenario.
\n' hhh+=graph_html+'\n' hhh+='
\n' hhh+='
\n' hhh+='
\n' hhh+='
\n' # Extra vision slide hhh+='

\n

\n' if debug: hhh+='\n

Debug time (preparing graph): '+str(time.time()-dt)+' sec.

\n' # Adding table hhh+=h rr={'return':0, 'style':st} if minimal: rr['graph_html']=graph_html rr['style']=graph_style rr['table']=bgraph3 rr['table_info']=igraph3 else: rr['html']=hhh return rr ############################################################################## # process raw results from mobile devices def process(i): """ Input: { } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 } """ import copy # Debug # ck.save_json_to_file({'json_file':'/tmp/xyz1.json','dict':i}) crowd_uid=i.get('crowd_uid','') email=i.get('email','') raw_results=i.get('raw_results',{}) cfb=i.get('cpu_freqs_before',{}) cfa=i.get('cpu_freqs_after',{}) features=i.get('platform_features',{}) fplat=features.get('platform',{}) fos=features.get('os',{}) fcpu=features.get('cpu',{}) fgpu=features.get('gpu',{}) plat_name=fplat.get('name','') plat_uid=features.get('platform_uid','') os_name=fos.get('name','') os_uid=features.get('os_uid','') gpu_uid=features.get('gpu_uid','') cpu_name=fcpu.get('name','') cpu_abi=fcpu.get('cpu_abi','') if cpu_name=='' and cpu_abi!='': cpu_name='unknown-'+fcpu.get('cpu_abi','') cpu_uid=features.get('cpu_uid','') gpu_name=fgpu.get('name','') gpgpu_name='' gpgpu_uid='' sn=fos.get('serial_number','') # Prepare high-level experiment meta meta={'cpu_name':cpu_name, 'cpu_abi':cpu_abi, 'os_name':os_name, 'plat_name':plat_name, 'gpu_name':gpu_name, 'gpgpu_name':gpgpu_name, 'crowd_uid':crowd_uid} # Load scenario and update meta r=ck.access({'action':'load', 'module_uoa':cfg['module_deps']['experiment.scenario.mobile'], 'data_uoa':crowd_uid}) if r['return']>0: return r xd=r['dict'] meta['engine']=xd.get('engine','') meta['model']=xd.get('model','') mmeta=copy.deepcopy(meta) # Extra meta which is not used to search similar case ... mmeta['platform_uid']=plat_uid mmeta['os_uid']=os_uid mmeta['cpu_uid']=cpu_uid mmeta['gpu_uid']=gpu_uid mmeta['gpgpu_uid']=gpgpu_uid # Generate behavior UID rx=ck.gen_uid({}) if rx['return']>0: return rx buid=rx['data_uid'] raw_results['user']=email raw_results['behavior_uid']=buid # Check if already exists duid='' ddd={} ii={'action':'search', 'module_uoa':work['self_module_uid'], 'repo_uoa':ck.cfg.get('record_local_repo_uoa',''), 'search_dict':{'meta':meta}, 'add_meta':'yes'} rx=ck.access(ii) if rx['return']>0: return rx lst=rx['lst'] if len(lst)==1: duid=lst[0]['data_uid'] ddd=lst[0]['meta'] else: rx=ck.gen_uid({}) if rx['return']>0: return rx duid=rx['data_uid'] # We keep time1,2,3 just for compatibility with the first beta version t=raw_results.get('time',[]) tx=raw_results.get('time1',None) if tx!=None: t.append(tx) tx=raw_results.get('time2',None) if tx!=None: t.append(tx) tx=raw_results.get('time3',None) if tx!=None: t.append(tx) raw_results['time']=t # Check XOpenME xopenme={} yopenme=raw_results.get('xopenme',[]) for xx in yopenme: for k in xx: v=xx[k] if k not in xopenme: xopenme[k]=[] if v!=None: xopenme[k].append(v) raw_results['xopenme']=xopenme # Record freq (not checking if changed at this stage) raw_results['cpu_freqs_before']=[cfb] raw_results['cpu_freqs_after']=[cfa] # Process freq before and freq after (for now no any intelligence) fb=raw_results # Process results results=ddd.get('all_raw_results',[]) # Check if already exists with this image topology found=False for q in results: if (q.get('image_height',None)==raw_results.get('image_height',None) and \ q.get('image_width',None)==raw_results.get('image_width',None)) or \ (q.get('image_height',None)==raw_results.get('image_width',None) and \ q.get('image_width',None)==raw_results.get('image_height',None)): t=q.get('time',[]) for tx in raw_results.get('time',[]): t.append(tx) q['time']=t tkk1=raw_results.get('xopenme',{}) tkk2=q.get('xopenme',{}) for tk in tkk1: if tk not in tkk2: tkk2[tk]=[] for tx in tkk1[tk]: if tx!=None: tkk2[tk].append(tx) q['xopenme']=tkk2 fb=q.get('cpu_freqs_before',[]) fb.append(cfb) q['cpu_freqs_before']=fb fa=q.get('cpu_freqs_after',[]) fa.append(cfa) q['cpu_freqs_before']=fa buid=q.get('behavior_uid','') found=True break if not found: results.append(raw_results) ddd['all_raw_results']=results xmeta=ddd.get('meta',{}) xmeta.update(mmeta) ddd['meta']=xmeta # Update meta rx=ck.access({'action':'update', 'module_uoa':work['self_module_uid'], 'data_uoa':duid, 'repo_uoa':ck.cfg.get('record_local_repo_uoa',''), 'dict':ddd, 'substitute':'yes', 'sort_keys':'yes'}) if rx['return']>0: return rx # Prepare url with results rx=ck.access({'action':'form_url_prefix', 'module_uoa':'wfe'}) if rx['return']>0: return rx url=rx['url']+'&action=index&module_uoa=wfe&native_action=show&native_module_uoa=program.optimization&scenario='+work['self_module_uid']+'&highlight_behavior_uid='+buid return {'return':0, 'status':'Results successfully added to Collective Knowledge (UID='+duid+')!', 'data_uid':duid, 'behavior_uid':buid, 'result_url':url} ############################################################################## # record unexpected behavior def process_unexpected_behavior(i): """ Input: { } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 } """ import os duid=i.get('data_uid','') buid=i.get('behavior_uid','') cuid=i.get('crowd_uid','') rres=i.get('raw_results','') ca=i.get('correct_answer','') file_base64=i.get('file_base64','') # Find data r=ck.access({'action':'load', 'module_uoa':work['self_module_uid'], 'data_uoa':duid}) if r['return']>0: return r d=r['dict'] p=r['path'] # Find behavior found=False arr=d.get('all_raw_results',[]) for q in arr: if q.get('behavior_uid','')==buid: found=True break if not found: return {'return':1, 'error':'can\'t find behavior '+buid+' in entry '+duid} # Generate UID for the file with unexpected behavior rx=ck.gen_uid({}) if rx['return']>0: return rx ff='misprediction-image-'+rx['data_uid']+'.jpg' pf=os.path.join(p,ff) mp=q.get('mispredictions',[]) qq={} qq['misprediction_results']=rres qq['mispredicted_image']=ff qq['correct_answer']=ca mp.append(qq) q['mispredictions']=mp # Record file rx=ck.convert_upload_string_to_file({'file_content_base64':file_base64, 'filename':pf}) if rx['return']>0: return rx # Update entry (should add lock in the future for parallel processing) r=ck.access({'action':'update', 'module_uoa':work['self_module_uid'], 'data_uoa':duid, 'dict':d, 'sort_keys':'yes', 'substitute':'yes', 'ignore_update':'yes'}) if r['return']>0: return r return {'return':0} ############################################################################## # generate scenario to crowdsource benchmarking/optimization of DNN engines/models on Android devices def generate(i): """ Input: { (prune_target_os) - prune generated scenarios by this target OS (ABI) (prune_engine) - prune generated scenarios by this engine } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 } """ import os import copy import shutil import itertools o=i.get('out','') p_tos=i.get('prune_target_os','') p_engine=i.get('prune_engine','') # Get platform params ck.out('Detecting host platform info ...') ck.out('') i['action']='detect' i['module_uoa']=cfg['module_deps']['platform'] i['out']='' rpp=ck.access(i) if rpp['return']>0: return rpp hos=rpp['host_os_uoa'] hosd=rpp['host_os_dict'] # Search all scenarios r=ck.access({'action':'search', 'module_uoa':cfg['module_deps']['experiment.scenario.mobile'], 'add_meta':'yes'}) if r['return']>0: return r lst=r['lst'] if len(lst)==0: return {'return':1, 'error':'no pre-recorded crowd-scenarios found ...'} uids={} found=False engine_meta={} engine_state='' # If engine changes, clean up found uids ... # Go through required engines (CPU,OpenCL) and ABIs, and compile classification with related libs ... for prog,b in list(itertools.product(cfg['prog_uoa'],abis)): abi=b['abi'] os_uoa=b['os_uoa'] if i.get('target_os','')!='': os_uoa=i['target_os'] engine=prog['engine'] prog_uoa=prog['program_uoa'] engine_key=prog['engine-deps-key'] engine_lib=prog.get('engine-lib','') # Check if should prune if p_tos!='' and p_tos!=os_uoa: continue if p_engine!='' and p_engine!=engine: continue if engine!=engine_state: uids={} found=False engine_state=engine ck.out(line) ck.out('Preparing "'+engine+'" engine with "'+abi+'" ABI for crowd-benchmarking and crowd-tuning ...') ck.out('') r=ck.inp({'text':'Press Enter to generate this scenario or N to skip this scenario: '}) s=r['string'] if s!='': continue # Compile classification (should compile all deps) ck.out('') ck.out('Compiling classification (and libs) ...') ii={'action':'compile', 'module_uoa':cfg['module_deps']['program'], 'data_uoa':prog_uoa, 'host_os':hos, 'target_os':os_uoa, 'speed':'yes', 'out':o} r=ck.access(ii) if r['return']>0: return r # Get various info cc=r.get('characteristics',{}) cs=cc.get('compilation_success','') if cs!='yes': return {'return':1, 'error':'compilation failed'} md5=cc.get('md5_sum','') bs=cc.get('binary_size',0) misc=r.get('misc',{}) pf=misc.get('path','') td=misc.get('tmp_dir','') of=misc.get('target_exe','') pv=misc.get('program_version','') pp=os.path.join(pf,td,of) if not os.path.isfile(pp): return {'return':1, 'error':'target binary not found ('+pp+')'} # Get info about deps deps=r.get('deps',{}) # Get info about lib if needed cdeps=deps.get(engine_key,{}) cfp='' lmd5='' ls=0 if engine_lib!='': # Force .so (sometimes points to .a) cfp=cdeps.get('cus',{}).get('full_path','') cfp=os.path.join(os.path.dirname(cfp),engine_lib) if os.path.isfile(cfp): r=ck.run_and_get_stdout({'cmd':hosd['md5sum']+' '+cfp, 'shell':'no'}) if r['return']>0: return r sto=r['stdout'].split(' ') if len(sto)==0: return {'return':1, 'error':'can\'t get MD5 of '+engine_lib} lmd5=sto[0] # MD5 of caffe lib ls=os.path.getsize(cfp) # xopenme odeps=deps.get('xopenme',{}) ofp=odeps.get('cus',{}).get('full_path','') # Force .so (sometimes points to .a) ofp=os.path.join(os.path.dirname(ofp),libxopenme) if not os.path.isfile(ofp): return {'return':1, 'error':'xopenme plugin not found ('+ofp+')'} r=ck.run_and_get_stdout({'cmd':hosd['md5sum']+' '+ofp, 'shell':'no'}) if r['return']>0: return r sto=r['stdout'].split(' ') if len(sto)==0: return {'return':1, 'error':'can\'t get MD5 of librtlxopenme.so'} omd5=sto[0] # MD5 of caffe lib ops=os.path.getsize(ofp) # Get versions of all deps dv={} for x in deps: dv[x]={} cx=deps[x].get('cus',{}) dv[x]['version']=cx.get('version','') dv[x]['revision']=cx.get('git_info',{}).get('revision','') dv[x]['iso_datetime_cut_revision']=cx.get('git_info',{}).get('iso_datetime_cut_revision','') dv[x]['tags']=deps[x].get('tags',[]) # Prepare info pack about this experiment meta={'classification_version':pv, 'deps':dv, 'host_os':hos, 'target_os':os_uoa} # Search caffe original code with a given ABI ck.out('') ck.out('Checking / updating scenario files ...') changed_files=[] # Which libs were substituted for q in lst: duid=q['data_uid'] duoa=q['data_uoa'] ruoa=q['repo_uid'] d=q['meta'] if d.get('outdated','')=='yes': # skip archived entries continue if d.get('engine','')!=engine: continue pe=q['path'] # Path to entry files=d.get('files',[]) # Process main binary files (in code directory) for ff in files: fduoa=ff.get('from_data_uoa','') if fduoa=='': sabi=ff.get('supported_abi',[]) if abi in sabi: fn=ff.get('filename','') p=ff.get('path','') xfs=ff.get('file_size',0) xmd5=ff.get('md5','') pep=os.path.join(pe,p,fn) if (fn==engine_lib and (not os.path.isfile(pep) or (xmd5!=lmd5 and xfs!=ls))) or \ (fn=='classification' and (not os.path.isfile(pep) or (xmd5!=md5 and xfs!=bs))) or \ (fn==libxopenme and (not os.path.isfile(pep) or (xmd5!=omd5 and xfs!=ops))): if not found: # If first time, tell that old scenario will be removed! ck.out('') ck.out('WARNING: we found OUTDATED binaries for this scenario') ck.out(' and plan to remove them - make sure that you archived them!') ck.out('') r=ck.inp({'text':'Would you like to proceed and remove outdated binaries (Y/n): '}) if r['return']>0: return r s=r['string'].strip().lower() if s!='': return {'return':0} found=True ck.out('') ck.out('Updating '+fn+' in '+duoa+' ('+p+')') oduid=p.split('/')[1] nduid=uids.get(oduid,'') # Generate new UID if nduid=='': r=ck.gen_uid({}) if r['return']>0: return r nduid=r['data_uid'] uids[oduid]=nduid ck.out(' * New code UID for '+oduid+' : '+nduid) else: ck.out(' * Reusing UID for '+oduid+' : '+nduid) np=os.path.join(pe,'code',nduid,abi) ck.out(' * New path: '+np) # Create new dir and copy file if not os.path.isdir(np): os.makedirs(np) fnp=os.path.join(np,fn) if fn==engine_lib: shutil.copy(cfp, fnp) elif fn=='classification': shutil.copy(pp, fnp) else: shutil.copy(ofp, fnp) # Remove old path px=os.path.join(pe,p) if os.path.isdir(px): ck.out(' * Removing dir: '+px) shutil.rmtree(px) # If whole directory is empty, remove it too px=os.path.join(pe,'code',oduid) if os.path.isdir(px) and len(os.listdir(px))==0: ck.out(' * Removing dir: '+px) shutil.rmtree(px) # Changing meta zp=np if not zp.startswith('code') and not zp.startswith('data'): j1=zp.find('code/') if j1<0: j1=zp.find('data/') if j1>0: zp=zp[j1:] changed_before={'filename':fn, 'from_data_uoa':duid, 'path':p, 'file_size':xfs, 'md5':xmd5} changed_after={'filename':fn, 'from_data_uoa':duid, 'path':zp} if fn==engine_lib: changed_after['file_size']=ls changed_after['md5']=lmd5 elif fn=='classification': changed_after['file_size']=bs changed_after['md5']=md5 else: changed_after['file_size']=ops changed_after['md5']=omd5 ff.update(changed_after) del(ff['from_data_uoa']) # Updating global meta of engine engine_meta[abi]={'program_version':pv, 'deps_versions':dv} d['engine_meta']=engine_meta # Add to changed changed_files.append({'before':changed_before, 'after':changed_after}) else: ck.out('') ck.out('*** Scenario binary '+fn+' is up to date! ***') # If changed original files, change them in all other meta if len(changed_files)>0: ck.out('') ck.out('Updating all scenarios with new files ...') ck.out('') for q in lst: duid=q['data_uid'] duoa=q['data_uoa'] muid=q['module_uid'] ruoa=q['repo_uid'] d=q['meta'] if d.get('outdated','')=='yes': # skip archived entries continue if d.get('engine','')!=engine: continue ck.out(' * '+duoa) d['engine_meta']=engine_meta pp=q['path'] # Path to entry files=d.get('files',[]) for ff in files: fduoa=ff.get('from_data_uoa','') if fduoa!='': # Check if needs to be changed ... for c in changed_files: before=c['before'] after=c['after'] rx=ck.compare_dicts({'dict1':ff, 'dict2':before}) if rx['return']>0: return rx if rx['equal']=='yes': ff.update(after) # Update entry now (aggregating all above changes) r=ck.access({'action':'update', 'module_uoa':muid, 'data_uoa':duid, 'repo_uoa':ruoa, 'dict':d, 'substitute':'yes', 'ignore_update':'yes', 'sort_keys':'yes'}) if r['return']>0: return r return {'return':0}