diff --git a/LinearFiltration_metrics.txt b/LinearFiltration_metrics.txt new file mode 100644 index 0000000..ac054a6 --- /dev/null +++ b/LinearFiltration_metrics.txt @@ -0,0 +1,7 @@ +execution_time_sec +0.000996 +0.030071 +0.001182 +0.001176 +0.001229 +0.001257 diff --git a/OOP.py b/OOP.py new file mode 100644 index 0000000..a7f69c6 --- /dev/null +++ b/OOP.py @@ -0,0 +1,48 @@ + +# This is from wikipedia pseudocode implementation of a single +# ThresholdLogic Unit. +# done to make me understand OOP the Python way + + +# no need for brackets if not inheriting +class ThresholdLogicUnit: + + # define members in init + def __init__(self, threshold, weights): + self.threshold = threshold + self.weights = weights + + + # If a function has to make use of member variables + # it has to have self as param + def fire(self,inputs): + tots = 0 + #for i in range(0,inputs.size()): + for val, weight in zip(inputs, self.weights): + if val: + tots+= weight + + return tots > self.threshold + + +def main(): + # data + weights = [0.5, -0.2, 0.8] + threshold = 1.0 + + # Instantiate the class + tlu = ThresholdLogicUnit(threshold, weights) + + # Test + test_inputs = [1, 1, 0] + result = tlu.fire(test_inputs) + + print(f"The unit fired: {result}") + +# The "Guard" +if __name__ == "__main__": + main() + + + + \ No newline at end of file diff --git a/Test.py b/Test.py new file mode 100644 index 0000000..e69de29 diff --git a/unlearning/WeightFiltration.py b/unlearning/WeightFiltration.py index e549a31..3ee5bd0 100644 --- a/unlearning/WeightFiltration.py +++ b/unlearning/WeightFiltration.py @@ -50,7 +50,6 @@ class WeightFiltration(Strategy): for epoch in range(self.epochs): t_loss_r, t_loss_f = 0.0, 0.0 steps = 0 - # forget and retain for (r_inputs, r_labels), (f_inputs, f_labels) in zip(retain_loader, forget_loader): r_inputs, r_labels = r_inputs.to(device), r_labels.to(device) diff --git a/unlearning/wf/WF_Net.py b/unlearning/wf/WF_Net.py new file mode 100644 index 0000000..eb50180 --- /dev/null +++ b/unlearning/wf/WF_Net.py @@ -0,0 +1,86 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + +class WF_Net(nn.Module): + """ + Implements Poppi et al.'s WF Model structure. + Wraps a pre-trained ResNet-18 and dynamically applies + weight-space gating matrix multiplication during the forward step. + """ + def __init__(self, original_model: nn.Module, num_classes: int): + super().__init__() + # Extract the sequence of blocks/layers L from the original model + self.conv1 = original_model.conv1 + self.bn1 = original_model.bn1 + self.relu = original_model.relu + self.maxpool = original_model.maxpool + + self.layer1 = original_model.layer1 + self.layer2 = original_model.layer2 + self.layer3 = original_model.layer3 + self.layer4 = original_model.layer4 + + self.avgpool = original_model.avgpool + self.fc = original_model.fc + + # Target layer for filtering: layer4 block 1 conv2 + # We extract its static tensor data out of the autograd parameter pool + self.target_conv = self.layer4[1].conv2 + self.original_w = nn.Parameter(self.target_conv.weight.data.clone().detach(), requires_grad=False) + + # Require: Alpha gating matrix. Shape: (num_classes, out_channels) + # Initialized to 1.5 as per Poppi et al.'s verbatim specification + out_channels = self.original_w.shape[0] + #self.alpha = nn.Parameter(torch.ones(num_classes, out_channels) * 1.5) + self.alpha = nn.Parameter(torch.ones(num_classes, out_channels)) + + def forward(self, x: torch.Tensor, target_class_indices: torch.Tensor) -> torch.Tensor: + # 1. Run through early sequence of layers undisturbed + x = self.maxpool(self.relu(self.bn1(self.conv1(x)))) + x = self.layer1(x) + x = self.layer2(x) + x = self.layer3(x) + + # Run layer4 block 0 normally + x = self.layer4[0](x) + + # ------------------------------------------------------------- + # HERE IT IS: Save the structural skip connection (identity) + # BEFORE modifying features via block 1's convolutions + # ------------------------------------------------------------- + identity = x + + # Now enter layer4 block 1 + x = self.layer4[1].conv1(x) + x = self.layer4[1].bn1(x) + x = self.layer4[1].relu(x) + + # [Your Step 1 Masking Math happens right here...] + batch_alpha = self.alpha[target_class_indices] + mask = torch.sigmoid(batch_alpha).view(x.size(0), -1, 1, 1) + + # Run the functional convolution + x = F.conv2d( + x, + weight=self.original_w, + bias=self.target_conv.bias, + stride=self.target_conv.stride, + padding=self.target_conv.padding + ) + + # Apply your WF-Net channel mask + x = x * mask + x = self.layer4[1].bn2(x) + + # ------------------------------------------------------------- + # HERE IT IS USED: Add the pristine identity back to the gated output + # ------------------------------------------------------------- + x = self.layer4[1].relu(x + identity) + + # Final Classification Head Sequence + x = self.avgpool(x) + x = torch.flatten(x, 1) + y_out = self.fc(x) + + return y_out