diff --git a/core/common/constant.py b/core/common/constant.py index aa111ddb..5444e289 100644 --- a/core/common/constant.py +++ b/core/common/constant.py @@ -22,7 +22,6 @@ class DatasetFormat(Enum): File format of inputting dataset. Currently, file formats are as follows: txt, csv. """ - CSV = "csv" TXT = "txt" JSON = "json" @@ -32,20 +31,16 @@ class ParadigmType(Enum): """ Algorithm paradigm type. """ - SINGLE_TASK_LEARNING = "singletasklearning" INCREMENTAL_LEARNING = "incrementallearning" MULTIEDGE_INFERENCE = "multiedgeinference" LIFELONG_LEARNING = "lifelonglearning" - FEDERATED_LEARNING = "federatedlearning" - FEDERATED_CLASS_INCREMENTAL_LEARNING = "federatedclassincrementallearning" class ModuleType(Enum): """ Algorithm module type. """ - BASEMODEL = "basemodel" # HEM @@ -68,26 +63,20 @@ class ModuleType(Enum): UNSEEN_SAMPLE_RECOGNITION = "unseen_sample_recognition" UNSEEN_SAMPLE_RE_RECOGNITION = "unseen_sample_re_recognition" - # FL_AGG - AGGREGATION = "aggregation" - class SystemMetricType(Enum): """ System metric type of ianvs. """ - SAMPLES_TRANSFER_RATIO = "samples_transfer_ratio" FWT = "FWT" BWT = "BWT" TASK_AVG_ACC = "task_avg_acc" MATRIX = "MATRIX" - FORGET_RATE = "forget_rate" class TestObjectType(Enum): """ Test object type of ianvs. """ - ALGORITHMS = "algorithms" diff --git a/core/storymanager/rank/rank.py b/core/storymanager/rank/rank.py index 5a558692..5c3d85d4 100644 --- a/core/storymanager/rank/rank.py +++ b/core/storymanager/rank/rank.py @@ -35,12 +35,15 @@ class Rank: def __init__(self, config): self.sort_by: list = [] - self.visualization: dict = {"mode": "selected_only", "method": "print_table"} + self.visualization: dict = { + "mode": "selected_only", + "method": "print_table" + } self.selected_dataitem: dict = { "paradigms": ["all"], "modules": ["all"], "hyperparameters": ["all"], - "metrics": ["all"], + "metrics": ["all"] } self.save_mode: str = "selected_and_all" @@ -59,21 +62,15 @@ def _parse_config(self, config): def _check_fields(self): if not self.sort_by and not isinstance(self.sort_by, list): - raise ValueError( - f"rank's sort_by({self.sort_by}) must be provided and be list type." - ) + raise ValueError(f"rank's sort_by({self.sort_by}) must be provided and be list type.") if not self.visualization and not isinstance(self.visualization, dict): - raise ValueError( - f"rank's visualization({self.visualization}) " - f"must be provided and be dict type." - ) + raise ValueError(f"rank's visualization({self.visualization}) " + f"must be provided and be dict type.") if not self.selected_dataitem and not isinstance(self.selected_dataitem, dict): - raise ValueError( - f"rank's selected_dataitem({self.selected_dataitem}) " - f"must be provided and be dict type." - ) + raise ValueError(f"rank's selected_dataitem({self.selected_dataitem}) " + f"must be provided and be dict type.") if not self.selected_dataitem.get("paradigms"): raise ValueError("not found paradigms of selected_dataitem in rank.") @@ -85,10 +82,8 @@ def _check_fields(self): raise ValueError("not found metrics of selected_dataitem in rank.") if not self.save_mode and not isinstance(self.save_mode, list): - raise ValueError( - f"rank's save_mode({self.save_mode}) " - f"must be provided and be list type." - ) + raise ValueError(f"rank's save_mode({self.save_mode}) " + f"must be provided and be list type.") @classmethod def _get_all_metric_names(cls, test_results) -> list: @@ -138,7 +133,7 @@ def _sort_all_df(self, all_df, all_metric_names): if metric_name not in all_metric_names: continue - print(metric_name) + sort_metric_list.append(metric_name) is_ascend_list.append(ele.get(metric_name) == "ascend") @@ -203,15 +198,7 @@ def _get_selected(self, test_cases, test_results) -> pd.DataFrame: if metric_names == ["all"]: metric_names = self._get_all_metric_names(test_results) - header = [ - "algorithm", - *metric_names, - "paradigm", - *module_types, - *hps_names, - "time", - "url", - ] + header = ["algorithm", *metric_names, "paradigm", *module_types, *hps_names, "time", "url"] all_df = copy.deepcopy(self.all_df) selected_df = pd.DataFrame(all_df, columns=header) @@ -233,16 +220,14 @@ def _draw_pictures(self, test_cases, test_results): for test_case in test_cases: out_put = test_case.output_dir test_result = test_results[test_case.id][0] - matrix = test_result.get("Matrix") - # print(out_put) + matrix = test_result.get('Matrix') + #print(out_put) for key in matrix.keys(): draw_heatmap_picture(out_put, key, matrix[key]) def _prepare(self, test_cases, test_results, output_dir): all_metric_names = self._get_all_metric_names(test_results) - print(f"in_prepare all_metric_names: {all_metric_names}") all_hps_names = self._get_all_hps_names(test_cases) - print(f"in_prepare all_hps_names: {all_hps_names}") all_module_types = self._get_all_module_types(test_cases) self.all_df_header = [ "algorithm", *all_metric_names, @@ -300,5 +285,4 @@ def plot(self): except Exception as err: raise RuntimeError( f"process visualization(method={method}) of " - f"rank file({self.selected_rank_file}) failed, error: {err}." - ) from err + f"rank file({self.selected_rank_file}) failed, error: {err}.") from err diff --git a/core/testcasecontroller/algorithm/algorithm.py b/core/testcasecontroller/algorithm/algorithm.py index 5bad73a9..cb2d9b7b 100644 --- a/core/testcasecontroller/algorithm/algorithm.py +++ b/core/testcasecontroller/algorithm/algorithm.py @@ -24,8 +24,6 @@ IncrementalLearning, MultiedgeInference, LifelongLearning, - FederatedLearning, - FederatedClassIncrementalLearning ) from core.testcasecontroller.generation_assistant import get_full_combinations @@ -66,21 +64,12 @@ def __init__(self, name, config): "train_ratio": 0.8, "splitting_method": "default" } - self.fl_data_setting: dict = { - "train_ratio": 1.0, - "splitting_method": "default", - "data_partition": "iid", - 'non_iid_ratio': 0.6, - "label_data_ratio": 1.0 - } - self.initial_model_url: str = "" self.modules: list = [] self.modules_list = None self._parse_config(config) self._load_third_party_packages() - # pylint: disable=R0911 def paradigm(self, workspace: str, **kwargs): """ get test process of AI algorithm paradigm. @@ -102,6 +91,7 @@ def paradigm(self, workspace: str, **kwargs): # pylint: disable=C0103 for k, v in self.__dict__.items(): config.update({k: v}) + if self.paradigm_type == ParadigmType.SINGLE_TASK_LEARNING.value: return SingleTaskLearning(workspace, **config) @@ -114,12 +104,6 @@ def paradigm(self, workspace: str, **kwargs): if self.paradigm_type == ParadigmType.LIFELONG_LEARNING.value: return LifelongLearning(workspace, **config) - if self.paradigm_type == ParadigmType.FEDERATED_LEARNING.value: - return FederatedLearning(workspace, **config) - - if self.paradigm_type == ParadigmType.FEDERATED_CLASS_INCREMENTAL_LEARNING.value: - return FederatedClassIncrementalLearning(workspace, **config) - return None def _check_fields(self): diff --git a/core/testcasecontroller/algorithm/module/module.py b/core/testcasecontroller/algorithm/module/module.py index 1772725e..6d487d97 100644 --- a/core/testcasecontroller/algorithm/module/module.py +++ b/core/testcasecontroller/algorithm/module/module.py @@ -86,7 +86,6 @@ def get_module_instance(self, module_type): function """ - print(f'hyperparameters_list: {self.hyperparameters_list}') class_factory_type = ClassType.GENERAL if module_type in [ModuleType.HARD_EXAMPLE_MINING.value]: class_factory_type = ClassType.HEM @@ -107,20 +106,6 @@ def get_module_instance(self, module_type): elif module_type in [ModuleType.UNSEEN_SAMPLE_RECOGNITION.value, ModuleType.UNSEEN_SAMPLE_RE_RECOGNITION.value]: class_factory_type = ClassType.UTD - elif module_type in [ModuleType.AGGREGATION.value]: - class_factory_type = ClassType.FL_AGG - agg = None - print(self.url) - if self.url : - try: - utils.load_module(self.url) - agg = ClassFactory.get_cls( - type_name=class_factory_type, t_cls_name=self.name)(**self.hyperparameters) - print(agg) - except Exception as err: - raise RuntimeError(f"module(type={module_type} loads class(name={self.name}) " - f"failed, error: {err}.") from err - return self.name, agg if self.url: try: @@ -128,6 +113,7 @@ def get_module_instance(self, module_type): # pylint: disable=E1134 func = ClassFactory.get_cls( type_name=class_factory_type, t_cls_name=self.name)(**self.hyperparameters) + return func except Exception as err: raise RuntimeError(f"module(type={module_type} loads class(name={self.name}) " diff --git a/core/testcasecontroller/algorithm/paradigm/__init__.py b/core/testcasecontroller/algorithm/paradigm/__init__.py index 5c50b243..c966bd38 100644 --- a/core/testcasecontroller/algorithm/paradigm/__init__.py +++ b/core/testcasecontroller/algorithm/paradigm/__init__.py @@ -17,4 +17,3 @@ from .singletask_learning import SingleTaskLearning from .multiedge_inference import MultiedgeInference from .lifelong_learning import LifelongLearning -from .federated_learning import FederatedLearning, FederatedClassIncrementalLearning diff --git a/core/testcasecontroller/algorithm/paradigm/base.py b/core/testcasecontroller/algorithm/paradigm/base.py index e5178e29..3fe39267 100644 --- a/core/testcasecontroller/algorithm/paradigm/base.py +++ b/core/testcasecontroller/algorithm/paradigm/base.py @@ -18,8 +18,8 @@ from sedna.core.incremental_learning import IncrementalLearning from sedna.core.lifelong_learning import LifelongLearning + from core.common.constant import ModuleType, ParadigmType -from .sedna_federated_learning import FederatedLearning class ParadigmBase: @@ -97,51 +97,33 @@ def build_paradigm_job(self, paradigm_type): return IncrementalLearning( estimator=self.module_instances.get(ModuleType.BASEMODEL.value), hard_example_mining=self.module_instances.get( - ModuleType.HARD_EXAMPLE_MINING.value - ), - ) + ModuleType.HARD_EXAMPLE_MINING.value)) if paradigm_type == ParadigmType.LIFELONG_LEARNING.value: return LifelongLearning( - estimator=self.module_instances.get(ModuleType.BASEMODEL.value), + estimator=self.module_instances.get( + ModuleType.BASEMODEL.value), task_definition=self.module_instances.get( - ModuleType.TASK_DEFINITION.value - ), + ModuleType.TASK_DEFINITION.value), task_relationship_discovery=self.module_instances.get( - ModuleType.TASK_RELATIONSHIP_DISCOVERY.value - ), + ModuleType.TASK_RELATIONSHIP_DISCOVERY.value), task_allocation=self.module_instances.get( - ModuleType.TASK_ALLOCATION.value - ), + ModuleType.TASK_ALLOCATION.value), task_remodeling=self.module_instances.get( - ModuleType.TASK_REMODELING.value - ), + ModuleType.TASK_REMODELING.value), inference_integrate=self.module_instances.get( - ModuleType.INFERENCE_INTEGRATE.value - ), + ModuleType.INFERENCE_INTEGRATE.value), task_update_decision=self.module_instances.get( - ModuleType.TASK_UPDATE_DECISION.value - ), + ModuleType.TASK_UPDATE_DECISION.value), unseen_task_allocation=self.module_instances.get( - ModuleType.UNSEEN_TASK_ALLOCATION.value - ), + ModuleType.UNSEEN_TASK_ALLOCATION.value), unseen_sample_recognition=self.module_instances.get( - ModuleType.UNSEEN_SAMPLE_RECOGNITION.value - ), + ModuleType.UNSEEN_SAMPLE_RECOGNITION.value), unseen_sample_re_recognition=self.module_instances.get( - ModuleType.UNSEEN_SAMPLE_RE_RECOGNITION.value - ), + ModuleType.UNSEEN_SAMPLE_RE_RECOGNITION.value) ) # pylint: disable=E1101 if paradigm_type == ParadigmType.MULTIEDGE_INFERENCE.value: return self.module_instances.get(ModuleType.BASEMODEL.value) - if paradigm_type in [ - ParadigmType.FEDERATED_LEARNING.value, - ParadigmType.FEDERATED_CLASS_INCREMENTAL_LEARNING.value, - ]: - return FederatedLearning( - estimator=self.module_instances.get(ModuleType.BASEMODEL.value) - ) - return None diff --git a/core/testcasecontroller/algorithm/paradigm/federated_learning/__init__.py b/core/testcasecontroller/algorithm/paradigm/federated_learning/__init__.py deleted file mode 100644 index 55ebbea2..00000000 --- a/core/testcasecontroller/algorithm/paradigm/federated_learning/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2022 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# pylint: disable=missing-module-docstring -from .federated_learning import FederatedLearning -from .federated_class_incremental_learning import FederatedClassIncrementalLearning diff --git a/core/testcasecontroller/algorithm/paradigm/federated_learning/federated_class_incremental_learning.py b/core/testcasecontroller/algorithm/paradigm/federated_learning/federated_class_incremental_learning.py deleted file mode 100644 index 3baaf072..00000000 --- a/core/testcasecontroller/algorithm/paradigm/federated_learning/federated_class_incremental_learning.py +++ /dev/null @@ -1,295 +0,0 @@ -# Copyright 2022 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Federated Class-Incremental Learning Paradigm""" -# pylint: disable=C0412 -# pylint: disable=W1203 -# pylint: disable=C0103 -# pylint: disable=C0206 -# pylint: disable=C0201 -import numpy as np -from core.common.log import LOGGER -from core.common.constant import ParadigmType, SystemMetricType -from core.testcasecontroller.metrics.metrics import get_metric_func -from .federated_learning import FederatedLearning - - -class FederatedClassIncrementalLearning(FederatedLearning): - # pylint: disable=too-many-instance-attributes - """ - FederatedClassIncrementalLearning - Federated Class-Incremental Learning Paradigm - Notes: - 1. Ianvs serves as testing tools for test objects, e.g., algorithms. - 2. Ianvs does NOT include code directly on test object. - 3. Algorithms serve as typical test objects in Ianvs - and detailed algorithms are thus NOT included in this Ianvs python file. - 4. As for the details of example test objects, e.g., algorithms, - please refer to third party packages in Ianvs example. - For example, AI workflow and interface pls refer to sedna - (sedna docs: https://sedna.readthedocs.io/en/latest/api/lib/index.html), - and module implementation pls refer to `examples' test algorithms`, - e.g., basemodel.py, hard_example_mining.py. - - Parameters - --------- - workspace: string - the output required for Federated Class-Incremental Learning paradigm. - kwargs: dict - config required for the test process of lifelong learning paradigm, - e.g.: algorithm modules, dataset, initial network, incremental rounds, - network eval config, etc. - """ - - def __init__(self, workspace, **kwargs): - super().__init__(workspace, **kwargs) - self.incremental_rounds = kwargs.get("incremental_rounds", 1) - self.system_metric_info = { - SystemMetricType.FORGET_RATE.value: [], - SystemMetricType.TASK_AVG_ACC.value: {}, - } - - self.aggregate_clients = [] - self.train_infos = [] - - self.forget_rate_metrics = [] - self.accuracy_per_round = [] - metrics_dict = kwargs.get("model_eval", {})["model_metric"] - _, accuracy_func = get_metric_func(metrics_dict) - self.accuracy_func = accuracy_func - - def task_definition(self, dataset_files, task_id): - """Define the task for the class incremental learning paradigm - - Args: - dataset_files (list): dataset_files for train data - task_id (int): task id for the current task - - Returns: - list: train dataset in numpy format for each task - """ - LOGGER.info(f"len(dataset_files): {len(dataset_files)}") - # 1. Partition Dataset - train_dataset_files, _ = dataset_files[task_id] - LOGGER.info(f"train_dataset_files: {train_dataset_files}") - train_datasets = self.train_data_partition(train_dataset_files) - LOGGER.info(f"train_datasets: {len(train_datasets)}") - task_size = self.get_task_size(train_datasets) - LOGGER.info(f"task_size: {task_size}") - # 2. According to setting, to split the label and unlabel data for each task - train_datasets = self.split_label_unlabel_data(train_datasets) - # 3. Return the dataset for each task [{label_data, unlabel_data}, ...] - return train_datasets, task_size - - def get_task_size(self, train_datasets): - """get the task size for each task - - Args: - train_datasets (list): train dataset for each client - - Returns: - int: task size for each task - """ - LOGGER.info(f"train_datasets: {len(train_datasets[0])}") - return np.unique(train_datasets[0][1]).shape[0] - - def split_label_unlabel_data(self, train_datasets): - """split train dataset into label and unlabel data for semi-supervised learning - - Args: - train_datasets (list): train dataset for each client - - Returns: - list: the new train dataset for each client that in label and unlabel format - [{label_x: [], label_y: [], unlabel_x: [], unlabel_y: []}, ...] - """ - label_ratio = self.fl_data_setting.get("label_data_ratio") - new_train_datasets = [] - train_dataset_len = len(train_datasets) - for i in range(train_dataset_len): - train_dataset_dict = {} - label_data_number = int(label_ratio * len(train_datasets[i][0])) - # split dataset into label and unlabel data - train_dataset_dict["label_x"] = train_datasets[i][0][:label_data_number] - train_dataset_dict["label_y"] = train_datasets[i][1][:label_data_number] - train_dataset_dict["unlabel_x"] = train_datasets[i][0][label_data_number:] - train_dataset_dict["unlabel_y"] = train_datasets[i][1][label_data_number:] - new_train_datasets.append(train_dataset_dict) - return new_train_datasets - - def init_client(self): - self.clients = [ - self.build_paradigm_job( - ParadigmType.FEDERATED_CLASS_INCREMENTAL_LEARNING.value - ) - for _ in range(self.clients_number) - ] - - def run(self): - """run the Federated Class-Incremental Learning paradigm - This function will run the Federated Class-Incremental Learning paradigm. - 1. initialize the clients - 2. split the dataset into several tasks - 3. train the model on the clients - 4. aggregate the model weights and maybe need to perform some helper function - 5. send the weights to the clients - 6. evaluate the model performance on old classes - 7. finally, return the prediction result and system metric information - Returns: - list: prediction result - dict: system metric information - """ - - self.init_client() - dataset_files = self._split_dataset(self.incremental_rounds) - test_dataset_files = self._split_test_dataset(self.incremental_rounds) - LOGGER.info(f"get the dataset_files: {dataset_files}") - forget_rate = self.system_metric_info.get(SystemMetricType.FORGET_RATE.value) - for task_id in range(self.incremental_rounds): - train_datasets, task_size = self.task_definition(dataset_files, task_id) - testdatasets = test_dataset_files[: task_id + 1] - for r in range(self.rounds): - LOGGER.info( - f"Round {r} task id: {task_id} len of train_datasets: {len(train_datasets)}" - ) - self.train( - train_datasets, task_id=task_id, round=r, task_size=task_size - ) - global_weights = self.aggregator.aggregate(self.aggregate_clients) - if hasattr(self.aggregator, "helper_function"): - self.helper_function(self.train_infos) - self.send_weights_to_clients(global_weights) - self.aggregate_clients.clear() - self.train_infos.clear() - forget_rate.append(self.evaluation(testdatasets, task_id)) - test_res = self.predict(self.dataset.test_url) - return test_res, self.system_metric_info - - def _split_test_dataset(self, split_time): - """split test dataset - This function will split a test dataset from test_url into several parts. - Each part will be used for the evaluation of the model after each round. - Args: - split_time (int): the number of split time - - Returns: - list : the test dataset for each round [{x: [], y: []}, ...] - """ - test_dataset = self.dataset.load_data(self.dataset.test_url, "eval") - all_data = len(test_dataset.x) - step = all_data // split_time - test_datasets_files = [] - index = 1 - while index <= split_time: - new_dataset = {} - if index == split_time: - new_dataset["x"] = test_dataset.x[step * (index - 1) :] - new_dataset["y"] = test_dataset.y[step * (index - 1) :] - else: - new_dataset["x"] = test_dataset.x[step * (index - 1) : step * index] - new_dataset["y"] = test_dataset.y[step * (index - 1) : step * index] - test_datasets_files.append(new_dataset) - index += 1 - return test_datasets_files - - def client_train(self, client_idx, train_datasets, validation_datasets, **kwargs): - """client train function that will be called by the thread - - Args: - client_idx (int): client index - train_datasets (list): train dataset for each client - validation_datasets (list): validation dataset for each client - """ - train_info = super().client_train( - client_idx, train_datasets, validation_datasets, **kwargs - ) - with self.lock: - self.train_infos.append(train_info) - - def helper_function(self, train_infos): - """helper function for FCI Method - Many of the FCI algorithms need server to perform some operations - after the training of each round e.g data generation, model update etc. - Args: - train_infos (list of dict): the train info that the clients want to send to the server - """ - - for i in range(self.clients_number): - helper_info = self.aggregator.helper_function(train_infos[i]) - self.clients[i].helper_function(helper_info) - LOGGER.info("finish helper function") - - # pylint: disable=too-many-locals - # pylint: disable=C0200 - def evaluation(self, testdataset_files, incremental_round): - """evaluate the model performance on old classes - - Args: - testdataset_files (list): the test dataset for each round - incremental_round (int): the total incremental training round - - Returns: - float: forget rate for the current round - reference: https://ieeexplore.ieee.org/document/10574196/ - """ - if self.accuracy_func is None: - raise ValueError("accuracy function is not defined") - LOGGER.info("********start evaluation********") - if isinstance(testdataset_files, str): - testdataset_files = [testdataset_files] - job = self.get_global_model() - # caculate the seen class accuracy - old_class_acc_list = ( - [] - ) # for current round [class_0: acc_0, class_1: acc1, ....] - for index in range(len(testdataset_files)): - acc_list = [] - for data_index in range(len(testdataset_files[index]["x"])): - data = testdataset_files[index]["x"][data_index] - res = job.inference([data]) - LOGGER.info( - f"label is {testdataset_files[index]['y'][data_index]}, res is {res}" - ) - acc = self.accuracy_func( - [testdataset_files[index]["y"][data_index]], res - ) - acc_list.append(acc) - old_class_acc_list.extend(acc_list) - current_forget_rate = 0.0 - max_acc_sum = 0 - self.accuracy_per_round.append(old_class_acc_list) - self.system_metric_info[SystemMetricType.TASK_AVG_ACC.value]["accuracy"] = ( - np.mean(old_class_acc_list) - ) - # caculate the forget rate - for i in range(len(old_class_acc_list)): - max_acc_diff = 0 - for j in range(incremental_round): - acc_per_round = self.accuracy_per_round[j] - if i < len(acc_per_round): - max_acc_diff = max( - max_acc_diff, acc_per_round[i] - old_class_acc_list[i] - ) - max_acc_sum += max_acc_diff - current_forget_rate = ( - max_acc_sum / len(old_class_acc_list) if incremental_round > 0 else 0.0 - ) - tavk_avg_acc = self.system_metric_info[SystemMetricType.TASK_AVG_ACC.value][ - "accuracy" - ] - LOGGER.info( - f"for current round: {incremental_round} forget rate: {current_forget_rate}" - f"task avg acc: {tavk_avg_acc}" - ) - return current_forget_rate diff --git a/core/testcasecontroller/algorithm/paradigm/federated_learning/federated_learning.py b/core/testcasecontroller/algorithm/paradigm/federated_learning/federated_learning.py deleted file mode 100644 index 2a714360..00000000 --- a/core/testcasecontroller/algorithm/paradigm/federated_learning/federated_learning.py +++ /dev/null @@ -1,242 +0,0 @@ -# Copyright 2022 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Federated Learning Paradigm""" -# pylint: disable=C0412 -# pylint: disable=W1203 -# pylint: disable=C0103 -# pylint: disable=C0206 -# pylint: disable=C0201 -# pylint: disable=W1203 -from threading import Thread, RLock - -from sedna.algorithms.aggregation import AggClient -from core.common.log import LOGGER -from core.common.constant import ParadigmType, ModuleType -from core.common.utils import get_file_format -from core.testcasecontroller.algorithm.paradigm.base import ParadigmBase -from core.testenvmanager.dataset.utils import read_data_from_file_to_npy, partition_data - - -class FederatedLearning(ParadigmBase): - # pylint: disable=too-many-instance-attributes - """ - FederatedLearning - Federated Learning Paradigm - Notes: - 1. Ianvs serves as testing tools for test objects, e.g., algorithms. - 2. Ianvs does NOT include code directly on test object. - 3. Algorithms serve as typical test objects in Ianvs - and detailed algorithms are thus NOT included in this Ianvs python file. - 4. As for the details of example test objects, e.g., algorithms, - please refer to third party packages in Ianvs example. - For example, AI workflow and interface pls refer to sedna - (sedna docs: https://sedna.readthedocs.io/en/latest/api/lib/index.html), - and module implementation pls refer to `examples' test algorithms`, - e.g., basemodel.py, hard_example_mining.py. - - Parameters - --------- - workspace: string - the output required for Federated Class-Incremental Learning paradigm. - kwargs: dict - config required for the test process of lifelong learning paradigm, - e.g.: algorithm modules, dataset, initial network, incremental rounds, - network eval config, etc. - """ - - def __init__(self, workspace, **kwargs): - ParadigmBase.__init__(self, workspace, **kwargs) - - self.workspace = workspace - self.kwargs = kwargs - - self.fl_data_setting = kwargs.get("fl_data_setting") - self.rounds = kwargs.get("round", 1) - self.clients = [] - self.lock = RLock() - - self.aggregate_clients = [] - self.clients_number = kwargs.get("client_number", 1) - _, self.aggregator = self.module_instances.get(ModuleType.AGGREGATION.value) - - def init_client(self): - """init clients for the paradigm of federated learning.""" - self.clients = [ - self.build_paradigm_job(ParadigmType.FEDERATED_LEARNING.value) - for i in range(self.clients_number) - ] - - def run(self): - """ - run the test flow of incremental learning paradigm. - - Returns - ------ - test result: numpy.ndarray - system metric info: dict - information needed to compute system metrics. - """ - # init client wait for connection - self.init_client() - dataset_files = self.get_all_train_data() - train_dataset_file, _ = dataset_files[0] - train_datasets = self.train_data_partition(train_dataset_file) - for r in range(self.rounds): - self.train(train_datasets, round=r) - global_weights = self.aggregator.aggregate(self.aggregate_clients) - self.send_weights_to_clients(global_weights) - self.aggregate_clients.clear() - test_res = self.predict(self.dataset.test_url) - return test_res, self.system_metric_info - - def get_all_train_data(self): - """Get all train data for the paradigm of federated learning. - - Returns: - list: train data list - """ - split_time = 1 # only one split ——all the data - return self._split_dataset(split_time) - - def _split_dataset(self, splitting_dataset_times=1): - """spit the dataset using ianvs dataset.split dataset method - - Args: - splitting_dataset_times (int, optional): . Defaults to 1. - - Returns: - list: dataset files - """ - train_dataset_ratio = self.fl_data_setting.get("train_ratio") - splitting_dataset_method = self.fl_data_setting.get("splitting_method") - return self.dataset.split_dataset( - self.dataset.train_url, - get_file_format(self.dataset.train_url), - train_dataset_ratio, - method=splitting_dataset_method, - dataset_types=("model_train", "model_eval"), - output_dir=self.dataset_output_dir(), - times=splitting_dataset_times, - ) - - def train_data_partition(self, train_dataset_file): - """ - Partition the dataset for the class incremental learning paradigm - - i.i.d - - non-i.i.d - """ - LOGGER.info(train_dataset_file) - train_datasets = None - if isinstance(train_dataset_file, str): - train_datasets = self.dataset.load_data(train_dataset_file, "train") - if isinstance(train_dataset_file, list): - train_datasets = [] - for file in train_dataset_file: - train_datasets.append(self.dataset.load_data(file, "train")) - assert train_datasets is not None, "train_dataset is None" - # translate file to real data that can be used in train - # - provide a default method to read data from file to npy - # - can support customized method to read data from file to npy - train_datasets = read_data_from_file_to_npy(train_datasets) - # Partition data to iid or non-iid - train_datasets = partition_data( - train_datasets, - self.clients_number, - self.fl_data_setting.get("data_partition"), - self.fl_data_setting.get("non_iid_ratio"), - ) - return train_datasets - - def client_train(self, client_idx, train_datasets, validation_datasets, **kwargs): - """client train - - Args: - client_idx (int): client index - train_datasets (list): train data for each client - validation_datasets (list): validation data for each client - """ - train_info = self.clients[client_idx].train( - train_datasets[client_idx], validation_datasets, **kwargs - ) - train_info["client_id"] = client_idx - agg_client = AggClient() - agg_client.num_samples = train_info["num_samples"] - agg_client.weights = self.clients[client_idx].get_weights() - with self.lock: - self.aggregate_clients.append(agg_client) - return train_info - - def train(self, train_datasets, **kwargs): - """train——multi-threading to perform client local training - - Args: - train_datasets (list): train data for each client - """ - client_threads = [] - LOGGER.info(f"len(self.clients): {len(self.clients)}") - for idx in range(self.clients_number): - client_thread = Thread( - target=self.client_train, - args=(idx, train_datasets, None), - kwargs=kwargs, - ) - client_thread.start() - client_threads.append(client_thread) - for thread in client_threads: - thread.join() - LOGGER.info("finish training") - - def send_weights_to_clients(self, global_weights): - """send weights to clients - - Args: - global_weights (list): aggregated weights - """ - for client in self.clients: - client.set_weights(global_weights) - LOGGER.info("finish send weights to clients") - - def get_global_model(self): - """get the global model for evaluation - After final round training, and aggregation - the global model can be the first client model - - Returns: - JobBase: sedna_federated_learning.FederatedLearning - """ - return self.clients[0] - - def predict(self, test_dataset_file): - """global test to predict the test dataset - - Args: - test_dataset_file (list): test data - - Returns: - list: test result - """ - test_dataset = None - if isinstance(test_dataset_file, str): - test_dataset = self.dataset.load_data(test_dataset_file, "eval") - if isinstance(test_dataset_file, list): - test_dataset = [] - for file in test_dataset_file: - test_dataset.append(self.dataset.load_data(file, "eval")) - assert test_dataset is not None, "test_dataset is None" - LOGGER.info(f" before predict {len(test_dataset.x)}") - job = self.get_global_model() - test_res = job.inference(test_dataset.x) - LOGGER.info(f" after predict {len(test_res)}") - return test_res diff --git a/core/testcasecontroller/algorithm/paradigm/sedna_federated_learning.py b/core/testcasecontroller/algorithm/paradigm/sedna_federated_learning.py deleted file mode 100644 index 3856c7ac..00000000 --- a/core/testcasecontroller/algorithm/paradigm/sedna_federated_learning.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright 2022 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Sedna Federated Learning JobBase""" -# pylint: disable=C0412 -import copy -from sedna.core.base import JobBase - - -# pylint: disable=C0412 -class FederatedLearning(JobBase): - """Federated Learning JobBase Represent the Federated Learning Client side""" - - # pylint: disable=too-many-locals - def __init__(self, estimator): - super().__init__(estimator) - cp_estimator = copy.deepcopy(estimator) - self.estimator = cp_estimator - - # pylint: disable=W0221 - def train(self, train_data, vald_data, **kwargs): - """Local training function - - Args: - train_data (list): training data - vald_data (list): validation data (optional) - - Returns: - dict: train info that will be used for aggregation - """ - return self.estimator.train(train_data, vald_data, **kwargs) - - def get_weights(self): - """get the weights of the model - - Returns: - list: weights of the model - """ - return self.estimator.get_weights() - - def set_weights(self, weights): - """set the weights of the model - - Args: - weights (list): set the weights of the model - """ - self.estimator.set_weights(weights) - - def helper_function(self, helper_info): - """helper function that can be used for any purpose - - Args: - helper_info (dict): helper info that generated by the server's helper function - - Returns: - None: None - """ - return self.estimator.helper_function(helper_info) diff --git a/core/testcasecontroller/metrics/metrics.py b/core/testcasecontroller/metrics/metrics.py index 8d3f9f52..4e9c886a 100644 --- a/core/testcasecontroller/metrics/metrics.py +++ b/core/testcasecontroller/metrics/metrics.py @@ -39,7 +39,8 @@ def samples_transfer_ratio_func(system_metric_info: dict): """ - info = system_metric_info.get(SystemMetricType.SAMPLES_TRANSFER_RATIO.value) + info = system_metric_info.get( + SystemMetricType.SAMPLES_TRANSFER_RATIO.value) inference_num = 0 transfer_num = 0 for inference_data, transfer_data in info: @@ -52,7 +53,8 @@ def compute(key, matrix): """ Compute BWT and FWT scores for a given matrix. """ - print(f"compute function: key={key}, matrix={matrix}, type(matrix)={type(matrix)}") + print( + f"compute function: key={key}, matrix={matrix}, type(matrix)={type(matrix)}") length = len(matrix) accuracy = 0.0 @@ -61,7 +63,7 @@ def compute(key, matrix): flag = True for row in matrix: - if not isinstance(row, list) or len(row) != length - 1: + if not isinstance(row, list) or len(row) != length-1: flag = False break @@ -70,29 +72,30 @@ def compute(key, matrix): fwt_score = np.nan return bwt_score, fwt_score - for i in range(length - 1): - for j in range(length - 1): - if "accuracy" in matrix[i + 1][j] and "accuracy" in matrix[i][j]: - accuracy += matrix[i + 1][j]["accuracy"] - bwt_score += matrix[i + 1][j]["accuracy"] - matrix[i][j]["accuracy"] + for i in range(length-1): + for j in range(length-1): + if 'accuracy' in matrix[i+1][j] and 'accuracy' in matrix[i][j]: + accuracy += matrix[i+1][j]['accuracy'] + bwt_score += matrix[i+1][j]['accuracy'] - \ + matrix[i][j]['accuracy'] - for i in range(0, length - 1): - if "accuracy" in matrix[i][i] and "accuracy" in matrix[0][i]: - fwt_score += matrix[i][i]["accuracy"] - matrix[0][i]["accuracy"] + for i in range(0, length-1): + if 'accuracy' in matrix[i][i] and 'accuracy' in matrix[0][i]: + fwt_score += matrix[i][i]['accuracy'] - matrix[0][i]['accuracy'] - accuracy = accuracy / ((length - 1) * (length - 1)) - bwt_score = bwt_score / ((length - 1) * (length - 1)) - fwt_score = fwt_score / (length - 1) + accuracy = accuracy / ((length-1) * (length-1)) + bwt_score = bwt_score / ((length-1) * (length-1)) + fwt_score = fwt_score / (length-1) print(f"{key} BWT_score: {bwt_score}") print(f"{key} FWT_score: {fwt_score}") my_matrix = [] - for i in range(length - 1): + for i in range(length-1): my_matrix.append([]) - for j in range(length - 1): - if "accuracy" in matrix[i + 1][j]: - my_matrix[i].append(matrix[i + 1][j]["accuracy"]) + for j in range(length-1): + if 'accuracy' in matrix[i+1][j]: + my_matrix[i].append(matrix[i+1][j]['accuracy']) return my_matrix, bwt_score, fwt_score @@ -138,17 +141,7 @@ def task_avg_acc_func(system_metric_info: dict): compute task average accuracy """ info = system_metric_info.get(SystemMetricType.TASK_AVG_ACC.value) - return round(info["accuracy"], 3) - - -def forget_rate_func(system_metric_info: dict): - """ - compute task forget rate - """ - info = system_metric_info.get(SystemMetricType.FORGET_RATE.value) - forget_rate = np.mean(info) - print(f"forget_rate: {forget_rate}") - return round(forget_rate, 3) + return info["accuracy"] def get_metric_func(metric_dict: dict): @@ -169,17 +162,14 @@ def get_metric_func(metric_dict: dict): name = metric_dict.get("name") url = metric_dict.get("url") - print(f"get metric func: name={name}, url={url}") if url: try: load_module(url) metric_func = ClassFactory.get_cls( - type_name=ClassType.GENERAL, t_cls_name=name - ) + type_name=ClassType.GENERAL, t_cls_name=name) return name, metric_func except Exception as err: raise RuntimeError( - f"get metric func(url={url}) failed, error: {err}." - ) from err + f"get metric func(url={url}) failed, error: {err}.") from err return name, getattr(sys.modules[__name__], str.lower(name) + "_func") diff --git a/core/testcasecontroller/testcase/testcase.py b/core/testcasecontroller/testcase/testcase.py index 11622aef..7775316d 100644 --- a/core/testcasecontroller/testcase/testcase.py +++ b/core/testcasecontroller/testcase/testcase.py @@ -66,7 +66,6 @@ def run(self, workspace): test_env_config = {} # pylint: disable=C0103 for k, v in self.test_env.__dict__.items(): - print(k,v) test_env_config[k] = v self.output_dir = self._get_output_dir(workspace) diff --git a/core/testenvmanager/dataset/dataset.py b/core/testenvmanager/dataset/dataset.py index e07f5601..16bd038f 100644 --- a/core/testenvmanager/dataset/dataset.py +++ b/core/testenvmanager/dataset/dataset.py @@ -55,15 +55,11 @@ def _parse_config(self, config): @classmethod def _check_dataset_url(cls, url): if not utils.is_local_file(url) and not os.path.isabs(url): - raise ValueError( - f"dataset file({url}) is not a local file and not a absolute path." - ) + raise ValueError(f"dataset file({url}) is not a local file and not a absolute path.") file_format = utils.get_file_format(url) if file_format not in [v.value for v in DatasetFormat.__members__.values()]: - raise ValueError( - f"dataset file({url})'s format({file_format}) is not supported." - ) + raise ValueError(f"dataset file({url})'s format({file_format}) is not supported.") @classmethod def _process_txt_index_file(cls, file_url): @@ -83,16 +79,15 @@ def _process_txt_index_file(cls, file_url): tmp_file = os.path.join(tempfile.mkdtemp(), "index.txt") with open(tmp_file, "w", encoding="utf-8") as file: for line in lines: - # copy all the files in the line + #copy all the files in the line line = line.strip() words = line.split(" ") length = len(words) - words[-1] = words[-1] + "\n" + words[-1] = words[-1] + '\n' for i in range(length): file.writelines( - f"{os.path.abspath(os.path.join(root, words[i]))}" - ) - if i < length - 1: + f"{os.path.abspath(os.path.join(root, words[i]))}") + if i < length-1: file.writelines(" ") new_file = tmp_file @@ -121,16 +116,8 @@ def process_dataset(self): self.test_url = self._process_index_file(self.test_url) # pylint: disable=too-many-arguments - def split_dataset( - self, - dataset_url, - dataset_format, - ratio, - method="default", - dataset_types=None, - output_dir=None, - times=1, - ): + def split_dataset(self, dataset_url, dataset_format, ratio, method="default", + dataset_types=None, output_dir=None, times=1): """ split dataset: step1: divide all data N(N = times) times to generate N pieces of data. @@ -165,48 +152,30 @@ def split_dataset( """ if method == "default": - return self._splitting_more_times( - dataset_url, - dataset_format, - ratio, - data_types=dataset_types, - output_dir=output_dir, - times=times, - ) + return self._splitting_more_times(dataset_url, dataset_format, ratio, + data_types=dataset_types, + output_dir=output_dir, + times=times) # add new splitting method for semantic segmantation if method == "city_splitting": - return self._city_splitting( - dataset_url, - dataset_format, - ratio, - data_types=dataset_types, - output_dir=output_dir, - times=times, - ) + return self._city_splitting(dataset_url, dataset_format, ratio, + data_types=dataset_types, + output_dir=output_dir, + times=times) if method == "fwt_splitting": - return self._fwt_splitting( - dataset_url, - dataset_format, - ratio, - data_types=dataset_types, - output_dir=output_dir, - times=times, - ) + return self._fwt_splitting(dataset_url, dataset_format, ratio, + data_types=dataset_types, + output_dir=output_dir, + times=times) if method == "hard-example_splitting": - return self._hard_example_splitting( - dataset_url, - dataset_format, - ratio, - data_types=dataset_types, - output_dir=output_dir, - times=times, - ) - - raise ValueError( - f"dataset splitting method({method}) is not supported," - f"currently, method supports 'default'." - ) + return self._hard_example_splitting(dataset_url, dataset_format, ratio, + data_types=dataset_types, + output_dir=output_dir, + times=times) + + raise ValueError(f"dataset splitting method({method}) is not supported," + f"currently, method supports 'default'.") @classmethod def _get_file_url(cls, output_dir, dataset_type, dataset_id, file_format): @@ -241,9 +210,8 @@ def _get_dataset_file(self, data, output_dir, dataset_type, index, dataset_forma return data_file - def _splitting_more_times( - self, data_file, data_format, ratio, data_types=None, output_dir=None, times=1 - ): + def _splitting_more_times(self, data_file, data_format, ratio, + data_types=None, output_dir=None, times=1): if not data_types: data_types = ("train", "eval") @@ -259,38 +227,24 @@ def _splitting_more_times( index = 1 while index <= times: if index == times: - new_dataset = all_data[step * (index - 1) :] + new_dataset = all_data[step * (index - 1):] else: - new_dataset = all_data[step * (index - 1) : step * index] + new_dataset = all_data[step * (index - 1):step * index] new_num = len(new_dataset) - data_files.append( - ( - self._get_dataset_file( - new_dataset[: int(new_num * ratio)], - output_dir, - data_types[0], - index, - data_format, - ), - self._get_dataset_file( - new_dataset[int(new_num * ratio) :], - output_dir, - data_types[1], - index, - data_format, - ), - ) - ) + data_files.append(( + self._get_dataset_file(new_dataset[:int(new_num * ratio)], output_dir, + data_types[0], index, data_format), + self._get_dataset_file(new_dataset[int(new_num * ratio):], output_dir, + data_types[1], index, data_format))) index += 1 return data_files - def _fwt_splitting( - self, data_file, data_format, ratio, data_types=None, output_dir=None, times=1 - ): + def _fwt_splitting(self, data_file, data_format, ratio, + data_types=None, output_dir=None, times=1): if not data_types: data_types = ("train", "eval") @@ -303,52 +257,33 @@ def _fwt_splitting( all_num = len(all_data) step = int(all_num / times) - data_files.append( - ( - self._get_dataset_file( - all_data[:1], output_dir, data_types[0], 0, data_format - ), - self._get_dataset_file( - all_data[:1], output_dir, data_types[1], 0, data_format - ), - ) - ) + data_files.append(( + self._get_dataset_file(all_data[:1], output_dir, + data_types[0], 0, data_format), + self._get_dataset_file(all_data[:1], output_dir, + data_types[1], 0, data_format))) index = 1 while index <= times: if index == times: - new_dataset = all_data[step * (index - 1) :] + new_dataset = all_data[step * (index - 1):] else: - new_dataset = all_data[step * (index - 1) : step * index] + new_dataset = all_data[step * (index - 1):step * index] new_num = len(new_dataset) - data_files.append( - ( - self._get_dataset_file( - new_dataset[: int(new_num * ratio)], - output_dir, - data_types[0], - index, - data_format, - ), - self._get_dataset_file( - new_dataset[int(new_num * ratio) :], - output_dir, - data_types[1], - index, - data_format, - ), - ) - ) + data_files.append(( + self._get_dataset_file(new_dataset[:int(new_num * ratio)], output_dir, + data_types[0], index, data_format), + self._get_dataset_file(new_dataset[int(new_num * ratio):], output_dir, + data_types[1], index, data_format))) index += 1 return data_files # add new splitting method for semantic segmentation - def _city_splitting( - self, data_file, data_format, ratio, data_types=None, output_dir=None, times=1 - ): + def _city_splitting(self, data_file, data_format, ratio, + data_types=None, output_dir=None, times=1): if not data_types: data_types = ("train", "eval") @@ -361,67 +296,38 @@ def _city_splitting( index0 = 0 for i, data in enumerate(all_data): - if "synthia_sim" in data: + if 'synthia_sim' in data: continue index0 = i break new_dataset = all_data[:index0] - data_files.append( - ( - self._get_dataset_file( - new_dataset[: int(len(new_dataset) * ratio)], - output_dir, - data_types[0], - 1, - data_format, - ), - self._get_dataset_file( - new_dataset[int(len(new_dataset) * ratio) :], - output_dir, - data_types[1], - 1, - data_format, - ), - ) - ) + data_files.append(( + self._get_dataset_file(new_dataset[:int(len(new_dataset) * ratio)], output_dir, + data_types[0], 1, data_format), + self._get_dataset_file(new_dataset[int(len(new_dataset) * ratio):], output_dir, + data_types[1], 1, data_format))) times = times - 1 - step = int((len(all_data) - index0) / times) + step = int((len(all_data)-index0) / times) index = 1 while index <= times: if index == times: - new_dataset = all_data[index0 + step * (index - 1) :] + new_dataset = all_data[index0 + step * (index - 1):] else: - new_dataset = all_data[ - index0 + step * (index - 1) : index0 + step * index - ] - - data_files.append( - ( - self._get_dataset_file( - new_dataset[: int(len(new_dataset) * ratio)], - output_dir, - data_types[0], - index + 1, - data_format, - ), - self._get_dataset_file( - new_dataset[int(len(new_dataset) * ratio) :], - output_dir, - data_types[1], - index + 1, - data_format, - ), - ) - ) + new_dataset = all_data[index0 + step * (index - 1):index0 + step * index] + + data_files.append(( + self._get_dataset_file(new_dataset[:int(len(new_dataset) * ratio)], output_dir, + data_types[0], index+1, data_format), + self._get_dataset_file(new_dataset[int(len(new_dataset) * ratio):], output_dir, + data_types[1], index+1, data_format))) index += 1 return data_files - def _hard_example_splitting( - self, data_file, data_format, ratio, data_types=None, output_dir=None, times=1 - ): + def _hard_example_splitting(self, data_file, data_format, ratio, + data_types=None, output_dir=None, times=1): if not data_types: data_types = ("train", "eval") @@ -433,65 +339,33 @@ def _hard_example_splitting( data_files = [] all_num = len(all_data) - step = int(all_num / (times * 2)) - data_files.append( - ( - self._get_dataset_file( - all_data[: int((all_num * ratio) / 2)], - output_dir, - data_types[0], - 0, - data_format, - ), - self._get_dataset_file( - all_data[int((all_num * ratio) / 2) : int(all_num / 2)], - output_dir, - data_types[1], - 0, - data_format, - ), - ) - ) + step = int(all_num / (times*2)) + data_files.append(( + self._get_dataset_file(all_data[:int((all_num * ratio)/2)], output_dir, + data_types[0], 0, data_format), + self._get_dataset_file(all_data[int((all_num * ratio)/2):int(all_num/2)], output_dir, + data_types[1], 0, data_format))) index = 1 while index <= times: if index == times: - new_dataset = all_data[int(all_num / 2) + step * (index - 1) :] + new_dataset = all_data[int(all_num/2)+step*(index-1):] else: - new_dataset = all_data[ - int(all_num / 2) - + step * (index - 1) : int(all_num / 2) - + step * index - ] + new_dataset = all_data[int(all_num/2)+step*(index-1): int(all_num/2)+step*index] new_num = len(new_dataset) - data_files.append( - ( - self._get_dataset_file( - new_dataset[: int(new_num * ratio)], - output_dir, - data_types[0], - index, - data_format, - ), - self._get_dataset_file( - new_dataset[int(new_num * ratio) :], - output_dir, - data_types[1], - index, - data_format, - ), - ) - ) + data_files.append(( + self._get_dataset_file(new_dataset[:int(new_num * ratio)], output_dir, + data_types[0], index, data_format), + self._get_dataset_file(new_dataset[int(new_num * ratio):], output_dir, + data_types[1], index, data_format))) index += 1 return data_files @classmethod - def load_data( - cls, file: str, data_type: str, label=None, use_raw=False, feature_process=None - ): + def load_data(cls, file: str, data_type: str, label=None, use_raw=False, feature_process=None): """ load data @@ -523,7 +397,7 @@ def load_data( if data_format == DatasetFormat.TXT.value: data = TxtDataParse(data_type=data_type, func=feature_process) - # print(file) + #print(file) data.parse(file, use_raw=use_raw) if data_format == DatasetFormat.JSON.value: diff --git a/core/testenvmanager/dataset/utils.py b/core/testenvmanager/dataset/utils.py deleted file mode 100644 index 1349ad07..00000000 --- a/core/testenvmanager/dataset/utils.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright 2022 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" Dataset utils to read data from file and partition data """ -# pylint: disable=W1203 -import random -import numpy as np -from sedna.datasources import BaseDataSource -from core.common.log import LOGGER - - -def read_data_from_file_to_npy(files: BaseDataSource): - """ - read data from file to numpy array - - Parameters - --------- - files: list - the address url of data file. - - Returns - ------- - list - data in numpy array. - - """ - x_train = [] - y_train = [] - for i, file in enumerate(files.x): - x_data = np.load(file) - y_data = np.full((x_data.shape[0],), (files.y[i]).astype(np.int32)) - x_train.append(x_data) - y_train.append(y_data) - x_train = np.concatenate(x_train, axis=0) - y_train = np.concatenate(y_train, axis=0) - return x_train, y_train - - -def partition_data(datasets, client_number, data_partition="iid", non_iid_ratio=0.6): - """ - Partition data into clients. - - Parameters - ---------- - datasets: list - The list containing the data and labels (x_data, y_data). - client_number: int - The number of clients. - partition_methods: str - The partition method, either 'iid' or 'non-iid'. - - Returns - ------- - list - A list of data for each client in numpy array format. - """ - LOGGER.info(data_partition) - data = [] - if data_partition == "iid": - x_data = datasets[0] - y_data = datasets[1] - indices = np.arange(len(x_data)) - np.random.shuffle(indices) - for i in range(client_number): - start = i * len(x_data) // client_number - end = (i + 1) * len(x_data) // client_number - data.append([x_data[indices[start:end]], y_data[indices[start:end]]]) - elif data_partition == "non-iid": - class_num = len(np.unique(datasets[1])) - x_data = datasets[0] - y_data = datasets[1] - - for i in range(client_number): - sample_number = int(class_num * non_iid_ratio) - current_class = random.sample(range(class_num), sample_number) - LOGGER.info(f"for client{i} the class is {current_class}") - indices = np.where(y_data == current_class)[0] - data.append([x_data[indices], y_data[indices]]) - else: - raise ValueError("paritiion_methods must be 'iid' or 'non-iid'") - return data diff --git a/core/testenvmanager/testenv/testenv.py b/core/testenvmanager/testenv/testenv.py index d9916d1a..4a4fef4d 100644 --- a/core/testenvmanager/testenv/testenv.py +++ b/core/testenvmanager/testenv/testenv.py @@ -39,12 +39,10 @@ def __init__(self, config): "url": "", }, "threshold": 0.9, - "operator": ">", + "operator": ">" } self.metrics = [] self.incremental_rounds = 2 - self.round = 1 - self.client_number = 1 self.dataset = None self._parse_config(config) @@ -53,10 +51,8 @@ def _check_fields(self): raise ValueError(f"not found testenv metrics({self.metrics}).") if not isinstance(self.incremental_rounds, int) or self.incremental_rounds < 2: - raise ValueError( - f"testenv incremental_rounds(value={self.incremental_rounds})" - f" must be int type and not less than 2." - ) + raise ValueError(f"testenv incremental_rounds(value={self.incremental_rounds})" + f" must be int type and not less than 2.") def _parse_config(self, config): config_dict = config[str.lower(TestEnv.__name__)] @@ -71,7 +67,7 @@ def _parse_config(self, config): self._check_fields() def prepare(self): - """prepare env""" + """ prepare env""" try: self.dataset.process_dataset() except Exception as err: diff --git a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/FedCiMatch.py b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/FedCiMatch.py deleted file mode 100644 index cc7b1ffc..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/FedCiMatch.py +++ /dev/null @@ -1,418 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import copy -import logging -import tensorflow as tf -import keras -import numpy as np -from model import resnet10, resnet18 -from agumentation import * -from data_prepocessor import * - - -def get_one_hot(target, num_classes): - y = tf.one_hot(target, depth=num_classes) - if len(y.shape) == 3: - y = tf.squeeze(y, axis=1) - return y - - -class FedCiMatch: - - def __init__( - self, num_classes, batch_size, epochs, learning_rate, memory_size - ) -> None: - self.num_classes = num_classes - self.batch_size = batch_size - self.epochs = epochs - self.learning_rate = learning_rate - self.memory_size = memory_size - self.task_size = None - self.warm_up_round = 4 - self.accept_threshold = 0.95 - self.old_task_id = -1 - - self.classifier = None - self.feature_extractor = self.build_feature_extractor() - - self.fe_weights_length = 0 - self.labeled_train_loader = None - self.unlabeled_train_loader = None - self.labeled_train_set = None - self.unlabeled_train_set = None - dataset_name = "cifar100" - self.data_preprocessor = Dataset_Preprocessor( - dataset_name, Weak_Augment(dataset_name), RandAugment(dataset_name) - ) - self.last_classes = None - self.current_classes = None - self.learned_classes = [] - self.learned_classes_num = 0 - self.exemplar_set = [] - self.seen_classes = [] - self.best_old_model = None - print(f"self epoch is {self.epochs}") - - def build_feature_extractor(self): - feature_extractor = resnet18() - - feature_extractor.build(input_shape=(None, 32, 32, 3)) - feature_extractor.call(keras.Input(shape=(32, 32, 3))) - return feature_extractor - - def build_classifier(self): - if self.classifier != None: - new_classifier = keras.Sequential( - [ - keras.layers.Dense( - self.num_classes, kernel_initializer="lecun_normal" - ) - ] - ) - new_classifier.build( - input_shape=(None, self.feature_extractor.layers[-2].output_shape[-1]) - ) - new_weights = new_classifier.get_weights() - old_weights = self.classifier.get_weights() - # weight - new_weights[0][0 : old_weights[0].shape[0], 0 : old_weights[0].shape[1]] = ( - old_weights[0] - ) - # bias - new_weights[1][0 : old_weights[1].shape[0]] = old_weights[1] - new_classifier.set_weights(new_weights) - self.classifier = new_classifier - else: - logging.info( - f"input shape is {self.feature_extractor.layers[-2].output_shape[-1]}" - ) - self.classifier = keras.Sequential( - [ - keras.layers.Dense( - self.num_classes, kernel_initializer="lecun_normal" - ) - ] - ) - self.classifier.build( - input_shape=(None, self.feature_extractor.layers[-2].output_shape[-1]) - ) - - logging.info(f"finish ! initialize classifier {self.classifier.summary()}") - - def get_weights(self): - weights = [] - fe_weights = self.feature_extractor.get_weights() - self.fe_weights_length = len(fe_weights) - clf_weights = self.classifier.get_weights() - weights.extend(fe_weights) - weights.extend(clf_weights) - return weights - - def set_weights(self, weights): - fe_weights = weights[: self.fe_weights_length] - clf_weights = weights[self.fe_weights_length :] - self.feature_extractor.set_weights(fe_weights) - self.classifier.set_weights(clf_weights) - - def model_call(self, x, training=False): - x = self.feature_extractor(x, training=training) - x = self.classifier(x, training=training) - # x = tf.nn.softmax(x) - return x - - def before_train(self, task_id, round, train_data, task_size): - if self.task_size is None: - self.task_size = task_size - is_new_task = task_id != self.old_task_id - self.is_new_task = is_new_task - if is_new_task: - self.best_old_model = ( - (self.feature_extractor, self.classifier) - if self.classifier is not None - else None - ) - self.is_new_task = True - self.old_task_id = task_id - self.num_classes = self.task_size * (task_id + 1) - logging.info(f"num_classes: {self.num_classes}") - if self.current_classes is not None: - self.last_classes = self.current_classes - # self.build_classifier() - self.current_classes = np.unique(train_data["label_y"]).tolist() - logging.info(f"current_classes: {self.current_classes}") - - self.labeled_train_set = (train_data["label_x"], train_data["label_y"]) - self.unlabeled_train_set = ( - train_data["unlabel_x"], - train_data["unlabel_y"], - ) - logging.info( - f"self.labeled_train_set is None :{self.labeled_train_set is None}" - ) - logging.info( - f"self.unlabeled_train_set is None :{self.unlabeled_train_set is None}" - ) - self.labeled_train_loader, self.unlabeled_train_loader = self.get_train_loader() - - def get_data_size(self): - logging.info( - f"self.labeled_train_set is None :{self.labeled_train_set is None}" - ) - logging.info( - f"self.unlabeled_train_set is None :{self.unlabeled_train_set is None}" - ) - data_size = len(self.labeled_train_set[0]) + len(self.unlabeled_train_set[0]) - logging.info(f"data size: {data_size}") - return data_size - - def get_train_loader(self): - train_x = self.labeled_train_set[0] - train_y = self.labeled_train_set[1] - logging.info( - f"train_x shape: {train_x.shape} and train_y shape: {train_y.shape} and len of exemplar_set: {len(self.exemplar_set)}" - ) - if len(self.exemplar_set) != 0: - for exm_set in self.exemplar_set: - train_x = np.concatenate((train_x, exm_set[0]), axis=0) - label = np.array(exm_set[1]) - train_y = np.concatenate((train_y, label), axis=0) - logging.info( - f"train_x shape: {train_x.shape} and train_y shape: {train_y.shape}" - ) - - logging.info( - f"train_x shape: {train_x.shape} and train_y shape: {train_y.shape}" - ) - label_data_loader = self.data_preprocessor.preprocess_labeled_dataset( - train_x, train_y, self.batch_size - ) - unlabel_data_loader = None - if len(self.unlabeled_train_set[0]) > 0: - unlabel_data_loader = self.data_preprocessor.preprocess_unlabeled_dataset( - self.unlabeled_train_set[0], - self.unlabeled_train_set[1], - self.batch_size, - ) - logging.info( - f"unlabel_x shape: {self.unlabeled_train_set[0].shape} and unlabel_y shape: {self.unlabeled_train_set[1].shape}" - ) - return label_data_loader, unlabel_data_loader - - def build_exemplar(self): - if self.is_new_task and self.current_classes is not None: - self.last_classes = self.current_classes - self.learned_classes.extend(self.last_classes) - self.learned_classes_num += len(self.learned_classes) - m = int(self.memory_size / self.num_classes) - self.reduce_exemplar_set(m) - for cls in self.last_classes: - images = self.get_train_data(cls) - self.construct_exemplar_set(images, cls, m) - self.is_new_task = False - - def reduce_exemplar_set(self, m): - for i in range(len(self.exemplar_set)): - old_exemplar_data = self.exemplar_set[i][0][:m] - old_exemplar_label = self.exemplar_set[i][1][:m] - self.exemplar_set[i] = (old_exemplar_data, old_exemplar_label) - - def get_train_data(self, class_id): - images = [] - train_x = self.labeled_train_set[0] - train_y = self.labeled_train_set[1] - for i in range(len(train_x)): - if train_y[i] == class_id: - images.append(train_x[i]) - return images - - def construct_exemplar_set(self, images, class_id, m): - exemplar_data = [] - exemplar_label = [] - class_mean, fe_ouput = self.compute_exemplar_mean(images) - diff = tf.abs(fe_ouput - class_mean) - distance = [float(tf.reduce_sum(dis).numpy()) for dis in diff] - - sorted_index = np.argsort(distance).tolist() - if len(sorted_index) > m: - sorted_index = sorted_index[:m] - exemplar_data = [images[i] for i in sorted_index] - exemplar_label = [class_id] * len(exemplar_data) - self.exemplar_set.append((exemplar_data, exemplar_label)) - - - def compute_exemplar_mean(self, images): - images_data = ( - tf.data.Dataset.from_tensor_slices(images) - .batch(self.batch_size) - .map(lambda x: tf.cast(x, dtype=tf.float32) / 255.0) - ) - fe_output = self.feature_extractor.predict(images_data) - print("fe_output shape:", fe_output.shape) - class_mean = tf.reduce_mean(fe_output, axis=0) - return class_mean, fe_output - - def train(self, round): - # optimizer = keras.optimizers.SGD( - # learning_rate=self.learning_rate, momentum=0.9, weight_decay=0.0001 - # ) - optimizer = keras.optimizers.Adam( - learning_rate=self.learning_rate, weight_decay=0.0001 - ) - q = [] - logging.info(f"is new task: {self.is_new_task}") - if self.is_new_task: - self.build_classifier() - all_params = [] - all_params.extend(self.feature_extractor.trainable_variables) - all_params.extend(self.classifier.trainable_variables) - - for epoch in range(self.epochs): - # following code is for unsupervised learning - # for labeled_data, unlabeled_data in zip( - # self.labeled_train_loader, self.unlabeled_train_loader - # ): - for step, (labeled_x, labeled_y) in enumerate(self.labeled_train_loader): - with tf.GradientTape() as tape: - input = self.feature_extractor(inputs=labeled_x, training=True) - y_pred = self.classifier(inputs=input, training=True) - label_pred = tf.argmax(y_pred, axis=1) - label_pred = tf.cast(label_pred, dtype=tf.int32) - label_pred = tf.reshape(label_pred, labeled_y.shape) - correct = tf.reduce_sum( - tf.cast(tf.equal(label_pred, labeled_y), dtype=tf.int32) - ) - CE_loss = self.supervised_loss(labeled_x, labeled_y) - KD_loss = self.distil_loss(labeled_x, labeled_y) - supervised_loss = CE_loss - - # following code is for unsupervised learning - # if epoch > self.warm_up_round: - # unsupervised_loss = self.unsupervised_loss( - # weak_unlabeled_x, strong_unlabeled_x, unlabeled_x - # ) - # logging.info(f"unsupervised loss: {unsupervised_loss}") - # loss = 0.5 * supervised_loss + 0.5 * unsupervised_loss - # else: - # loss = supervised_loss - loss = CE_loss + KD_loss - logging.info( - f"epoch {epoch} loss: {loss} correct {correct} and total {labeled_x.shape[0]} class is {np.unique(labeled_y)}" - ) - grads = tape.gradient(loss, all_params) - optimizer.apply_gradients(zip(grads, all_params)) - - def caculate_pre_update(self): - q = [] - for images, _ in self.labeled_train_loader: - x = self.feature_extractor(images, training=False) - x = self.classifier(x, training=False) - x = tf.nn.sigmoid(x) - q.append(x) - logging.info(f"q shape: {len(q)}") - return q - - def supervised_loss(self, x, y): - input = x - input = self.feature_extractor(input, training=True) - y_pred = self.classifier(input, training=True) - target = get_one_hot(y, self.num_classes) - loss = keras.losses.categorical_crossentropy(target, y_pred, from_logits=True) - logging.info(f"loss shape: {loss.shape}") - loss = tf.reduce_mean(loss) - logging.info(f"CE loss: {loss}") - - return loss - - def distil_loss(self, x, y): - KD_loss = 0 - - if len(self.learned_classes) > 0 and self.best_old_model is not None: - g = self.feature_extractor(x, training=True) - g = self.classifier(g, training=True) - og = self.best_old_model[0](x, training=False) - og = self.best_old_model[1](og, training=False) - sigmoid_og = tf.nn.sigmoid(og) - sigmoid_g = tf.nn.sigmoid(g) - BCELoss = keras.losses.BinaryCrossentropy() - loss = [] - for y in self.learned_classes: - if y not in self.current_classes: - loss.append(BCELoss(sigmoid_og[:, y], sigmoid_g[:, y])) - KD_loss = tf.reduce_sum(loss) - logging.info(f"KD_loss: {KD_loss}") - return KD_loss - - def unsupervised_loss(self, weak_x, strong_x, x): - prob_on_wux = tf.nn.softmax( - self.classifier( - self.feature_extractor(weak_x, training=True), training=True - ) - ) - pseudo_mask = tf.cast( - tf.reduce_max(prob_on_wux, axis=1) > self.accept_threshold, tf.float32 - ) - pse_uy = tf.one_hot( - tf.argmax(prob_on_wux, axis=1), depth=self.num_classes - ).numpy() - prob_on_sux = tf.nn.softmax( - self.classifier( - self.feature_extractor(strong_x, training=True), training=True - ) - ) - loss = keras.losses.categorical_crossentropy(pse_uy, prob_on_sux) - loss = tf.reduce_mean(loss * pseudo_mask) - return loss - - def predict(self, x): - mean = np.array((0.5071, 0.4867, 0.4408), np.float32).reshape(1, 1, -1) - std = np.array((0.2675, 0.2565, 0.2761), np.float32).reshape(1, 1, -1) - x = (tf.cast(x, dtype=tf.float32) / 255.0 - mean) / std - pred = self.classifier(self.feature_extractor(x, training=False)) - prob = tf.nn.softmax(pred, axis=1) - pred = tf.argmax(prob, axis=1) - pred = tf.cast(pred, dtype=tf.int32) - return pred - - def icarl_predict(self, x): - mean = np.array((0.5071, 0.4867, 0.4408), np.float32).reshape(1, 1, -1) - std = np.array((0.2675, 0.2565, 0.2761), np.float32).reshape(1, 1, -1) - x = (tf.cast(x, dtype=tf.float32) / 255.0 - mean) / std - bs = x.shape[0] - print(x.shape) - exemplar_mean = [] - for exemplar in self.exemplar_set: - # features = [] - ex, _ = exemplar - ex = (tf.cast(ex, dtype=tf.float32) / 255.0 - mean) / std - feature = self.feature_extractor(ex, training=False) - feature = feature / tf.norm(feature) - mu_y = tf.reduce_mean(feature, axis=0) - mu_y = mu_y / tf.norm(mu_y) - exemplar_mean.append(mu_y) - means = tf.stack(exemplar_mean) # shape: (num_classes, feature_shape) - means = tf.stack([means] * bs) # shape: (bs, num_classes, feature_shape) - means = tf.transpose( - means, perm=[0, 2, 1] - ) # shape: (bs, feature_shape, num_classes) - feature = self.feature_extractor( - x, training=False - ) # shape (bs , feature_shape) - feature = feature / tf.norm(feature) - feature = tf.expand_dims(feature, axis=2) - feature = tf.tile(feature, [1, 1, self.num_classes]) - dists = tf.pow((feature - means), 2) - dists = tf.reduce_sum(dists, axis=1) # shape: (bs, num_classes) - preds = tf.argmin(dists, axis=1) # shape: (bs) - logging.info(f"preds : {preds}") - return preds diff --git a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/aggregation.py b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/aggregation.py deleted file mode 100644 index 46c57f19..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/aggregation.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import abc - -import numpy as np -from sedna.algorithms.aggregation.aggregation import BaseAggregation -from sedna.common.class_factory import ClassType, ClassFactory - -@ClassFactory.register(ClassType.FL_AGG, "FedAvg") -class FedAvg(BaseAggregation, abc.ABC): - def __init__(self): - super(FedAvg, self).__init__() - - def aggregate(self, clients): - """ - Calculate the average weight according to the number of samples - - Parameters - ---------- - clients: List - All clients in federated learning job - - Returns - ------- - update_weights : Array-like - final weights use to update model layer - """ - - - print("aggregation....") - if not len(clients): - return self.weights - self.total_size = sum([c.num_samples for c in clients]) - old_weight = [np.zeros(np.array(c).shape) for c in - next(iter(clients)).weights] - updates = [] - for inx, row in enumerate(old_weight): - for c in clients: - row += (np.array(c.weights[inx]) * c.num_samples - / self.total_size) - updates.append(row.tolist()) - - print("finish aggregation....") - return [np.array(layer) for layer in updates] diff --git a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/agumentation.py b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/agumentation.py deleted file mode 100644 index 89d1bef2..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/agumentation.py +++ /dev/null @@ -1,230 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import numpy as np -import random -import tensorflow as tf -from PIL import Image, ImageEnhance, ImageOps - - -""" -Reference: https://github.com/heartInsert/randaugment -""" - - -class Rand_Augment: - def __init__(self, Numbers=None, max_Magnitude=None): - self.transforms = [ - "autocontrast", - "equalize", - "rotate", - "solarize", - "color", - "posterize", - "contrast", - "brightness", - "sharpness", - "shearX", - "shearY", - "translateX", - "translateY", - ] - if Numbers is None: - self.Numbers = len(self.transforms) // 2 - else: - self.Numbers = Numbers - if max_Magnitude is None: - self.max_Magnitude = 10 - else: - self.max_Magnitude = max_Magnitude - fillcolor = 128 - self.ranges = { - # these Magnitude range , you must test it yourself , see what will happen after these operation , - # it is no need to obey the value in autoaugment.py - "shearX": np.linspace(0, 0.3, 10), - "shearY": np.linspace(0, 0.3, 10), - "translateX": np.linspace(0, 0.2, 10), - "translateY": np.linspace(0, 0.2, 10), - "rotate": np.linspace(0, 360, 10), - "color": np.linspace(0.0, 0.9, 10), - "posterize": np.round(np.linspace(8, 4, 10), 0).astype(int), - "solarize": np.linspace(256, 231, 10), - "contrast": np.linspace(0.0, 0.5, 10), - "sharpness": np.linspace(0.0, 0.9, 10), - "brightness": np.linspace(0.0, 0.3, 10), - "autocontrast": [0] * 10, - "equalize": [0] * 10, - "invert": [0] * 10, - } - self.func = { - "shearX": lambda img, magnitude: img.transform( - img.size, - Image.AFFINE, - (1, magnitude * random.choice([-1, 1]), 0, 0, 1, 0), - Image.BICUBIC, - fill=fillcolor, - ), - "shearY": lambda img, magnitude: img.transform( - img.size, - Image.AFFINE, - (1, 0, 0, magnitude * random.choice([-1, 1]), 1, 0), - Image.BICUBIC, - fill=fillcolor, - ), - "translateX": lambda img, magnitude: img.transform( - img.size, - Image.AFFINE, - (1, 0, magnitude * img.size[0] * random.choice([-1, 1]), 0, 1, 0), - fill=fillcolor, - ), - "translateY": lambda img, magnitude: img.transform( - img.size, - Image.AFFINE, - (1, 0, 0, 0, 1, magnitude * img.size[1] * random.choice([-1, 1])), - fill=fillcolor, - ), - "rotate": lambda img, magnitude: self.rotate_with_fill(img, magnitude), - # "rotate": lambda img, magnitude: img.rotate(magnitude * random.choice([-1, 1])), - "color": lambda img, magnitude: ImageEnhance.Color(img).enhance( - 1 + magnitude * random.choice([-1, 1]) - ), - "posterize": lambda img, magnitude: ImageOps.posterize(img, magnitude), - "solarize": lambda img, magnitude: ImageOps.solarize(img, magnitude), - "contrast": lambda img, magnitude: ImageEnhance.Contrast(img).enhance( - 1 + magnitude * random.choice([-1, 1]) - ), - "sharpness": lambda img, magnitude: ImageEnhance.Sharpness(img).enhance( - 1 + magnitude * random.choice([-1, 1]) - ), - "brightness": lambda img, magnitude: ImageEnhance.Brightness(img).enhance( - 1 + magnitude * random.choice([-1, 1]) - ), - "autocontrast": lambda img, magnitude: ImageOps.autocontrast(img), - "equalize": lambda img, magnitude: img, - "invert": lambda img, magnitude: ImageOps.invert(img), - } - - def rand_augment(self): - """Generate a set of distortions. - Args: - N: Number of augmentation transformations to apply sequentially. N is len(transforms)/2 will be best - M: Max_Magnitude for all the transformations. should be <= self.max_Magnitude - """ - - M = np.random.randint(0, self.max_Magnitude, self.Numbers) - - sampled_ops = np.random.choice(self.transforms, self.Numbers) - return [(op, Magnitude) for (op, Magnitude) in zip(sampled_ops, M)] - - def __call__(self, image): - operations = self.rand_augment() - for op_name, M in operations: - operation = self.func[op_name] - mag = self.ranges[op_name][M] - image = operation(image, mag) - return image - - def rotate_with_fill(self, img, magnitude): - # I don't know why rotate must change to RGBA , it is copy from Autoaugment - pytorch - rot = img.convert("RGBA").rotate(magnitude) - return Image.composite( - rot, Image.new("RGBA", rot.size, (128,) * 4), rot - ).convert(img.mode) - - def test_single_operation(self, image, op_name, M=-1): - """ - :param image: image - :param op_name: operation name in self.transforms - :param M: -1 stands for the max Magnitude in there operation - :return: - """ - operation = self.func[op_name] - mag = self.ranges[op_name][M] - image = operation(image, mag) - return image - - -class Base_Augment: - def __init__(self, dataset_name: str) -> None: - self.dataset_name = dataset_name - - def __call__(self, images): - return images - - -class Weak_Augment(Base_Augment): - def __init__(self, dataset_name: str) -> None: - super().__init__(dataset_name) - self.augment_impl = self.augment_for_cifar - - def augment_mirror(self, x): - new_images = x.copy() - indices = np.arange(len(new_images)).tolist() - sampled = random.sample( - indices, int(round(0.5 * len(indices))) - ) # flip horizontally 50% - new_images[sampled] = np.fliplr(new_images[sampled]) - return new_images # random shift - - def augment_shift(self, x, w): - y = tf.pad(x, [[0] * 2, [w] * 2, [w] * 2, [0] * 2], mode="REFLECT") - return tf.image.random_crop(y, tf.shape(x)) - - def augment_for_cifar(self, images: np.ndarray): - return self.augment_shift(self.augment_mirror(images), 4) - - def __call__(self, images: np.ndarray): - return self.augment_impl(images) - - -class Strong_Augment(Base_Augment): - def __init__(self, dataset_name: str) -> None: - super().__init__(dataset_name) - - def augment_mirror(self, x): - new_images = x.copy() - indices = np.arange(len(new_images)).tolist() - sampled = random.sample( - indices, int(round(0.5 * len(indices))) - ) # flip horizontally 50% - new_images[sampled] = np.fliplr(new_images[sampled]) - return new_images # random shift - - def augment_shift_mnist(self, x, w): - y = tf.pad(x, [[0] * 2, [w] * 2, [w] * 2], mode="REFLECT") - return tf.image.random_crop(y, tf.shape(x)) - - def __call__(self, images: np.ndarray): - return self.augment_shift_mnist(self.augment_mirror(images), 4) - - -class RandAugment(Base_Augment): - def __init__(self, dataset_name: str) -> None: - super().__init__(dataset_name) - self.rand_augment = Rand_Augment() - self.input_shape = (32, 32, 3) - - def __call__(self, images): - print("images:", images.shape) - - return np.array( - [ - np.array( - self.rand_augment( - Image.fromarray(np.reshape(img, self.input_shape)) - ) - ) - for img in images - ] - ) diff --git a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/algorithm.yaml b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/algorithm.yaml deleted file mode 100644 index 0701a660..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/algorithm.yaml +++ /dev/null @@ -1,28 +0,0 @@ -algorithm: - paradigm_type: "federatedclassincrementallearning" - fl_data_setting: - train_ratio: 1.0 - splitting_method: "default" - label_data_ratio: 1.0 - data_partition: "iid" - non_iid_ratio: "0.6" - initial_model_url: "/home/wyd/ianvs/project/init_model/cnn.pb" - - modules: - - type: "basemodel" - name: "FediCarl-Client" - url: "./examples/cifar100/fci_ssl/fed_ci_match/algorithm/basemodel.py" - hyperparameters: - - batch_size: - values: - - 128 - - learning_rate: - values: - - 0.001 - - epochs: - values: - - 1 - - type: "aggregation" - name: "FedAvg" - url: "./examples/cifar100/fci_ssl/fed_ci_match/algorithm/aggregation.py" - diff --git a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/basemodel.py b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/basemodel.py deleted file mode 100644 index e351ddf0..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/basemodel.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import sys - -sys.path.append(".") -sys.path.append("..") -import os -import numpy as np -import keras -import tensorflow as tf -from sedna.common.class_factory import ClassType, ClassFactory -from model import resnet10 -from FedCiMatch import FedCiMatch -import logging - -os.environ["BACKEND_TYPE"] = "KERAS" -__all__ = ["BaseModel"] -logging.getLogger().setLevel(logging.INFO) - - -@ClassFactory.register(ClassType.GENERAL, alias="FediCarl-Client") -class BaseModel: - def __init__(self, **kwargs) -> None: - self.kwargs = kwargs - self.learning_rate = kwargs.get("learning_rate", 0.001) - self.epochs = kwargs.get("epochs", 1) - self.batch_size = kwargs.get("batch_size", 32) - self.task_size = kwargs.get("task_size", 2) - self.memory_size = kwargs.get("memory_size", 2000) - self.num_classes = 50 # the number of class for the first task - self.FedCiMatch = FedCiMatch( - self.num_classes, - self.batch_size, - self.epochs, - self.learning_rate, - self.memory_size, - ) - self.class_learned = 0 - - def get_weights(self): - print("get weights") - return self.FedCiMatch.get_weights() - - def set_weights(self, weights): - print("set weights") - self.FedCiMatch.set_weights(weights) - - def train(self, train_data, val_data, **kwargs): - task_id = kwargs.get("task_id", 0) - round = kwargs.get("round", 1) - task_size = kwargs.get("task_size", self.task_size) - logging.info(f"in train: {round} task_id: {task_id}") - self.class_learned += self.task_size - self.FedCiMatch.before_train(task_id, round, train_data, task_size) - self.FedCiMatch.train(round) - logging.info(f"update example memory") - self.FedCiMatch.build_exemplar() - return {"num_samples": self.FedCiMatch.get_data_size(), "task_id": task_id} - - def predict(self, data_files, **kwargs): - result = {} - for data in data_files: - x = np.load(data) - logging.info(f"predicting {x.shape}") - res = self.FedCiMatch.predict(x) - result[data] = res.numpy() - print("finish predict") - return result diff --git a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/data_prepocessor.py b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/data_prepocessor.py deleted file mode 100644 index 004143ac..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/data_prepocessor.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tensorflow as tf -import numpy as np -from agumentation import Base_Augment - - -class Dataset_Preprocessor: - def __init__( - self, - dataset_name: str, - weak_augment_helper: Base_Augment, - strong_augment_helper: Base_Augment, - ) -> None: - self.weak_augment_helper = weak_augment_helper - self.strong_augment_helper = strong_augment_helper - self.mean = 0.0 - self.std = 1.0 - - if dataset_name == "cifar100": - self.mean = np.array((0.5071, 0.4867, 0.4408), np.float32).reshape(1, 1, -1) - self.std = np.array((0.2675, 0.2565, 0.2761), np.float32).reshape(1, 1, -1) - print(f"mean: {self.mean}, std: {self.std}") - - def preprocess_labeled_dataset(self, x, y, batch_size): - # wx = self.weak_augment_helper(x) - return ( - tf.data.Dataset.from_tensor_slices((x, y)) - .shuffle(100000) - .map( - lambda x, y: ( - (tf.cast(x, dtype=tf.float32) / 255.0 - self.mean) / self.std, - tf.cast(y, dtype=tf.int32), - ) - ) - .batch(batch_size) - ) - - def preprocess_unlabeled_dataset(self, ux, uy, batch_size): - # unlabeled_train_db = tf.data.Dataset.from_tensor_slices((ux, ux, ux, uy)) - - wux = self.weak_augment_helper(ux) - sux = self.strong_augment_helper(ux) - return ( - tf.data.Dataset.from_tensor_slices((ux, wux, sux, uy)) - .shuffle(1000) - .map( - lambda ux, wux, sux, uy: ( - (tf.cast(ux, dtype=tf.float32) / 255.0 - self.mean) / self.std, - (tf.cast(wux, dtype=tf.float32) / 255.0 - self.mean) / self.std, - (tf.cast(sux, dtype=tf.float32) / 255.0 - self.mean) / self.std, - tf.cast(uy, dtype=tf.int32), - ) - ) - .batch(batch_size) - ) diff --git a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/model.py b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/model.py deleted file mode 100644 index 7ffeb2dd..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/model.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tensorflow as tf -import keras - - -# Input--conv2D--BN--ReLU--conv2D--BN--ReLU--Output -# \ / -# ------------------------------ -class BasicBlock(keras.layers.Layer): - def __init__(self, filter_num, stride=1): - super(BasicBlock, self).__init__() - - self.conv1 = keras.layers.Conv2D( - filter_num, (3, 3), strides=stride, padding="same" - ) - self.bn1 = keras.layers.BatchNormalization() - self.relu = keras.layers.Activation("relu") - - self.conv2 = keras.layers.Conv2D(filter_num, (3, 3), strides=1, padding="same") - self.bn2 = keras.layers.BatchNormalization() - - if stride != 1: - self.downsample = keras.models.Sequential() - self.downsample.add(keras.layers.Conv2D(filter_num, (1, 1), strides=stride)) - else: - self.downsample = lambda x: x - - def call(self, inputs, training=None): - # [b, h, w, c] - out = self.conv1(inputs) - out = self.bn1(out, training=training) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out, training=training) - - identity = self.downsample(inputs) - - output = keras.layers.add([out, identity]) - output = tf.nn.relu(output) - - return output - - -class ResNet(keras.Model): - def __init__(self, layer_dims): # [2, 2, 2, 2] - super(ResNet, self).__init__() - self.layer_dims = layer_dims - - self.stem = keras.models.Sequential( - [ - keras.layers.Conv2D(64, (3, 3), strides=(1, 1)), - keras.layers.BatchNormalization(), - keras.layers.Activation("relu"), - keras.layers.MaxPool2D( - pool_size=(2, 2), strides=(1, 1), padding="same" - ), - ] - ) - - self.layer1 = self.build_resblock(64, layer_dims[0]) - self.layer2 = self.build_resblock(128, layer_dims[1], stride=2) - self.layer3 = self.build_resblock(256, layer_dims[2], stride=2) - self.layer4 = self.build_resblock(512, layer_dims[3], stride=2) - - # output: [b, 512, h, w], - self.avgpool = keras.layers.GlobalAveragePooling2D() - - def call(self, inputs, training=None): - x = self.stem(inputs, training=training) - - x = self.layer1(x, training=training) - x = self.layer2(x, training=training) - x = self.layer3(x, training=training) - x = self.layer4(x, training=training) - x = self.avgpool(x) - return x - - def build_resblock(self, filter_num, blocks, stride=1): - res_blocks = keras.models.Sequential() - # may down sample - res_blocks.add(BasicBlock(filter_num, stride)) - for _ in range(1, blocks): - res_blocks.add(BasicBlock(filter_num, stride=1)) - return res_blocks - - def get_config(self): - return { - "layer_dims": self.layer_dims, - } - - @classmethod - def from_config(cls, config): - return cls(**config) - - -class LeNet(keras.Model): - def __init__(self, input_shape, channels=3, num_classes=10): - super(LeNet, self).__init__() - self.input_shape = input_shape - self.channels = channels - self.num_classes = num_classes - - self.conv1 = keras.layers.Conv2D( - 6, - kernel_size=5, - strides=1, - activation="relu", - input_shape=(input_shape, input_shape, channels), - ) - self.pool1 = keras.layers.MaxPool2D(pool_size=2, strides=2) - self.conv2 = keras.layers.Conv2D( - 16, kernel_size=5, strides=1, activation="relu" - ) - self.pool2 = keras.layers.MaxPool2D(pool_size=2, strides=2) - self.flatten = keras.layers.Flatten() - - self.fc1 = keras.layers.Dense(120, activation="relu") - self.fc2 = keras.layers.Dense(84, activation="relu") - self.fc3 = keras.layers.Dense(num_classes, activation="softmax") - - def call(self, inputs, training=None): - x = self.conv1(inputs) - x = self.pool1(x) - x = self.conv2(x) - x = self.pool2(x) - x = self.flatten(x) - x = self.fc1(x) - x = self.fc2(x) - x = self.fc3(x) - return x - - def get_config(self): - return { - "input_shape": self.input_shape, - "channels": self.channels, - "num_classes": self.num_classes, - } - - @classmethod - def from_config(cls, config): - return cls(**config) - - -def lenet5(input_shape, num_classes: int): - return LeNet(input_shape, 3, num_classes) - - -def resnet10(): - return ResNet([1, 1, 1, 1]) - - -def resnet18(): - return ResNet([2, 2, 2, 2]) - - -def resnet34(): - return ResNet([3, 4, 6, 3]) diff --git a/examples/cifar100/fci_ssl/fed_ci_match/benchmarkingjob.yaml b/examples/cifar100/fci_ssl/fed_ci_match/benchmarkingjob.yaml deleted file mode 100644 index 5eb8a6de..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match/benchmarkingjob.yaml +++ /dev/null @@ -1,71 +0,0 @@ -benchmarkingjob: - # job name of bechmarking; string type; - name: "benchmarkingjob" - # the url address of job workspace that will reserve the output of tests; string type; - workspace: "/home/wyd/ianvs/federated_class_incremental_learning/workspace" - - # the url address of test environment configuration file; string type; - # the file format supports yaml/yml; - testenv: "./examples/cifar100/fci_ssl/fed_ci_match/testenv/testenv.yaml" - - # the configuration of test object - test_object: - # test type; string type; - # currently the option of value is "algorithms",the others will be added in succession. - type: "algorithms" - # test algorithm configuration files; list type; - algorithms: - # algorithm name; string type; - - name: "FediCarl" - # the url address of test algorithm configuration file; string type; - # the file format supports yaml/yml - url: "./examples/cifar100/fci_ssl/fed_ci_match/algorithm/algorithm.yaml" - - # the configuration of ranking leaderboard - rank: - # rank leaderboard with metric of test case's evaluation and order ; list type; - # the sorting priority is based on the sequence of metrics in the list from front to back; - sort_by: [ { "task_avg_acc": "descend" } ] - - # visualization configuration - visualization: - # mode of visualization in the leaderboard; string type; - # There are quite a few possible dataitems in the leaderboard. Not all of them can be shown simultaneously on the screen. - # In the leaderboard, we provide the "selected_only" mode for the user to configure what is shown or is not shown. - mode: "selected_only" - # method of visualization for selected dataitems; string type; - # currently the options of value are as follows: - # 1> "print_table": print selected dataitems; - method: "print_table" - - # selected dataitem configuration - # The user can add his/her interested dataitems in terms of "paradigms", "modules", "hyperparameters" and "metrics", - # so that the selected columns will be shown. - selected_dataitem: - # currently the options of value are as follows: - # 1> "all": select all paradigms in the leaderboard; - # 2> paradigms in the leaderboard, e.g., "singletasklearning" - paradigms: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all modules in the leaderboard; - # 2> modules in the leaderboard, e.g., "basemodel" - modules: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all hyperparameters in the leaderboard; - # 2> hyperparameters in the leaderboard, e.g., "momentum" - hyperparameters: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all metrics in the leaderboard; - # 2> metrics in the leaderboard, e.g., "F1_SCORE" - metrics: [ "task_avg_acc", "forget_rate" ] - - # network of save selected and all dataitems in workspace `./rank` ; string type; - # currently the options of value are as follows: - # 1> "selected_and_all": save selected and all dataitems; - # 2> "selected_only": save selected dataitems; - save_mode: "selected_and_all" - - - - - diff --git a/examples/cifar100/fci_ssl/fed_ci_match/testenv/acc.py b/examples/cifar100/fci_ssl/fed_ci_match/testenv/acc.py deleted file mode 100644 index f55961f3..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match/testenv/acc.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tensorflow as tf -import numpy as np -from sedna.common.class_factory import ClassFactory, ClassType - -__all__ = ['acc'] - - -@ClassFactory.register(ClassType.GENERAL, alias='accuracy') -def accuracy(y_true, y_pred, **kwargs): - print(f"y_true: {y_true}") - y_pred_arr = [val for val in y_pred.values()] - y_true_arr = [] - for i in range(len(y_pred_arr)): - y_true_arr.append(np.full(y_pred_arr[i].shape, int(y_true[i]))) - y_pred = tf.cast(tf.convert_to_tensor(np.concatenate(y_pred_arr, axis=0)), tf.int64) - - y_true = tf.cast(tf.convert_to_tensor(np.concatenate(y_true_arr, axis=0)), tf.int64) - # print(y_true, y_pred) - total = tf.shape(y_true)[0] - correct = tf.reduce_sum(tf.cast(tf.equal(y_true, y_pred), tf.int32)) - print(f"correct:{correct}, total:{total}") - acc = float(int(correct) / total) - print(f"acc:{acc}") - return acc - diff --git a/examples/cifar100/fci_ssl/fed_ci_match/testenv/testenv.yaml b/examples/cifar100/fci_ssl/fed_ci_match/testenv/testenv.yaml deleted file mode 100644 index c27c9229..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match/testenv/testenv.yaml +++ /dev/null @@ -1,38 +0,0 @@ -testenv: - backend: "tensorflow" - dataset: - name: 'cifar100' - # the url address of train dataset index; string type; - train_url: "/home/wyd/ianvs/project/data/cifar100/cifar100_train.txt" - # the url address of test dataset index; string type; - test_url: "/home/wyd/ianvs/project/data/cifar100/cifar100_test.txt" - - - # network eval configuration of incremental learning; - model_eval: - # metric used for network evaluation - model_metric: - # metric name; string type; - name: "accuracy" - # the url address of python file - url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/fci_ssl/fed_ci_match/testenv/acc.py" - - # condition of triggering inference network to update - # threshold of the condition; types are float/int - threshold: 0.01 - # operator of the condition; string type; - # values are ">=", ">", "<=", "<" and "="; - operator: "<=" - - # metrics configuration for test case's evaluation; list type; - metrics: - # metric name; string type; - # - name: "accuracy" - # # the url address of python file - # url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/fci_ssl/fed_ci_match/testenv/acc.py" - - name: "forget_rate" - - name: "task_avg_acc" - # incremental rounds setting of incremental learning; int type; default value is 2; - incremental_rounds: 2 - round: 1 - client_number: 5 \ No newline at end of file diff --git a/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/FedCiMatch.py b/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/FedCiMatch.py deleted file mode 100644 index 77513380..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/FedCiMatch.py +++ /dev/null @@ -1,309 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import copy -import logging -import tensorflow as tf -import keras -import numpy as np -from model import resnet10 -from agumentation import * -from data_prepocessor import * - - -def get_one_hot(target, num_classes): - y = tf.one_hot(target, depth=num_classes) - if len(y.shape) == 3: - y = tf.squeeze(y, axis=1) - return y - - -class FedCiMatch: - - def __init__( - self, num_classes, batch_size, epochs, learning_rate, memory_size - ) -> None: - self.num_classes = num_classes - self.batch_size = batch_size - self.epochs = epochs - self.learning_rate = learning_rate - self.memory_size = memory_size - self.task_size = None - self.warm_up_round = 1 - self.accept_threshold = 0.85 - self.old_task_id = -1 - - self.classifier = None - self.feature_extractor = self._build_feature_extractor() - dataset_name = "cifar100" - self.data_preprocessor = Dataset_Preprocessor( - dataset_name, Weak_Augment(dataset_name), RandAugment(dataset_name) - ) - - self.observed_classes = [] - self.class_mapping = {} - self.class_per_round = [] - self.x_exemplars = [] - self.y_exemplars = [] - self.num_meta_round = 5 - self.beta = 0.1 - self.num_rounds = 100 - - print(f"self epoch is {self.epochs}") - - def _build_feature_extractor(self): - self.global_model = resnet10(is_combined=True) - self.global_model.build(input_shape=(None, 32, 32, 3)) - self.global_model.call(keras.Input(shape=(32, 32, 3))) - feature_extractor = resnet10(is_combined=True) - feature_extractor.build(input_shape=(None, 32, 32, 3)) - feature_extractor.call(keras.Input(shape=(32, 32, 3))) - return feature_extractor - - def _build_classifier(self): - logging.info(f"build classifier with classes {len(self.class_mapping)}") - if self.classifier != None: - new_classifier = keras.Sequential( - [ - keras.layers.Dense( - len(self.class_mapping), kernel_initializer="lecun_normal" - ) - ] - ) - new_classifier.build( - input_shape=(None, self.feature_extractor.layers[-2].output_shape[-1]) - ) - new_weights = new_classifier.get_weights() - old_weights = self.classifier.get_weights() - # weight - new_weights[0][0 : old_weights[0].shape[0], 0 : old_weights[0].shape[1]] = ( - old_weights[0] - ) - # bias - new_weights[1][0 : old_weights[1].shape[0]] = old_weights[1] - new_classifier.set_weights(new_weights) - self.classifier = new_classifier - else: - logging.info( - f"input shape is {self.feature_extractor.layers[-2].output_shape[-1]}" - ) - self.classifier = keras.Sequential( - [ - keras.layers.Dense( - len(self.class_mapping), kernel_initializer="lecun_normal" - ) - ] - ) - self.classifier.build( - input_shape=(None, self.feature_extractor.layers[-2].output_shape[-1]) - ) - - - def get_weights(self): - return self.feature_extractor.get_weights() - - def set_weights(self, weights): - self.feature_extractor.set_weights(weights) - self.global_model.set_weights(weights) - - def get_data_size(self): - data_size = len(self.labeled_train_set[0]) + len(self.unlabeled_train_set[0]) - logging.info(f"data size: {data_size}") - return data_size - - def model_call(self, x, training=False): - x = self.feature_extractor(x, training=training) - x = self.classifier(x, training=training) - return x - - def _build_class_mapping(self): - y_train = self.labeled_train_set[1] - y = np.unique(y_train) - logging.info(f'build class mapping, y is {y}') - for i in y: - if not i in self.class_mapping.keys(): - self.class_mapping[i] = len(self.class_mapping) - self.class_per_round.append([self.class_mapping[i] for i in y]) - logging.info(f'build class mapping, class mapping is {self.class_mapping} and class per round is {self.class_per_round}') - - def _mix_with_exemplar(self): - x_train, y_train = self.labeled_train_set - if len(self.x_exemplars) == 0: - return - x_train = np.concatenate([x_train, np.array(self.x_exemplars)], axis=0) - y_train = np.concatenate([y_train, np.array(self.y_exemplars)], axis=0) - self.labeled_train_set = (x_train, y_train) - - def get_train_loader(self): - label_train_loader = self.data_preprocessor.preprocess_labeled_dataset( - self.labeled_train_set[0], self.labeled_train_set[1], self.batch_size - ) - un_label_train_loader = None - if len(self.unlabeled_train_set[0]) > 0: - un_label_train_loader = self.data_preprocessor.preprocess_unlabeled_dataset( - self.unlabeled_train_set[0], self.unlabeled_train_set[1], self.batch_size - ) - return label_train_loader, un_label_train_loader - - def before_train(self, task_id, round, train_data, task_size): - if self.task_size is None: - self.task_size = task_size - self.labeled_train_set = (train_data["label_x"], train_data["label_y"]) - self.unlabeled_train_set = ( - train_data["unlabel_x"], - train_data["unlabel_y"], - ) - self._build_class_mapping() - self._build_classifier() - if task_id > 0: - self._mix_with_exemplar() - self.feature_extractor.initialize_alpha() - self.labeled_train_loader, self.unlabeled_train_loader = self.get_train_loader() - - def train(self, task_id, round): - optimizer = keras.optimizers.SGD(learning_rate=self.learning_rate, momentum=0.9, weight_decay=0.0001) - all_parameter = [] - all_parameter.extend(self.feature_extractor.trainable_variables) - all_parameter.extend(self.classifier.trainable_variables) - - for epoch in range(self.epochs): - for x, y in self.labeled_train_loader: - y = np.array([self.class_mapping[i] for i in y.numpy()]) - tasks = self._split_tasks(x, y) - base_model_weights = self.feature_extractor.get_weights() - meta_model_weights = [] - for task_x, task_y in tasks: - self.feature_extractor.set_weights(base_model_weights) - for _ in range(self.num_meta_round): - with tf.GradientTape() as tape: - base_loss = self._loss(task_x, task_y) - l2_loss = self._loss_l2(self.global_model) - loss = base_loss + l2_loss*0.1 - grads = tape.gradient(loss, all_parameter) - optimizer.apply_gradients(zip(grads, all_parameter)) - meta_model_weights.append(self.feature_extractor.get_weights()) - logging.info(f'Round{round} task{task_id} epoch{epoch} loss is {loss} ') - self._merge_models(round, base_model_weights, meta_model_weights) - - self.feature_extractor.merge_to_local_model() - self.store_exemplars(task_id) - - def evaluate(self): - total_num = 0 - total_correct = 0 - for x,y in self.labeled_train_loader: - logits = self.classifier(self.feature_extractor(x, training=False)) - prob = tf.nn.softmax(logits, axis=1) - pred = tf.argmax(prob, axis=1) - pred = tf.cast(pred, dtype=tf.int32) - pred = tf.reshape(pred, y.shape) - correct = tf.cast(tf.equal(pred, y), dtype=tf.int32) - correct = tf.reduce_sum(correct) - - total_num += x.shape[0] - total_correct += int(correct) - - acc = total_correct / total_num - del total_correct, total_num - return acc - - def _loss(self,x ,y): - feature = self.feature_extractor(x) - prediction = self.classifier(feature) - loss = keras.losses.categorical_crossentropy(tf.one_hot(y, len(self.class_mapping)), prediction, from_logits=True) - return tf.reduce_mean(loss) - - def _loss_l2(self, global_model): - return 0.0 - - def unsupervised_loss(self, sux, wux): - return 0.0 - - def _merge_models(self, round, base_model_weights, meta_model_weights): - eta = np.exp(-self.beta * (round + 1 ) / self.num_rounds) - merged_meta_parameters = [ - np.average( - [meta_model_weights[i][j] for i in range(len(meta_model_weights))], axis=0 - )for j in range(len(meta_model_weights[0])) - - ] - self.feature_extractor.set_weights([eta * l_meta + (1-eta) * l_base for l_base, l_meta in zip(base_model_weights, merged_meta_parameters)]) - - def _split_tasks(self, x, y): - tasks = [] - for classes in self.class_per_round: - task = None - for cl in classes: - x_cl = x[y == cl] - y_cl = y[y == cl] - if task is None: - task = (x_cl, y_cl) - else: - task = (np.concatenate([task[0], x_cl], axis=0), - np.concatenate([task[1], y_cl], axis=0)) - if len(task[0]) > 0: - self.random_shuffle(task[0],task[1]) - tasks.append(task) - return tasks - - def random_shuffle(self, x, y): - p = np.random.permutation(len(x)) - return x[p], y[p] - - def store_exemplars(self, task): - x = self.labeled_train_set[0] - y = self.labeled_train_set[1] - logging.info(f'Storing exemplars..') - new_classes = self.class_per_round[-1] - model_classes = np.concatenate(self.class_per_round).tolist() - old_classes = model_classes[:(-len(new_classes))] - exemplars_per_class = int(self.memory_size / (len(new_classes) + len(old_classes))) - - if task > 0 : - labels = np.array(self.y_exemplars) - new_x_exemplars = [] - new_y_exemplars = [] - for cl in old_classes: - cl_x = np.array(self.x_exemplars)[labels == cl] - cl_y = np.array(self.y_exemplars)[labels == cl] - new_x_exemplars.extend(cl_x[:exemplars_per_class]) - new_y_exemplars.extend(cl_y[:exemplars_per_class]) - self.x_exemplars = new_x_exemplars - self.y_exemplars = new_y_exemplars - - for cl in new_classes: - logging.info(f'Processing class {cl} and y is {y.shape}') - cl_x = x[y == cl] - cl_y = y[y == cl] - - cl_feat = self.feature_extractor(cl_x) - cl_mean = tf.reduce_mean(cl_feat, axis=0) - - diff = tf.abs(cl_feat - cl_mean) - distance = [float(tf.reduce_sum(dis).numpy()) for dis in diff] - - sorted_index = np.argsort(distance).tolist() - if len(cl_x) > exemplars_per_class: - sorted_index = sorted_index[:exemplars_per_class] - self.x_exemplars.extend(cl_x[sorted_index]) - self.y_exemplars.extend(cl_y[sorted_index]) - - def predict(self, x): - mean = np.array((0.5071, 0.4867, 0.4408), np.float32).reshape(1, 1, -1) - std = np.array((0.2675, 0.2565, 0.2761), np.float32).reshape(1, 1, -1) - x = (tf.cast(x, dtype=tf.float32) / 255.0 - mean) / std - pred = self.classifier(self.feature_extractor(x, training=False)) - prob = tf.nn.softmax(pred, axis=1) - pred = tf.argmax(prob, axis=1) - pred = tf.cast(pred, dtype=tf.int32) - return pred \ No newline at end of file diff --git a/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/aggregation.py b/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/aggregation.py deleted file mode 100644 index fdf10494..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/aggregation.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import abc - -import keras -import numpy as np -from sedna.algorithms.aggregation.aggregation import BaseAggregation -from sedna.common.class_factory import ClassType, ClassFactory -from model import resnet10 - -@ClassFactory.register(ClassType.FL_AGG, "FedAvg") -class FedAvg(BaseAggregation, abc.ABC): - def __init__(self): - super(FedAvg, self).__init__() - self.global_feature_extractor = resnet10(True) - self.global_feature_extractor.build((None, 32, 32, 3)) - self.global_feature_extractor.call(keras.Input(shape=(32, 32, 3))) - - def aggregate(self, clients): - """ - Calculate the average weight according to the number of samples - - Parameters - ---------- - clients: List - All clients in federated learning job - - Returns - ------- - update_weights : Array-like - final weights use to update model layer - """ - - - print("aggregation....") - if not len(clients): - return self.weights - self.total_size = sum([c.num_samples for c in clients]) - old_weight = [np.zeros(np.array(c).shape) for c in - next(iter(clients)).weights] - updates = [] - for inx, row in enumerate(old_weight): - for c in clients: - row += (np.array(c.weights[inx]) * c.num_samples - / self.total_size) - updates.append(row.tolist()) - global_weights = [np.array(layer) for layer in updates] - self.global_feature_extractor.set_weights(global_weights) - self.global_feature_extractor.switch_to_global() - print("finish aggregation....") - return [np.array(layer) for layer in updates] diff --git a/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/agumentation.py b/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/agumentation.py deleted file mode 100644 index 89d1bef2..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/agumentation.py +++ /dev/null @@ -1,230 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import numpy as np -import random -import tensorflow as tf -from PIL import Image, ImageEnhance, ImageOps - - -""" -Reference: https://github.com/heartInsert/randaugment -""" - - -class Rand_Augment: - def __init__(self, Numbers=None, max_Magnitude=None): - self.transforms = [ - "autocontrast", - "equalize", - "rotate", - "solarize", - "color", - "posterize", - "contrast", - "brightness", - "sharpness", - "shearX", - "shearY", - "translateX", - "translateY", - ] - if Numbers is None: - self.Numbers = len(self.transforms) // 2 - else: - self.Numbers = Numbers - if max_Magnitude is None: - self.max_Magnitude = 10 - else: - self.max_Magnitude = max_Magnitude - fillcolor = 128 - self.ranges = { - # these Magnitude range , you must test it yourself , see what will happen after these operation , - # it is no need to obey the value in autoaugment.py - "shearX": np.linspace(0, 0.3, 10), - "shearY": np.linspace(0, 0.3, 10), - "translateX": np.linspace(0, 0.2, 10), - "translateY": np.linspace(0, 0.2, 10), - "rotate": np.linspace(0, 360, 10), - "color": np.linspace(0.0, 0.9, 10), - "posterize": np.round(np.linspace(8, 4, 10), 0).astype(int), - "solarize": np.linspace(256, 231, 10), - "contrast": np.linspace(0.0, 0.5, 10), - "sharpness": np.linspace(0.0, 0.9, 10), - "brightness": np.linspace(0.0, 0.3, 10), - "autocontrast": [0] * 10, - "equalize": [0] * 10, - "invert": [0] * 10, - } - self.func = { - "shearX": lambda img, magnitude: img.transform( - img.size, - Image.AFFINE, - (1, magnitude * random.choice([-1, 1]), 0, 0, 1, 0), - Image.BICUBIC, - fill=fillcolor, - ), - "shearY": lambda img, magnitude: img.transform( - img.size, - Image.AFFINE, - (1, 0, 0, magnitude * random.choice([-1, 1]), 1, 0), - Image.BICUBIC, - fill=fillcolor, - ), - "translateX": lambda img, magnitude: img.transform( - img.size, - Image.AFFINE, - (1, 0, magnitude * img.size[0] * random.choice([-1, 1]), 0, 1, 0), - fill=fillcolor, - ), - "translateY": lambda img, magnitude: img.transform( - img.size, - Image.AFFINE, - (1, 0, 0, 0, 1, magnitude * img.size[1] * random.choice([-1, 1])), - fill=fillcolor, - ), - "rotate": lambda img, magnitude: self.rotate_with_fill(img, magnitude), - # "rotate": lambda img, magnitude: img.rotate(magnitude * random.choice([-1, 1])), - "color": lambda img, magnitude: ImageEnhance.Color(img).enhance( - 1 + magnitude * random.choice([-1, 1]) - ), - "posterize": lambda img, magnitude: ImageOps.posterize(img, magnitude), - "solarize": lambda img, magnitude: ImageOps.solarize(img, magnitude), - "contrast": lambda img, magnitude: ImageEnhance.Contrast(img).enhance( - 1 + magnitude * random.choice([-1, 1]) - ), - "sharpness": lambda img, magnitude: ImageEnhance.Sharpness(img).enhance( - 1 + magnitude * random.choice([-1, 1]) - ), - "brightness": lambda img, magnitude: ImageEnhance.Brightness(img).enhance( - 1 + magnitude * random.choice([-1, 1]) - ), - "autocontrast": lambda img, magnitude: ImageOps.autocontrast(img), - "equalize": lambda img, magnitude: img, - "invert": lambda img, magnitude: ImageOps.invert(img), - } - - def rand_augment(self): - """Generate a set of distortions. - Args: - N: Number of augmentation transformations to apply sequentially. N is len(transforms)/2 will be best - M: Max_Magnitude for all the transformations. should be <= self.max_Magnitude - """ - - M = np.random.randint(0, self.max_Magnitude, self.Numbers) - - sampled_ops = np.random.choice(self.transforms, self.Numbers) - return [(op, Magnitude) for (op, Magnitude) in zip(sampled_ops, M)] - - def __call__(self, image): - operations = self.rand_augment() - for op_name, M in operations: - operation = self.func[op_name] - mag = self.ranges[op_name][M] - image = operation(image, mag) - return image - - def rotate_with_fill(self, img, magnitude): - # I don't know why rotate must change to RGBA , it is copy from Autoaugment - pytorch - rot = img.convert("RGBA").rotate(magnitude) - return Image.composite( - rot, Image.new("RGBA", rot.size, (128,) * 4), rot - ).convert(img.mode) - - def test_single_operation(self, image, op_name, M=-1): - """ - :param image: image - :param op_name: operation name in self.transforms - :param M: -1 stands for the max Magnitude in there operation - :return: - """ - operation = self.func[op_name] - mag = self.ranges[op_name][M] - image = operation(image, mag) - return image - - -class Base_Augment: - def __init__(self, dataset_name: str) -> None: - self.dataset_name = dataset_name - - def __call__(self, images): - return images - - -class Weak_Augment(Base_Augment): - def __init__(self, dataset_name: str) -> None: - super().__init__(dataset_name) - self.augment_impl = self.augment_for_cifar - - def augment_mirror(self, x): - new_images = x.copy() - indices = np.arange(len(new_images)).tolist() - sampled = random.sample( - indices, int(round(0.5 * len(indices))) - ) # flip horizontally 50% - new_images[sampled] = np.fliplr(new_images[sampled]) - return new_images # random shift - - def augment_shift(self, x, w): - y = tf.pad(x, [[0] * 2, [w] * 2, [w] * 2, [0] * 2], mode="REFLECT") - return tf.image.random_crop(y, tf.shape(x)) - - def augment_for_cifar(self, images: np.ndarray): - return self.augment_shift(self.augment_mirror(images), 4) - - def __call__(self, images: np.ndarray): - return self.augment_impl(images) - - -class Strong_Augment(Base_Augment): - def __init__(self, dataset_name: str) -> None: - super().__init__(dataset_name) - - def augment_mirror(self, x): - new_images = x.copy() - indices = np.arange(len(new_images)).tolist() - sampled = random.sample( - indices, int(round(0.5 * len(indices))) - ) # flip horizontally 50% - new_images[sampled] = np.fliplr(new_images[sampled]) - return new_images # random shift - - def augment_shift_mnist(self, x, w): - y = tf.pad(x, [[0] * 2, [w] * 2, [w] * 2], mode="REFLECT") - return tf.image.random_crop(y, tf.shape(x)) - - def __call__(self, images: np.ndarray): - return self.augment_shift_mnist(self.augment_mirror(images), 4) - - -class RandAugment(Base_Augment): - def __init__(self, dataset_name: str) -> None: - super().__init__(dataset_name) - self.rand_augment = Rand_Augment() - self.input_shape = (32, 32, 3) - - def __call__(self, images): - print("images:", images.shape) - - return np.array( - [ - np.array( - self.rand_augment( - Image.fromarray(np.reshape(img, self.input_shape)) - ) - ) - for img in images - ] - ) diff --git a/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/algorithm.yaml b/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/algorithm.yaml deleted file mode 100644 index c9bd7996..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/algorithm.yaml +++ /dev/null @@ -1,27 +0,0 @@ -algorithm: - paradigm_type: "federatedclassincrementallearning" - fl_data_setting: - train_ratio: 1.0 - splitting_method: "default" - label_data_ratio: 1.0 - data_partition: "iid" - initial_model_url: "/home/wyd/ianvs/project/init_model/cnn.pb" - - modules: - - type: "basemodel" - name: "fci_ssl" - url: "./examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/basemodel.py" - hyperparameters: - - batch_size: - values: - - 128 - - learning_rate: - values: - - 0.001 - - epochs: - values: - - 16 - - type: "aggregation" - name: "FedAvg" - url: "./examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/aggregation.py" - diff --git a/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/basemodel.py b/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/basemodel.py deleted file mode 100644 index 09932267..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/basemodel.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import numpy as np -from sedna.common.class_factory import ClassType, ClassFactory -from model import resnet10 -from FedCiMatch import FedCiMatch -import logging - -os.environ["BACKEND_TYPE"] = "KERAS" -__all__ = ["BaseModel"] -logging.getLogger().setLevel(logging.INFO) - - -@ClassFactory.register(ClassType.GENERAL, alias="fci_ssl") -class BaseModel: - def __init__(self, **kwargs) -> None: - self.kwargs = kwargs - self.learning_rate = kwargs.get("learning_rate", 0.001) - self.epochs = kwargs.get("epochs", 1) - self.batch_size = kwargs.get("batch_size", 32) - self.task_size = kwargs.get("task_size", 10) - self.memory_size = kwargs.get("memory_size", 2000) - self.num_classes = 2 # the number of class for the first task - self.FedCiMatch = FedCiMatch( - self.num_classes, - self.batch_size, - self.epochs, - self.learning_rate, - self.memory_size, - ) - self.class_learned = 0 - - def get_weights(self): - print("get weights") - return self.FedCiMatch.get_weights() - - def set_weights(self, weights): - print("set weights") - self.FedCiMatch.set_weights(weights) - - def train(self, train_data, val_data, **kwargs): - task_id = kwargs.get("task_id", 0) - round = kwargs.get("round", 1) - round = task_id * 1 + round - task_size = kwargs.get("task_size", self.task_size) - logging.info(f"in train: {round} task_id: {task_id}") - self.FedCiMatch.before_train(task_id, round, train_data, task_size) - self.FedCiMatch.train(task_id, round) - return {"num_samples": self.FedCiMatch.get_data_size(), "task_id": task_id} - - def predict(self, data_files, **kwargs): - result = {} - for data in data_files: - x = np.load(data) - logging.info(f"predicting {x.shape}") - res = self.FedCiMatch.predict(x) - result[data] = res.numpy() - print("finish predict") - return result diff --git a/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/data_prepocessor.py b/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/data_prepocessor.py deleted file mode 100644 index 15fe199c..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/data_prepocessor.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tensorflow as tf -import numpy as np -from agumentation import Base_Augment - -class Dataset_Preprocessor: - def __init__(self, - dataset_name:str, - weak_augment_helper:Base_Augment, - strong_augment_helper:Base_Augment) -> None: - self.weak_augment_helper = weak_augment_helper - self.strong_augment_helper = strong_augment_helper - self.mean = 0.0 - self.std = 1.0 - if dataset_name == 'cifar100': - self.mean = np.array((0.5071, 0.4867, 0.4408), np.float32).reshape(1, 1, -1) - self.std = np.array((0.2675, 0.2565, 0.2761), np.float32).reshape(1, 1, -1) - print(f"mean: {self.mean}, std: {self.std}") - def preprocess_labeled_dataset(self, x, y, batch_size): - return tf.data.Dataset.from_tensor_slices((x, y)).shuffle(100000).map( - lambda x,y:( - (tf.cast(x, dtype=tf.float32) / 255. - self.mean) / self.std, - tf.cast(y, dtype=tf.int32) - ) - ).batch(batch_size) - - - def preprocess_unlabeled_dataset(self, ux, uy, batch_size): - wux = self.weak_augment_helper(ux) - sux = self.strong_augment_helper(ux) - return tf.data.Dataset.from_tensor_slices((ux, wux, sux, uy)).shuffle(1000).map( - lambda ux,wux,sux,uy: ( - (tf.cast(ux, dtype=tf.float32) / 255. - self.mean) / self.std, - (tf.cast(wux, dtype=tf.float32) / 255. - self.mean) / self.std, - (tf.cast(sux, dtype=tf.float32) / 255. - self.mean) / self.std, - tf.cast(uy, dtype=tf.int32) - ) - ).batch(batch_size) - diff --git a/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/model.py b/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/model.py deleted file mode 100644 index a6f08fa8..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/model.py +++ /dev/null @@ -1,309 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from typing import List -import tensorflow as tf -import numpy as np -import keras -from keras import layers, Sequential - - -class Conv2D(keras.layers.Layer): - def __init__( - self, - is_combined: bool, - alpha: float, - filter_num, - kernel_size, - strides=(1, 1), - padding: str = "valid", - ): - super(Conv2D, self).__init__() - self.is_combined = is_combined - self.alpha = tf.Variable(alpha) - self.conv_local = layers.Conv2D( - filter_num, kernel_size, strides, padding, kernel_initializer="he_normal" - ) - self.conv_global = layers.Conv2D( - filter_num, kernel_size, strides, padding, kernel_initializer="he_normal" - ) - - def call(self, inputs): - return self.alpha * self.conv_global(inputs) + ( - 1 - self.alpha - ) * self.conv_local(inputs) - - def get_alpha(self): - return self.alpha - - def set_alpha(self, alpha): - self.alpha.assign(alpha) - - def get_global_weights(self): - return self.conv_global.get_weights() - - def set_global_weights(self, global_weights): - self.conv_global.set_weights(global_weights) - - def get_global_variables(self): - return self.conv_global.trainable_variables - - def merge_to_local(self): - new_weight = [] - for w_local, w_global in zip( - self.conv_local.get_weights(), self.conv_global.get_weights() - ): - new_weight.append(self.alpha * w_global + (1 - self.alpha) * w_local) - self.conv_local.set_weights(new_weight) - self.alpha.assign(0.0) - - def switch_to_global(self): - self.conv_global.set_weights(self.conv_local.get_weights()) - - -# Input--conv2D--BN--ReLU--conv2D--BN--ReLU--Output -# \ / -# ------------------------------ -class BasicBlock(keras.Model): - def __init__(self, is_combined: bool, filter_num, stride=1): - super(BasicBlock, self).__init__() - - self.filter_num = filter_num - self.stride = stride - - self.conv1 = Conv2D( - is_combined, 0.0, filter_num, (3, 3), strides=stride, padding="same" - ) - self.bn1 = layers.BatchNormalization() - self.relu = layers.Activation("relu") - - self.conv2 = Conv2D( - is_combined, 0.0, filter_num, (3, 3), strides=1, padding="same" - ) - self.bn2 = layers.BatchNormalization() - - if stride != 1: - self.downsample = Sequential() - self.downsample.add( - Conv2D(is_combined, 0.0, filter_num, (1, 1), strides=stride) - ) - else: - self.downsample = lambda x: x - - def call(self, inputs, training=None): - # [b, h, w, c] - out = self.conv1(inputs) - out = self.bn1(out, training=training) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out, training=training) - - identity = self.downsample(inputs) - - output = layers.add([out, identity]) - output = tf.nn.relu(output) - - return output - - -class ResNet(keras.Model): - def __init__(self, is_combined: bool, layer_dims): # [2, 2, 2, 2] - super(ResNet, self).__init__() - - self.is_combined = is_combined - self.stem = Sequential( - [ - Conv2D(is_combined, 0.0, 64, (3, 3), strides=(1, 1)), - layers.BatchNormalization(), - layers.Activation("relu"), - layers.MaxPool2D(pool_size=(2, 2), strides=(1, 1), padding="same"), - ] - ) - - self.layer1 = self.build_resblock(64, layer_dims[0]) - self.layer2 = self.build_resblock(128, layer_dims[1], stride=2) - self.layer3 = self.build_resblock(256, layer_dims[2], stride=2) - self.layer4 = self.build_resblock(512, layer_dims[3], stride=2) - - # output: [b, 512, h, w], - self.avgpool = layers.GlobalAveragePooling2D() - - def call(self, inputs, training=None): - x = self.stem(inputs, training=training) - - x = self.layer1(x, training=training) - x = self.layer2(x, training=training) - x = self.layer3(x, training=training) - x = self.layer4(x, training=training) - - # [b, c] - x = self.avgpool(x) - return x - - def build_resblock(self, filter_num, blocks, stride=1): - res_blocks = [] - # may down sample - res_blocks.append(BasicBlock(self.is_combined, filter_num, stride)) - for _ in range(1, blocks): - res_blocks.append(BasicBlock(self.is_combined, filter_num, stride=1)) - return Sequential(res_blocks) - - def get_alpha(self): - convs = self._get_all_conv_layers() - ret = [] - for conv in convs: - ret.append(conv.get_alpha()) - return ret - - def set_alpha(self, alpha=0.0): - convs = self._get_all_conv_layers() - for conv in convs: - conv.set_alpha(alpha) - - def merge_to_local_model(self): - convs = self._get_all_conv_layers() - for conv in convs: - conv.merge_to_local() - - def switch_to_global(self): - convs = self._get_all_conv_layers() - for conv in convs: - conv.switch_to_global() - - def initialize_alpha(self): - convs = self._get_all_conv_layers() - for conv in convs: - conv.set_alpha(np.random.random()) - - def set_global_model(self, global_model): - local_convs = self._get_all_conv_layers() - global_convs = global_model._get_all_conv_layers() - for local_conv, global_conv in zip(local_convs, global_convs): - local_conv.set_global_weights(global_conv.get_global_weights()) - - def get_global_variables(self): - convs = self._get_all_conv_layers() - ret = [] - for conv in convs: - ret.extend(conv.get_global_variables()) - return ret - - def _get_all_conv_layers(self) -> List[Conv2D]: - def get_all_conv_layers_(model): - convs = [] - for i in model.layers: - if isinstance(i, Conv2D): - convs.append(i) - elif isinstance(i, keras.Model): - convs.extend(get_all_conv_layers_(i)) - return convs - - return get_all_conv_layers_(self) - - -def resnet10(is_combined=False) -> ResNet: - return ResNet(is_combined, [1, 1, 1, 1]) - - -def resnet18(is_combined=False) -> ResNet: - return ResNet(is_combined, [2, 2, 2, 2]) - - -def resnet34(is_combined=False) -> ResNet: - return ResNet(is_combined, [3, 4, 6, 3]) - - -class LeNet5(keras.Model): - def __init__(self): # [2, 2, 2, 2] - super(LeNet5, self).__init__() - self.cnn_layers = keras.Sequential( - [ - Conv2D(True, 0.0, 6, kernel_size=(5, 5), padding="valid"), - layers.ReLU(), - layers.MaxPool2D(pool_size=(2, 2)), - Conv2D(True, 0.0, 16, kernel_size=(5, 5), padding="valid"), - layers.ReLU(), - layers.MaxPool2D(pool_size=(2, 2)), - ] - ) - - self.flatten = layers.Flatten() - - self.fc_layers = keras.Sequential( - [ - layers.Dense(120), - layers.ReLU(), - layers.Dense(84), - layers.ReLU(), - ] - ) - - def call(self, inputs, training=None): - x = self.cnn_layers(inputs, training=training) - - x = self.flatten(x, training=training) - x = self.fc_layers(x, training=training) - - def get_alpha(self): - convs = self._get_all_conv_layers() - ret = [] - for conv in convs: - ret.append(conv.get_alpha()) - return ret - - def set_alpha(self, alpha=0.0): - convs = self._get_all_conv_layers() - for conv in convs: - conv.set_alpha(alpha) - - def merge_to_local_model(self): - convs = self._get_all_conv_layers() - for conv in convs: - conv.merge_to_local() - - def switch_to_global(self): - convs = self._get_all_conv_layers() - for conv in convs: - conv.switch_to_global() - - def initialize_alpha(self): - convs = self._get_all_conv_layers() - for conv in convs: - conv.set_alpha(np.random.random()) - - def set_global_model(self, global_model): - local_convs = self._get_all_conv_layers() - global_convs = global_model._get_all_conv_layers() - for local_conv, global_conv in zip(local_convs, global_convs): - local_conv.set_global_weights(global_conv.get_global_weights()) - - def get_global_variables(self): - convs = self._get_all_conv_layers() - ret = [] - for conv in convs: - ret.extend(conv.get_global_variables()) - return ret - - def _get_all_conv_layers(self) -> List[Conv2D]: - def get_all_conv_layers_(model): - convs = [] - for i in model.layers: - if isinstance(i, Conv2D): - convs.append(i) - elif isinstance(i, keras.Model): - convs.extend(get_all_conv_layers_(i)) - return convs - - return get_all_conv_layers_(self) diff --git a/examples/cifar100/fci_ssl/fed_ci_match_v2/benchmarkingjob.yaml b/examples/cifar100/fci_ssl/fed_ci_match_v2/benchmarkingjob.yaml deleted file mode 100644 index b6564058..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match_v2/benchmarkingjob.yaml +++ /dev/null @@ -1,71 +0,0 @@ -benchmarkingjob: - # job name of bechmarking; string type; - name: "benchmarkingjob" - # the url address of job workspace that will reserve the output of tests; string type; - workspace: "/home/wyd/ianvs/federated_class_incremental_learning/workspace" - - # the url address of test environment configuration file; string type; - # the file format supports yaml/yml; - testenv: "./examples/cifar100/fci_ssl/fed_ci_match_v2/testenv/testenv.yaml" - - # the configuration of test object - test_object: - # test type; string type; - # currently the option of value is "algorithms",the others will be added in succession. - type: "algorithms" - # test algorithm configuration files; list type; - algorithms: - # algorithm name; string type; - - name: "fci_ssl_test" - # the url address of test algorithm configuration file; string type; - # the file format supports yaml/yml - url: "./examples/cifar100/fci_ssl/fed_ci_match_v2/algorithm/algorithm.yaml" - - # the configuration of ranking leaderboard - rank: - # rank leaderboard with metric of test case's evaluation and order ; list type; - # the sorting priority is based on the sequence of metrics in the list from front to back; - sort_by: [ { "accuracy": "descend" } ] - - # visualization configuration - visualization: - # mode of visualization in the leaderboard; string type; - # There are quite a few possible dataitems in the leaderboard. Not all of them can be shown simultaneously on the screen. - # In the leaderboard, we provide the "selected_only" mode for the user to configure what is shown or is not shown. - mode: "selected_only" - # method of visualization for selected dataitems; string type; - # currently the options of value are as follows: - # 1> "print_table": print selected dataitems; - method: "print_table" - - # selected dataitem configuration - # The user can add his/her interested dataitems in terms of "paradigms", "modules", "hyperparameters" and "metrics", - # so that the selected columns will be shown. - selected_dataitem: - # currently the options of value are as follows: - # 1> "all": select all paradigms in the leaderboard; - # 2> paradigms in the leaderboard, e.g., "singletasklearning" - paradigms: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all modules in the leaderboard; - # 2> modules in the leaderboard, e.g., "basemodel" - modules: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all hyperparameters in the leaderboard; - # 2> hyperparameters in the leaderboard, e.g., "momentum" - hyperparameters: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all metrics in the leaderboard; - # 2> metrics in the leaderboard, e.g., "F1_SCORE" - metrics: [ "accuracy", "forget_rate" ] - - # network of save selected and all dataitems in workspace `./rank` ; string type; - # currently the options of value are as follows: - # 1> "selected_and_all": save selected and all dataitems; - # 2> "selected_only": save selected dataitems; - save_mode: "selected_and_all" - - - - - diff --git a/examples/cifar100/fci_ssl/fed_ci_match_v2/testenv/acc.py b/examples/cifar100/fci_ssl/fed_ci_match_v2/testenv/acc.py deleted file mode 100644 index 9dcb119e..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match_v2/testenv/acc.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tensorflow as tf -import numpy as np -from sedna.common.class_factory import ClassFactory, ClassType - -__all__ = ["acc"] - - -@ClassFactory.register(ClassType.GENERAL, alias="accuracy") -def accuracy(y_true, y_pred, **kwargs): - y_pred_arr = [val for val in y_pred.values()] - y_true_arr = [] - for i in range(len(y_pred_arr)): - y_true_arr.append(np.full(y_pred_arr[i].shape, int(y_true[i]))) - y_pred = tf.cast(tf.convert_to_tensor(np.concatenate(y_pred_arr, axis=0)), tf.int64) - y_true = tf.cast(tf.convert_to_tensor(np.concatenate(y_true_arr, axis=0)), tf.int64) - total = tf.shape(y_true)[0] - correct = tf.reduce_sum(tf.cast(tf.equal(y_true, y_pred), tf.int32)) - print(f"correct:{correct}, total:{total}") - acc = float(int(correct) / total) - print(f"acc:{acc}") - return acc diff --git a/examples/cifar100/fci_ssl/fed_ci_match_v2/testenv/testenv.yaml b/examples/cifar100/fci_ssl/fed_ci_match_v2/testenv/testenv.yaml deleted file mode 100644 index bb906c28..00000000 --- a/examples/cifar100/fci_ssl/fed_ci_match_v2/testenv/testenv.yaml +++ /dev/null @@ -1,37 +0,0 @@ -testenv: - backend: "tensorflow" - dataset: - name: 'cifar100' - # the url address of train dataset index; string type; - train_url: "/home/wyd/ianvs/project/data/cifar100/cifar100_train.txt" - # the url address of test dataset index; string type; - test_url: "/home/wyd/ianvs/project/data/cifar100/cifar100_test.txt" - - - # network eval configuration of incremental learning; - model_eval: - # metric used for network evaluation - model_metric: - # metric name; string type; - name: "accuracy" - # the url address of python file - url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/fci_ssl/fed_ci_match_v2/testenv/acc.py" - - # condition of triggering inference network to update - # threshold of the condition; types are float/int - threshold: 0.01 - # operator of the condition; string type; - # values are ">=", ">", "<=", "<" and "="; - operator: "<=" - - # metrics configuration for test case's evaluation; list type; - metrics: - # metric name; string type; - - name: "accuracy" - # the url address of python file - url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/fci_ssl/fed_ci_match_v2/testenv/acc.py" - - name: "forget_rate" - # incremental rounds setting of incremental learning; int type; default value is 2; - incremental_rounds: 50 - round: 1 - client_number: 2 \ No newline at end of file diff --git a/examples/cifar100/fci_ssl/fedavg/__init__.py b/examples/cifar100/fci_ssl/fedavg/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/cifar100/fci_ssl/fedavg/algorithm/__init__.py b/examples/cifar100/fci_ssl/fedavg/algorithm/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/cifar100/fci_ssl/fedavg/algorithm/aggregation.py b/examples/cifar100/fci_ssl/fedavg/algorithm/aggregation.py deleted file mode 100644 index 1aa52589..00000000 --- a/examples/cifar100/fci_ssl/fedavg/algorithm/aggregation.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import abc -from copy import deepcopy -from typing import List - -import numpy as np -from sedna.algorithms.aggregation.aggregation import BaseAggregation -from sedna.common.class_factory import ClassType, ClassFactory - - -@ClassFactory.register(ClassType.FL_AGG, "FedAvg") -class FedAvg(BaseAggregation, abc.ABC): - def __init__(self): - super(FedAvg, self).__init__() - - """ - Federated averaging algorithm - """ - - def aggregate(self, clients: List): - """ - Calculate the average weight according to the number of samples - - Parameters - ---------- - clients: List - All clients in federated learning job - - Returns - ------- - update_weights : Array-like - final weights use to update model layer - """ - - print("aggregation....") - if not len(clients): - return self.weights - self.total_size = sum([c.num_samples for c in clients]) - old_weight = [np.zeros(np.array(c).shape) for c in next(iter(clients)).weights] - updates = [] - for inx, row in enumerate(old_weight): - for c in clients: - row += np.array(c.weights[inx]) * c.num_samples / self.total_size - updates.append(row.tolist()) - self.weights = deepcopy(updates) - print("finish aggregation....") - return [np.array(layer) for layer in updates] diff --git a/examples/cifar100/fci_ssl/fedavg/algorithm/algorithm.yaml b/examples/cifar100/fci_ssl/fedavg/algorithm/algorithm.yaml deleted file mode 100644 index 0b4c683f..00000000 --- a/examples/cifar100/fci_ssl/fedavg/algorithm/algorithm.yaml +++ /dev/null @@ -1,49 +0,0 @@ -algorithm: - # paradigm name; string type; - # currently the options of value are as follows: - # 1> "singletasklearning" - # 2> "incrementallearning" - paradigm_type: "federatedclassincrementallearning" - fl_data_setting: - # ratio of training dataset; float type; - # the default value is 0.8. - train_ratio: 1.0 - # the method of splitting dataset; string type; optional; - # currently the options of value are as follows: - # 1> "default": the dataset is evenly divided based train_ratio; - splitting_method: "default" - label_data_ratio: 1.0 - data_partition: "iid" - # the url address of initial network for network pre-training; string url; - # the url address of initial network; string type; optional; - initial_model_url: "/home/wyd/ianvs/project/init_model/cnn.pb" - # algorithm module configuration in the paradigm; list type; - # incremental rounds setting of incremental learning; int type; default value is 2; - - modules: - # kind of algorithm module; string type; - # currently the options of value are as follows: - # 1> "basemodel" - - type: "basemodel" - # name of python module; string type; - # example: basemodel.py has BaseModel module that the alias is "FPN" for this benchmarking; - name: "fedavg-client" - # the url address of python module; string type; - url: "./examples/cifar100/fci_ssl/fedavg/algorithm/basemodel.py" - - # hyperparameters configuration for the python module; list type; - hyperparameters: - # name of the hyperparameter; string type; - - batch_size: - values: - - 64 - - learning_rate: - values: - - 0.001 - - epochs: - values: - - 1 - - type: "aggregation" - name: "FedAvg" - url: "./examples/cifar100/fci_ssl/fedavg/algorithm/aggregation.py" - diff --git a/examples/cifar100/fci_ssl/fedavg/algorithm/basemodel.py b/examples/cifar100/fci_ssl/fedavg/algorithm/basemodel.py deleted file mode 100644 index 7fdc725b..00000000 --- a/examples/cifar100/fci_ssl/fedavg/algorithm/basemodel.py +++ /dev/null @@ -1,211 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import logging -import keras -import numpy as np -import tensorflow as tf -from sedna.common.class_factory import ClassType, ClassFactory -from model import resnet10 - -__all__ = ["BaseModel"] -os.environ["BACKEND_TYPE"] = "KERAS" -logging.getLogger().setLevel(logging.INFO) - - -@ClassFactory.register(ClassType.GENERAL, alias="fedavg-client") -class BaseModel: - def __init__(self, **kwargs): - self.kwargs = kwargs - print(f"kwargs: {kwargs}") - self.batch_size = kwargs.get("batch_size", 1) - print(f"batch_size: {self.batch_size}") - self.epochs = kwargs.get("epochs", 1) - self.learning_rate = kwargs.get("learning_rate", 0.001) - self.num_classes = 50 - self.task_size = 50 - self.old_task_id = -1 - self.mean = np.array((0.5071, 0.4867, 0.4408), np.float32).reshape(1, 1, -1) - self.std = np.array((0.2675, 0.2565, 0.2761), np.float32).reshape(1, 1, -1) - self.fe = resnet10() - logging.info(type(self.fe)) - self.classifier = None - self._init_model() - - def _init_model(self): - self.fe.compile( - optimizer="sgd", - loss="sparse_categorical_crossentropy", - metrics=["accuracy"], - ) - self.fe.call(keras.Input(shape=(32, 32, 3))) - fe_weights = self.fe.get_weights() - self.fe_weights_length = len(fe_weights) - - def load(self, model_url=None): - logging.info(f"load model from {model_url}") - - def _initialize(self): - logging.info(f"initialize finished") - - def get_weights(self): - weights = [] - fe_weights = self.fe.get_weights() - self.fe_weights_length = len(fe_weights) - clf_weights = self.classifier.get_weights() - weights.extend(fe_weights) - weights.extend(clf_weights) - return weights - - def set_weights(self, weights): - fe_weights = weights[: self.fe_weights_length] - clf_weights = weights[self.fe_weights_length :] - self.fe.set_weights(fe_weights) - self.classifier.set_weights(clf_weights) - - def save(self, model_path=""): - logging.info("save model") - - def model_info(self, model_path, result, relpath): - logging.info("model info") - return {} - - def build_classifier(self): - if self.classifier != None: - new_classifier = keras.Sequential( - [ - keras.layers.Dense( - self.num_classes, kernel_initializer="lecun_normal" - ) - ] - ) - new_classifier.build( - input_shape=(None, self.fe.layers[-2].output_shape[-1]) - ) - new_weights = new_classifier.get_weights() - old_weights = self.classifier.get_weights() - # weight - new_weights[0][0 : old_weights[0].shape[0], 0 : old_weights[0].shape[1]] = ( - old_weights[0] - ) - # bias - new_weights[1][0 : old_weights[1].shape[0]] = old_weights[1] - new_classifier.set_weights(new_weights) - self.classifier = new_classifier - else: - logging.info(f"input shape is {self.fe.layers[-2].output_shape[-1]}") - self.classifier = keras.Sequential( - [ - keras.layers.Dense( - self.num_classes, kernel_initializer="lecun_normal" - ) - ] - ) - self.classifier.build( - input_shape=(None, self.fe.layers[-2].output_shape[-1]) - ) - - logging.info(f"finish ! initialize classifier {self.classifier.summary()}") - - def train(self, train_data, valid_data, **kwargs): - optimizer = keras.optimizers.SGD( - learning_rate=self.learning_rate, momentum=0.9, weight_decay=0.0001 - ) - round = kwargs.get("round", -1) - task_id = kwargs.get("task_id", -1) - if self.old_task_id != task_id: - self.old_task_id = task_id - self.num_classes = self.task_size * (task_id + 1) - self.build_classifier() - data = (train_data["label_x"], train_data["label_y"]) - train_db = self.data_process(data) - logging.info(train_db) - all_params = [] - all_params.extend(self.fe.trainable_variables) - all_params.extend(self.classifier.trainable_variables) - for epoch in range(self.epochs): - total_loss = 0 - total_num = 0 - logging.info(f"Epoch {epoch + 1} / {self.epochs}") - logging.info("-" * 50) - for x, y in train_db: - with tf.GradientTape() as tape: - logits = self.classifier(self.fe(x, training=True), training=True) - loss = tf.reduce_mean( - keras.losses.sparse_categorical_crossentropy( - y, logits, from_logits=True - ) - ) - grads = tape.gradient(loss, all_params) - optimizer.apply(grads, all_params) - total_loss += loss - total_num += 1 - - logging.info( - f"train round {round}: Epoch {epoch + 1} avg loss: {total_loss / total_num}" - ) - logging.info(f"finish round {round} train") - return {"num_samples": data[0].shape[0]} - - def predict(self, data_files, **kwargs): - result = {} - for data in data_files: - x = np.load(data) - logging.info(f"predicting {x.shape}") - mean = np.array((0.5071, 0.4867, 0.4408), np.float32).reshape(1, 1, -1) - std = np.array((0.2675, 0.2565, 0.2761), np.float32).reshape(1, 1, -1) - x = (tf.cast(x, dtype=tf.float32) / 255.0 - mean) / std - pred = self.classifier(self.fe(x, training=False)) - prob = tf.nn.softmax(pred, axis=1) - pred = tf.argmax(prob, axis=1) - pred = tf.cast(pred, dtype=tf.int32) - result[data] = pred.numpy() - logging.info("finish predict") - return result - - def eval(self, data, round, **kwargs): - total_num = 0 - total_correct = 0 - data = self.data_process(data) - for i, (x, y) in enumerate(data): - logits = self.model(x, training=False) - # prob = tf.nn.softmax(logits, axis=1) - pred = tf.argmax(logits, axis=1) - pred = tf.cast(pred, dtype=tf.int32) - correct = tf.cast(tf.equal(pred, y), dtype=tf.int32) - correct = tf.reduce_sum(correct) - total_num += x.shape[0] - total_correct += int(correct) - logging.info(f"total_correct: {total_correct}, total_num: {total_num}") - acc = total_correct / total_num - del total_correct - logging.info(f"finsih round {round}evaluate, acc: {acc}") - return acc - - def data_process(self, data, **kwargs): - - assert data is not None, "data is None" - # data[0]'shape = (50000, 32,32,3) data[1]'shape = (50000,) - return ( - tf.data.Dataset.from_tensor_slices((data[0], data[1])) - .shuffle(100000) - .map( - lambda x, y: ( - (tf.cast(x, dtype=tf.float32) / 255.0 - self.mean) / self.std, - tf.cast(y, dtype=tf.int32), - ) - ) - .batch(self.batch_size) - ) diff --git a/examples/cifar100/fci_ssl/fedavg/algorithm/model.py b/examples/cifar100/fci_ssl/fedavg/algorithm/model.py deleted file mode 100644 index 5e5ea3a5..00000000 --- a/examples/cifar100/fci_ssl/fedavg/algorithm/model.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tensorflow as tf -import keras - - -# Input--conv2D--BN--ReLU--conv2D--BN--ReLU--Output -# \ / -# ------------------------------ -class BasicBlock(keras.layers.Layer): - def __init__(self, filter_num, stride=1): - super(BasicBlock, self).__init__() - - self.conv1 = keras.layers.Conv2D( - filter_num, (3, 3), strides=stride, padding="same" - ) - self.bn1 = keras.layers.BatchNormalization() - self.relu = keras.layers.Activation("relu") - - self.conv2 = keras.layers.Conv2D(filter_num, (3, 3), strides=1, padding="same") - self.bn2 = keras.layers.BatchNormalization() - - if stride != 1: - self.downsample = keras.models.Sequential() - self.downsample.add(keras.layers.Conv2D(filter_num, (1, 1), strides=stride)) - else: - self.downsample = lambda x: x - - def call(self, inputs, training=None): - # [b, h, w, c] - out = self.conv1(inputs) - out = self.bn1(out, training=training) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out, training=training) - - identity = self.downsample(inputs) - - output = keras.layers.add([out, identity]) - output = tf.nn.relu(output) - - return output - - -class ResNet(keras.Model): - def __init__(self, layer_dims): # [2, 2, 2, 2] - super(ResNet, self).__init__() - self.layer_dims = layer_dims - - self.stem = keras.models.Sequential( - [ - keras.layers.Conv2D(64, (3, 3), strides=(1, 1)), - keras.layers.BatchNormalization(), - keras.layers.Activation("relu"), - keras.layers.MaxPool2D( - pool_size=(2, 2), strides=(1, 1), padding="same" - ), - ] - ) - - self.layer1 = self.build_resblock(64, layer_dims[0]) - self.layer2 = self.build_resblock(128, layer_dims[1], stride=2) - self.layer3 = self.build_resblock(256, layer_dims[2], stride=2) - self.layer4 = self.build_resblock(512, layer_dims[3], stride=2) - - # output: [b, 512, h, w], - self.avgpool = keras.layers.GlobalAveragePooling2D() - - def call(self, inputs, training=None): - x = self.stem(inputs, training=training) - - x = self.layer1(x, training=training) - x = self.layer2(x, training=training) - x = self.layer3(x, training=training) - x = self.layer4(x, training=training) - x = self.avgpool(x) - return x - - def build_resblock(self, filter_num, blocks, stride=1): - res_blocks = keras.models.Sequential() - # may down sample - res_blocks.add(BasicBlock(filter_num, stride)) - for _ in range(1, blocks): - res_blocks.add(BasicBlock(filter_num, stride=1)) - return res_blocks - - def get_config(self): - return { - "layer_dims": self.layer_dims, - } - - @classmethod - def from_config(cls, config): - return cls(**config) - - -class LeNet(keras.Model): - def __init__(self, input_shape, channels=3, num_classes=10): - super(LeNet, self).__init__() - self.input_shape = input_shape - self.channels = channels - self.num_classes = num_classes - - self.conv1 = keras.layers.Conv2D( - 6, - kernel_size=5, - strides=1, - activation="relu", - input_shape=(input_shape, input_shape, channels), - ) - self.pool1 = keras.layers.MaxPool2D(pool_size=2, strides=2) - self.conv2 = keras.layers.Conv2D( - 16, kernel_size=5, strides=1, activation="relu" - ) - self.pool2 = keras.layers.MaxPool2D(pool_size=2, strides=2) - self.flatten = keras.layers.Flatten() - - self.fc1 = keras.layers.Dense(120, activation="relu") - self.fc2 = keras.layers.Dense(84, activation="relu") - self.fc3 = keras.layers.Dense(num_classes, activation="softmax") - - def call(self, inputs, training=None): - x = self.conv1(inputs) - x = self.pool1(x) - x = self.conv2(x) - x = self.pool2(x) - x = self.flatten(x) - x = self.fc1(x) - x = self.fc2(x) - x = self.fc3(x) - return x - - def get_config(self): - return { - "input_shape": self.input_shape, - "channels": self.channels, - "num_classes": self.num_classes, - } - - @classmethod - def from_config(cls, config): - return cls(**config) - - -def lenet5(input_shape, num_classes: int): - return LeNet(input_shape, 3, num_classes) - - -def resnet10(): - return ResNet([1, 1, 1, 1]) - - -def resnet18(num_classes: int): - return ResNet([2, 2, 2, 2]) - - -def resnet34(num_classes: int): - return ResNet([3, 4, 6, 3]) diff --git a/examples/cifar100/fci_ssl/fedavg/algorithm/resnet.py b/examples/cifar100/fci_ssl/fedavg/algorithm/resnet.py deleted file mode 100644 index cbc4de72..00000000 --- a/examples/cifar100/fci_ssl/fedavg/algorithm/resnet.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tensorflow as tf -import keras - - -# Input--conv2D--BN--ReLU--conv2D--BN--ReLU--Output -# \ / -# ------------------------------ -class BasicBlock(keras.layers.Layer): - def __init__(self, filter_num, stride=1): - super(BasicBlock, self).__init__() - - self.conv1 = keras.layers.Conv2D( - filter_num, (3, 3), strides=stride, padding="same" - ) - self.bn1 = keras.layers.BatchNormalization() - self.relu = keras.layers.Activation("relu") - - self.conv2 = keras.layers.Conv2D(filter_num, (3, 3), strides=1, padding="same") - self.bn2 = keras.layers.BatchNormalization() - - if stride != 1: - self.downsample = keras.models.Sequential() - self.downsample.add(keras.layers.Conv2D(filter_num, (1, 1), strides=stride)) - else: - self.downsample = lambda x: x - - def call(self, inputs, training=None): - # [b, h, w, c] - out = self.conv1(inputs) - out = self.bn1(out, training=training) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out, training=training) - - identity = self.downsample(inputs) - - output = keras.layers.add([out, identity]) - output = tf.nn.relu(output) - - return output - - -class ResNet(keras.Model): - def __init__(self, layer_dims, num_classes=10): # [2, 2, 2, 2] - super(ResNet, self).__init__() - self.layer_dims = layer_dims - self.num_classes = num_classes - - self.stem = keras.models.Sequential( - [ - keras.layers.Conv2D(64, (3, 3), strides=(1, 1)), - keras.layers.BatchNormalization(), - keras.layers.Activation("relu"), - keras.layers.MaxPool2D( - pool_size=(2, 2), strides=(1, 1), padding="same" - ), - ] - ) - - self.layer1 = self.build_resblock(64, layer_dims[0]) - self.layer2 = self.build_resblock(128, layer_dims[1], stride=2) - self.layer3 = self.build_resblock(256, layer_dims[2], stride=2) - self.layer4 = self.build_resblock(512, layer_dims[3], stride=2) - - # output: [b, 512, h, w], - self.avgpool = keras.layers.GlobalAveragePooling2D() - # self.fc = keras.layers.Dense(num_classes) - - def call(self, inputs, training=None): - x = self.stem(inputs, training=training) - - x = self.layer1(x, training=training) - x = self.layer2(x, training=training) - x = self.layer3(x, training=training) - x = self.layer4(x, training=training) - - # [b, c] - x = self.avgpool(x) - return x - - def build_resblock(self, filter_num, blocks, stride=1): - res_blocks = keras.models.Sequential() - # may down sample - res_blocks.add(BasicBlock(filter_num, stride)) - for _ in range(1, blocks): - res_blocks.add(BasicBlock(filter_num, stride=1)) - return res_blocks - - def get_config(self): - return {"layer_dims": self.layer_dims, "num_classes": self.num_classes} - - @classmethod - def from_config(cls, config): - return cls(**config) - - -def resnet10(num_classes: int): - return ResNet([1, 1, 1, 1], num_classes) - - -def resnet18(num_classes: int): - return ResNet([2, 2, 2, 2], num_classes) - - -def resnet34(num_classes: int): - return ResNet([3, 4, 6, 3], num_classes) diff --git a/examples/cifar100/fci_ssl/fedavg/benchmarkingjob.yaml b/examples/cifar100/fci_ssl/fedavg/benchmarkingjob.yaml deleted file mode 100644 index 6f4b9642..00000000 --- a/examples/cifar100/fci_ssl/fedavg/benchmarkingjob.yaml +++ /dev/null @@ -1,71 +0,0 @@ -benchmarkingjob: - # job name of bechmarking; string type; - name: "benchmarkingjob" - # the url address of job workspace that will reserve the output of tests; string type; - workspace: "/home/wyd/ianvs/federated_class_incremental_learning/workspace" - - # the url address of test environment configuration file; string type; - # the file format supports yaml/yml; - testenv: "./examples/cifar100/fci_ssl/fedavg/testenv/testenv.yaml" - - # the configuration of test object - test_object: - # test type; string type; - # currently the option of value is "algorithms",the others will be added in succession. - type: "algorithms" - # test algorithm configuration files; list type; - algorithms:conda - # algorithm name; string type; - - name: "basic-fedavg" - # the url address of test algorithm configuration file; string type; - # the file format supports yaml/yml - url: "./examples/cifar100/fci_ssl/fedavg/algorithm/algorithm.yaml" - - # the configuration of ranking leaderboard - rank: - # rank leaderboard with metric of test case's evaluation and order ; list type; - # the sorting priority is based on the sequence of metrics in the list from front to back; - sort_by: [ { "task_avg_acc": "descend" } ] - - # visualization configuration - visualization: - # mode of visualization in the leaderboard; string type; - # There are quite a few possible dataitems in the leaderboard. Not all of them can be shown simultaneously on the screen. - # In the leaderboard, we provide the "selected_only" mode for the user to configure what is shown or is not shown. - mode: "selected_only" - # method of visualization for selected dataitems; string type; - # currently the options of value are as follows: - # 1> "print_table": print selected dataitems; - method: "print_table" - - # selected dataitem configuration - # The user can add his/her interested dataitems in terms of "paradigms", "modules", "hyperparameters" and "metrics", - # so that the selected columns will be shown. - selected_dataitem: - # currently the options of value are as follows: - # 1> "all": select all paradigms in the leaderboard; - # 2> paradigms in the leaderboard, e.g., "singletasklearning" - paradigms: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all modules in the leaderboard; - # 2> modules in the leaderboard, e.g., "basemodel" - modules: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all hyperparameters in the leaderboard; - # 2> hyperparameters in the leaderboard, e.g., "momentum" - hyperparameters: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all metrics in the leaderboard; - # 2> metrics in the leaderboard, e.g., "F1_SCORE" - metrics: ["task_avg_acc","forget_rate"] - - # network of save selected and all dataitems in workspace `./rank` ; string type; - # currently the options of value are as follows: - # 1> "selected_and_all": save selected and all dataitems; - # 2> "selected_only": save selected dataitems; - save_mode: "selected_and_all" - - - - - diff --git a/examples/cifar100/fci_ssl/fedavg/test.py b/examples/cifar100/fci_ssl/fedavg/test.py deleted file mode 100644 index 5b3cf860..00000000 --- a/examples/cifar100/fci_ssl/fedavg/test.py +++ /dev/null @@ -1,14 +0,0 @@ -from algorithm.resnet import resnet10 -from algorithm.network import NetWork, incremental_learning -import copy -import numpy as np -fe = resnet10(10) -model = NetWork(10, fe) -new_model = copy.deepcopy(model) - -x = np.random.rand(1, 32, 32, 3) -y = np.random.randint(0, 10, 1) -model.compile(optimizer='sgd', loss='sparse_categorical_crossentropy', metrics=['accuracy']) -model.fit(x, y, epochs=1) -new_model.compile(optimizer='sgd', loss='sparse_categorical_crossentropy', metrics=['accuracy']) -new_model.fit(x, y, epochs=1) \ No newline at end of file diff --git a/examples/cifar100/fci_ssl/fedavg/testenv/__init__.py b/examples/cifar100/fci_ssl/fedavg/testenv/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/cifar100/fci_ssl/fedavg/testenv/acc.py b/examples/cifar100/fci_ssl/fedavg/testenv/acc.py deleted file mode 100644 index 0f1eaf1c..00000000 --- a/examples/cifar100/fci_ssl/fedavg/testenv/acc.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tensorflow as tf -import numpy as np -from sedna.common.class_factory import ClassFactory, ClassType - -__all__ = ["acc"] - - -@ClassFactory.register(ClassType.GENERAL, alias="accuracy") -def accuracy(y_true, y_pred, **kwargs): - y_pred_arr = [val for val in y_pred.values()] - y_true_arr = [] - for i in range(len(y_pred_arr)): - y_true_arr.append(np.full(y_pred_arr[i].shape, int(y_true[i]))) - y_pred = tf.cast(tf.convert_to_tensor(np.concatenate(y_pred_arr, axis=0)), tf.int64) - y_true = tf.cast(tf.convert_to_tensor(np.concatenate(y_true_arr, axis=0)), tf.int64) - total = tf.shape(y_true)[0] - correct = tf.reduce_sum(tf.cast(tf.equal(y_true, y_pred), tf.int32)) - acc = float(int(correct) / total) - print(f"acc:{acc}") - return acc diff --git a/examples/cifar100/fci_ssl/fedavg/testenv/testenv.yaml b/examples/cifar100/fci_ssl/fedavg/testenv/testenv.yaml deleted file mode 100644 index 74eff7c0..00000000 --- a/examples/cifar100/fci_ssl/fedavg/testenv/testenv.yaml +++ /dev/null @@ -1,38 +0,0 @@ -testenv: - backend: "tensorflow" - dataset: - name: 'cifar100' - # the url address of train dataset index; string type; - train_url: "/home/wyd/ianvs/project/data/cifar100/cifar100_train.txt" - # the url address of test dataset index; string type; - test_url: "/home/wyd/ianvs/project/data/cifar100/cifar100_test.txt" - - - # network eval configuration of incremental learning; - model_eval: - # metric used for network evaluation - model_metric: - # metric name; string type; - name: "accuracy" - # the url address of python file - url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/fci_ssl/fedavg/testenv/acc.py" - - # condition of triggering inference network to update - # threshold of the condition; types are float/int - threshold: 0.01 - # operator of the condition; string type; - # values are ">=", ">", "<=", "<" and "="; - operator: "<=" - - # metrics configuration for test case's evaluation; list type; - metrics: - # metric name; string type; - - name: "accuracy" - # the url address of python file - url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/fci_ssl/fedavg/testenv/acc.py" - - name: "task_avg_acc" - - name: "forget_rate" - # incremental rounds setting of incremental learning; int type; default value is 2; - incremental_rounds: 2 - round: 1 - client_number: 1 \ No newline at end of file diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/GLFC.py b/examples/cifar100/fci_ssl/glfc/algorithm/GLFC.py deleted file mode 100644 index 469ae720..00000000 --- a/examples/cifar100/fci_ssl/glfc/algorithm/GLFC.py +++ /dev/null @@ -1,447 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import copy -import numpy as np -import tensorflow as tf -import keras -import logging -from agumentation import * -from data_prepocessor import * -from model import resnet10 - - -def get_one_hot(target, num_classes): - y = tf.one_hot(target, depth=num_classes) - if len(y.shape) == 3: - y = tf.squeeze(y, axis=1) - return y - - -class GLFC_Client: - def __init__( - self, - num_classes, - batch_size, - task_size, - memory_size, - epochs, - learning_rate, - encode_model, - ): - self.epochs = epochs - self.learning_rate = learning_rate - - self.encode_model = encode_model - - self.num_classes = num_classes - logging.info(f"num_classes is {num_classes}") - self.batch_size = batch_size - self.task_size = task_size - - self.old_model = None - self.train_set = None - - self.exemplar_set = [] # - self.class_mean_set = [] - self.learned_classes = [] - self.learned_classes_numebr = 0 - self.memory_size = memory_size - - self.old_task_id = -1 - self.current_classes = None - self.last_class = None - self.train_loader = None - self.build_feature_extractor() - self.classifier = None - self.labeled_train_set = None - self.unlabeled_train_set = None - self.data_preprocessor = Dataset_Preprocessor( - "cifar100", Weak_Augment("cifar100"), RandAugment("cifar100") - ) - self.warm_up_epochs = 10 - - def build_feature_extractor(self): - self.feature_extractor = resnet10() - self.feature_extractor.build(input_shape=(None, 32, 32, 3)) - self.feature_extractor.call(keras.Input(shape=(32, 32, 3))) - self.feature_extractor.load_weights( - "examples/cifar100/fci_ssl/glfc/algorithm/feature_extractor.weights.h5" - ) - - def _initialize_classifier(self): - if self.classifier != None: - new_classifier = tf.keras.Sequential( - [ - tf.keras.layers.Dense( - self.num_classes, kernel_initializer="lecun_normal" - ) - ] - ) - new_classifier.build( - input_shape=(None, self.feature_extractor.layers[-2].output_shape[-1]) - ) - new_weights = new_classifier.get_weights() - old_weights = self.classifier.get_weights() - # weight - new_weights[0][0 : old_weights[0].shape[0], 0 : old_weights[0].shape[1]] = ( - old_weights[0] - ) - # bias - new_weights[1][0 : old_weights[1].shape[0]] = old_weights[1] - new_classifier.set_weights(new_weights) - self.classifier = new_classifier - else: - logging.info( - f"input shape is {self.feature_extractor.layers[-2].output_shape[-1]}" - ) - self.classifier = tf.keras.Sequential( - [ - tf.keras.layers.Dense( - self.num_classes, kernel_initializer="lecun_normal" - ) - ] - ) - self.classifier.build( - input_shape=(None, self.feature_extractor.layers[-2].output_shape[-1]) - ) - - logging.info(f"finish ! initialize classifier {self.classifier.summary()}") - - def before_train(self, task_id, train_data, class_learned, old_model): - logging.info(f"------before train task_id: {task_id}------") - self.need_update = task_id != self.old_task_id - if self.need_update: - self.old_task_id = task_id - self.num_classes = self.task_size * (task_id + 1) - if self.current_classes is not None: - self.last_class = self.current_classes - logging.info( - f"self.last_class is , {self.last_class}, {self.num_classes} tasksize is {self.task_size}, task_id is {task_id}" - ) - self._initialize_classifier() - self.current_classes = np.unique(train_data["label_y"]).tolist() - self.update_new_set(self.need_update) - self.labeled_train_set = (train_data["label_x"], train_data["label_y"]) - self.unlabeled_train_set = ( - train_data["unlabel_x"], - train_data["unlabel_y"], - ) - if len(old_model) != 0: - self.old_model = old_model[0] - self.labeled_train_set = (train_data["label_x"], train_data["label_y"]) - self.unlabeled_train_set = (train_data["unlabel_x"], train_data["unlabel_y"]) - self.labeled_train_loader, self.unlabeled_train_loader = self._get_train_loader( - True - ) - logging.info( - f"------finish before train task_id: {task_id} {self.current_classes}------" - ) - - def update_new_set(self, need_update): - if need_update and self.last_class is not None: - # update exemplar - self.learned_classes += self.last_class - self.learned_classes_numebr += len(self.last_class) - m = int(self.memory_size / self.learned_classes_numebr) - self._reduce_exemplar_set(m) - for i in self.last_class: - images = self.get_train_set_data(i) - self._construct_exemplar_set(images, i, m) - - def _get_train_loader(self, mix): - self.mean = np.array((0.5071, 0.4867, 0.4408), np.float32).reshape(1, 1, -1) - self.std = np.array((0.2675, 0.2565, 0.2761), np.float32).reshape(1, 1, -1) - train_x = self.labeled_train_set[0] - train_y = self.labeled_train_set[1] - if mix: - for exm_set in self.exemplar_set: - logging.info(f"mix the exemplar{len(exm_set[0])}, {len(exm_set[1])}") - label = np.array(exm_set[1]) - train_x = np.concatenate((train_x, exm_set[0]), axis=0) - train_y = np.concatenate((train_y, label), axis=0) - label_data_loader = self.data_preprocessor.preprocess_labeled_dataset( - train_x, train_y, self.batch_size - ) - unlabel_data_loader = None - if len(self.unlabeled_train_set[0]) > 0: - unlabel_data_loader = self.data_preprocessor.preprocess_unlabeled_dataset( - self.unlabeled_train_set[0], - self.unlabeled_train_set[1], - self.batch_size, - ) - logging.info( - f"unlabel_x shape: {self.unlabeled_train_set[0].shape} and unlabel_y shape: {self.unlabeled_train_set[1].shape}" - ) - return (label_data_loader, unlabel_data_loader) - - def train(self, round): - opt = keras.optimizers.Adam( - learning_rate=self.learning_rate, weight_decay=0.00001 - ) - feature_extractor_params = self.feature_extractor.trainable_variables - classifier_params = self.classifier.trainable_variables - all_params = [] - all_params.extend(feature_extractor_params) - all_params.extend(classifier_params) - - for epoch in range(self.epochs): - # following code is for semi-supervised learning - # for labeled_data, unlabeled_data in zip( - # self.labeled_train_loader, self.unlabeled_train_loader - # ): - # labeled_x, labeled_y = labeled_data - # unlabeled_x, weak_unlabeled_x, strong_unlabeled_x, unlabeled_y = ( - # unlabeled_data - # ) - - # following code is for supervised learning - for step, (x, y) in enumerate(self.labeled_train_loader): - # opt = keras.optimizers.SGD(learning_rate=self.learning_rate, weight_decay=0.00001) - with tf.GradientTape() as tape: - supervised_loss = self._compute_loss(x, y) - loss = supervised_loss - - # following code is for semi-supervised learning - # if epoch > self.warm_up_epochs: - # unsupervised_loss = self.unsupervised_loss( - # weak_unlabeled_x, strong_unlabeled_x - # ) - # loss = loss + 0.5 * unsupervised_loss - # logging.info( - # f"supervised loss is {supervised_loss} unsupervised loss is {unsupervised_loss}" - # ) - logging.info( - f"------round{round} epoch{epoch} loss: {loss} and loss dim is {loss.shape}------" - ) - grads = tape.gradient(loss, all_params) - opt.apply_gradients(zip(grads, all_params)) - - logging.info(f"------finish round{round} traning------") - - def model_call(self, x, training=False): - input = self.feature_extractor(inputs=x, training=training) - return self.classifier(inputs=input, training=training) - - def _compute_loss(self, imgs, labels): - logging.info(f"self.old_model is available: {self.old_model is not None}") - y_pred = self.model_call(imgs, training=True) - target = get_one_hot(labels, self.num_classes) - logits = y_pred - pred = tf.argmax(logits, axis=1) - pred = tf.cast(pred, dtype=tf.int32) - pred = tf.reshape(pred, labels.shape) - - y = tf.cast(labels, dtype=tf.int32) - correct = tf.cast(tf.equal(pred, y), dtype=tf.int32) - correct = tf.reduce_sum(correct) - logging.info( - f"current class numbers is {self.num_classes} correct is {correct} and acc is {correct/imgs.shape[0]} tasksize is {self.task_size} self.old_task_id {self.old_task_id}" - ) - if self.old_model == None: - w = self.efficient_old_class_weight(target, labels) - loss = tf.reduce_mean( - keras.losses.categorical_crossentropy(target, y_pred, from_logits=True) - * w - ) - logging.info( - f"in _compute_loss, without old model loss is {loss} and shape is {loss.shape}" - ) - return loss - else: - w = self.efficient_old_class_weight(target, labels) - loss = tf.reduce_mean( - keras.losses.binary_crossentropy(target, y_pred, from_logits=True) * w - ) - distill_target = tf.Variable(get_one_hot(labels, self.num_classes)) - old_target = tf.sigmoid(self.old_model[1](self.old_model[0]((imgs)))) - old_task_size = old_target.shape[1] - distill_target[:, :old_task_size].assign(old_target) - loss_old = tf.reduce_mean( - keras.losses.binary_crossentropy( - distill_target, y_pred, from_logits=True - ) - ) - logging.info(f"loss old is {loss_old}") - return loss + loss_old - - def unsupervised_loss(self, weak_x, strong_x): - self.accept_threshold = 0.95 - prob_on_wux = tf.nn.softmax( - self.classifier( - self.feature_extractor(weak_x, training=True), training=True - ) - ) - pseudo_mask = tf.cast( - (tf.reduce_max(prob_on_wux, axis=1) >= self.accept_threshold), tf.float32 - ) - pse_uy = tf.one_hot( - tf.argmax(prob_on_wux, axis=1), depth=self.num_classes - ).numpy() - prob_on_sux = tf.nn.softmax( - self.classifier( - self.feature_extractor(strong_x, training=True), training=True - ) - ) - loss = keras.losses.categorical_crossentropy(pse_uy, prob_on_sux) - loss = tf.reduce_mean(loss * pseudo_mask) - return loss - - def efficient_old_class_weight(self, output, labels): - pred = tf.sigmoid(output) - N, C = pred.shape - class_mask = tf.zeros([N, C], dtype=tf.float32) - class_mask = tf.Variable(class_mask) - ids = np.zeros([N, 2], dtype=np.int32) - for i in range(N): - ids[i][0] = i - ids[i][1] = labels[i] - updates = tf.ones([N], dtype=tf.float32) - class_mask = tf.tensor_scatter_nd_update(class_mask, ids, updates) - target = get_one_hot(labels, self.num_classes) - g = tf.abs(target - pred) - g = tf.reduce_sum(g * class_mask, axis=1) - idx = tf.cast(tf.reshape(labels, (-1, 1)), tf.int32) - if len(self.learned_classes) != 0: - for i in self.learned_classes: - mask = tf.math.not_equal(idx, i) - negative_value = tf.cast(tf.fill(tf.shape(idx), -1), tf.int32) - idx = tf.where(mask, idx, negative_value) - index1 = tf.cast(tf.equal(idx, -1), tf.float32) - index2 = tf.cast(tf.not_equal(idx, -1), tf.float32) - w1 = tf.where( - tf.not_equal(tf.reduce_sum(index1), 0), - tf.math.divide( - g * index1, (tf.reduce_sum(g * index1) / tf.reduce_sum(index1)) - ), - tf.zeros_like(g), - ) - w2 = tf.where( - tf.not_equal(tf.reduce_sum(index2), 0), - tf.math.divide( - g * index2, (tf.reduce_sum(g * index2) / tf.reduce_sum(index2)) - ), - tf.zeros_like(g), - ) - w = w1 + w2 - return w - else: - return tf.ones(g.shape, dtype=tf.float32) - - def get_train_set_data(self, class_id): - - images = [] - train_x = self.labeled_train_set[0] - train_y = self.labeled_train_set[1] - for i in range(len(train_x)): - if train_y[i] == class_id: - images.append(train_x[i]) - return images - - def get_data_size(self): - logging.info( - f"self.labeled_train_set is None :{self.labeled_train_set is None}" - ) - logging.info( - f"self.unlabeled_train_set is None :{self.unlabeled_train_set is None}" - ) - data_size = len(self.labeled_train_set[0]) - logging.info(f"data size: {data_size}") - return data_size - - def _reduce_exemplar_set(self, m): - for i in range(len(self.exemplar_set)): - old_exemplar_data = self.exemplar_set[i][0][:m] - old_exemplar_label = self.exemplar_set[i][1][:m] - self.exemplar_set[i] = (old_exemplar_data, old_exemplar_label) - - def _construct_exemplar_set(self, images, label, m): - class_mean, fe_outpu = self.compute_class_mean(images) - exemplar = [] - labels = [] - now_class_mean = np.zeros((1, 512)) - for i in range(m): - x = class_mean - (now_class_mean + fe_outpu) / (i + 1) - x = np.linalg.norm(x) - index = np.argmin(x) - now_class_mean += fe_outpu[index] - exemplar.append(images[index]) - labels.append(label) - self.exemplar_set.append((exemplar, labels)) - - def compute_class_mean(self, images): - images_data = tf.data.Dataset.from_tensor_slices(images).batch(self.batch_size) - fe_output = self.feature_extractor.predict(images_data) - fe_output = tf.nn.l2_normalize(fe_output).numpy() - class_mean = tf.reduce_mean(fe_output, axis=0) - return class_mean, fe_output - - def proto_grad(self): - if self.need_update == False: - return None - self.need_update = False - cri_loss = keras.losses.SparseCategoricalCrossentropy() - proto = [] - proto_grad = [] - logging.info(f"self. current class is {self.current_classes}") - for i in self.current_classes: - images = self.get_train_set_data(i) - class_mean, fe_output = self.compute_class_mean(images) - dis = np.linalg.norm(class_mean - fe_output, axis=1) - pro_index = np.argmin(dis) - proto.append(images[pro_index]) - - for i in range(len(proto)): - data = proto[i] - data = tf.cast(tf.expand_dims(data, axis=0), tf.float32) - label = self.current_classes[i] - label = tf.constant([label]) - target = get_one_hot(label, self.num_classes) - logging.info( - f"proto_grad target shape is {target.shape} and num_classes is {self.num_classes}" - ) - proto_fe = resnet10() - proto_fe.build(input_shape=(None, 32, 32, 3)) - proto_fe.call(keras.Input(shape=(32, 32, 3))) - proto_fe.set_weights(self.feature_extractor.get_weights()) - proto_clf = copy.deepcopy(self.classifier) - proto_param = proto_fe.trainable_variables - proto_param.extend(proto_clf.trainable_variables) - with tf.GradientTape() as tape: - outputs = self.encode_model(data) - loss_cls = cri_loss(label, outputs) - dy_dx = tape.gradient(loss_cls, self.encode_model.trainable_variables) - original_dy_dx = [tf.identity(grad) for grad in dy_dx] - proto_grad.append(original_dy_dx) - return proto_grad - - def evaluate(self): - logging.info("evaluate") - total_num = 0 - total_correct = 0 - for x, y in self.train_loader: - logits = self.model_call(x, training=False) - pred = tf.argmax(logits, axis=1) - pred = tf.cast(pred, dtype=tf.int32) - pred = tf.reshape(pred, y.shape) - logging.info(pred) - y = tf.cast(y, dtype=tf.int32) - correct = tf.cast(tf.equal(pred, y), dtype=tf.int32) - correct = tf.reduce_sum(correct) - total_num += x.shape[0] - total_correct += int(correct) - acc = total_correct / total_num - del total_correct - logging.info(f"finsih task {self.old_task_id} evaluate, acc: {acc}") - return acc diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/aggregation.py b/examples/cifar100/fci_ssl/glfc/algorithm/aggregation.py deleted file mode 100644 index a61c791b..00000000 --- a/examples/cifar100/fci_ssl/glfc/algorithm/aggregation.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import abc -from copy import deepcopy -from typing import List - -import numpy as np -from sedna.algorithms.aggregation.aggregation import BaseAggregation -from sedna.common.class_factory import ClassType, ClassFactory -from proxy_server import ProxyServer - - -@ClassFactory.register(ClassType.FL_AGG, "FedAvg") -class FedAvg(BaseAggregation, abc.ABC): - def __init__(self): - super(FedAvg, self).__init__() - self.proxy_server = ProxyServer( - learning_rate=0.01, num_classes=10, test_data=None - ) - self.task_id = -1 - self.num_classes = 10 - - def aggregate(self, clients): - """ - Calculate the average weight according to the number of samples - - Parameters - ---------- - clients: List - All clients in federated learning job - - Returns - ------- - update_weights : Array-like - final weights use to update model layer - """ - - print("aggregation....") - if not len(clients): - return self.weights - self.total_size = sum([c.num_samples for c in clients]) - old_weight = [np.zeros(np.array(c).shape) for c in next(iter(clients)).weights] - updates = [] - for inx, row in enumerate(old_weight): - for c in clients: - row += np.array(c.weights[inx]) * c.num_samples / self.total_size - updates.append(row.tolist()) - - self.weights = [np.array(layer) for layer in updates] - - print("finish aggregation....") - return self.weights - - def helper_function(self, train_infos, **kwargs): - proto_grad = [] - task_id = -1 - for key, value in train_infos.items(): - if "proto_grad" == key and value is not None: - for grad_i in value: - proto_grad.append(grad_i) - if "task_id" == key: - task_id = max(value, task_id) - self.proxy_server.dataload(proto_grad) - if task_id > self.task_id: - self.task_id = task_id - print(f"incremental num classes is {self.num_classes * (task_id + 1)}") - self.proxy_server.increment_class(self.num_classes * (task_id + 1)) - self.proxy_server.set_weights(self.weights) - print(f"finish set weight for proxy server") - return {"best_old_model": self.proxy_server.model_back()} diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/agumentation.py b/examples/cifar100/fci_ssl/glfc/algorithm/agumentation.py deleted file mode 100644 index 89d1bef2..00000000 --- a/examples/cifar100/fci_ssl/glfc/algorithm/agumentation.py +++ /dev/null @@ -1,230 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import numpy as np -import random -import tensorflow as tf -from PIL import Image, ImageEnhance, ImageOps - - -""" -Reference: https://github.com/heartInsert/randaugment -""" - - -class Rand_Augment: - def __init__(self, Numbers=None, max_Magnitude=None): - self.transforms = [ - "autocontrast", - "equalize", - "rotate", - "solarize", - "color", - "posterize", - "contrast", - "brightness", - "sharpness", - "shearX", - "shearY", - "translateX", - "translateY", - ] - if Numbers is None: - self.Numbers = len(self.transforms) // 2 - else: - self.Numbers = Numbers - if max_Magnitude is None: - self.max_Magnitude = 10 - else: - self.max_Magnitude = max_Magnitude - fillcolor = 128 - self.ranges = { - # these Magnitude range , you must test it yourself , see what will happen after these operation , - # it is no need to obey the value in autoaugment.py - "shearX": np.linspace(0, 0.3, 10), - "shearY": np.linspace(0, 0.3, 10), - "translateX": np.linspace(0, 0.2, 10), - "translateY": np.linspace(0, 0.2, 10), - "rotate": np.linspace(0, 360, 10), - "color": np.linspace(0.0, 0.9, 10), - "posterize": np.round(np.linspace(8, 4, 10), 0).astype(int), - "solarize": np.linspace(256, 231, 10), - "contrast": np.linspace(0.0, 0.5, 10), - "sharpness": np.linspace(0.0, 0.9, 10), - "brightness": np.linspace(0.0, 0.3, 10), - "autocontrast": [0] * 10, - "equalize": [0] * 10, - "invert": [0] * 10, - } - self.func = { - "shearX": lambda img, magnitude: img.transform( - img.size, - Image.AFFINE, - (1, magnitude * random.choice([-1, 1]), 0, 0, 1, 0), - Image.BICUBIC, - fill=fillcolor, - ), - "shearY": lambda img, magnitude: img.transform( - img.size, - Image.AFFINE, - (1, 0, 0, magnitude * random.choice([-1, 1]), 1, 0), - Image.BICUBIC, - fill=fillcolor, - ), - "translateX": lambda img, magnitude: img.transform( - img.size, - Image.AFFINE, - (1, 0, magnitude * img.size[0] * random.choice([-1, 1]), 0, 1, 0), - fill=fillcolor, - ), - "translateY": lambda img, magnitude: img.transform( - img.size, - Image.AFFINE, - (1, 0, 0, 0, 1, magnitude * img.size[1] * random.choice([-1, 1])), - fill=fillcolor, - ), - "rotate": lambda img, magnitude: self.rotate_with_fill(img, magnitude), - # "rotate": lambda img, magnitude: img.rotate(magnitude * random.choice([-1, 1])), - "color": lambda img, magnitude: ImageEnhance.Color(img).enhance( - 1 + magnitude * random.choice([-1, 1]) - ), - "posterize": lambda img, magnitude: ImageOps.posterize(img, magnitude), - "solarize": lambda img, magnitude: ImageOps.solarize(img, magnitude), - "contrast": lambda img, magnitude: ImageEnhance.Contrast(img).enhance( - 1 + magnitude * random.choice([-1, 1]) - ), - "sharpness": lambda img, magnitude: ImageEnhance.Sharpness(img).enhance( - 1 + magnitude * random.choice([-1, 1]) - ), - "brightness": lambda img, magnitude: ImageEnhance.Brightness(img).enhance( - 1 + magnitude * random.choice([-1, 1]) - ), - "autocontrast": lambda img, magnitude: ImageOps.autocontrast(img), - "equalize": lambda img, magnitude: img, - "invert": lambda img, magnitude: ImageOps.invert(img), - } - - def rand_augment(self): - """Generate a set of distortions. - Args: - N: Number of augmentation transformations to apply sequentially. N is len(transforms)/2 will be best - M: Max_Magnitude for all the transformations. should be <= self.max_Magnitude - """ - - M = np.random.randint(0, self.max_Magnitude, self.Numbers) - - sampled_ops = np.random.choice(self.transforms, self.Numbers) - return [(op, Magnitude) for (op, Magnitude) in zip(sampled_ops, M)] - - def __call__(self, image): - operations = self.rand_augment() - for op_name, M in operations: - operation = self.func[op_name] - mag = self.ranges[op_name][M] - image = operation(image, mag) - return image - - def rotate_with_fill(self, img, magnitude): - # I don't know why rotate must change to RGBA , it is copy from Autoaugment - pytorch - rot = img.convert("RGBA").rotate(magnitude) - return Image.composite( - rot, Image.new("RGBA", rot.size, (128,) * 4), rot - ).convert(img.mode) - - def test_single_operation(self, image, op_name, M=-1): - """ - :param image: image - :param op_name: operation name in self.transforms - :param M: -1 stands for the max Magnitude in there operation - :return: - """ - operation = self.func[op_name] - mag = self.ranges[op_name][M] - image = operation(image, mag) - return image - - -class Base_Augment: - def __init__(self, dataset_name: str) -> None: - self.dataset_name = dataset_name - - def __call__(self, images): - return images - - -class Weak_Augment(Base_Augment): - def __init__(self, dataset_name: str) -> None: - super().__init__(dataset_name) - self.augment_impl = self.augment_for_cifar - - def augment_mirror(self, x): - new_images = x.copy() - indices = np.arange(len(new_images)).tolist() - sampled = random.sample( - indices, int(round(0.5 * len(indices))) - ) # flip horizontally 50% - new_images[sampled] = np.fliplr(new_images[sampled]) - return new_images # random shift - - def augment_shift(self, x, w): - y = tf.pad(x, [[0] * 2, [w] * 2, [w] * 2, [0] * 2], mode="REFLECT") - return tf.image.random_crop(y, tf.shape(x)) - - def augment_for_cifar(self, images: np.ndarray): - return self.augment_shift(self.augment_mirror(images), 4) - - def __call__(self, images: np.ndarray): - return self.augment_impl(images) - - -class Strong_Augment(Base_Augment): - def __init__(self, dataset_name: str) -> None: - super().__init__(dataset_name) - - def augment_mirror(self, x): - new_images = x.copy() - indices = np.arange(len(new_images)).tolist() - sampled = random.sample( - indices, int(round(0.5 * len(indices))) - ) # flip horizontally 50% - new_images[sampled] = np.fliplr(new_images[sampled]) - return new_images # random shift - - def augment_shift_mnist(self, x, w): - y = tf.pad(x, [[0] * 2, [w] * 2, [w] * 2], mode="REFLECT") - return tf.image.random_crop(y, tf.shape(x)) - - def __call__(self, images: np.ndarray): - return self.augment_shift_mnist(self.augment_mirror(images), 4) - - -class RandAugment(Base_Augment): - def __init__(self, dataset_name: str) -> None: - super().__init__(dataset_name) - self.rand_augment = Rand_Augment() - self.input_shape = (32, 32, 3) - - def __call__(self, images): - print("images:", images.shape) - - return np.array( - [ - np.array( - self.rand_augment( - Image.fromarray(np.reshape(img, self.input_shape)) - ) - ) - for img in images - ] - ) diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/algorithm.yaml b/examples/cifar100/fci_ssl/glfc/algorithm/algorithm.yaml deleted file mode 100644 index f09a6e86..00000000 --- a/examples/cifar100/fci_ssl/glfc/algorithm/algorithm.yaml +++ /dev/null @@ -1,49 +0,0 @@ -algorithm: - # paradigm name; string type; - # currently the options of value are as follows: - # 1> "singletasklearning" - # 2> "incrementallearning" - paradigm_type: "federatedclassincrementallearning" - fl_data_setting: - # ratio of training dataset; float type; - # the default value is 0.8. - train_ratio: 1.0 - # the method of splitting dataset; string type; optional; - # currently the options of value are as follows: - # 1> "default": the dataset is evenly divided based train_ratio; - splitting_method: "default" - label_data_ratio: 1.0 - data_partition: "iid" - # the url address of initial network for network pre-training; string url; - # the url address of initial network; string type; optional; - initial_model_url: "/home/wyd/ianvs/project/init_model/cnn.pb" - # algorithm module configuration in the paradigm; list type; - # incremental rounds setting of incremental learning; int type; default value is 2; - - modules: - # kind of algorithm module; string type; - # currently the options of value are as follows: - # 1> "basemodel" - - type: "basemodel" - # name of python module; string type; - # example: basemodel.py has BaseModel module that the alias is "FPN" for this benchmarking; - name: "GLFCMatch-Client" - # the url address of python module; string type; - url: "./examples/cifar100/fci_ssl/glfc/algorithm/basemodel.py" - - # hyperparameters configuration for the python module; list type; - hyperparameters: - # name of the hyperparameter; string type; - - batch_size: - values: - - 64 - - learning_rate: - values: - - 0.001 - - epochs: - values: - - 20 - - type: "aggregation" - name: "FedAvg" - url: "./examples/cifar100/fci_ssl/glfc/algorithm/aggregation.py" - diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/basemodel.py b/examples/cifar100/fci_ssl/glfc/algorithm/basemodel.py deleted file mode 100644 index e459e8ee..00000000 --- a/examples/cifar100/fci_ssl/glfc/algorithm/basemodel.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import numpy as np -import keras -import tensorflow as tf -from sedna.common.class_factory import ClassType, ClassFactory -from model import resnet10, lenet5 -from GLFC import GLFC_Client -import logging - -os.environ["BACKEND_TYPE"] = "KERAS" -__all__ = ["BaseModel"] -logging.getLogger().setLevel(logging.INFO) - - -@ClassFactory.register(ClassType.GENERAL, alias="GLFCMatch-Client") -class BaseModel: - def __init__(self, **kwargs) -> None: - self.kwargs = kwargs - self.learning_rate = kwargs.get("learning_rate", 0.001) - self.epochs = kwargs.get("epochs", 1) - self.batch_size = kwargs.get("batch_size", 32) - self.task_size = kwargs.get("task_size", 10) - self.memory_size = kwargs.get("memory_size", 2000) - self.encode_model = lenet5(32, 100) - self.encode_model.call(keras.Input(shape=(32, 32, 3))) - self.num_classes = 10 # the number of class for the first task - self.GLFC_Client = GLFC_Client( - self.num_classes, - self.batch_size, - self.task_size, - self.memory_size, - self.epochs, - self.learning_rate, - self.encode_model, - ) - self.best_old_model = [] - self.class_learned = 0 - self.fe_weights_length = len(self.GLFC_Client.feature_extractor.get_weights()) - - def get_weights(self): - print("get weights") - weights = [] - fe_weights = self.GLFC_Client.feature_extractor.get_weights() - clf_weights = self.GLFC_Client.classifier.get_weights() - weights.extend(fe_weights) - weights.extend(clf_weights) - return weights - - def set_weights(self, weights): - print("set weights") - fe_weights = weights[: self.fe_weights_length] - - clf_weights = weights[self.fe_weights_length :] - self.GLFC_Client.feature_extractor.set_weights(fe_weights) - self.GLFC_Client.classifier.set_weights(clf_weights) - - def train(self, train_data, val_data, **kwargs): - task_id = kwargs.get("task_id", 0) - round = kwargs.get("round", 1) - logging.info(f"in train: {round} task_id: {task_id}") - self.class_learned += self.task_size - self.GLFC_Client.before_train( - task_id, train_data, self.class_learned, old_model=self.best_old_model - ) - - self.GLFC_Client.train(round) - proto_grad = self.GLFC_Client.proto_grad() - return { - "num_samples": self.GLFC_Client.get_data_size(), - "proto_grad": proto_grad, - "task_id": task_id, - } - - def helper_function(self, helper_info, **kwargs): - self.best_old_model = helper_info["best_old_model"] - if self.best_old_model[1] != None: - self.GLFC_Client.old_model = self.best_old_model[1] - else: - self.GLFC_Client.old_model = self.best_old_model[0] - - def predict(self, datas, **kwargs): - result = {} - mean = np.array((0.5071, 0.4867, 0.4408), np.float32).reshape(1, 1, -1) - std = np.array((0.2675, 0.2565, 0.2761), np.float32).reshape(1, 1, -1) - for data in datas: - x = np.load(data) - x = (tf.cast(x, dtype=tf.float32) / 255.0 - mean) / std - logits = self.GLFC_Client.model_call(x, training=False) - pred = tf.cast(tf.argmax(logits, axis=1), tf.int32) - result[data] = pred.numpy() - print("finish predict") - return result diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/data_prepocessor.py b/examples/cifar100/fci_ssl/glfc/algorithm/data_prepocessor.py deleted file mode 100644 index 449e09f2..00000000 --- a/examples/cifar100/fci_ssl/glfc/algorithm/data_prepocessor.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tensorflow as tf -import numpy as np -from agumentation import Base_Augment - - -class Dataset_Preprocessor: - def __init__( - self, - dataset_name: str, - weak_augment_helper: Base_Augment, - strong_augment_helper: Base_Augment, - ) -> None: - self.weak_augment_helper = weak_augment_helper - self.strong_augment_helper = strong_augment_helper - self.mean = 0.0 - self.std = 1.0 - if dataset_name == "cifar100": - self.mean = np.array((0.5071, 0.4867, 0.4408), np.float32).reshape(1, 1, -1) - self.std = np.array((0.2675, 0.2565, 0.2761), np.float32).reshape(1, 1, -1) - print(f"mean: {self.mean}, std: {self.std}") - - def preprocess_labeled_dataset(self, x, y, batch_size): - return ( - tf.data.Dataset.from_tensor_slices((x, y)) - .shuffle(100000) - .map( - lambda x, y: ( - (tf.cast(x, dtype=tf.float32) / 255.0 - self.mean) / self.std, - tf.cast(y, dtype=tf.int32), - ) - ) - .batch(batch_size) - ) - - def preprocess_unlabeled_dataset(self, ux, uy, batch_size): - - wux = self.weak_augment_helper(ux) - sux = self.strong_augment_helper(ux) - return ( - tf.data.Dataset.from_tensor_slices((ux, wux, sux, uy)) - .shuffle(100000) - .map( - lambda ux, wux, sux, uy: ( - (tf.cast(ux, dtype=tf.float32) / 255.0 - self.mean) / self.std, - (tf.cast(wux, dtype=tf.float32) / 255.0 - self.mean) / self.std, - (tf.cast(sux, dtype=tf.float32) / 255.0 - self.mean) / self.std, - tf.cast(uy, dtype=tf.int32), - ) - ) - .batch(batch_size) - ) diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/feature_extractor.weights.h5 b/examples/cifar100/fci_ssl/glfc/algorithm/feature_extractor.weights.h5 deleted file mode 100644 index d216d072..00000000 Binary files a/examples/cifar100/fci_ssl/glfc/algorithm/feature_extractor.weights.h5 and /dev/null differ diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/model.py b/examples/cifar100/fci_ssl/glfc/algorithm/model.py deleted file mode 100644 index 8498eba4..00000000 --- a/examples/cifar100/fci_ssl/glfc/algorithm/model.py +++ /dev/null @@ -1,205 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tensorflow as tf -import keras - - -# Input--conv2D--BN--ReLU--conv2D--BN--ReLU--Output -# \ / -# ------------------------------ -class BasicBlock(keras.layers.Layer): - def __init__(self, filter_num, stride=1): - super(BasicBlock, self).__init__() - - self.conv1 = keras.layers.Conv2D( - filter_num, (3, 3), strides=stride, padding="same" - ) - self.bn1 = keras.layers.BatchNormalization() - self.relu = keras.layers.Activation("relu") - - self.conv2 = keras.layers.Conv2D(filter_num, (3, 3), strides=1, padding="same") - self.bn2 = keras.layers.BatchNormalization() - - if stride != 1: - self.downsample = keras.models.Sequential() - self.downsample.add(keras.layers.Conv2D(filter_num, (1, 1), strides=stride)) - else: - self.downsample = lambda x: x - - def call(self, inputs, training=None): - # [b, h, w, c] - out = self.conv1(inputs) - out = self.bn1(out, training=training) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out, training=training) - - identity = self.downsample(inputs) - - output = keras.layers.add([out, identity]) - output = tf.nn.relu(output) - - return output - - -# restnet -class ResNet(keras.Model): - def __init__(self, layer_dims): # [2, 2, 2, 2] - super(ResNet, self).__init__() - self.layer_dims = layer_dims - - self.stem = keras.models.Sequential( - [ - keras.layers.Conv2D(64, (3, 3), strides=(1, 1)), - keras.layers.BatchNormalization(), - keras.layers.Activation("relu"), - keras.layers.MaxPool2D( - pool_size=(2, 2), strides=(1, 1), padding="same" - ), - ] - ) - - self.layer1 = self.build_resblock(64, layer_dims[0]) - self.layer2 = self.build_resblock(128, layer_dims[1], stride=2) - self.layer3 = self.build_resblock(256, layer_dims[2], stride=2) - self.layer4 = self.build_resblock(512, layer_dims[3], stride=2) - - # output: [b, 512, h, w], - self.avgpool = keras.layers.GlobalAveragePooling2D() - - def call(self, inputs, training=None): - x = self.stem(inputs, training=training) - - x = self.layer1(x, training=training) - x = self.layer2(x, training=training) - x = self.layer3(x, training=training) - x = self.layer4(x, training=training) - x = self.avgpool(x) - return x - - def build_resblock(self, filter_num, blocks, stride=1): - res_blocks = keras.models.Sequential() - # may down sample - res_blocks.add(BasicBlock(filter_num, stride)) - for _ in range(1, blocks): - res_blocks.add(BasicBlock(filter_num, stride=1)) - return res_blocks - - def get_config(self): - return { - "layer_dims": self.layer_dims, - } - - @classmethod - def from_config(cls, config): - return cls(**config) - - -class LeNet(keras.Model): - def __init__(self, input_shape, channels=3, num_classes=10): - super(LeNet, self).__init__() - self.input_shape = input_shape - self.channels = channels - self.num_classes = num_classes - - self.conv1 = keras.layers.Conv2D( - 6, - kernel_size=5, - strides=1, - activation="relu", - input_shape=(input_shape, input_shape, channels), - kernel_initializer=keras.initializers.RandomUniform( - minval=-0.5, maxval=0.5 - ), - bias_initializer=keras.initializers.RandomUniform(minval=-0.5, maxval=0.5), - ) - - self.pool1 = keras.layers.MaxPool2D(pool_size=2, strides=2) - self.conv2 = keras.layers.Conv2D( - 16, - kernel_size=5, - strides=1, - activation="relu", - kernel_initializer=keras.initializers.RandomUniform( - minval=-0.5, maxval=0.5 - ), - bias_initializer=keras.initializers.RandomUniform(minval=-0.5, maxval=0.5), - ) - self.pool2 = keras.layers.MaxPool2D(pool_size=2, strides=2) - self.flatten = keras.layers.Flatten() - - self.fc1 = keras.layers.Dense( - 120, - activation="relu", - kernel_initializer=keras.initializers.RandomUniform( - minval=-0.5, maxval=0.5 - ), - bias_initializer=keras.initializers.RandomUniform(minval=-0.5, maxval=0.5), - ) - self.fc2 = keras.layers.Dense( - 84, - activation="relu", - kernel_initializer=keras.initializers.RandomUniform( - minval=-0.5, maxval=0.5 - ), - bias_initializer=keras.initializers.RandomUniform(minval=-0.5, maxval=0.5), - ) - self.fc3 = keras.layers.Dense( - num_classes, - activation="softmax", - kernel_initializer=keras.initializers.RandomUniform( - minval=-0.5, maxval=0.5 - ), - bias_initializer=keras.initializers.RandomUniform(minval=-0.5, maxval=0.5), - ) - - def call(self, inputs, training=None): - x = self.conv1(inputs) - x = self.pool1(x) - x = self.conv2(x) - x = self.pool2(x) - x = self.flatten(x) - x = self.fc1(x) - x = self.fc2(x) - x = self.fc3(x) - return x - - def get_config(self): - return { - "input_shape": self.input_shape, - "channels": self.channels, - "num_classes": self.num_classes, - } - - @classmethod - def from_config(cls, config): - return cls(**config) - - -def lenet5(input_shape, num_classes: int): - return LeNet(input_shape, 3, num_classes) - - -def resnet10(): - return ResNet([1, 1, 1, 1]) - - -def resnet18(num_classes: int): - return ResNet([2, 2, 2, 2]) - - -def resnet34(num_classes: int): - return ResNet([3, 4, 6, 3]) diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/proxy_server.py b/examples/cifar100/fci_ssl/glfc/algorithm/proxy_server.py deleted file mode 100644 index 69e03901..00000000 --- a/examples/cifar100/fci_ssl/glfc/algorithm/proxy_server.py +++ /dev/null @@ -1,201 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import keras -import copy -import numpy as np -import tensorflow as tf -import logging -from model import resnet10, resnet18, resnet34, lenet5 - -logging.getLogger().setLevel(logging.INFO) - - -class ProxyData: - def __init__(self): - self.test_data = [] - self.test_label = [] - - -class ProxyServer: - def __init__(self, learning_rate, num_classes, **kwargs): - self.learning_rate = learning_rate - - self.encode_model = lenet5(32, 100) - - self.monitor_dataset = ProxyData() - self.new_set = [] - self.new_set_label = [] - self.num_classes = num_classes - self.proto_grad = None - - self.best_perf = 0 - - self.num_image = 20 - self.Iteration = 250 - - self.build_model() - self.fe_weights_length = len(self.feature_extractor.get_weights()) - self.classifier = None - self.best_model_1 = None - self.best_model_2 = None - - def build_model(self): - self.feature_extractor = resnet10() - self.feature_extractor.build(input_shape=(None, 32, 32, 3)) - self.feature_extractor.call(keras.Input(shape=(32, 32, 3))) - - def set_weights(self, weights): - print(f"set weights {self.num_classes}") - fe_weights = weights[: self.fe_weights_length] - clf_weights = weights[self.fe_weights_length :] - self.feature_extractor.set_weights(fe_weights) - self.classifier.set_weights(clf_weights) - - def increment_class(self, num_classes): - print(f"increment class {num_classes}") - self.num_classes = num_classes - self._initialize_classifier() - - def _initialize_classifier(self): - if self.classifier != None: - new_classifier = tf.keras.Sequential( - [ - tf.keras.layers.Dense( - self.num_classes, kernel_initializer="lecun_normal" - ) - ] - ) - new_classifier.build( - input_shape=(None, self.feature_extractor.layers[-2].output_shape[-1]) - ) - new_weights = new_classifier.get_weights() - old_weights = self.classifier.get_weights() - # weight - new_weights[0][0 : old_weights[0].shape[0], 0 : old_weights[0].shape[1]] = ( - old_weights[0] - ) - # bias - new_weights[1][0 : old_weights[1].shape[0]] = old_weights[1] - new_classifier.set_weights(new_weights) - self.classifier = new_classifier - else: - - self.classifier = tf.keras.Sequential( - [ - tf.keras.layers.Dense( - self.num_classes, kernel_initializer="lecun_normal" - ) - ] - ) - self.classifier.build( - input_shape=(None, self.feature_extractor.layers[-2].output_shape[-1]) - ) - self.best_model_1 = (self.feature_extractor, self.classifier) - logging.info(f"finish ! initialize classifier {self.classifier}") - - def model_back(self): - return [self.best_model_1, self.best_model_2] - - def dataload(self, proto_grad): - self._initialize_classifier() - self.proto_grad = proto_grad - if len(proto_grad) != 0: - self.reconstruction() - self.monitor_dataset.test_data = self.new_set - self.monitor_dataset.test_label = self.new_set_label - self.last_perf = 0 - self.best_model_1 = self.best_model_2 - cur_perf = self.monitor() - logging.info(f"in proxy server, current performance is {cur_perf}") - if cur_perf > self.best_perf: - self.best_perf = cur_perf - self.best_model_2 = (self.feature_extractor, self.classifier) - - def monitor(self): - correct, total = 0, 0 - for x, y in zip( - self.monitor_dataset.test_data, self.monitor_dataset.test_label - ): - y_pred = self.classifier(self.feature_extractor((x))) - - predicts = tf.argmax(y_pred, axis=-1) - predicts = tf.cast(predicts, tf.int32) - logging.info(f"y_pred {predicts} and y {y}") - correct += tf.reduce_sum(tf.cast(tf.equal(predicts, y), dtype=tf.int32)) - total += x.shape[0] - acc = 100 * correct / total - return acc - - def grad2label(self): - proto_grad_label = [] - for w_single in self.proto_grad: - pred = tf.argmin(tf.reduce_sum(w_single[-2], axis=-1), axis=-1) - proto_grad_label.append(pred) - return proto_grad_label - - def reconstruction(self): - self.new_set = [] - self.new_set_label = [] - proto_label = self.grad2label() - proto_label = np.array(proto_label) - class_ratio = np.zeros((1, 100)) - - for i in proto_label: - class_ratio[0][i] += 1 - - for label_i in range(100): - if class_ratio[0][label_i] > 0: - agumentation = [] - - grad_index = np.where(proto_label == label_i) - logging.info(f"grad index : {grad_index} and label is {label_i}") - for j in range(len(grad_index[0])): - grad_true_temp = self.proto_grad[grad_index[0][j]] - - dummy_data = tf.Variable( - np.random.rand(1, 32, 32, 3), trainable=True - ) - label_pred = tf.constant([label_i]) - - opt = keras.optimizers.SGD(learning_rate=0.1) - cri = keras.losses.SparseCategoricalCrossentropy() - - recon_model = copy.deepcopy(self.encode_model) - - for iter in range(self.Iteration): - with tf.GradientTape() as tape0: - with tf.GradientTape() as tape1: - y_pred = recon_model(dummy_data) - loss = cri(label_pred, y_pred) - dummy_dy_dx = tape1.gradient( - loss, recon_model.trainable_variables - ) - - grad_diff = 0 - for gx, gy in zip(dummy_dy_dx, grad_true_temp): - gx = tf.cast(gx, tf.double) - gy = tf.cast(gy, tf.double) - sub_value = tf.subtract(gx, gy) - pow_value = tf.pow(sub_value, 2) - grad_diff += tf.reduce_sum(pow_value) - grad = tape0.gradient(grad_diff, dummy_data) - opt.apply_gradients(zip([grad], [dummy_data])) - - if iter >= self.Iteration - self.num_image: - dummy_data_temp = np.asarray(dummy_data) - agumentation.append(dummy_data_temp) - - self.new_set.extend(agumentation) - self.new_set_label.extend([label_i]) diff --git a/examples/cifar100/fci_ssl/glfc/benchmarkingjob.yaml b/examples/cifar100/fci_ssl/glfc/benchmarkingjob.yaml deleted file mode 100644 index 006d14ce..00000000 --- a/examples/cifar100/fci_ssl/glfc/benchmarkingjob.yaml +++ /dev/null @@ -1,71 +0,0 @@ -benchmarkingjob: - # job name of bechmarking; string type; - name: "benchmarkingjob" - # the url address of job workspace that will reserve the output of tests; string type; - workspace: "/home/wyd/ianvs/federated_class_incremental_learning/workspace" - - # the url address of test environment configuration file; string type; - # the file format supports yaml/yml; - testenv: "./examples/cifar100/fci_ssl/glfc/testenv/testenv.yaml" - - # the configuration of test object - test_object: - # test type; string type; - # currently the option of value is "algorithms",the others will be added in succession. - type: "algorithms" - # test algorithm configuration files; list type; - algorithms: - # algorithm name; string type; - - name: "GLFCMatch" - # the url address of test algorithm configuration file; string type; - # the file format supports yaml/yml - url: "./examples/cifar100/fci_ssl/glfc/algorithm/algorithm.yaml" - - # the configuration of ranking leaderboard - rank: - # rank leaderboard with metric of test case's evaluation and order ; list type; - # the sorting priority is based on the sequence of metrics in the list from front to back; - sort_by: [ { "task_avg_acc": "descend" } ] - - # visualization configuration - visualization: - # mode of visualization in the leaderboard; string type; - # There are quite a few possible dataitems in the leaderboard. Not all of them can be shown simultaneously on the screen. - # In the leaderboard, we provide the "selected_only" mode for the user to configure what is shown or is not shown. - mode: "selected_only" - # method of visualization for selected dataitems; string type; - # currently the options of value are as follows: - # 1> "print_table": print selected dataitems; - method: "print_table" - - # selected dataitem configuration - # The user can add his/her interested dataitems in terms of "paradigms", "modules", "hyperparameters" and "metrics", - # so that the selected columns will be shown. - selected_dataitem: - # currently the options of value are as follows: - # 1> "all": select all paradigms in the leaderboard; - # 2> paradigms in the leaderboard, e.g., "singletasklearning" - paradigms: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all modules in the leaderboard; - # 2> modules in the leaderboard, e.g., "basemodel" - modules: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all hyperparameters in the leaderboard; - # 2> hyperparameters in the leaderboard, e.g., "momentum" - hyperparameters: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all metrics in the leaderboard; - # 2> metrics in the leaderboard, e.g., "F1_SCORE" - metrics: [ "task_avg_acc","forget_rate" ] - - # network of save selected and all dataitems in workspace `./rank` ; string type; - # currently the options of value are as follows: - # 1> "selected_and_all": save selected and all dataitems; - # 2> "selected_only": save selected dataitems; - save_mode: "selected_and_all" - - - - - diff --git a/examples/cifar100/fci_ssl/glfc/testenv/acc.py b/examples/cifar100/fci_ssl/glfc/testenv/acc.py deleted file mode 100644 index 0fe532d3..00000000 --- a/examples/cifar100/fci_ssl/glfc/testenv/acc.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tensorflow as tf -import numpy as np -from sedna.common.class_factory import ClassFactory, ClassType - -__all__ = ['acc'] - - -@ClassFactory.register(ClassType.GENERAL, alias='accuracy') -def accuracy(y_true, y_pred, **kwargs): - y_pred_arr = [val for val in y_pred.values()] - y_true_arr = [] - for i in range(len(y_pred_arr)): - y_true_arr.append(np.full(y_pred_arr[i].shape, int(y_true[i]))) - y_pred = tf.cast(tf.convert_to_tensor(np.concatenate(y_pred_arr, axis=0)), tf.int64) - y_true = tf.cast(tf.convert_to_tensor(np.concatenate(y_true_arr, axis=0)), tf.int64) - total = tf.shape(y_true)[0] - correct = tf.reduce_sum(tf.cast(tf.equal(y_true, y_pred), tf.int32)) - acc = float(int(correct) / total) - print(f"acc:{acc}") - return acc - diff --git a/examples/cifar100/fci_ssl/glfc/testenv/testenv.yaml b/examples/cifar100/fci_ssl/glfc/testenv/testenv.yaml deleted file mode 100644 index 5c5b961e..00000000 --- a/examples/cifar100/fci_ssl/glfc/testenv/testenv.yaml +++ /dev/null @@ -1,31 +0,0 @@ -testenv: - backend: "tensorflow" - dataset: - name: 'cifar100' - # the url address of train dataset index; string type; - train_url: "/home/wyd/ianvs/project/data/cifar100/cifar100_train.txt" - # the url address of test dataset index; string type; - test_url: "/home/wyd/ianvs/project/data/cifar100/cifar100_test.txt" - - - # network eval configuration of incremental learning; - model_eval: - # metric used for network evaluation - model_metric: - # metric name; string type; - name: "accuracy" - # the url address of python file - url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/fci_ssl/glfc/testenv/acc.py" - - # metrics configuration for test case's evaluation; list type; - metrics: - # metric name; string type; - # - name: "accuracy" - # # the url address of python file - # url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/fci_ssl/glfc/testenv/acc.py" - - name: "forget_rate" - - name: "task_avg_acc" - # incremental rounds setting of incremental learning; int type; default value is 2; - incremental_rounds: 10 - round: 5 - client_number: 5 \ No newline at end of file diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/__init__.py b/examples/cifar100/federated_class_incremental_learning/fedavg/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/__init__.py b/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/aggregation.py b/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/aggregation.py deleted file mode 100644 index 53237aa1..00000000 --- a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/aggregation.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import abc -from copy import deepcopy -from typing import List - -import numpy as np -from sedna.algorithms.aggregation.aggregation import BaseAggregation -from sedna.common.class_factory import ClassType, ClassFactory - - -@ClassFactory.register(ClassType.FL_AGG, "FedAvg") -class FedAvg(BaseAggregation, abc.ABC): - def __init__(self): - super(FedAvg, self).__init__() - - """ - Federated averaging algorithm - """ - - def aggregate(self, clients: List): - """ - Calculate the average weight according to the number of samples - - Parameters - ---------- - clients: List - All clients in federated learning job - - Returns - ------- - update_weights : Array-like - final weights use to update model layer - """ - - print("aggregation....") - if not len(clients): - return self.weights - self.total_size = sum([c.num_samples for c in clients]) - old_weight = [np.zeros(np.array(c).shape) for c in next(iter(clients)).weights] - updates = [] - for inx, row in enumerate(old_weight): - for c in clients: - row += np.array(c.weights[inx]) * c.num_samples / self.total_size - updates.append(row.tolist()) - self.weights = deepcopy(updates) - print("finish aggregation....") - return updates diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/algorithm.yaml b/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/algorithm.yaml deleted file mode 100644 index 5c6b87cf..00000000 --- a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/algorithm.yaml +++ /dev/null @@ -1,49 +0,0 @@ -algorithm: - # paradigm name; string type; - # currently the options of value are as follows: - # 1> "singletasklearning" - # 2> "incrementallearning" - paradigm_type: "federatedclassincrementallearning" - fl_data_setting: - # ratio of training dataset; float type; - # the default value is 0.8. - train_ratio: 1.0 - # the method of splitting dataset; string type; optional; - # currently the options of value are as follows: - # 1> "default": the dataset is evenly divided based train_ratio; - splitting_method: "default" - label_data_ratio: 1.0 - data_partition: "iid" - # the url address of initial network for network pre-training; string url; - # the url address of initial network; string type; optional; - initial_model_url: "/home/wyd/ianvs/project/init_model/cnn.pb" - # algorithm module configuration in the paradigm; list type; - # incremental rounds setting of incremental learning; int type; default value is 2; - - modules: - # kind of algorithm module; string type; - # currently the options of value are as follows: - # 1> "basemodel" - - type: "basemodel" - # name of python module; string type; - # example: basemodel.py has BaseModel module that the alias is "FPN" for this benchmarking; - name: "fcil" - # the url address of python module; string type; - url: "./examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/basemodel.py" - - # hyperparameters configuration for the python module; list type; - hyperparameters: - # name of the hyperparameter; string type; - - batch_size: - values: - - 32 - - learning_rate: - values: - - 0.001 - - epochs: - values: - - 1 - - type: "aggregation" - name: "FedAvg" - url: "./examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/aggregation.py" - diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/basemodel.py b/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/basemodel.py deleted file mode 100644 index d9bc71b5..00000000 --- a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/basemodel.py +++ /dev/null @@ -1,162 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import zipfile -import logging -import keras -import numpy as np -import tensorflow as tf -from sedna.common.class_factory import ClassType, ClassFactory -from resnet import resnet10 -from network import NetWork, incremental_learning - -__all__ = ["BaseModel"] -os.environ["BACKEND_TYPE"] = "KERAS" -logging.getLogger().setLevel(logging.INFO) - - -@ClassFactory.register(ClassType.GENERAL, alias="fcil") -class BaseModel: - def __init__(self, **kwargs): - self.kwargs = kwargs - print(f"kwargs: {kwargs}") - self.batch_size = kwargs.get("batch_size", 1) - print(f"batch_size: {self.batch_size}") - self.epochs = kwargs.get("epochs", 1) - self.lr = kwargs.get("lr", 0.001) - self.optimizer = keras.optimizers.SGD(learning_rate=self.lr) - self.old_task_id = -1 - self.fe = resnet10(10) - logging.info(type(self.fe)) - self.model = NetWork(100, self.fe) - self._init_model() - - def _init_model(self): - self.model.compile( - optimizer="sgd", - loss="sparse_categorical_crossentropy", - metrics=["accuracy"], - ) - x = np.random.rand(1, 32, 32, 3) - y = np.random.randint(0, 10, 1) - - self.model.fit(x, y, epochs=1) - - def load(self, model_url=None): - logging.info(f"load model from {model_url}") - extra_model_path = os.path.basename(model_url) + "/model" - with zipfile.ZipFile(model_url, "r") as zip_ref: - zip_ref.extractall(extra_model_path) - self.model = tf.saved_model.load(extra_model_path) - - def _initialize(self): - logging.info(f"initialize finished") - - def get_weights(self): - logging.info(f"get_weights") - weights = [layer.tolist() for layer in self.model.get_weights()] - logging.info(len(weights)) - return weights - - def set_weights(self, weights): - weights = [np.array(layer) for layer in weights] - self.model.set_weights(weights) - logging.info("----------finish set weights-------------") - - def save(self, model_path=""): - logging.info("save model") - - def model_info(self, model_path, result, relpath): - logging.info("model info") - return {} - - def train(self, train_data, valid_data, **kwargs): - round = kwargs.get("round", -1) - self.model.compile( - optimizer=self.optimizer, - loss="sparse_categorical_crossentropy", - metrics=["accuracy"], - ) - logging.info( - f"train data: {train_data['label_x'].shape} {train_data['label_y'].shape}" - ) - train_db = self.data_process(train_data) - logging.info(train_db) - for epoch in range(self.epochs): - total_loss = 0 - total_num = 0 - logging.info(f"Epoch {epoch + 1} / {self.epochs}") - logging.info("-" * 50) - for x, y in train_db: - with tf.GradientTape() as tape: - logits = self.model(x, training=True) - loss = tf.reduce_mean( - keras.losses.sparse_categorical_crossentropy( - y, logits, from_logits=True - ) - ) - grads = tape.gradient(loss, self.model.trainable_variables) - self.optimizer.apply(grads, self.model.trainable_variables) - # self.optimizer.apply_gradients(zip(grads, self.model.trainable_variables)) - total_loss += loss - total_num += 1 - - logging.info( - f"train round {round}: Epoch {epoch + 1} avg loss: {total_loss / total_num}" - ) - logging.info(f"finish round {round} train") - self.eval(train_data, round) - return {"num_samples": train_data["label_x"].shape[0]} - - def predict(self, data, **kwargs): - result = {} - for data in data.x: - x = np.load(data) - logits = self.model(x, training=False) - pred = tf.cast(tf.argmax(logits, axis=1), tf.int32) - result[data] = pred.numpy() - logging.info("finish predict") - return result - - def eval(self, data, round, **kwargs): - total_num = 0 - total_correct = 0 - data = self.data_process(data) - for i, (x, y) in enumerate(data): - logits = self.model(x, training=False) - pred = tf.argmax(logits, axis=1) - pred = tf.cast(pred, dtype=tf.int32) - pred = tf.reshape(pred, y.shape) - correct = tf.cast(tf.equal(pred, y), dtype=tf.int32) - correct = tf.reduce_sum(correct) - total_num += x.shape[0] - total_correct += int(correct) - logging.info(f"total_correct: {total_correct}, total_num: {total_num}") - acc = total_correct / total_num - del total_correct - logging.info(f"finsih round {round}evaluate, acc: {acc}") - return acc - - def data_process(self, data, **kwargs): - - assert data is not None, "data is None" - x_trian = data["label_x"] - y_train = data["label_y"] - # data[0]'shape = (50000, 32,32,3) data[1]'shape = (50000,1) - return ( - tf.data.Dataset.from_tensor_slices((x_trian, y_train)) - .shuffle(100000) - .batch(self.batch_size) - ) diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/network.py b/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/network.py deleted file mode 100644 index 20630623..00000000 --- a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/network.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import keras -import tensorflow as tf -import numpy as np -from keras.src.layers import Dense -from resnet import resnet10 - - -class NetWork(keras.Model): - def __init__(self, num_classes, feature_extractor): - super(NetWork, self).__init__() - self.num_classes = num_classes - self.feature = feature_extractor - self.fc = Dense(num_classes, activation="softmax") - - def call(self, inputs): - x = self.feature(inputs) - x = self.fc(x) - return x - - def feature_extractor(self, inputs): - return self.feature.predict(inputs) - - def predict(self, fea_input): - return self.fc(fea_input) - - def get_config(self): - return { - "num_classes": self.num_classes, - "feature_extractor": self.feature, - } - - @classmethod - def from_config(cls, config): - return cls(**config) - - -def incremental_learning(old_model: NetWork, num_class): - new_model = NetWork(num_class, resnet10(num_class)) - x = np.random.rand(1, 32, 32, 3) - y = np.random.randint(0, num_class, 1) - new_model.compile( - optimizer="sgd", loss="sparse_categorical_crossentropy", metrics=["accuracy"] - ) - new_model.fit(x, y, epochs=1) - print(old_model.fc.units, new_model.fc.units) - for layer in old_model.layers: - if hasattr(new_model.feature, layer.name): - new_model.feature.__setattr__(layer.name, layer) - if num_class > old_model.fc.units: - original_use_bias = hasattr(old_model.fc, "bias") - print("original_use_bias", original_use_bias) - init_class = old_model.fc.units - k = new_model.fc.kernel - new_model.fc.kernel.assign( - tf.pad(old_model.fc.kernel, [[0, 0], [0, num_class - init_class]]) - ) - if original_use_bias: - new_model.fc.bias.assign( - tf.pad(old_model.fc.bias, [[0, num_class - init_class]]) - ) - new_model.build((None, 32, 32, 3)) - return new_model - - -def copy_model(model: NetWork): - cfg = model.get_config() - - copy_model = model.from_config(cfg) - return copy_model diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/resnet.py b/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/resnet.py deleted file mode 100644 index 48db9122..00000000 --- a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/resnet.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tensorflow as tf -import keras - - -# Input--conv2D--BN--ReLU--conv2D--BN--ReLU--Output -# \ / -# ------------------------------ -class BasicBlock(keras.layers.Layer): - def __init__(self, filter_num, stride=1): - super(BasicBlock, self).__init__() - - self.conv1 = keras.layers.Conv2D( - filter_num, (3, 3), strides=stride, padding="same" - ) - self.bn1 = keras.layers.BatchNormalization() - self.relu = keras.layers.Activation("relu") - - self.conv2 = keras.layers.Conv2D(filter_num, (3, 3), strides=1, padding="same") - self.bn2 = keras.layers.BatchNormalization() - - if stride != 1: - self.downsample = keras.models.Sequential() - self.downsample.add(keras.layers.Conv2D(filter_num, (1, 1), strides=stride)) - else: - self.downsample = lambda x: x - - def call(self, inputs, training=None): - # [b, h, w, c] - out = self.conv1(inputs) - out = self.bn1(out, training=training) - out = self.relu(out) - - out = self.conv2(out) - out = self.bn2(out, training=training) - - identity = self.downsample(inputs) - - output = keras.layers.add([out, identity]) - output = tf.nn.relu(output) - - return output - - -class ResNet(keras.Model): - def __init__(self, layer_dims, num_classes=10): # [2, 2, 2, 2] - super(ResNet, self).__init__() - self.layer_dims = layer_dims - self.num_classes = num_classes - - self.stem = keras.models.Sequential( - [ - keras.layers.Conv2D(64, (3, 3), strides=(1, 1)), - keras.layers.BatchNormalization(), - keras.layers.Activation("relu"), - keras.layers.MaxPool2D( - pool_size=(2, 2), strides=(1, 1), padding="same" - ), - ] - ) - - self.layer1 = self.build_resblock(64, layer_dims[0]) - self.layer2 = self.build_resblock(128, layer_dims[1], stride=2) - self.layer3 = self.build_resblock(256, layer_dims[2], stride=2) - self.layer4 = self.build_resblock(512, layer_dims[3], stride=2) - - # output: [b, 512, h, w], - self.avgpool = keras.layers.GlobalAveragePooling2D() - - def call(self, inputs, training=None): - x = self.stem(inputs, training=training) - - x = self.layer1(x, training=training) - x = self.layer2(x, training=training) - x = self.layer3(x, training=training) - x = self.layer4(x, training=training) - - # [b, c] - x = self.avgpool(x) - return x - - def build_resblock(self, filter_num, blocks, stride=1): - res_blocks = keras.models.Sequential() - # may down sample - res_blocks.add(BasicBlock(filter_num, stride)) - for _ in range(1, blocks): - res_blocks.add(BasicBlock(filter_num, stride=1)) - return res_blocks - - def get_config(self): - return {"layer_dims": self.layer_dims, "num_classes": self.num_classes} - - @classmethod - def from_config(cls, config): - return cls(**config) - - -def resnet10(num_classes: int): - return ResNet([1, 1, 1, 1], num_classes) - - -def resnet18(num_classes: int): - return ResNet([2, 2, 2, 2], num_classes) - - -def resnet34(num_classes: int): - return ResNet([3, 4, 6, 3], num_classes) diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/benchmarkingjob.yaml b/examples/cifar100/federated_class_incremental_learning/fedavg/benchmarkingjob.yaml deleted file mode 100644 index 864c9b29..00000000 --- a/examples/cifar100/federated_class_incremental_learning/fedavg/benchmarkingjob.yaml +++ /dev/null @@ -1,71 +0,0 @@ -benchmarkingjob: - # job name of bechmarking; string type; - name: "benchmarkingjob" - # the url address of job workspace that will reserve the output of tests; string type; - workspace: "/home/wyd/ianvs/federated_class_incremental_learning/workspace" - - # the url address of test environment configuration file; string type; - # the file format supports yaml/yml; - testenv: "./examples/cifar100/federated_class_incremental_learning/fedavg/testenv/testenv.yaml" - - # the configuration of test object - test_object: - # test type; string type; - # currently the option of value is "algorithms",the others will be added in succession. - type: "algorithms" - # test algorithm configuration files; list type; - algorithms: - # algorithm name; string type; - - name: "fcil_test" - # the url address of test algorithm configuration file; string type; - # the file format supports yaml/yml - url: "./examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/algorithm.yaml" - - # the configuration of ranking leaderboard - rank: - # rank leaderboard with metric of test case's evaluation and order ; list type; - # the sorting priority is based on the sequence of metrics in the list from front to back; - sort_by: [ { "accuracy": "descend" } ] - - # visualization configuration - visualization: - # mode of visualization in the leaderboard; string type; - # There are quite a few possible dataitems in the leaderboard. Not all of them can be shown simultaneously on the screen. - # In the leaderboard, we provide the "selected_only" mode for the user to configure what is shown or is not shown. - mode: "selected_only" - # method of visualization for selected dataitems; string type; - # currently the options of value are as follows: - # 1> "print_table": print selected dataitems; - method: "print_table" - - # selected dataitem configuration - # The user can add his/her interested dataitems in terms of "paradigms", "modules", "hyperparameters" and "metrics", - # so that the selected columns will be shown. - selected_dataitem: - # currently the options of value are as follows: - # 1> "all": select all paradigms in the leaderboard; - # 2> paradigms in the leaderboard, e.g., "singletasklearning" - paradigms: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all modules in the leaderboard; - # 2> modules in the leaderboard, e.g., "basemodel" - modules: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all hyperparameters in the leaderboard; - # 2> hyperparameters in the leaderboard, e.g., "momentum" - hyperparameters: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all metrics in the leaderboard; - # 2> metrics in the leaderboard, e.g., "F1_SCORE" - metrics: [ "accuracy" ] - - # network of save selected and all dataitems in workspace `./rank` ; string type; - # currently the options of value are as follows: - # 1> "selected_and_all": save selected and all dataitems; - # 2> "selected_only": save selected dataitems; - save_mode: "selected_and_all" - - - - - diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/testenv/__init__.py b/examples/cifar100/federated_class_incremental_learning/fedavg/testenv/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/testenv/acc.py b/examples/cifar100/federated_class_incremental_learning/fedavg/testenv/acc.py deleted file mode 100644 index 0fe532d3..00000000 --- a/examples/cifar100/federated_class_incremental_learning/fedavg/testenv/acc.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tensorflow as tf -import numpy as np -from sedna.common.class_factory import ClassFactory, ClassType - -__all__ = ['acc'] - - -@ClassFactory.register(ClassType.GENERAL, alias='accuracy') -def accuracy(y_true, y_pred, **kwargs): - y_pred_arr = [val for val in y_pred.values()] - y_true_arr = [] - for i in range(len(y_pred_arr)): - y_true_arr.append(np.full(y_pred_arr[i].shape, int(y_true[i]))) - y_pred = tf.cast(tf.convert_to_tensor(np.concatenate(y_pred_arr, axis=0)), tf.int64) - y_true = tf.cast(tf.convert_to_tensor(np.concatenate(y_true_arr, axis=0)), tf.int64) - total = tf.shape(y_true)[0] - correct = tf.reduce_sum(tf.cast(tf.equal(y_true, y_pred), tf.int32)) - acc = float(int(correct) / total) - print(f"acc:{acc}") - return acc - diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/testenv/testenv.yaml b/examples/cifar100/federated_class_incremental_learning/fedavg/testenv/testenv.yaml deleted file mode 100644 index 78ae70cd..00000000 --- a/examples/cifar100/federated_class_incremental_learning/fedavg/testenv/testenv.yaml +++ /dev/null @@ -1,36 +0,0 @@ -testenv: - backend: "tensorflow" - dataset: - name: 'cifar100' - # the url address of train dataset index; string type; - train_url: "/home/wyd/ianvs/project/data/cifar100/cifar100_train.txt" - # the url address of test dataset index; string type; - test_url: "/home/wyd/ianvs/project/data/cifar100/cifar100_test.txt" - - - # network eval configuration of incremental learning; - model_eval: - # metric used for network evaluation - model_metric: - # metric name; string type; - name: "accuracy" - # the url address of python file - url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/federated_class_incremental_learning/fedavg/testenv/acc.py" - - # condition of triggering inference network to update - # threshold of the condition; types are float/int - threshold: 0.01 - # operator of the condition; string type; - # values are ">=", ">", "<=", "<" and "="; - operator: "<=" - - # metrics configuration for test case's evaluation; list type; - metrics: - # metric name; string type; - - name: "accuracy" - # the url address of python file - url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/federated_class_incremental_learning/fedavg/testenv/acc.py" - - # incremental rounds setting of incremental learning; int type; default value is 2; - incremental_rounds: 2 - round: 2 \ No newline at end of file diff --git a/examples/cifar100/federated_learning/fedavg/algorithm/aggregation.py b/examples/cifar100/federated_learning/fedavg/algorithm/aggregation.py deleted file mode 100644 index df1de505..00000000 --- a/examples/cifar100/federated_learning/fedavg/algorithm/aggregation.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import abc -from copy import deepcopy - -import numpy as np -from sedna.algorithms.aggregation.aggregation import BaseAggregation -from sedna.common.class_factory import ClassType, ClassFactory - - -@ClassFactory.register(ClassType.FL_AGG, "FedAvg") -class FedAvg(BaseAggregation, abc.ABC): - def __init__(self): - super(FedAvg, self).__init__() - - def aggregate(self, clients): - """ - Calculate the average weight according to the number of samples - - Parameters - ---------- - clients: - All clients in federated learning job - - Returns - ------- - update_weights : Array-like - final weights use to update model layer - """ - - print("aggregation....") - if not len(clients): - return self.weights - self.total_size = sum([c.num_samples for c in clients]) - old_weight = [np.zeros(np.array(c).shape) for c in next(iter(clients)).weights] - updates = [] - for inx, row in enumerate(old_weight): - for c in clients: - row += np.array(c.weights[inx]) * c.num_samples / self.total_size - updates.append(row.tolist()) - self.weights = deepcopy(updates) - print("finish aggregation....") - return updates diff --git a/examples/cifar100/federated_learning/fedavg/algorithm/algorithm.yaml b/examples/cifar100/federated_learning/fedavg/algorithm/algorithm.yaml deleted file mode 100644 index 7b37eb88..00000000 --- a/examples/cifar100/federated_learning/fedavg/algorithm/algorithm.yaml +++ /dev/null @@ -1,49 +0,0 @@ -algorithm: - # paradigm name; string type; - # currently the options of value are as follows: - # 1> "singletasklearning" - # 2> "incrementallearning" - paradigm_type: "federatedlearning" - fl_data_setting: - # ratio of training dataset; float type; - # the default value is 0.8. - train_ratio: 1.0 - # the method of splitting dataset; string type; optional; - # currently the options of value are as follows: - # 1> "default": the dataset is evenly divided based train_ratio; - splitting_method: "default" - label_data_ratio: 1.0 - data_partition: "iid" - # the url address of initial network for network pre-training; string url; - # the url address of initial network; string type; optional; - initial_model_url: "/home/wyd/ianvs/project/init_model/restnet.pb" - # algorithm module configuration in the paradigm; list type; - # incremental rounds setting of incremental learning; int type; default value is 2; - - modules: - # kind of algorithm module; string type; - # currently the options of value are as follows: - # 1> "basemodel" - - type: "basemodel" - # name of python module; string type; - # example: basemodel.py has BaseModel module that the alias is "FPN" for this benchmarking; - name: "fedavg" - # the url address of python module; string type; - url: "./examples/cifar100/federated_learning/fedavg/algorithm/basemodel.py" - - # hyperparameters configuration for the python module; list type; - hyperparameters: - # name of the hyperparameter; string type; - - batch_size: - values: - - 32 - - learning_rate: - values: - - 0.001 - - epochs: - values: - - 10 - - type: "aggregation" - name: "FedAvg" - url: "./examples/cifar100/federated_learning/fedavg/algorithm/aggregation.py" - diff --git a/examples/cifar100/federated_learning/fedavg/algorithm/basemodel.py b/examples/cifar100/federated_learning/fedavg/algorithm/basemodel.py deleted file mode 100644 index faf9e3fe..00000000 --- a/examples/cifar100/federated_learning/fedavg/algorithm/basemodel.py +++ /dev/null @@ -1,194 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import zipfile - -import keras -import numpy as np -import tensorflow as tf -from keras import Sequential -from keras.src.layers import Conv2D, MaxPooling2D, Flatten, Dropout, Dense -from sedna.common.class_factory import ClassType, ClassFactory - -__all__ = ["BaseModel"] -os.environ["BACKEND_TYPE"] = "KEARS" - - -@ClassFactory.register(ClassType.GENERAL, alias="fedavg") -class BaseModel: - def __init__(self, **kwargs): - self.batch_size = kwargs.get("batch_size", 1) - print(f"batch_size: {self.batch_size}") - self.epochs = kwargs.get("epochs", 1) - self.lr = kwargs.get("lr", 0.001) - self.model = self.build(num_classes=100) - self.optimizer = tf.keras.optimizers.SGD( - learning_rate=self.lr, weight_decay=0.0001 - ) - self._init_model() - - @staticmethod - def build(num_classes: int): - model = Sequential() - model.add( - Conv2D( - 64, - kernel_size=(3, 3), - activation="relu", - strides=(2, 2), - input_shape=(32, 32, 3), - ) - ) - model.add(MaxPooling2D(pool_size=(2, 2))) - model.add(Conv2D(32, kernel_size=(3, 3), activation="relu")) - model.add(MaxPooling2D(pool_size=(2, 2))) - model.add(Flatten()) - model.add(Dropout(0.25)) - model.add(Dense(64, activation="relu")) - model.add(Dense(32, activation="relu")) - model.add(Dropout(0.5)) - model.add(Dense(num_classes, activation="softmax")) - - model.compile( - loss="categorical_crossentropy", optimizer="sgd", metrics=["accuracy"] - ) - return model - - def _init_model(self): - self.model.compile( - optimizer="sgd", - loss="sparse_categorical_crossentropy", - metrics=["accuracy"], - ) - x = np.random.rand(1, 32, 32, 3) - y = np.random.randint(0, 100, 1) - - self.model.fit(x, y, epochs=1) - - def load(self, model_url=None): - print(f"load model from {model_url}") - extra_model_path = os.path.basename(model_url) + "/model" - with zipfile.ZipFile(model_url, "r") as zip_ref: - zip_ref.extractall(extra_model_path) - self.model = tf.saved_model.load(extra_model_path) - - def _initialize(self): - print(f"initialize finished") - - def get_weights(self): - print(f"get_weights") - weights = [layer.tolist() for layer in self.model.get_weights()] - print(len(weights)) - return weights - - def set_weights(self, weights): - weights = [np.array(layer) for layer in weights] - self.model.set_weights(weights) - print("----------finish set weights-------------") - - def save(self, model_path=""): - print("save model") - - def model_info(self, model_path, result, relpath): - print("model info") - return {} - - def train(self, train_data, valid_data, **kwargs): - round = kwargs.get("round", -1) - print(f"train data: {train_data[0].shape} {train_data[1].shape}") - train_db = self.data_process(train_data) - print(train_db) - for epoch in range(self.epochs): - total_loss = 0 - total_num = 0 - print(f"Epoch {epoch + 1} / {self.epochs}") - print("-" * 50) - for x, y in train_db: - with tf.GradientTape() as tape: - logits = self.model(x, training=True) - y_pred = tf.cast(tf.argmax(logits, axis=1), tf.int32) - correct = tf.equal(y_pred, y) - correct = tf.cast(correct, dtype=tf.int32) - correct = tf.reduce_sum(correct) - y = tf.one_hot(y, depth=100) - # y = tf.squeeze(y, axis=1) - loss = tf.reduce_mean( - keras.losses.categorical_crossentropy( - y, logits, from_logits=True - ) - ) - print( - f"loss is {loss}, correct {correct} total is {x.shape[0]} acc : {correct / x.shape[0]}" - ) - grads = tape.gradient(loss, self.model.trainable_variables) - self.optimizer.apply(grads, self.model.trainable_variables) - total_loss += loss - total_num += 1 - - print( - f"train round {round}: Epoch {epoch + 1} avg loss: {total_loss / total_num}" - ) - print(f"finish round {round} train") - return {"num_samples": train_data[0].shape[0]} - - def predict(self, data, **kwargs): - result = {} - mean = np.array((0.5071, 0.4867, 0.4408), np.float32).reshape(1, 1, -1) - std = np.array((0.2675, 0.2565, 0.2761), np.float32).reshape(1, 1, -1) - for data in data.x: - x = np.load(data) - x = (tf.cast(x, dtype=tf.float32) / 255.0 - mean) / std - logits = self.model(x, training=False) - pred = tf.cast(tf.argmax(logits, axis=1), tf.int32) - result[data] = pred.numpy() - print("finish predict") - return result - - def eval(self, data, round, **kwargs): - total_num = 0 - total_correct = 0 - data = self.data_process(data) - print(f"in evalute data: {data}") - for i, (x, y) in enumerate(data): - logits = self.model(x, training=False) - pred = tf.argmax(logits, axis=1) - pred = tf.cast(pred, dtype=tf.int32) - pred = tf.reshape(pred, y.shape) - correct = tf.cast(tf.equal(pred, y), dtype=tf.int32) - correct = tf.reduce_sum(correct) - total_num += x.shape[0] - total_correct += int(correct) - print(f"total_correct: {total_correct}, total_num: {total_num}") - acc = total_correct / total_num - del total_correct - print(f"finsih round {round}evaluate, acc: {acc}") - return acc - - def data_process(self, data, **kwargs): - mean = np.array((0.5071, 0.4867, 0.4408), np.float32).reshape(1, 1, -1) - std = np.array((0.2675, 0.2565, 0.2761), np.float32).reshape(1, 1, -1) - assert data is not None, "data is None" - # data[0]'shape = (50000, 32,32,3) data[1]'shape = (50000,1) - return ( - tf.data.Dataset.from_tensor_slices((data[0][:5000], data[1][:5000])) - .shuffle(100000) - .map( - lambda x, y: ( - (tf.cast(x, dtype=tf.float32) / 255.0 - mean) / std, - tf.cast(y, dtype=tf.int32), - ) - ) - .batch(self.batch_size) - ) diff --git a/examples/cifar100/federated_learning/fedavg/benchmarkingjob.yaml b/examples/cifar100/federated_learning/fedavg/benchmarkingjob.yaml deleted file mode 100644 index 6f6e794f..00000000 --- a/examples/cifar100/federated_learning/fedavg/benchmarkingjob.yaml +++ /dev/null @@ -1,71 +0,0 @@ -benchmarkingjob: - # job name of bechmarking; string type; - name: "benchmarkingjob" - # the url address of job workspace that will reserve the output of tests; string type; - workspace: "/home/wyd/ianvs/federated_learning/workspace" - - # the url address of test environment configuration file; string type; - # the file format supports yaml/yml; - testenv: "./examples/cifar100/federated_learning/fedavg/testenv/testenv.yaml" - - # the configuration of test object - test_object: - # test type; string type; - # currently the option of value is "algorithms",the others will be added in succession. - type: "algorithms" - # test algorithm configuration files; list type; - algorithms: - # algorithm name; string type; - - name: "fedavg_test" - # the url address of test algorithm configuration file; string type; - # the file format supports yaml/yml - url: "./examples/cifar100/federated_learning/fedavg/algorithm/algorithm.yaml" - - # the configuration of ranking leaderboard - rank: - # rank leaderboard with metric of test case's evaluation and order ; list type; - # the sorting priority is based on the sequence of metrics in the list from front to back; - sort_by: [ { "accuracy": "descend" } ] - - # visualization configuration - visualization: - # mode of visualization in the leaderboard; string type; - # There are quite a few possible dataitems in the leaderboard. Not all of them can be shown simultaneously on the screen. - # In the leaderboard, we provide the "selected_only" mode for the user to configure what is shown or is not shown. - mode: "selected_only" - # method of visualization for selected dataitems; string type; - # currently the options of value are as follows: - # 1> "print_table": print selected dataitems; - method: "print_table" - - # selected dataitem configuration - # The user can add his/her interested dataitems in terms of "paradigms", "modules", "hyperparameters" and "metrics", - # so that the selected columns will be shown. - selected_dataitem: - # currently the options of value are as follows: - # 1> "all": select all paradigms in the leaderboard; - # 2> paradigms in the leaderboard, e.g., "singletasklearning" - paradigms: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all modules in the leaderboard; - # 2> modules in the leaderboard, e.g., "basemodel" - modules: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all hyperparameters in the leaderboard; - # 2> hyperparameters in the leaderboard, e.g., "momentum" - hyperparameters: [ "all" ] - # currently the options of value are as follows: - # 1> "all": select all metrics in the leaderboard; - # 2> metrics in the leaderboard, e.g., "F1_SCORE" - metrics: [ "accuracy" ] - - # network of save selected and all dataitems in workspace `./rank` ; string type; - # currently the options of value are as follows: - # 1> "selected_and_all": save selected and all dataitems; - # 2> "selected_only": save selected dataitems; - save_mode: "selected_and_all" - - - - - diff --git a/examples/cifar100/federated_learning/fedavg/testenv/__init__.py b/examples/cifar100/federated_learning/fedavg/testenv/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/cifar100/federated_learning/fedavg/testenv/acc.py b/examples/cifar100/federated_learning/fedavg/testenv/acc.py deleted file mode 100644 index e7e9fde9..00000000 --- a/examples/cifar100/federated_learning/fedavg/testenv/acc.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import tensorflow as tf -import numpy as np -from sedna.common.class_factory import ClassFactory, ClassType - -__all__ = ["acc"] - - -@ClassFactory.register(ClassType.GENERAL, alias="accuracy") -def accuracy(y_true, y_pred, **kwargs): - y_pred_arr = [val for val in y_pred.values()] - y_true_arr = [] - for i in range(len(y_pred_arr)): - y_true_arr.append(np.full(y_pred_arr[i].shape, int(y_true[i]))) - y_pred = tf.cast(tf.convert_to_tensor(np.concatenate(y_pred_arr, axis=0)), tf.int64) - y_true = tf.cast(tf.convert_to_tensor(np.concatenate(y_true_arr, axis=0)), tf.int64) - total = tf.shape(y_true)[0] - correct = tf.reduce_sum(tf.cast(tf.equal(y_true, y_pred), tf.int32)) - acc = float(int(correct) / total) - print(f"acc:{acc}") - return acc diff --git a/examples/cifar100/federated_learning/fedavg/testenv/testenv.yaml b/examples/cifar100/federated_learning/fedavg/testenv/testenv.yaml deleted file mode 100644 index 4e3912b1..00000000 --- a/examples/cifar100/federated_learning/fedavg/testenv/testenv.yaml +++ /dev/null @@ -1,37 +0,0 @@ -testenv: - backend: "tensorflow" - dataset: - name: 'cifar100' - # the url address of train dataset index; string type; - train_url: "/home/wyd/ianvs/project/data/cifar100/cifar100_train.txt" - # the url address of test dataset index; string type; - test_url: "/home/wyd/ianvs/project/data/cifar100/cifar100_test.txt" - - - # network eval configuration of incremental learning; - model_eval: - # metric used for network evaluation - model_metric: - # metric name; string type; - name: "accuracy" - # the url address of python file - url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/federated_learning/fedavg/testenv/acc.py" - - # condition of triggering inference network to update - # threshold of the condition; types are float/int - threshold: 0.01 - # operator of the condition; string type; - # values are ">=", ">", "<=", "<" and "="; - operator: "<=" - - # metrics configuration for test case's evaluation; list type; - metrics: - # metric name; string type; - - name: "accuracy" - # the url address of python file - url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/federated_learning/fedavg/testenv/acc.py" - - # incremental rounds setting of incremental learning; int type; default value is 2; - task_size: 10 - incremental_rounds: 10 - round: 200 \ No newline at end of file diff --git a/examples/cifar100/sedna_federated_learning/aggregation_worker/aggregate.py b/examples/cifar100/sedna_federated_learning/aggregation_worker/aggregate.py deleted file mode 100644 index 26eabb65..00000000 --- a/examples/cifar100/sedna_federated_learning/aggregation_worker/aggregate.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from sedna.common.config import Context -from sedna.service.server import AggregationServer - - -def run_server(): - aggregation_algorithm = Context.get_parameters("aggregation_algorithm", "FedAvg") - exit_round = int(Context.get_parameters("exit_round", 3)) - participants_count = int(Context.get_parameters("participants_count", 1)) - - server = AggregationServer( - aggregation=aggregation_algorithm, - exit_round=exit_round, - ws_size=20 * 1024 * 1024, - participants_count=participants_count, - host="127.0.0.1", - ) - server.start() - - -if __name__ == "__main__": - run_server() diff --git a/examples/cifar100/sedna_federated_learning/train_worker/basemodel.py b/examples/cifar100/sedna_federated_learning/train_worker/basemodel.py deleted file mode 100644 index 60ed50e9..00000000 --- a/examples/cifar100/sedna_federated_learning/train_worker/basemodel.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import tensorflow as tf -import numpy as np -from keras.src.layers import Dense, MaxPooling2D, Conv2D, Flatten, Dropout -from keras.src.models import Sequential - -os.environ["BACKEND_TYPE"] = "KERAS" - - -class Estimator: - def __init__(self, **kwargs): - """Model init""" - - self.model = self.build() - self.has_init = False - - @staticmethod - def build(): - model = Sequential() - model.add( - Conv2D( - 64, - kernel_size=(3, 3), - activation="relu", - strides=(2, 2), - input_shape=(32, 32, 3), - ) - ) - model.add(MaxPooling2D(pool_size=(2, 2))) - model.add(Conv2D(32, kernel_size=(3, 3), activation="relu")) - model.add(MaxPooling2D(pool_size=(2, 2))) - model.add(Flatten()) - model.add(Dropout(0.25)) - model.add(Dense(64, activation="relu")) - model.add(Dense(32, activation="relu")) - model.add(Dropout(0.5)) - model.add(Dense(1, activation="softmax")) - - model.compile( - loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"] - ) - return model - - def train( - self, - train_data, - valid_data=None, - epochs=1, - batch_size=1, - learning_rate=0.01, - validation_split=0.2, - ): - """Model train""" - train_loader = ( - tf.data.Dataset.from_tensor_slices(train_data) - .shuffle(500000) - .batch(batch_size) - ) - history = self.model.fit(train_loader, epochs=int(epochs)) - return {k: list(map(np.float, v)) for k, v in history.history.items()} - - def get_weights(self): - return self.model.get_weights() - - def set_weights(self, weights): - self.model.set_weights(weights) - - def load_weights(self, model): - if not os.path.isfile(model): - return - return self.model.load_weights(model) - - def predict(self, datas): - return self.model.predict(datas) - - def evaluate(self, test_data, **kwargs): - pass - - def load(self, model_url): - print("load model") - - def save(self, model_path=None): - """ - save model as a single pb file from checkpoint - """ - print("save model") diff --git a/examples/cifar100/sedna_federated_learning/train_worker/train.py b/examples/cifar100/sedna_federated_learning/train_worker/train.py deleted file mode 100644 index 5471708d..00000000 --- a/examples/cifar100/sedna_federated_learning/train_worker/train.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from sedna.core.federated_learning import FederatedLearning -from sedna.datasources import TxtDataParse -import numpy as np -from basemodel import Estimator - - -def read_data_from_file_to_npy(files): - """ - read data from file to numpy array - - Parameters - --------- - files: list - the address url of data file. - - Returns - ------- - list - data in numpy array. - - """ - x_train = [] - y_train = [] - for i, file in enumerate(files.x): - x = np.load(file) - y = np.full((x.shape[0], 1), (files.y[i]).astype(np.int32)) - x_train.append(x) - y_train.append(y) - x_train = np.concatenate(x_train, axis=0) - y_train = np.concatenate(y_train, axis=0) - print(x_train.shape, y_train.shape) - return x_train, y_train - - -def main(): - train_file = "/home/wyd/ianvs/project/data/cifar100/cifar100_train.txt" - train_data = TxtDataParse(data_type="train") - train_data.parse(train_file) - train_data = read_data_from_file_to_npy(train_data) - epochs = 3 - batch_size = 128 - fl_job = FederatedLearning(estimator=Estimator(), aggregation="FedAvg") - fl_job.train(train_data=train_data, epochs=epochs, batch_size=batch_size) - - -if __name__ == "__main__": - main() diff --git a/examples/cifar100/utils.py b/examples/cifar100/utils.py deleted file mode 100644 index 57333e20..00000000 --- a/examples/cifar100/utils.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright 2021 The KubeEdge Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tensorflow as tf -import numpy as np -import os - - -def process_cifar100(): - if not os.path.exists("/home/wyd/ianvs/project/data/cifar100"): - os.makedirs("/home/wyd/ianvs/project/data/cifar100") - train_txt = "/home/wyd/ianvs/project/data/cifar100/cifar100_train.txt" - with open(train_txt, "w") as f: - pass - test_txt = "/home/wyd/ianvs/project/data/cifar100/cifar100_test.txt" - with open(test_txt, "w") as f: - pass - # load CIFAR-100 dataset - (x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar100.load_data() - print(y_test.shape) - - # change label to class index - class_labels = np.unique(y_train) # get all class - train_class_dict = {label: [] for label in class_labels} - test_class_dict = {label: [] for label in class_labels} - # organize training data by category - for img, label in zip(x_train, y_train): - train_class_dict[label[0]].append(img) - # organize testing data by category - for img, label in zip(x_test, y_test): - # test_class_dict[label[0]].append(img) - test_class_dict[label[0]].append(img) - # save training data to local file - for label, imgs in train_class_dict.items(): - data = np.array(imgs) - print(data.shape) - np.save( - f"/home/wyd/ianvs/project/data/cifar100/cifar100_train_index_{label}.npy", - data, - ) - with open(train_txt, "a") as f: - f.write( - f"/home/wyd/ianvs/project/data/cifar100/cifar100_train_index_{label}.npy\t{label}\n" - ) - # save test data to local file - for label, imgs in test_class_dict.items(): - np.save( - f"/home/wyd/ianvs/project/data/cifar100/cifar100_test_index_{label}.npy", - np.array(imgs), - ) - with open(test_txt, "a") as f: - f.write( - f"/home/wyd/ianvs/project/data/cifar100/cifar100_test_index_{label}.npy\t{label}\n" - ) - print(f"CIFAR-100 have saved as ianvs format") - - -if __name__ == "__main__": - process_cifar100()