diff --git a/core/common/constant.py b/core/common/constant.py index 2c5adcce..33b5087d 100644 --- a/core/common/constant.py +++ b/core/common/constant.py @@ -77,6 +77,8 @@ class SystemMetricType(Enum): BWT = "BWT" TASK_AVG_ACC = "task_avg_acc" MATRIX = "MATRIX" + FORGET_RATE = "FORGET_RATE" + class TestObjectType(Enum): diff --git a/core/testcasecontroller/algorithm/module/module.py b/core/testcasecontroller/algorithm/module/module.py index 6e58a313..1772725e 100644 --- a/core/testcasecontroller/algorithm/module/module.py +++ b/core/testcasecontroller/algorithm/module/module.py @@ -128,7 +128,6 @@ 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/federated_learning/federated_class_incremental_learning.py b/core/testcasecontroller/algorithm/paradigm/federated_learning/federated_class_incremental_learning.py index 4e3b078e..e5171c6f 100644 --- a/core/testcasecontroller/algorithm/paradigm/federated_learning/federated_class_incremental_learning.py +++ b/core/testcasecontroller/algorithm/paradigm/federated_learning/federated_class_incremental_learning.py @@ -15,11 +15,12 @@ """Federated Class-Incremental Learning Paradigm""" import numpy as np from core.common.constant import ParadigmType +from core.common.utils import get_file_format from .federated_learning import FederatedLearning from sedna.algorithms.aggregation import AggClient from core.common.log import LOGGER from threading import Thread, RLock - +from core.testcasecontroller.metrics.metrics import get_metric_func class FederatedClassIncrementalLearning(FederatedLearning): """ @@ -55,6 +56,9 @@ def __init__(self, workspace, **kwargs): self.lock = RLock() self.aggregate_clients=[] self.train_infos=[] + self.forget_rate_metrics = [] + self.accuracy_per_round = [] + self.metrics_dict = kwargs.get('model_eval', {})['model_metric'] def get_task_size(self, train_datasets): return np.unique([train_datasets[i][1] for i in range(len(train_datasets))]).shape[0] @@ -63,24 +67,29 @@ def task_definition(self, dataset_files, task_id): """ Define the task for the class incremental learning paradigm """ + 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 - need_split_label_unlabel_data = 1.0 - self.fl_data_setting.get("label_data_ratio") > 1e-6 - if need_split_label_unlabel_data: - train_datasets = self.split_label_unlabel_data(train_datasets) + # need_split_label_unlabel_data = 1.0 - self.fl_data_setting.get("label_data_ratio") > 1e-6 + # if need_split_label_unlabel_data: + 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 split_label_unlabel_data(self, train_datasets): - label_ratio = self.fl.data_setting.get("label_data_ratio") + label_ratio = self.fl_data_setting.get("label_data_ratio") new_train_datasets = [] for i in range(len(train_datasets)): train_dataset_dict = {} + LOGGER.info(f"train_datasets[i][0]: {train_datasets[i][0].shape}, {len(train_datasets[i])}") label_data_number = int(label_ratio * len(train_datasets[i][0])) + LOGGER.info(f"label_data_number: {label_data_number}") # 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] @@ -98,8 +107,12 @@ def run(self): # split_time = self.rounds // self.task_size # split the dataset into several tasks # print(f'split_time: {split_time}') 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}') + 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}") self._train(train_datasets, task_id=task_id, round=r, task_size=task_size) @@ -109,10 +122,35 @@ def run(self): self.send_weights_to_clients(global_weights) self.aggregate_clients.clear() self.train_infos.clear() + # test_res = self.predict(self.dataset.test_url) + # self.system_metric_info = 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): + + 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):] + # new_dataset = (test_dataset.x[step * (index - 1):], 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] + # new_dataset = (test_dataset.x[step * (index - 1):step * index], test_dataset.y[step * (index - 1):step * index]) + test_datasets_files.append(new_dataset) + index += 1 + return test_datasets_files + + def train_data_partition(self, train_dataset_file): return super().train_data_partition(train_dataset_file) @@ -130,7 +168,7 @@ def client_train(self, client_idx, train_datasets, validation_datasets, **kwargs def _train(self, train_datasets, **kwargs): client_threads = [] - print(f'len(self.clients): {len(self.clients)}') + print(f'len(self.clients): {len(self.clients)} len train_datasets: {len(train_datasets)}') for idx in range(len(self.clients)): client_thread = Thread(target=self.client_train, args=(idx, train_datasets, None), kwargs=kwargs) client_thread.start() @@ -148,5 +186,51 @@ def helper_function(self,train_infos): helper_info = self.aggregator.helper_function(train_infos[i]) self.clients[i].helper_function(helper_info) LOGGER.info('finish helper function') + + def evaluation(self, testdataset_files, incremental_round): + _, accuracy_func = get_metric_func(self.metrics_dict) + LOGGER.info('*'*20 +'start evaluation' + '*'*20) + if isinstance(testdataset_files, str): + testdataset_files = [testdataset_files] + job = self.get_global_model() + # caculate the old 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 = accuracy_func([testdataset_files[index]['y'][data_index]], res ) + acc_list.append(acc) + old_class_acc_list.extend(acc_list) + # old_classes.extend(current_classes) + current_forget_rate = 0.0 + max_acc_sum = 0 + self.accuracy_per_round.append(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] + LOGGER.info(f'acc_per_round: {acc_per_round} and len is {len(acc_per_round)}') + if i < len(acc_per_round): + LOGGER.info(f'acc_per_round: {acc_per_round[i]} and diff is {acc_per_round[i] - old_class_acc_list[i]}') + max_acc_diff = max(max_acc_diff, acc_per_round[i] - old_class_acc_list[i]) + max_acc_sum += max_acc_diff + LOGGER.info(f'max_acc_diff: {max_acc_diff}') + current_forget_rate = max_acc_sum / len(old_class_acc_list) if incremental_round > 0 else 0.0 - \ No newline at end of file + LOGGER.info(f'for current round: {incremental_round} forget rate: {current_forget_rate}') + self.forget_rate_metrics.append(current_forget_rate) + # caculate the new class accuracy + new_classes_acc = 0 + for index in range(len(testdataset_files[-1]['x'])): + data = testdataset_files[-1]['x'][index] + LOGGER.info(data) + res = job.inference([data]) + acc = accuracy_func([testdataset_files[-1]['y'][index]], res) + new_classes_acc += acc + new_classes_acc = new_classes_acc / float(len(testdataset_files[-1]['x'])) + LOGGER.info(f'for current round : {incremental_round} new_classes_acc: {new_classes_acc} {self.accuracy_per_round}') + LOGGER.info('*'*20 +'finish evaluation' + '*'*20) diff --git a/core/testcasecontroller/algorithm/paradigm/federated_learning/federated_learning.py b/core/testcasecontroller/algorithm/paradigm/federated_learning/federated_learning.py index 49ef2a97..cd12b9b9 100644 --- a/core/testcasecontroller/algorithm/paradigm/federated_learning/federated_learning.py +++ b/core/testcasecontroller/algorithm/paradigm/federated_learning/federated_learning.py @@ -176,7 +176,7 @@ def predict(self, test_dataset_file): test_dataset.append(self.dataset.load_data(file, "eval")) assert test_dataset is not None, "test_dataset is None" job = self.get_global_model() - test_res = job.inference(test_dataset) + test_res = job.inference(test_dataset.x) LOGGER.info(f" after predict {len(test_res)}") return test_res diff --git a/core/testcasecontroller/metrics/metrics.py b/core/testcasecontroller/metrics/metrics.py index 4e9c886a..9f61b7eb 100644 --- a/core/testcasecontroller/metrics/metrics.py +++ b/core/testcasecontroller/metrics/metrics.py @@ -143,6 +143,13 @@ def task_avg_acc_func(system_metric_info: dict): info = system_metric_info.get(SystemMetricType.TASK_AVG_ACC.value) return info["accuracy"] +def task_forget_rate(system_metric_info: dict): + """ + compute task forget rate + """ + info = system_metric_info.get(SystemMetricType.TASK_FORGET_RATE.value) + return info["forget_rate"][-1] + def get_metric_func(metric_dict: dict): """ diff --git a/core/testenvmanager/dataset/utils.py b/core/testenvmanager/dataset/utils.py index 0878d75a..9534ebc2 100644 --- a/core/testenvmanager/dataset/utils.py +++ b/core/testenvmanager/dataset/utils.py @@ -33,10 +33,10 @@ def read_data_from_file_to_npy( files: BaseDataSource): """ x_train = [] y_train = [] - # print(files.x, files.y) + print(files.x, files.y) for i, file in enumerate(files.x): x = np.load(file) - # print(x.shape) + print(x.shape) # print(type(files.y[i])) y = np.full((x.shape[0], 1), (files.y[i]).astype(np.int32)) x_train.append(x) @@ -69,6 +69,7 @@ def partition_data(datasets, client_number, data_partition ='iid', non_iid_ratio if data_partition == 'iid': x_data = datasets[0] y_data = datasets[1] + print(f'x_data shape: {x_data.shape}, y_data shape: {y_data.shape}') indices = np.arange(len(x_data)) np.random.shuffle(indices) data = [] diff --git a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/FedCiMatch.py b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/FedCiMatch.py new file mode 100644 index 00000000..8437aeb8 --- /dev/null +++ b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/FedCiMatch.py @@ -0,0 +1,282 @@ +# 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 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): + # print(f'in get one hot, target shape is {target.shape}') + y = tf.one_hot(target, depth=num_classes) + # print(f'in get one hot, y shape is {y.shape}') + if len(y.shape) == 3: + y = tf.squeeze(y, axis=1) + # print(f'in get one hot, after tf.squeeze y shape is {y.shape}') + 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() + + 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 = [] + print(f'self epoch is {self.epochs}') + + def build_feature_extractor(self): + feature_extractor = resnet10() + 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([ + # tf.keras.Input(shape=(None, self.feature_extractor.layers[-2].output_shape[-1])), + 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([ + # tf.keras.Input(shape=(None, self.feature_extractor.layers[-2].output_shape[-1])), + 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 + if is_new_task: + 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_exemplar(is_new_task) + 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] + print(f'train_x shape: {train_x.shape} and train_y shape: {train_y.shape}') + if len(self.exemplar_set) != 0: + for exm_set in self.exemplar_set: + # print('in get train loader' , exm_set[0].shape) + train_x = np.concatenate((train_x, exm_set[0]), axis=0) + label = np.array(exm_set[1]) + label = label.reshape(-1, 1) + train_y = np.concatenate((train_y, label), axis=0) + print(f'unlabel_x shape: {self.unlabeled_train_set[0].shape} and unlabel_y shape: {self.unlabeled_train_set[1].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) + return label_data_loader, unlabel_data_loader + + + def build_exemplar(self, is_new_task): + if is_new_task and self.last_classes is not None: + 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) + + 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) + now_class_mean = np.zeros((1, fe_ouput.shape[1])) + for i in range(m): + x = class_mean - (now_class_mean + fe_ouput)/(i+1) + x = np.linalg.norm(x) + index = np.argmin(x) + now_class_mean += fe_ouput[index] + exemplar_data.append(images[index]) + exemplar_label.append(class_id) + 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.) + fe_output = self.feature_extractor.predict(images_data) + print('fe_output shape:', fe_output.shape) + fe_output = tf.nn.l2_normalize(fe_output).numpy() + class_mean = np.mean(fe_output, axis=0) + return class_mean, fe_output + + def train(self, round): + + optimizer = keras.optimizers.SGD(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) + # all_params = [] + # all_params.extend(self.feature_extractor.trainable_variables) + # all_params.extend(self.classifier.trainable_variables) + + for epoch in range(self.epochs): + # 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): + # print(labeled_data.shape) + # labeled_x, labeled_y = labeled_data + # unlabeled_x, weak_unlabeled_x, strong_unlabeled_x, unlabeled_y = unlabeled_data + with tf.GradientTape() as tape: + input = self.feature_extractor(inputs=labeled_x,training=True) + y_pred = self.classifier(inputs=input, training=True) + # target = get_one_hot(labeled_y, self.num_classes) + 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) + # logging.info(f"{label_pred.numpy()}") + correct = tf.reduce_sum(tf.cast(tf.equal(label_pred, labeled_y), dtype=tf.int32)) + loss = tf.reduce_mean(keras.losses.sparse_categorical_crossentropy(labeled_y, y_pred, from_logits=True)) + # if round > self.warm_up_round: + # unsupervised_loss = self.unsupervised_loss(weak_unlabeled_x, strong_unlabeled_x, unlabeled_x) + # loss = 0.5 * supervised_loss + 0.5 * unsupervised_loss + logging.info(f"epoch {epoch} step {step} loss: {loss} correct {correct} and total {labeled_x.shape[0]}") + grads = tape.gradient(loss, all_params) + optimizer.apply_gradients(zip(grads, all_params)) + + def supervised_loss(self, x, y): + x = self.feature_extractor(x,training=True) + y_pred = tf.nn.softmax(self.classifier(x, training=True)) + # y_pred = tf.nn.softmax( self.model_call(x, training=True)) + target = get_one_hot(y, self.num_classes) + # logits = y_pred + # # prob = tf.nn.softmax(logits, axis=1) + # pred = tf.argmax(logits, axis=1) + # pred = tf.cast(pred, dtype=tf.int32) + # pred = tf.reshape(pred, y.shape) + + # labels = tf.cast(y, dtype=tf.int32) + # correct = tf.cast(tf.equal(pred, labels), 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/x.shape[0]}') + loss = tf.reduce_mean(keras.losses.categorical_crossentropy(target, y_pred, from_logits=True) ) + + return loss + + + def unsupervised_loss(self, weak_x, strong_x, x): + prob_on_wux = tf.nn.softmax(self.model_call(weak_x, 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) + prob_on_sux = tf.nn.softmax(self.model_call(strong_x, training=True)) + loss = keras.losses.categorical_crossentropy(pse_uy, prob_on_sux, from_logits=True) + 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. - mean) /std + x = self.feature_extractor(x, training=False) + y_pred = tf.nn.softmax(self.classifier(x, training=False)) + # logging.info(f"y_pred is {y_pred}") + pred = tf.cast(tf.argmax(y_pred, axis=1), tf.int32) + return pred \ No newline at end of file diff --git a/examples/cifar100/fci_ssl/fed_ci_match/algorithm/aggregation.py b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/aggregation.py new file mode 100644 index 00000000..17eff805 --- /dev/null +++ b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/aggregation.py @@ -0,0 +1,57 @@ +# 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]) + # print(next(iter(clients)).weights) + 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 new file mode 100644 index 00000000..f4bb7763 --- /dev/null +++ b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/agumentation.py @@ -0,0 +1,221 @@ +# 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) + if self.dataset_name in ['cifar10', 'cifar100']: + self.augment_impl = self.augment_for_cifar + elif self.dataset_name == 'svhn': + self.augment_impl = self.augment_for_svhn + elif self.dataset_name == 'stl10': + self.augment_impl = self.augment_for_stl10 + elif self.dataset_name == 'mnist': + self.augment_impl = self.augment_for_mnist + + 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): + ''' + 随机平移图像 + ''' + # 根据[b, 32, 32, 3]或[b, 96, 96, 3]填充图像 + 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_shift_mnist(self, x, w): + ''' + 随机平移图像 + ''' + # 根据[b, 32, 32, 3]或[b, 96, 96, 3]填充图像 + y = tf.pad(x, [[0] * 2, [w] * 2, [w] * 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 augment_for_svhn(self, images: np.ndarray): + return self.augment_shift(images, 4) + + def augment_for_stl10(self, images: np.ndarray): + return self.augment_shift(self.augment_mirror(images), 12) + + def augment_for_mnist(self, images: np.ndarray): + return self.augment_shift_mnist(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): + ''' + 随机平移图像 + ''' + # 根据[b, 32, 32, 3]或[b, 96, 96, 3]填充图像 + 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() + if self.dataset_name in ['cifar10', 'cifar100', 'svhn']: + self.input_shape = (32, 32, 3) + elif self.dataset_name == 'stl10': + self.input_shape = (96, 96, 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 new file mode 100644 index 00000000..dc947768 --- /dev/null +++ b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/algorithm.yaml @@ -0,0 +1,27 @@ +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/algorithm/basemodel.py" + hyperparameters: + - batch_size: + values: + - 128 + - learning_rate: + values: + - 0.001 + - epochs: + values: + - 4 + - 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 new file mode 100644 index 00000000..19c64f92 --- /dev/null +++ b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/basemodel.py @@ -0,0 +1,68 @@ +# 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 +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', 1000) + # self.fe = self.build_feature_extractor() + self.num_classes = 10 # 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) + 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) + res = self.FedCiMatch.predict(x) + # pred = tf.cast(tf.argmax(logits, axis=1), tf.int32) + 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 new file mode 100644 index 00000000..37a8ee18 --- /dev/null +++ b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/data_prepocessor.py @@ -0,0 +1,56 @@ +# 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. - 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. - 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/algorithm/model.py b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/model.py new file mode 100644 index 00000000..db7e875c --- /dev/null +++ b/examples/cifar100/fci_ssl/fed_ci_match/algorithm/model.py @@ -0,0 +1,157 @@ +# 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 +# import keras +# from keras import layers, Sequential + +# 卷积块 +# 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]) \ No newline at end of file diff --git a/examples/cifar100/fci_ssl/fed_ci_match/benchmarkingjob.yaml b/examples/cifar100/fci_ssl/fed_ci_match/benchmarkingjob.yaml index e69de29b..914ed23c 100644 --- a/examples/cifar100/fci_ssl/fed_ci_match/benchmarkingjob.yaml +++ b/examples/cifar100/fci_ssl/fed_ci_match/benchmarkingjob.yaml @@ -0,0 +1,71 @@ +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: "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/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/fci_ssl/fed_ci_match/test_train.py b/examples/cifar100/fci_ssl/fed_ci_match/test_train.py new file mode 100644 index 00000000..9f086f1a --- /dev/null +++ b/examples/cifar100/fci_ssl/fed_ci_match/test_train.py @@ -0,0 +1,141 @@ +from sedna.datasources import TxtDataParse +import numpy as np +import tensorflow as tf +from algorithm.model import resnet10 +import keras +import logging +logging.getLogger().setLevel(logging.INFO) +def build_classifier(feature_extractor): + classifier = keras.Sequential([ + # tf.keras.Input(shape=(None, self.feature_extractor.layers[-2].output_shape[-1])), + keras.layers.Dense(10, kernel_initializer='lecun_normal') + ]) + classifier.build(input_shape=(None, feature_extractor.layers[-2].output_shape[-1])) + logging.info(f"finish ! initialize classifier {classifier.summary()}") + return classifier +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 = [] + print(files.x, files.y) + for i, file in enumerate(files.x[:10]): + x = np.load(file) + # print(x.shape) + # print(type(files.y[i])) + 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 train(feature_extractor, classifier, + train_data, valid_data=None, + epochs=60, + batch_size=128, + learning_rate=0.01, + validation_split=0.2): + """ Model train """ + 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) + all_parameter = [] + all_parameter.extend(feature_extractor.trainable_variables) + all_parameter.extend(classifier.trainable_variables) + optimizer = tf.keras.optimizers.SGD(learning_rate=learning_rate) + train_loader = tf.data.Dataset.from_tensor_slices(train_data).shuffle(500000).map( + lambda x,y:( + (tf.cast(x, dtype=tf.float32) / 255. - mean) / std, + tf.cast(y, dtype=tf.int32) + ) + ).batch(batch_size) + for epoch in range(epochs): + epoch_loss = 0 + step = 0 + for _, (x, y) in enumerate(train_loader): + with tf.GradientTape() as tape: + feature = feature_extractor(x) + logits = tf.nn.softmax(classifier(feature)) + loss = tf.reduce_mean(keras.losses.sparse_categorical_crossentropy(y , logits)) + step += 1 + logging.info(f"epoch {epoch} step {step} loss: {loss}") + epoch_loss += loss + grads = tape.gradient(loss, all_parameter) + optimizer.apply_gradients(zip(grads, all_parameter)) + logging.info(f"epoch {epoch} loss: {epoch_loss/step}") + +def evaluate(feature_extractor, classifier, test_data_x, test_data_y): + 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) + test_loader = tf.data.Dataset.from_tensor_slices((test_data_x, test_data_y)).map( + lambda x,y:( + (tf.cast(x, dtype=tf.float32) / 255. - mean) / std, + tf.cast(y, dtype=tf.int32) + ) + ).batch(32) + acc = 0 + total_num = 0 + total_correct = 0 + for _, (x, y) in enumerate(test_loader): + # feature = feature_extractor(x) + # logits = classifier(feature) + # pred = tf.cast(tf.argmax(logits, axis=1), tf.int64) + # y = tf.cast(y, tf.int64) + # acc += tf.reduce_sum(tf.cast(tf.equal(pred, y), tf.float32)) + logits = classifier(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) + # print(f"pred: {pred} y: {y}") + correct = tf.cast(tf.equal(pred, y), dtype=tf.int32) + # print(correct) + correct = tf.reduce_sum(correct, axis=0) + + # print(f"correct: {correct} total: {x.shape[0]}") + total_num += x.shape[0] + total_correct += int(correct) + + acc = total_correct / total_num + logging.info(f"test acc: {acc}") + +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) + train_data_x = train_data[0] + train_data_y = train_data[1] + print(len(train_data_x), len(train_data_y)) + test_file = '/home/wyd/ianvs/project/data/cifar100/cifar100_test.txt' + test_data = TxtDataParse(data_type='eval') + test_data.parse(test_file) + test_data = read_data_from_file_to_npy(test_data) + test_data_x = test_data[0] + test_data_y = test_data[1] + feature_extractor = resnet10() + feature_extractor.build((None, 32,32,3)) + feature_extractor.call(keras.Input(shape=(32,32,3))) + classifier = build_classifier(feature_extractor) + train(feature_extractor, classifier, train_data) + evaluate(feature_extractor, classifier, test_data_x, test_data_y) + + + + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/examples/cifar100/fci_ssl/fed_ci_match/testenv/acc.py b/examples/cifar100/fci_ssl/fed_ci_match/testenv/acc.py new file mode 100644 index 00000000..f55961f3 --- /dev/null +++ b/examples/cifar100/fci_ssl/fed_ci_match/testenv/acc.py @@ -0,0 +1,39 @@ +# 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 new file mode 100644 index 00000000..7653e637 --- /dev/null +++ b/examples/cifar100/fci_ssl/fed_ci_match/testenv/testenv.yaml @@ -0,0 +1,36 @@ +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" + + # incremental rounds setting of incremental learning; int type; default value is 2; + incremental_rounds: 10 + round: 10 \ No newline at end of file diff --git a/examples/cifar100/fci_ssl/fedavg/algorithm/algorithm.yaml b/examples/cifar100/fci_ssl/fedavg/algorithm/algorithm.yaml index 2cfbd464..427a19f9 100644 --- a/examples/cifar100/fci_ssl/fedavg/algorithm/algorithm.yaml +++ b/examples/cifar100/fci_ssl/fedavg/algorithm/algorithm.yaml @@ -29,7 +29,7 @@ algorithm: # 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/federated-learning/federated-class-incremental-learning/fedavg/algorithm/basemodel.py" + url: "./examples/cifar100/fci_ssl/fedavg/algorithm/basemodel.py" # hyperparameters configuration for the python module; list type; hyperparameters: @@ -45,5 +45,5 @@ algorithm: - 1 - type: "aggregation" name: "FedAvg" - url: "./examples/federated-learning/federated-class-incremental-learning/fedavg/algorithm/aggregation.py" + url: "./examples/cifar100/fci_ssl/fedavg/algorithm/aggregation.py" diff --git a/examples/cifar100/fci_ssl/fedavg/benchmarkingjob.yaml b/examples/cifar100/fci_ssl/fedavg/benchmarkingjob.yaml index 87b8e729..f287fc0c 100644 --- a/examples/cifar100/fci_ssl/fedavg/benchmarkingjob.yaml +++ b/examples/cifar100/fci_ssl/fedavg/benchmarkingjob.yaml @@ -6,7 +6,7 @@ benchmarkingjob: # the url address of test environment configuration file; string type; # the file format supports yaml/yml; - testenv: "./examples/federated-learning/federated-class-incremental-learning/fedavg/testenv/testenv.yaml" + testenv: "./examples/cifar100/fci_ssl/fedavg/testenv/testenv.yaml" # the configuration of test object test_object: @@ -19,7 +19,7 @@ benchmarkingjob: - name: "fcil_test" # the url address of test algorithm configuration file; string type; # the file format supports yaml/yml - url: "./examples/federated-learning/federated-class-incremental-learning/fedavg/algorithm/algorithm.yaml" + url: "./examples/cifar100/fci_ssl/fedavg/algorithm/aggregation.py" # the configuration of ranking leaderboard rank: diff --git a/examples/cifar100/fci_ssl/fedavg/testenv/testenv.yaml b/examples/cifar100/fci_ssl/fedavg/testenv/testenv.yaml index 2baae28b..d7747e83 100644 --- a/examples/cifar100/fci_ssl/fedavg/testenv/testenv.yaml +++ b/examples/cifar100/fci_ssl/fedavg/testenv/testenv.yaml @@ -15,7 +15,7 @@ testenv: # metric name; string type; name: "accuracy" # the url address of python file - url: "/home/wyd/ianvs/project/ianvs/examples/federated-learning/federated-class-incremental-learning/fedavg/testenv/acc.py" + 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 @@ -29,7 +29,7 @@ testenv: # metric name; string type; - name: "accuracy" # the url address of python file - url: "/home/wyd/ianvs/project/ianvs/examples/federated-learning/federated-class-incremental-learning/fedavg/testenv/acc.py" + url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/fci_ssl/fedavg/testenv/acc.py" # incremental rounds setting of incremental learning; int type; default value is 2; incremental_rounds: 2 diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/GLFC.py b/examples/cifar100/fci_ssl/glfc/algorithm/GLFC.py index 72e0c531..207d5192 100644 --- a/examples/cifar100/fci_ssl/glfc/algorithm/GLFC.py +++ b/examples/cifar100/fci_ssl/glfc/algorithm/GLFC.py @@ -1,3 +1,17 @@ +# 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 @@ -38,13 +52,15 @@ def __init__(self, num_classes, batch_size, task_size, memory_size, epochs, lear self.memory_size = memory_size self.old_task_id = -1 - self.current_class = None + self.current_classes = None self.last_class = None self.train_loader = None self.build_feature_extractor() self.classifier = None # self._initialize_classifier() # assert self.classifier is not None + self.labeled_train_set = None + self.unlabeled_train_set= None def build_feature_extractor(self): self.feature_extractor = resnet10() @@ -84,20 +100,20 @@ def before_train(self, task_id, train_data, class_learned, old_model): if need_update: self.old_task_id = task_id self.num_classes = self.task_size * (task_id + 1) - if self.current_class is not None: - self.last_class = self.current_class + 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}') self._initialize_classifier() - self.current_class = np.unique(train_data[1]).tolist() + self.current_classes = np.unique(train_data['label_y']).tolist() self.update_new_set(need_update) - if len(old_model) != 0: - self.old_model = old_model[1] - else: - if len(old_model) != 0: - self.old_model = old_model[0] - self.train_set = train_data + 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.train_loader = self._get_train_loader(True) - logging.info(f'------finish before train task_id: {task_id}------') + 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: @@ -115,16 +131,26 @@ def update_new_set(self, need_update): 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) # print(self.train_set[0].shape, self.train_set[1].shape) + 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]) label = label.reshape(-1, 1) - self.train_set[0] = np.concatenate((self.train_set[0],exm_set[0]), axis=0) - self.train_set[1] = np.concatenate((self.train_set[1],label), axis=0) - logging.info(f'{self.train_set[0].shape}, {self.train_set[1].shape}') - return tf.data.Dataset.from_tensor_slices((self.train_set[0], self.train_set[1])).shuffle(buffer_size=10000000).batch(self.batch_size) + train_x = np.concatenate((train_x,exm_set[0]), axis=0) + train_y = np.concatenate((train_y,label), axis=0) + # logging.info(f'{ train_set[0].shape}, {self.train_set[1].shape}') + + return tf.data.Dataset.from_tensor_slices((train_x, train_y)).shuffle(buffer_size=10000000).batch(self.batch_size).map( + lambda x,y:( + (tf.cast(x, dtype=tf.float32) / 255. - self.mean) / self.std, + tf.cast(y, dtype=tf.int32) + ) + ) def train(self, round): # self._initialize_classifier() @@ -140,11 +166,13 @@ def train(self, round): for step, (x, y) in enumerate(self.train_loader): # opt = keras.optimizers.SGD(learning_rate=self.learning_rate, weight_decay=0.00001) with tf.GradientTape() as tape: - logits = self.model_call(x, training=True) - # # y = get_one_hot(y, self.num_classes) - loss = tf.reduce_mean(keras.losses.sparse_categorical_crossentropy(y, logits, from_logits=True)) - # loss = self._compute_loss(x, y) - # logging.info(f'------round{round} epoch{epoch} step{step} loss: {loss} and loss dim is {loss.shape}------') + input = self.feature_extractor(inputs=x,training=True) + y_pred = self.classifier(inputs=input, training=True) + target = get_one_hot(y, self.num_classes) + + loss = tf.reduce_mean(keras.losses.sparse_categorical_crossentropy(y, y_pred, from_logits=True)) + # loss = self._compute_loss(x, y) + logging.info(f'------round{round} epoch{epoch} step{step} loss: {loss} and loss dim is {loss.shape}------') grads = tape.gradient(loss, all_params) # # print(f'grads shape is {len(grads)} and type is {type(grads)}') opt.apply_gradients(zip(grads, all_params)) @@ -169,7 +197,7 @@ def _compute_loss(self, imgs, labels): 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'correct is {correct} and acc is {correct/imgs.shape[0]}') + logging.info(f'current class numbers is {self.num_classes} correct is {correct} and acc is {correct/imgs.shape[0]}') # print(f"total_correct: {total_correct}, total_num: {total_num}") if self.old_model == None: w = self.efficient_old_class_weight(target, labels ) @@ -247,15 +275,23 @@ def efficient_old_class_weight(self, output, labels): return tf.ones(g.shape, dtype=tf.float32) def get_train_set_data(self, class_id): + images = [] - # print(len(self.train_set[0])) - for i in range(len(self.train_set[0])): - # print(self.train_set[1][i]) - if self.train_set[1][i] == class_id: - # print(self.train_set[0][i].shape) - images.append(self.train_set[0][i]) + 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: + print(train_x[i].shape) + 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] @@ -272,11 +308,6 @@ def _construct_exemplar_set(self, images,label, m): x = np.linalg.norm(x) index = np.argmin(x) now_class_mean += fe_outpu[index] - # print( - # 'construct exemplar: ',len(images[index]), - # 'type: ',type(images[index]), - # 'shape: ',images[index].shape - # ) exemplar.append(images[index]) labels.append(label) self.exemplar_set.append((exemplar, labels)) @@ -295,7 +326,8 @@ def proto_grad(self): cri_loss = keras.losses.SparseCategoricalCrossentropy() proto = [] proto_grad = [] - for i in self.current_class: + logging.info(f'self. current class is {self.current_classes}') + for i in self.current_classes: images = self.get_train_set_data(i) # print(f'image shape is {len(images)}') class_mean, fe_output = self.compute_class_mean(images) @@ -307,7 +339,7 @@ def proto_grad(self): data = proto[i] data = tf.cast(tf.expand_dims(data, axis=0), tf.float32) # print(f"in proto_grad, data shape is {data.shape}") - label = self.current_class[i] + label = self.current_classes[i] # print("in proto_grad, label shape is ", label.shape) label = tf.constant([label]) target = get_one_hot(label, self.num_classes) diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/aggregation.py b/examples/cifar100/fci_ssl/glfc/algorithm/aggregation.py index 3ec7b9d9..687449a2 100644 --- a/examples/cifar100/fci_ssl/glfc/algorithm/aggregation.py +++ b/examples/cifar100/fci_ssl/glfc/algorithm/aggregation.py @@ -1,3 +1,17 @@ +# 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 diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/algorithm.yaml b/examples/cifar100/fci_ssl/glfc/algorithm/algorithm.yaml index ae91bf77..51711d41 100644 --- a/examples/cifar100/fci_ssl/glfc/algorithm/algorithm.yaml +++ b/examples/cifar100/fci_ssl/glfc/algorithm/algorithm.yaml @@ -29,7 +29,7 @@ algorithm: # example: basemodel.py has BaseModel module that the alias is "FPN" for this benchmarking; name: "glfc" # the url address of python module; string type; - url: "./examples/federated-learning/federated-class-incremental-learning/glfc/algorithm/basemodel.py" + url: "./examples/cifar100/fci_ssl/glfc/algorithm/basemodel.py" # hyperparameters configuration for the python module; list type; hyperparameters: @@ -42,8 +42,8 @@ algorithm: - 0.001 - epochs: values: - - 3 + - 10 - type: "aggregation" name: "FedAvg" - url: "./examples/federated-learning/federated-class-incremental-learning/glfc/algorithm/aggregation.py" + url: "./examples/cifar100/fci_ssl/glfc/algorithm/aggregation.py" diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/args.py b/examples/cifar100/fci_ssl/glfc/algorithm/args.py deleted file mode 100644 index 56fcb757..00000000 --- a/examples/cifar100/fci_ssl/glfc/algorithm/args.py +++ /dev/null @@ -1,17 +0,0 @@ -class ClientArgs: - def __init__(self, **kwargs): - self.epochs = 1 - self.batch_size = 32 - self.task_size = 10 - self.learning_rate = 0.001 - - self.memory_size = 2000 - - - -class ServerArgs: - def __init__(self, **kwargs): - self.epochs = 1 - self.batch_size = 32 - self.task_size = 10 - self.memory_size = 2000 \ No newline at end of file diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/basemodel.py b/examples/cifar100/fci_ssl/glfc/algorithm/basemodel.py index 67df71ef..d4217071 100644 --- a/examples/cifar100/fci_ssl/glfc/algorithm/basemodel.py +++ b/examples/cifar100/fci_ssl/glfc/algorithm/basemodel.py @@ -1,3 +1,17 @@ +# 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 @@ -57,7 +71,7 @@ def train(self, train_data,val_data, **kwargs): proto_grad = self.GLFC_Client.proto_grad() print(type(proto_grad)) # self.GLFC_Client.evaluate() - return {'num_samples': len(train_data[0]) , 'proto_grad' : proto_grad, 'task_id': task_id} + 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'] diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/model.py b/examples/cifar100/fci_ssl/glfc/algorithm/model.py index abac6414..db7e875c 100644 --- a/examples/cifar100/fci_ssl/glfc/algorithm/model.py +++ b/examples/cifar100/fci_ssl/glfc/algorithm/model.py @@ -1,3 +1,17 @@ +# 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 # import keras diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/network.py b/examples/cifar100/fci_ssl/glfc/algorithm/network.py index a98b8d11..b9a3fdcd 100644 --- a/examples/cifar100/fci_ssl/glfc/algorithm/network.py +++ b/examples/cifar100/fci_ssl/glfc/algorithm/network.py @@ -1,3 +1,17 @@ +# 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 diff --git a/examples/cifar100/fci_ssl/glfc/algorithm/proxy_server.py b/examples/cifar100/fci_ssl/glfc/algorithm/proxy_server.py index dd7fb303..6d293177 100644 --- a/examples/cifar100/fci_ssl/glfc/algorithm/proxy_server.py +++ b/examples/cifar100/fci_ssl/glfc/algorithm/proxy_server.py @@ -1,3 +1,17 @@ +# 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 diff --git a/examples/cifar100/fci_ssl/glfc/benchmarkingjob.yaml b/examples/cifar100/fci_ssl/glfc/benchmarkingjob.yaml index aa251512..17e861fa 100644 --- a/examples/cifar100/fci_ssl/glfc/benchmarkingjob.yaml +++ b/examples/cifar100/fci_ssl/glfc/benchmarkingjob.yaml @@ -6,7 +6,7 @@ benchmarkingjob: # the url address of test environment configuration file; string type; # the file format supports yaml/yml; - testenv: "./examples/federated-learning/federated-class-incremental-learning/glfc/testenv/testenv.yaml" + testenv: "./examples/cifar100/fci_ssl/glfc/testenv/testenv.yaml" # the configuration of test object test_object: @@ -19,7 +19,7 @@ benchmarkingjob: - name: "fcil_test" # the url address of test algorithm configuration file; string type; # the file format supports yaml/yml - url: "./examples/federated-learning/federated-class-incremental-learning/glfc/algorithm/algorithm.yaml" + url: "./examples/cifar100/fci_ssl/glfc/algorithm/algorithm.yaml" # the configuration of ranking leaderboard rank: diff --git a/examples/cifar100/fci_ssl/glfc/test._train.py b/examples/cifar100/fci_ssl/glfc/test._train.py deleted file mode 100644 index 4a3a9b74..00000000 --- a/examples/cifar100/fci_ssl/glfc/test._train.py +++ /dev/null @@ -1,45 +0,0 @@ -import tensorflow as tf -import keras -from algorithm.network import NetWork, incremental_learning -from algorithm.model import resnet10, lenet5 -from sedna.datasources import TxtDataParse -from core.testenvmanager.dataset.utils import read_data_from_file_to_npy -import copy -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) -train_loader = tf.data.Dataset.from_tensor_slices(train_data).shuffle(500000).batch(32) -x_train, y_train = train_data -task_id = 0 -fe = resnet10(10) -model = NetWork(100, fe) -model.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy']) -for range in range(100): - optimizer = tf.keras.optimizers.SGD(learning_rate=0.01) - x_task_data, y_task_data = x_train[task_id*5000 : (task_id+1)*5000], y_train[task_id*5000 : (task_id+1)*5000] - task_loader = tf.data.Dataset.from_tensor_slices((x_task_data, y_task_data)).shuffle(5000).batch(128) - # if range != 0 and range % 10 == 0: - # task_id += 1 - # model = incremental_learning(model, 10*(task_id+1)) - for x, y in task_loader: - model.fit(x, y, epochs=1) - # print(y) - # with tf.GradientTape() as tape: - # logits = model(x, training=True) - # label =y - # # print(y.shape[0]) - # y = tf.one_hot(y, 100) - # y = tf.squeeze(y, axis=1) - # loss = tf.reduce_mean(keras.losses.categorical_crossentropy(y, logits, from_logits=True)) - # pred = tf.argmax(logits, axis=1) - # pred = tf.cast(pred, dtype=tf.int32) - # pred = tf.reshape(pred, label.shape) - # # print(pred.shape, label.shape) - # correct = tf.cast(tf.equal(pred, label), dtype=tf.int32) - # # print(correct.shape) - # correct = tf.reduce_sum(correct) - # # print(correct, y.shape[0]) - # grads = tape.gradient(loss, model.trainable_variables) - # optimizer.apply(grads, model.trainable_variables) - # print(f'loss: {loss}, accuracy: {correct / x.shape[0]}') diff --git a/examples/cifar100/fci_ssl/glfc/test.py b/examples/cifar100/fci_ssl/glfc/test.py deleted file mode 100644 index 781c7b39..00000000 --- a/examples/cifar100/fci_ssl/glfc/test.py +++ /dev/null @@ -1,214 +0,0 @@ -from keras import Sequential,Input -from keras.src.layers import Conv2D, MaxPooling2D, Flatten, Dropout, Dense -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]) - -feature_extractor = resnet10(10) -feature_extractor.build(input_shape=(None, 32, 32, 3)) -feature_extractor.call(Input(shape=(32, 32, 3))) -print(feature_extractor.summary()) -classifier = Sequential([ - - Dense( 10, kernel_initializer='lecun_normal') - ]) -x = np.random.random((32, 512)) -classifier(x) -# all_weight = [] -# fe_weight = feature_extractor.get_weights() -# clf_weight = classifier.get_weights() - -# all_weight.extend(fe_weight) -# all_weight.extend(clf_weight) -# print(len(fe_weight)) -# print('-'*50) -# print(len(clf_weight)) - -# print(len(all_weight)) -# proto_fe = resnet10() -# proto_fe.build(input_shape=(None, 32, 32, 3)) -# proto_fe.call(keras.Input(shape=(32, 32, 3))) -# print(proto_fe.summary()) -# proto_fe.set_weights(fe_weight) -# for w in fe_weight: -# print(w.shape) \ No newline at end of file diff --git a/examples/cifar100/fci_ssl/glfc/testenv/acc.py b/examples/cifar100/fci_ssl/glfc/testenv/acc.py index ab2cff16..9f2becd0 100644 --- a/examples/cifar100/fci_ssl/glfc/testenv/acc.py +++ b/examples/cifar100/fci_ssl/glfc/testenv/acc.py @@ -1,3 +1,17 @@ +# 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 diff --git a/examples/cifar100/fci_ssl/glfc/testenv/testenv.yaml b/examples/cifar100/fci_ssl/glfc/testenv/testenv.yaml index 2c703114..e2d23f57 100644 --- a/examples/cifar100/fci_ssl/glfc/testenv/testenv.yaml +++ b/examples/cifar100/fci_ssl/glfc/testenv/testenv.yaml @@ -15,7 +15,7 @@ testenv: # metric name; string type; name: "accuracy" # the url address of python file - url: "/home/wyd/ianvs/project/ianvs/examples/federated-learning/federated-class-incremental-learning/glfc/testenv/acc.py" + url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/fci_ssl/glfc/testenv/acc.py" # condition of triggering inference network to update # threshold of the condition; types are float/int @@ -29,9 +29,8 @@ testenv: # metric name; string type; - name: "accuracy" # the url address of python file - url: "/home/wyd/ianvs/project/ianvs/examples/federated-learning/federated-class-incremental-learning/glfc/testenv/acc.py" + url: "/home/wyd/ianvs/project/ianvs/examples/cifar100/fci_ssl/glfc/testenv/acc.py" # incremental rounds setting of incremental learning; int type; default value is 2; - task_size: 10 - incremental_rounds: 100 + incremental_rounds: 10 round: 10 \ No newline at end of file diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/aggregation.py b/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/aggregation.py index 99333d66..cbc0a0ae 100644 --- a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/aggregation.py +++ b/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/aggregation.py @@ -1,3 +1,17 @@ +# 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 diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/basemodel.py b/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/basemodel.py index 74896252..dcb03a08 100644 --- a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/basemodel.py +++ b/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/basemodel.py @@ -1,3 +1,17 @@ +# 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 diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/network.py b/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/network.py index 87c6f779..95ddf383 100644 --- a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/network.py +++ b/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/network.py @@ -1,3 +1,17 @@ +# 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 diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/resnet.py b/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/resnet.py index fbcd1f79..7c910209 100644 --- a/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/resnet.py +++ b/examples/cifar100/federated_class_incremental_learning/fedavg/algorithm/resnet.py @@ -1,3 +1,17 @@ +# 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 # import keras diff --git a/examples/cifar100/federated_class_incremental_learning/fedavg/test.py b/examples/cifar100/federated_class_incremental_learning/fedavg/test.py deleted file mode 100644 index 5b3cf860..00000000 --- a/examples/cifar100/federated_class_incremental_learning/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/federated_class_incremental_learning/fedavg/testenv/acc.py b/examples/cifar100/federated_class_incremental_learning/fedavg/testenv/acc.py index ab2cff16..9f2becd0 100644 --- a/examples/cifar100/federated_class_incremental_learning/fedavg/testenv/acc.py +++ b/examples/cifar100/federated_class_incremental_learning/fedavg/testenv/acc.py @@ -1,3 +1,17 @@ +# 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 diff --git a/examples/cifar100/federated_learning/fedavg/__init__.py b/examples/cifar100/federated_learning/fedavg/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/cifar100/federated_learning/fedavg/algorithm/__init__.py b/examples/cifar100/federated_learning/fedavg/algorithm/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/cifar100/federated_learning/fedavg/algorithm/aggregation.py b/examples/cifar100/federated_learning/fedavg/algorithm/aggregation.py index e282c708..f966d8ab 100644 --- a/examples/cifar100/federated_learning/fedavg/algorithm/aggregation.py +++ b/examples/cifar100/federated_learning/fedavg/algorithm/aggregation.py @@ -1,3 +1,17 @@ +# 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 diff --git a/examples/cifar100/federated_learning/fedavg/algorithm/algorithm.yaml b/examples/cifar100/federated_learning/fedavg/algorithm/algorithm.yaml index 1e6e7afa..7b37eb88 100644 --- a/examples/cifar100/federated_learning/fedavg/algorithm/algorithm.yaml +++ b/examples/cifar100/federated_learning/fedavg/algorithm/algorithm.yaml @@ -42,7 +42,7 @@ algorithm: - 0.001 - epochs: values: - - 3 + - 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 index 638bb46a..f77dff09 100644 --- a/examples/cifar100/federated_learning/fedavg/algorithm/basemodel.py +++ b/examples/cifar100/federated_learning/fedavg/algorithm/basemodel.py @@ -1,3 +1,17 @@ +# 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 @@ -17,7 +31,7 @@ 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('epoch', 1) + self.epochs = kwargs.get('epochs', 1) self.lr = kwargs.get('lr', 0.001) self.optimizer = tf.keras.optimizers.SGD(learning_rate=self.lr) self.model = self.build(num_classes=100) @@ -108,8 +122,11 @@ def train(self, train_data, valid_data, **kwargs): 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. - mean) /std logits = self.model(x, training=False) pred = tf.cast(tf.argmax(logits, axis=1), tf.int32) result[data] = pred.numpy() @@ -139,7 +156,13 @@ def eval(self, data, round, **kwargs): 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).batch(self.batch_size) + 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. - mean) / std, + tf.cast(y, dtype=tf.int32) + ) + ).batch(self.batch_size) diff --git a/examples/cifar100/federated_learning/fedavg/testenv/testenv.yaml b/examples/cifar100/federated_learning/fedavg/testenv/testenv.yaml index 1f761027..4e3912b1 100644 --- a/examples/cifar100/federated_learning/fedavg/testenv/testenv.yaml +++ b/examples/cifar100/federated_learning/fedavg/testenv/testenv.yaml @@ -34,4 +34,4 @@ testenv: # incremental rounds setting of incremental learning; int type; default value is 2; task_size: 10 incremental_rounds: 10 - round: 10 \ No newline at end of file + round: 200 \ No newline at end of file diff --git a/examples/cifar100/utils.py b/examples/cifar100/utils.py index 0635d8b5..f5106fdc 100644 --- a/examples/cifar100/utils.py +++ b/examples/cifar100/utils.py @@ -1,3 +1,17 @@ +# 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 @@ -16,7 +30,7 @@ def process_cifar100(): (x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar100.load_data() print(y_test.shape) # 数据预处理:归一化 - x_train, x_test = x_train / 255.0, x_test / 255.0 + # x_train, x_test = x_train / 255.0, x_test / 255.0 # 将标签转换为类别索引 class_labels = np.unique(y_train) # 获取所有类别 @@ -29,25 +43,10 @@ def process_cifar100(): # print(type(img)) # print('----') train_class_dict[label[0]].append(img) - # np.save(f'../../../../../data/cifar100/cifar100_train_index_{train_cnt}.npy', img) - # train_file_str.append(f'cifar100_train_index_{train_cnt}.npy\t{label}\n') - # train_cnt += 1 - # test_cnt = 0 - # test_file_str = [] # # 按类别组织测试数据 for img, label in zip(x_test, y_test): # test_class_dict[label[0]].append(img) test_class_dict[label[0]].append(img) - # np.save(f'../../../../../data/cifar100/cifar100_test_index_{test_cnt}.npy', img) - # test_file_str.append(f'cifar100_train_index_{test_cnt}.npy\t{label[0]}\n') - # test_cnt += 1 - # for line in train_file_str: - # with open(train_txt, 'a') as f: - # f.write(line) - # for line in test_file_str: - # with open(test_txt, 'a') as f: - # f.write(line) - # 保存训练数据到本地文件 for label, imgs in train_class_dict.items(): data = np.array(imgs) @@ -66,50 +65,3 @@ def process_cifar100(): if __name__ == '__main__': process_cifar100() - # arr = np.load("/home/wyd/ianvs/project/data/cifar100/cifar100_train_index_0.npy") - # print(arr.shape) - # print(arr) - # process_cifar100() - # (x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar100.load_data() - # 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_train = x_train[:5000] - # y_train = y_train[:5000] - # batch_size=32 - # train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(batch_size).map( - # lambda x, y: ( - # (tf.cast(x, dtype=tf.float32) / 255. - mean) / std, - # tf.cast(y, dtype=tf.int32) - # ) - # ) - # from algorithm.resnet import resnet18 - # model = resnet18(100) - # optimizer = tf.keras.optimizers.SGD(learning_rate=0.001) - # for epoch in range(10): - # for _, (x,y) in enumerate(train_db): - # with tf.GradientTape() as tape: - # logits = model(x, training=True) - # y = tf.one_hot(y, depth=100) - # y = tf.squeeze(y, axis=1) - # loss = tf.reduce_mean(tf.keras.losses.categorical_crossentropy(y, logits, from_logits=True)) - # grads = tape.gradient(loss, model.trainable_variables) - # optimizer.apply_gradients(zip(grads, model.trainable_variables)) - # print(f"train round {1}: Epoch {epoch + 1} loss: {loss.numpy():.4f}") - # total_num = 0 - # total_correct = 0 - # for _, (x,y) in enumerate(train_db): - # logits = model(x, training=False) - # # prob = tf.nn.softmax(logits, axis=1) - # pred = tf.argmax(logits, axis=1) - # pred = tf.cast(pred, dtype=tf.int32) - # pred = tf.reshape(pred, y.shape) - # # print(pred.shape, 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}")