From 62c4389b789ff850b56cca1cf2dea4b9cd865d44 Mon Sep 17 00:00:00 2001 From: TRD Date: Wed, 17 Nov 2021 17:52:25 +0530 Subject: [PATCH] Major Enhancement(Support Tensorflow Model) --- blobcity/code_gen/Generator.py | 36 +++++++++---- blobcity/code_gen/IpynbMeta.py | 3 +- blobcity/code_gen/PyMeta.py | 2 + blobcity/code_gen/SourceCodes.py | 13 ++--- blobcity/main/driver.py | 4 +- blobcity/main/modelSelection.py | 87 ++++++++++++++++++++++++++++---- blobcity/store/Model.py | 49 +++++++++++++----- 7 files changed, 153 insertions(+), 41 deletions(-) diff --git a/blobcity/code_gen/Generator.py b/blobcity/code_gen/Generator.py index d803de9..83b9473 100644 --- a/blobcity/code_gen/Generator.py +++ b/blobcity/code_gen/Generator.py @@ -215,15 +215,16 @@ def modeler(yml_data,key,with_doc,codes="",nb=None): The function adds code syntax related to the Machine learning model initialization and training. """ - param=SourceCode.parameters.replace("PARAM", str(yml_data['model']['parameters'])) - model=SourceCode.models_init.replace("MODELNAME", str(yml_data['model']['type'])) + if yml_data['model']['type'] not in ['TF','tf','Tensorflow']: + param=SourceCode.parameters.replace("PARAM", str(yml_data['model']['parameters'])) + model=SourceCode.models_init.replace("MODELNAME", str(yml_data['model']['type'])) + else:param,model="\n",SourceCode.tf_load + imports,metaDesc=SourceCode.models[key][yml_data['model']['type']],PyComments.models[key][yml_data['model']['type']] if nb!=None: nb['cells'][1]['source']=nb['cells'][1]['source']+imports - if with_doc: - nb['cells'].append(nbf.v4.new_markdown_cell(IpynbComments.models[key][yml_data['model']['type']])) - + if with_doc:nb['cells'].append(nbf.v4.new_markdown_cell(IpynbComments.models[key][yml_data['model']['type']])) nb['cells'].append(nbf.v4.new_code_cell(param+model)) return nb elif codes!="": @@ -233,7 +234,7 @@ def modeler(yml_data,key,with_doc,codes="",nb=None): codes=codes+"# "+metaDesc return codes+param+model -def model_metrics(key,codes="",nb=None,with_doc=False): +def model_metrics(yml_data,key,codes="",nb=None,with_doc=False): """ param1: dictionary : AutoAI steps data param2: string : Code syntaxs @@ -246,11 +247,26 @@ def model_metrics(key,codes="",nb=None,with_doc=False): if with_doc: if nb!=None: nb['cells'].append(nbf.v4.new_markdown_cell(IpynbComments.procedure['metrics'])) else: codes=codes+PyComments.procedure['metrics'] + if nb!=None and codes=="": - nb['cells'].append(nbf.v4.new_code_cell(SourceCode.metric[key])) + if yml_data['model']['type'] not in ['TF','tf','Tensorflow']: + nb['cells'].append(nbf.v4.new_code_cell(SourceCode.metric[key])) + else: + if key == 'Classification': + tf_metric_type=SourceCode.tf_metric[key]['binary'] if yml_data['model']['classification_type']=='binary' else SourceCode.tf_metric[key]['multi'] + nb['cells'].append(nbf.v4.new_code_cell(tf_metric_type)) + else:nb['cells'].append(nbf.v4.new_code_cell(SourceCode.tf_metric[key])) return nb else: - return codes+SourceCode.metric[key] + if yml_data['model']['type'] not in ['TF','tf','Tensorflow']: + return codes+SourceCode.metric[key] + else: + if key == 'Classification': + tf_metric_type=SourceCode.tf_metric[key]['binary'] if yml_data['model']['classification_type']=='binary' else SourceCode.tf_metric[key]['multi'] + return codes+tf_metric_type + else: + return codes+SourceCode.tf_metric[key] + def pycoder(yml_data,CGpath,doc=False): """ @@ -270,7 +286,7 @@ def pycoder(yml_data,CGpath,doc=False): codes=add_corr_matrix(codes=codes,with_doc=doc) codes=splits(codes=codes,with_doc=doc) codes=modeler(yml_data,key,doc,codes=codes) - codes=model_metrics(key,codes=codes,with_doc=doc) + codes=model_metrics(yml_data,key,codes=codes,with_doc=doc) write_pycode(CGpath,codes) def ipynbcoder(yml_data,CGpath,doc=True): @@ -292,7 +308,7 @@ def ipynbcoder(yml_data,CGpath,doc=True): nb=add_corr_matrix(nb=nb,with_doc=doc) nb=splits(nb=nb,with_doc=doc) nb=modeler(yml_data,key,doc,nb=nb) - nb=model_metrics(key,nb=nb,with_doc=doc) + nb=model_metrics(yml_data,key,nb=nb,with_doc=doc) write_ipynbcode(CGpath,nb) def code_generator(data,filepath,doc=None): diff --git a/blobcity/code_gen/IpynbMeta.py b/blobcity/code_gen/IpynbMeta.py index 39423fd..9586937 100644 --- a/blobcity/code_gen/IpynbMeta.py +++ b/blobcity/code_gen/IpynbMeta.py @@ -19,6 +19,7 @@ class IpynbComments: models={ 'Classification':{ + 'TF':"""### Neural Network/Deep Learning Model \nDeep learning is a subset of machine learning, which is essentially a neural network with three or more layers. These neural networks attempt to simulate the behavior of the human brain-albeit far from matching its ability-allowing it to 'learn' from large amounts of data. While a neural network with a single layer can still make approximate predictions, additional hidden layers can help to optimize and refine for accuracy.""", 'LinearDiscriminantAnalysis':"""### Model\n A classifier with a linear decision boundary, generated by fitting class conditional densities to the data and using Bayes’ rule.\n The model fits a Gaussian density to each class, assuming that all classes share the same covariance matrix.\n The fitted model can also be used to reduce the dimensionality of the input by projecting it to the most discriminative directions, using the transform method. 1. solver: Solver to use, possible values: {'svd', 'lsqr', 'eigen'} @@ -337,6 +338,7 @@ class IpynbComments: 7. **n_iter_no_change** -> Number of iterations with no improvement to wait before early stopping.""" }, 'Regression':{ + 'TF':"""### Neural Network/Deep Learning Model \nDeep learning is a subset of machine learning, which is essentially a neural network with three or more layers. These neural networks attempt to simulate the behavior of the human brain-albeit far from matching its ability-allowing it to 'learn' from large amounts of data. While a neural network with a single layer can still make approximate predictions, additional hidden layers can help to optimize and refine for accuracy.""", 'OrthogonalMatchingPursuit':"""### Model \nOrthogonalMatchingPursuit and orthogonal_mp implements the OMP algorithm for approximating the fit of a linear model with constraints imposed on the number of non-zero coefficients \n OMP is based on a greedy algorithm that includes at each step the atom most highly correlated with the current residual. It is similar to the simpler matching pursuit (MP) method, but better in that at each iteration, the residual is recomputed using an orthogonal projection on the space of the previously chosen dictionary elements. @@ -765,7 +767,6 @@ class IpynbComments: 2. **tol** -> Stopping criterion. 3. **max_iter** -> The maximal number of iterations for the solver.""", -'TF':'Neural Network Model Description' } } diff --git a/blobcity/code_gen/PyMeta.py b/blobcity/code_gen/PyMeta.py index caf0289..e2f9070 100644 --- a/blobcity/code_gen/PyMeta.py +++ b/blobcity/code_gen/PyMeta.py @@ -42,6 +42,7 @@ class PyComments: 'LGBMClassifier':'LightGBM is a gradient boosting framework that uses tree based learning algorithms.\n# It is designed to be distributed and efficiency\r\n', 'PassiveAggressiveClassifier':'The passive-aggressive algorithms are a family of algorithms for large-scale learning.\n# They are similar to the Perceptron in that they do not require a learning rate. However, contrary to the Perceptron,\n# they include a regularization parameter C.\r\n', 'LinearDiscriminantAnalysis':'A classifier with a linear decision boundary, generated by fitting class conditional densities to the data and using Bayes’ rule.\n #The model fits a Gaussian density to each class, assuming that all classes share the same covariance matrix.\n# The fitted model can also be used to reduce the dimensionality of the input by projecting it to the most discriminative directions, using the transform method.\r\n', + 'TF':"Deep learning is a subset of machine learning, which is essentially a neural network with three or more layers.\n# These neural networks attempt to simulate the behavior of the human brain—albeit far from matching its ability—allowing it to “learn” from large amounts of data.\n# While a neural network with a single layer can still make approximate predictions,\n# additional hidden layers can help to optimize and refine for accuracy." }, 'Regression':{ 'OrthogonalMatchingPursuit':"OrthogonalMatchingPursuit and orthogonal_mp implements the OMP algorithm for approximating\n# the fit of a linear model with constraints imposed on the number of non-zero coefficients\n# OMP is based on a greedy algorithm that includes at each step the atom most highly\n# correlated with the current residual. It is similar to the simpler matching pursuit (MP) method, but better in that at each iteration, the residual is recomputed using an orthogonal projection on the space of the previously chosen dictionary elements.", @@ -72,6 +73,7 @@ class PyComments: 'HuberRegressor':"Linear regression model that is robust to outliers.\n# The Huber Regressor optimizes the squared loss for the samples\n# where |(y - X'w) / sigma| < epsilon and the absolute loss for the samples where |(y - X'w) / sigma| > epsilon, where w and sigma are parameters to be optimized.\n# The parameter sigma makes sure that if y is scaled up or down by a certain factor, one does not need to rescale epsilon to achieve the same robustness.\n# Note that this does not take into account the fact that the different features of X may be of different scales.\n# This makes sure that the loss function is not heavily influenced by the outliers while not completely ignoring their effect.\r\n", 'ElasticNet':'Elastic Net first emerged as a result of critique on Lasso, whose variable selection can be too dependent on data and thus unstable.\n# The solution is to combine the penalties of Ridge regression and Lasso to get the best of both worlds.\r\n', 'PoissonRegressor':"Poisson regression is a generalized linear model form of regression used to model count data and contingency tables.\n# It assumes the response variable or target variable Y has a Poisson distribution, and assumes the logarithm of its expected value can be modeled by a linear combination of unknown parameters.\n# It is sometimes known as a log-linear model, especially when used to model contingency tables.\r\n", + 'TF':"Deep learning is a subset of machine learning, which is essentially a neural network with three or more layers.\n# These neural networks attempt to simulate the behavior of the human brain—albeit far from matching its ability—allowing it to “learn” from large amounts of data.\n# While a neural network with a single layer can still make approximate predictions,\n# additional hidden layers can help to optimize and refine for accuracy." } } diff --git a/blobcity/code_gen/SourceCodes.py b/blobcity/code_gen/SourceCodes.py index aa20a0a..4d5d659 100644 --- a/blobcity/code_gen/SourceCodes.py +++ b/blobcity/code_gen/SourceCodes.py @@ -87,13 +87,13 @@ class SourceCode: } cor_matrix="f,ax = plt.subplots(figsize=(18, 18))\rmatrix = np.triu(X.corr())\rse.heatmap(X.corr(), annot=True, linewidths=.5, fmt= '.1f',ax=ax, mask=matrix)\rplt.show()\n" - tf_model_load="model = tf.keras.models.load_model(./autoaimodel.h5)" - tf_model_metric={ + tf_load="model = bc.load('PICKLE FILE PATH')\r#summary\rnn=model.model\rnn.summary()\nnn.fit(X_train,Y_train,epochs=10)" + tf_metric={ 'Classification':{ - 'binary':"y_pred=model.predict(Y_test)\ry_pred=np.round(y_pred)# Classification Report\rprint(classification_report(Y_test,y_pred))\r\n", - 'multi':"y_pred=model.predict(Y_test)\ry_pred=np.argmax(y_pred,axis=1)# Classification Report\rprint(classification_report(Y_test,y_pred))\r\n" + 'binary':"\ry_pred=nn.predict(X_test)\ry_pred=np.round(y_pred)# Classification Report\rprint(classification_report(Y_test,y_pred))\r\n", + 'multi':"\rnn=model.model\ry_pred=nn.predict(test_df)\ry_pred=np.argmax(y_pred,axis=1)# Classification Report\rprint(classification_report(Y_test,y_pred))\r\n" }, - 'Regression':"# Metrics\r\ny_pred=model.predict(X_test)\rprint('R2 Score: {:.2f}'.format(r2_score(Y_test,y_pred)))\r"+\ + 'Regression':"# Metrics\r\ntest_df = pd.DataFrame(X_test,columns = X.columns.to_list())\nnn=model.model\ry_pred=nn.predict(test_df)\rprint('R2 Score: {:.2f}'.format(r2_score(Y_test,y_pred)))\r"+\ "print('Mean Absolute Error {:.2f}'.format(mean_absolute_error(Y_test,y_pred)))\r"+\ "print('Mean Squared Error {:.2f}'.format(mean_squared_error(Y_test,y_pred)))" } @@ -121,6 +121,7 @@ class SourceCode: 'LinearDiscriminantAnalysis':'from sklearn.discriminant_analysis import LinearDiscriminantAnalysis\r\n', 'PassiveAggressiveClassifier':'from sklearn.linear_model import PassiveAggressiveClassifier\r\n', 'LGBMClassifier':'from lightgbm import LGBMClassifier\r\n', + 'TF':'import blobcity as bc\r\n' }, 'Regression':{ 'OrthogonalMatchingPursuit':'from sklearn.linear_model import OrthogonalMatchingPursuit\r\n', @@ -151,6 +152,6 @@ class SourceCode: 'HuberRegressor':'from sklearn.linear_model import HuberRegressor\r\n', 'ElasticNet':'from sklearn.linear_model import ElasticNet\r\n', 'PoissonRegressor':'from sklearn.linear_model import PoissonRegressor\r\n', - 'TF':'import tensorflow as tf\r\n' + 'TF':'import blobcity as bc\r\n' } } \ No newline at end of file diff --git a/blobcity/main/driver.py b/blobcity/main/driver.py index 15d932e..640783f 100644 --- a/blobcity/main/driver.py +++ b/blobcity/main/driver.py @@ -33,7 +33,9 @@ def train(file=None, df=None, target=None,features=None,use_neural=False,accurac param3: string: target/dependent column name. - param4: float: range[0.1,1.0] + param4: boolean: whether to train tensorflow models + + param5: float: range[0.1,1.0] return: Model Class Object Performs a model search on the data proivded. A yaml file is generated once the best fit model configuration diff --git a/blobcity/main/modelSelection.py b/blobcity/main/modelSelection.py index a9c6678..a2ba492 100644 --- a/blobcity/main/modelSelection.py +++ b/blobcity/main/modelSelection.py @@ -19,19 +19,33 @@ import numpy as np import pandas as pd from math import isnan +import autokeras as ak +import tensorflow as tf from blobcity.store import Model from blobcity.utils import Progress +from sklearn.metrics import r2_score from blobcity.config import tuner as Tuner from sklearn.exceptions import ConvergenceWarning from sklearn.model_selection import cross_val_score + from blobcity.config import classifier_config,regressor_config with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=ConvergenceWarning) warnings.filterwarnings("ignore", category=DeprecationWarning) + tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.ERROR) os.environ["PYTHONWARNINGS"] = "ignore" """ This python file consists of function to get best performing model for a given dataset. """ + +class CustomCallback(tf.keras.callbacks.Callback): + def on_train_end(self, logs=None): + prog.trials=prog.trials-1 + prog.update_progressbar(1) + def on_epoch_end(self, epoch, logs=None): + prog.trials=prog.trials-1 + prog.update_progressbar(1) + def getKFold(X): """ @@ -73,7 +87,7 @@ def sort_score(modelScore): sorted_dict=dict(sorted(modelScore.items(), key=lambda item: item[1],reverse=True)) return sorted_dict -def train_on_sample_data(dataframe,target,models,DictClass,prog): +def train_on_sample_data(dataframe,target,models): """ param1: pandas.DataFrame param2: string @@ -104,7 +118,7 @@ def train_on_sample_data(dataframe,target,models,DictClass,prog): clean_dict = {k: modelScore[k] for k in modelScore if not isnan(modelScore[k])} return dict(itertools.islice(sort_score(clean_dict).items(), 5)) -def train_on_full_data(dataframe,target,models,best,DictClass,prog): +def train_on_full_data(X,Y,models,best): """ param1: pandas.DataFrame param2: string @@ -115,7 +129,6 @@ def train_on_full_data(dataframe,target,models,best,DictClass,prog): Function returns single best model with best accuracy in a dictionary. Accuracy is calculated using average cross validation score on specified kfold counts. """ - X,Y=dataframe.drop(target,axis=1),dataframe[target] k=getKFold(X) modelScore={} prog.create_progressbar(len(best),"Deep Search (Stage 2 of 3) :") @@ -131,11 +144,42 @@ def train_on_full_data(dataframe,target,models,best,DictClass,prog): clean_dict = {k: modelScore[k] for k in modelScore if not isnan(modelScore[k])} return dict(itertools.islice(sort_score(clean_dict).items(), 1)) +def train_on_neural(X,Y,ptype): + """ + param1: pandas.DataFrame + param2: string + param3: string + param4: Class object + return: keras model + + Function returns keras model. + """ + max_trials,n_epochs=10,20 + prog.create_progressbar(n_counters=((max_trials+2)*n_epochs),desc="Neural Network Models") + clf = ak.StructuredDataClassifier(overwrite=True,max_trials=max_trials) if ptype=='Classification' else ak.StructuredDataRegressor(overwrite=True,max_trials=max_trials) + clf.fit(X,Y, epochs=n_epochs,verbose=0,callbacks=[CustomCallback()]) + loss,acc=clf.evaluate(X,Y,verbose=0) + y_pred=clf.predict(X,verbose=0) + if ptype=="Classification": + y_pred= y_pred.astype(np.int) + if ptype=='Regression': + acc=r2_score(Y,y_pred) + print("Loss: {}, Accuracy: {:.2f}".format(loss,acc)) + else: + print("Loss: {}, Accuracy: {:.2f}".format(loss,acc)) + results= Tuner.metricResults(Y,y_pred,ptype) + plot_data=Tuner.prediction_data(Y, y_pred, ptype) + prog.update_progressbar(prog.trials) + prog.close_progressbar() + return (clf,acc,results,plot_data) + def model_search(dataframe,target,DictClass,use_neural=False,accuracy_criteria=0.99): """ param1: pandas.DataFrame param2: string param3: Class object + param4: boolean + param5: float return: Class object Function first fetches model dictionary which consists of model object and required parameter, @@ -147,20 +191,41 @@ def model_search(dataframe,target,DictClass,use_neural=False,accuracy_criteria=0 Then update YAML dictionary with appropriate model details such has selected type and parameters. Function finally return a model class object. """ + global prog prog=Progress() ptype=DictClass.getdict()['problem']["type"] modelsList=classifier_config().models if ptype=="Classification" else regressor_config().models + X,Y=dataframe.drop(target,axis=1),dataframe[target] if dataframe.shape[0]>500: - best=train_on_full_data(dataframe,target,modelsList,train_on_sample_data(dataframe,target,modelsList,DictClass,prog),DictClass,prog) + best=train_on_full_data(X,Y,modelsList,train_on_sample_data(dataframe,target,modelsList)) else: - best=train_on_full_data(dataframe,target,modelsList,modelsList,DictClass,prog) + best=train_on_full_data(X,Y,modelsList,modelsList) modelResult = Tuner.tune_model(dataframe,target,best,modelsList,ptype,accuracy=accuracy_criteria) modelData=Model() if ptype=="Classification":modelData.target_encode=DictClass.get_encoded_label() modelData.featureList=dataframe.drop(target,axis=1).columns.to_list() - modelData.model,modelData.params,acc,modelData.metrics,modelData.plot_data = modelResult - DictClass.addKeyValue('model',{'type': modelData.model.__class__.__name__}) - DictClass.UpdateNestedKeyValue('model','parameters',modelResult[1]) - print("{} CV Score : {:.2f}".format(modelData.model.__class__.__name__,acc)) - return modelData - + if use_neural: + neural_network=train_on_neural(X,Y,ptype) + if modelResult[2]>neural_network[1]: + modelData.model,modelData.params,acc,modelData.metrics,modelData.plot_data = modelResult + DictClass.addKeyValue('model',{'type': modelData.model.__class__.__name__}) + DictClass.UpdateNestedKeyValue('model','parameters',modelResult[1]) + class_name=modelData.model.__class__.__name__ + else: + modelData.model,acc,modelData.metrics,modelData.plot_data = neural_network + DictClass.addKeyValue('model',{'type':'TF'}) + if ptype=="Classification": + n_labels=dataframe[target].nunique(dropna=False) + cls_type='binary' if n_labels<=2 else 'multiclass' + DictClass.UpdateNestedKeyValue('model','classification_type',cls_type) + DictClass.UpdateNestedKeyValue('model','save_type',"h5") + if ptype=='Regression': + DictClass.UpdateNestedKeyValue('model','save_type',"pb") + class_name="Neural Network" + else: + modelData.model,modelData.params,acc,modelData.metrics,modelData.plot_data = modelResult + DictClass.addKeyValue('model',{'type': modelData.model.__class__.__name__}) + DictClass.UpdateNestedKeyValue('model','parameters',modelResult[1]) + class_name=modelData.model.__class__.__name__ + print("{} CV Score : {:.2f}".format(class_name,acc)) + return modelData \ No newline at end of file diff --git a/blobcity/store/Model.py b/blobcity/store/Model.py index b2b5134..36ae66b 100644 --- a/blobcity/store/Model.py +++ b/blobcity/store/Model.py @@ -45,6 +45,10 @@ def __init__(self): def __quick_clean(self,test_dataframe): """ + param1:pandas.DataFrame + return:pandas.DataFrame + + Function return processed dataframe """ cols=test_dataframe.columns.to_list() for i in cols: @@ -60,26 +64,39 @@ def __quick_clean(self,test_dataframe): return test_dataframe def __json_to_df(self,test): + """ + param1:Dictionary + return: pandas.DataFrame + + Function converts Dictionary/Json to pandas.DataFrame + """ test={k : [v] for k,v in test.items()} test=pd.DataFrame.from_dict(test) test=pd.get_dummies(test) return test def __check_columns(self,test,cols): - n_cols=0 - for i in cols: - if i not in test.columns.to_list(): - test.insert(n_cols, i, [0]*test.shape[0]) - n_cols+=1 + """ + param1: pandas.DataFrame + param2: list + return: pandas.DataFrame + + Function adds missing columns into the dataframe at the appropriate index. + """ + if isinstance(test,pd.DataFrame): + n_cols=0 + for i in cols: + if i not in test.columns.to_list(): + test.insert(n_cols, i, [0]*test.shape[0]) + n_cols+=1 return test def predict(self,test,return_type="list",path=""): """ - param1: self - param2: pd.DataFrame - param3: string : either list or pd.DataFrame to return - param4: string : file path to store output pd.DataFrame,supported file types {'csv','json','xlsx'} - return: List/pd.DataFrame + param1: pd.DataFrame + param2: string : either list or pd.DataFrame to return + param3: string : file path to store output pd.DataFrame,supported file types {'csv','json','xlsx'} + return: List or pd.DataFrame Function returns List/Array for predicted value from the trained model. """ @@ -96,8 +113,10 @@ def predict(self,test,return_type="list",path=""): result=self.model.predict(test_df) else: result=self.model.predict(test) - if self.yamldata['problem']["type"]=='Classification': + if self.yamldata['model']['type']=='TF': + if self.yamldata['model']['classification_type']=='binary': result=np.round(result).flatten().astype(np.int) + else: result=np.argmax(result,axis=1).flatten().astype(np.int) main_result=[] if self.target_encode!={}: for i in range(len(result)): @@ -105,6 +124,8 @@ def predict(self,test,return_type="list",path=""): if k == result[i]: main_result.append(self.target_encode[k]) result= main_result + else: + if self.yamldata['model']['type']=='TF': result=result.flatten().astype(np.float) result_dataframe=test.copy(deep=True) result_dataframe['prediction']=result @@ -134,6 +155,7 @@ def save(self, model_path=None): param: Entire Path for model pickle file, Supported formats is .pkl. Function saves the model and its weights serially and returns the filepath where it is saved. + if the model is of type Tensorflow/Neural Network the function will save addition Tensorflow files or .h5 file with same path and name for the file. """ if model_path not in [None,""]: path_components = model_path.split('.') @@ -141,18 +163,21 @@ def save(self, model_path=None): if extension == 'pkl' and self.yamldata['model']['type'] not in ['TF','tf','Tensorflow']: final_path = model_path dill.dump(self, open(final_path, 'wb')) - print("The model is stored at {}".format(final_path)) + print("The model class is stored at {}".format(final_path)) elif extension=='pkl' and self.yamldata['model']['type'] in ['TF','tf','Tensorflow']: base_path=os.path.splitext(model_path)[0] tmp=self.model if self.yamldata['problem']['type']=="Classification": tmp.export_model().save(base_path+".h5") + print(f"Stored Tensorflow model at: {base_path}.h5") elif self.yamldata['problem']['type']=="Regression": tmp.export_model().save(base_path, save_format="tf") + print(f"Stored Custom Tensorflow files at : {base_path}") else: raise TypeError("Wrong problem type identified") self.model=None dill.dump(self, open(model_path, 'wb')) + print(f"Stored Model Class at : {base_path}.pkl") self.model=tmp else: raise TypeError(f"{extension} file type must be .pkl")