class_docstr: Bias Correction (BiC) plugin.
  
  Technique introduced in:
  "Wu, Yue, et al. "Large scale incremental learning." Proceedings
  of the IEEE/CVF Conference on Computer Vision and Pattern
  Recognition. 2019"
  
  Implementation based on FACIL, as in:
  https://github.com/mmasana/FACIL/blob/master/src/approach/bic.py
code: def after_training_exp(self, strategy, **kwargs):
      self.model_old = deepcopy(strategy.model)
      task_id = strategy.clock.train_exp_counter
  
      self.storage_policy.update(strategy, **kwargs)
  
      if task_id > 0:
          list_subsets = []
          for _, class_buf in self.val_buffer.items():
              list_subsets.append(class_buf.buffer)
  
          stage_set = _concat_taskaware_classification_datasets(
              list_subsets
          )
          stage_loader = DataLoader(
              stage_set,
              batch_size=strategy.train_mb_size,
              shuffle=True,
              num_workers=4,
          )
  
          bic_optimizer = torch.optim.SGD(
              self.bias_layer[task_id].parameters(),
              lr=self.lr,
              momentum=0.9,
          )
  
          # verbose here is actually correct
          # The PyTorch type stubs for MultiStepLR are broken
          scheduler = MultiStepLR(
              bic_optimizer,
              milestones=[50, 100, 150],
              gamma=0.1,
              verbose=False,
          )  # type: ignore
  
          # Loop epochs
          for e in range(self.stage_2_epochs):
              total, t_acc, t_loss = 0, 0, 0
              for inputs in stage_loader:
                  x = inputs[0].to(strategy.device)
                  y_real = inputs[1].to(strategy.device)
  
                  outputs = strategy.model(x)
                  for t in self.bias_layer.keys():
                      outputs = self.bias_layer[t](outputs)
  
                  loss = torch.nn.functional.cross_entropy(
                      outputs, y_real
                  )
  
                  _, preds = torch.max(outputs, 1)
                  t_acc += torch.sum(preds == y_real.data)
                  t_loss += loss.item() * x.size(0)
                  total += x.size(0)
  
                  loss += 0.1 * (
                      (self.bias_layer[task_id].beta.sum() ** 2) / 2
                  )
  
                  bic_optimizer.zero_grad()
                  loss.backward()
                  bic_optimizer.step()
  
              scheduler.step()
              if (e + 1) % (int(self.stage_2_epochs / 4)) == 0:
                  print(
                      "| E {:3d} | Train: loss={:.3f}, S2 acc={:5.1f}% |".format(
                          e + 1, t_loss / total, 100 * t_acc / total
                      )
                  )
  
imports: from collections import defaultdict
  from typing import Dict, List, Optional, TYPE_CHECKING, Sequence, Set, SupportsInt
  from copy import deepcopy
  import torch
  from torch.utils.data import DataLoader
  from torch.optim.lr_scheduler import MultiStepLR
  from avalanche.benchmarks.utils import _taskaware_classification_subset, _concat_taskaware_classification_datasets
  from avalanche.benchmarks.utils.data import AvalancheDataset
  from avalanche.benchmarks.utils.data_loader import ReplayDataLoader
  from avalanche.training.plugins.strategy_plugin import SupervisedPlugin
  from avalanche.training.storage_policy import ExemplarsBuffer, ExperienceBalancedBuffer, ReservoirSamplingBuffer
  from avalanche.models.dynamic_modules import MultiTaskModule
  from avalanche.models.bic_model import BiasLayer
label_desc: Use image node_id 8 for calling the BiCPlugin obj's underlying member method code with example usage: obj.after_training_exp(strategy) without return types