diff --git a/tests/test_reuse/test_averaging_reuse.py b/learnware/client/scripts/__init__.py similarity index 100% rename from tests/test_reuse/test_averaging_reuse.py rename to learnware/client/scripts/__init__.py diff --git a/learnware/learnware/__init__.py b/learnware/learnware/__init__.py index 738a55d..0f88957 100644 --- a/learnware/learnware/__init__.py +++ b/learnware/learnware/__init__.py @@ -1,8 +1,8 @@ import os import copy +from typing import Optional from .base import Learnware - from .utils import get_stat_spec_from_config from ..specification import Specification from ..utils import read_yaml_to_dict @@ -12,7 +12,7 @@ from ..config import C logger = get_module_logger("learnware.learnware") -def get_learnware_from_dirpath(id: str, semantic_spec: dict, learnware_dirpath, ignore_error=True) -> Learnware: +def get_learnware_from_dirpath(id: str, semantic_spec: dict, learnware_dirpath, ignore_error=True) -> Optional[Learnware]: """Get the learnware object from dirpath, and provide the manage interface tor Learnware class Parameters diff --git a/learnware/tests/module.py b/learnware/tests/module.py index 52300a6..9556bdf 100644 --- a/learnware/tests/module.py +++ b/learnware/tests/module.py @@ -8,3 +8,8 @@ def get_semantic_specification(): semantic_specification["Name"] = {"Type": "String", "Values": "test"} semantic_specification["Description"] = {"Type": "String", "Values": "test"} return semantic_specification + + + + +def get_requirements_file() \ No newline at end of file diff --git a/learnware/tests/templates/__init__.py b/learnware/tests/templates/__init__.py new file mode 100644 index 0000000..8eba13c --- /dev/null +++ b/learnware/tests/templates/__init__.py @@ -0,0 +1,76 @@ +import os +import tempfile +from shutil import copyfile +from typing import List, Tuple, Union, Optional + +from ...utils import save_dict_to_yaml +from ...config import C + +class LearnwareTemplate: + def __init__(self): + self.model_templates = { + "pickle": { + "class_name": 'PickleLoadedModel', + "template_path": os.path.join(C.package_path, "tests", "templates", "pickle_model.py") + } + } + + def generate_requirements(self, filepath, requirements: Optional[List[Union[Tuple[str, str, str], str]]] = None): + requirements = [] if requirements is None else requirements + operators = {"==", "~=", ">=", "<=", ">", "<"} + requirements_str = "" + for requirement in requirements: + if isinstance(requirement, str): + line_str = requirement.strip() + "\n" + elif isinstance(requirement, tuple): + assert requirement[1] in operators, f"The operator of requirements is not supported." + line_str = requirement[0].strip() + requirement[1].strip() + requirement[2].strip() + "\n" + else: + raise TypeError(f"requirement must be type str/tuple, rather than {type(requirement)}") + + requirements_str += line_str + + with open(filepath, "w") as fdout: + fdout.write(requirements_str) + + def generate_learnware_yaml(self, filepath, model_config: Optional[dict] = None, stat_spec_config: Optional[List[dict]] = None): + learnware_config = {} + if model_config is not None: + learnware_config["model"] = model_config + if stat_spec_config is not None: + learnware_config["stat_specifications"] = stat_spec_config + + save_dict_to_yaml(learnware_config, filepath) + + + + def generate_learnware_zipfile( + self, + learnware_zippath: str, + model_template: str = "pickle", + model_kwargs: Optional[dict] = None, + stat_spec_config: Optional[List[dict]] = None, + requirements: Optional[List[Union[Tuple[str, str, str], str]]] = None, + **kwargs, + ): + with tempfile.TemporaryDirectory(suffix="learnware_template") as tempdir: + requirement_filepath = os.path.join(tempdir, "requirements.txt") + self.generate_requirements(requirement_filepath, requirements) + + model_filepath = os.path.join(tempdir, "__init__.py") + copyfile(self.model_templates[model_template]["template_path"], model_filepath) + + learnware_yaml_filepath = os.path.join(tempdir, "requirements.txt") + model_config = { + "class_name": self.model_templates[model_template]["class_name"], + "kwargs": {} if model_kwargs is None else model_kwargs + } + self.generate_learnware_yaml(learnware_yaml_filepath, model_config, stat_spec_config) + + if model_template == "pickle": + pickle_filepath = os.path.join(tempdir, model_config["kwargs"]["pickle_filepath"]) + copyfile(kwargs["pickle_filepath"], pickle_filepath) + + + def generate_template_semantic_spec(self): + pass \ No newline at end of file diff --git a/learnware/tests/templates/pickle_model.py b/learnware/tests/templates/pickle_model.py new file mode 100644 index 0000000..267f44f --- /dev/null +++ b/learnware/tests/templates/pickle_model.py @@ -0,0 +1,31 @@ +import os +import pickle +import numpy as np +from learnware.model.base import BaseModel + +class PickleLoadedModel(BaseModel): + + def __init__( + self, + input_shape, + output_shape, + pickle_filepath, + predict_method="predict", + fit_method="fit", + finetune_method="finetune", + ): + super(PickleLoadedModel, self).__init__(input_shape=input_shape, output_shape=output_shape) + with open(pickle_filepath, "rb") as fd: + self.model = pickle.load(fd) + self.predict_method = predict_method + self.fit_method = fit_method + self.finetune_method = finetune_method + + def predict(self, X: np.ndarray) -> np.ndarray: + return getattr(self.model, self.predict_method)(X) + + def fit(self, X: np.ndarray, y: np.ndarray): + getattr(self.model, self.fit_method)(X, y) + + def finetune(self, X: np.ndarray, y: np.ndarray): + getattr(self.model, self.finetune_method)(X, y) diff --git a/tests/test_reuse/test_averaging.py b/tests/test_reuse/test_averaging.py new file mode 100644 index 0000000..de4dde5 --- /dev/null +++ b/tests/test_reuse/test_averaging.py @@ -0,0 +1,43 @@ +import os +import json +import string +import random +import torch +import unittest +import tempfile +import numpy as np + +from learnware.specification import RKMETableSpecification, HeteroMapTableSpecification +from learnware.specification import generate_stat_spec +from learnware.market.heterogeneous.organizer import HeteroMap + +class TestAveragingReuse(unittest.TestCase): + + def setUp(self): + self.hetero_map = HeteroMap() + + def _test_hetero_spec(self, X): + rkme: RKMETableSpecification = generate_stat_spec(type="table", X=X) + hetero_spec = self.hetero_map.hetero_mapping(rkme_spec=rkme, features=dict()) + with tempfile.TemporaryDirectory(prefix="learnware_") as tempdir: + rkme_path = os.path.join(tempdir, "rkme.json") + hetero_spec.save(rkme_path) + + with open(rkme_path, "r") as f: + data = json.load(f) + assert data["type"] == "HeteroMapTableSpecification" + + rkme2 = HeteroMapTableSpecification() + rkme2.load(rkme_path) + assert rkme2.type == "HeteroMapTableSpecification" + + + def test_hetero_rkme(self): + self._test_hetero_spec(np.random.uniform(-10000, 10000, size=(5000, 200))) + self._test_hetero_spec(np.random.uniform(-10000, 10000, size=(10000, 100))) + self._test_hetero_spec(np.random.uniform(-10000, 10000, size=(5, 20))) + self._test_hetero_spec(np.random.uniform(-10000, 10000, size=(1, 50))) + self._test_hetero_spec(np.random.uniform(-10000, 10000, size=(100, 150))) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_workflow/learnware_example/example_init.py b/tests/test_workflow/learnware_example/example_init.py index 47d3708..16188b3 100644 --- a/tests/test_workflow/learnware_example/example_init.py +++ b/tests/test_workflow/learnware_example/example_init.py @@ -8,7 +8,7 @@ class SVM(BaseModel): def __init__(self): super(SVM, self).__init__(input_shape=(64,), output_shape=(10,)) dir_path = os.path.dirname(os.path.abspath(__file__)) - self.model = joblib.load(os.path.join(dir_path, "svm.pkl")) + self.model = pickle.load(os.path.join(dir_path, "svm.pkl")) def fit(self, X: np.ndarray, y: np.ndarray): pass