perforatedai.network_perforatedai
1from perforatedai import globals_perforatedai as GPA 2from perforatedai import utils_perforatedai as UPA 3import sys 4 5from safetensors.torch import load_file 6import copy 7 8import torch.nn as nn 9import torch 10import pdb 11 12from threading import Thread 13 14 15doing_threading = False 16loaded_full_print = False 17 18 19def convert_network(net, layer_name=""): 20 """Convert a model to use PAI perforated wrappers. 21 22 Parameters 23 ---------- 24 net : nn.Module 25 Model or submodule to convert. 26 layer_name : str, optional 27 Required name when converting a single module directly. 28 29 Returns 30 ------- 31 nn.Module 32 Converted module tree. 33 """ 34 # If the net itself has a substitution make that substitution first 35 if type(net) in GPA.pc.get_modules_to_replace(): 36 net = UPA.replace_predefined_modules(net) 37 # If the net itself should be converted make the converstion 38 if type(net) in GPA.pc.get_modules_to_perforate(): 39 if layer_name == "": 40 print( 41 "converting a single layer without a name, add a layer_name param to the call" 42 ) 43 sys.exit(-1) 44 net = PerforatedModule(net, layer_name) 45 # Otherwise, check the module recursively if there are other modules to convert 46 else: 47 net = UPA.convert_module(net, 0, "", [], [], PerforatedModule, PAITrackedModule) 48 return net 49 50 51def get_pai_modules(net, depth, seen_ids=None): 52 """Collect unique PerforatedModule instances from a module tree. 53 54 Parameters 55 ---------- 56 net : nn.Module 57 Root module to traverse. 58 depth : int 59 Current recursion depth. 60 seen_ids : set or None, optional 61 Set of module object IDs already collected. 62 63 Returns 64 ------- 65 list 66 List of unique ``PerforatedModule`` objects. 67 """ 68 if seen_ids is None: 69 seen_ids = set() 70 all_members = net.__dir__() 71 this_list = [] 72 if issubclass(type(net), nn.Sequential) or issubclass(type(net), nn.ModuleList): 73 for submodule_id, layer in net.named_children(): 74 if net.get_submodule(submodule_id) is net: 75 continue 76 if type(net.get_submodule(submodule_id)) is PerforatedModule: 77 module = net.get_submodule(submodule_id) 78 if id(module) in seen_ids: 79 continue 80 seen_ids.add(id(module)) 81 this_list = this_list + [module] 82 else: 83 this_list = this_list + get_pai_modules( 84 net.get_submodule(submodule_id), depth + 1, seen_ids 85 ) 86 else: 87 for member in all_members: 88 if isinstance(getattr(type(net), member, None), property): 89 continue 90 if getattr(net, member, None) is net: 91 continue 92 if type(getattr(net, member, None)) is PerforatedModule: 93 module = getattr(net, member) 94 if id(module) in seen_ids: 95 continue 96 seen_ids.add(id(module)) 97 this_list = this_list + [module] 98 elif issubclass(type(getattr(net, member, None)), nn.Module): 99 this_list = this_list + get_pai_modules( 100 getattr(net, member), depth + 1, seen_ids 101 ) 102 return this_list 103 104 105def load_pai_model_from_dict(net, state_dict): 106 """Load PAI-specific module state from a state dictionary. 107 108 Parameters 109 ---------- 110 net : nn.Module 111 Converted network containing perforated modules. 112 state_dict : dict 113 Serialized model state. 114 115 Returns 116 ------- 117 nn.Module 118 Network with loaded state and reconstructed runtime buffers. 119 """ 120 pai_modules = get_pai_modules(net, 0) 121 if pai_modules == []: 122 print("No PAI modules were found something went wrong with convert network") 123 pdb.set_trace() 124 sys.exit() 125 for module in pai_modules: 126 # Set up name to be what will be saved in the state dict 127 module_name = UPA.get_module_base_name(module) 128 # Then instantiate as many Dendrites as were created during training 129 num_cycles = int(state_dict[module_name + ".num_cycles"].item()) 130 # extract node index from state_dict 131 nodeCount = 10 132 # also extract view tuple 133 if num_cycles > 0: 134 module.simulate_cycles(num_cycles, nodeCount) 135 if not module.processor is None: 136 processor = copy.deepcopy(module.processor) 137 processor.pre = module.processor.post_n1 138 processor.post = module.processor.post_n2 139 module.processor_array.append(processor) 140 else: 141 module.processor_array.append(None) 142 143 # Create ParameterList for skip_weights based on num_cycles 144 num_params = num_cycles // 2 145 skip_weights_list = nn.ParameterList() 146 for i in range(num_params): 147 param_key = module_name + f".skip_weights.{i}" 148 if param_key in state_dict: 149 param = nn.Parameter(torch.randn(state_dict[param_key].shape)) 150 skip_weights_list.append(param) 151 module.skip_weights = skip_weights_list 152 153 # module.register_buffer('skip_weights', torch.zeros(state_dict[module_name + '.skip_weights'].shape)) 154 module.register_buffer("view_tuple", state_dict[module_name + ".view_tuple"]) 155 156 net.load_state_dict(state_dict) 157 158 for module in pai_modules: 159 temp = tuple(module.view_tuple.tolist()) 160 del module.view_tuple 161 module.view_tuple = temp 162 163 return net 164 # figure out if doing this 'thread' stuff is actually helping at all. 165 # If its not just get rid of it to simplify things. 166 # to test this will have to first get load_pai_model actually set up and working then run a test with and #without threading. 167 168 169def load_pai_model(net, filename): 170 """Load a saved PAI model file into a converted network. 171 172 Parameters 173 ---------- 174 net : nn.Module 175 Base network architecture. 176 filename : str 177 Path to a safetensors state file. 178 179 Returns 180 ------- 181 nn.Module 182 Loaded PAI network. 183 """ 184 net = convert_network(net) 185 state_dict = load_file(filename) 186 return load_pai_model_from_dict(net, state_dict) 187 188 189class PerforatedModule(nn.Module): 190 def __init__(self, original_module, name): 191 """Initialize a perforated wrapper around a single module. 192 193 Parameters 194 ---------- 195 original_module : nn.Module 196 Original module being wrapped. 197 name : str 198 Qualified module name used for save/load mapping. 199 """ 200 super(PerforatedModule, self).__init__() 201 self.name = name 202 self.register_buffer("node_index", torch.tensor(-1)) 203 self.register_buffer("num_cycles", torch.tensor(-1)) 204 self.register_buffer("view_tuple", torch.tensor(-1)) 205 self.processor_array = [] 206 self.processor = None 207 self.layer_array = nn.ModuleList([original_module]) 208 # If this original module has processing functions save the processor 209 if type(original_module) in GPA.pc.get_modules_with_processing(): 210 module_index = GPA.pc.get_modules_with_processing().index( 211 type(original_module) 212 ) 213 self.processor = GPA.pc.get_modules_processing_classes()[module_index]() 214 elif ( 215 type(original_module).__name__ in GPA.pc.get_module_names_with_processing() 216 ): 217 module_index = GPA.pc.get_module_names_with_processing().index( 218 type(original_module).__name__ 219 ) 220 self.processor = GPA.pc.get_module_by_name_processing_classes()[ 221 module_index 222 ]() 223 224 def simulate_cycles(self, num_cycles, nodeCount): 225 """Expand internal layer/processor lists for stored dendrite cycles. 226 227 Parameters 228 ---------- 229 num_cycles : int 230 Number of perforation cycles represented in saved state. 231 nodeCount : int 232 Node count hint kept for interface compatibility. 233 234 Returns 235 ------- 236 None 237 This function does not return a value. 238 """ 239 for i in range(0, num_cycles, 2): 240 self.layer_array.append(copy.deepcopy(self.layer_array[0])) 241 if not self.processor is None: 242 processor = copy.deepcopy(self.processor) 243 processor.pre = self.processor.pre_d 244 processor.post = self.processor.post_d 245 self.processor_array.append(processor) 246 else: 247 self.processor_array.append(None) 248 249 def process_and_forward(self, *args2, **kwargs2): 250 """Execute one dendrite layer and write output into shared storage. 251 252 Parameters 253 ---------- 254 *args2 : tuple 255 Positional values where first entries are layer index and output 256 buffer, followed by layer inputs. 257 **kwargs2 : dict 258 Keyword arguments forwarded to the wrapped layer. 259 260 Returns 261 ------- 262 None 263 This function does not return a value. 264 """ 265 c = args2[0] 266 dendrite_outs = args2[1] 267 args2 = args2[2:] 268 if self.processor_array[c] != None: 269 out_values = self.processor_array[c].pre(*args2, **kwargs2) 270 out_values = self.layer_array[c](*args2, **kwargs2) 271 if self.processor_array[c] != None: 272 out = self.processor_array[c].post(out_values) 273 else: 274 out = out_values 275 dendrite_outs[c] = out 276 277 def process_and_pre(self, *args, **kwargs): 278 """Run the final pre-dendrite layer pass and cache its output. 279 280 Parameters 281 ---------- 282 *args : tuple 283 Positional values with output buffer first, then model inputs. 284 **kwargs : dict 285 Keyword arguments forwarded to the wrapped layer. 286 287 Returns 288 ------- 289 None 290 This function does not return a value. 291 """ 292 dendrite_outs = args[0] 293 args = args[1:] 294 out = self.layer_array[-1].forward(*args, **kwargs) 295 if not self.processor_array[-1] is None: 296 out = self.processor_array[-1].pre(out) 297 dendrite_outs[len(self.layer_array) - 1] = out 298 299 def forward(self, *args, **kwargs): 300 """Run perforated forward pass with dendrite accumulation. 301 302 Parameters 303 ---------- 304 *args : tuple 305 Positional arguments forwarded through wrapped layers. 306 **kwargs : dict 307 Keyword arguments forwarded through wrapped layers. 308 309 Returns 310 ------- 311 Any 312 Final model output after optional processor post-processing. 313 """ 314 # this is currently false anyway, just remove the doing multi idea 315 doing_multi = doing_threading 316 dendrite_outs = [None] * len(self.layer_array) 317 threads = {} 318 for c in range(0, len(self.layer_array) - 1): 319 args2, kwargs2 = args, kwargs 320 if doing_multi: 321 threads[c] = Thread( 322 target=self.process_and_forward, 323 args=(c, dendrite_outs, *args), 324 kwargs=kwargs, 325 ) 326 else: 327 self.process_and_forward(c, dendrite_outs, *args2, **kwargs2) 328 if doing_multi: 329 threads[len(self.layer_array) - 1] = Thread( 330 target=self.process_and_pre, args=(dendrite_outs, *args), kwargs=kwargs 331 ) 332 else: 333 self.process_and_pre(dendrite_outs, *args, **kwargs) 334 if doing_multi: 335 for i in range(len(dendrite_outs)): 336 threads[i].start() 337 for i in range(len(dendrite_outs)): 338 threads[i].join() 339 for out_index in range(0, len(self.layer_array)): 340 current_out = dendrite_outs[out_index] 341 342 if len(self.layer_array) > 1 and hasattr(self, "skip_weights"): 343 for in_index in range(0, out_index): 344 # Use out_index - 1 because skip_weights[0] is never used 345 current_out = ( 346 current_out 347 + self.skip_weights[out_index - 1][in_index, :] 348 .reshape(self.view_tuple) 349 .to(current_out.device) 350 * dendrite_outs[in_index] 351 ) 352 if out_index < len(self.layer_array) - 1: 353 current_out = GPA.pc.get_pai_forward_function()(current_out) 354 dendrite_outs[out_index] = current_out 355 if not self.processor_array[-1] is None: 356 current_out = self.processor_array[-1].post(current_out) 357 return current_out 358 359 360class PAITrackedModule(nn.Module): 361 """Wrapper for modules you don't want to add dendrites to. Ensures all modules are accounted for.""" 362 363 def __init__(self, start_module, name): 364 """Initialize PAITrackedModule. 365 366 This function sets up the tracked neuron module to wrap the start_module 367 without adding dendrites. 368 369 Parameters 370 ---------- 371 start_module : nn.Module 372 The module to wrap. 373 name : str 374 The name of the neuron module. 375 """ 376 super(PAITrackedModule, self).__init__() 377 378 if isinstance(start_module, nn.Module): 379 self.main_module = start_module 380 else: 381 print("start_module must be nn.Module: %s" % name) 382 print(type(start_module)) 383 print(start_module) 384 sys.exit(-1) 385 self.name = name 386 387 self.type = "tracked_module" 388 389 def __getattr__(self, name): 390 """Get member variables from the main module. 391 392 Parameters 393 ---------- 394 name : str 395 The name of the variable to retrieve. 396 Returns 397 ------- 398 The requested variable. 399 400 Notes 401 ----- 402 This method first attempts to retrieve the attribute from the PAINeuronModule instance. 403 If it fails, it tries to get the attribute from the wrapped main_module. 404 This allows seamless access to the main module's attributes without modifying original code. 405 """ 406 try: 407 return super().__getattr__(name) 408 except AttributeError: 409 return getattr(self.main_module, name) 410 411 def forward(self, *args, **kwargs): 412 """Forward pass for tracked layer. 413 414 Parameters 415 ---------- 416 *args : tuple 417 Positional arguments for the forward pass. 418 **kwargs : dict 419 Keyword arguments for the forward pass. 420 421 Returns 422 ------- 423 Any 424 The output of the module 425 426 Notes 427 ----- 428 The output of this forward function will have the same format as the output 429 of the original module 430 """ 431 return self.main_module(*args, **kwargs) 432 433 def __str__(self): 434 """String representation of the layer. 435 436 Parameters 437 ---------- 438 None 439 440 Returns 441 ------- 442 str 443 String representation of the layer. 444 445 Notes 446 ----- 447 Setting for verbose changes level of details in the string output. 448 """ 449 450 if GPA.pc.get_verbose(): 451 total_string = self.main_module.__str__() 452 total_string = "PAITrackedLayer(" + total_string + ")" 453 return total_string 454 else: 455 total_string = self.main_module.__str__() 456 total_string = "PAITrackedLayer(" + total_string + ")" 457 return total_string 458 459 def __repr__(self): 460 """Representation of the layer.""" 461 return self.__str__()
20def convert_network(net, layer_name=""): 21 """Convert a model to use PAI perforated wrappers. 22 23 Parameters 24 ---------- 25 net : nn.Module 26 Model or submodule to convert. 27 layer_name : str, optional 28 Required name when converting a single module directly. 29 30 Returns 31 ------- 32 nn.Module 33 Converted module tree. 34 """ 35 # If the net itself has a substitution make that substitution first 36 if type(net) in GPA.pc.get_modules_to_replace(): 37 net = UPA.replace_predefined_modules(net) 38 # If the net itself should be converted make the converstion 39 if type(net) in GPA.pc.get_modules_to_perforate(): 40 if layer_name == "": 41 print( 42 "converting a single layer without a name, add a layer_name param to the call" 43 ) 44 sys.exit(-1) 45 net = PerforatedModule(net, layer_name) 46 # Otherwise, check the module recursively if there are other modules to convert 47 else: 48 net = UPA.convert_module(net, 0, "", [], [], PerforatedModule, PAITrackedModule) 49 return net
Convert a model to use PAI perforated wrappers.
Parameters
- net (nn.Module): Model or submodule to convert.
- layer_name (str, optional): Required name when converting a single module directly.
Returns
- nn.Module: Converted module tree.
52def get_pai_modules(net, depth, seen_ids=None): 53 """Collect unique PerforatedModule instances from a module tree. 54 55 Parameters 56 ---------- 57 net : nn.Module 58 Root module to traverse. 59 depth : int 60 Current recursion depth. 61 seen_ids : set or None, optional 62 Set of module object IDs already collected. 63 64 Returns 65 ------- 66 list 67 List of unique ``PerforatedModule`` objects. 68 """ 69 if seen_ids is None: 70 seen_ids = set() 71 all_members = net.__dir__() 72 this_list = [] 73 if issubclass(type(net), nn.Sequential) or issubclass(type(net), nn.ModuleList): 74 for submodule_id, layer in net.named_children(): 75 if net.get_submodule(submodule_id) is net: 76 continue 77 if type(net.get_submodule(submodule_id)) is PerforatedModule: 78 module = net.get_submodule(submodule_id) 79 if id(module) in seen_ids: 80 continue 81 seen_ids.add(id(module)) 82 this_list = this_list + [module] 83 else: 84 this_list = this_list + get_pai_modules( 85 net.get_submodule(submodule_id), depth + 1, seen_ids 86 ) 87 else: 88 for member in all_members: 89 if isinstance(getattr(type(net), member, None), property): 90 continue 91 if getattr(net, member, None) is net: 92 continue 93 if type(getattr(net, member, None)) is PerforatedModule: 94 module = getattr(net, member) 95 if id(module) in seen_ids: 96 continue 97 seen_ids.add(id(module)) 98 this_list = this_list + [module] 99 elif issubclass(type(getattr(net, member, None)), nn.Module): 100 this_list = this_list + get_pai_modules( 101 getattr(net, member), depth + 1, seen_ids 102 ) 103 return this_list
Collect unique PerforatedModule instances from a module tree.
Parameters
- net (nn.Module): Root module to traverse.
- depth (int): Current recursion depth.
- seen_ids (set or None, optional): Set of module object IDs already collected.
Returns
- list: List of unique
PerforatedModuleobjects.
106def load_pai_model_from_dict(net, state_dict): 107 """Load PAI-specific module state from a state dictionary. 108 109 Parameters 110 ---------- 111 net : nn.Module 112 Converted network containing perforated modules. 113 state_dict : dict 114 Serialized model state. 115 116 Returns 117 ------- 118 nn.Module 119 Network with loaded state and reconstructed runtime buffers. 120 """ 121 pai_modules = get_pai_modules(net, 0) 122 if pai_modules == []: 123 print("No PAI modules were found something went wrong with convert network") 124 pdb.set_trace() 125 sys.exit() 126 for module in pai_modules: 127 # Set up name to be what will be saved in the state dict 128 module_name = UPA.get_module_base_name(module) 129 # Then instantiate as many Dendrites as were created during training 130 num_cycles = int(state_dict[module_name + ".num_cycles"].item()) 131 # extract node index from state_dict 132 nodeCount = 10 133 # also extract view tuple 134 if num_cycles > 0: 135 module.simulate_cycles(num_cycles, nodeCount) 136 if not module.processor is None: 137 processor = copy.deepcopy(module.processor) 138 processor.pre = module.processor.post_n1 139 processor.post = module.processor.post_n2 140 module.processor_array.append(processor) 141 else: 142 module.processor_array.append(None) 143 144 # Create ParameterList for skip_weights based on num_cycles 145 num_params = num_cycles // 2 146 skip_weights_list = nn.ParameterList() 147 for i in range(num_params): 148 param_key = module_name + f".skip_weights.{i}" 149 if param_key in state_dict: 150 param = nn.Parameter(torch.randn(state_dict[param_key].shape)) 151 skip_weights_list.append(param) 152 module.skip_weights = skip_weights_list 153 154 # module.register_buffer('skip_weights', torch.zeros(state_dict[module_name + '.skip_weights'].shape)) 155 module.register_buffer("view_tuple", state_dict[module_name + ".view_tuple"]) 156 157 net.load_state_dict(state_dict) 158 159 for module in pai_modules: 160 temp = tuple(module.view_tuple.tolist()) 161 del module.view_tuple 162 module.view_tuple = temp 163 164 return net 165 # figure out if doing this 'thread' stuff is actually helping at all. 166 # If its not just get rid of it to simplify things. 167 # to test this will have to first get load_pai_model actually set up and working then run a test with and #without threading.
Load PAI-specific module state from a state dictionary.
Parameters
- net (nn.Module): Converted network containing perforated modules.
- state_dict (dict): Serialized model state.
Returns
- nn.Module: Network with loaded state and reconstructed runtime buffers.
170def load_pai_model(net, filename): 171 """Load a saved PAI model file into a converted network. 172 173 Parameters 174 ---------- 175 net : nn.Module 176 Base network architecture. 177 filename : str 178 Path to a safetensors state file. 179 180 Returns 181 ------- 182 nn.Module 183 Loaded PAI network. 184 """ 185 net = convert_network(net) 186 state_dict = load_file(filename) 187 return load_pai_model_from_dict(net, state_dict)
Load a saved PAI model file into a converted network.
Parameters
- net (nn.Module): Base network architecture.
- filename (str): Path to a safetensors state file.
Returns
- nn.Module: Loaded PAI network.
190class PerforatedModule(nn.Module): 191 def __init__(self, original_module, name): 192 """Initialize a perforated wrapper around a single module. 193 194 Parameters 195 ---------- 196 original_module : nn.Module 197 Original module being wrapped. 198 name : str 199 Qualified module name used for save/load mapping. 200 """ 201 super(PerforatedModule, self).__init__() 202 self.name = name 203 self.register_buffer("node_index", torch.tensor(-1)) 204 self.register_buffer("num_cycles", torch.tensor(-1)) 205 self.register_buffer("view_tuple", torch.tensor(-1)) 206 self.processor_array = [] 207 self.processor = None 208 self.layer_array = nn.ModuleList([original_module]) 209 # If this original module has processing functions save the processor 210 if type(original_module) in GPA.pc.get_modules_with_processing(): 211 module_index = GPA.pc.get_modules_with_processing().index( 212 type(original_module) 213 ) 214 self.processor = GPA.pc.get_modules_processing_classes()[module_index]() 215 elif ( 216 type(original_module).__name__ in GPA.pc.get_module_names_with_processing() 217 ): 218 module_index = GPA.pc.get_module_names_with_processing().index( 219 type(original_module).__name__ 220 ) 221 self.processor = GPA.pc.get_module_by_name_processing_classes()[ 222 module_index 223 ]() 224 225 def simulate_cycles(self, num_cycles, nodeCount): 226 """Expand internal layer/processor lists for stored dendrite cycles. 227 228 Parameters 229 ---------- 230 num_cycles : int 231 Number of perforation cycles represented in saved state. 232 nodeCount : int 233 Node count hint kept for interface compatibility. 234 235 Returns 236 ------- 237 None 238 This function does not return a value. 239 """ 240 for i in range(0, num_cycles, 2): 241 self.layer_array.append(copy.deepcopy(self.layer_array[0])) 242 if not self.processor is None: 243 processor = copy.deepcopy(self.processor) 244 processor.pre = self.processor.pre_d 245 processor.post = self.processor.post_d 246 self.processor_array.append(processor) 247 else: 248 self.processor_array.append(None) 249 250 def process_and_forward(self, *args2, **kwargs2): 251 """Execute one dendrite layer and write output into shared storage. 252 253 Parameters 254 ---------- 255 *args2 : tuple 256 Positional values where first entries are layer index and output 257 buffer, followed by layer inputs. 258 **kwargs2 : dict 259 Keyword arguments forwarded to the wrapped layer. 260 261 Returns 262 ------- 263 None 264 This function does not return a value. 265 """ 266 c = args2[0] 267 dendrite_outs = args2[1] 268 args2 = args2[2:] 269 if self.processor_array[c] != None: 270 out_values = self.processor_array[c].pre(*args2, **kwargs2) 271 out_values = self.layer_array[c](*args2, **kwargs2) 272 if self.processor_array[c] != None: 273 out = self.processor_array[c].post(out_values) 274 else: 275 out = out_values 276 dendrite_outs[c] = out 277 278 def process_and_pre(self, *args, **kwargs): 279 """Run the final pre-dendrite layer pass and cache its output. 280 281 Parameters 282 ---------- 283 *args : tuple 284 Positional values with output buffer first, then model inputs. 285 **kwargs : dict 286 Keyword arguments forwarded to the wrapped layer. 287 288 Returns 289 ------- 290 None 291 This function does not return a value. 292 """ 293 dendrite_outs = args[0] 294 args = args[1:] 295 out = self.layer_array[-1].forward(*args, **kwargs) 296 if not self.processor_array[-1] is None: 297 out = self.processor_array[-1].pre(out) 298 dendrite_outs[len(self.layer_array) - 1] = out 299 300 def forward(self, *args, **kwargs): 301 """Run perforated forward pass with dendrite accumulation. 302 303 Parameters 304 ---------- 305 *args : tuple 306 Positional arguments forwarded through wrapped layers. 307 **kwargs : dict 308 Keyword arguments forwarded through wrapped layers. 309 310 Returns 311 ------- 312 Any 313 Final model output after optional processor post-processing. 314 """ 315 # this is currently false anyway, just remove the doing multi idea 316 doing_multi = doing_threading 317 dendrite_outs = [None] * len(self.layer_array) 318 threads = {} 319 for c in range(0, len(self.layer_array) - 1): 320 args2, kwargs2 = args, kwargs 321 if doing_multi: 322 threads[c] = Thread( 323 target=self.process_and_forward, 324 args=(c, dendrite_outs, *args), 325 kwargs=kwargs, 326 ) 327 else: 328 self.process_and_forward(c, dendrite_outs, *args2, **kwargs2) 329 if doing_multi: 330 threads[len(self.layer_array) - 1] = Thread( 331 target=self.process_and_pre, args=(dendrite_outs, *args), kwargs=kwargs 332 ) 333 else: 334 self.process_and_pre(dendrite_outs, *args, **kwargs) 335 if doing_multi: 336 for i in range(len(dendrite_outs)): 337 threads[i].start() 338 for i in range(len(dendrite_outs)): 339 threads[i].join() 340 for out_index in range(0, len(self.layer_array)): 341 current_out = dendrite_outs[out_index] 342 343 if len(self.layer_array) > 1 and hasattr(self, "skip_weights"): 344 for in_index in range(0, out_index): 345 # Use out_index - 1 because skip_weights[0] is never used 346 current_out = ( 347 current_out 348 + self.skip_weights[out_index - 1][in_index, :] 349 .reshape(self.view_tuple) 350 .to(current_out.device) 351 * dendrite_outs[in_index] 352 ) 353 if out_index < len(self.layer_array) - 1: 354 current_out = GPA.pc.get_pai_forward_function()(current_out) 355 dendrite_outs[out_index] = current_out 356 if not self.processor_array[-1] is None: 357 current_out = self.processor_array[-1].post(current_out) 358 return current_out
Base class for all neural network modules.
Your models should also subclass this class.
Modules can also contain other Modules, allowing them to be nested in a tree structure. You can assign the submodules as regular attributes::
import torch.nn as nn
import torch.nn.functional as F
class Model(nn.Module):
def __init__(self) -> None:
super().__init__()
self.conv1 = nn.Conv2d(1, 20, 5)
self.conv2 = nn.Conv2d(20, 20, 5)
def forward(self, x):
x = F.relu(self.conv1(x))
return F.relu(self.conv2(x))
Submodules assigned in this way will be registered, and will also have their
parameters converted when you call to(), etc.
As per the example above, an __init__() call to the parent class
must be made before assignment on the child.
:ivar training: Boolean represents whether this module is in training or evaluation mode. :vartype training: bool
191 def __init__(self, original_module, name): 192 """Initialize a perforated wrapper around a single module. 193 194 Parameters 195 ---------- 196 original_module : nn.Module 197 Original module being wrapped. 198 name : str 199 Qualified module name used for save/load mapping. 200 """ 201 super(PerforatedModule, self).__init__() 202 self.name = name 203 self.register_buffer("node_index", torch.tensor(-1)) 204 self.register_buffer("num_cycles", torch.tensor(-1)) 205 self.register_buffer("view_tuple", torch.tensor(-1)) 206 self.processor_array = [] 207 self.processor = None 208 self.layer_array = nn.ModuleList([original_module]) 209 # If this original module has processing functions save the processor 210 if type(original_module) in GPA.pc.get_modules_with_processing(): 211 module_index = GPA.pc.get_modules_with_processing().index( 212 type(original_module) 213 ) 214 self.processor = GPA.pc.get_modules_processing_classes()[module_index]() 215 elif ( 216 type(original_module).__name__ in GPA.pc.get_module_names_with_processing() 217 ): 218 module_index = GPA.pc.get_module_names_with_processing().index( 219 type(original_module).__name__ 220 ) 221 self.processor = GPA.pc.get_module_by_name_processing_classes()[ 222 module_index 223 ]()
Initialize a perforated wrapper around a single module.
Parameters
- original_module (nn.Module): Original module being wrapped.
- name (str): Qualified module name used for save/load mapping.
225 def simulate_cycles(self, num_cycles, nodeCount): 226 """Expand internal layer/processor lists for stored dendrite cycles. 227 228 Parameters 229 ---------- 230 num_cycles : int 231 Number of perforation cycles represented in saved state. 232 nodeCount : int 233 Node count hint kept for interface compatibility. 234 235 Returns 236 ------- 237 None 238 This function does not return a value. 239 """ 240 for i in range(0, num_cycles, 2): 241 self.layer_array.append(copy.deepcopy(self.layer_array[0])) 242 if not self.processor is None: 243 processor = copy.deepcopy(self.processor) 244 processor.pre = self.processor.pre_d 245 processor.post = self.processor.post_d 246 self.processor_array.append(processor) 247 else: 248 self.processor_array.append(None)
Expand internal layer/processor lists for stored dendrite cycles.
Parameters
- num_cycles (int): Number of perforation cycles represented in saved state.
- nodeCount (int): Node count hint kept for interface compatibility.
Returns
- None: This function does not return a value.
250 def process_and_forward(self, *args2, **kwargs2): 251 """Execute one dendrite layer and write output into shared storage. 252 253 Parameters 254 ---------- 255 *args2 : tuple 256 Positional values where first entries are layer index and output 257 buffer, followed by layer inputs. 258 **kwargs2 : dict 259 Keyword arguments forwarded to the wrapped layer. 260 261 Returns 262 ------- 263 None 264 This function does not return a value. 265 """ 266 c = args2[0] 267 dendrite_outs = args2[1] 268 args2 = args2[2:] 269 if self.processor_array[c] != None: 270 out_values = self.processor_array[c].pre(*args2, **kwargs2) 271 out_values = self.layer_array[c](*args2, **kwargs2) 272 if self.processor_array[c] != None: 273 out = self.processor_array[c].post(out_values) 274 else: 275 out = out_values 276 dendrite_outs[c] = out
Execute one dendrite layer and write output into shared storage.
Parameters
- *args2 (tuple): Positional values where first entries are layer index and output buffer, followed by layer inputs.
- **kwargs2 (dict): Keyword arguments forwarded to the wrapped layer.
Returns
- None: This function does not return a value.
278 def process_and_pre(self, *args, **kwargs): 279 """Run the final pre-dendrite layer pass and cache its output. 280 281 Parameters 282 ---------- 283 *args : tuple 284 Positional values with output buffer first, then model inputs. 285 **kwargs : dict 286 Keyword arguments forwarded to the wrapped layer. 287 288 Returns 289 ------- 290 None 291 This function does not return a value. 292 """ 293 dendrite_outs = args[0] 294 args = args[1:] 295 out = self.layer_array[-1].forward(*args, **kwargs) 296 if not self.processor_array[-1] is None: 297 out = self.processor_array[-1].pre(out) 298 dendrite_outs[len(self.layer_array) - 1] = out
Run the final pre-dendrite layer pass and cache its output.
Parameters
- *args (tuple): Positional values with output buffer first, then model inputs.
- **kwargs (dict): Keyword arguments forwarded to the wrapped layer.
Returns
- None: This function does not return a value.
300 def forward(self, *args, **kwargs): 301 """Run perforated forward pass with dendrite accumulation. 302 303 Parameters 304 ---------- 305 *args : tuple 306 Positional arguments forwarded through wrapped layers. 307 **kwargs : dict 308 Keyword arguments forwarded through wrapped layers. 309 310 Returns 311 ------- 312 Any 313 Final model output after optional processor post-processing. 314 """ 315 # this is currently false anyway, just remove the doing multi idea 316 doing_multi = doing_threading 317 dendrite_outs = [None] * len(self.layer_array) 318 threads = {} 319 for c in range(0, len(self.layer_array) - 1): 320 args2, kwargs2 = args, kwargs 321 if doing_multi: 322 threads[c] = Thread( 323 target=self.process_and_forward, 324 args=(c, dendrite_outs, *args), 325 kwargs=kwargs, 326 ) 327 else: 328 self.process_and_forward(c, dendrite_outs, *args2, **kwargs2) 329 if doing_multi: 330 threads[len(self.layer_array) - 1] = Thread( 331 target=self.process_and_pre, args=(dendrite_outs, *args), kwargs=kwargs 332 ) 333 else: 334 self.process_and_pre(dendrite_outs, *args, **kwargs) 335 if doing_multi: 336 for i in range(len(dendrite_outs)): 337 threads[i].start() 338 for i in range(len(dendrite_outs)): 339 threads[i].join() 340 for out_index in range(0, len(self.layer_array)): 341 current_out = dendrite_outs[out_index] 342 343 if len(self.layer_array) > 1 and hasattr(self, "skip_weights"): 344 for in_index in range(0, out_index): 345 # Use out_index - 1 because skip_weights[0] is never used 346 current_out = ( 347 current_out 348 + self.skip_weights[out_index - 1][in_index, :] 349 .reshape(self.view_tuple) 350 .to(current_out.device) 351 * dendrite_outs[in_index] 352 ) 353 if out_index < len(self.layer_array) - 1: 354 current_out = GPA.pc.get_pai_forward_function()(current_out) 355 dendrite_outs[out_index] = current_out 356 if not self.processor_array[-1] is None: 357 current_out = self.processor_array[-1].post(current_out) 358 return current_out
Run perforated forward pass with dendrite accumulation.
Parameters
- *args (tuple): Positional arguments forwarded through wrapped layers.
- **kwargs (dict): Keyword arguments forwarded through wrapped layers.
Returns
- Any: Final model output after optional processor post-processing.
361class PAITrackedModule(nn.Module): 362 """Wrapper for modules you don't want to add dendrites to. Ensures all modules are accounted for.""" 363 364 def __init__(self, start_module, name): 365 """Initialize PAITrackedModule. 366 367 This function sets up the tracked neuron module to wrap the start_module 368 without adding dendrites. 369 370 Parameters 371 ---------- 372 start_module : nn.Module 373 The module to wrap. 374 name : str 375 The name of the neuron module. 376 """ 377 super(PAITrackedModule, self).__init__() 378 379 if isinstance(start_module, nn.Module): 380 self.main_module = start_module 381 else: 382 print("start_module must be nn.Module: %s" % name) 383 print(type(start_module)) 384 print(start_module) 385 sys.exit(-1) 386 self.name = name 387 388 self.type = "tracked_module" 389 390 def __getattr__(self, name): 391 """Get member variables from the main module. 392 393 Parameters 394 ---------- 395 name : str 396 The name of the variable to retrieve. 397 Returns 398 ------- 399 The requested variable. 400 401 Notes 402 ----- 403 This method first attempts to retrieve the attribute from the PAINeuronModule instance. 404 If it fails, it tries to get the attribute from the wrapped main_module. 405 This allows seamless access to the main module's attributes without modifying original code. 406 """ 407 try: 408 return super().__getattr__(name) 409 except AttributeError: 410 return getattr(self.main_module, name) 411 412 def forward(self, *args, **kwargs): 413 """Forward pass for tracked layer. 414 415 Parameters 416 ---------- 417 *args : tuple 418 Positional arguments for the forward pass. 419 **kwargs : dict 420 Keyword arguments for the forward pass. 421 422 Returns 423 ------- 424 Any 425 The output of the module 426 427 Notes 428 ----- 429 The output of this forward function will have the same format as the output 430 of the original module 431 """ 432 return self.main_module(*args, **kwargs) 433 434 def __str__(self): 435 """String representation of the layer. 436 437 Parameters 438 ---------- 439 None 440 441 Returns 442 ------- 443 str 444 String representation of the layer. 445 446 Notes 447 ----- 448 Setting for verbose changes level of details in the string output. 449 """ 450 451 if GPA.pc.get_verbose(): 452 total_string = self.main_module.__str__() 453 total_string = "PAITrackedLayer(" + total_string + ")" 454 return total_string 455 else: 456 total_string = self.main_module.__str__() 457 total_string = "PAITrackedLayer(" + total_string + ")" 458 return total_string 459 460 def __repr__(self): 461 """Representation of the layer.""" 462 return self.__str__()
Wrapper for modules you don't want to add dendrites to. Ensures all modules are accounted for.
364 def __init__(self, start_module, name): 365 """Initialize PAITrackedModule. 366 367 This function sets up the tracked neuron module to wrap the start_module 368 without adding dendrites. 369 370 Parameters 371 ---------- 372 start_module : nn.Module 373 The module to wrap. 374 name : str 375 The name of the neuron module. 376 """ 377 super(PAITrackedModule, self).__init__() 378 379 if isinstance(start_module, nn.Module): 380 self.main_module = start_module 381 else: 382 print("start_module must be nn.Module: %s" % name) 383 print(type(start_module)) 384 print(start_module) 385 sys.exit(-1) 386 self.name = name 387 388 self.type = "tracked_module"
Initialize PAITrackedModule.
This function sets up the tracked neuron module to wrap the start_module without adding dendrites.
Parameters
- start_module (nn.Module): The module to wrap.
- name (str): The name of the neuron module.
1167 def type(self, dst_type: dtype | str) -> Self: 1168 r"""Casts all parameters and buffers to :attr:`dst_type`. 1169 1170 .. note:: 1171 This method modifies the module in-place. 1172 1173 Args: 1174 dst_type (type or string): the desired type 1175 1176 Returns: 1177 Module: self 1178 """ 1179 return self._apply(lambda t: t.type(dst_type))
Casts all parameters and buffers to dst_type.
This method modifies the module in-place.
Args: dst_type (type or string): the desired type
Returns: Module: self
412 def forward(self, *args, **kwargs): 413 """Forward pass for tracked layer. 414 415 Parameters 416 ---------- 417 *args : tuple 418 Positional arguments for the forward pass. 419 **kwargs : dict 420 Keyword arguments for the forward pass. 421 422 Returns 423 ------- 424 Any 425 The output of the module 426 427 Notes 428 ----- 429 The output of this forward function will have the same format as the output 430 of the original module 431 """ 432 return self.main_module(*args, **kwargs)
Forward pass for tracked layer.
Parameters
- *args (tuple): Positional arguments for the forward pass.
- **kwargs (dict): Keyword arguments for the forward pass.
Returns
- Any: The output of the module
Notes
The output of this forward function will have the same format as the output of the original module