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 a PAI or plain model from a state dictionary into an unconverted network. 107 108 Parameters 109 ---------- 110 net : nn.Module 111 Base network architecture (not yet converted to PerforatedModules). 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 # Find which module paths need PAI wrapping from the state dict 121 pai_module_names = set( 122 key[: -len(".num_cycles")] 123 for key in state_dict.keys() 124 if key.endswith(".num_cycles") 125 ) 126 127 # Clean up tracked-module scaffolding: for any key containing .main_module. 128 # where the prefix is not a PAI module, strip .main_module. out so the 129 # weights load directly into the plain module. Also drop any module_id keys. 130 cleaned = {} 131 for key, value in state_dict.items(): 132 if ".main_module." in key: 133 prefix = key[: key.index(".main_module.")] 134 if prefix not in pai_module_names: 135 key = key.replace(".main_module.", ".", 1) 136 if "module_id" in key.split("."): 137 continue 138 cleaned[key] = value 139 state_dict = cleaned 140 141 if not pai_module_names: 142 net.load_state_dict(state_dict) 143 return net 144 145 # Wrap each identified module as a PerforatedModule in-place 146 for module_name in pai_module_names: 147 parts = module_name.split(".") 148 if len(parts) == 1: 149 parent = net 150 attr = parts[0] 151 else: 152 parent = net.get_submodule(".".join(parts[:-1])) 153 attr = parts[-1] 154 original_module = getattr(parent, attr) 155 setattr(parent, attr, PerforatedModule(original_module, "." + module_name)) 156 157 pai_modules = get_pai_modules(net, 0) 158 159 for module in pai_modules: 160 # Set up name to be what will be saved in the state dict 161 module_name = UPA.get_module_base_name(module) 162 # Then instantiate as many Dendrites as were created during training 163 num_cycles = int(state_dict[module_name + ".num_cycles"].item()) 164 # extract node index from state_dict 165 nodeCount = 10 166 # also extract view tuple 167 if num_cycles > 0: 168 module.simulate_cycles(num_cycles, nodeCount) 169 if not module.processor is None: 170 processor = copy.deepcopy(module.processor) 171 processor.pre = module.processor.post_n1 172 processor.post = module.processor.post_n2 173 module.processor_array.append(processor) 174 else: 175 module.processor_array.append(None) 176 177 # Create ParameterList for skip_weights based on num_cycles 178 num_params = num_cycles // 2 179 skip_weights_list = nn.ParameterList() 180 for i in range(num_params): 181 param_key = module_name + f".skip_weights.{i}" 182 if param_key in state_dict: 183 param = nn.Parameter(torch.randn(state_dict[param_key].shape)) 184 skip_weights_list.append(param) 185 module.skip_weights = skip_weights_list 186 187 module.register_buffer("view_tuple", state_dict[module_name + ".view_tuple"]) 188 189 net.load_state_dict(state_dict) 190 191 for module in pai_modules: 192 temp = tuple(module.view_tuple.tolist()) 193 del module.view_tuple 194 module.view_tuple = temp 195 196 return net 197 198 199def load_pai_model(net, filename): 200 """Load a saved PAI model file into a network. 201 202 Parameters 203 ---------- 204 net : nn.Module 205 Base network architecture. 206 filename : str 207 Path to a safetensors state file. 208 209 Returns 210 ------- 211 nn.Module 212 Loaded network. 213 """ 214 state_dict = load_file(filename) 215 return load_pai_model_from_dict(net, state_dict) 216 217 218class PerforatedModule(nn.Module): 219 def __init__(self, original_module, name): 220 """Initialize a perforated wrapper around a single module. 221 222 Parameters 223 ---------- 224 original_module : nn.Module 225 Original module being wrapped. 226 name : str 227 Qualified module name used for save/load mapping. 228 """ 229 super(PerforatedModule, self).__init__() 230 self.name = name 231 self.register_buffer("node_index", torch.tensor(-1)) 232 self.register_buffer("num_cycles", torch.tensor(-1)) 233 self.register_buffer("view_tuple", torch.tensor(-1)) 234 self.processor_array = [] 235 self.processor = None 236 self.layer_array = nn.ModuleList([original_module]) 237 # If this original module has processing functions save the processor 238 if type(original_module) in GPA.pc.get_modules_with_processing(): 239 module_index = GPA.pc.get_modules_with_processing().index( 240 type(original_module) 241 ) 242 self.processor = GPA.pc.get_modules_processing_classes()[module_index]() 243 elif ( 244 type(original_module).__name__ in GPA.pc.get_module_names_with_processing() 245 ): 246 module_index = GPA.pc.get_module_names_with_processing().index( 247 type(original_module).__name__ 248 ) 249 self.processor = GPA.pc.get_module_by_name_processing_classes()[ 250 module_index 251 ]() 252 253 def simulate_cycles(self, num_cycles, nodeCount): 254 """Expand internal layer/processor lists for stored dendrite cycles. 255 256 Parameters 257 ---------- 258 num_cycles : int 259 Number of perforation cycles represented in saved state. 260 nodeCount : int 261 Node count hint kept for interface compatibility. 262 263 Returns 264 ------- 265 None 266 This function does not return a value. 267 """ 268 for i in range(0, num_cycles, 2): 269 self.layer_array.append(copy.deepcopy(self.layer_array[0])) 270 if not self.processor is None: 271 processor = copy.deepcopy(self.processor) 272 processor.pre = self.processor.pre_d 273 processor.post = self.processor.post_d 274 self.processor_array.append(processor) 275 else: 276 self.processor_array.append(None) 277 278 def process_and_forward(self, *args2, **kwargs2): 279 """Execute one dendrite layer and write output into shared storage. 280 281 Parameters 282 ---------- 283 *args2 : tuple 284 Positional values where first entries are layer index and output 285 buffer, followed by layer inputs. 286 **kwargs2 : dict 287 Keyword arguments forwarded to the wrapped layer. 288 289 Returns 290 ------- 291 None 292 This function does not return a value. 293 """ 294 c = args2[0] 295 dendrite_outs = args2[1] 296 args2 = args2[2:] 297 if self.processor_array[c] != None: 298 out_values = self.processor_array[c].pre(*args2, **kwargs2) 299 out_values = self.layer_array[c](*args2, **kwargs2) 300 if self.processor_array[c] != None: 301 out = self.processor_array[c].post(out_values) 302 else: 303 out = out_values 304 dendrite_outs[c] = out 305 306 def process_and_pre(self, *args, **kwargs): 307 """Run the final pre-dendrite layer pass and cache its output. 308 309 Parameters 310 ---------- 311 *args : tuple 312 Positional values with output buffer first, then model inputs. 313 **kwargs : dict 314 Keyword arguments forwarded to the wrapped layer. 315 316 Returns 317 ------- 318 None 319 This function does not return a value. 320 """ 321 dendrite_outs = args[0] 322 args = args[1:] 323 out = self.layer_array[-1].forward(*args, **kwargs) 324 if not self.processor_array[-1] is None: 325 out = self.processor_array[-1].pre(out) 326 dendrite_outs[len(self.layer_array) - 1] = out 327 328 def forward(self, *args, **kwargs): 329 """Run perforated forward pass with dendrite accumulation. 330 331 Parameters 332 ---------- 333 *args : tuple 334 Positional arguments forwarded through wrapped layers. 335 **kwargs : dict 336 Keyword arguments forwarded through wrapped layers. 337 338 Returns 339 ------- 340 Any 341 Final model output after optional processor post-processing. 342 """ 343 # this is currently false anyway, just remove the doing multi idea 344 doing_multi = doing_threading 345 dendrite_outs = [None] * len(self.layer_array) 346 threads = {} 347 for c in range(0, len(self.layer_array) - 1): 348 args2, kwargs2 = args, kwargs 349 if doing_multi: 350 threads[c] = Thread( 351 target=self.process_and_forward, 352 args=(c, dendrite_outs, *args), 353 kwargs=kwargs, 354 ) 355 else: 356 self.process_and_forward(c, dendrite_outs, *args2, **kwargs2) 357 if doing_multi: 358 threads[len(self.layer_array) - 1] = Thread( 359 target=self.process_and_pre, args=(dendrite_outs, *args), kwargs=kwargs 360 ) 361 else: 362 self.process_and_pre(dendrite_outs, *args, **kwargs) 363 if doing_multi: 364 for i in range(len(dendrite_outs)): 365 threads[i].start() 366 for i in range(len(dendrite_outs)): 367 threads[i].join() 368 for out_index in range(0, len(self.layer_array)): 369 current_out = dendrite_outs[out_index] 370 371 if len(self.layer_array) > 1 and hasattr(self, "skip_weights") and len(self.skip_weights) > 0: 372 for in_index in range(0, out_index): 373 # Use out_index - 1 because skip_weights[0] is never used 374 current_out = ( 375 current_out 376 + self.skip_weights[out_index - 1][in_index, :] 377 .reshape(self.view_tuple) 378 .to(current_out.device) 379 * dendrite_outs[in_index] 380 ) 381 if out_index < len(self.layer_array) - 1: 382 current_out = GPA.pc.get_pai_forward_function()(current_out) 383 dendrite_outs[out_index] = current_out 384 if not self.processor_array[-1] is None: 385 current_out = self.processor_array[-1].post(current_out) 386 return current_out 387 388 389class PAITrackedModule(nn.Module): 390 """Wrapper for modules you don't want to add dendrites to. Ensures all modules are accounted for.""" 391 392 def __init__(self, start_module, name): 393 """Initialize PAITrackedModule. 394 395 This function sets up the tracked neuron module to wrap the start_module 396 without adding dendrites. 397 398 Parameters 399 ---------- 400 start_module : nn.Module 401 The module to wrap. 402 name : str 403 The name of the neuron module. 404 """ 405 super(PAITrackedModule, self).__init__() 406 407 if isinstance(start_module, nn.Module): 408 self.main_module = start_module 409 else: 410 print("start_module must be nn.Module: %s" % name) 411 print(type(start_module)) 412 print(start_module) 413 sys.exit(-1) 414 self.name = name 415 416 self.type = "tracked_module" 417 418 def __getattr__(self, name): 419 """Get member variables from the main module. 420 421 Parameters 422 ---------- 423 name : str 424 The name of the variable to retrieve. 425 Returns 426 ------- 427 The requested variable. 428 429 Notes 430 ----- 431 This method first attempts to retrieve the attribute from the PAINeuronModule instance. 432 If it fails, it tries to get the attribute from the wrapped main_module. 433 This allows seamless access to the main module's attributes without modifying original code. 434 """ 435 try: 436 return super().__getattr__(name) 437 except AttributeError: 438 return getattr(self.main_module, name) 439 440 def forward(self, *args, **kwargs): 441 """Forward pass for tracked layer. 442 443 Parameters 444 ---------- 445 *args : tuple 446 Positional arguments for the forward pass. 447 **kwargs : dict 448 Keyword arguments for the forward pass. 449 450 Returns 451 ------- 452 Any 453 The output of the module 454 455 Notes 456 ----- 457 The output of this forward function will have the same format as the output 458 of the original module 459 """ 460 return self.main_module(*args, **kwargs) 461 462 def __str__(self): 463 """String representation of the layer. 464 465 Parameters 466 ---------- 467 None 468 469 Returns 470 ------- 471 str 472 String representation of the layer. 473 474 Notes 475 ----- 476 Setting for verbose changes level of details in the string output. 477 """ 478 479 if GPA.pc.get_verbose(): 480 total_string = self.main_module.__str__() 481 total_string = "PAITrackedLayer(" + total_string + ")" 482 return total_string 483 else: 484 total_string = self.main_module.__str__() 485 total_string = "PAITrackedLayer(" + total_string + ")" 486 return total_string 487 488 def __repr__(self): 489 """Representation of the layer.""" 490 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 a PAI or plain model from a state dictionary into an unconverted network. 108 109 Parameters 110 ---------- 111 net : nn.Module 112 Base network architecture (not yet converted to PerforatedModules). 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 # Find which module paths need PAI wrapping from the state dict 122 pai_module_names = set( 123 key[: -len(".num_cycles")] 124 for key in state_dict.keys() 125 if key.endswith(".num_cycles") 126 ) 127 128 # Clean up tracked-module scaffolding: for any key containing .main_module. 129 # where the prefix is not a PAI module, strip .main_module. out so the 130 # weights load directly into the plain module. Also drop any module_id keys. 131 cleaned = {} 132 for key, value in state_dict.items(): 133 if ".main_module." in key: 134 prefix = key[: key.index(".main_module.")] 135 if prefix not in pai_module_names: 136 key = key.replace(".main_module.", ".", 1) 137 if "module_id" in key.split("."): 138 continue 139 cleaned[key] = value 140 state_dict = cleaned 141 142 if not pai_module_names: 143 net.load_state_dict(state_dict) 144 return net 145 146 # Wrap each identified module as a PerforatedModule in-place 147 for module_name in pai_module_names: 148 parts = module_name.split(".") 149 if len(parts) == 1: 150 parent = net 151 attr = parts[0] 152 else: 153 parent = net.get_submodule(".".join(parts[:-1])) 154 attr = parts[-1] 155 original_module = getattr(parent, attr) 156 setattr(parent, attr, PerforatedModule(original_module, "." + module_name)) 157 158 pai_modules = get_pai_modules(net, 0) 159 160 for module in pai_modules: 161 # Set up name to be what will be saved in the state dict 162 module_name = UPA.get_module_base_name(module) 163 # Then instantiate as many Dendrites as were created during training 164 num_cycles = int(state_dict[module_name + ".num_cycles"].item()) 165 # extract node index from state_dict 166 nodeCount = 10 167 # also extract view tuple 168 if num_cycles > 0: 169 module.simulate_cycles(num_cycles, nodeCount) 170 if not module.processor is None: 171 processor = copy.deepcopy(module.processor) 172 processor.pre = module.processor.post_n1 173 processor.post = module.processor.post_n2 174 module.processor_array.append(processor) 175 else: 176 module.processor_array.append(None) 177 178 # Create ParameterList for skip_weights based on num_cycles 179 num_params = num_cycles // 2 180 skip_weights_list = nn.ParameterList() 181 for i in range(num_params): 182 param_key = module_name + f".skip_weights.{i}" 183 if param_key in state_dict: 184 param = nn.Parameter(torch.randn(state_dict[param_key].shape)) 185 skip_weights_list.append(param) 186 module.skip_weights = skip_weights_list 187 188 module.register_buffer("view_tuple", state_dict[module_name + ".view_tuple"]) 189 190 net.load_state_dict(state_dict) 191 192 for module in pai_modules: 193 temp = tuple(module.view_tuple.tolist()) 194 del module.view_tuple 195 module.view_tuple = temp 196 197 return net
Load a PAI or plain model from a state dictionary into an unconverted network.
Parameters
- net (nn.Module): Base network architecture (not yet converted to PerforatedModules).
- state_dict (dict): Serialized model state.
Returns
- nn.Module: Network with loaded state and reconstructed runtime buffers.
200def load_pai_model(net, filename): 201 """Load a saved PAI model file into a network. 202 203 Parameters 204 ---------- 205 net : nn.Module 206 Base network architecture. 207 filename : str 208 Path to a safetensors state file. 209 210 Returns 211 ------- 212 nn.Module 213 Loaded network. 214 """ 215 state_dict = load_file(filename) 216 return load_pai_model_from_dict(net, state_dict)
Load a saved PAI model file into a network.
Parameters
- net (nn.Module): Base network architecture.
- filename (str): Path to a safetensors state file.
Returns
- nn.Module: Loaded network.
219class PerforatedModule(nn.Module): 220 def __init__(self, original_module, name): 221 """Initialize a perforated wrapper around a single module. 222 223 Parameters 224 ---------- 225 original_module : nn.Module 226 Original module being wrapped. 227 name : str 228 Qualified module name used for save/load mapping. 229 """ 230 super(PerforatedModule, self).__init__() 231 self.name = name 232 self.register_buffer("node_index", torch.tensor(-1)) 233 self.register_buffer("num_cycles", torch.tensor(-1)) 234 self.register_buffer("view_tuple", torch.tensor(-1)) 235 self.processor_array = [] 236 self.processor = None 237 self.layer_array = nn.ModuleList([original_module]) 238 # If this original module has processing functions save the processor 239 if type(original_module) in GPA.pc.get_modules_with_processing(): 240 module_index = GPA.pc.get_modules_with_processing().index( 241 type(original_module) 242 ) 243 self.processor = GPA.pc.get_modules_processing_classes()[module_index]() 244 elif ( 245 type(original_module).__name__ in GPA.pc.get_module_names_with_processing() 246 ): 247 module_index = GPA.pc.get_module_names_with_processing().index( 248 type(original_module).__name__ 249 ) 250 self.processor = GPA.pc.get_module_by_name_processing_classes()[ 251 module_index 252 ]() 253 254 def simulate_cycles(self, num_cycles, nodeCount): 255 """Expand internal layer/processor lists for stored dendrite cycles. 256 257 Parameters 258 ---------- 259 num_cycles : int 260 Number of perforation cycles represented in saved state. 261 nodeCount : int 262 Node count hint kept for interface compatibility. 263 264 Returns 265 ------- 266 None 267 This function does not return a value. 268 """ 269 for i in range(0, num_cycles, 2): 270 self.layer_array.append(copy.deepcopy(self.layer_array[0])) 271 if not self.processor is None: 272 processor = copy.deepcopy(self.processor) 273 processor.pre = self.processor.pre_d 274 processor.post = self.processor.post_d 275 self.processor_array.append(processor) 276 else: 277 self.processor_array.append(None) 278 279 def process_and_forward(self, *args2, **kwargs2): 280 """Execute one dendrite layer and write output into shared storage. 281 282 Parameters 283 ---------- 284 *args2 : tuple 285 Positional values where first entries are layer index and output 286 buffer, followed by layer inputs. 287 **kwargs2 : dict 288 Keyword arguments forwarded to the wrapped layer. 289 290 Returns 291 ------- 292 None 293 This function does not return a value. 294 """ 295 c = args2[0] 296 dendrite_outs = args2[1] 297 args2 = args2[2:] 298 if self.processor_array[c] != None: 299 out_values = self.processor_array[c].pre(*args2, **kwargs2) 300 out_values = self.layer_array[c](*args2, **kwargs2) 301 if self.processor_array[c] != None: 302 out = self.processor_array[c].post(out_values) 303 else: 304 out = out_values 305 dendrite_outs[c] = out 306 307 def process_and_pre(self, *args, **kwargs): 308 """Run the final pre-dendrite layer pass and cache its output. 309 310 Parameters 311 ---------- 312 *args : tuple 313 Positional values with output buffer first, then model inputs. 314 **kwargs : dict 315 Keyword arguments forwarded to the wrapped layer. 316 317 Returns 318 ------- 319 None 320 This function does not return a value. 321 """ 322 dendrite_outs = args[0] 323 args = args[1:] 324 out = self.layer_array[-1].forward(*args, **kwargs) 325 if not self.processor_array[-1] is None: 326 out = self.processor_array[-1].pre(out) 327 dendrite_outs[len(self.layer_array) - 1] = out 328 329 def forward(self, *args, **kwargs): 330 """Run perforated forward pass with dendrite accumulation. 331 332 Parameters 333 ---------- 334 *args : tuple 335 Positional arguments forwarded through wrapped layers. 336 **kwargs : dict 337 Keyword arguments forwarded through wrapped layers. 338 339 Returns 340 ------- 341 Any 342 Final model output after optional processor post-processing. 343 """ 344 # this is currently false anyway, just remove the doing multi idea 345 doing_multi = doing_threading 346 dendrite_outs = [None] * len(self.layer_array) 347 threads = {} 348 for c in range(0, len(self.layer_array) - 1): 349 args2, kwargs2 = args, kwargs 350 if doing_multi: 351 threads[c] = Thread( 352 target=self.process_and_forward, 353 args=(c, dendrite_outs, *args), 354 kwargs=kwargs, 355 ) 356 else: 357 self.process_and_forward(c, dendrite_outs, *args2, **kwargs2) 358 if doing_multi: 359 threads[len(self.layer_array) - 1] = Thread( 360 target=self.process_and_pre, args=(dendrite_outs, *args), kwargs=kwargs 361 ) 362 else: 363 self.process_and_pre(dendrite_outs, *args, **kwargs) 364 if doing_multi: 365 for i in range(len(dendrite_outs)): 366 threads[i].start() 367 for i in range(len(dendrite_outs)): 368 threads[i].join() 369 for out_index in range(0, len(self.layer_array)): 370 current_out = dendrite_outs[out_index] 371 372 if len(self.layer_array) > 1 and hasattr(self, "skip_weights") and len(self.skip_weights) > 0: 373 for in_index in range(0, out_index): 374 # Use out_index - 1 because skip_weights[0] is never used 375 current_out = ( 376 current_out 377 + self.skip_weights[out_index - 1][in_index, :] 378 .reshape(self.view_tuple) 379 .to(current_out.device) 380 * dendrite_outs[in_index] 381 ) 382 if out_index < len(self.layer_array) - 1: 383 current_out = GPA.pc.get_pai_forward_function()(current_out) 384 dendrite_outs[out_index] = current_out 385 if not self.processor_array[-1] is None: 386 current_out = self.processor_array[-1].post(current_out) 387 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
220 def __init__(self, original_module, name): 221 """Initialize a perforated wrapper around a single module. 222 223 Parameters 224 ---------- 225 original_module : nn.Module 226 Original module being wrapped. 227 name : str 228 Qualified module name used for save/load mapping. 229 """ 230 super(PerforatedModule, self).__init__() 231 self.name = name 232 self.register_buffer("node_index", torch.tensor(-1)) 233 self.register_buffer("num_cycles", torch.tensor(-1)) 234 self.register_buffer("view_tuple", torch.tensor(-1)) 235 self.processor_array = [] 236 self.processor = None 237 self.layer_array = nn.ModuleList([original_module]) 238 # If this original module has processing functions save the processor 239 if type(original_module) in GPA.pc.get_modules_with_processing(): 240 module_index = GPA.pc.get_modules_with_processing().index( 241 type(original_module) 242 ) 243 self.processor = GPA.pc.get_modules_processing_classes()[module_index]() 244 elif ( 245 type(original_module).__name__ in GPA.pc.get_module_names_with_processing() 246 ): 247 module_index = GPA.pc.get_module_names_with_processing().index( 248 type(original_module).__name__ 249 ) 250 self.processor = GPA.pc.get_module_by_name_processing_classes()[ 251 module_index 252 ]()
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.
254 def simulate_cycles(self, num_cycles, nodeCount): 255 """Expand internal layer/processor lists for stored dendrite cycles. 256 257 Parameters 258 ---------- 259 num_cycles : int 260 Number of perforation cycles represented in saved state. 261 nodeCount : int 262 Node count hint kept for interface compatibility. 263 264 Returns 265 ------- 266 None 267 This function does not return a value. 268 """ 269 for i in range(0, num_cycles, 2): 270 self.layer_array.append(copy.deepcopy(self.layer_array[0])) 271 if not self.processor is None: 272 processor = copy.deepcopy(self.processor) 273 processor.pre = self.processor.pre_d 274 processor.post = self.processor.post_d 275 self.processor_array.append(processor) 276 else: 277 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.
279 def process_and_forward(self, *args2, **kwargs2): 280 """Execute one dendrite layer and write output into shared storage. 281 282 Parameters 283 ---------- 284 *args2 : tuple 285 Positional values where first entries are layer index and output 286 buffer, followed by layer inputs. 287 **kwargs2 : dict 288 Keyword arguments forwarded to the wrapped layer. 289 290 Returns 291 ------- 292 None 293 This function does not return a value. 294 """ 295 c = args2[0] 296 dendrite_outs = args2[1] 297 args2 = args2[2:] 298 if self.processor_array[c] != None: 299 out_values = self.processor_array[c].pre(*args2, **kwargs2) 300 out_values = self.layer_array[c](*args2, **kwargs2) 301 if self.processor_array[c] != None: 302 out = self.processor_array[c].post(out_values) 303 else: 304 out = out_values 305 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.
307 def process_and_pre(self, *args, **kwargs): 308 """Run the final pre-dendrite layer pass and cache its output. 309 310 Parameters 311 ---------- 312 *args : tuple 313 Positional values with output buffer first, then model inputs. 314 **kwargs : dict 315 Keyword arguments forwarded to the wrapped layer. 316 317 Returns 318 ------- 319 None 320 This function does not return a value. 321 """ 322 dendrite_outs = args[0] 323 args = args[1:] 324 out = self.layer_array[-1].forward(*args, **kwargs) 325 if not self.processor_array[-1] is None: 326 out = self.processor_array[-1].pre(out) 327 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.
329 def forward(self, *args, **kwargs): 330 """Run perforated forward pass with dendrite accumulation. 331 332 Parameters 333 ---------- 334 *args : tuple 335 Positional arguments forwarded through wrapped layers. 336 **kwargs : dict 337 Keyword arguments forwarded through wrapped layers. 338 339 Returns 340 ------- 341 Any 342 Final model output after optional processor post-processing. 343 """ 344 # this is currently false anyway, just remove the doing multi idea 345 doing_multi = doing_threading 346 dendrite_outs = [None] * len(self.layer_array) 347 threads = {} 348 for c in range(0, len(self.layer_array) - 1): 349 args2, kwargs2 = args, kwargs 350 if doing_multi: 351 threads[c] = Thread( 352 target=self.process_and_forward, 353 args=(c, dendrite_outs, *args), 354 kwargs=kwargs, 355 ) 356 else: 357 self.process_and_forward(c, dendrite_outs, *args2, **kwargs2) 358 if doing_multi: 359 threads[len(self.layer_array) - 1] = Thread( 360 target=self.process_and_pre, args=(dendrite_outs, *args), kwargs=kwargs 361 ) 362 else: 363 self.process_and_pre(dendrite_outs, *args, **kwargs) 364 if doing_multi: 365 for i in range(len(dendrite_outs)): 366 threads[i].start() 367 for i in range(len(dendrite_outs)): 368 threads[i].join() 369 for out_index in range(0, len(self.layer_array)): 370 current_out = dendrite_outs[out_index] 371 372 if len(self.layer_array) > 1 and hasattr(self, "skip_weights") and len(self.skip_weights) > 0: 373 for in_index in range(0, out_index): 374 # Use out_index - 1 because skip_weights[0] is never used 375 current_out = ( 376 current_out 377 + self.skip_weights[out_index - 1][in_index, :] 378 .reshape(self.view_tuple) 379 .to(current_out.device) 380 * dendrite_outs[in_index] 381 ) 382 if out_index < len(self.layer_array) - 1: 383 current_out = GPA.pc.get_pai_forward_function()(current_out) 384 dendrite_outs[out_index] = current_out 385 if not self.processor_array[-1] is None: 386 current_out = self.processor_array[-1].post(current_out) 387 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.
390class PAITrackedModule(nn.Module): 391 """Wrapper for modules you don't want to add dendrites to. Ensures all modules are accounted for.""" 392 393 def __init__(self, start_module, name): 394 """Initialize PAITrackedModule. 395 396 This function sets up the tracked neuron module to wrap the start_module 397 without adding dendrites. 398 399 Parameters 400 ---------- 401 start_module : nn.Module 402 The module to wrap. 403 name : str 404 The name of the neuron module. 405 """ 406 super(PAITrackedModule, self).__init__() 407 408 if isinstance(start_module, nn.Module): 409 self.main_module = start_module 410 else: 411 print("start_module must be nn.Module: %s" % name) 412 print(type(start_module)) 413 print(start_module) 414 sys.exit(-1) 415 self.name = name 416 417 self.type = "tracked_module" 418 419 def __getattr__(self, name): 420 """Get member variables from the main module. 421 422 Parameters 423 ---------- 424 name : str 425 The name of the variable to retrieve. 426 Returns 427 ------- 428 The requested variable. 429 430 Notes 431 ----- 432 This method first attempts to retrieve the attribute from the PAINeuronModule instance. 433 If it fails, it tries to get the attribute from the wrapped main_module. 434 This allows seamless access to the main module's attributes without modifying original code. 435 """ 436 try: 437 return super().__getattr__(name) 438 except AttributeError: 439 return getattr(self.main_module, name) 440 441 def forward(self, *args, **kwargs): 442 """Forward pass for tracked layer. 443 444 Parameters 445 ---------- 446 *args : tuple 447 Positional arguments for the forward pass. 448 **kwargs : dict 449 Keyword arguments for the forward pass. 450 451 Returns 452 ------- 453 Any 454 The output of the module 455 456 Notes 457 ----- 458 The output of this forward function will have the same format as the output 459 of the original module 460 """ 461 return self.main_module(*args, **kwargs) 462 463 def __str__(self): 464 """String representation of the layer. 465 466 Parameters 467 ---------- 468 None 469 470 Returns 471 ------- 472 str 473 String representation of the layer. 474 475 Notes 476 ----- 477 Setting for verbose changes level of details in the string output. 478 """ 479 480 if GPA.pc.get_verbose(): 481 total_string = self.main_module.__str__() 482 total_string = "PAITrackedLayer(" + total_string + ")" 483 return total_string 484 else: 485 total_string = self.main_module.__str__() 486 total_string = "PAITrackedLayer(" + total_string + ")" 487 return total_string 488 489 def __repr__(self): 490 """Representation of the layer.""" 491 return self.__str__()
Wrapper for modules you don't want to add dendrites to. Ensures all modules are accounted for.
393 def __init__(self, start_module, name): 394 """Initialize PAITrackedModule. 395 396 This function sets up the tracked neuron module to wrap the start_module 397 without adding dendrites. 398 399 Parameters 400 ---------- 401 start_module : nn.Module 402 The module to wrap. 403 name : str 404 The name of the neuron module. 405 """ 406 super(PAITrackedModule, self).__init__() 407 408 if isinstance(start_module, nn.Module): 409 self.main_module = start_module 410 else: 411 print("start_module must be nn.Module: %s" % name) 412 print(type(start_module)) 413 print(start_module) 414 sys.exit(-1) 415 self.name = name 416 417 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
441 def forward(self, *args, **kwargs): 442 """Forward pass for tracked layer. 443 444 Parameters 445 ---------- 446 *args : tuple 447 Positional arguments for the forward pass. 448 **kwargs : dict 449 Keyword arguments for the forward pass. 450 451 Returns 452 ------- 453 Any 454 The output of the module 455 456 Notes 457 ----- 458 The output of this forward function will have the same format as the output 459 of the original module 460 """ 461 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