Package: ml-pipeline-engine
ML Pipeline EngineГрафовый движок для конвейеров ML-моделейЧто нужно, чтобы сделать свой пайплайн?Написать классы узловСвязать узлы посредством указания зависимостиПоддерживаемые типы узловПротоколыDataSourceFeatureBaseMLModelBaseProcessorBaseFeatureVectorizerBaseПримерыПростейший пример на основе функцийdefinvert_number(num:float):return-numdefadd_const(num:Input(invert_number),const:float=0.1):returnnum+constdefdouble_number(num:Input(add_const)):returnnum*2build_dag(input_node=invert_number,output_node=double_number).run(pipeline_context(num=2.5))Пример, который реализует переключение нод по условию (Аналог switch)defident(num:float):returnnumdefswitch_node(num:Input(ident)):ifnum<0.0:return'invert'ifnum==0.0:return'const'ifnum==1.0:return'double'return'add_sub_chain'defconst_noinput():return10.0defadd_100(num:Input(ident)):returnnum+100defadd_some_const(num:Input(ident),const:float=9.0):returnnum+constdefinvert_number(num:Input(ident)):return-numdefsub_ident(num:Input(add_some_const),num_ident:Input(ident)):returnnum-num_identdefdouble_number(num:Input(ident)):returnnum*2SomeSwitchCase=SwitchCase(switch=switch_node,cases=[('const',const_noinput),('double',double_number),('invert',invert_number),('add_sub_chain',sub_ident),],)defcase_node(num:SomeSwitchCase,num2:Input(ident),num3:Input(add_100)):returnnum+num2+num3defout(num:Input(case_node)):returnnumbuild_dag(input_node=ident,output_node=out).run(pipeline_context(num=input_num))Пример с использованием классов как узлов графаclassSomeInput(NodeBase):name='input'defprocess(self,base_num:int,other_num:int)->dict:return{'base_num':base_num,'other_num':other_num,}classSomeDataSource(NodeBase):name='some_data_source'defcollect(self,inp:Input(SomeInput))->int:returninp['base_num']+100classSomeFeature(NodeBase):name='some_feature'defextract(self,ds_value:Input(SomeDataSource),inp:Input(SomeInput))->int:returnds_value+inp['other_num']+10classSomeVectorizer(NodeBase):name='some_vectorizer'defvectorize(self,feature_value:Input(SomeFeature))->int:returnfeature_value+20classSomeMLModel(NodeBase):name='some_model'defpredict(self,vec_value:Input(SomeVectorizer)):return(vec_value+30)/100build_dag(input_node=SomeInput,output_node=SomeMLModel).run(pipeline_context(base_num=10,other_num=5))Пример с использованием Generic-нод, которые под собой хранят общее поведение и не зависимы от конкретной моделиclassSomeInput(NodeBase):name='input'defprocess(self,base_num:int,other_num:int)->dict:return{'base_num':base_num,'other_num':other_num,}classSomeDataSource(NodeBase):name='some_data_source'defcollect(self,inp:Input(SomeInput))->int:returninp['base_num']+100classSomeCommonFeature(NodeBase):name='some_feature'defextract(self,ds_value:Input(SomeDataSource),inp:Input(SomeInput))->int:returnds_value+inp['other_num']+10# Пример Generic-нодыclassGenericVectorizer(NodeBase):name='some_vectorizer'defvectorize(self,feature_value:InputGeneric(NodeLike))->int:returnfeature_value+20classAnotherFeature(NodeBase):name='another_feature'defextract(self,inp:Input(SomeInput))->int:returninp['base_num']+100_000# Первый переопределенный подграфSomeParticularVectorizer=build_node(# noqaGenericVectorizer,feature_value=Input(SomeCommonFeature),)# Второй переопределенный подграфAnotherParticularVectorizer=build_node(# noqaGenericVectorizer,feature_value=Input(AnotherFeature),)classSomeMLModel(NodeBase):name='some_model'defpredict(self,vec_value:Input(SomeParticularVectorizer)):return(vec_value+30)/100classAnotherMlModel(NodeBase):name='another_model'defpredict(self,vec_value:Input(AnotherParticularVectorizer)):return(vec_value+30)/100# Первый графbuild_dag(input_node=SomeInput,output_node=SomeMLModel).run(pipeline_context(base_num=10,other_num=5))# Второй графbuild_dag(input_node=SomeInput,output_node=AnotherMlModel).run(pipeline_context(base_num=10,other_num=5))Пример указания ретраяclassHierarchyException(Exception):passclassSecondHierarchyException(Exception):passclassSomeMlModel(NodeBase):node_type='ml_model'delay=1.1attempts=5exceptions=(SecondHierarchyException,)defpredict(self,*args,**kwargs):...Пример запуска чарта с хранилищем артефактовfromml_pipeline_engine.artifact_store.store.filesystemimportFileSystemArtifactStoredefinvert_number(num:float):return-numdefadd_const(num:Input(invert_number),const:float=0.1):returnnum+constdefdouble_number(num:Input(add_const)):returnnum*2dag=build_dag(input_node=invert_number,output_node=double_number)chart=PipelineChart(model_name='name',entrypoint=dag,# Тут может быть указан артефакт стор, который пишет данные в S3artifact_store=...ifnotis_local()elseFileSystemArtifactStore,# Добавление эвентов позволяет отслеживать полезную нагрузку, результаты и ошибки узлов для построения графиковevent_managers=[DatabaseEventManager],)result=chart.run(input_kwargs=dict(...,),)Установка либы для разработкиПервоначальная настройкаУстанавливаем Python>=3.8Устанавливаем зависимости для тестирования:pip3install-e".[tests]"Подключаем pre-commit hookspre-commitinstall-fЗапуск тестовpython-mpytesttests