diff --git a/.gitignore b/.gitignore index 3d54005..a5d9c85 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,5 @@ __pycache__/ build/ dist/ sklearn_json.egg-info/ - +.ignore/* +test.* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..5a65a8b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,16 @@ +exclude: "models/" +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.3.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace +- repo: https://github.com/psf/black + rev: 22.10.0 + hooks: + - id: black +- repo: https://github.com/charliermarsh/ruff-pre-commit + rev: 'v0.0.265' + hooks: + - id: ruff diff --git a/README.md b/README.md index a2b7337..3fc5a70 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ deserialized_model.predict(X) ``` # Features -The list of supported models is rapidly growing. If you have a request for a model or feature, please reach out to support@mlrequest.com. +The list of supported models is rapidly growing. If you have a request for a model or feature, please reach out to aheldrich@yahoo.com. sklearn-json requires scikit-learn >= 0.21.3. @@ -65,3 +65,7 @@ sklearn-json requires scikit-learn >= 0.21.3. * `sklearn.ensemble.RandomForestRegressor` * `sklearn.ensemble.GradientBoostingRegressor` * `sklearn.neural_network.MLPRegressor` + +## Upcoming + +* preproccessing diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..caa6e56 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,46 @@ +[tool.ruff] +# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default. Add isort +select = ["E", "F", "I", "D"] +ignore = ["D100", "D101", "D102", "D103", "D104"] + +# Allow autofix for all enabled rules (when `--fix`) is provided. +fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "EM", "ERA", "EXE", "FBT", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "UP", "YTT"] +unfixable = [] + +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", +] + +# Same as Black. +line-length = 88 + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +# Assume Python 3.10. +target-version = "py310" + +[tool.ruff.mccabe] +# Unlike Flake8, default to a complexity level of 10. +max-complexity = 10 diff --git a/setup.py b/setup.py index ec0b7e3..dd77912 100644 --- a/setup.py +++ b/setup.py @@ -1,24 +1,25 @@ import setuptools + with open("README.md", "r") as fh: long_description = fh.read() setuptools.setup( - name='sklearn-json', - version='0.1.0', - author="Mathieu Rodrigue", - author_email="support@mlrequest.com", - description="A safe, transparent way to share and deploy scikit-learn models.", - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/mlrequest/sklearn-json", - packages=setuptools.find_packages(), - install_requires=[ - 'scikit-learn>=0.21.3', - ], - classifiers=[ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - ], - python_requires='>=3.5', - ) + name="sklearn-json", + version="0.1.0", + author="Mathieu Rodrigue", + author_email="support@mlrequest.com", + description="A safe, transparent way to share and deploy scikit-learn models.", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/mlrequest/sklearn-json", + packages=setuptools.find_packages(), + install_requires=[ + "scikit-learn>=0.21.3", + ], + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], + python_requires=">=3.5", +) diff --git a/sklearn_json/__init__.py b/sklearn_json/__init__.py index 50f6aa3..bdd452d 100644 --- a/sklearn_json/__init__.py +++ b/sklearn_json/__init__.py @@ -1,16 +1,28 @@ -from sklearn_json import classification as clf -from sklearn_json import regression as reg -from sklearn import svm, discriminant_analysis, dummy -from sklearn.linear_model import LogisticRegression, Perceptron -from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor -from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier, RandomForestRegressor, GradientBoostingRegressor, _gb_losses -from sklearn.naive_bayes import BernoulliNB, GaussianNB, MultinomialNB, ComplementNB -from sklearn.linear_model import LinearRegression, Lasso, Ridge +import json + +from sklearn import discriminant_analysis, svm +from sklearn.ensemble import ( + GradientBoostingClassifier, + GradientBoostingRegressor, + RandomForestClassifier, + RandomForestRegressor, +) +from sklearn.linear_model import ( + Lasso, + LinearRegression, + LogisticRegression, + Perceptron, + Ridge, +) +from sklearn.naive_bayes import BernoulliNB, ComplementNB, GaussianNB, MultinomialNB from sklearn.neural_network import MLPClassifier, MLPRegressor from sklearn.svm import SVR -import json +from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor -__version__ = '0.1.0' +from sklearn_json import classification as clf +from sklearn_json import regression as reg + +__version__ = "0.1.0" def serialize_model(model): @@ -58,55 +70,55 @@ def serialize_model(model): elif isinstance(model, MLPRegressor): return reg.serialize_mlp_regressor(model) else: - raise ModellNotSupported('This model type is not currently supported. Email support@mlrequest.com to request a feature or report a bug.') + raise ModellNotSupported("This model type is not currently supported.") def deserialize_model(model_dict): - if model_dict['meta'] == 'lr': + if model_dict["meta"] == "lr": return clf.deserialize_logistic_regression(model_dict) - elif model_dict['meta'] == 'bernoulli-nb': + elif model_dict["meta"] == "bernoulli-nb": return clf.deserialize_bernoulli_nb(model_dict) - elif model_dict['meta'] == 'gaussian-nb': + elif model_dict["meta"] == "gaussian-nb": return clf.deserialize_gaussian_nb(model_dict) - elif model_dict['meta'] == 'multinomial-nb': + elif model_dict["meta"] == "multinomial-nb": return clf.deserialize_multinomial_nb(model_dict) - elif model_dict['meta'] == 'complement-nb': + elif model_dict["meta"] == "complement-nb": return clf.deserialize_complement_nb(model_dict) - elif model_dict['meta'] == 'lda': + elif model_dict["meta"] == "lda": return clf.deserialize_lda(model_dict) - elif model_dict['meta'] == 'qda': + elif model_dict["meta"] == "qda": return clf.deserialize_qda(model_dict) - elif model_dict['meta'] == 'svm': + elif model_dict["meta"] == "svm": return clf.deserialize_svm(model_dict) - elif model_dict['meta'] == 'perceptron': + elif model_dict["meta"] == "perceptron": return clf.deserialize_perceptron(model_dict) - elif model_dict['meta'] == 'decision-tree': + elif model_dict["meta"] == "decision-tree": return clf.deserialize_decision_tree(model_dict) - elif model_dict['meta'] == 'gb': + elif model_dict["meta"] == "gb": return clf.deserialize_gradient_boosting(model_dict) - elif model_dict['meta'] == 'rf': + elif model_dict["meta"] == "rf": return clf.deserialize_random_forest(model_dict) - elif model_dict['meta'] == 'mlp': + elif model_dict["meta"] == "mlp": return clf.deserialize_mlp(model_dict) - elif model_dict['meta'] == 'linear-regression': + elif model_dict["meta"] == "linear-regression": return reg.deserialize_linear_regressor(model_dict) - elif model_dict['meta'] == 'lasso-regression': + elif model_dict["meta"] == "lasso-regression": return reg.deserialize_lasso_regressor(model_dict) - elif model_dict['meta'] == 'ridge-regression': + elif model_dict["meta"] == "ridge-regression": return reg.deserialize_ridge_regressor(model_dict) - elif model_dict['meta'] == 'svr': + elif model_dict["meta"] == "svr": return reg.deserialize_svr(model_dict) - elif model_dict['meta'] == 'decision-tree-regression': + elif model_dict["meta"] == "decision-tree-regression": return reg.deserialize_decision_tree_regressor(model_dict) - elif model_dict['meta'] == 'gb-regression': + elif model_dict["meta"] == "gb-regression": return reg.deserialize_gradient_boosting_regressor(model_dict) - elif model_dict['meta'] == 'rf-regression': + elif model_dict["meta"] == "rf-regression": return reg.deserialize_random_forest_regressor(model_dict) - elif model_dict['meta'] == 'mlp-regression': + elif model_dict["meta"] == "mlp-regression": return reg.deserialize_mlp_regressor(model_dict) else: - raise ModellNotSupported('Model type not supported or corrupt JSON file. Email support@mlrequest.com to request a feature or report a bug.') + raise ModellNotSupported("Model type not supported or corrupt JSON file.") def to_dict(model): @@ -118,14 +130,15 @@ def from_dict(model_dict): def to_json(model, model_name): - with open(model_name, 'w') as model_json: + with open(model_name, "w") as model_json: json.dump(serialize_model(model), model_json) def from_json(model_name): - with open(model_name, 'r') as model_json: + with open(model_name, "r") as model_json: model_dict = json.load(model_json) return deserialize_model(model_dict) + class ModellNotSupported(Exception): - pass \ No newline at end of file + pass diff --git a/sklearn_json/classification.py b/sklearn_json/classification.py index d0cb36c..da90939 100644 --- a/sklearn_json/classification.py +++ b/sklearn_json/classification.py @@ -1,277 +1,293 @@ import numpy as np import scipy as sp -from sklearn import svm, discriminant_analysis, dummy +from sklearn import discriminant_analysis, dummy, svm +from sklearn.ensemble import ( + GradientBoostingClassifier, + RandomForestClassifier, + _gb_losses, +) from sklearn.linear_model import LogisticRegression, Perceptron -from sklearn.tree import DecisionTreeClassifier -from sklearn.tree._tree import Tree -from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier, _gb_losses -from sklearn.naive_bayes import BernoulliNB, GaussianNB, MultinomialNB, ComplementNB +from sklearn.naive_bayes import BernoulliNB, ComplementNB, GaussianNB, MultinomialNB from sklearn.neural_network import MLPClassifier from sklearn.preprocessing import LabelBinarizer -from sklearn_json import regression -from sklearn_json import csr +from sklearn.tree import DecisionTreeClassifier +from sklearn.tree._tree import Tree -import json +from sklearn_json import csr, regression def serialize_logistic_regression(model): serialized_model = { - 'meta': 'lr', - 'classes_': model.classes_.tolist(), - 'coef_': model.coef_.tolist(), - 'intercept_': model.intercept_.tolist(), - 'n_iter_': model.n_iter_.tolist(), - 'params': model.get_params() + "meta": "lr", + "classes_": model.classes_.tolist(), + "coef_": model.coef_.tolist(), + "intercept_": model.intercept_.tolist(), + "n_iter_": model.n_iter_.tolist(), + "params": model.get_params(), } return serialized_model def deserialize_logistic_regression(model_dict): - model = LogisticRegression(model_dict['params']) + model = LogisticRegression(**model_dict["params"]) - model.classes_ = np.array(model_dict['classes_']) - model.coef_ = np.array(model_dict['coef_']) - model.intercept_ = np.array(model_dict['intercept_']) - model.n_iter_ = np.array(model_dict['intercept_']) + model.classes_ = np.array(model_dict["classes_"]) + model.coef_ = np.array(model_dict["coef_"]) + model.intercept_ = np.array(model_dict["intercept_"]) + model.n_iter_ = np.array(model_dict["intercept_"]) return model def serialize_bernoulli_nb(model): serialized_model = { - 'meta': 'bernoulli-nb', - 'classes_': model.classes_.tolist(), - 'class_count_': model.class_count_.tolist(), - 'class_log_prior_': model.class_log_prior_.tolist(), - 'feature_count_': model.feature_count_.tolist(), - 'feature_log_prob_': model.feature_log_prob_.tolist(), - 'params': model.get_params() + "meta": "bernoulli-nb", + "classes_": model.classes_.tolist(), + "class_count_": model.class_count_.tolist(), + "class_log_prior_": model.class_log_prior_.tolist(), + "feature_count_": model.feature_count_.tolist(), + "feature_log_prob_": model.feature_log_prob_.tolist(), + "params": model.get_params(), } return serialized_model def deserialize_bernoulli_nb(model_dict): - model = BernoulliNB(model_dict['params']) + model = BernoulliNB(**model_dict["params"]) - model.classes_ = np.array(model_dict['classes_']) - model.class_count_ = np.array(model_dict['class_count_']) - model.class_log_prior_ = np.array(model_dict['class_log_prior_']) - model.feature_count_= np.array(model_dict['feature_count_']) - model.feature_log_prob_ = np.array(model_dict['feature_log_prob_']) + model.classes_ = np.array(model_dict["classes_"]) + model.class_count_ = np.array(model_dict["class_count_"]) + model.class_log_prior_ = np.array(model_dict["class_log_prior_"]) + model.feature_count_ = np.array(model_dict["feature_count_"]) + model.feature_log_prob_ = np.array(model_dict["feature_log_prob_"]) return model def serialize_gaussian_nb(model): serialized_model = { - 'meta': 'gaussian-nb', - 'classes_': model.classes_.tolist(), - 'class_count_': model.class_count_.tolist(), - 'class_prior_': model.class_prior_.tolist(), - 'theta_': model.theta_.tolist(), - 'sigma_': model.sigma_.tolist(), - 'epsilon_': model.epsilon_, - 'params': model.get_params() + "meta": "gaussian-nb", + "classes_": model.classes_.tolist(), + "class_count_": model.class_count_.tolist(), + "class_prior_": model.class_prior_.tolist(), + "theta_": model.theta_.tolist(), + "var_": model.var_.tolist(), + "epsilon_": model.epsilon_, + "params": model.get_params(), } return serialized_model def deserialize_gaussian_nb(model_dict): - model = GaussianNB(model_dict['params']) + model = GaussianNB(**model_dict["params"]) - model.classes_ = np.array(model_dict['classes_']) - model.class_count_ = np.array(model_dict['class_count_']) - model.class_prior_ = np.array(model_dict['class_prior_']) - model.theta_ = np.array(model_dict['theta_']) - model.sigma_ = np.array(model_dict['sigma_']) - model.epsilon_ = model_dict['epsilon_'] + model.classes_ = np.array(model_dict["classes_"]) + model.class_count_ = np.array(model_dict["class_count_"]) + model.class_prior_ = np.array(model_dict["class_prior_"]) + model.theta_ = np.array(model_dict["theta_"]) + model.var_ = np.array(model_dict["var_"]) + model.epsilon_ = model_dict["epsilon_"] return model def serialize_multinomial_nb(model): serialized_model = { - 'meta': 'multinomial-nb', - 'classes_': model.classes_.tolist(), - 'class_count_': model.class_count_.tolist(), - 'class_log_prior_': model.class_log_prior_.tolist(), - 'feature_count_': model.feature_count_.tolist(), - 'feature_log_prob_': model.feature_log_prob_.tolist(), - 'params': model.get_params() + "meta": "multinomial-nb", + "classes_": model.classes_.tolist(), + "class_count_": model.class_count_.tolist(), + "class_log_prior_": model.class_log_prior_.tolist(), + "feature_count_": model.feature_count_.tolist(), + "feature_log_prob_": model.feature_log_prob_.tolist(), + "params": model.get_params(), } return serialized_model def deserialize_multinomial_nb(model_dict): - model = MultinomialNB(model_dict['params']) + model = MultinomialNB(**model_dict["params"]) - model.classes_ = np.array(model_dict['classes_']) - model.class_count_ = np.array(model_dict['class_count_']) - model.class_log_prior_ = np.array(model_dict['class_log_prior_']) - model.feature_count_= np.array(model_dict['feature_count_']) - model.feature_log_prob_ = np.array(model_dict['feature_log_prob_']) + model.classes_ = np.array(model_dict["classes_"]) + model.class_count_ = np.array(model_dict["class_count_"]) + model.class_log_prior_ = np.array(model_dict["class_log_prior_"]) + model.feature_count_ = np.array(model_dict["feature_count_"]) + model.feature_log_prob_ = np.array(model_dict["feature_log_prob_"]) return model def serialize_complement_nb(model): serialized_model = { - 'meta': 'complement-nb', - 'classes_': model.classes_.tolist(), - 'class_count_': model.class_count_.tolist(), - 'class_log_prior_': model.class_log_prior_.tolist(), - 'feature_count_': model.feature_count_.tolist(), - 'feature_log_prob_': model.feature_log_prob_.tolist(), - 'feature_all_': model.feature_all_.tolist(), - 'params': model.get_params() + "meta": "complement-nb", + "classes_": model.classes_.tolist(), + "class_count_": model.class_count_.tolist(), + "class_log_prior_": model.class_log_prior_.tolist(), + "feature_count_": model.feature_count_.tolist(), + "feature_log_prob_": model.feature_log_prob_.tolist(), + "feature_all_": model.feature_all_.tolist(), + "params": model.get_params(), } return serialized_model def deserialize_complement_nb(model_dict): - model = ComplementNB(model_dict['params']) + model = ComplementNB(**model_dict["params"]) - model.classes_ = np.array(model_dict['classes_']) - model.class_count_ = np.array(model_dict['class_count_']) - model.class_log_prior_ = np.array(model_dict['class_log_prior_']) - model.feature_count_= np.array(model_dict['feature_count_']) - model.feature_log_prob_ = np.array(model_dict['feature_log_prob_']) - model.feature_all_ = np.array(model_dict['feature_all_']) + model.classes_ = np.array(model_dict["classes_"]) + model.class_count_ = np.array(model_dict["class_count_"]) + model.class_log_prior_ = np.array(model_dict["class_log_prior_"]) + model.feature_count_ = np.array(model_dict["feature_count_"]) + model.feature_log_prob_ = np.array(model_dict["feature_log_prob_"]) + model.feature_all_ = np.array(model_dict["feature_all_"]) return model def serialize_lda(model): serialized_model = { - 'meta': 'lda', - 'coef_': model.coef_.tolist(), - 'intercept_': model.intercept_.tolist(), - 'explained_variance_ratio_': model.explained_variance_ratio_.tolist(), - 'means_': model.means_.tolist(), - 'priors_': model.priors_.tolist(), - 'scalings_': model.scalings_.tolist(), - 'xbar_': model.xbar_.tolist(), - 'classes_': model.classes_.tolist(), - 'params': model.get_params() + "meta": "lda", + "coef_": model.coef_.tolist(), + "intercept_": model.intercept_.tolist(), + "explained_variance_ratio_": model.explained_variance_ratio_.tolist(), + "means_": model.means_.tolist(), + "priors_": model.priors_.tolist(), + "scalings_": model.scalings_.tolist(), + "xbar_": model.xbar_.tolist(), + "classes_": model.classes_.tolist(), + "params": model.get_params(), } - if 'covariance_' in model.__dict__: - serialized_model['covariance_'] = model.covariance_.tolist() + if "covariance_" in model.__dict__: + serialized_model["covariance_"] = model.covariance_.tolist() return serialized_model def deserialize_lda(model_dict): - model = discriminant_analysis.LinearDiscriminantAnalysis(**model_dict['params']) - - model.coef_ = np.array(model_dict['coef_']).astype(np.float64) - model.intercept_ = np.array(model_dict['intercept_']).astype(np.float64) - model.explained_variance_ratio_ = np.array(model_dict['explained_variance_ratio_']).astype(np.float64) - model.means_ = np.array(model_dict['means_']).astype(np.float64) - model.priors_ = np.array(model_dict['priors_']).astype(np.float64) - model.scalings_ = np.array(model_dict['scalings_']).astype(np.float64) - model.xbar_ = np.array(model_dict['xbar_']).astype(np.float64) - model.classes_ = np.array(model_dict['classes_']).astype(np.int64) + model = discriminant_analysis.LinearDiscriminantAnalysis(**model_dict["params"]) + + model.coef_ = np.array(model_dict["coef_"]).astype(np.float64) + model.intercept_ = np.array(model_dict["intercept_"]).astype(np.float64) + model.explained_variance_ratio_ = np.array( + model_dict["explained_variance_ratio_"] + ).astype(np.float64) + model.means_ = np.array(model_dict["means_"]).astype(np.float64) + model.priors_ = np.array(model_dict["priors_"]).astype(np.float64) + model.scalings_ = np.array(model_dict["scalings_"]).astype(np.float64) + model.xbar_ = np.array(model_dict["xbar_"]).astype(np.float64) + model.classes_ = np.array(model_dict["classes_"]).astype(np.int64) return model def serialize_qda(model): serialized_model = { - 'meta': 'qda', - 'means_': model.means_.tolist(), - 'priors_': model.priors_.tolist(), - 'scalings_': [array.tolist() for array in model.scalings_], - 'rotations_': [array.tolist() for array in model.rotations_], - 'classes_': model.classes_.tolist(), - 'params': model.get_params() + "meta": "qda", + "means_": model.means_.tolist(), + "priors_": model.priors_.tolist(), + "scalings_": [array.tolist() for array in model.scalings_], + "rotations_": [array.tolist() for array in model.rotations_], + "classes_": model.classes_.tolist(), + "params": model.get_params(), } - if 'covariance_' in model.__dict__: - serialized_model['covariance_'] = model.covariance_.tolist() + if "covariance_" in model.__dict__: + serialized_model["covariance_"] = model.covariance_.tolist() return serialized_model def deserialize_qda(model_dict): - model = discriminant_analysis.QuadraticDiscriminantAnalysis(**model_dict['params']) + model = discriminant_analysis.QuadraticDiscriminantAnalysis(**model_dict["params"]) - model.means_ = np.array(model_dict['means_']).astype(np.float64) - model.priors_ = np.array(model_dict['priors_']).astype(np.float64) - model.scalings_ = np.array(model_dict['scalings_']).astype(np.float64) - model.rotations_ = np.array(model_dict['rotations_']).astype(np.float64) - model.classes_ = np.array(model_dict['classes_']).astype(np.int64) + model.means_ = np.array(model_dict["means_"]).astype(np.float64) + model.priors_ = np.array(model_dict["priors_"]).astype(np.float64) + model.scalings_ = np.array(model_dict["scalings_"]).astype(np.float64) + model.rotations_ = np.array(model_dict["rotations_"]).astype(np.float64) + model.classes_ = np.array(model_dict["classes_"]).astype(np.int64) return model def serialize_svm(model): serialized_model = { - 'meta': 'svm', - 'class_weight_': model.class_weight_.tolist(), - 'classes_': model.classes_.tolist(), - 'support_': model.support_.tolist(), - 'n_support_': model.n_support_.tolist(), - 'intercept_': model.intercept_.tolist(), - 'probA_': model.probA_.tolist(), - 'probB_': model.probB_.tolist(), - '_intercept_': model._intercept_.tolist(), - 'shape_fit_': model.shape_fit_, - '_gamma': model._gamma, - 'params': model.get_params() + "meta": "svm", + "class_weight_": model.class_weight_.tolist(), + "classes_": model.classes_.tolist(), + "support_": model.support_.tolist(), + "n_support_": model.n_support_.tolist(), + "intercept_": model.intercept_.tolist(), + "probA_": model.probA_.tolist(), + "probB_": model.probB_.tolist(), + "_intercept_": model._intercept_.tolist(), + "shape_fit_": model.shape_fit_, + "_gamma": model._gamma, + "params": model.get_params(), } if isinstance(model.support_vectors_, sp.sparse.csr_matrix): - serialized_model['support_vectors_'] = csr.serialize_csr_matrix(model.support_vectors_) + serialized_model["support_vectors_"] = csr.serialize_csr_matrix( + model.support_vectors_ + ) elif isinstance(model.support_vectors_, np.ndarray): - serialized_model['support_vectors_'] = model.support_vectors_.tolist() + serialized_model["support_vectors_"] = model.support_vectors_.tolist() if isinstance(model.dual_coef_, sp.sparse.csr_matrix): - serialized_model['dual_coef_'] = csr.serialize_csr_matrix(model.dual_coef_) + serialized_model["dual_coef_"] = csr.serialize_csr_matrix(model.dual_coef_) elif isinstance(model.dual_coef_, np.ndarray): - serialized_model['dual_coef_'] = model.dual_coef_.tolist() + serialized_model["dual_coef_"] = model.dual_coef_.tolist() if isinstance(model._dual_coef_, sp.sparse.csr_matrix): - serialized_model['_dual_coef_'] = csr.serialize_csr_matrix(model._dual_coef_) + serialized_model["_dual_coef_"] = csr.serialize_csr_matrix(model._dual_coef_) elif isinstance(model._dual_coef_, np.ndarray): - serialized_model['_dual_coef_'] = model._dual_coef_.tolist() + serialized_model["_dual_coef_"] = model._dual_coef_.tolist() return serialized_model def deserialize_svm(model_dict): - model = svm.SVC(**model_dict['params']) - model.shape_fit_ = model_dict['shape_fit_'] - model._gamma = model_dict['_gamma'] - - model.class_weight_ = np.array(model_dict['class_weight_']).astype(np.float64) - model.classes_ = np.array(model_dict['classes_']) - model.support_ = np.array(model_dict['support_']).astype(np.int32) - model.n_support_ = np.array(model_dict['n_support_']).astype(np.int32) - model.intercept_ = np.array(model_dict['intercept_']).astype(np.float64) - model.probA_ = np.array(model_dict['probA_']).astype(np.float64) - model.probB_ = np.array(model_dict['probB_']).astype(np.float64) - model._intercept_ = np.array(model_dict['_intercept_']).astype(np.float64) - - if 'meta' in model_dict['support_vectors_'] and model_dict['support_vectors_']['meta'] == 'csr': - model.support_vectors_ = csr.deserialize_csr_matrix(model_dict['support_vectors_']) + model = svm.SVC(**model_dict["params"]) + model.shape_fit_ = model_dict["shape_fit_"] + model._gamma = model_dict["_gamma"] + + model.class_weight_ = np.array(model_dict["class_weight_"]).astype(np.float64) + model.classes_ = np.array(model_dict["classes_"]) + model.support_ = np.array(model_dict["support_"]).astype(np.int32) + model._n_support = np.array(model_dict["n_support_"]).astype(np.int32) + model.intercept_ = np.array(model_dict["intercept_"]).astype(np.float64) + model._probA = np.array(model_dict["probA_"]).astype(np.float64) + model._probB = np.array(model_dict["probB_"]).astype(np.float64) + model._intercept_ = np.array(model_dict["_intercept_"]).astype(np.float64) + + if ( + "meta" in model_dict["support_vectors_"] + and model_dict["support_vectors_"]["meta"] == "csr" + ): + model.support_vectors_ = csr.deserialize_csr_matrix( + model_dict["support_vectors_"] + ) model._sparse = True else: - model.support_vectors_ = np.array(model_dict['support_vectors_']).astype(np.float64) + model.support_vectors_ = np.array(model_dict["support_vectors_"]).astype( + np.float64 + ) model._sparse = False - if 'meta' in model_dict['dual_coef_'] and model_dict['dual_coef_']['meta'] == 'csr': - model.dual_coef_ = csr.deserialize_csr_matrix(model_dict['dual_coef_']) + if "meta" in model_dict["dual_coef_"] and model_dict["dual_coef_"]["meta"] == "csr": + model.dual_coef_ = csr.deserialize_csr_matrix(model_dict["dual_coef_"]) else: - model.dual_coef_ = np.array(model_dict['dual_coef_']).astype(np.float64) + model.dual_coef_ = np.array(model_dict["dual_coef_"]).astype(np.float64) - if 'meta' in model_dict['_dual_coef_'] and model_dict['_dual_coef_']['meta'] == 'csr': - model._dual_coef_ = csr.deserialize_csr_matrix(model_dict['_dual_coef_']) + if ( + "meta" in model_dict["_dual_coef_"] + and model_dict["_dual_coef_"]["meta"] == "csr" + ): + model._dual_coef_ = csr.deserialize_csr_matrix(model_dict["_dual_coef_"]) else: - model._dual_coef_ = np.array(model_dict['_dual_coef_']).astype(np.float64) + model._dual_coef_ = np.array(model_dict["_dual_coef_"]).astype(np.float64) return model @@ -285,19 +301,30 @@ def serialize_dummy_classifier(model): def serialize_tree(tree): serialized_tree = tree.__getstate__() - dtypes = serialized_tree['nodes'].dtype - serialized_tree['nodes'] = serialized_tree['nodes'].tolist() - serialized_tree['values'] = serialized_tree['values'].tolist() + dtypes = serialized_tree["nodes"].dtype + serialized_tree["nodes"] = serialized_tree["nodes"].tolist() + serialized_tree["values"] = serialized_tree["values"].tolist() return serialized_tree, dtypes def deserialize_tree(tree_dict, n_features, n_classes, n_outputs): - tree_dict['nodes'] = [tuple(lst) for lst in tree_dict['nodes']] - - names = ['left_child', 'right_child', 'feature', 'threshold', 'impurity', 'n_node_samples', 'weighted_n_node_samples'] - tree_dict['nodes'] = np.array(tree_dict['nodes'], dtype=np.dtype({'names': names, 'formats': tree_dict['nodes_dtype']})) - tree_dict['values'] = np.array(tree_dict['values']) + tree_dict["nodes"] = [tuple(lst) for lst in tree_dict["nodes"]] + + names = [ + "left_child", + "right_child", + "feature", + "threshold", + "impurity", + "n_node_samples", + "weighted_n_node_samples", + ] + tree_dict["nodes"] = np.array( + tree_dict["nodes"], + dtype=np.dtype({"names": names, "formats": tree_dict["nodes_dtype"]}), + ) + tree_dict["values"] = np.array(tree_dict["values"]) tree = Tree(n_features, np.array([n_classes], dtype=np.intp), n_outputs) tree.__setstate__(tree_dict) @@ -306,39 +333,44 @@ def deserialize_tree(tree_dict, n_features, n_classes, n_outputs): def serialize_decision_tree(model): - tree, dtypes = serialize_tree(model.tree_) - serialized_model = { - 'meta': 'decision-tree', - 'feature_importances_': model.feature_importances_.tolist(), - 'max_features_': model.max_features_, - 'n_classes_': int(model.n_classes_), - 'n_features_': model.n_features_, - 'n_outputs_': model.n_outputs_, - 'tree_': tree, - 'classes_': model.classes_.tolist(), - 'params': model.get_params() - } - - - tree_dtypes = [] - for i in range(0, len(dtypes)): - tree_dtypes.append(dtypes[i].str) - - serialized_model['tree_']['nodes_dtype'] = tree_dtypes + serialized_model = {"meta": "decision-tree"} + serialized_model.update( + { + k: ( + v.tolist() + if isinstance(v, np.ndarray) + else int(v) + if isinstance(v, np.int64) + else v + ) + for k, v in vars(model).items() + } + ) + serialized_model["params"] = model.get_params() + if hasattr(model, "tree_"): + tree, dtypes = serialize_tree(model.tree_) + tree_dtypes = [] + for i in range(0, len(dtypes)): + tree_dtypes.append(dtypes[i].str) + serialized_model["tree_"] = {"nodes_dtype": tree_dtypes, **tree} return serialized_model def deserialize_decision_tree(model_dict): - deserialized_model = DecisionTreeClassifier(**model_dict['params']) - - deserialized_model.classes_ = np.array(model_dict['classes_']) - deserialized_model.max_features_ = model_dict['max_features_'] - deserialized_model.n_classes_ = model_dict['n_classes_'] - deserialized_model.n_features_ = model_dict['n_features_'] - deserialized_model.n_outputs_ = model_dict['n_outputs_'] - - tree = deserialize_tree(model_dict['tree_'], model_dict['n_features_'], model_dict['n_classes_'], model_dict['n_outputs_']) + deserialized_model = DecisionTreeClassifier(**model_dict["params"]) + deserialized_model.classes_ = np.array(model_dict["classes_"]) + deserialized_model.max_features_ = model_dict["max_features_"] + deserialized_model.n_classes_ = model_dict["n_classes_"] + deserialized_model.n_features_in_ = model_dict["n_features_in_"] + deserialized_model.n_outputs_ = model_dict["n_outputs_"] + + tree = deserialize_tree( + model_dict["tree_"], + model_dict["n_features_in_"], + model_dict["n_classes_"], + model_dict["n_outputs_"], + ) deserialized_model.tree_ = tree return deserialized_model @@ -346,159 +378,142 @@ def deserialize_decision_tree(model_dict): def serialize_gradient_boosting(model): serialized_model = { - 'meta': 'gb', - 'classes_': model.classes_.tolist(), - 'max_features_': model.max_features_, - 'n_classes_': model.n_classes_, - 'n_features_': model.n_features_, - 'train_score_': model.train_score_.tolist(), - 'params': model.get_params(), - 'estimators_shape': list(model.estimators_.shape), - 'estimators_': [] + "meta": "gb", + "classes_": model.classes_.tolist(), + "max_features_": model.max_features_, + "n_classes_": model.n_classes_, + "n_features_in_": model.n_features_in_, + "train_score_": model.train_score_.tolist(), + "params": model.get_params(), + "estimators_shape": list(model.estimators_.shape), + "estimators_": [], } - if isinstance(model.init_, dummy.DummyClassifier): - serialized_model['init_'] = serialize_dummy_classifier(model.init_) - serialized_model['init_']['meta'] = 'dummy' + if isinstance(model.init_, dummy.DummyClassifier): + serialized_model["init_"] = serialize_dummy_classifier(model.init_) + serialized_model["init_"]["meta"] = "dummy" elif isinstance(model.init_, str): - serialized_model['init_'] = model.init_ + serialized_model["init_"] = model.init_ - if isinstance(model.loss_, _gb_losses.BinomialDeviance): - serialized_model['loss_'] = 'deviance' - elif isinstance(model.loss_, _gb_losses.ExponentialLoss): - serialized_model['loss_'] = 'exponential' - elif isinstance(model.loss_, _gb_losses.MultinomialDeviance): - serialized_model['loss_'] = 'multinomial' + if isinstance(model._loss, _gb_losses.BinomialDeviance): + serialized_model["loss_"] = "deviance" + elif isinstance(model._loss, _gb_losses.ExponentialLoss): + serialized_model["loss_"] = "exponential" + elif isinstance(model._loss, _gb_losses.MultinomialDeviance): + serialized_model["loss_"] = "multinomial" - if 'priors' in model.init_.__dict__: - serialized_model['priors'] = model.init_.priors.tolist() + if "priors" in model.init_.__dict__: + serialized_model["priors"] = model.init_.priors.tolist() - serialized_model['estimators_'] = [regression.serialize_decision_tree_regressor(regression_tree) for regression_tree in model.estimators_.reshape(-1, )] + serialized_model["estimators_"] = [ + regression.serialize_decision_tree_regressor(regression_tree) + for regression_tree in model.estimators_.reshape( + -1, + ) + ] return serialized_model def deserialize_gradient_boosting(model_dict): - model = GradientBoostingClassifier(**model_dict['params']) - estimators = [regression.deserialize_decision_tree_regressor(tree) for tree in model_dict['estimators_']] - model.estimators_ = np.array(estimators).reshape(model_dict['estimators_shape']) - if 'init_' in model_dict and model_dict['init_']['meta'] == 'dummy': + model = GradientBoostingClassifier(**model_dict["params"]) + estimators = [ + regression.deserialize_decision_tree_regressor(tree) + for tree in model_dict["estimators_"] + ] + model.estimators_ = np.array(estimators).reshape(model_dict["estimators_shape"]) + if "init_" in model_dict and model_dict["init_"]["meta"] == "dummy": model.init_ = dummy.DummyClassifier() - model.init_.__dict__ = model_dict['init_'] - model.init_.__dict__.pop('meta') - - model.classes_ = np.array(model_dict['classes_']) - model.train_score_ = np.array(model_dict['train_score_']) - model.max_features_ = model_dict['max_features_'] - model.n_classes_ = model_dict['n_classes_'] - model.n_features_ = model_dict['n_features_'] - if model_dict['loss_'] == 'deviance': - model.loss_ = _gb_losses.BinomialDeviance(model.n_classes_) - elif model_dict['loss_'] == 'exponential': - model.loss_ = _gb_losses.ExponentialLoss(model.n_classes_) - elif model_dict['loss_'] == 'multinomial': - model.loss_ = _gb_losses.MultinomialDeviance(model.n_classes_) - - if 'priors' in model_dict: - model.init_.priors = np.array(model_dict['priors']) + model.init_.__dict__ = model_dict["init_"] + model.init_.__dict__.pop("meta") + + model.classes_ = np.array(model_dict["classes_"]) + model.train_score_ = np.array(model_dict["train_score_"]) + model.max_features_ = model_dict["max_features_"] + model.n_classes_ = model_dict["n_classes_"] + model.n_features_in_ = model_dict["n_features_in_"] + if model_dict["loss_"] == "deviance": + model._loss = _gb_losses.BinomialDeviance(model.n_classes_) + elif model_dict["loss_"] == "exponential": + model._loss = _gb_losses.ExponentialLoss(model.n_classes_) + elif model_dict["loss_"] == "multinomial": + model._loss = _gb_losses.MultinomialDeviance(model.n_classes_) + + if "priors" in model_dict: + model.init_.priors = np.array(model_dict["priors"]) return model def serialize_random_forest(model): - serialized_model = { - 'meta': 'rf', - 'max_depth': model.max_depth, - 'min_samples_split': model.min_samples_split, - 'min_samples_leaf': model.min_samples_leaf, - 'min_weight_fraction_leaf': model.min_weight_fraction_leaf, - 'max_features': model.max_features, - 'max_leaf_nodes': model.max_leaf_nodes, - 'min_impurity_decrease': model.min_impurity_decrease, - 'min_impurity_split': model.min_impurity_split, - 'n_features_': model.n_features_, - 'n_outputs_': model.n_outputs_, - 'classes_': model.classes_.tolist(), - 'estimators_': [serialize_decision_tree(decision_tree) for decision_tree in model.estimators_], - 'params': model.get_params() - } - - if 'oob_score_' in model.__dict__: - serialized_model['oob_score_'] = model.oob_score_ - if 'oob_decision_function_' in model.__dict__: - serialized_model['oob_decision_function_'] = model.oob_decision_function_.tolist() - - if isinstance(model.n_classes_, int): - serialized_model['n_classes_'] = model.n_classes_ - else: - serialized_model['n_classes_'] = model.n_classes_.tolist() + serialized_model = {"meta": "rf"} + serialized_model.update( + { + k: (v.tolist() if isinstance(v, np.ndarray) else v) + for k, v in vars(model).items() + } + ) + serialized_model["params"] = model.get_params() + serialized_model.pop("estimator") + serialized_model.pop("estimator_") + serialized_model["estimators_"] = [ + serialize_decision_tree(decision_tree) for decision_tree in model.estimators_ + ] return serialized_model def deserialize_random_forest(model_dict): - model = RandomForestClassifier(**model_dict['params']) - estimators = [deserialize_decision_tree(decision_tree) for decision_tree in model_dict['estimators_']] + model = RandomForestClassifier(**model_dict["params"]) + attrs = [k for k in model_dict if k not in model_dict["params"]] + for attr in attrs: + if isinstance(model_dict[attr], list): + attr_val = np.array(model_dict[attr]) + else: + attr_val = model_dict[attr] + setattr(model, attr, attr_val) + estimators = [ + deserialize_decision_tree(decision_tree) + for decision_tree in model_dict["estimators_"] + ] model.estimators_ = np.array(estimators) - model.classes_ = np.array(model_dict['classes_']) - model.n_features_ = model_dict['n_features_'] - model.n_outputs_ = model_dict['n_outputs_'] - model.max_depth = model_dict['max_depth'] - model.min_samples_split = model_dict['min_samples_split'] - model.min_samples_leaf = model_dict['min_samples_leaf'] - model.min_weight_fraction_leaf = model_dict['min_weight_fraction_leaf'] - model.max_features = model_dict['max_features'] - model.max_leaf_nodes = model_dict['max_leaf_nodes'] - model.min_impurity_decrease = model_dict['min_impurity_decrease'] - model.min_impurity_split = model_dict['min_impurity_split'] - - if 'oob_score_' in model_dict: - model.oob_score_ = model_dict['oob_score_'] - if 'oob_decision_function_' in model_dict: - model.oob_decision_function_ = model_dict['oob_decision_function_'] - - if isinstance(model_dict['n_classes_'], list): - model.n_classes_ = np.array(model_dict['n_classes_']) - else: - model.n_classes_ = model_dict['n_classes_'] - return model def serialize_perceptron(model): serialized_model = { - 'meta': 'perceptron', - 'coef_': model.coef_.tolist(), - 'intercept_': model.intercept_.tolist(), - 'n_iter_': model.n_iter_, - 'classes_': model.classes_.tolist(), - 'params': model.get_params() + "meta": "perceptron", + "coef_": model.coef_.tolist(), + "intercept_": model.intercept_.tolist(), + "n_iter_": model.n_iter_, + "classes_": model.classes_.tolist(), + "params": model.get_params(), } - if 'covariance_' in model.__dict__: - serialized_model['covariance_'] = model.covariance_.tolist() + if "covariance_" in model.__dict__: + serialized_model["covariance_"] = model.covariance_.tolist() return serialized_model def deserialize_perceptron(model_dict): - model = Perceptron(**model_dict['params']) + model = Perceptron(**model_dict["params"]) - model.coef_ = np.array(model_dict['coef_']).astype(np.float64) - model.intercept_ = np.array(model_dict['intercept_']).astype(np.float64) - model.n_iter_ = np.array(model_dict['n_iter_']).astype(np.float64) - model.classes_ = np.array(model_dict['classes_']).astype(np.int64) + model.coef_ = np.array(model_dict["coef_"]).astype(np.float64) + model.intercept_ = np.array(model_dict["intercept_"]).astype(np.float64) + model.n_iter_ = np.array(model_dict["n_iter_"]).astype(np.float64) + model.classes_ = np.array(model_dict["classes_"]).astype(np.int64) return model def serialize_label_binarizer(label_binarizer): serialized_label_binarizer = { - 'neg_label': label_binarizer.neg_label, - 'pos_label': label_binarizer.pos_label, - 'sparse_output': label_binarizer.sparse_output, - 'y_type_': label_binarizer.y_type_, - 'sparse_input_': label_binarizer.sparse_input_, - 'classes_': label_binarizer.classes_.tolist() + "neg_label": label_binarizer.neg_label, + "pos_label": label_binarizer.pos_label, + "sparse_output": label_binarizer.sparse_output, + "y_type_": label_binarizer.y_type_, + "sparse_input_": label_binarizer.sparse_input_, + "classes_": label_binarizer.classes_.tolist(), } return serialized_label_binarizer @@ -506,50 +521,50 @@ def serialize_label_binarizer(label_binarizer): def deserialize_label_binarizer(label_binarizer_dict): label_binarizer = LabelBinarizer() - label_binarizer.neg_label = label_binarizer_dict['neg_label'] - label_binarizer.pos_label = label_binarizer_dict['pos_label'] - label_binarizer.sparse_output = label_binarizer_dict['sparse_output'] - label_binarizer.y_type_ = label_binarizer_dict['y_type_'] - label_binarizer.sparse_input_ = label_binarizer_dict['sparse_input_'] - label_binarizer.classes_ = np.array(label_binarizer_dict['classes_']) + label_binarizer.neg_label = label_binarizer_dict["neg_label"] + label_binarizer.pos_label = label_binarizer_dict["pos_label"] + label_binarizer.sparse_output = label_binarizer_dict["sparse_output"] + label_binarizer.y_type_ = label_binarizer_dict["y_type_"] + label_binarizer.sparse_input_ = label_binarizer_dict["sparse_input_"] + label_binarizer.classes_ = np.array(label_binarizer_dict["classes_"]) return label_binarizer def serialize_mlp(model): serialized_model = { - 'meta': 'mlp', - 'coefs_': [array.tolist() for array in model.coefs_], - 'loss_': model.loss_, - 'intercepts_': [array.tolist() for array in model.intercepts_], - 'n_iter_': model.n_iter_, - 'n_layers_': model.n_layers_, - 'n_outputs_': model.n_outputs_, - 'out_activation_': model.out_activation_, - '_label_binarizer': serialize_label_binarizer(model._label_binarizer), - 'params': model.get_params() + "meta": "mlp", + "coefs_": [array.tolist() for array in model.coefs_], + "loss_": model.loss_, + "intercepts_": [array.tolist() for array in model.intercepts_], + "n_iter_": model.n_iter_, + "n_layers_": model.n_layers_, + "n_outputs_": model.n_outputs_, + "out_activation_": model.out_activation_, + "_label_binarizer": serialize_label_binarizer(model._label_binarizer), + "params": model.get_params(), } if isinstance(model.classes_, list): - serialized_model['classes_'] = [array.tolist() for array in model.classes_] + serialized_model["classes_"] = [array.tolist() for array in model.classes_] else: - serialized_model['classes_'] = model.classes_.tolist() + serialized_model["classes_"] = model.classes_.tolist() return serialized_model def deserialize_mlp(model_dict): - model = MLPClassifier(**model_dict['params']) - - model.coefs_ = np.array(model_dict['coefs_']) - model.loss_ = model_dict['loss_'] - model.intercepts_ = np.array(model_dict['intercepts_']) - model.n_iter_ = model_dict['n_iter_'] - model.n_layers_ = model_dict['n_layers_'] - model.n_outputs_ = model_dict['n_outputs_'] - model.out_activation_ = model_dict['out_activation_'] - model._label_binarizer = deserialize_label_binarizer(model_dict['_label_binarizer']) - - model.classes_ = np.array(model_dict['classes_']) + model = MLPClassifier(**model_dict["params"]) + + model.coefs_ = [np.array(x) for x in model_dict["coefs_"]] + model.loss_ = model_dict["loss_"] + model.intercepts_ = [np.array(x) for x in model_dict["intercepts_"]] + model.n_iter_ = model_dict["n_iter_"] + model.n_layers_ = model_dict["n_layers_"] + model.n_outputs_ = model_dict["n_outputs_"] + model.out_activation_ = model_dict["out_activation_"] + model._label_binarizer = deserialize_label_binarizer(model_dict["_label_binarizer"]) + + model.classes_ = np.array(model_dict["classes_"]) return model diff --git a/sklearn_json/csr.py b/sklearn_json/csr.py index df2690c..5e299ec 100644 --- a/sklearn_json/csr.py +++ b/sklearn_json/csr.py @@ -4,19 +4,21 @@ def serialize_csr_matrix(csr_matrix): serialized_csr_matrix = { - 'meta': 'csr', - 'data': csr_matrix.data.tolist(), - 'indices': csr_matrix.indices.tolist(), - 'indptr': csr_matrix.indptr.tolist(), - '_shape': csr_matrix._shape, + "meta": "csr", + "data": csr_matrix.data.tolist(), + "indices": csr_matrix.indices.tolist(), + "indptr": csr_matrix.indptr.tolist(), + "_shape": csr_matrix._shape, } return serialized_csr_matrix -def deserialize_csr_matrix(csr_dict, data_type=np.float64, indices_type=np.int32, indptr_type=np.int32): - csr_matrix = sp.sparse.csr_matrix(tuple(csr_dict['_shape'])) - csr_matrix.data = np.array(csr_dict['data']).astype(data_type) - csr_matrix.indices = np.array(csr_dict['indices']).astype(indices_type) - csr_matrix.indptr = np.array(csr_dict['indptr']).astype(indptr_type) +def deserialize_csr_matrix( + csr_dict, data_type=np.float64, indices_type=np.int32, indptr_type=np.int32 +): + csr_matrix = sp.sparse.csr_matrix(tuple(csr_dict["_shape"])) + csr_matrix.data = np.array(csr_dict["data"]).astype(data_type) + csr_matrix.indices = np.array(csr_dict["indices"]).astype(indices_type) + csr_matrix.indptr = np.array(csr_dict["indptr"]).astype(indptr_type) return csr_matrix diff --git a/sklearn_json/regression.py b/sklearn_json/regression.py index 208e44f..0608d45 100644 --- a/sklearn_json/regression.py +++ b/sklearn_json/regression.py @@ -1,169 +1,190 @@ -from sklearn.linear_model import LinearRegression, Lasso, Ridge -from sklearn.tree import DecisionTreeRegressor -from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor, _gb_losses +import numpy as np +import scipy as sp +from sklearn import dummy +from sklearn.ensemble import ( + GradientBoostingRegressor, + RandomForestRegressor, + _gb_losses, +) +from sklearn.linear_model import Lasso, LinearRegression, Ridge from sklearn.neural_network import MLPRegressor -from sklearn.tree._tree import Tree from sklearn.svm import SVR -from sklearn import dummy +from sklearn.tree import DecisionTreeRegressor +from sklearn.tree._tree import Tree + from sklearn_json import csr -import numpy as np -import scipy as sp def serialize_linear_regressor(model): serialized_model = { - 'meta': 'linear-regression', - 'coef_': model.coef_.tolist(), - 'intercept_': model.intercept_.tolist(), - 'params': model.get_params() + "meta": "linear-regression", + "coef_": model.coef_.tolist(), + "intercept_": model.intercept_.tolist(), + "params": model.get_params(), } return serialized_model def deserialize_linear_regressor(model_dict): - model = LinearRegression(model_dict['params']) + model = LinearRegression(**model_dict["params"]) - model.coef_ = np.array(model_dict['coef_']) - model.intercept_ = np.array(model_dict['intercept_']) + model.coef_ = np.array(model_dict["coef_"]) + model.intercept_ = np.array(model_dict["intercept_"]) return model def serialize_lasso_regressor(model): serialized_model = { - 'meta': 'lasso-regression', - 'coef_': model.coef_.tolist(), - 'params': model.get_params() + "meta": "lasso-regression", + "coef_": model.coef_.tolist(), + "params": model.get_params(), } if isinstance(model.n_iter_, int): - serialized_model['n_iter_'] = model.n_iter_ + serialized_model["n_iter_"] = model.n_iter_ else: - serialized_model['n_iter_'] = model.n_iter_.tolist() + serialized_model["n_iter_"] = model.n_iter_.tolist() if isinstance(model.n_iter_, float): - serialized_model['intercept_'] = model.intercept_ + serialized_model["intercept_"] = model.intercept_ else: - serialized_model['intercept_'] = model.intercept_.tolist() + serialized_model["intercept_"] = model.intercept_.tolist() return serialized_model def deserialize_lasso_regressor(model_dict): - model = Lasso(model_dict['params']) + model = Lasso(model_dict["params"]) - model.coef_ = np.array(model_dict['coef_']) + model.coef_ = np.array(model_dict["coef_"]) - if isinstance(model_dict['n_iter_'], list): - model.n_iter_ = np.array(model_dict['n_iter_']) + if isinstance(model_dict["n_iter_"], list): + model.n_iter_ = np.array(model_dict["n_iter_"]) else: - model.n_iter_ = int(model_dict['n_iter_']) + model.n_iter_ = int(model_dict["n_iter_"]) - if isinstance(model_dict['intercept_'], list): - model.intercept_ = np.array(model_dict['intercept_']) + if isinstance(model_dict["intercept_"], list): + model.intercept_ = np.array(model_dict["intercept_"]) else: - model.intercept_ = float(model_dict['intercept_']) + model.intercept_ = float(model_dict["intercept_"]) return model def serialize_ridge_regressor(model): serialized_model = { - 'meta': 'ridge-regression', - 'coef_': model.coef_.tolist(), - 'params': model.get_params() + "meta": "ridge-regression", + "coef_": model.coef_.tolist(), + "params": model.get_params(), } if model.n_iter_: - serialized_model['n_iter_'] = model.n_iter_.tolist() + serialized_model["n_iter_"] = model.n_iter_.tolist() if isinstance(model.n_iter_, float): - serialized_model['intercept_'] = model.intercept_ + serialized_model["intercept_"] = model.intercept_ else: - serialized_model['intercept_'] = model.intercept_.tolist() + serialized_model["intercept_"] = model.intercept_.tolist() return serialized_model def deserialize_ridge_regressor(model_dict): - model = Ridge(model_dict['params']) + model = Ridge(model_dict["params"]) - model.coef_ = np.array(model_dict['coef_']) + model.coef_ = np.array(model_dict["coef_"]) - if 'n_iter_' in model_dict: - model.n_iter_ = np.array(model_dict['n_iter_']) + if "n_iter_" in model_dict: + model.n_iter_ = np.array(model_dict["n_iter_"]) - if isinstance(model_dict['intercept_'], list): - model.intercept_ = np.array(model_dict['intercept_']) + if isinstance(model_dict["intercept_"], list): + model.intercept_ = np.array(model_dict["intercept_"]) else: - model.intercept_ = float(model_dict['intercept_']) + model.intercept_ = float(model_dict["intercept_"]) return model def serialize_svr(model): serialized_model = { - 'meta': 'svr', - 'class_weight_': model.class_weight_.tolist(), - 'support_': model.support_.tolist(), - 'n_support_': model.n_support_.tolist(), - 'intercept_': model.intercept_.tolist(), - 'probA_': model.probA_.tolist(), - 'probB_': model.probB_.tolist(), - '_intercept_': model._intercept_.tolist(), - 'shape_fit_': model.shape_fit_, - '_gamma': model._gamma, - 'params': model.get_params() + "meta": "svr", + "params": model.get_params(), } + serialized_model.update( + { + k: ( + v.tolist() + if isinstance(v, np.ndarray) + else int(v) + if isinstance(v, np.int64) + else v + ) + for k, v in vars(model).items() + } + ) if isinstance(model.support_vectors_, sp.sparse.csr_matrix): - serialized_model['support_vectors_'] = csr.serialize_csr_matrix(model.support_vectors_) + serialized_model["support_vectors_"] = csr.serialize_csr_matrix( + model.support_vectors_ + ) elif isinstance(model.support_vectors_, np.ndarray): - serialized_model['support_vectors_'] = model.support_vectors_.tolist() + serialized_model["support_vectors_"] = model.support_vectors_.tolist() if isinstance(model.dual_coef_, sp.sparse.csr_matrix): - serialized_model['dual_coef_'] = csr.serialize_csr_matrix(model.dual_coef_) + serialized_model["dual_coef_"] = csr.serialize_csr_matrix(model.dual_coef_) elif isinstance(model.dual_coef_, np.ndarray): - serialized_model['dual_coef_'] = model.dual_coef_.tolist() + serialized_model["dual_coef_"] = model.dual_coef_.tolist() if isinstance(model._dual_coef_, sp.sparse.csr_matrix): - serialized_model['_dual_coef_'] = csr.serialize_csr_matrix(model._dual_coef_) + serialized_model["_dual_coef_"] = csr.serialize_csr_matrix(model._dual_coef_) elif isinstance(model._dual_coef_, np.ndarray): - serialized_model['_dual_coef_'] = model._dual_coef_.tolist() + serialized_model["_dual_coef_"] = model._dual_coef_.tolist() return serialized_model def deserialize_svr(model_dict): - model = SVR(**model_dict['params']) - model.shape_fit_ = model_dict['shape_fit_'] - model._gamma = model_dict['_gamma'] - - model.class_weight_ = np.array(model_dict['class_weight_']).astype(np.float64) - model.support_ = np.array(model_dict['support_']).astype(np.int32) - model.n_support_ = np.array(model_dict['n_support_']).astype(np.int32) - model.intercept_ = np.array(model_dict['intercept_']).astype(np.float64) - model.probA_ = np.array(model_dict['probA_']).astype(np.float64) - model.probB_ = np.array(model_dict['probB_']).astype(np.float64) - model._intercept_ = np.array(model_dict['_intercept_']).astype(np.float64) - - if 'meta' in model_dict['support_vectors_'] and model_dict['support_vectors_']['meta'] == 'csr': - model.support_vectors_ = csr.deserialize_csr_matrix(model_dict['support_vectors_']) + model = SVR(**model_dict["params"]) + attrs = [k for k in model_dict if k not in model_dict["params"]] + for attr in attrs: + if isinstance(model_dict[attr], list): + attr_val = np.array(model_dict[attr]) + else: + attr_val = model_dict[attr] + setattr(model, attr, attr_val) + + if ( + "meta" in model_dict["support_vectors_"] + and model_dict["support_vectors_"]["meta"] == "csr" + ): + model.support_vectors_ = csr.deserialize_csr_matrix( + model_dict["support_vectors_"] + ) model._sparse = True else: - model.support_vectors_ = np.array(model_dict['support_vectors_']).astype(np.float64) + model.support_vectors_ = np.array(model_dict["support_vectors_"]).astype( + np.float64 + ) model._sparse = False - if 'meta' in model_dict['dual_coef_'] and model_dict['dual_coef_']['meta'] == 'csr': - model.dual_coef_ = csr.deserialize_csr_matrix(model_dict['dual_coef_']) + if "meta" in model_dict["dual_coef_"] and model_dict["dual_coef_"]["meta"] == "csr": + model.dual_coef_ = csr.deserialize_csr_matrix(model_dict["dual_coef_"]) else: - model.dual_coef_ = np.array(model_dict['dual_coef_']).astype(np.float64) + model.dual_coef_ = np.array(model_dict["dual_coef_"]).astype(np.float64) - if 'meta' in model_dict['_dual_coef_'] and model_dict['_dual_coef_']['meta'] == 'csr': - model._dual_coef_ = csr.deserialize_csr_matrix(model_dict['_dual_coef_']) + if ( + "meta" in model_dict["_dual_coef_"] + and model_dict["_dual_coef_"]["meta"] == "csr" + ): + model._dual_coef_ = csr.deserialize_csr_matrix(model_dict["_dual_coef_"]) else: - model._dual_coef_ = np.array(model_dict['_dual_coef_']).astype(np.float64) + model._dual_coef_ = np.array(model_dict["_dual_coef_"]).astype(np.float64) + + for k, v in vars(model).items(): + if isinstance(v, np.ndarray) and v.dtype == np.int64: + setattr(model, k, v.astype(np.int32)) return model @@ -171,19 +192,30 @@ def deserialize_svr(model_dict): def serialize_tree(tree): serialized_tree = tree.__getstate__() # serialized_tree['nodes_dtype'] = serialized_tree['nodes'].dtype - dtypes = serialized_tree['nodes'].dtype - serialized_tree['nodes'] = serialized_tree['nodes'].tolist() - serialized_tree['values'] = serialized_tree['values'].tolist() + dtypes = serialized_tree["nodes"].dtype + serialized_tree["nodes"] = serialized_tree["nodes"].tolist() + serialized_tree["values"] = serialized_tree["values"].tolist() return serialized_tree, dtypes def deserialize_tree(tree_dict, n_features, n_classes, n_outputs): - tree_dict['nodes'] = [tuple(lst) for lst in tree_dict['nodes']] - - names = ['left_child', 'right_child', 'feature', 'threshold', 'impurity', 'n_node_samples', 'weighted_n_node_samples'] - tree_dict['nodes'] = np.array(tree_dict['nodes'], dtype=np.dtype({'names': names, 'formats': tree_dict['nodes_dtype']})) - tree_dict['values'] = np.array(tree_dict['values']) + tree_dict["nodes"] = [tuple(lst) for lst in tree_dict["nodes"]] + + names = [ + "left_child", + "right_child", + "feature", + "threshold", + "impurity", + "n_node_samples", + "weighted_n_node_samples", + ] + tree_dict["nodes"] = np.array( + tree_dict["nodes"], + dtype=np.dtype({"names": names, "formats": tree_dict["nodes_dtype"]}), + ) + tree_dict["values"] = np.array(tree_dict["values"]) tree = Tree(n_features, np.array([n_classes], dtype=np.intp), n_outputs) tree.__setstate__(tree_dict) @@ -194,12 +226,12 @@ def deserialize_tree(tree_dict, n_features, n_classes, n_outputs): def serialize_decision_tree_regressor(model): tree, dtypes = serialize_tree(model.tree_) serialized_model = { - 'meta': 'decision-tree-regression', - 'feature_importances_': model.feature_importances_.tolist(), - 'max_features_': model.max_features_, - 'n_features_': model.n_features_, - 'n_outputs_': model.n_outputs_, - 'tree_': tree + "meta": "decision-tree-regression", + "feature_importances_": model.feature_importances_.tolist(), + "max_features_": model.max_features_, + "n_features_in_": model.n_features_in_, + "n_outputs_": model.n_outputs_, + "tree_": tree, } # serialized_model. @@ -208,7 +240,7 @@ def serialize_decision_tree_regressor(model): for i in range(0, len(dtypes)): tree_dtypes.append(dtypes[i].str) - serialized_model['tree_']['nodes_dtype'] = tree_dtypes + serialized_model["tree_"]["nodes_dtype"] = tree_dtypes return serialized_model @@ -216,11 +248,13 @@ def serialize_decision_tree_regressor(model): def deserialize_decision_tree_regressor(model_dict): deserialized_decision_tree = DecisionTreeRegressor() - deserialized_decision_tree.max_features_ = model_dict['max_features_'] - deserialized_decision_tree.n_features_ = model_dict['n_features_'] - deserialized_decision_tree.n_outputs_ = model_dict['n_outputs_'] + deserialized_decision_tree.max_features_ = model_dict["max_features_"] + deserialized_decision_tree.n_features_in_ = model_dict["n_features_in_"] + deserialized_decision_tree.n_outputs_ = model_dict["n_outputs_"] - tree = deserialize_tree(model_dict['tree_'], model_dict['n_features_'], 1, model_dict['n_outputs_']) + tree = deserialize_tree( + model_dict["tree_"], model_dict["n_features_in_"], 1, model_dict["n_outputs_"] + ) deserialized_decision_tree.tree_ = tree return deserialized_decision_tree @@ -232,142 +266,144 @@ def serialize_dummy_regressor(model): def serialize_gradient_boosting_regressor(model): - serialized_model = { - 'meta': 'gb-regression', - 'max_features_': model.max_features_, - 'n_features_': model.n_features_, - 'train_score_': model.train_score_.tolist(), - 'params': model.get_params(), - 'estimators_shape': list(model.estimators_.shape), - 'estimators_': [] + "meta": "gb-regression", + "max_features_": model.max_features_, + "n_features_in_": model.n_features_in_, + "train_score_": model.train_score_.tolist(), + "params": model.get_params(), + "estimators_shape": list(model.estimators_.shape), + "estimators_": [], } - if isinstance(model.init_, dummy.DummyRegressor): - serialized_model['init_'] = serialize_dummy_regressor(model.init_) - serialized_model['init_']['meta'] = 'dummy' + if isinstance(model.init_, dummy.DummyRegressor): + serialized_model["init_"] = serialize_dummy_regressor(model.init_) + serialized_model["init_"]["meta"] = "dummy" elif isinstance(model.init_, str): - serialized_model['init_'] = model.init_ + serialized_model["init_"] = model.init_ - if isinstance(model.loss_, _gb_losses.LeastSquaresError): - serialized_model['loss_'] = 'ls' - elif isinstance(model.loss_, _gb_losses.LeastAbsoluteError): - serialized_model['loss_'] = 'lad' - elif isinstance(model.loss_, _gb_losses.HuberLossFunction): - serialized_model['loss_'] = 'huber' - elif isinstance(model.loss_, _gb_losses.QuantileLossFunction): - serialized_model['loss_'] = 'quantile' + if isinstance(model._loss, _gb_losses.LeastSquaresError): + serialized_model["loss_"] = "ls" + elif isinstance(model._loss, _gb_losses.LeastAbsoluteError): + serialized_model["loss_"] = "lad" + elif isinstance(model._loss, _gb_losses.HuberLossFunction): + serialized_model["loss_"] = "huber" + elif isinstance(model._loss, _gb_losses.QuantileLossFunction): + serialized_model["loss_"] = "quantile" - if 'priors' in model.init_.__dict__: - serialized_model['priors'] = model.init_.priors.tolist() + if "priors" in model.init_.__dict__: + serialized_model["priors"] = model.init_.priors.tolist() for tree in model.estimators_.reshape((-1,)): - serialized_model['estimators_'].append(serialize_decision_tree_regressor(tree)) + serialized_model["estimators_"].append(serialize_decision_tree_regressor(tree)) return serialized_model def deserialize_gradient_boosting_regressor(model_dict): - model = GradientBoostingRegressor(**model_dict['params']) - trees = [deserialize_decision_tree_regressor(tree) for tree in model_dict['estimators_']] - model.estimators_ = np.array(trees).reshape(model_dict['estimators_shape']) - if 'init_' in model_dict and model_dict['init_']['meta'] == 'dummy': + model = GradientBoostingRegressor(**model_dict["params"]) + trees = [ + deserialize_decision_tree_regressor(tree) for tree in model_dict["estimators_"] + ] + model.estimators_ = np.array(trees).reshape(model_dict["estimators_shape"]) + if "init_" in model_dict and model_dict["init_"]["meta"] == "dummy": model.init_ = dummy.DummyRegressor() - model.init_.__dict__ = model_dict['init_'] - model.init_.__dict__.pop('meta') - - - model.train_score_ = np.array(model_dict['train_score_']) - model.max_features_ = model_dict['max_features_'] - model.n_features_ = model_dict['n_features_'] - if model_dict['loss_'] == 'ls': - model.loss_ = _gb_losses.LeastSquaresError(1) - elif model_dict['loss_'] == 'lad': - model.loss_ = _gb_losses.LeastAbsoluteError(1) - elif model_dict['loss_'] == 'huber': - model.loss_ = _gb_losses.HuberLossFunction(1) - elif model_dict['loss_'] == 'quantile': - model.loss_ = _gb_losses.QuantileLossFunction(1) - - if 'priors' in model_dict: - model.init_.priors = np.array(model_dict['priors']) + model.init_.__dict__ = model_dict["init_"] + model.init_.__dict__.pop("meta") + + model.train_score_ = np.array(model_dict["train_score_"]) + model.max_features_ = model_dict["max_features_"] + model.n_features_in_ = model_dict["n_features_in_"] + if model_dict["loss_"] == "ls": + model._loss = _gb_losses.LeastSquaresError() + elif model_dict["loss_"] == "lad": + model._loss = _gb_losses.LeastAbsoluteError() + elif model_dict["loss_"] == "huber": + model._loss = _gb_losses.HuberLossFunction() + elif model_dict["loss_"] == "quantile": + model._loss = _gb_losses.QuantileLossFunction() + + if "priors" in model_dict: + model.init_.priors = np.array(model_dict["priors"]) return model def serialize_random_forest_regressor(model): - serialized_model = { - 'meta': 'rf-regression', - 'max_depth': model.max_depth, - 'min_samples_split': model.min_samples_split, - 'min_samples_leaf': model.min_samples_leaf, - 'min_weight_fraction_leaf': model.min_weight_fraction_leaf, - 'max_features': model.max_features, - 'max_leaf_nodes': model.max_leaf_nodes, - 'min_impurity_decrease': model.min_impurity_decrease, - 'min_impurity_split': model.min_impurity_split, - 'n_features_': model.n_features_, - 'n_outputs_': model.n_outputs_, - 'estimators_': [serialize_decision_tree_regressor(decision_tree) for decision_tree in model.estimators_], - 'params': model.get_params() + "meta": "rf-regression", + "max_depth": model.max_depth, + "min_samples_split": model.min_samples_split, + "min_samples_leaf": model.min_samples_leaf, + "min_weight_fraction_leaf": model.min_weight_fraction_leaf, + "max_features": model.max_features, + "max_leaf_nodes": model.max_leaf_nodes, + "min_impurity_decrease": model.min_impurity_decrease, + "n_outputs_": model.n_outputs_, + "n_features_in_": model.n_features_in_, + "estimators_": [ + serialize_decision_tree_regressor(decision_tree) + for decision_tree in model.estimators_ + ], + "params": model.get_params(), } - if 'oob_score_' in model.__dict__: - serialized_model['oob_score_'] = model.oob_score_ - if 'oob_decision_function_' in model.__dict__: - serialized_model['oob_prediction_'] = model.oob_prediction_.tolist() + if "oob_score_" in model.__dict__: + serialized_model["oob_score_"] = model.oob_score_ + if "oob_decision_function_" in model.__dict__: + serialized_model["oob_prediction_"] = model.oob_prediction_.tolist() return serialized_model def deserialize_random_forest_regressor(model_dict): - model = RandomForestRegressor(**model_dict['params']) - estimators = [deserialize_decision_tree_regressor(decision_tree) for decision_tree in model_dict['estimators_']] + model = RandomForestRegressor(**model_dict["params"]) + estimators = [ + deserialize_decision_tree_regressor(decision_tree) + for decision_tree in model_dict["estimators_"] + ] model.estimators_ = np.array(estimators) - - model.n_features_ = model_dict['n_features_'] - model.n_outputs_ = model_dict['n_outputs_'] - model.max_depth = model_dict['max_depth'] - model.min_samples_split = model_dict['min_samples_split'] - model.min_samples_leaf = model_dict['min_samples_leaf'] - model.min_weight_fraction_leaf = model_dict['min_weight_fraction_leaf'] - model.max_features = model_dict['max_features'] - model.max_leaf_nodes = model_dict['max_leaf_nodes'] - model.min_impurity_decrease = model_dict['min_impurity_decrease'] - model.min_impurity_split = model_dict['min_impurity_split'] - - if 'oob_score_' in model_dict: - model.oob_score_ = model_dict['oob_score_'] - if 'oob_prediction_' in model_dict: - model.oob_prediction_ =np.array(model_dict['oob_prediction_']) + model.n_features_in_ = model_dict["n_features_in_"] + model.n_outputs_ = model_dict["n_outputs_"] + model.max_depth = model_dict["max_depth"] + model.min_samples_split = model_dict["min_samples_split"] + model.min_samples_leaf = model_dict["min_samples_leaf"] + model.min_weight_fraction_leaf = model_dict["min_weight_fraction_leaf"] + model.max_features = model_dict["max_features"] + model.max_leaf_nodes = model_dict["max_leaf_nodes"] + model.min_impurity_decrease = model_dict["min_impurity_decrease"] + + if "oob_score_" in model_dict: + model.oob_score_ = model_dict["oob_score_"] + if "oob_prediction_" in model_dict: + model.oob_prediction_ = np.array(model_dict["oob_prediction_"]) return model def serialize_mlp_regressor(model): serialized_model = { - 'meta': 'mlp-regression', - 'coefs_': model.coefs_, - 'loss_': model.loss_, - 'intercepts_': model.intercepts_, - 'n_iter_': model.n_iter_, - 'n_layers_': model.n_layers_, - 'n_outputs_': model.n_outputs_, - 'out_activation_': model.out_activation_, - 'params': model.get_params() + "meta": "mlp-regression", + "coefs_": model.coefs_, + "loss_": model.loss_, + "intercepts_": model.intercepts_, + "n_iter_": model.n_iter_, + "n_layers_": model.n_layers_, + "n_outputs_": model.n_outputs_, + "out_activation_": model.out_activation_, + "params": model.get_params(), } return serialized_model def deserialize_mlp_regressor(model_dict): - model = MLPRegressor(**model_dict['params']) - - model.coefs_ = model_dict['coefs_'] - model.loss_ = model_dict['loss_'] - model.intercepts_ = model_dict['intercepts_'] - model.n_iter_ = model_dict['n_iter_'] - model.n_layers_ = model_dict['n_layers_'] - model.n_outputs_ = model_dict['n_outputs_'] - model.out_activation_ = model_dict['out_activation_'] + model = MLPRegressor(**model_dict["params"]) + + model.coefs_ = model_dict["coefs_"] + model.loss_ = model_dict["loss_"] + model.intercepts_ = model_dict["intercepts_"] + model.n_iter_ = model_dict["n_iter_"] + model.n_layers_ = model_dict["n_layers_"] + model.n_outputs_ = model_dict["n_outputs_"] + model.out_activation_ = model_dict["out_activation_"] return model diff --git a/test/test_classification.py b/test/test_classification.py index da7dd63..9dc92db 100644 --- a/test/test_classification.py +++ b/test/test_classification.py @@ -1,199 +1,243 @@ +import random + +import numpy as np +import pytest +from numpy import testing +from sklearn import discriminant_analysis, svm from sklearn.datasets import make_classification +from sklearn.ensemble import GradientBoostingClassifier, RandomForestClassifier from sklearn.feature_extraction import FeatureHasher -from sklearn import svm, discriminant_analysis from sklearn.linear_model import LogisticRegression, Perceptron -from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier -from sklearn.naive_bayes import BernoulliNB, GaussianNB, MultinomialNB, ComplementNB +from sklearn.naive_bayes import BernoulliNB, ComplementNB, GaussianNB, MultinomialNB from sklearn.neural_network import MLPClassifier from sklearn.tree import DecisionTreeClassifier -import unittest -import random -import numpy as np -from numpy import testing -import sklearn_json as skljson - - -class TestAPI(unittest.TestCase): - def setUp(self): - self.X, self.y = make_classification(n_samples=50, n_features=3, n_classes=3, n_informative=3, n_redundant=0, random_state=0, shuffle=False) - - feature_hasher = FeatureHasher(n_features=3) - features = [] - for i in range(0, 100): - features.append({'a': random.randint(0, 2), 'b': random.randint(3, 5), 'c': random.randint(6, 8)}) - self.y_sparse = [random.randint(0, 2) for i in range(0, 100)] - self.X_sparse = feature_hasher.transform(features) +import sklearn_json as skljson +X, y = make_classification( + n_samples=50, + n_features=3, + n_classes=3, + n_informative=3, + n_redundant=0, + random_state=0, + shuffle=False, +) + +feature_hasher = FeatureHasher(n_features=3) +features = [] +for i in range(0, 100): + features.append( + { + "a": random.randint(0, 2), + "b": random.randint(3, 5), + "c": random.randint(6, 8), + } + ) +y_sparse = [random.randint(0, 2) for i in range(0, 100)] +X_sparse = feature_hasher.transform(features) + + +@pytest.mark.usefixtures("tmp_path") +class TestClassification: def check_model(self, model, abs=False): # Given if abs: - model.fit(np.absolute(self.X), self.y) + model.fit(np.absolute(X), y) else: - model.fit(self.X, self.y) + model.fit(X, y) # When serialized_model = skljson.to_dict(model) deserialized_model = skljson.from_dict(serialized_model) # Then - expected_predictions = model.predict(self.X) - actual_predictions = deserialized_model.predict(self.X) + expected_predictions = model.predict(X) + actual_predictions = deserialized_model.predict(X) testing.assert_array_equal(expected_predictions, actual_predictions) def check_sparse_model(self, model, abs=False): # Given if abs: - model.fit(np.absolute(self.X_sparse), self.y_sparse) + model.fit(np.absolute(X_sparse), y_sparse) else: - model.fit(self.X_sparse, self.y_sparse) + model.fit(X_sparse, y_sparse) # When serialized_model = skljson.to_dict(model) deserialized_model = skljson.from_dict(serialized_model) # Then - expected_predictions = model.predict(self.X) - actual_predictions = deserialized_model.predict(self.X) + expected_predictions = model.predict(X) + actual_predictions = deserialized_model.predict(X) testing.assert_array_equal(expected_predictions, actual_predictions) - def check_model_json(self, model, model_name, abs=False): + def check_model_json(self, model, model_name, tmp_path, abs=False): # Given if abs: - model.fit(np.absolute(self.X), self.y) + model.fit(np.absolute(X), y) else: - model.fit(self.X, self.y) + model.fit(X, y) # When - serialized_model = skljson.to_json(model, model_name) - deserialized_model = skljson.from_json(model_name) + skljson.to_json(model, tmp_path / model_name) + deserialized_model = skljson.from_json(tmp_path / model_name) # Then - expected_predictions = model.predict(self.X) - actual_predictions = deserialized_model.predict(self.X) + expected_predictions = model.predict(X) + actual_predictions = deserialized_model.predict(X) testing.assert_array_equal(expected_predictions, actual_predictions) - def check_sparse_model_json(self, model, model_name, abs=False): + def check_sparse_model_json(self, model, model_name, tmp_path, abs=False): # Given if abs: - model.fit(np.absolute(self.X_sparse), self.y_sparse) + model.fit(np.absolute(X_sparse), y_sparse) else: - model.fit(self.X_sparse, self.y_sparse) + model.fit(X_sparse, y_sparse) # When - serialized_model = skljson.to_json(model, model_name) - deserialized_model = skljson.from_json(model_name) + skljson.to_json(model, tmp_path / model_name) + deserialized_model = skljson.from_json(tmp_path / model_name) # Then - expected_predictions = model.predict(self.X) - actual_predictions = deserialized_model.predict(self.X) + expected_predictions = model.predict(X) + actual_predictions = deserialized_model.predict(X) testing.assert_array_equal(expected_predictions, actual_predictions) - def test_bernoulli_nb(self): + def test_bernoulli_nb(self, tmp_path): self.check_model(BernoulliNB()) self.check_sparse_model(BernoulliNB()) - model_name = 'bernoulli-nb.json' - self.check_model_json(BernoulliNB(), model_name) - self.check_sparse_model_json(BernoulliNB(), model_name) + model_name = "bernoulli-nb.json" + self.check_model_json(BernoulliNB(), model_name, tmp_path) + self.check_sparse_model_json(BernoulliNB(), model_name, tmp_path) - def test_guassian_nb(self): + def test_guassian_nb(self, tmp_path): self.check_model(GaussianNB()) - model_name = 'gaussian-nb.json' - self.check_model_json(GaussianNB(), model_name) + model_name = "gaussian-nb.json" + self.check_model_json(GaussianNB(), model_name, tmp_path) # No sklearn implementation for sparse matrix - def test_multinomial_nb(self): + def test_multinomial_nb(self, tmp_path): self.check_model(MultinomialNB(), abs=True) self.check_sparse_model(MultinomialNB(), abs=True) - model_name = 'multinomial-nb.json' - self.check_model_json(MultinomialNB(), model_name, abs=True) - self.check_sparse_model_json(MultinomialNB(), model_name, abs=True) + model_name = "multinomial-nb.json" + self.check_model_json(MultinomialNB(), model_name, tmp_path, abs=True) + self.check_sparse_model_json(MultinomialNB(), model_name, tmp_path, abs=True) - def test_complement_nb(self): + def test_complement_nb(self, tmp_path): self.check_model(ComplementNB(), abs=True) - model_name = 'complement-nb.json' - self.check_model_json(ComplementNB(), model_name, abs=True) + model_name = "complement-nb.json" + self.check_model_json(ComplementNB(), model_name, tmp_path, abs=True) # No sklearn implementation for sparse matrix - def test_logistic_regression(self): + def test_logistic_regression(self, tmp_path): self.check_model(LogisticRegression()) self.check_sparse_model(LogisticRegression()) - model_name = 'lr.json' - self.check_model_json(LogisticRegression(), model_name) - self.check_sparse_model_json(LogisticRegression(), model_name) + model_name = "lr.json" + self.check_model_json(LogisticRegression(), model_name, tmp_path) + self.check_sparse_model_json(LogisticRegression(), model_name, tmp_path) - def test_lda(self): + def test_lda(self, tmp_path): self.check_model(discriminant_analysis.LinearDiscriminantAnalysis()) - model_name = 'lda.json' - self.check_model_json(discriminant_analysis.LinearDiscriminantAnalysis(), model_name) + model_name = "lda.json" + self.check_model_json( + discriminant_analysis.LinearDiscriminantAnalysis(), model_name, tmp_path + ) # No sklearn implementation for sparse matrix - def test_qda(self): + def test_qda(self, tmp_path): self.check_model(discriminant_analysis.QuadraticDiscriminantAnalysis()) - model_name = 'qda.json' - self.check_model_json(discriminant_analysis.QuadraticDiscriminantAnalysis(), model_name) + model_name = "qda.json" + self.check_model_json( + discriminant_analysis.QuadraticDiscriminantAnalysis(), model_name, tmp_path + ) # No sklearn implementation for sparse matrix - def test_svm(self): - self.check_model(svm.SVC(gamma=0.001, C=100., kernel='linear')) - self.check_sparse_model(svm.SVC(gamma=0.001, C=100., kernel='linear')) + def test_svm(self, tmp_path): + self.check_model(svm.SVC(gamma=0.001, C=100.0, kernel="linear")) + self.check_sparse_model(svm.SVC(gamma=0.001, C=100.0, kernel="linear")) - model_name = 'svm.json' - self.check_model_json(svm.SVC(), model_name) - self.check_sparse_model_json(svm.SVC(), model_name) + model_name = "svm.json" + self.check_model_json(svm.SVC(), model_name, tmp_path) + self.check_sparse_model_json(svm.SVC(), model_name, tmp_path) - def test_decision_tree(self): + def test_decision_tree(self, tmp_path): self.check_model(DecisionTreeClassifier()) self.check_sparse_model(DecisionTreeClassifier()) - model_name = 'dt.json' - self.check_model_json(DecisionTreeClassifier(), model_name) - self.check_sparse_model_json(DecisionTreeClassifier(), model_name) + model_name = "dt.json" + self.check_model_json(DecisionTreeClassifier(), model_name, tmp_path) + self.check_sparse_model_json(DecisionTreeClassifier(), model_name, tmp_path) - def test_gradient_boosting(self): + def test_gradient_boosting(self, tmp_path): self.check_model(GradientBoostingClassifier(n_estimators=25, learning_rate=1.0)) - self.check_sparse_model(GradientBoostingClassifier(n_estimators=25, learning_rate=1.0)) - - model_name = 'gb.json' - self.check_model_json(GradientBoostingClassifier(), model_name) - self.check_sparse_model_json(GradientBoostingClassifier(), model_name) - - def test_random_forest(self): - self.check_model(RandomForestClassifier(n_estimators=10, max_depth=5, random_state=0)) - self.check_sparse_model(RandomForestClassifier(n_estimators=10, max_depth=5, random_state=0)) - - model_name = 'rf.json' - self.check_model_json(RandomForestClassifier(), model_name) - self.check_sparse_model_json(RandomForestClassifier(), model_name) - - def test_perceptron(self): + self.check_sparse_model( + GradientBoostingClassifier(n_estimators=25, learning_rate=1.0) + ) + + model_name = "gb.json" + self.check_model_json(GradientBoostingClassifier(), model_name, tmp_path) + self.check_sparse_model_json(GradientBoostingClassifier(), model_name, tmp_path) + + def test_random_forest(self, tmp_path): + self.check_model( + RandomForestClassifier(n_estimators=10, max_depth=5, random_state=0) + ) + self.check_sparse_model( + RandomForestClassifier(n_estimators=10, max_depth=5, random_state=0) + ) + + model_name = "rf.json" + self.check_model_json(RandomForestClassifier(), model_name, tmp_path) + self.check_sparse_model_json(RandomForestClassifier(), model_name, tmp_path) + + def test_perceptron(self, tmp_path): self.check_model(Perceptron()) self.check_sparse_model(Perceptron()) - model_name = 'perceptron.json' - self.check_model_json(Perceptron(), model_name) - self.check_sparse_model_json(Perceptron(), model_name) - - def test_mlp(self): - self.check_model(MLPClassifier(solver='lbfgs', alpha=1e-5, hidden_layer_sizes=(5, 2), random_state=1)) - self.check_sparse_model(MLPClassifier(solver='lbfgs', alpha=1e-5, hidden_layer_sizes=(5, 2), random_state=1)) - - model_name = 'mlp.json' - self.check_model_json(MLPClassifier(solver='lbfgs', alpha=1e-5, hidden_layer_sizes=(5, 2), random_state=1), model_name) - self.check_sparse_model_json(MLPClassifier(solver='lbfgs', alpha=1e-5, hidden_layer_sizes=(5, 2), random_state=1), model_name) - + model_name = "perceptron.json" + self.check_model_json(Perceptron(), model_name, tmp_path) + self.check_sparse_model_json(Perceptron(), model_name, tmp_path) + + def test_mlp(self, tmp_path): + self.check_model( + MLPClassifier( + solver="lbfgs", alpha=1e-5, hidden_layer_sizes=(5, 2), random_state=1 + ) + ) + self.check_sparse_model( + MLPClassifier( + solver="lbfgs", alpha=1e-5, hidden_layer_sizes=(5, 2), random_state=1 + ) + ) + + model_name = "mlp.json" + self.check_model_json( + MLPClassifier( + solver="lbfgs", alpha=1e-5, hidden_layer_sizes=(5, 2), random_state=1 + ), + model_name, + tmp_path, + ) + self.check_sparse_model_json( + MLPClassifier( + solver="lbfgs", alpha=1e-5, hidden_layer_sizes=(5, 2), random_state=1 + ), + model_name, + tmp_path, + ) diff --git a/test/test_regression.py b/test/test_regression.py index adf6f96..1de9296 100644 --- a/test/test_regression.py +++ b/test/test_regression.py @@ -1,53 +1,62 @@ +import random + +import pytest +from numpy import testing from sklearn.datasets import make_regression +from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor from sklearn.feature_extraction import FeatureHasher -from sklearn.linear_model import LinearRegression, Lasso, Ridge -from sklearn.tree import DecisionTreeRegressor -from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor +from sklearn.linear_model import Lasso, LinearRegression, Ridge from sklearn.neural_network import MLPRegressor from sklearn.svm import SVR -from numpy import testing -import random -import unittest -import sklearn_json as skljson - - -class TestAPI(unittest.TestCase): - - def setUp(self): - self.X, self.y = make_regression(n_samples=50, n_features=3, n_informative=3, random_state=0, shuffle=False) +from sklearn.tree import DecisionTreeRegressor - feature_hasher = FeatureHasher(n_features=3) - features = [] - for i in range(0, 100): - features.append({'a': random.randint(0, 2), 'b': random.randint(3, 5), 'c': random.randint(6, 8)}) - self.y_sparse = [random.random() for i in range(0, 100)] - self.X_sparse = feature_hasher.transform(features) +import sklearn_json as skljson +X, y = make_regression( + n_samples=50, n_features=3, n_informative=3, random_state=0, shuffle=False +) + +feature_hasher = FeatureHasher(n_features=3) +features = [] +for i in range(0, 100): + features.append( + { + "a": random.randint(0, 2), + "b": random.randint(3, 5), + "c": random.randint(6, 8), + } + ) +y_sparse = [random.random() for i in range(0, 100)] +X_sparse = feature_hasher.transform(features) + + +@pytest.mark.usefixtures("tmp_path") +class TestRegression: def check_model(self, model): # Given - model.fit(self.X, self.y) + model.fit(X, y) # When serialized_model = skljson.to_dict(model) deserialized_model = skljson.from_dict(serialized_model) # Then - expected_predictions = model.predict(self.X) - actual_predictions = deserialized_model.predict(self.X) + expected_predictions = model.predict(X) + actual_predictions = deserialized_model.predict(X) testing.assert_array_equal(expected_predictions, actual_predictions) def check_sparse_model(self, model): # Given - model.fit(self.X_sparse, self.y_sparse) + model.fit(X_sparse, y_sparse) # When serialized_model = skljson.to_dict(model) deserialized_model = skljson.from_dict(serialized_model) # Then - expected_predictions = model.predict(self.X_sparse) - actual_predictions = deserialized_model.predict(self.X_sparse) + expected_predictions = model.predict(X_sparse) + actual_predictions = deserialized_model.predict(X_sparse) testing.assert_array_equal(expected_predictions, actual_predictions) @@ -64,8 +73,8 @@ def test_ridge_regression(self): self.check_sparse_model(Ridge(alpha=0.5)) def test_svr(self): - self.check_model(SVR(gamma='scale', C=1.0, epsilon=0.2)) - self.check_sparse_model(SVR(gamma='scale', C=1.0, epsilon=0.2)) + self.check_model(SVR(gamma="scale", C=1.0, epsilon=0.2)) + self.check_sparse_model(SVR(gamma="scale", C=1.0, epsilon=0.2)) def test_decision_tree_regression(self): self.check_model(DecisionTreeRegressor()) @@ -76,10 +85,13 @@ def test_gradient_boosting_regression(self): self.check_sparse_model(GradientBoostingRegressor()) def test_random_forest_regression(self): - self.check_model(RandomForestRegressor(max_depth=2, random_state=0, n_estimators=100)) - self.check_sparse_model(RandomForestRegressor(max_depth=2, random_state=0, n_estimators=100)) + self.check_model( + RandomForestRegressor(max_depth=2, random_state=0, n_estimators=100) + ) + self.check_sparse_model( + RandomForestRegressor(max_depth=2, random_state=0, n_estimators=100) + ) def test_mlp_regression(self): self.check_model(MLPRegressor()) self.check_sparse_model(MLPRegressor()) -