Skip to content

Commit

Permalink
rebase and fix conflict
Browse files Browse the repository at this point in the history
Signed-off-by: Marchons <[email protected]>
  • Loading branch information
Yoda-wu committed Sep 26, 2024
1 parent 69c3ebf commit aee0358
Show file tree
Hide file tree
Showing 24 changed files with 874 additions and 535 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -269,33 +269,27 @@ def evaluation(self, testdataset_files, incremental_round):
[testdataset_files[index]["y"][data_index]], res
)
acc_list.append(acc)
if index == len(testdataset_files) - 1:
self.system_metric_info[SystemMetricType.TASK_AVG_ACC.value][
"accuracy"
] = np.mean(acc_list)
old_class_acc_list.extend(acc_list)
current_forget_rate = 0.0
max_acc_sum = 0
self.accuracy_per_round.append(old_class_acc_list)
self.system_metric_info[SystemMetricType.TASK_AVG_ACC.value]["accuracy"] = (
np.mean(old_class_acc_list)
)
# caculate the forget rate
for i in range(len(old_class_acc_list)):
max_acc_diff = 0
for j in range(incremental_round):
acc_per_round = self.accuracy_per_round[j]
if i < len(acc_per_round):
LOGGER.info(
f"acc_per_round: {acc_per_round[i]}"
+ f" 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
)
LOGGER.info(
f"for current round: {incremental_round} forget rate: {current_forget_rate}"
f"for current round: {incremental_round} forget rate: {current_forget_rate} task avg acc: {self.system_metric_info[SystemMetricType.TASK_AVG_ACC.value]['accuracy']}"
)
return current_forget_rate
11 changes: 7 additions & 4 deletions core/testenvmanager/testenv/testenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ def __init__(self, config):
"url": "",
},
"threshold": 0.9,
"operator": ">"
"operator": ">",
}
self.metrics = []
self.incremental_rounds = 2
self.task_size = 1
self.round = 1
self.client_number = 1
self.dataset = None
self._parse_config(config)

Expand All @@ -53,8 +54,10 @@ def _check_fields(self):
raise ValueError(f"not found testenv metrics({self.metrics}).")

if not isinstance(self.incremental_rounds, int) or self.incremental_rounds < 2:
raise ValueError(f"testenv incremental_rounds(value={self.incremental_rounds})"
f" must be int type and not less than 2.")
raise ValueError(
f"testenv incremental_rounds(value={self.incremental_rounds})"
f" must be int type and not less than 2."
)

def _parse_config(self, config):
config_dict = config[str.lower(TestEnv.__name__)]
Expand All @@ -69,7 +72,7 @@ def _parse_config(self, config):
self._check_fields()

def prepare(self):
""" prepare env"""
"""prepare env"""
try:
self.dataset.process_dataset()
except Exception as err:
Expand Down
117 changes: 73 additions & 44 deletions examples/cifar100/fci_ssl/fed_ci_match/algorithm/FedCiMatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ def before_train(self, task_id, round, train_data, task_size):
if self.task_size is None:
self.task_size = task_size
is_new_task = task_id != self.old_task_id
self.is_new_task = is_new_task
if is_new_task:
self.best_old_model = (
(self.feature_extractor, self.classifier)
Expand All @@ -152,7 +153,7 @@ def before_train(self, task_id, round, train_data, task_size):
logging.info(f"num_classes: {self.num_classes}")
if self.current_classes is not None:
self.last_classes = self.current_classes
self.build_classifier()
# self.build_classifier()
self.current_classes = np.unique(train_data["label_y"]).tolist()
logging.info(f"current_classes: {self.current_classes}")

Expand Down Expand Up @@ -196,10 +197,10 @@ def get_train_loader(self):
logging.info(
f"train_x shape: {train_x.shape} and train_y shape: {train_y.shape}"
)
logging.info(
f"unlabel_x shape: {self.unlabeled_train_set[0].shape} and unlabel_y shape: {self.unlabeled_train_set[1].shape}"
)

logging.info(
f"train_x shape: {train_x.shape} and train_y shape: {train_y.shape}"
)
label_data_loader = self.data_preprocessor.preprocess_labeled_dataset(
train_x, train_y, self.batch_size
)
Expand All @@ -210,6 +211,9 @@ def get_train_loader(self):
self.unlabeled_train_set[1],
self.batch_size,
)
logging.info(
f"unlabel_x shape: {self.unlabeled_train_set[0].shape} and unlabel_y shape: {self.unlabeled_train_set[1].shape}"
)
return label_data_loader, unlabel_data_loader

def build_exemplar(self):
Expand Down Expand Up @@ -243,15 +247,25 @@ 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)
# cl_mean = tf.reduce_mean(class_mean, axis=0)
# now_class_mean = np.zeros((1, fe_ouput.shape[1]))
diff = tf.abs(fe_ouput - class_mean)
distance = [float(tf.reduce_sum(dis).numpy()) for dis in diff]

sorted_index = np.argsort(distance).tolist()
if len(sorted_index) > m:
sorted_index = sorted_index[:m]
exemplar_data = [images[i] for i in sorted_index]
exemplar_label = [class_id] * len(exemplar_data)
self.exemplar_set.append((exemplar_data, exemplar_label))
# 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 = (
Expand All @@ -261,24 +275,28 @@ def compute_exemplar_mean(self, images):
)
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)
class_mean = tf.reduce_mean(fe_output, axis=0)
# 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, momentum=0.9, weight_decay=0.0001
# )
optimizer = keras.optimizers.Adam(
learning_rate=self.learning_rate, weight_decay=0.00001
learning_rate=self.learning_rate, weight_decay=0.0001
)
feature_extractor_params = self.feature_extractor.trainable_variables
classifier_params = self.classifier.trainable_variables
q = []
logging.info(f"is new task: {self.is_new_task}")
# if self.classifier is not None:
# q = self.caculate_pre_update()
if self.is_new_task:
self.build_classifier()
# self.is_new_task = False
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)
q = self.caculate_pre_update()
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):
Expand All @@ -295,13 +313,16 @@ def train(self, round):
correct = tf.reduce_sum(
tf.cast(tf.equal(label_pred, labeled_y), dtype=tf.int32)
)
loss = self.supervised_loss(labeled_x, labeled_y, q, step)
CE_loss = self.supervised_loss(labeled_x, labeled_y)
KD_loss = self.distil_loss(labeled_x, labeled_y, q, step)
# 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
loss = CE_loss if KD_loss == 0 else CE_loss + 0.4 * KD_loss
loss = CE_loss
logging.info(
f"epoch {epoch} step {step} loss: {loss} correct {correct} and total {labeled_x.shape[0]}"
f"epoch {epoch} step {step} loss: {loss} correct {correct} and total {labeled_x.shape[0]} class is {np.unique(labeled_y)}"
)
grads = tape.gradient(loss, all_params)
optimizer.apply_gradients(zip(grads, all_params))
Expand All @@ -316,43 +337,51 @@ def caculate_pre_update(self):
logging.info(f"q shape: {len(q)}")
return q

def supervised_loss(self, x, y, q, step):
def supervised_loss(self, x, y):
input = x
input = self.feature_extractor(input, training=True)
y_pred = self.classifier(input, training=True)
target = get_one_hot(y, self.num_classes)
loss = keras.losses.categorical_crossentropy(target, y_pred, from_logits=True)
logging.info(f"loss shape: {loss.shape}")
loss = tf.reduce_mean(loss)
KD_loss = self.distil_loss(x, y, q, step)
return loss + KD_loss
logging.info(f"CE loss: {loss}")

return loss

def distil_loss(self, x, y, q, step):
KD_loss = 0

if len(self.learned_classes) > 0 and self.best_old_model is not None:
g = self.feature_extractor(x, training=True)
g = self.classifier(g, training=True)
og = self.best_old_model[0](x, training=False)
og = self.best_old_model[1](og, training=False)
logging.info(f"og shape: {og.shape} g shape: {g.shape}")
og = tf.nn.sigmoid(og)
g = tf.nn.sigmoid(g)
distil_target = tf.Variable(g)
distil_target[:, : og.shape[1]].assign(og)
q_i = q[step]
are_equal = tf.reduce_all(tf.equal(q_i, g))
logging.info(
f"q_i shape: {q_i.shape} g shape: {g.shape} are equal: {are_equal.numpy()}"
# logging.info(f"og shape: {og.shape} g shape: {g.shape}")
sigmoid_og = tf.nn.sigmoid(og)
sigmoid_g = tf.nn.sigmoid(g)
softmax_og = tf.nn.softmax(og)
softmax_g = tf.nn.softmax(g)
kl_loss = keras.losses.kl_divergence(
softmax_og, softmax_g[:, : og.shape[1]]
)
# distil_target = tf.Variable(g)
# distil_target[:, : og.shape[1]].assign(og)
# q_i = q[step]
# are_equal = tf.reduce_all(tf.equal(q_i, g))
# logging.info(
# f"q_i shape: {q_i.shape} g shape: {g.shape} are equal: {are_equal.numpy()}"
# )
BCELoss = keras.losses.BinaryCrossentropy()
test_distill_loss = BCELoss(distil_target, g)
logging.info(f"test loss {test_distill_loss}")
# test_distill_loss = BCELoss(og, g[:, : og.shape[1]])
# logging.info(f"test loss {kl_loss.numpy()}")
loss = []
for y in self.learned_classes:
if y not in self.current_classes:
loss.append(BCELoss(q_i[:, y], g[:, y]))
# logging.info(f"test loss: {len(loss)}")
loss.append(BCELoss(sigmoid_og[:, y], sigmoid_g[:, y]))
# logging.info(f"test loss: {len(loss)} {loss}")
KD_loss = tf.reduce_sum(loss)
# KD_loss = tf.reduce_mean(kl_loss)
logging.info(f"KD_loss: {KD_loss}")
return KD_loss

Expand All @@ -377,8 +406,8 @@ def predict(self, x):
prob = tf.nn.softmax(pred, axis=1)
pred = tf.argmax(prob, axis=1)
pred = tf.cast(pred, dtype=tf.int32)
return pred
return pred

def icarl_predict(self, x):
mean = np.array((0.5071, 0.4867, 0.4408), np.float32).reshape(1, 1, -1)
std = np.array((0.2675, 0.2565, 0.2761), np.float32).reshape(1, 1, -1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ algorithm:

modules:
- type: "basemodel"
name: "fci_ssl"
name: "FedCILMatch"
url: "./examples/cifar100/fci_ssl/fed_ci_match/algorithm/basemodel.py"
hyperparameters:
- batch_size:
values:
- 128
- 64
- learning_rate:
values:
- 0.001
- epochs:
values:
- 1
- 24
- type: "aggregation"
name: "FedAvg"
url: "./examples/cifar100/fci_ssl/fed_ci_match/algorithm/aggregation.py"
Expand Down
11 changes: 7 additions & 4 deletions examples/cifar100/fci_ssl/fed_ci_match/algorithm/basemodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys

sys.path.append(".")
sys.path.append("..")
import os
import numpy as np
import keras
Expand All @@ -26,17 +29,17 @@
logging.getLogger().setLevel(logging.INFO)


@ClassFactory.register(ClassType.GENERAL, alias="fci_ssl")
@ClassFactory.register(ClassType.GENERAL, alias="FedCILMatch")
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.task_size = kwargs.get("task_size", 2)
self.memory_size = kwargs.get("memory_size", 2000)
# self.fe = self.build_feature_extractor()
self.num_classes = 10 # the number of class for the first task
self.num_classes = 50 # the number of class for the first task
self.FedCiMatch = FedCiMatch(
self.num_classes,
self.batch_size,
Expand Down Expand Up @@ -71,7 +74,7 @@ def predict(self, data_files, **kwargs):
for data in data_files:
x = np.load(data)
logging.info(f"predicting {x.shape}")
res = self.FedCiMatch.icarl_predict(x)
res = self.FedCiMatch.predict(x)
# pred = tf.cast(tf.argmax(logits, axis=1), tf.int32)
result[data] = res.numpy()
print("finish predict")
Expand Down
Loading

0 comments on commit aee0358

Please sign in to comment.