1- import tensorflow as tf
1+ import torch
2+ import torch .nn .functional as F
23import numpy as np
34
45class BirdParticle :
56 """
6- Represents a particle in the Particle Swarm Optimization (PSO) algorithm for adversarial attacks.
7-
8- The BirdParticle class encapsulates the state of each particle, including its position, velocity,
9- fitness evaluation, and the updates to its velocity and position based on the PSO algorithm.
7+ Represents a particle in the Particle Swarm Optimization (PSO) algorithm for adversarial attacks (PyTorch version).
108 """
119
12- def __init__ (self , model : tf . keras . Model , input_data : tf .Tensor , target_class : int , num_iterations : int = 20 ,
13- velocity : tf .Tensor = None , inertia_weight : float = 0.5 ,
10+ def __init__ (self , model : torch . nn . Module , input_data : torch .Tensor , target_class : int , num_iterations : int = 20 ,
11+ velocity : torch .Tensor = None , inertia_weight : float = 0.5 ,
1412 cognitive_weight : float = 1.0 , social_weight : float = 1.0 ,
15- momentum : float = 0.9 , clip_value_position : float = 1.0 ):
13+ momentum : float = 0.9 , clip_value_position : float = 1.0 , device = 'cpu' ):
1614 """
1715 Initialize a particle in the PSO algorithm.
1816
1917 Args:
20- model (tf.keras.Model ): The model to attack.
21- input_data (tf .Tensor): The input data (image) to attack.
18+ model (torch.nn.Module ): The model to attack.
19+ input_data (torch .Tensor): The input data (image) to attack.
2220 target_class (int): The target class for misclassification.
23- velocity (tf.Tensor, optional): The initial velocity for the particle's movement. Defaults to zero velocity if not provided.
24- inertia_weight (float): The inertia weight for the velocity update. Default is 0.5.
25- cognitive_weight (float): The cognitive weight for the velocity update. Default is 1.0.
26- social_weight (float): The social weight for the velocity update. Default is 1.0.
27- momentum (float): The momentum for the velocity update. Default is 0.9.
21+ velocity (torch.Tensor, optional): Initial velocity; defaults to zero.
22+ inertia_weight (float): Inertia weight for velocity update.
23+ cognitive_weight (float): Cognitive weight for velocity update.
24+ social_weight (float): Social weight for velocity update.
25+ momentum (float): Momentum for velocity update.
26+ clip_value_position (float): Max absolute value to clip position.
27+ device (str): Device to run on.
2828 """
29- self .model = model
29+ self .device = device
30+ self .model = model .to (device )
3031 self .num_iterations = num_iterations
31- self .original_data = tf .identity (input_data ) # Clone the input data
32+ self .original_data = input_data .clone ().detach ().to (device )
33+ self .position = input_data .clone ().detach ().to (device )
3234 self .target_class = target_class
33- self .best_position = tf .identity (input_data ) # Clone the input data
35+ self .velocity = velocity .clone ().detach ().to (device ) if velocity is not None else torch .zeros_like (input_data ).to (device )
36+ self .best_position = self .position .clone ().detach ()
3437 self .best_score = - np .inf
35- self .position = tf .identity (input_data ) # Clone the input data
36- self .velocity = velocity if velocity is not None else tf .zeros_like (input_data )
37- self .history = [self .position ]
38+ self .history = [self .position .clone ().detach ()]
3839 self .clip_value_position = clip_value_position
39- # Class attributes
40+
41+ # PSO hyperparameters
4042 self .inertia_weight = inertia_weight
4143 self .cognitive_weight = cognitive_weight
4244 self .social_weight = social_weight
@@ -45,52 +47,47 @@ def __init__(self, model: tf.keras.Model, input_data: tf.Tensor, target_class: i
4547 def fitness (self ) -> float :
4648 """
4749 Compute the fitness score for the particle, which is the softmax probability of the target class.
48-
49- Higher fitness scores correspond to better success in the attack (misclassifying the image into the target class).
50-
5150 Returns:
52- float: Fitness score for this particle (higher is better) .
51+ float: Target class softmax probability .
5352 """
54- output = self .model (self .position ) # Add batch dimension and pass through the model
55- probabilities = tf .nn .softmax (output , axis = 1 ) # Get probabilities for each class
56- target_prob = probabilities [:, self .target_class ] # Target class probability
57-
58- return target_prob .numpy ().item () # Return the target class probability as fitness score
53+ self .model .eval ()
54+ with torch .no_grad ():
55+ input_tensor = self .position
56+ output = self .model (input_tensor .to (self .device ))
57+ probabilities = F .softmax (output , dim = 1 )
58+ target_prob = probabilities [:, self .target_class ]
59+ return target_prob .item ()
5960
60- def update_velocity (self , global_best_position : tf .Tensor ) -> None :
61+ def update_velocity (self , global_best_position : torch .Tensor ) -> None :
6162 """
62- Update the velocity of the particle based on the PSO update rule.
63+ Update the particle's velocity using the PSO rule.
6364
6465 Args:
65- global_best_position (tf .Tensor): The global best position found by the swarm.
66+ global_best_position (torch .Tensor): Global best position in the swarm.
6667 """
68+ r1 = torch .rand_like (self .position ).to (self .device )
69+ r2 = torch .rand_like (self .position ).to (self .device )
70+
6771 inertia = self .inertia_weight * self .velocity
68- cognitive = self .cognitive_weight * tf .random .uniform (self .position .shape ) * (self .best_position - self .position )
69- social = self .social_weight * tf .random .uniform (self .position .shape ) * (global_best_position - self .position )
70-
71- # Apply momentum to velocity update:
72- self .velocity = self .momentum * self .velocity + inertia + cognitive + social # Apply momentum
72+ cognitive = self .cognitive_weight * r1 * (self .best_position - self .position )
73+ social = self .social_weight * r2 * (global_best_position .to (self .device ) - self .position )
74+
75+ self .velocity = self .momentum * self .velocity + inertia + cognitive + social
7376
7477 def update_position (self ) -> None :
7578 """
76- Update the position of the particle based on the updated velocity.
77-
78- The position is updated directly without any bounds checking.
79+ Update the particle's position based on the new velocity.
7980 """
80- self .position = self .position + self .velocity # Update position directly without clipping
81- self .position = tf . clip_by_value (self .position + self . velocity , 0 , 1 ) # Ensure position stays within bounds
82- self .history . append ( tf . identity ( self .position )) # Store the position history
83- self .position = tf . clip_by_value (self .position , - self . clip_value_position , self . clip_value_position )
84-
81+ self .position = self .position + self .velocity
82+ self .position = torch . clamp (self .position , 0.0 , 1.0 ) # Keep values in [0, 1]
83+ self .position = torch . clamp ( self .position , - self . clip_value_position , self . clip_value_position )
84+ self .history . append (self .position . clone (). detach () )
85+
8586 def evaluate (self ) -> None :
8687 """
87- Evaluate the fitness of the current particle and update its personal best.
88-
89- The fitness score is calculated using the target class probability. If the current fitness score
90- is better than the personal best, update the best position and score.
88+ Evaluate current fitness and update personal best if needed.
9189 """
92- score = self .fitness () # Get the current fitness score based on the perturbation
93- if score > self .best_score : # If score is better than the personal best, update the best position
90+ score = self .fitness ()
91+ if score > self .best_score :
9492 self .best_score = score
95- self .best_position = tf .identity (self .position ) # Clone the current position
96-
93+ self .best_position = self .position .clone ().detach ()
0 commit comments