perforatedai.tracker_perforatedai
1# Copyright (c) 2025 Perforated AI 2 3import io 4import math 5import os 6import shutil 7import sys 8import time 9from datetime import datetime 10from pydoc import locate 11 12import matplotlib as mpl 13import matplotlib.pyplot as plt 14import numpy as np 15import pandas as pd 16import torch 17import torch.nn as nn 18import torch.nn.functional as F 19import torch.nn.init as init 20import pdb 21 22from perforatedai import globals_perforatedai as GPA 23from perforatedai import modules_perforatedai as PA 24from perforatedai import utils_perforatedai as UPA 25 26try: 27 from dashboard_utils.event_emitter import emitter as _dashboard_emitter 28except ImportError as e: 29 print(f"[ERROR] Failed to import dashboard_utils.event_emitter: {e}") 30 _dashboard_emitter = None 31 32 33def _pai_log(level, message): 34 if _dashboard_emitter is not None: 35 _dashboard_emitter.log(GPA.pc, level, message) 36 else: 37 if level in ("warning", "error") or not GPA.pc.get_silent(): 38 print(message) 39 40 41try: 42 from perforatedbp import tracker_pbp as TPB 43except ModuleNotFoundError: 44 pass # Module not found, pass silently 45except ImportError as e: 46 print(f"Import error occurred: {e}") 47 48mpl.use("Agg") 49 50# Status constants for restructuring during add_validation_score 51NO_MODEL_UPDATE = 0 52NETWORK_RESTRUCTURED = 1 53TRAINING_COMPLETE = 2 54 55# Status constant for each batch 56STEP_CLEARED = 0 57STEP_CALLED = 1 58 59# Shared palette for save_graphs PNGs so the local graphs read as the same 60# product as the dashboard charts. White background is kept intentionally - 61# these PNGs get embedded in docs and papers. 62_PAI_PALETTE = { 63 "dendrite": "#00E0B5", # Dendrite / PAI 64 "normal": "#FD4D00", # Normal 65 "validation": "#59CD6C", # Validation score 66 "train": "#E072A4", # Train score 67 "test": "#9781E6", # Test / next extra score 68 "learning_rate": "#1A659E", # Learning rate 69 "grid": "#004145", # Grid lines (use at low alpha) 70 "marker_best": "#1A1A1A", # Global-best marker (not a data colour) 71 "marker_improved": "#7A7A7A", # Epoch-last-improved marker 72} 73 74# Cycle for extra_scores beyond train/test, and for dendrite layer curves. 75_PAI_EXTRA_CYCLE = ["#9781E6", "#1A659E", "#00A5A5", "#F2B807"] 76_PAI_DENDRITE_CYCLE = [ 77 "#00E0B5", 78 "#00A5A5", 79 "#1A659E", 80 "#9781E6", 81 "#E072A4", 82 "#F2B807", 83] 84 85 86def _pai_grid(ax): 87 """Apply the shared PAI grid styling to an axis.""" 88 ax.grid(True, color=_PAI_PALETTE["grid"], alpha=0.25) 89 90 91def _pai_extra_score_color(extra_name, extra_index): 92 """Colour for an ``extra_scores`` series by name, then by cycle position.""" 93 lowered = str(extra_name).lower() 94 if lowered == "train": 95 return _PAI_PALETTE["train"] 96 if lowered == "test": 97 return _PAI_PALETTE["test"] 98 return _PAI_EXTRA_CYCLE[extra_index % len(_PAI_EXTRA_CYCLE)] 99 100 101def update_restructuring_status(old_status, new_status): 102 """Update restructured variable during add_validation_score 103 104 Update the restructuring status based on the new status. 105 If the new status is that there was not an update, 106 dont overwrite the old status which may show there was an update. 107 108 Parameters 109 ---------- 110 old_status : int 111 The old restructuring status. 112 new_status : int 113 The new restructuring status. 114 115 Returns 116 ------- 117 int 118 The updated restructuring status. 119 120 """ 121 if new_status == NETWORK_RESTRUCTURED or new_status == TRAINING_COMPLETE: 122 return NETWORK_RESTRUCTURED 123 else: 124 return old_status 125 126 127def update_learning_rate(): 128 """Update the learning rate in the tracker. 129 130 Parameters 131 ---------- 132 None 133 134 Returns 135 ------- 136 None 137 This function does not return a value. 138 """ 139 for param_group in GPA.pai_tracker.member_vars["optimizer_instance"].param_groups: 140 learning_rate = param_group["lr"] 141 GPA.pai_tracker.add_learning_rate(learning_rate) 142 143 144def update_param_count(net): 145 """Update the parameter count in the tracker if not already set. 146 147 Parameters 148 ---------- 149 net : torch.nn.Module 150 The neural network model to count parameters for. 151 Returns 152 ------- 153 None 154 """ 155 if len(GPA.pai_tracker.member_vars["param_counts"]) == 0: 156 GPA.pai_tracker.member_vars["param_counts"].append(UPA.count_params(net)) 157 158 159def check_input_problems(net, accuracy): 160 """Check for potential input problems in add_validation_score. 161 162 Parameters 163 ---------- 164 net : torch.nn.Module 165 The neural network model to check. 166 accuracy : float, int, or torch.Tensor 167 The accuracy score to validate. 168 169 Returns 170 ------- 171 float 172 The validated accuracy score. 173 174 """ 175 176 # Make sure you are passing in the model and not the dataparallel wrapper 177 if issubclass(type(net), nn.DataParallel): 178 _pai_log("error", "Need to call .module when using add validation score") 179 pdb.set_trace() 180 sys.exit(-1) 181 182 if "module" in net.__dir__(): 183 _pai_log("error", "Need to call .module when using add validation score") 184 pdb.set_trace() 185 sys.exit(-1) 186 187 if not isinstance(accuracy, (float, int)): 188 try: 189 accuracy = accuracy.item() 190 except: 191 _pai_log( 192 "error", 193 f"Scores added for add_validation_score should be float, int, or tensor, yours is a: {type(accuracy)}", 194 ) 195 pdb.set_trace() 196 sys.exit(-1) 197 return accuracy 198 199 200def update_running_accuracy(accuracy, epochs_since_cycle_switch): 201 """Add the new accuracy to the tracker. 202 203 Parameters 204 ---------- 205 accuracy : float, int, or torch.Tensor 206 The accuracy score to add. 207 epochs_since_cycle_switch : int 208 The number of epochs since the last cycle switch. 209 210 Returns 211 ------- 212 None 213 214 """ 215 # Only update running_accuracy when neurons are being updated 216 if GPA.pai_tracker.member_vars["mode"] == "n" or GPA.pc.get_learn_dendrites_live(): 217 if epochs_since_cycle_switch < GPA.pc.get_initial_history_after_switches(): 218 if epochs_since_cycle_switch <= 0: 219 GPA.pai_tracker.member_vars["running_accuracy"] = accuracy 220 else: 221 GPA.pai_tracker.member_vars[ 222 "running_accuracy" 223 ] = GPA.pai_tracker.member_vars["running_accuracy"] * ( 224 1 - (1.0 / (epochs_since_cycle_switch + 1)) 225 ) + accuracy * ( 226 1.0 / (epochs_since_cycle_switch + 1) 227 ) 228 else: 229 GPA.pai_tracker.member_vars[ 230 "running_accuracy" 231 ] = GPA.pai_tracker.member_vars["running_accuracy"] * ( 232 1.0 - 1.0 / GPA.pc.get_history_lookback() 233 ) + accuracy * ( 234 1.0 / GPA.pc.get_history_lookback() 235 ) 236 237 GPA.pai_tracker.member_vars["accuracies"].append(accuracy) 238 if GPA.pai_tracker.member_vars["mode"] == "n": 239 GPA.pai_tracker.member_vars["n_accuracies"].append(accuracy) 240 241 if ( 242 GPA.pc.get_drawing_pai() 243 or GPA.pai_tracker.member_vars["mode"] == "n" 244 or GPA.pc.get_learn_dendrites_live() 245 ): 246 GPA.pai_tracker.member_vars["running_accuracies"].append( 247 GPA.pai_tracker.member_vars["running_accuracy"] 248 ) 249 250 251def score_beats_current_best(new_score, old_score): 252 """Check if the new score beats the current best score. 253 254 Parameters 255 ---------- 256 new_score : float 257 The new score to compare. 258 old_score : float 259 The old score to compare against. 260 261 Returns 262 ------- 263 bool 264 True if the new score beats the old score, False otherwise. 265 266 Notes 267 ----- 268 Must beat the old score by the margins set in globals for improvement thresholds. 269 270 """ 271 return ( 272 GPA.pai_tracker.member_vars["maximizing_score"] 273 and (new_score * (1.0 - GPA.pc.get_improvement_threshold()) > old_score) 274 and new_score - GPA.pc.get_improvement_threshold_raw() > old_score 275 ) or ( 276 (not GPA.pai_tracker.member_vars["maximizing_score"]) 277 and (new_score * (1.0 + GPA.pc.get_improvement_threshold()) < old_score) 278 and (new_score + GPA.pc.get_improvement_threshold_raw()) < old_score 279 ) 280 281 282def check_new_best(net, accuracy, epochs_since_cycle_switch): 283 """Check if the new accuracy is a new best. 284 285 Performs saves if new best score is found. 286 287 Parameters 288 ---------- 289 net : torch.nn.Module 290 The neural network model being trained. 291 accuracy : float 292 The accuracy score to check. 293 epochs_since_cycle_switch : int 294 The number of epochs since the last cycle switch. 295 296 Returns 297 ------- 298 None 299 300 """ 301 score_improved = score_beats_current_best( 302 GPA.pai_tracker.member_vars["running_accuracy"], 303 GPA.pai_tracker.member_vars["current_best_validation_score"], 304 ) 305 306 enough_time = ( 307 epochs_since_cycle_switch > GPA.pc.get_initial_history_after_switches() 308 ) or (GPA.pai_tracker.member_vars["switch_mode"] == GPA.pc.DOING_SWITCH_EVERY_TIME) 309 310 if ( 311 score_improved 312 or GPA.pai_tracker.member_vars["current_best_validation_score"] == 0 313 ) and enough_time: 314 315 if GPA.pai_tracker.member_vars["maximizing_score"]: 316 if GPA.pc.get_verbose(): 317 print( 318 f"\n\nGot score of {accuracy:.10f} " 319 f'(average {GPA.pai_tracker.member_vars["running_accuracy"]}, ' 320 f"*{1-GPA.pc.get_improvement_threshold()}=" 321 f'{GPA.pai_tracker.member_vars["running_accuracy"]*(1.0 - GPA.pc.get_improvement_threshold())}) ' 322 f'which is higher than {GPA.pai_tracker.member_vars["current_best_validation_score"]:.10f} ' 323 f"by {GPA.pc.get_improvement_threshold_raw()} so setting epoch to " 324 f'{GPA.pai_tracker.member_vars["num_epochs_run"]}\n\n' 325 ) 326 else: 327 if GPA.pc.get_verbose(): 328 print( 329 f"\n\nGot score of {accuracy:.10f} " 330 f'(average {GPA.pai_tracker.member_vars["running_accuracy"]}, ' 331 f"*{1+GPA.pc.get_improvement_threshold()}=" 332 f'{GPA.pai_tracker.member_vars["running_accuracy"]*(1.0 + GPA.pc.get_improvement_threshold())}) ' 333 f'which is lower than {GPA.pai_tracker.member_vars["current_best_validation_score"]:.10f} ' 334 f'so setting epoch to {GPA.pai_tracker.member_vars["num_epochs_run"]}\n\n' 335 ) 336 337 # Set the new best score 338 GPA.pai_tracker.member_vars["current_best_validation_score"] = ( 339 GPA.pai_tracker.member_vars["running_accuracy"] 340 ) 341 GPA.pai_tracker.member_vars["epoch_last_improved"] = ( 342 GPA.pai_tracker.member_vars["num_epochs_run"] 343 ) 344 if GPA.pc.get_verbose(): 345 print( 346 f'2 epoch improved is {GPA.pai_tracker.member_vars["epoch_last_improved"]}' 347 ) 348 # Immediately update this list before saving so loading will have it correctly 349 GPA.pai_tracker.member_vars["last_improved_accuracies"].append( 350 GPA.pai_tracker.member_vars["epoch_last_improved"] 351 ) 352 # Check if global best 353 is_global_best = score_beats_current_best( 354 GPA.pai_tracker.member_vars["current_best_validation_score"], 355 GPA.pai_tracker.member_vars["global_best_validation_score"], 356 ) 357 358 if ( 359 is_global_best 360 or GPA.pai_tracker.member_vars["global_best_validation_score"] == 0 361 ): 362 if GPA.pc.get_verbose(): 363 print( 364 f"This also beats global best of " 365 f'{GPA.pai_tracker.member_vars["global_best_validation_score"]} so saving' 366 ) 367 GPA.pai_tracker.member_vars["global_best_validation_score"] = ( 368 GPA.pai_tracker.member_vars["current_best_validation_score"] 369 ) 370 GPA.pai_tracker.member_vars["current_n_set_global_best"] = True 371 UPA.save_system(net, GPA.pc.get_save_name(), "best_model") 372 if GPA.pc.get_pai_saves(): 373 UPA.pai_save_system(net, GPA.pc.get_save_name(), "best_model") 374 else: 375 if GPA.pc.get_verbose(): 376 print("Not saving new best because:") 377 if epochs_since_cycle_switch <= GPA.pc.get_initial_history_after_switches(): 378 print( 379 f"Not enough history since switch {epochs_since_cycle_switch} <= " 380 f"{GPA.pc.get_initial_history_after_switches()}" 381 ) 382 elif GPA.pai_tracker.member_vars["maximizing_score"]: 383 print( 384 f"Got score of {accuracy} " 385 f'(average {GPA.pai_tracker.member_vars["running_accuracy"]}, ' 386 f"*{1-GPA.pc.get_improvement_threshold()}=" 387 f'{GPA.pai_tracker.member_vars["running_accuracy"]*(1.0 - GPA.pc.get_improvement_threshold())}) ' 388 f"which is not higher than " 389 f'{GPA.pai_tracker.member_vars["current_best_validation_score"]}' 390 ) 391 else: 392 print( 393 f"Got score of {accuracy} " 394 f'(average {GPA.pai_tracker.member_vars["running_accuracy"]}, ' 395 f"*{1+GPA.pc.get_improvement_threshold()}=" 396 f'{GPA.pai_tracker.member_vars["running_accuracy"]*(1.0 + GPA.pc.get_improvement_threshold())}) ' 397 f"which is not lower than " 398 f'{GPA.pai_tracker.member_vars["current_best_validation_score"]}' 399 ) 400 GPA.pai_tracker.member_vars["last_improved_accuracies"].append( 401 GPA.pai_tracker.member_vars["epoch_last_improved"] 402 ) 403 # If it's the first epoch, save as best anyway 404 if len(GPA.pai_tracker.member_vars["accuracies"]) == 1: 405 if GPA.pc.get_verbose(): 406 print("Saving first model or all models") 407 UPA.save_system(net, GPA.pc.get_save_name(), "best_model") 408 if GPA.pc.get_pai_saves(): 409 UPA.pai_save_system(net, GPA.pc.get_save_name(), "best_model") 410 411 412def process_no_improvement(net): 413 """Handle the case where no improvement is observed. 414 415 If the new dendrite did not improve scores, but its time to switch modes 416 either trigger the end of learning or reset to the previous dendrite 417 to try again. 418 419 Parameters 420 ---------- 421 net : torch.nn.Module 422 The neural network model being trained. 423 424 Returns 425 ------- 426 int 427 The status of restructuring or training completion. 428 torch.nn.Module 429 The potentially modified neural network model. 430 431 """ 432 if GPA.pc.get_verbose(): 433 print( 434 f"Planning to switch to p mode but best beat last: " 435 f'{GPA.pai_tracker.member_vars["current_n_set_global_best"]} ' 436 f"current start lr steps: " 437 f'{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]} ' 438 f"and last maximum lr steps: " 439 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_steps"]} ' 440 f'for rate: {GPA.pai_tracker.member_vars["last_max_learning_rate_value"]:.8f}' 441 ) 442 443 now = datetime.now() 444 dt_string = now.strftime("_%d.%m.%Y.%H.%M.%S") 445 446 if GPA.pc.get_verbose(): 447 print( 448 f'1 saving break {dt_string}_noImprove_lr_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}' 449 ) 450 451 GPA.pai_tracker.save_graphs( 452 f'{dt_string}_noImprove_lr_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}' 453 ) 454 455 if ( 456 GPA.pai_tracker.member_vars["num_dendrite_tries"] 457 < GPA.pc.get_max_dendrite_tries() -1 458 ): 459 _pai_log( 460 "info", 461 f"The newest added dendrites did not improve but current tries " 462 f'{GPA.pai_tracker.member_vars["num_dendrite_tries"] + 1} ' 463 f"is less than max tries {GPA.pc.get_max_dendrite_tries()} " 464 f"so loading last switch and trying new Dendrites.", 465 ) 466 old_tries = GPA.pai_tracker.member_vars["num_dendrite_tries"] 467 # Load best model from previous n mode 468 net = UPA.change_learning_modes( 469 net, 470 GPA.pc.get_save_name(), 471 "best_model", 472 GPA.pai_tracker.member_vars["doing_pai"], 473 ) 474 GPA.pai_tracker.member_vars["num_dendrite_tries"] = old_tries + 1 475 return NETWORK_RESTRUCTURED, net 476 else: 477 _pai_log( 478 "info", 479 f"The newest added dendrites did not improve system and " 480 f'{GPA.pai_tracker.member_vars["num_dendrite_tries"] + 1} > ' 481 f"{GPA.pc.get_max_dendrite_tries()} so returning training_complete.", 482 ) 483 _pai_log("info", "You should now exit your training loop and best_model will be your final model for inference") 484 if not GPA.pc.get_perforated_backpropagation() and GPA.pai_tracker.member_vars["num_dendrites_added"] > 0: 485 _pai_log("info", "For improved results, try perforated backpropagation next time!") 486 487 old_silent = GPA.pc.get_silent() 488 GPA.pc.set_silent(True) 489 UPA.load_system(net, GPA.pc.get_save_name(), "best_model", switch_call=True) 490 GPA.pc.set_silent(old_silent) 491 GPA.pai_tracker.save_graphs() 492 UPA.pai_save_system(net, GPA.pc.get_save_name(), "final_clean") 493 return TRAINING_COMPLETE, net 494 495 496def process_final_network(net): 497 """When the max number of dendrites has been hit load the best_model and return 498 499 Parameters 500 ---------- 501 net : torch.nn.Module 502 The neural network model being trained. 503 504 Returns 505 ------- 506 torch.nn.Module 507 The final neural network model. 508 """ 509 510 _pai_log("info", f"Last Dendrites were good and this hit the max of {GPA.pc.get_max_dendrites()}") 511 if not GPA.pc.get_perforated_backpropagation() and GPA.pai_tracker.member_vars["num_dendrites_added"] > 0: 512 _pai_log("info", "For improved results, try perforated backpropagation next time!") 513 GPA.pai_tracker.save_graphs("before_final") 514 UPA.load_system(net, GPA.pc.get_save_name(), "best_model", switch_call=True) 515 GPA.pai_tracker.save_graphs() 516 UPA.pai_save_system(net, GPA.pc.get_save_name(), "final_clean") 517 return net 518 519 520def process_scheduler_update(net, accuracy, epochs_since_cycle_switch): 521 """Updates the scheduler 522 523 This increments the scheduler, but if we are automatically sweeping 524 to find the best initial learning rate for a new set of dendrites 525 this function also triggers the network at addition time to 526 try the next value. 527 528 Process for finding best initial learning rate for dendrites: 529 1. Start at default rate 530 2. Learn at that rate until scheduler increments twice 531 3. Save that version, start dendrites at LR current increment - 1 532 4. Repeat 2 and 3 until version has worse final score at set LR 533 5. Load previous model with best accuracy at that LR as initial rate 534 535 Parameters 536 ---------- 537 net : torch.nn.Module 538 The neural network model being trained. 539 accuracy : float 540 The accuracy of the model at the current learning rate. 541 epochs_since_cycle_switch : int 542 The number of epochs since the last cycle switch. 543 544 Returns 545 ------- 546 int 547 The status of restructuring or training completion. 548 torch.nn.Module 549 The potentially modified neural network model. 550 """ 551 552 restructured = False 553 for param_group in GPA.pai_tracker.member_vars["optimizer_instance"].param_groups: 554 learning_rate1 = param_group["lr"] 555 556 if ( 557 type(GPA.pai_tracker.member_vars["scheduler_instance"]) 558 is torch.optim.lr_scheduler.ReduceLROnPlateau 559 ): 560 if ( 561 epochs_since_cycle_switch > GPA.pc.get_initial_history_after_switches() 562 or GPA.pai_tracker.member_vars["mode"] == "p" 563 ): 564 if GPA.pc.get_verbose(): 565 print( 566 f"Updating scheduler with last improved " 567 f'{GPA.pai_tracker.member_vars["epoch_last_improved"]} ' 568 f'from current {GPA.pai_tracker.member_vars["num_epochs_run"]}' 569 ) 570 if GPA.pai_tracker.member_vars["scheduler"] is not None: 571 GPA.pai_tracker.member_vars["scheduler_instance"].step(metrics=accuracy) 572 if ( 573 GPA.pai_tracker.member_vars["scheduler"] 574 is torch.optim.lr_scheduler.ReduceLROnPlateau 575 ): 576 if GPA.pc.get_verbose(): 577 print( 578 f"Scheduler is now at " 579 f'{GPA.pai_tracker.member_vars["scheduler_instance"].num_bad_epochs} bad epochs' 580 ) 581 else: 582 if GPA.pc.get_verbose(): 583 print("Not stepping optimizer since hasnt initialized") 584 585 elif GPA.pai_tracker.member_vars["scheduler"] is not None: 586 if ( 587 epochs_since_cycle_switch > GPA.pc.get_initial_history_after_switches() 588 or GPA.pai_tracker.member_vars["mode"] == "p" 589 ): 590 if GPA.pc.get_verbose(): 591 if hasattr(GPA.pai_tracker.member_vars["scheduler_instance"], '_step_count'): 592 count = GPA.pai_tracker.member_vars["scheduler_instance"]._step_count 593 else: 594 count = GPA.pai_tracker.member_vars["scheduler_instance"].last_epoch 595 596 print( 597 f"Incrementing scheduler to count " 598 f'{count}' 599 ) 600 GPA.pai_tracker.member_vars["scheduler_instance"].step() 601 if ( 602 GPA.pai_tracker.member_vars["scheduler"] 603 is torch.optim.lr_scheduler.ReduceLROnPlateau 604 ): 605 if GPA.pc.get_verbose(): 606 print( 607 f"Scheduler is now at " 608 f'{GPA.pai_tracker.member_vars["scheduler_instance"].num_bad_epochs} bad epochs' 609 ) 610 611 if ( 612 epochs_since_cycle_switch <= GPA.pc.get_initial_history_after_switches() 613 and GPA.pai_tracker.member_vars["mode"] == "n" 614 ): 615 if GPA.pc.get_verbose(): 616 print( 617 f"Not stepping with history {GPA.pc.get_initial_history_after_switches()} " 618 f"and current {epochs_since_cycle_switch}" 619 ) 620 621 for param_group in GPA.pai_tracker.member_vars["optimizer_instance"].param_groups: 622 learning_rate2 = param_group["lr"] 623 624 stepped = False 625 at_last_count = False 626 627 if GPA.pc.get_verbose(): 628 print( 629 f"Checking if at last with scores " 630 f'{len(GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"])}, ' 631 f"count since switch {epochs_since_cycle_switch} " 632 f"and last total lr step count " 633 f'{GPA.pai_tracker.member_vars["initial_lr_test_epoch_count"]}' 634 ) 635 636 # Check if at double or exactly the test count 637 if ( 638 len(GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"]) == 0 639 and epochs_since_cycle_switch 640 == GPA.pai_tracker.member_vars["initial_lr_test_epoch_count"] * 2 641 ) or ( 642 len(GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"]) == 1 643 and epochs_since_cycle_switch 644 == GPA.pai_tracker.member_vars["initial_lr_test_epoch_count"] 645 ): 646 at_last_count = True 647 648 if GPA.pc.get_verbose(): 649 print( 650 f"At last count {at_last_count} with count {epochs_since_cycle_switch} " 651 f'and last LR count {GPA.pai_tracker.member_vars["initial_lr_test_epoch_count"]}' 652 ) 653 654 if learning_rate1 != learning_rate2: 655 stepped = True 656 GPA.pai_tracker.member_vars["current_step_count"] += 1 657 658 if GPA.pc.get_verbose(): 659 print( 660 f"Learning rate just stepped to {learning_rate2:.10e} " 661 f'with {GPA.pai_tracker.member_vars["current_step_count"]} total steps' 662 ) 663 664 if ( 665 GPA.pai_tracker.member_vars["current_step_count"] 666 == GPA.pai_tracker.member_vars["last_max_learning_rate_steps"] 667 ): 668 if GPA.pc.get_verbose(): 669 print( 670 f'{GPA.pai_tracker.member_vars["current_step_count"]} ' 671 f"steps is the max of the last switch mode" 672 ) 673 # Set it when 1->2 gets to 2, not when 0->1 hits 2 as stopping point 674 if ( 675 GPA.pai_tracker.member_vars["current_step_count"] 676 - GPA.pai_tracker.member_vars[ 677 "current_n_learning_rate_initial_skip_steps" 678 ] 679 == 1 680 ): 681 GPA.pai_tracker.member_vars["initial_lr_test_epoch_count"] = ( 682 epochs_since_cycle_switch 683 ) 684 685 if GPA.pc.get_verbose(): 686 print( 687 f"Learning rates were {learning_rate1:.8e} and {learning_rate2:.8e} " 688 f'started with {GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}, ' 689 f'and is now at {GPA.pai_tracker.member_vars["current_step_count"]} ' 690 f'committed {GPA.pai_tracker.member_vars["committed_to_initial_rate"]} ' 691 f"then either this (non zero) or eventually comparing to " 692 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_steps"]} ' 693 f'steps or rate {GPA.pai_tracker.member_vars["last_max_learning_rate_value"]:.8f}' 694 ) 695 696 # If learning rate just stepped, check restart at lower rate 697 if ( 698 (GPA.pai_tracker.member_vars["scheduler"] is not None) 699 and 700 # If potentially might have higher accuracy 701 ( 702 (GPA.pai_tracker.member_vars["mode"] == "n") 703 or GPA.pc.get_learn_dendrites_live() 704 ) 705 and 706 # And learning rate just stepped 707 (stepped or at_last_count) 708 ): 709 710 # If this is the first dendrite addition (last_max_learning_rate_steps == 0), 711 # immediately commit to the initial rate without searching 712 if GPA.pai_tracker.member_vars["last_max_learning_rate_steps"] == 0: 713 if GPA.pc.get_verbose(): 714 print( 715 f"First dendrite addition detected (last_max_learning_rate_steps == 0), " 716 f"immediately committing to initial rate without search" 717 ) 718 GPA.pai_tracker.member_vars["committed_to_initial_rate"] = True 719 GPA.pai_tracker.member_vars["last_max_learning_rate_steps"] = ( 720 GPA.pai_tracker.member_vars["current_step_count"] 721 ) 722 GPA.pai_tracker.member_vars["last_max_learning_rate_value"] = ( 723 learning_rate2 724 ) 725 726 # If hasn't committed to a learning rate for this cycle yet 727 if not GPA.pai_tracker.member_vars["committed_to_initial_rate"]: 728 best_score_so_far = GPA.pai_tracker.member_vars[ 729 "global_best_validation_score" 730 ] 731 732 if GPA.pc.get_verbose(): 733 print( 734 f"In statements to check next learning rate with " 735 f"stepped {stepped} and max count {at_last_count}" 736 ) 737 738 # If no scores saved for this dendrite and initial LR test did second step 739 if len( 740 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"] 741 ) == 0 and ( 742 GPA.pai_tracker.member_vars["current_step_count"] 743 - GPA.pai_tracker.member_vars[ 744 "current_n_learning_rate_initial_skip_steps" 745 ] 746 == 2 747 or at_last_count 748 ): 749 750 restructured = True 751 GPA.pai_tracker.clear_optimizer_and_scheduler() 752 753 # Save system for this initial condition 754 old_global = GPA.pai_tracker.member_vars["global_best_validation_score"] 755 old_accuracy = GPA.pai_tracker.member_vars[ 756 "current_best_validation_score" 757 ] 758 old_counts = GPA.pai_tracker.member_vars["initial_lr_test_epoch_count"] 759 skip1 = GPA.pai_tracker.member_vars[ 760 "current_n_learning_rate_initial_skip_steps" 761 ] 762 763 now = datetime.now() 764 dt_string = now.strftime("_%d.%m.%Y.%H.%M.%S") 765 766 GPA.pai_tracker.save_graphs( 767 f'{dt_string}_PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}' 768 ) 769 770 if GPA.pc.get_test_saves(): 771 UPA.save_system( 772 net, 773 GPA.pc.get_save_name(), 774 f'PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}', 775 ) 776 777 if GPA.pc.get_verbose(): 778 print( 779 f"Saving with initial steps: {dt_string}_PBCount_" 780 f'{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_' 781 f'{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]} ' 782 f"with current best {old_accuracy}" 783 ) 784 785 # Load back at start and try with lower initial learning rate 786 net = UPA.load_system( 787 net, 788 GPA.pc.get_save_name(), 789 f'switch_{len(GPA.pai_tracker.member_vars["switch_epochs"])}', 790 switch_call=True, 791 ) 792 GPA.pai_tracker.member_vars[ 793 "current_n_learning_rate_initial_skip_steps" 794 ] = (skip1 + 1) 795 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"].append( 796 old_accuracy 797 ) 798 GPA.pai_tracker.member_vars["global_best_validation_score"] = old_global 799 GPA.pai_tracker.member_vars["initial_lr_test_epoch_count"] = old_counts 800 801 # If there is one score already, this is first step at next score 802 elif len(GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"]) == 1: 803 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"].append( 804 GPA.pai_tracker.member_vars["current_best_validation_score"] 805 ) 806 807 # If this LR's score was worse than last LR's score 808 lr_score_worse = False 809 if GPA.pai_tracker.member_vars["maximizing_score"]: 810 lr_score_worse = ( 811 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][0] 812 > GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][1] 813 ) 814 else: 815 lr_score_worse = ( 816 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][0] 817 < GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][1] 818 ) 819 820 if lr_score_worse: 821 restructured = True 822 GPA.pai_tracker.clear_optimizer_and_scheduler() 823 824 if GPA.pc.get_verbose(): 825 print( 826 f'Got initial {GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]-1} ' 827 f'step score {GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][0]} ' 828 f'and {GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]} ' 829 f'score at step {GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][1]} ' 830 f"so loading old score" 831 ) 832 833 prior_best = GPA.pai_tracker.member_vars[ 834 "current_cycle_lr_max_scores" 835 ][0] 836 837 now = datetime.now() 838 dt_string = now.strftime("_%d.%m.%Y.%H.%M.%S") 839 840 GPA.pai_tracker.save_graphs( 841 f'{dt_string}_PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}' 842 ) 843 844 if GPA.pc.get_test_saves(): 845 UPA.save_system( 846 net, 847 GPA.pc.get_save_name(), 848 f'PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}', 849 ) 850 851 if GPA.pc.get_verbose(): 852 print( 853 f"Saving with initial steps: {dt_string}_PBCount_" 854 f'{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_' 855 f'{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}' 856 ) 857 858 if GPA.pc.get_test_saves(): 859 net = UPA.load_system( 860 net, 861 GPA.pc.get_save_name(), 862 f'PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]-1}', 863 switch_call=True, 864 ) 865 866 # Save graphs for chosen one 867 now = datetime.now() 868 dt_string = now.strftime("_%d.%m.%Y.%H.%M.%S") 869 870 GPA.pai_tracker.save_graphs( 871 f'{dt_string}_PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}PICKED' 872 ) 873 874 if GPA.pc.get_test_saves(): 875 UPA.save_system( 876 net, 877 GPA.pc.get_save_name(), 878 f'PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}', 879 ) 880 881 if GPA.pc.get_verbose(): 882 print( 883 f"Saving with initial steps: {dt_string}_PBCount_" 884 f'{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_' 885 f'{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}' 886 ) 887 888 GPA.pai_tracker.member_vars["committed_to_initial_rate"] = True 889 GPA.pai_tracker.member_vars["last_max_learning_rate_steps"] = ( 890 GPA.pai_tracker.member_vars["current_step_count"] 891 ) 892 GPA.pai_tracker.member_vars["last_max_learning_rate_value"] = ( 893 learning_rate2 894 ) 895 GPA.pai_tracker.member_vars["current_best_validation_score"] = ( 896 prior_best 897 ) 898 899 if GPA.pc.get_verbose(): 900 print( 901 f"Setting last max steps to " 902 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_steps"]} ' 903 f'and lr {GPA.pai_tracker.member_vars["last_max_learning_rate_value"]}' 904 ) 905 906 else: # Current LR score is better 907 if GPA.pc.get_verbose(): 908 print( 909 f'Got initial {GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]-1} ' 910 f'step score {GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][0]} ' 911 f'and {GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]} ' 912 f'score at step {GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][1]} ' 913 f"so NOT loading old score and continuing with this score" 914 ) 915 916 if at_last_count: # If this is the last one, set it to be picked 917 restructured = True 918 GPA.pai_tracker.clear_optimizer_and_scheduler() 919 920 now = datetime.now() 921 dt_string = now.strftime("_%d.%m.%Y.%H.%M.%S") 922 923 GPA.pai_tracker.save_graphs( 924 f'{dt_string}_PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}PICKED' 925 ) 926 927 if GPA.pc.get_test_saves(): 928 UPA.save_system( 929 net, 930 GPA.pc.get_save_name(), 931 f'PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}', 932 ) 933 934 if GPA.pc.get_verbose(): 935 print( 936 f"Saving with initial steps: {dt_string}_PBCount_" 937 f'{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_' 938 f'{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}' 939 ) 940 941 GPA.pai_tracker.member_vars["committed_to_initial_rate"] = True 942 GPA.pai_tracker.member_vars["last_max_learning_rate_steps"] = ( 943 GPA.pai_tracker.member_vars["current_step_count"] 944 ) 945 GPA.pai_tracker.member_vars["last_max_learning_rate_value"] = ( 946 learning_rate2 947 ) 948 949 if GPA.pc.get_verbose(): 950 print( 951 f"Setting last max steps to " 952 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_steps"]} ' 953 f'and lr {GPA.pai_tracker.member_vars["last_max_learning_rate_value"]}' 954 ) 955 956 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"] = [] 957 958 elif len(GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"]) == 2: 959 print( 960 "Should never be here. Please let Perforated AI know if this happened." 961 ) 962 pdb.set_trace() 963 964 GPA.pai_tracker.member_vars["global_best_validation_score"] = ( 965 best_score_so_far 966 ) 967 968 else: 969 if GPA.pc.get_verbose(): 970 print( 971 f"Setting last max steps to " 972 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_steps"]} ' 973 f'and lr {GPA.pai_tracker.member_vars["last_max_learning_rate_value"]}' 974 ) 975 GPA.pai_tracker.member_vars["last_max_learning_rate_steps"] += 1 976 GPA.pai_tracker.member_vars["last_max_learning_rate_value"] = learning_rate2 977 if restructured: 978 return NETWORK_RESTRUCTURED, net 979 else: 980 return NO_MODEL_UPDATE, net 981 982 983class PAINeuronModuleTracker: 984 """ 985 Manager class that tracks all neuron layers and dendrite layers, 986 controls when new dendrites are added, and communicates signals to modules. 987 """ 988 989 def __init__( 990 self, 991 doing_pai, 992 save_name, 993 making_graphs=True, 994 param_vals_setting=-1, 995 values_per_train_epoch=-1, 996 values_per_val_epoch=-1, 997 ): 998 """Initialize the tracker 999 1000 Parameters 1001 ---------- 1002 doing_pai : bool 1003 Whether or not dendrites should be used. 1004 save_name : str 1005 The base name for saving models and graphs. 1006 making_graphs : bool, optional 1007 Whether or not to generate graphs, by default True. 1008 param_vals_setting : int, optional 1009 Parameter values setting, by default -1. 1010 values_per_train_epoch : int, optional 1011 The number of values to look back for graphing 1012 during training, by default -1 (all values). 1013 values_per_val_epoch : int, optional 1014 The number of values to look back for graphing 1015 during validation, by default -1 (all values). 1016 Returns 1017 ------- 1018 None 1019 """ 1020 1021 # Dict of member vars and their types for saving 1022 self.member_vars = {} 1023 self.member_var_types = {} 1024 1025 # Whether or not PAI will be running 1026 self.member_vars["doing_pai"] = doing_pai 1027 self.member_var_types["doing_pai"] = "bool" 1028 1029 # How many Dendrites have been added 1030 self.member_vars["num_dendrites_added"] = 0 1031 self.member_var_types["num_dendrites_added"] = "int" 1032 1033 # How many Dendrites have been successfully integrated, does not count currently training dendrites 1034 self.member_vars["num_dendrites_integrated"] = 0 1035 self.member_var_types["num_dendrites_integrated"] = "int" 1036 1037 # How many cycles have been run, *2 or *2+1 of the above 1038 self.member_vars["num_cycles"] = 0 1039 self.member_var_types["num_cycles"] = "int" 1040 1041 # Pointers to all neuron wrapped modules 1042 self.neuron_module_vector = [] 1043 1044 # Pointers to all non neuron modules for tracking 1045 self.tracked_neuron_module_vector = [] 1046 1047 # Neuron training or dendrite training mode 1048 self.member_vars["mode"] = "n" 1049 self.member_var_types["mode"] = "string" 1050 1051 # Number of epochs run excluding overwritten epochs 1052 self.member_vars["num_epochs_run"] = -1 1053 self.member_var_types["num_epochs_run"] = "int" 1054 1055 # Number including overwritten epochs 1056 self.member_vars["total_epochs_run"] = -1 1057 self.member_var_types["total_epochs_run"] = "int" 1058 1059 # Last epoch that validation/correlation score was improved 1060 self.member_vars["epoch_last_improved"] = 0 1061 self.member_var_types["epoch_last_improved"] = "int" 1062 1063 # Running validation accuracy 1064 self.member_vars["running_accuracy"] = 0 1065 self.member_var_types["running_accuracy"] = "float" 1066 1067 # True if maxing validation, False if minimizing Loss 1068 self.member_vars["maximizing_score"] = True 1069 self.member_var_types["maximizing_score"] = "bool" 1070 1071 # Mode for switching back and forth between learning modes 1072 self.member_vars["switch_mode"] = GPA.pc.get_switch_mode() 1073 self.member_var_types["switch_mode"] = "int" 1074 1075 # Epoch of the last switch 1076 self.member_vars["last_switch"] = 0 1077 self.member_var_types["last_switch"] = "int" 1078 1079 # Highest validation score from current cycle 1080 self.member_vars["current_best_validation_score"] = 0 1081 self.member_var_types["current_best_validation_score"] = "float" 1082 1083 # Last epoch where the learning rate was updated 1084 self.member_vars["initial_lr_test_epoch_count"] = -1 1085 self.member_var_types["initial_lr_test_epoch_count"] = "int" 1086 1087 # Highest validation score of full run 1088 self.member_vars["global_best_validation_score"] = 0 1089 self.member_var_types["global_best_validation_score"] = "float" 1090 1091 # List of switch epochs 1092 self.member_vars["switch_epochs"] = [] 1093 self.member_var_types["switch_epochs"] = "int array" 1094 1095 # Parameter counts at each network structure 1096 self.member_vars["param_counts"] = [] 1097 self.member_var_types["param_counts"] = "int array" 1098 1099 # List of epochs where switch was made to neuron training 1100 self.member_vars["n_switch_epochs"] = [] 1101 self.member_var_types["n_switch_epochs"] = "int array" 1102 1103 # List of epochs where switch was made to dendrite training 1104 self.member_vars["p_switch_epochs"] = [] 1105 self.member_var_types["p_switch_epochs"] = "int array" 1106 1107 # List of validation accuracies 1108 self.member_vars["accuracies"] = [] 1109 self.member_var_types["accuracies"] = "float array" 1110 1111 # List of epochs where score improved for scheduler updates 1112 self.member_vars["last_improved_accuracies"] = [] 1113 self.member_var_types["last_improved_accuracies"] = "int array" 1114 1115 # List of test accuracy scores registered 1116 self.member_vars["test_accuracies"] = [] 1117 self.member_var_types["test_accuracies"] = "float array" 1118 1119 # List of accuracies registered during neuron training 1120 self.member_vars["n_accuracies"] = [] 1121 self.member_var_types["n_accuracies"] = "float array" 1122 1123 # List of accuracies registered during dendrite training 1124 self.member_vars["p_accuracies"] = [] 1125 self.member_var_types["p_accuracies"] = "float array" 1126 1127 # Running average accuracies from recent epochs 1128 self.member_vars["running_accuracies"] = [] 1129 self.member_var_types["running_accuracies"] = "float array" 1130 1131 # List of additional scores recorded 1132 self.member_vars["extra_scores"] = {} 1133 self.member_var_types["extra_scores"] = "float array dictionary" 1134 1135 # Extra scores not set to be graphed 1136 self.member_vars["extra_scores_without_graphing"] = {} 1137 self.member_var_types["extra_scores_without_graphing"] = ( 1138 "float array dictionary" 1139 ) 1140 1141 # List of test scores 1142 self.member_vars["test_scores"] = [] 1143 self.member_var_types["test_scores"] = "float array" 1144 1145 # Extra scores calculated during neuron training 1146 self.member_vars["n_extra_scores"] = {} 1147 self.member_var_types["n_extra_scores"] = "float array dictionary" 1148 1149 # List of training losses calculated 1150 self.member_vars["training_loss"] = [] 1151 self.member_var_types["training_loss"] = "float array" 1152 1153 # List of learning rates at each epoch 1154 self.member_vars["training_learning_rates"] = [] 1155 self.member_var_types["training_learning_rates"] = "float array" 1156 1157 # Best dendrite scores 1158 self.member_vars["best_scores"] = [] 1159 self.member_var_types["best_scores"] = "float array array" 1160 1161 # Current dendrite scores 1162 self.member_vars["current_scores"] = [] 1163 self.member_var_types["current_scores"] = "float array array" 1164 1165 # Times for neuron training epochs 1166 self.member_vars["n_epoch_times"] = [] 1167 self.member_var_types["n_epoch_times"] = "float array" 1168 1169 # Timing values 1170 self.member_vars["p_epoch_times"] = [] 1171 self.member_var_types["p_epoch_times"] = "float array" 1172 self.member_vars["n_train_times"] = [] 1173 self.member_var_types["n_train_times"] = "float array" 1174 self.member_vars["p_train_times"] = [] 1175 self.member_var_types["p_train_times"] = "float array" 1176 self.member_vars["n_val_times"] = [] 1177 self.member_var_types["n_val_times"] = "float array" 1178 self.member_vars["p_val_times"] = [] 1179 self.member_var_types["p_val_times"] = "float array" 1180 1181 # Setting for tracking timing 1182 self.member_vars["manual_train_switch"] = False 1183 self.member_var_types["manual_train_switch"] = "bool" 1184 1185 # Tracking scores overwritten when reloading best model 1186 self.member_vars["overwritten_extras"] = [] 1187 self.member_var_types["overwritten_extras"] = "float array dictionary array" 1188 self.member_vars["overwritten_vals"] = [] 1189 self.member_var_types["overwritten_vals"] = "float array array" 1190 self.member_vars["overwritten_epochs"] = 0 1191 self.member_var_types["overwritten_epochs"] = "int" 1192 1193 # Setting for determining scores 1194 self.member_vars["param_vals_setting"] = GPA.pc.get_param_vals_setting() 1195 self.member_var_types["param_vals_setting"] = "int" 1196 1197 # Optimizer and scheduler types and instances 1198 self.member_vars["optimizer"] = None 1199 self.member_var_types["optimizer"] = "type" 1200 self.member_vars["scheduler"] = None 1201 self.member_var_types["scheduler"] = "type" 1202 self.member_vars["optimizer_instance"] = None 1203 self.member_var_types["optimizer_instance"] = "empty array" 1204 self.member_vars["scheduler_instance"] = None 1205 self.member_var_types["scheduler_instance"] = "empty array" 1206 1207 # Flag for if the tracker was loaded 1208 self.loaded = False 1209 1210 # flag for 1211 self.member_vars["step_status"] = STEP_CLEARED 1212 self.member_var_types["step_status"] = "int" 1213 1214 1215 # Settings for tracking learning rates 1216 self.member_vars["current_n_learning_rate_initial_skip_steps"] = 0 1217 self.member_var_types["current_n_learning_rate_initial_skip_steps"] = "int" 1218 self.member_vars["last_max_learning_rate_steps"] = 0 1219 self.member_var_types["last_max_learning_rate_steps"] = "int" 1220 self.member_vars["last_max_learning_rate_value"] = -1 1221 self.member_var_types["last_max_learning_rate_value"] = "float" 1222 self.member_vars["current_cycle_lr_max_scores"] = [] 1223 self.member_var_types["current_cycle_lr_max_scores"] = "float array" 1224 self.member_vars["current_step_count"] = 0 1225 self.member_var_types["current_step_count"] = "int" 1226 self.member_vars["committed_to_initial_rate"] = True 1227 self.member_var_types["committed_to_initial_rate"] = "bool" 1228 self.member_vars["best_mean_score_improved_this_epoch"] = 0 1229 self.member_var_types["best_mean_score_improved_this_epoch"] = "int" 1230 1231 # Flag for if current dendrite achieved highest global score 1232 self.member_vars["current_n_set_global_best"] = True 1233 self.member_var_types["current_n_set_global_best"] = "bool" 1234 1235 # Number of tries adding this dendrite count 1236 self.member_vars["num_dendrite_tries"] = 0 1237 self.member_var_types["num_dendrite_tries"] = "int" 1238 1239 # Count of batches per epoch 1240 self.values_per_train_epoch = values_per_train_epoch 1241 self.values_per_val_epoch = values_per_val_epoch 1242 1243 self.save_name = save_name 1244 self.making_graphs = making_graphs 1245 1246 self.start_time = time.time() 1247 self.saved_time = 0 1248 self.start_epoch(internal_call=True) 1249 1250 if GPA.pc.get_verbose(): 1251 print(f'Initializing with switch_mode {self.member_vars["switch_mode"]}') 1252 1253 def to_string(self): 1254 """Convert tracker values to string for saving with safetensors. 1255 1256 Parameters 1257 ---------- 1258 None 1259 1260 Returns 1261 ------- 1262 str 1263 Serialized tracker state suitable for storage in a safetensors field. 1264 """ 1265 1266 full_string = "" 1267 for var in self.member_vars: 1268 full_string += var + "," 1269 if self.member_vars[var] is None: 1270 full_string += "None" 1271 full_string += "\n" 1272 elif self.member_var_types[var] == "bool": 1273 full_string += str(self.member_vars[var]) 1274 full_string += "\n" 1275 elif self.member_var_types[var] in ("int", "float", "string"): 1276 full_string += str(self.member_vars[var]) 1277 full_string += "\n" 1278 elif self.member_var_types[var] == "type": 1279 name = ( 1280 self.member_vars[var].__module__ 1281 + "." 1282 + self.member_vars[var].__name__ 1283 ) 1284 full_string += str(self.member_vars[var]) 1285 full_string += "\n" 1286 elif self.member_var_types[var] == "empty array": 1287 full_string += "[]" 1288 full_string += "\n" 1289 elif self.member_var_types[var] in ("int array", "float array"): 1290 full_string += "\n" 1291 string = "" 1292 for val in self.member_vars[var]: 1293 string += str(val) + "," 1294 # Remove the last comma 1295 string = string[:-1] 1296 full_string += string 1297 full_string += "\n" 1298 elif self.member_var_types[var] == "float array dictionary array": 1299 full_string += "\n" 1300 for array in self.member_vars[var]: 1301 for key in array: 1302 string = key + "," 1303 for val in array[key]: 1304 string += str(val) + "," 1305 # Remove the last comma 1306 string = string[:-1] 1307 full_string += string 1308 full_string += "\n" 1309 full_string += "endkey" 1310 full_string += "\n" 1311 full_string += "endarray" 1312 full_string += "\n" 1313 elif self.member_var_types[var] == "float array dictionary": 1314 full_string += "\n" 1315 for key in self.member_vars[var]: 1316 string = key + "," 1317 for val in self.member_vars[var][key]: 1318 string += str(val) + "," 1319 # Remove the last comma 1320 string = string[:-1] 1321 full_string += string 1322 full_string += "\n" 1323 full_string += "end" 1324 full_string += "\n" 1325 elif self.member_var_types[var] == "float array array": 1326 full_string += "\n" 1327 for array in self.member_vars[var]: 1328 string = "" 1329 for val in array: 1330 string += str(val) + "," 1331 # Remove the last comma 1332 string = string[:-1] 1333 full_string += string 1334 full_string += "\n" 1335 full_string += "end" 1336 full_string += "\n" 1337 else: 1338 print("Did not find a member variable") 1339 pdb.set_trace() 1340 return full_string 1341 1342 def from_string(self, string): 1343 """Load tracker values from string. 1344 1345 Parameters 1346 ---------- 1347 string : str 1348 The string to load from. 1349 1350 Returns 1351 ------- 1352 None 1353 This function does not return a value. 1354 """ 1355 f = io.StringIO(string) 1356 while True: 1357 line = f.readline() 1358 if not line: 1359 break 1360 vals = line.split(",") 1361 var = vals[0] 1362 1363 if self.member_var_types[var] == "bool": 1364 val = vals[1][:-1] 1365 if val == "True": 1366 self.member_vars[var] = True 1367 elif val == "False": 1368 self.member_vars[var] = False 1369 elif val == "1": 1370 self.member_vars[var] = 1 1371 elif val == "0": 1372 self.member_vars[var] = 0 1373 else: 1374 print("Something went wrong with loading") 1375 pdb.set_trace() 1376 elif self.member_var_types[var] == "int": 1377 val = vals[1] 1378 self.member_vars[var] = int(val) 1379 elif self.member_var_types[var] == "float": 1380 val = vals[1] 1381 self.member_vars[var] = float(val) 1382 elif self.member_var_types[var] == "string": 1383 val = vals[1][:-1] 1384 self.member_vars[var] = val 1385 elif self.member_var_types[var] == "type": 1386 # Ignore loading types, tracker should have them set up 1387 continue 1388 elif self.member_var_types[var] == "empty array": 1389 val = vals[1] 1390 self.member_vars[var] = [] 1391 elif self.member_var_types[var] == "int array": 1392 vals = f.readline()[:-1].split(",") 1393 self.member_vars[var] = [] 1394 if vals[0] == "": 1395 continue 1396 for val in vals: 1397 self.member_vars[var].append(int(val)) 1398 elif self.member_var_types[var] == "float array": 1399 vals = f.readline()[:-1].split(",") 1400 self.member_vars[var] = [] 1401 if vals[0] == "": 1402 continue 1403 for val in vals: 1404 self.member_vars[var].append(float(val)) 1405 elif self.member_var_types[var] == "float array dictionary array": 1406 self.member_vars[var] = [] 1407 line2 = f.readline()[:-1] 1408 while line2 != "endarray": 1409 temp = {} 1410 while line2 != "endkey": 1411 vals = line2.split(",") 1412 name = vals[0] 1413 temp[name] = [] 1414 vals = vals[1:] 1415 for val in vals: 1416 temp[name].append(float(val)) 1417 line2 = f.readline()[:-1] 1418 self.member_vars[var].append(temp) 1419 line2 = f.readline()[:-1] 1420 elif self.member_var_types[var] == "float array dictionary": 1421 self.member_vars[var] = {} 1422 line2 = f.readline()[:-1] 1423 while line2 != "end": 1424 vals = line2.split(",") 1425 name = vals[0] 1426 self.member_vars[var][name] = [] 1427 vals = vals[1:] 1428 for val in vals: 1429 self.member_vars[var][name].append(float(val)) 1430 line2 = f.readline()[:-1] 1431 elif self.member_var_types[var] == "float array array": 1432 self.member_vars[var] = [] 1433 line2 = f.readline()[:-1] 1434 while line2 != "end": 1435 vals = line2.split(",") 1436 self.member_vars[var].append([]) 1437 if line2: 1438 for val in vals: 1439 self.member_vars[var][-1].append(float(val)) 1440 line2 = f.readline()[:-1] 1441 else: 1442 print("Did not find a member variable") 1443 1444 pdb.set_trace() 1445 1446 def from_string_debug(self, string): 1447 """Debug function to print tracker values from string without loading them. 1448 1449 Parameters 1450 ---------- 1451 string : str 1452 The string to debug load from. 1453 1454 Returns 1455 ------- 1456 None 1457 This function does not return a value. 1458 """ 1459 f = io.StringIO(string) 1460 print("=== DEBUGGING TRACKER VARIABLES ===") 1461 1462 while True: 1463 line = f.readline() 1464 if not line: 1465 break 1466 vals = line.split(",") 1467 var = vals[0] 1468 1469 print(f"\nVariable: {var}") 1470 print(f"Type: {self.member_var_types.get(var, 'UNKNOWN TYPE')}") 1471 print(f"Current value: {self.member_vars.get(var, 'NOT SET')}") 1472 1473 if self.member_var_types.get(var) == "bool": 1474 val = vals[1][:-1] 1475 print(f"Would set to: {val} -> {val == 'True'}") 1476 1477 elif self.member_var_types.get(var) == "int": 1478 val = vals[1] 1479 print(f"Would set to: {int(val)}") 1480 1481 elif self.member_var_types.get(var) == "float": 1482 val = vals[1] 1483 print(f"Would set to: {float(val)}") 1484 1485 elif self.member_var_types.get(var) == "string": 1486 val = vals[1][:-1] 1487 print(f"Would set to: '{val}'") 1488 1489 elif self.member_var_types.get(var) == "type": 1490 print("Would skip (type loading)") 1491 1492 elif self.member_var_types.get(var) == "empty array": 1493 val = vals[1] 1494 print(f"Would set to: [] (empty array)") 1495 1496 elif self.member_var_types.get(var) == "int array": 1497 vals_line = f.readline()[:-1].split(",") 1498 print(f"Would set to int array with {len(vals_line)} elements:") 1499 if vals_line[0] != "": 1500 print( 1501 f" Elements: {vals_line[:5]}{'...' if len(vals_line) > 5 else ''}" 1502 ) 1503 else: 1504 print(" Empty array") 1505 1506 elif self.member_var_types.get(var) == "float array": 1507 vals_line = f.readline()[:-1].split(",") 1508 print(f"Would set to float array with {len(vals_line)} elements:") 1509 if vals_line[0] != "": 1510 print( 1511 f" Elements: {vals_line[:5]}{'...' if len(vals_line) > 5 else ''}" 1512 ) 1513 else: 1514 print(" Empty array") 1515 1516 elif self.member_var_types.get(var) == "float array dictionary array": 1517 print("Would process float array dictionary array:") 1518 array_count = 0 1519 line2 = f.readline()[:-1] 1520 while line2 != "endarray": 1521 key_count = 0 1522 while line2 != "endkey": 1523 vals_dict = line2.split(",") 1524 name = vals_dict[0] 1525 print( 1526 f" Array {array_count}, Key '{name}': {len(vals_dict)-1} elements" 1527 ) 1528 key_count += 1 1529 line2 = f.readline()[:-1] 1530 print(f" Array {array_count} has {key_count} keys") 1531 array_count += 1 1532 line2 = f.readline()[:-1] 1533 print(f" Total arrays: {array_count}") 1534 1535 elif self.member_var_types.get(var) == "float array dictionary": 1536 print("Would process float array dictionary:") 1537 line2 = f.readline()[:-1] 1538 key_count = 0 1539 while line2 != "end": 1540 vals_dict = line2.split(",") 1541 name = vals_dict[0] 1542 print(f" Key '{name}': {len(vals_dict)-1} elements") 1543 key_count += 1 1544 line2 = f.readline()[:-1] 1545 print(f" Total keys: {key_count}") 1546 1547 elif self.member_var_types.get(var) == "float array array": 1548 print("Would process float array array:") 1549 line2 = f.readline()[:-1] 1550 array_count = 0 1551 while line2 != "end": 1552 if line2: 1553 vals_array = line2.split(",") 1554 print(f" Array {array_count}: {len(vals_array)} elements") 1555 else: 1556 print(f" Array {array_count}: empty") 1557 array_count += 1 1558 line2 = f.readline()[:-1] 1559 print(f" Total arrays: {array_count}") 1560 1561 else: 1562 print(f"UNKNOWN TYPE: {self.member_var_types.get(var, 'NOT FOUND')}") 1563 1564 print("\n=== END DEBUG ===") 1565 1566 def save_tracker_settings(self): 1567 """Save tracker settings for DistributedDataParallel use. 1568 1569 Saves settings in save_name/array_dims.csv 1570 1571 Parameters 1572 ---------- 1573 None 1574 Returns 1575 ------- 1576 None 1577 1578 ----- 1579 Instructions for use are in API customization.md 1580 """ 1581 if not os.path.isdir(self.save_name): 1582 os.makedirs(self.save_name) 1583 f = open(self.save_name + "/array_dims.csv", "w") 1584 for layer in self.neuron_module_vector: 1585 dv = layer.dendrite_module.dendrite_values[0] 1586 shape_str = ",".join(str(s) for s in dv.dendrite_storage_shape.tolist()) 1587 f.write(f"{layer.name},{shape_str}\n") 1588 f.close() 1589 if not GPA.pc.get_silent(): 1590 print("Tracker settings saved.") 1591 print("You may now delete save_tracker_settings") 1592 1593 def initialize_tracker_settings(self): 1594 """Initialize tracker settings from saved file. 1595 1596 This function loads tracker settings from a CSV file and applies them 1597 to the layers the tracker is managing. 1598 1599 Parameters 1600 ---------- 1601 None 1602 1603 Returns 1604 ------- 1605 None 1606 1607 """ 1608 1609 channels = {} 1610 if not os.path.exists(self.save_name + "/array_dims.csv"): 1611 print( 1612 "You must call save_tracker_settings before " 1613 "initialize_tracker_settings" 1614 ) 1615 print("Follow instructions in customization.md") 1616 pdb.set_trace() 1617 f = open(self.save_name + "/array_dims.csv", "r") 1618 for line in f: 1619 parts = line.strip().split(",") 1620 channels[parts[0]] = [int(s) for s in parts[1:]] 1621 for layer in self.neuron_module_vector: 1622 dv = layer.dendrite_module.dendrite_values[0] 1623 dv.setup_arrays(channels[layer.name]) 1624 1625 def set_optimizer_instance(self, optimizer_instance, additional_optimizers=[]): 1626 """Set optimizer instance directly. 1627 1628 Parameters 1629 ---------- 1630 optimizer_instance : object 1631 The optimizer instance to set. 1632 1633 Returns 1634 ------- 1635 None 1636 1637 """ 1638 # This call must be first before the parameters get filtered. 1639 optimizer_instance.zero_grad() 1640 try: 1641 for param_group in optimizer_instance.param_groups: 1642 if ( 1643 param_group["weight_decay"] > 0 1644 and GPA.pc.get_weight_decay_accepted() is False 1645 ): 1646 _pai_log( 1647 "warning", 1648 "For PAI training it is recommended to not use weight decay in your optimizer", 1649 ) 1650 1651 except: 1652 pass 1653 self.member_vars["optimizer_instance"] = optimizer_instance 1654 if GPA.pc.get_perforated_backpropagation(): 1655 TPB.setup_optimizer_pb(self.member_vars["optimizer_instance"]) 1656 for optimizer in additional_optimizers: 1657 TPB.filter_params(optimizer) 1658 1659 def set_optimizer(self, optimizer): 1660 """Set optimizer type to be initialized later 1661 1662 Parameters 1663 ---------- 1664 optimizer : object 1665 The optimizer type to set. 1666 1667 Returns 1668 ------- 1669 None 1670 1671 """ 1672 self.member_vars["optimizer"] = optimizer 1673 1674 def set_scheduler(self, scheduler): 1675 """Set scheduler type to be initialized later 1676 1677 Parameters 1678 ---------- 1679 scheduler : object 1680 The scheduler type to set. 1681 1682 Returns 1683 ------- 1684 None 1685 1686 """ 1687 if scheduler is not torch.optim.lr_scheduler.ReduceLROnPlateau: 1688 if GPA.pc.get_verbose(): 1689 print("Not using ReduceLROnPlateau, this is not recommended") 1690 self.member_vars["scheduler"] = scheduler 1691 1692 def increment_scheduler(self, num_ticks, mode): 1693 """Increment the scheduler a set number of times. 1694 1695 Used for finding best initial learning rate when adding dendrites. 1696 1697 Parameters 1698 ---------- 1699 num_ticks : int 1700 The number of scheduler steps to take. 1701 mode : str 1702 The mode for stepping the scheduler. Options are: 1703 - "step_learning_rate": Step based on improved accuracy epochs 1704 - "increment_epoch_count": Step based on total epoch count 1705 1706 Returns 1707 ------- 1708 current_steps : int 1709 The number of learning rate changes that occurred. 1710 learning_rate1 : float 1711 The final learning rate after stepping. 1712 1713 """ 1714 1715 current_steps = 0 1716 current_ticker = 0 1717 1718 for param_group in GPA.pai_tracker.member_vars[ 1719 "optimizer_instance" 1720 ].param_groups: 1721 learning_rate1 = param_group["lr"] 1722 1723 if GPA.pc.get_verbose(): 1724 print("Using scheduler:") 1725 print(type(self.member_vars["scheduler_instance"])) 1726 1727 while current_ticker < num_ticks: 1728 if GPA.pc.get_verbose(): 1729 print( 1730 f"Lower start rate initial {learning_rate1} " 1731 f'stepping {GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]} times' 1732 ) 1733 1734 if ( 1735 type(self.member_vars["scheduler_instance"]) 1736 is torch.optim.lr_scheduler.ReduceLROnPlateau 1737 ): 1738 if mode == "step_learning_rate": 1739 # Step with counter as last improved accuracy 1740 self.member_vars["scheduler_instance"].step( 1741 metrics=self.member_vars["last_improved_accuracies"][ 1742 GPA.pai_tracker.steps_after_switch() - 1 1743 ] 1744 ) 1745 elif mode == "increment_epoch_count": 1746 # Step with improved epoch counts up to current location 1747 self.member_vars["scheduler_instance"].step( 1748 metrics=self.member_vars["last_improved_accuracies"][ 1749 -((num_ticks - 1) - current_ticker) - 1 1750 ] 1751 ) 1752 else: 1753 self.member_vars["scheduler_instance"].step() 1754 1755 for param_group in GPA.pai_tracker.member_vars[ 1756 "optimizer_instance" 1757 ].param_groups: 1758 learning_rate2 = param_group["lr"] 1759 1760 if learning_rate2 != learning_rate1: 1761 current_steps += 1 1762 learning_rate1 = learning_rate2 1763 if mode == "step_learning_rate": 1764 current_ticker += 1 1765 if GPA.pc.get_verbose(): 1766 print(f"1 step {current_steps} to {learning_rate2}") 1767 1768 if mode == "increment_epoch_count": 1769 current_ticker += 1 1770 1771 return current_steps, learning_rate1 1772 1773 def setup_optimizer(self, net, opt_args, sched_args=None, parameters=None): 1774 """Initialize the optimizer and scheduler when added. 1775 1776 Parameters 1777 ---------- 1778 net : object 1779 The neural network model. 1780 opt_args : dict 1781 The arguments for the optimizer. 1782 sched_args : dict, optional 1783 The arguments for the scheduler, by default None. 1784 1785 Returns 1786 ------- 1787 optimizer : object 1788 The initialized optimizer instance. 1789 scheduler : object or None 1790 The initialized scheduler instance, or None if no scheduler was set. 1791 1792 """ 1793 if "weight_decay" in opt_args and not GPA.pc.get_weight_decay_accepted(): 1794 _pai_log( 1795 "warning", 1796 "For PAI training it is recommended to not use weight decay in your optimizer", 1797 ) 1798 1799 if ("model" not in opt_args.keys()) and "params" not in opt_args.keys(): 1800 print("In setup_optimizer it will be depreciated to not pass in params yourself in the future") 1801 print("please change the settings to include params") 1802 if self.member_vars["mode"] == "n": 1803 if parameters is not None: 1804 opt_args["params"] = parameters 1805 else: 1806 opt_args["params"] = filter(lambda p: p.requires_grad, net.parameters()) 1807 else: 1808 params = UPA.get_pai_network_params(net) 1809 if parameters is not None: 1810 # Filter parameters to only those in params, preserving weight_decay 1811 params_set = set(params) 1812 filtered_params = [] 1813 for param_group in parameters: 1814 filtered_group_params = [p for p in param_group["params"] if p in params_set] 1815 if filtered_group_params: 1816 filtered_params.append({ 1817 "params": filtered_group_params, 1818 "weight_decay": param_group["weight_decay"] 1819 }) 1820 opt_args["params"] = filtered_params 1821 else: 1822 opt_args["params"] = params 1823 elif "params" in opt_args.keys(): 1824 # Check if params is a list of param groups (dicts) or a single param group 1825 params_value = opt_args["params"] 1826 if isinstance(params_value, list) and len(params_value) > 0: 1827 # Check if it's a list of dicts (multiple param groups) or list of tensors (single group) 1828 if isinstance(params_value[0], dict): 1829 # Multiple param groups format: [{"params": [...], "lr": ...}, ...] 1830 # Filter each param group for requires_grad 1831 filtered_param_groups = [] 1832 for param_group in params_value: 1833 filtered_group_params = [p for p in param_group["params"] if p.requires_grad] 1834 if filtered_group_params: 1835 new_group = param_group.copy() 1836 new_group["params"] = filtered_group_params 1837 filtered_param_groups.append(new_group) 1838 opt_args["params"] = filtered_param_groups 1839 else: 1840 # Single param group format: [tensor1, tensor2, ...] or generator 1841 # Filter for requires_grad 1842 opt_args["params"] = [p for p in params_value if p.requires_grad] 1843 elif hasattr(params_value, '__iter__'): 1844 # Handle generators or other iterables 1845 opt_args["params"] = [p for p in params_value if p.requires_grad] 1846 1847 optimizer = self.member_vars["optimizer"](**opt_args) 1848 self.set_optimizer_instance(optimizer) 1849 1850 if self.member_vars["scheduler"] is not None: 1851 # Handle SequentialLR specially 1852 if self.member_vars["scheduler"] is torch.optim.lr_scheduler.SequentialLR: 1853 """ 1854 sched_args should be a dict with "schedulers" (list of tuples) and "milestones" 1855 For example: 1856 sequential_schedArgs = { 1857 "schedulers": [ 1858 (warmup_scheduler_class, warmup_schedArgs), 1859 (main_scheduler_class, main_schedArgs) 1860 ], 1861 "milestones": [switch_epoch] 1862 } 1863 """ 1864 schedulers = [] 1865 milestones = sched_args.get("milestones", []) 1866 scheduler_configs = sched_args.get("schedulers", []) 1867 1868 for scheduler_class, scheduler_args in scheduler_configs: 1869 schedulers.append(scheduler_class(optimizer, **scheduler_args)) 1870 1871 self.member_vars["scheduler_instance"] = torch.optim.lr_scheduler.SequentialLR( 1872 optimizer, schedulers=schedulers, milestones=milestones 1873 ) 1874 else: 1875 self.member_vars["scheduler_instance"] = self.member_vars["scheduler"]( 1876 optimizer, **sched_args 1877 ) 1878 current_steps = 0 1879 1880 for param_group in GPA.pai_tracker.member_vars[ 1881 "optimizer_instance" 1882 ].param_groups: 1883 learning_rate1 = param_group["lr"] 1884 1885 if GPA.pc.get_verbose(): 1886 print( 1887 f"Resetting scheduler with {GPA.pai_tracker.steps_after_switch()} " 1888 f"steps and {GPA.pc.get_initial_history_after_switches()} initial ticks to skip" 1889 ) 1890 1891 # Find setting of previously used learning rate before adding dendrites 1892 if ( 1893 GPA.pai_tracker.member_vars[ 1894 "current_n_learning_rate_initial_skip_steps" 1895 ] 1896 != 0 1897 ): 1898 additional_steps, learning_rate1 = self.increment_scheduler( 1899 GPA.pai_tracker.member_vars[ 1900 "current_n_learning_rate_initial_skip_steps" 1901 ], 1902 "step_learning_rate", 1903 ) 1904 current_steps += additional_steps 1905 1906 if self.member_vars["mode"] == "n" or GPA.pc.get_learn_dendrites_live(): 1907 initial = GPA.pc.get_initial_history_after_switches() 1908 else: 1909 initial = 0 1910 1911 if GPA.pai_tracker.steps_after_switch() > initial: 1912 # Minus extra 1 because this gets called after start epoch 1913 additional_steps, learning_rate1 = self.increment_scheduler( 1914 (GPA.pai_tracker.steps_after_switch() - initial) - 1, 1915 "increment_epoch_count", 1916 ) 1917 current_steps += additional_steps 1918 1919 if GPA.pc.get_verbose(): 1920 print( 1921 f"Scheduler update loop with {current_steps} " 1922 f"ended with {learning_rate1}" 1923 ) 1924 print( 1925 f"Scheduler ended with {current_steps} steps " 1926 f"and lr of {learning_rate1}" 1927 ) 1928 1929 self.member_vars["current_step_count"] = current_steps 1930 return optimizer, self.member_vars["scheduler_instance"] 1931 else: 1932 return optimizer, None 1933 1934 def clear_optimizer_and_scheduler(self): 1935 """Clear the instances for saving. 1936 1937 Parameters 1938 ---------- 1939 None 1940 1941 Returns 1942 ------- 1943 None 1944 This function does not return a value. 1945 """ 1946 self.member_vars["optimizer_instance"] = None 1947 self.member_vars["scheduler_instance"] = None 1948 1949 def switch_time(self): 1950 """Determine if it's time to switch between neuron and dendrite training. 1951 1952 Parameters 1953 ---------- 1954 None 1955 1956 Returns 1957 ------- 1958 bool 1959 True if it's time to switch, False otherwise. 1960 1961 Notes 1962 ----- 1963 Based on current settings and history of scores. 1964 """ 1965 1966 switch_phrase = "No mode, this should never be the case." 1967 switch_number = GPA.pc.get_n_epochs_to_switch() 1968 if self.member_vars["switch_mode"] == GPA.pc.DOING_SWITCH_EVERY_TIME: 1969 switch_phrase = "DOING_SWITCH_EVERY_TIME" 1970 elif self.member_vars["switch_mode"] == GPA.pc.DOING_HISTORY: 1971 switch_phrase = "DOING_HISTORY" 1972 elif self.member_vars["switch_mode"] == GPA.pc.DOING_FIXED_SWITCH: 1973 switch_phrase = "DOING_FIXED_SWITCH" 1974 switch_number = GPA.pc.get_fixed_switch_num() 1975 elif self.member_vars["switch_mode"] == GPA.pc.DOING_NO_SWITCH: 1976 switch_phrase = "DOING_NO_SWITCH" 1977 else: 1978 print( 1979 "A switch mode must be set. Check your settings for GPA.pc.set_switch_mode()." 1980 ) 1981 pdb.set_trace() 1982 if not GPA.pc.get_silent(): 1983 if(GPA.pc.get_perforated_backpropagation()): 1984 print( 1985 f'Checking PAI switch with mode {self.member_vars["mode"]}, ' 1986 f'switch mode {switch_phrase}, epoch {self.member_vars["num_epochs_run"]}, ' 1987 f'last improved epoch {self.member_vars["epoch_last_improved"]}, ' 1988 f'total epochs {self.member_vars["total_epochs_run"]}, ' 1989 f'n: {switch_number}, p: {GPA.pc.get_p_epochs_to_switch()}, ' 1990 f'num_cycles: {self.member_vars["num_cycles"]}' 1991 ) 1992 else: 1993 print( 1994 f'Checking PAI switch with mode {self.member_vars["mode"]}, ' 1995 f'switch mode {switch_phrase}, epoch {self.member_vars["num_epochs_run"]}, ' 1996 f'last improved epoch {self.member_vars["epoch_last_improved"]}, ' 1997 f'total epochs {self.member_vars["total_epochs_run"]}, ' 1998 f'n: {switch_number}, num_cycles: {self.member_vars["num_cycles"]}' 1999 ) 2000 print( 2001 f' Score tracking: current_n_set_global_best={self.member_vars["current_n_set_global_best"]}, ' 2002 f'global_best={self.member_vars["global_best_validation_score"]:.4f}, ' 2003 f'current_best={self.member_vars["current_best_validation_score"]:.4f}' 2004 ) 2005 if GPA.pc.get_perforated_backpropagation(): 2006 # this will fill in epoch last improved 2007 TPB.best_pai_score_improved_this_epoch(self) ## CLOSED ONLY 2008 if self.member_vars["switch_mode"] == GPA.pc.DOING_NO_SWITCH: 2009 if not GPA.pc.get_silent(): 2010 print("Returning False - doing no switch mode") 2011 return False 2012 2013 if self.member_vars["switch_mode"] == GPA.pc.DOING_SWITCH_EVERY_TIME: 2014 if not GPA.pc.get_silent(): 2015 print("Returning True - switching every time") 2016 return True 2017 2018 # Check if we're in the middle of learning rate optimization 2019 # If so, block ALL switch triggers until committed 2020 if GPA.pc.get_verbose(): 2021 print("=== LR Optimization Check ===") 2022 print(f' mode == "n": {self.member_vars["mode"] == "n"}') 2023 print(f" get_learn_dendrites_live(): {GPA.pc.get_learn_dendrites_live()}") 2024 print(f' committed_to_initial_rate: {GPA.pai_tracker.member_vars["committed_to_initial_rate"]}') 2025 print(f" get_dont_give_up_unless_learning_rate_lowered(): {GPA.pc.get_dont_give_up_unless_learning_rate_lowered()}") 2026 print(f' current_n_learning_rate_initial_skip_steps: {self.member_vars["current_n_learning_rate_initial_skip_steps"]}') 2027 print(f' last_max_learning_rate_steps: {self.member_vars["last_max_learning_rate_steps"]}') 2028 print(f' skip_steps < max_steps: {self.member_vars["current_n_learning_rate_initial_skip_steps"] < self.member_vars["last_max_learning_rate_steps"]}') 2029 print(f' scheduler is not None: {self.member_vars["scheduler"] is not None}') 2030 print("=============================") 2031 2032 if ( 2033 ((self.member_vars["mode"] == "n") or GPA.pc.get_learn_dendrites_live()) 2034 and (GPA.pai_tracker.member_vars["committed_to_initial_rate"] is False) 2035 and (GPA.pc.get_dont_give_up_unless_learning_rate_lowered()) 2036 and ( 2037 self.member_vars["current_n_learning_rate_initial_skip_steps"] 2038 <= self.member_vars["last_max_learning_rate_steps"] 2039 ) 2040 and self.member_vars["scheduler"] is not None 2041 ): 2042 if not GPA.pc.get_silent(): 2043 print( 2044 f"Returning False - learning rate optimization in progress. " 2045 f"Not committed yet. Comparing " 2046 f'initial {self.member_vars["current_n_learning_rate_initial_skip_steps"]} ' 2047 f'to last max {self.member_vars["last_max_learning_rate_steps"]}' 2048 ) 2049 return False 2050 2051 if len(self.member_vars["switch_epochs"]) == 0: 2052 this_count = self.member_vars["num_epochs_run"] 2053 else: 2054 this_count = ( 2055 self.member_vars["num_epochs_run"] 2056 - self.member_vars["switch_epochs"][-1] 2057 ) 2058 cap_switch = False 2059 if GPA.pc.get_perforated_backpropagation(): 2060 cap_switch = TPB.check_cap_switch(self, this_count) 2061 2062 if self.member_vars["switch_mode"] == GPA.pc.DOING_HISTORY and ( 2063 ( 2064 (self.member_vars["mode"] == "n") 2065 and ( 2066 self.member_vars["num_epochs_run"] 2067 - self.member_vars["epoch_last_improved"] 2068 >= GPA.pc.get_n_epochs_to_switch() 2069 ) 2070 and this_count 2071 >= GPA.pc.get_initial_history_after_switches() 2072 + GPA.pc.get_n_epochs_to_switch() 2073 ) 2074 or (GPA.pc.get_perforated_backpropagation() and TPB.history_switch(self)) 2075 or cap_switch 2076 ): 2077 if not GPA.pc.get_silent(): 2078 print("Returning True - History and last improved is hit") 2079 return True 2080 2081 if self.member_vars["switch_mode"] == GPA.pc.DOING_FIXED_SWITCH and ( 2082 ( 2083 self.member_vars["total_epochs_run"] % GPA.pc.get_fixed_switch_num() 2084 == GPA.pc.get_fixed_switch_num() - 1 2085 ) 2086 and self.member_vars["num_epochs_run"] 2087 >= GPA.pc.get_first_fixed_switch_num() - 1 2088 ): 2089 if not GPA.pc.get_silent(): 2090 print("Returning True - Fixed switch number is hit") 2091 return True 2092 2093 if not GPA.pc.get_silent(): 2094 print("Returning False - no triggers to switch have been hit") 2095 return False 2096 2097 def steps_after_switch(self): 2098 """Based on settings, return value for steps since a switch. 2099 2100 Different options for param vals setting determine what is returned. 2101 2102 Parameters 2103 ---------- 2104 None 2105 2106 Returns 2107 ------- 2108 int 2109 The number of epochs since the last switch, or total epochs run, 2110 depending on settings. 2111 2112 """ 2113 if self.member_vars["param_vals_setting"] == GPA.pc.PARAM_VALS_BY_TOTAL_EPOCH: 2114 return self.member_vars["num_epochs_run"] 2115 elif ( 2116 self.member_vars["param_vals_setting"] == GPA.pc.PARAM_VALS_BY_UPDATE_EPOCH 2117 ): 2118 return self.member_vars["num_epochs_run"] - self.member_vars["last_switch"] 2119 elif ( 2120 self.member_vars["param_vals_setting"] 2121 == GPA.pc.PARAM_VALS_BY_NEURON_EPOCH_START 2122 ): 2123 if self.member_vars["mode"] == "p": 2124 return ( 2125 self.member_vars["num_epochs_run"] - self.member_vars["last_switch"] 2126 ) 2127 else: 2128 return self.member_vars["num_epochs_run"] 2129 else: 2130 print( 2131 f'{self.member_vars["param_vals_setting"]} is not a valid param vals option' 2132 ) 2133 pdb.set_trace() 2134 2135 def add_pai_neuron_module(self, new_module, initial_add=True): 2136 """Add neuron modules to internal vectors. 2137 2138 Parameters 2139 ---------- 2140 new_module : object 2141 The new module to add. 2142 initial_add : bool, optional 2143 Whether this is the initial addition rather than loading from file 2144 2145 Returns 2146 ------- 2147 None 2148 2149 """ 2150 2151 # If it's a duplicate, ignore the second addition 2152 if new_module in self.neuron_module_vector: 2153 return 2154 self.neuron_module_vector.append(new_module) 2155 if self.member_vars["doing_pai"]: 2156 PA.set_wrapped_params(new_module) 2157 if initial_add: 2158 self.member_vars["best_scores"].append([]) 2159 self.member_vars["current_scores"].append([]) 2160 2161 def add_tracked_neuron_module(self, new_module, initial_add=True): 2162 """Add tracked modules to internal vectors 2163 2164 Parameters 2165 ---------- 2166 new_module : object 2167 The new module to add. 2168 initial_add : bool, optional 2169 Whether this is the initial addition rather than loading from file 2170 2171 Returns 2172 ------- 2173 None 2174 2175 """ 2176 # If it's a duplicate, ignore the second addition 2177 if new_module in self.tracked_neuron_module_vector: 2178 return 2179 self.tracked_neuron_module_vector.append(new_module) 2180 if self.member_vars["doing_pai"]: 2181 PA.set_tracked_params(new_module) 2182 2183 def reset_module_vector(self, net, load_from_restart): 2184 """Clear internal vectors and reset from network. 2185 2186 Parameters 2187 ---------- 2188 net : object 2189 The neural network model. 2190 load_from_restart : bool 2191 Whether loading from a restart file. 2192 2193 Returns 2194 ------- 2195 None 2196 2197 """ 2198 self.neuron_module_vector = [] 2199 self.tracked_neuron_module_vector = [] 2200 this_list = UPA.get_pai_modules(net, 0) 2201 for module in this_list: 2202 self.add_pai_neuron_module(module, initial_add=load_from_restart) 2203 this_list = UPA.get_tracked_modules(net, 0) 2204 for module in this_list: 2205 self.add_tracked_neuron_module(module, initial_add=load_from_restart) 2206 2207 def reset_vals_for_score_reset(self): 2208 """Reset cycle scores for new cycle. 2209 2210 Parameters 2211 ---------- 2212 None 2213 2214 Returns 2215 ------- 2216 None 2217 This function does not return a value. 2218 """ 2219 2220 if GPA.pc.get_find_best_lr(): 2221 self.member_vars["committed_to_initial_rate"] = False 2222 print("Resetting committed to initial rate to False") 2223 # If retaining all dendrties always say that the current dendrites set global best for saving and loading 2224 if GPA.pc.get_retain_all_dendrites(): 2225 self.member_vars["current_n_set_global_best"] = True 2226 self.member_vars["global_best_validation_score"] = 0 2227 else: 2228 self.member_vars["current_n_set_global_best"] = False 2229 2230 # Don't reset global best, but do reset current best 2231 self.member_vars["current_best_validation_score"] = 0 2232 self.member_vars["initial_lr_test_epoch_count"] = -1 2233 2234 def set_dendrite_training(self): 2235 """Signal all layers to start dendrite training. 2236 2237 Parameters 2238 ---------- 2239 None 2240 2241 Returns 2242 ------- 2243 None 2244 This function does not return a value. 2245 """ 2246 if GPA.pc.get_verbose(): 2247 print("Calling set_dendrite_training") 2248 2249 for layer in self.neuron_module_vector[:]: 2250 worked = layer.set_mode("p") 2251 """ 2252 worked is False when a layer was added to the neuron module vector 2253 but then it's never actually been used. This can happen when 2254 you have set a layer to have requires_grad = False or when 2255 you have a module as a member variable but it's not actually 2256 part of the network. Should be moved to be a tracked layer 2257 rather than a neuron layer. 2258 """ 2259 if not worked: 2260 self.neuron_module_vector.remove(layer) 2261 2262 for layer in self.tracked_neuron_module_vector[:]: 2263 worked = layer.set_mode("p") 2264 2265 self.create_new_dendrite_module() 2266 self.member_vars["mode"] = "p" 2267 self.member_vars["current_n_learning_rate_initial_skip_steps"] = 0 2268 2269 if GPA.pc.get_learn_dendrites_live(): 2270 self.reset_vals_for_score_reset() 2271 2272 self.member_vars["last_max_learning_rate_steps"] = self.member_vars[ 2273 "current_step_count" 2274 ] 2275 2276 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"] = [] 2277 GPA.pai_tracker.member_vars["num_cycles"] += 1 2278 2279 2280 def set_neuron_training(self): 2281 """Signal all layers to start neuron training. 2282 2283 Parameters 2284 ---------- 2285 None 2286 2287 Returns 2288 ------- 2289 None 2290 This function does not return a value. 2291 """ 2292 for module in self.neuron_module_vector: 2293 module.set_mode("n") 2294 for module in self.tracked_neuron_module_vector[:]: 2295 module.set_mode("n") 2296 2297 self.member_vars["mode"] = "n" 2298 self.member_vars["num_dendrites_added"] += 1 2299 self.member_vars["current_n_learning_rate_initial_skip_steps"] = 0 2300 self.reset_vals_for_score_reset() 2301 2302 self.member_vars["current_cycle_lr_max_scores"] = [] 2303 if GPA.pc.get_learn_dendrites_live(): 2304 self.member_vars["last_max_learning_rate_steps"] = self.member_vars[ 2305 "current_step_count" 2306 ] 2307 GPA.pai_tracker.member_vars["num_cycles"] += 1 2308 2309 if GPA.pc.get_reset_best_score_on_switch(): 2310 GPA.pai_tracker.member_vars["current_best_validation_score"] = 0 2311 GPA.pai_tracker.member_vars["running_accuracy"] = 0 2312 2313 def start_epoch(self, internal_call=False): 2314 """Perform steps for when a new training epoch is about to begin. 2315 2316 Parameters 2317 ---------- 2318 internal_call : bool, optional 2319 Whether this is an internal call or manual call 2320 2321 Returns 2322 ------- 2323 None 2324 2325 Notes 2326 ----- 2327 If you ever need to call this manually, set internal_call to False. 2328 2329 """ 2330 if self.member_vars["manual_train_switch"] and internal_call: 2331 return 2332 2333 if not internal_call and not self.member_vars["manual_train_switch"]: 2334 self.member_vars["manual_train_switch"] = True 2335 self.saved_time = 0 2336 self.member_vars["num_epochs_run"] = -1 2337 self.member_vars["total_epochs_run"] = -1 2338 2339 end = time.time() 2340 if self.member_vars["manual_train_switch"]: 2341 if self.saved_time != 0: 2342 if self.member_vars["mode"] == "p": 2343 self.member_vars["p_val_times"].append(end - self.saved_time) 2344 else: 2345 self.member_vars["n_val_times"].append(end - self.saved_time) 2346 2347 if self.member_vars["mode"] == "p": 2348 for layer in self.neuron_module_vector: 2349 for m in range(0, GPA.pc.get_global_candidates()): 2350 with torch.no_grad(): 2351 if GPA.pc.get_verbose(): 2352 print(f"Resetting score for {layer.name}") 2353 # Snapshot best_score before reset so we can compute per-epoch improvement 2354 layer.dendrite_module.dendrite_values[ 2355 m 2356 ].epoch_start_best_score.copy_( 2357 layer.dendrite_module.dendrite_values[ 2358 m 2359 ].best_score.detach() 2360 ) 2361 layer.dendrite_module.dendrite_values[ 2362 m 2363 ].best_score_improved_this_epoch = ( 2364 layer.dendrite_module.dendrite_values[ 2365 m 2366 ].best_score_improved_this_epoch 2367 * 0 2368 ) 2369 layer.dendrite_module.dendrite_values[ 2370 m 2371 ].nodes_best_improved_this_epoch = ( 2372 layer.dendrite_module.dendrite_values[ 2373 m 2374 ].nodes_best_improved_this_epoch 2375 * 0 2376 ) 2377 layer.dendrite_module.dendrite_values[ 2378 m 2379 ].nodes_improved_any = ( 2380 layer.dendrite_module.dendrite_values[ 2381 m 2382 ].nodes_improved_any 2383 * 0 2384 ) 2385 if GPA.pc.get_perforated_backpropagation(): 2386 self.member_vars["best_mean_score_improved_this_epoch"] = 0 2387 self.member_vars["num_epochs_run"] += 1 2388 self.member_vars["total_epochs_run"] = ( 2389 self.member_vars["num_epochs_run"] + self.member_vars["overwritten_epochs"] 2390 ) 2391 self.saved_time = end 2392 2393 def stop_epoch(self, internal_call=False): 2394 """Perform steps when a training epoch has completed. 2395 2396 Parameters 2397 ---------- 2398 internal_call : bool, optional 2399 Whether this is an internal call or manual call 2400 2401 Returns 2402 ------- 2403 None 2404 2405 Notes 2406 ----- 2407 If you ever need to call this manually, set internal_call to False. 2408 2409 """ 2410 end = time.time() 2411 if self.member_vars["manual_train_switch"] and internal_call: 2412 return 2413 2414 if self.member_vars["manual_train_switch"]: 2415 if self.member_vars["mode"] == "p": 2416 self.member_vars["p_train_times"].append(end - self.saved_time) 2417 else: 2418 self.member_vars["n_train_times"].append(end - self.saved_time) 2419 else: 2420 if self.member_vars["mode"] == "p": 2421 self.member_vars["p_epoch_times"].append(end - self.saved_time) 2422 else: 2423 self.member_vars["n_epoch_times"].append(end - self.saved_time) 2424 2425 self.saved_time = end 2426 2427 def initialize( 2428 self, 2429 model, 2430 doing_pai=True, 2431 save_name="PAI", 2432 making_graphs=True, 2433 maximizing_score=True, 2434 num_classes=10000, 2435 values_per_train_epoch=-1, 2436 values_per_val_epoch=-1, 2437 zooming_graph=True, 2438 ): 2439 """Setup the tracker with initial settings. 2440 2441 2442 Parameters 2443 ---------- 2444 model : object 2445 The neural network model. 2446 doing_pai : bool, optional 2447 Whether to add dendrites, by default True. 2448 save_name : str, optional 2449 The name under which to save the model. 2450 making_graphs : bool, optional 2451 Whether to make graphs, by default True. 2452 maximizing_score : bool, optional 2453 Whether to maximize the score, by default True. 2454 num_classes : int, optional 2455 The number of classes in the dataset, unused 2456 values_per_train_epoch : int, optional 2457 The number of values to look back for graphing 2458 during training, by default -1 (all values). 2459 values_per_val_epoch : int, optional 2460 The number of values to look back for graphing 2461 during validation, by default -1 (all values). 2462 zooming_graph : bool, optional 2463 Whether to zoom on graphs, by default True. 2464 2465 2466 Returns 2467 ------- 2468 nn.Module 2469 Converted model instance configured for the tracker settings. 2470 """ 2471 model = UPA.convert_network(model) 2472 self.member_vars["doing_pai"] = doing_pai 2473 self.member_vars["maximizing_score"] = maximizing_score 2474 self.save_name = save_name 2475 self.zooming_graph = zooming_graph 2476 self.making_graphs = making_graphs 2477 2478 if not self.loaded: 2479 self.member_vars["running_accuracy"] = (1.0 / num_classes) * 100 2480 2481 self.values_per_train_epoch = values_per_train_epoch 2482 self.values_per_val_epoch = values_per_val_epoch 2483 2484 if GPA.pc.get_testing_dendrite_capacity(): 2485 if not GPA.pc.get_silent(): 2486 print("Running a test of Dendrite Capacity.") 2487 GPA.pc.set_switch_mode(GPA.pc.DOING_SWITCH_EVERY_TIME) 2488 self.member_vars["switch_mode"] = GPA.pc.get_switch_mode() 2489 GPA.pc.set_retain_all_dendrites(True) 2490 GPA.pc.set_max_dendrite_tries(1000) 2491 GPA.pc.set_max_dendrites(1000) 2492 if GPA.pc.get_perforated_backpropagation(): 2493 GPA.pc.set_initial_correlation_batches(1) 2494 else: 2495 if not GPA.pc.get_silent(): 2496 print("Running Dendrite Experiment") 2497 return model 2498 2499 def generate_accuracy_plots(self, ax, save_folder, extra_string): 2500 """ 2501 Generate plots and csvs for accuracy 2502 2503 Parameters 2504 ---------- 2505 ax : object 2506 The matplotlib axis to plot on. 2507 save_folder : str 2508 The folder to save the plots and csvs in. 2509 extra_string : str 2510 An extra string to append to the filenames. 2511 2512 Returns 2513 ------- 2514 None 2515 2516 """ 2517 2518 # If scores are being saved for epochs that get overwritten, plot them 2519 for list_id in range(len(self.member_vars["overwritten_extras"])): 2520 for extra_id in self.member_vars["overwritten_extras"][list_id]: 2521 ax.plot( 2522 np.arange( 2523 len(self.member_vars["overwritten_extras"][list_id][extra_id]) 2524 ), 2525 self.member_vars["overwritten_extras"][list_id][extra_id], 2526 color=_PAI_PALETTE["train"], 2527 alpha=0.35, 2528 ) 2529 ax.plot( 2530 np.arange(len(self.member_vars["overwritten_vals"][list_id])), 2531 self.member_vars["overwritten_vals"][list_id], 2532 color=_PAI_PALETTE["validation"], 2533 alpha=0.35, 2534 ) 2535 2536 # Determine which accuracy vector to use 2537 if GPA.pc.get_drawing_pai(): 2538 accuracies = self.member_vars["accuracies"] 2539 else: 2540 accuracies = self.member_vars["n_accuracies"] 2541 2542 # Get pointer to additional scores being saved 2543 extra_scores = self.member_vars["extra_scores"] 2544 2545 # Plot the main accuracy scores 2546 ax.plot( 2547 np.arange(len(accuracies)), 2548 accuracies, 2549 label="Validation Scores", 2550 color=_PAI_PALETTE["validation"], 2551 ) 2552 ax.plot( 2553 np.arange(len(self.member_vars["running_accuracies"])), 2554 self.member_vars["running_accuracies"], 2555 label="Validation Running Scores", 2556 color=_PAI_PALETTE["validation"], 2557 alpha=0.4, 2558 ) 2559 2560 # Plot additional scores 2561 for extra_index, extra_score in enumerate(extra_scores): 2562 ax.plot( 2563 np.arange(len(extra_scores[extra_score])), 2564 extra_scores[extra_score], 2565 label=extra_score, 2566 color=_pai_extra_score_color(extra_score, extra_index), 2567 ) 2568 2569 plt.title(save_folder + "/" + self.save_name + "Scores") 2570 plt.xlabel("Epochs") 2571 plt.ylabel("Score") 2572 2573 # Add point at epoch last improved and best validation score 2574 if GPA.pc.get_drawing_pai(): 2575 ax.plot( 2576 self.member_vars["epoch_last_improved"], 2577 self.member_vars["global_best_validation_score"], 2578 "o", 2579 color=_PAI_PALETTE["marker_best"], 2580 label="Global best (y)", 2581 ) 2582 ax.plot( 2583 self.member_vars["epoch_last_improved"], 2584 accuracies[self.member_vars["epoch_last_improved"]], 2585 "o", 2586 color=_PAI_PALETTE["marker_improved"], 2587 label="Epoch Last Improved", 2588 ) 2589 else: 2590 if self.member_vars["mode"] == "n": 2591 missed_time = ( 2592 self.member_vars["num_epochs_run"] 2593 - self.member_vars["epoch_last_improved"] 2594 ) 2595 ax.plot( 2596 (len(self.member_vars["n_accuracies"]) - 1) - missed_time, 2597 self.member_vars["n_accuracies"][-(missed_time + 1)], 2598 "o", 2599 color=_PAI_PALETTE["marker_improved"], 2600 label="Epoch Last Improved", 2601 ) 2602 2603 # Generate csv file for the values graphed 2604 pd1 = pd.DataFrame( 2605 {"Epochs": np.arange(len(accuracies)), "Validation Scores": accuracies} 2606 ) 2607 pd2 = pd.DataFrame( 2608 { 2609 "Epochs": np.arange(len(self.member_vars["running_accuracies"])), 2610 "Validation Running Scores": self.member_vars["running_accuracies"], 2611 } 2612 ) 2613 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2614 for extra_score in extra_scores: 2615 pd2 = pd.DataFrame( 2616 { 2617 "Epochs": np.arange(len(extra_scores[extra_score])), 2618 extra_score: extra_scores[extra_score], 2619 } 2620 ) 2621 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2622 extra_scores_without_graphing = self.member_vars[ 2623 "extra_scores_without_graphing" 2624 ] 2625 for extra_score in extra_scores_without_graphing: 2626 pd2 = pd.DataFrame( 2627 { 2628 "Epochs": np.arange( 2629 len(extra_scores_without_graphing[extra_score]) 2630 ), 2631 extra_score: extra_scores_without_graphing[extra_score], 2632 } 2633 ) 2634 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2635 pd1.to_csv( 2636 save_folder + "/" + self.save_name + extra_string + "Scores.csv", 2637 index=False, 2638 ) 2639 del pd1, pd2 2640 2641 # Set y min and max to zoom in on important part of axis 2642 if ( 2643 len(self.member_vars["switch_epochs"]) > 0 2644 and self.member_vars["switch_epochs"][0] > 0 2645 and self.zooming_graph 2646 ): 2647 if GPA.pai_tracker.member_vars["maximizing_score"]: 2648 min_val = np.array( 2649 accuracies[0 : self.member_vars["switch_epochs"][0]] 2650 ).mean() 2651 for extra_score in extra_scores: 2652 min_pot = np.array( 2653 extra_scores[extra_score][ 2654 0 : self.member_vars["switch_epochs"][0] 2655 ] 2656 ).mean() 2657 if min_pot < min_val: 2658 min_val = min_pot 2659 ax.set_ylim(ymin=min_val) 2660 else: 2661 max_val = np.array( 2662 accuracies[0 : self.member_vars["switch_epochs"][0]] 2663 ).mean() 2664 for extra_score in extra_scores: 2665 max_pot = np.array( 2666 extra_scores[extra_score][ 2667 0 : self.member_vars["switch_epochs"][0] 2668 ] 2669 ).mean() 2670 if max_pot > max_val: 2671 max_val = max_pot 2672 ax.set_ylim(ymax=max_val) 2673 2674 ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left") 2675 _pai_grid(ax) 2676 2677 # Draw vertical lines for epochs where a dendrite switch occurred. 2678 # Colour by the phase being entered: dendrite/PAI phase -> teal, 2679 # normal phase -> orange, alternating starting with dendrite. 2680 if GPA.pc.get_drawing_pai() and self.member_vars["doing_pai"]: 2681 for i, switcher in enumerate(self.member_vars["switch_epochs"]): 2682 # Mode always starts at "n" and switch_epochs alternates 2683 # n->p->n->p..., so the first switch enters the dendrite 2684 # phase and every other one alternates from there. 2685 entering_p = (i % 2) == 0 2686 plt.axvline( 2687 x=switcher, 2688 ymin=0, 2689 ymax=1, 2690 color=_PAI_PALETTE["dendrite"] 2691 if entering_p 2692 else _PAI_PALETTE["normal"], 2693 ) 2694 else: 2695 for switcher in self.member_vars["n_switch_epochs"]: 2696 plt.axvline( 2697 x=switcher, ymin=0, ymax=1, color=_PAI_PALETTE["normal"] 2698 ) 2699 2700 def generate_time_plots(self, ax, save_folder, extra_string): 2701 """ 2702 Generate plots and csvs for timing 2703 2704 Parameters 2705 ---------- 2706 ax : object 2707 The matplotlib axis to plot on. 2708 save_folder : str 2709 The folder to save the plots and csvs in. 2710 extra_string : str 2711 An extra string to append to the filenames. 2712 2713 Returns 2714 ------- 2715 None 2716 2717 """ 2718 if self.member_vars["manual_train_switch"]: 2719 ax.plot( 2720 np.arange(len(self.member_vars["n_train_times"])), 2721 self.member_vars["n_train_times"], 2722 label="Normal Epoch Train Times", 2723 color=_PAI_PALETTE["normal"], 2724 ) 2725 ax.plot( 2726 np.arange(len(self.member_vars["p_train_times"])), 2727 self.member_vars["p_train_times"], 2728 label="PAI Epoch Train Times", 2729 color=_PAI_PALETTE["dendrite"], 2730 ) 2731 ax.plot( 2732 np.arange(len(self.member_vars["n_val_times"])), 2733 self.member_vars["n_val_times"], 2734 label="Normal Epoch Val Times", 2735 color=_PAI_PALETTE["normal"], 2736 alpha=0.45, 2737 ) 2738 ax.plot( 2739 np.arange(len(self.member_vars["p_val_times"])), 2740 self.member_vars["p_val_times"], 2741 label="PAI Epoch Val Times", 2742 color=_PAI_PALETTE["dendrite"], 2743 alpha=0.45, 2744 ) 2745 2746 plt.title( 2747 save_folder + "/" + self.save_name + "times (by train() and eval())" 2748 ) 2749 plt.xlabel("Iteration") 2750 plt.ylabel("Epoch Time in Seconds ") 2751 ax.set_ylim(ymin=0) 2752 ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left") 2753 _pai_grid(ax) 2754 2755 pd1 = pd.DataFrame( 2756 { 2757 "Epochs": np.arange(len(self.member_vars["n_train_times"])), 2758 "Normal Epoch Train Times": self.member_vars["n_train_times"], 2759 } 2760 ) 2761 pd2 = pd.DataFrame( 2762 { 2763 "Epochs": np.arange(len(self.member_vars["p_train_times"])), 2764 "PAI Epoch Train Times": self.member_vars["p_train_times"], 2765 } 2766 ) 2767 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2768 2769 pd2 = pd.DataFrame( 2770 { 2771 "Epochs": np.arange(len(self.member_vars["n_val_times"])), 2772 "Normal Epoch Val Times": self.member_vars["n_val_times"], 2773 } 2774 ) 2775 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2776 2777 pd2 = pd.DataFrame( 2778 { 2779 "Epochs": np.arange(len(self.member_vars["p_val_times"])), 2780 "PAI Epoch Val Times": self.member_vars["p_val_times"], 2781 } 2782 ) 2783 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2784 2785 pd1.to_csv( 2786 save_folder + "/" + self.save_name + extra_string + "Times.csv", 2787 index=False, 2788 ) 2789 del pd1, pd2 2790 else: 2791 ax.plot( 2792 np.arange(len(self.member_vars["n_epoch_times"])), 2793 self.member_vars["n_epoch_times"], 2794 label="Normal Epoch Times", 2795 color=_PAI_PALETTE["normal"], 2796 ) 2797 ax.plot( 2798 np.arange(len(self.member_vars["p_epoch_times"])), 2799 self.member_vars["p_epoch_times"], 2800 label="PAI Epoch Times", 2801 color=_PAI_PALETTE["dendrite"], 2802 ) 2803 2804 plt.title( 2805 save_folder + "/" + self.save_name + "times (by train() and eval())" 2806 ) 2807 plt.xlabel("Iteration") 2808 plt.ylabel("Epoch Time in Seconds ") 2809 ax.set_ylim(ymin=0) 2810 ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left") 2811 _pai_grid(ax) 2812 2813 pd1 = pd.DataFrame( 2814 { 2815 "Epochs": np.arange(len(self.member_vars["n_epoch_times"])), 2816 "Normal Epoch Times": self.member_vars["n_epoch_times"], 2817 } 2818 ) 2819 pd2 = pd.DataFrame( 2820 { 2821 "Epochs": np.arange(len(self.member_vars["p_epoch_times"])), 2822 "PAI Epoch Times": self.member_vars["p_epoch_times"], 2823 } 2824 ) 2825 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2826 2827 pd1.to_csv( 2828 save_folder + "/" + self.save_name + extra_string + "Times.csv", 2829 index=False, 2830 ) 2831 del pd1, pd2 2832 2833 if self.values_per_train_epoch != -1 and self.values_per_val_epoch != -1: 2834 ax2 = ax.twinx() # Second axes sharing same x-axis 2835 ax2.set_ylabel("Single Datapoint Time in Seconds") 2836 2837 ax2.plot( 2838 np.arange(len(self.member_vars["n_train_times"])), 2839 np.array(self.member_vars["n_train_times"]) 2840 / self.values_per_train_epoch, 2841 linestyle="dashed", 2842 label="Normal Train Item Times", 2843 color=_PAI_PALETTE["normal"], 2844 ) 2845 ax2.plot( 2846 np.arange(len(self.member_vars["p_train_times"])), 2847 np.array(self.member_vars["p_train_times"]) 2848 / self.values_per_train_epoch, 2849 linestyle="dashed", 2850 label="PAI Train Item Times", 2851 color=_PAI_PALETTE["dendrite"], 2852 ) 2853 ax2.plot( 2854 np.arange(len(self.member_vars["n_val_times"])), 2855 np.array(self.member_vars["n_val_times"]) / self.values_per_val_epoch, 2856 linestyle="dashed", 2857 label="Normal Val Item Times", 2858 color=_PAI_PALETTE["normal"], 2859 alpha=0.45, 2860 ) 2861 ax2.plot( 2862 np.arange(len(self.member_vars["p_val_times"])), 2863 np.array(self.member_vars["p_val_times"]) / self.values_per_val_epoch, 2864 linestyle="dashed", 2865 label="PAI Val Item Times", 2866 color=_PAI_PALETTE["dendrite"], 2867 alpha=0.45, 2868 ) 2869 ax2.tick_params(axis="y") 2870 ax2.set_ylim(ymin=0) 2871 ax2.legend(bbox_to_anchor=(1.05, 1), loc="upper left") 2872 2873 def generate_learning_rate_plots(self, ax, save_folder, extra_string): 2874 """ 2875 Generate plots and csvs for learning rate 2876 2877 Parameters 2878 ---------- 2879 ax : object 2880 The matplotlib axis to plot on. 2881 save_folder : str 2882 The folder to save the plots and csvs in. 2883 extra_string : str 2884 An extra string to append to the filenames. 2885 2886 Returns 2887 ------- 2888 None 2889 2890 """ 2891 ax.plot( 2892 np.arange(len(self.member_vars["training_learning_rates"])), 2893 self.member_vars["training_learning_rates"], 2894 label="learning_rate", 2895 color=_PAI_PALETTE["learning_rate"], 2896 ) 2897 plt.title(save_folder + "/" + self.save_name + "learning_rate") 2898 plt.xlabel("Epochs") 2899 plt.ylabel("learning_rate") 2900 ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left") 2901 _pai_grid(ax) 2902 2903 pd1 = pd.DataFrame( 2904 { 2905 "Epochs": np.arange(len(self.member_vars["training_learning_rates"])), 2906 "learning_rate": self.member_vars["training_learning_rates"], 2907 } 2908 ) 2909 pd1.to_csv( 2910 save_folder + "/" + self.save_name + extra_string + "learning_rate.csv", 2911 index=False, 2912 ) 2913 del pd1 2914 2915 def get_current_pb_scores(self): 2916 """ 2917 Get the latest best PBScore of each dendrite layer, the same numbers 2918 written to the Best PBScores csv. 2919 2920 Returns 2921 ------- 2922 dict 2923 Layer name to score. Empty outside of dendrite scoring phases, 2924 when no candidate dendrites are being scored. 2925 2926 """ 2927 if not self.member_vars["doing_pai"]: 2928 return {} 2929 if not GPA.pc.get_perforated_backpropagation(): 2930 return {} 2931 # Scores only advance while candidate dendrites are being trained 2932 if ( 2933 self.member_vars["mode"] != "p" 2934 and not GPA.pc.get_learn_dendrites_live() 2935 ): 2936 return {} 2937 2938 scores = {} 2939 for layer_id in range(len(self.neuron_module_vector)): 2940 if layer_id >= len(self.member_vars["best_scores"]): 2941 continue 2942 layer_scores = self.member_vars["best_scores"][layer_id] 2943 if len(layer_scores) == 0: 2944 continue 2945 score = layer_scores[-1] 2946 if hasattr(score, "item"): 2947 score = score.item() 2948 score = float(score) 2949 if math.isnan(score) or math.isinf(score): 2950 continue 2951 scores[self.neuron_module_vector[layer_id].name] = score 2952 return scores 2953 2954 def get_current_pb_scores_current(self): 2955 """ 2956 Like :meth:`get_current_pb_scores` but returns the *current* (not best) 2957 PBScore of each dendrite layer - ``current_scores[layer][-1]`` - the 2958 dashed series in ``generate_dendrite_learning_plots``. 2959 2960 Returns 2961 ------- 2962 dict 2963 Layer name to score. Empty outside of dendrite scoring phases. 2964 """ 2965 if not self.member_vars["doing_pai"]: 2966 return {} 2967 if not GPA.pc.get_perforated_backpropagation(): 2968 return {} 2969 if ( 2970 self.member_vars["mode"] != "p" 2971 and not GPA.pc.get_learn_dendrites_live() 2972 ): 2973 return {} 2974 2975 scores = {} 2976 for layer_id in range(len(self.neuron_module_vector)): 2977 if layer_id >= len(self.member_vars["current_scores"]): 2978 continue 2979 layer_scores = self.member_vars["current_scores"][layer_id] 2980 if len(layer_scores) == 0: 2981 continue 2982 score = layer_scores[-1] 2983 if hasattr(score, "item"): 2984 score = score.item() 2985 score = float(score) 2986 if math.isnan(score) or math.isinf(score): 2987 continue 2988 scores[self.neuron_module_vector[layer_id].name] = score 2989 return scores 2990 2991 def generate_dendrite_learning_plots(self, ax, save_folder, extra_string): 2992 """ 2993 Generate dendrite score plots for the tracker. 2994 Also saves csv files associated with the plots. 2995 2996 Parameters 2997 ---------- 2998 ax : matplotlib.axes.Axes 2999 Axis used for plotting dendrite-learning curves. 3000 save_folder : str 3001 Directory where plot images and CSV summaries are written. 3002 extra_string : str 3003 Filename suffix used to distinguish this output set. 3004 3005 Returns 3006 ------- 3007 None 3008 Saves plots and score CSV files to disk. 3009 """ 3010 if self.member_vars["doing_pai"]: 3011 pd1 = None 3012 pd2 = None 3013 num_colors = len(self.neuron_module_vector) 3014 3015 layer_colors = [ 3016 _PAI_DENDRITE_CYCLE[i % len(_PAI_DENDRITE_CYCLE)] 3017 for i in range(num_colors) 3018 ] 3019 3020 for layer_id in range(len(self.neuron_module_vector)): 3021 color = layer_colors[layer_id] 3022 ax.plot( 3023 np.arange(len(self.member_vars["best_scores"][layer_id])), 3024 self.member_vars["best_scores"][layer_id], 3025 label=self.neuron_module_vector[layer_id].name, 3026 color=color, 3027 ) 3028 3029 pd2 = pd.DataFrame( 3030 { 3031 "Epochs": np.arange( 3032 len(self.member_vars["best_scores"][layer_id]) 3033 ), 3034 f"Best ever for all nodes Layer {self.neuron_module_vector[layer_id].name}": self.member_vars[ 3035 "best_scores" 3036 ][ 3037 layer_id 3038 ], 3039 } 3040 ) 3041 3042 if pd1 is None: 3043 pd1 = pd2 3044 else: 3045 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 3046 3047 if len(self.member_vars["current_scores"][layer_id]) != 0: 3048 ax.plot( 3049 np.arange(len(self.member_vars["current_scores"][layer_id])), 3050 self.member_vars["current_scores"][layer_id], 3051 label=f"Current:{self.neuron_module_vector[layer_id].name}", 3052 color=color, 3053 linestyle="--", 3054 ) 3055 3056 pd2 = pd.DataFrame( 3057 { 3058 "Epochs": np.arange( 3059 len(self.member_vars["current_scores"][layer_id]) 3060 ), 3061 f"Best current for all nodes Layer {self.neuron_module_vector[layer_id].name}": self.member_vars[ 3062 "current_scores" 3063 ][ 3064 layer_id 3065 ], 3066 } 3067 ) 3068 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 3069 3070 plt.title(save_folder + "/" + self.save_name + " Best PBScores") 3071 plt.xlabel("Epochs") 3072 plt.ylabel("Best PBScore") 3073 ax.legend( 3074 bbox_to_anchor=(1.05, 1), 3075 loc="upper left", 3076 ncol=max(1, math.ceil(len(self.neuron_module_vector) / 30)), 3077 ) 3078 _pai_grid(ax) 3079 for switcher in self.member_vars["p_switch_epochs"]: 3080 plt.axvline( 3081 x=switcher, ymin=0, ymax=1, color=_PAI_PALETTE["dendrite"] 3082 ) 3083 3084 if self.member_vars["mode"] == "p": 3085 missed_time = ( 3086 self.member_vars["num_epochs_run"] 3087 - self.member_vars["epoch_last_improved"] 3088 ) 3089 plt.axvline( 3090 x=(len(self.member_vars["best_scores"][0]) - (missed_time + 1)), 3091 ymin=0, 3092 ymax=1, 3093 color=_PAI_PALETTE["normal"], 3094 ) 3095 3096 # pd1 here will be none if no PB layers are created 3097 if pd1 is not None: 3098 pd1.to_csv( 3099 save_folder 3100 + "/" 3101 + self.save_name 3102 + extra_string 3103 + "Best PBScores.csv", 3104 index=False, 3105 ) 3106 del pd1, pd2 3107 3108 def generate_extra_csv_files(self, save_folder, extra_string): 3109 """ 3110 Generate additional csvs 3111 3112 Parameters 3113 ---------- 3114 save_folder : str 3115 The folder to save the plots and csvs in. 3116 extra_string : str 3117 An extra string to append to the filenames. 3118 3119 Returns 3120 ------- 3121 None 3122 3123 """ 3124 pd1 = pd.DataFrame( 3125 { 3126 "Switch Number": np.arange(len(self.member_vars["switch_epochs"])), 3127 "Switch Epoch": self.member_vars["switch_epochs"], 3128 } 3129 ) 3130 pd1.to_csv( 3131 save_folder + "/" + self.save_name + extra_string + "switch_epochs.csv", 3132 index=False, 3133 ) 3134 del pd1 3135 3136 pd1 = pd.DataFrame( 3137 { 3138 "Switch Number": np.arange(len(self.member_vars["param_counts"])), 3139 "Param Count": self.member_vars["param_counts"], 3140 } 3141 ) 3142 pd1.to_csv( 3143 save_folder + "/" + self.save_name + extra_string + "param_counts.csv", 3144 index=False, 3145 ) 3146 del pd1 3147 3148 """ 3149 Create best_arch_scores.csv file 3150 When working with dendrites there is a tradeoff between additional param count and score improvement. 3151 This file will help track that tradeoff by recording the best scores for all extra_scores 3152 and extra_scores_without_graphing for each architecture version. 3153 The scores recorded here are from the epoch when the best validation score was found 3154 within each switch_epoch boundary. 3155 """ 3156 switch_counts = len(self.member_vars["switch_epochs"]) 3157 best_valid = [] 3158 associated_params = [] 3159 3160 # Initialize dictionaries to store best scores for each extra score type 3161 best_extra_scores = {} 3162 for score_name in self.member_vars["extra_scores"]: 3163 best_extra_scores[score_name] = [] 3164 for score_name in self.member_vars["extra_scores_without_graphing"]: 3165 best_extra_scores[score_name] = [] 3166 3167 for switch in range(0, switch_counts, 2): 3168 start_index = 0 3169 if switch != 0: 3170 start_index = self.member_vars["switch_epochs"][switch - 1] + 1 3171 end_index = self.member_vars["switch_epochs"][switch] + 1 3172 3173 if GPA.pai_tracker.member_vars["maximizing_score"]: 3174 best_valid_index = start_index + np.argmax( 3175 self.member_vars["accuracies"][start_index:end_index] 3176 ) 3177 else: 3178 best_valid_index = start_index + np.argmin( 3179 self.member_vars["accuracies"][start_index:end_index] 3180 ) 3181 3182 best_valid_score = self.member_vars["accuracies"][best_valid_index] 3183 best_valid.append(best_valid_score) 3184 3185 # Get corresponding scores from all extra_scores 3186 for score_name in self.member_vars["extra_scores"]: 3187 if best_valid_index < len(self.member_vars["extra_scores"][score_name]): 3188 best_extra_scores[score_name].append( 3189 self.member_vars["extra_scores"][score_name][best_valid_index] 3190 ) 3191 else: 3192 best_extra_scores[score_name].append(None) 3193 3194 # Get corresponding scores from all extra_scores_without_graphing 3195 for score_name in self.member_vars["extra_scores_without_graphing"]: 3196 if best_valid_index < len(self.member_vars["extra_scores_without_graphing"][score_name]): 3197 best_extra_scores[score_name].append( 3198 self.member_vars["extra_scores_without_graphing"][score_name][best_valid_index] 3199 ) 3200 else: 3201 best_extra_scores[score_name].append(None) 3202 3203 if self.member_vars["doing_pai"]: 3204 associated_params.append(self.member_vars["param_counts"][switch]) 3205 else: 3206 associated_params.append(self.member_vars["param_counts"][-1]) 3207 3208 # If in neuron training mode but not the very first epoch 3209 if self.member_vars["mode"] == "n" and ( 3210 (len(self.member_vars["switch_epochs"]) == 0) 3211 or ( 3212 self.member_vars["switch_epochs"][-1] + 1 3213 != len(self.member_vars["accuracies"]) 3214 ) 3215 ): 3216 start_index = 0 3217 if len(self.member_vars["switch_epochs"]) != 0: 3218 start_index = self.member_vars["switch_epochs"][-1] + 1 3219 3220 if GPA.pai_tracker.member_vars["maximizing_score"]: 3221 best_valid_index = start_index + np.argmax( 3222 self.member_vars["accuracies"][start_index:] 3223 ) 3224 else: 3225 best_valid_index = start_index + np.argmin( 3226 self.member_vars["accuracies"][start_index:] 3227 ) 3228 3229 best_valid_score = self.member_vars["accuracies"][best_valid_index] 3230 best_valid.append(best_valid_score) 3231 3232 # Get corresponding scores from all extra_scores 3233 for score_name in self.member_vars["extra_scores"]: 3234 if best_valid_index < len(self.member_vars["extra_scores"][score_name]): 3235 best_extra_scores[score_name].append( 3236 self.member_vars["extra_scores"][score_name][best_valid_index] 3237 ) 3238 else: 3239 best_extra_scores[score_name].append(None) 3240 3241 # Get corresponding scores from all extra_scores_without_graphing 3242 for score_name in self.member_vars["extra_scores_without_graphing"]: 3243 if best_valid_index < len(self.member_vars["extra_scores_without_graphing"][score_name]): 3244 best_extra_scores[score_name].append( 3245 self.member_vars["extra_scores_without_graphing"][score_name][best_valid_index] 3246 ) 3247 else: 3248 best_extra_scores[score_name].append(None) 3249 3250 associated_params.append(self.member_vars["param_counts"][-1]) 3251 3252 # Build dataframe with all columns 3253 csv_data = { 3254 "Param Counts": associated_params, 3255 "Max Valid Scores": best_valid, 3256 } 3257 3258 # Add columns for each extra score 3259 for score_name in best_extra_scores: 3260 csv_data[score_name] = best_extra_scores[score_name] 3261 3262 pd1 = pd.DataFrame(csv_data) 3263 pd1.to_csv( 3264 save_folder + "/" + self.save_name + extra_string + "_best_arch_scores.csv", 3265 index=False, 3266 ) 3267 del pd1 3268 3269 def save_graphs(self, extra_string=""): 3270 """ 3271 Save graphs and csvs for all the values the tracker records 3272 3273 Parameters 3274 ---------- 3275 extra_string : str 3276 An extra string to append to the filenames. 3277 3278 Returns 3279 ------- 3280 None 3281 3282 """ 3283 # If running DDP only save with rank 0 3284 if "RANK" in os.environ: 3285 if int(os.environ["RANK"]) != 0: 3286 return 3287 if not self.making_graphs: 3288 return 3289 3290 save_folder = "./" + self.save_name + "/" 3291 3292 plt.ioff() 3293 fig = plt.figure(figsize=(28, 14)) 3294 3295 # Plot with accuracy scores 3296 ax = plt.subplot(221) 3297 self.generate_accuracy_plots(ax, save_folder, extra_string) 3298 3299 # Plot dendrite learning scores 3300 ax = plt.subplot(222) 3301 self.generate_dendrite_learning_plots(ax, save_folder, extra_string) 3302 3303 if GPA.pc.get_drawing_extra_graphs(): 3304 # Plot learning rates for each training epoch 3305 ax = plt.subplot(223) 3306 self.generate_learning_rate_plots(ax, save_folder, extra_string) 3307 3308 # Plot the times for each training epoch 3309 ax = plt.subplot(224) 3310 self.generate_time_plots(ax, save_folder, extra_string) 3311 3312 # Generate extra CSV files 3313 self.generate_extra_csv_files(save_folder, extra_string) 3314 3315 fig.tight_layout() 3316 plt.savefig(save_folder + "/" + self.save_name + extra_string + ".png") 3317 plt.close("all") 3318 3319 def add_loss(self, loss): 3320 """Add loss to tracking vectors. 3321 3322 Parameters 3323 ---------- 3324 loss : float or int 3325 The loss value to add. 3326 3327 Returns 3328 ------- 3329 None 3330 3331 """ 3332 if not isinstance(loss, (float, int)): 3333 loss = loss.item() 3334 self.member_vars["training_loss"].append(loss) 3335 3336 def add_learning_rate(self, learning_rate): 3337 """Add learning rate to tracking vectors. 3338 3339 Parameters 3340 ---------- 3341 learning_rate : float or int 3342 The learning rate value to add. 3343 3344 Returns 3345 ------- 3346 None 3347 3348 """ 3349 if not isinstance(learning_rate, (float, int)): 3350 learning_rate = learning_rate.item() 3351 self.member_vars["training_learning_rates"].append(learning_rate) 3352 3353 def add_extra_score(self, score, extra_score_name): 3354 """Add extra score to tracking vectors. 3355 3356 Parameters 3357 ---------- 3358 score : float or int 3359 The score value to add. 3360 3361 extra_score_name : str 3362 The name of the extra score. 3363 3364 Returns 3365 ------- 3366 None 3367 3368 """ 3369 if not isinstance(score, (float, int)): 3370 try: 3371 score = score.item() 3372 except: 3373 print( 3374 "Scores added for Perforated Backpropagation should be " 3375 "float, int, or tensor, yours is a:" 3376 ) 3377 print(type(score)) 3378 pdb.set_trace() 3379 3380 if GPA.pc.get_verbose(): 3381 print(f"Adding extra score {extra_score_name} of {float(score)}") 3382 3383 if extra_score_name not in self.member_vars["extra_scores"]: 3384 self.member_vars["extra_scores"][extra_score_name] = [] 3385 self.member_vars["extra_scores"][extra_score_name].append(score) 3386 3387 if self.member_vars["mode"] == "n": 3388 if extra_score_name not in self.member_vars["n_extra_scores"]: 3389 self.member_vars["n_extra_scores"][extra_score_name] = [] 3390 self.member_vars["n_extra_scores"][extra_score_name].append(score) 3391 3392 def add_extra_score_without_graphing(self, score, extra_score_name): 3393 """Add extra score without graphing to tracking vectors. 3394 3395 Parameters 3396 ---------- 3397 score : float or int 3398 The score value to add. 3399 3400 extra_score_name : str 3401 The name of the extra score. 3402 3403 Returns 3404 ------- 3405 None 3406 3407 """ 3408 if not isinstance(score, (float, int)): 3409 try: 3410 score = score.item() 3411 except: 3412 print( 3413 "Scores added for Perforated Backpropagation should be " 3414 "float, int, or tensor, yours is a:" 3415 ) 3416 print(type(score)) 3417 print("in add_extra_score_without_graphing") 3418 pdb.set_trace() 3419 3420 if GPA.pc.get_verbose(): 3421 print(f"Adding extra score {extra_score_name} of {float(score)}") 3422 3423 if extra_score_name not in self.member_vars["extra_scores_without_graphing"]: 3424 self.member_vars["extra_scores_without_graphing"][extra_score_name] = [] 3425 self.member_vars["extra_scores_without_graphing"][extra_score_name].append( 3426 score 3427 ) 3428 3429 def add_test_score(self, score, extra_score_name): 3430 """Add test score to tracking vectors. 3431 3432 Parameters 3433 ---------- 3434 score : float or int 3435 The score value to add. 3436 3437 extra_score_name : str 3438 The name of the extra score. 3439 3440 Returns 3441 ------- 3442 None 3443 3444 Notes 3445 ----- 3446 This function is a wrapper around `add_extra_score` that separates 3447 test score for adding to best_arch_scores.csv. 3448 3449 """ 3450 self.add_extra_score(score, extra_score_name) 3451 3452 if not isinstance(score, (float, int)): 3453 try: 3454 score = score.item() 3455 except: 3456 print( 3457 "Scores added for Perforated Backpropagation should be " 3458 "float, int, or tensor, yours is a:" 3459 ) 3460 print(type(score)) 3461 print("in add_test_score") 3462 pdb.set_trace() 3463 3464 if GPA.pc.get_verbose(): 3465 print(f"Adding test score {extra_score_name} of {float(score)}") 3466 self.member_vars["test_scores"].append(score) 3467 3468 def add_validation_score(self, accuracy, net, force_switch=False): 3469 """Function to add the validation score. 3470 3471 This is complex because it determines neuron and dendrite switching. 3472 3473 Parameters 3474 ---------- 3475 accuracy : float or int 3476 The accuracy or loss value to add. 3477 net : object 3478 The neural network model. 3479 force_switch : bool, optional 3480 Whether to force a switch, by default False. 3481 3482 Returns 3483 ------- 3484 net : object 3485 The potentially modified neural network model. 3486 training_complete : bool 3487 Whether training is complete. 3488 restructured : bool 3489 Whether the model has been restructured. 3490 3491 Notes 3492 ----- 3493 WARNING: Do not call self anywhere in this function. When systems 3494 get loaded the actual tracker you are working with can change. 3495 """ 3496 3497 _pai_log("info", f"Adding validation score {accuracy:.8f}") 3498 3499 update_learning_rate() 3500 update_param_count(net) 3501 3502 accuracy = check_input_problems(net, accuracy) 3503 3504 if len(GPA.pai_tracker.member_vars["switch_epochs"]) == 0: 3505 epochs_since_cycle_switch = GPA.pai_tracker.member_vars["num_epochs_run"] 3506 else: 3507 epochs_since_cycle_switch = ( 3508 GPA.pai_tracker.member_vars["num_epochs_run"] 3509 - GPA.pai_tracker.member_vars["switch_epochs"][-1] 3510 ) 3511 3512 update_running_accuracy(accuracy, epochs_since_cycle_switch) 3513 if GPA.pc.get_perforated_backpropagation(): 3514 TPB.update_pb_scores(self) 3515 3516 # Captured before any switch below flips the mode and reloads scores. 3517 # epoch_phase is sampled here too so the emitted epoch event's phase and 3518 # its pb_scores always describe the same side of a switch boundary. 3519 epoch_phase = GPA.pai_tracker.member_vars["mode"] 3520 epoch_pb_scores = self.get_current_pb_scores() 3521 epoch_pb_scores_current = self.get_current_pb_scores_current() 3522 3523 GPA.pai_tracker.stop_epoch(internal_call=True) 3524 3525 # If it is neuron training mode 3526 if ( 3527 GPA.pai_tracker.member_vars["mode"] == "n" 3528 or GPA.pc.get_learn_dendrites_live() 3529 ): 3530 check_new_best(net, accuracy, epochs_since_cycle_switch) 3531 elif GPA.pc.get_perforated_backpropagation(): 3532 TPB.check_best_pai_score_improvement() 3533 3534 # Save the latest model 3535 if GPA.pc.get_test_saves(): 3536 UPA.save_system(net, GPA.pc.get_save_name(), "latest") 3537 if GPA.pc.get_pai_saves(): 3538 UPA.pai_save_system(net, GPA.pc.get_save_name(), "latest") 3539 3540 restructuring_status_value = NO_MODEL_UPDATE 3541 # If it is time to switch based on scores and counter or a manual switch 3542 if GPA.pai_tracker.switch_time() or force_switch: 3543 # If testing dendrite capacity switch after enough dendrites added 3544 if ( 3545 (GPA.pai_tracker.member_vars["mode"] == "n") 3546 and (GPA.pai_tracker.member_vars["num_dendrites_added"] > 2) 3547 and GPA.pc.get_testing_dendrite_capacity() 3548 ): 3549 GPA.pai_tracker.save_graphs() 3550 _pai_log( 3551 "info", 3552 "Successfully added 3 dendrites with GPA.pc.set_testing_dendrite_capacity(True) (default). " 3553 "You may now set that to False and run a real experiment.", 3554 ) 3555 return net, False, True 3556 3557 # If doing neuron training but this dendrite count didn't improve 3558 if ( 3559 (GPA.pai_tracker.member_vars["mode"] == "n") 3560 or GPA.pc.get_learn_dendrites_live() 3561 ) and (GPA.pai_tracker.member_vars["current_n_set_global_best"] is False): 3562 new_restructuring_status_value, net = process_no_improvement(net) 3563 # if this was the final try return that training is complete 3564 if new_restructuring_status_value == TRAINING_COMPLETE: 3565 if _dashboard_emitter is not None: 3566 _dashboard_emitter.emit_run_end( 3567 GPA.pc, 3568 epoch_last_improved=GPA.pai_tracker.member_vars[ 3569 "epoch_last_improved" 3570 ], 3571 global_best_score=GPA.pai_tracker.member_vars[ 3572 "global_best_validation_score" 3573 ], 3574 ) 3575 return net, True, True 3576 else: 3577 restructuring_status_value = update_restructuring_status( 3578 restructuring_status_value, new_restructuring_status_value 3579 ) 3580 # Else if did improve, do a normal switch process 3581 else: 3582 if GPA.pc.get_verbose(): 3583 print( 3584 f"Calling switch_mode with " 3585 f'{GPA.pai_tracker.member_vars["current_n_set_global_best"]}, ' 3586 f'{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}, ' 3587 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_steps"]}, ' 3588 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_value"]},' 3589 f'{GPA.pc.get_max_dendrites()},' 3590 f'{GPA.pai_tracker.member_vars["num_dendrites_added"]},' 3591 f'{GPA.pai_tracker.member_vars["num_dendrite_tries"]},' 3592 ) 3593 import pdb; pdb.set_trace 3594 # If the max number of dendrites has been hit or not doing pai and adding dendtites 3595 # then return rather than adding more 3596 if ( 3597 (GPA.pai_tracker.member_vars["mode"] == "n") 3598 and ( 3599 GPA.pc.get_max_dendrites() 3600 == GPA.pai_tracker.member_vars["num_dendrites_added"] 3601 ) 3602 ) or (GPA.pai_tracker.member_vars["doing_pai"] is False): 3603 if GPA.pc.get_verbose(): 3604 print( 3605 "Max dendrites reached or not doing PAI, finishing training" 3606 ) 3607 net = process_final_network(net) 3608 # Increment integrated if we have dendrites (means they're integrated) 3609 if GPA.pai_tracker.member_vars["num_dendrites_added"] > 0: 3610 GPA.pai_tracker.member_vars["num_dendrites_integrated"] += 1 3611 _pai_log("info", f"Final dendrites successfully integrated! Total integrated: {GPA.pai_tracker.member_vars['num_dendrites_integrated']}") 3612 if _dashboard_emitter is not None: 3613 _dashboard_emitter.emit_dendrite_added( 3614 GPA.pc, 3615 epoch_index=len( 3616 GPA.pai_tracker.member_vars["accuracies"] 3617 ) - 1, 3618 num_dendrites_integrated=GPA.pai_tracker.member_vars[ 3619 "num_dendrites_integrated" 3620 ], 3621 param_count=UPA.count_params(net), 3622 ) 3623 if _dashboard_emitter is not None: 3624 _dashboard_emitter.emit_run_end( 3625 GPA.pc, 3626 epoch_last_improved=GPA.pai_tracker.member_vars[ 3627 "epoch_last_improved" 3628 ], 3629 global_best_score=GPA.pai_tracker.member_vars[ 3630 "global_best_validation_score" 3631 ], 3632 ) 3633 return net, True, True 3634 3635 # Otherwise if its neuron training mode reset the counter of failed dendrites 3636 # Check if we should increment integrated count BEFORE change_learning_modes loads old state 3637 should_increment_integrated = False 3638 if GPA.pai_tracker.member_vars["mode"] == "n": 3639 GPA.pai_tracker.member_vars["num_dendrite_tries"] = 0 3640 if GPA.pc.get_verbose(): 3641 print( 3642 "Adding new dendrites without resetting which means " 3643 "the last ones improved. Resetting num_dendrite_tries" 3644 ) 3645 # Remember to increment after change_learning_modes (which loads old tracker state) 3646 if GPA.pai_tracker.member_vars["num_dendrites_added"] > 0: 3647 should_increment_integrated = True 3648 3649 GPA.pai_tracker.save_graphs( 3650 f'_beforeSwitch_{len(GPA.pai_tracker.member_vars["switch_epochs"])}' 3651 ) 3652 3653 if GPA.pc.get_test_saves(): 3654 UPA.save_system( 3655 net, 3656 GPA.pc.get_save_name(), 3657 f'beforeSwitch_{len(GPA.pai_tracker.member_vars["switch_epochs"])}', 3658 ) 3659 # Copy current best model from this set of dendrites 3660 # If running DDP only copy with rank 0 3661 if "RANK" not in os.environ or int(os.environ["RANK"]) == 0: 3662 shutil.copyfile( 3663 f"{GPA.pc.get_save_name()}/best_model.pt", 3664 f'{GPA.pc.get_save_name()}/best_model_beforeSwitch_{len(GPA.pai_tracker.member_vars["switch_epochs"])}.pt', 3665 ) 3666 3667 if GPA.pc.get_dashboard_debug(): 3668 _pai_log( 3669 "debug", 3670 "[emitter/finding4] before change_learning_modes: " 3671 f'len(accuracies)={len(GPA.pai_tracker.member_vars["accuracies"])} ' 3672 f'num_epochs_run={GPA.pai_tracker.member_vars["num_epochs_run"]}', 3673 ) 3674 net = UPA.change_learning_modes( 3675 net, 3676 GPA.pc.get_save_name(), 3677 "best_model", 3678 GPA.pai_tracker.member_vars["doing_pai"], 3679 ) 3680 restructuring_status_value = NETWORK_RESTRUCTURED 3681 if GPA.pc.get_dashboard_debug(): 3682 _pai_log( 3683 "debug", 3684 "[emitter/finding4] after change_learning_modes: " 3685 f'len(accuracies)={len(GPA.pai_tracker.member_vars["accuracies"])} ' 3686 f'num_epochs_run={GPA.pai_tracker.member_vars["num_epochs_run"]}', 3687 ) 3688 3689 # Now increment after change_learning_modes has loaded the best model 3690 # This ensures the increment persists and doesn't get overwritten 3691 if should_increment_integrated: 3692 GPA.pai_tracker.member_vars["num_dendrites_integrated"] += 1 3693 _pai_log("info", f"Dendrites successfully integrated! Total integrated: {GPA.pai_tracker.member_vars['num_dendrites_integrated']}") 3694 if _dashboard_emitter is not None: 3695 _dashboard_emitter.emit_dendrite_added( 3696 GPA.pc, 3697 epoch_index=len( 3698 GPA.pai_tracker.member_vars["accuracies"] 3699 ) - 1, 3700 num_dendrites_integrated=GPA.pai_tracker.member_vars[ 3701 "num_dendrites_integrated" 3702 ], 3703 param_count=UPA.count_params(net), 3704 ) 3705 3706 # If restructured is true, clear scheduler/optimizer before saving 3707 if restructuring_status_value != NETWORK_RESTRUCTURED: 3708 print( 3709 "Restructured should always be triggered here, let us know if you encounter this situation" 3710 ) 3711 pdb.set_trace() 3712 3713 # Since there is a restructuring optimizer and scheduler must be reinitialized after return 3714 GPA.pai_tracker.clear_optimizer_and_scheduler() 3715 3716 # Save the model from after the switch 3717 UPA.save_system( 3718 net, 3719 GPA.pc.get_save_name(), 3720 f'switch_{len(GPA.pai_tracker.member_vars["switch_epochs"])}', 3721 ) 3722 3723 # If not time to switch and you have a scheduler, perform the update step 3724 elif GPA.pai_tracker.member_vars["scheduler"] is not None: 3725 new_restructuring_status_value, net = process_scheduler_update( 3726 net, accuracy, epochs_since_cycle_switch 3727 ) 3728 restructuring_status_value = update_restructuring_status( 3729 restructuring_status_value, new_restructuring_status_value 3730 ) 3731 3732 GPA.pai_tracker.start_epoch(internal_call=True) 3733 if _dashboard_emitter is not None: 3734 _mv = GPA.pai_tracker.member_vars 3735 _lr = _mv["training_learning_rates"][-1] if _mv["training_learning_rates"] else None 3736 # epoch_index is the committed coordinate: len(accuracies) - 1, 3737 # captured after update_running_accuracy appends and after any reload 3738 # truncation in switch handling. Independent of num_epochs_run. 3739 _epoch_index = len(_mv["accuracies"]) - 1 3740 if GPA.pc.get_dashboard_debug(): 3741 _pai_log( 3742 "debug", 3743 "[emitter/finding4] emit_epoch: " 3744 f"epoch_index={_epoch_index} " 3745 f'true_epoch={_mv["num_epochs_run"]} ' 3746 f'restructured={restructuring_status_value == NETWORK_RESTRUCTURED} ' 3747 f'tail_accuracies={_mv["accuracies"][-4:]}', 3748 ) 3749 # Phase captured at the top of the method (pre-switch), matching the 3750 # point pb_scores were sampled - see Finding 2, ticket 06. 3751 _phase = epoch_phase 3752 # scores: validation (reported), running (checkpointed member var), 3753 # then every add_extra_score name verbatim. 3754 _scores = { 3755 "validation": accuracy, 3756 "running": _mv["running_accuracies"][-1] if _mv["running_accuracies"] else None, 3757 } 3758 for _name, _vals in _mv["extra_scores"].items(): 3759 if _vals: 3760 _scores[_name] = _vals[-1] 3761 _n_times = _mv["n_epoch_times"] or [(_mv["n_train_times"][-1] + _mv["n_val_times"][-1]) if (_mv["n_train_times"] and _mv["n_val_times"]) else None] 3762 _p_times = _mv["p_epoch_times"] or [(_mv["p_train_times"][-1] + _mv["p_val_times"][-1]) if (_mv["p_train_times"] and _mv["p_val_times"]) else None] 3763 _dashboard_emitter.emit_epoch( 3764 GPA.pc, 3765 epoch_index=_epoch_index, 3766 phase=_phase, 3767 scores=_scores, 3768 learning_rate=_lr, 3769 normal_time=_n_times[-1] if _phase == "n" else None, 3770 pai_time=_p_times[-1] if _phase == "p" else None, 3771 pb_scores=epoch_pb_scores, 3772 pb_scores_current=epoch_pb_scores_current, 3773 ) 3774 GPA.pai_tracker.save_graphs() 3775 3776 if restructuring_status_value == NETWORK_RESTRUCTURED: 3777 GPA.pai_tracker.member_vars["epoch_last_improved"] = ( 3778 GPA.pai_tracker.member_vars["num_epochs_run"] 3779 ) 3780 if GPA.pc.get_verbose(): 3781 print( 3782 f"Setting epoch last improved to " 3783 f'{GPA.pai_tracker.member_vars["epoch_last_improved"]}' 3784 ) 3785 3786 now = datetime.now() 3787 dt_string = now.strftime("_%d.%m.%Y.%H.%M.%S") 3788 3789 if GPA.pc.get_verbose(): 3790 print("Not saving restructure right now") 3791 3792 """ 3793 This block of code helped with a save issue with safetensors and huggingface, but it breaks DDP. 3794 Temporarily removing it to avoid DDP issues, but if you encounter save issues try adding it back in. 3795 for param in net.parameters(): 3796 param.data = param.data.contiguous() 3797 """ 3798 if GPA.pc.get_verbose(): 3799 print( 3800 f"Completed adding score. Restructured is {restructuring_status_value}, " 3801 f"\ncurrent switch list is:" 3802 ) 3803 print(GPA.pai_tracker.member_vars["switch_epochs"]) 3804 3805 if _dashboard_emitter is not None and restructuring_status_value == NETWORK_RESTRUCTURED: 3806 _param_count = UPA.count_params(net) 3807 _dashboard_emitter.emit_switch( 3808 GPA.pc, 3809 # switch_epochs was already appended in change_learning_modes, 3810 # so subtract 1 for a 0-based ordinal (matches matplotlib's 3811 # np.arange(len(switch_epochs)) labelling) - Finding 1, ticket 06. 3812 switch_ordinal=len(GPA.pai_tracker.member_vars["switch_epochs"]) - 1, 3813 epoch_index=len(GPA.pai_tracker.member_vars["accuracies"]) - 1, 3814 param_count=_param_count, 3815 switch_type=GPA.pai_tracker.member_vars["mode"], 3816 ) 3817 3818 # Always False for training complete if nothing triggered that training is over 3819 return net, restructuring_status_value, False 3820 3821 def clear_all_processors(self): 3822 """Clear all processors from modules. 3823 3824 Parameters 3825 ---------- 3826 None 3827 3828 Returns 3829 ------- 3830 None 3831 This function does not return a value. 3832 """ 3833 for module in self.neuron_module_vector: 3834 module.clear_processors() 3835 3836 def create_new_dendrite_module(self): 3837 """Add dendrite module to all neuron modules. 3838 3839 Parameters 3840 ---------- 3841 None 3842 3843 Returns 3844 ------- 3845 None 3846 This function does not return a value. 3847 """ 3848 for module in self.neuron_module_vector: 3849 module.create_new_dendrite_module() 3850 3851 def set_create_dendrite_global(self, fn): 3852 """Call set_create_dendrite(fn) on every tracked PAINeuronModule.""" 3853 for module in self.neuron_module_vector: 3854 module.set_create_dendrite(fn) 3855 3856 def set_dendrite_loss_fn_global(self, fn): 3857 """Set the global dendrite loss function used by all dendrite modules.""" 3858 from perforatedbp import modules_pbp as MPB 3859 MPB.dendrite_loss_fn = fn 3860 3861 def apply_pb_grads(self): 3862 """Apply perforated backpropagation gradients to all modules. 3863 3864 Parameters 3865 ---------- 3866 None 3867 3868 Returns 3869 ------- 3870 None 3871 This function does not return a value. 3872 """ 3873 if self.member_vars["mode"] == "p": 3874 for module in self.neuron_module_vector: 3875 module.apply_pb_grads() 3876 3877 def apply_pb_zero(self): 3878 """Apply perforated backpropagation zero gradients to all modules. 3879 3880 Parameters 3881 ---------- 3882 None 3883 3884 Returns 3885 ------- 3886 None 3887 This function does not return a value. 3888 """ 3889 if self.member_vars["mode"] == "p": 3890 for module in self.neuron_module_vector: 3891 module.apply_pb_zero()
102def update_restructuring_status(old_status, new_status): 103 """Update restructured variable during add_validation_score 104 105 Update the restructuring status based on the new status. 106 If the new status is that there was not an update, 107 dont overwrite the old status which may show there was an update. 108 109 Parameters 110 ---------- 111 old_status : int 112 The old restructuring status. 113 new_status : int 114 The new restructuring status. 115 116 Returns 117 ------- 118 int 119 The updated restructuring status. 120 121 """ 122 if new_status == NETWORK_RESTRUCTURED or new_status == TRAINING_COMPLETE: 123 return NETWORK_RESTRUCTURED 124 else: 125 return old_status
Update restructured variable during add_validation_score
Update the restructuring status based on the new status. If the new status is that there was not an update, dont overwrite the old status which may show there was an update.
Parameters
- old_status (int): The old restructuring status.
- new_status (int): The new restructuring status.
Returns
- int: The updated restructuring status.
128def update_learning_rate(): 129 """Update the learning rate in the tracker. 130 131 Parameters 132 ---------- 133 None 134 135 Returns 136 ------- 137 None 138 This function does not return a value. 139 """ 140 for param_group in GPA.pai_tracker.member_vars["optimizer_instance"].param_groups: 141 learning_rate = param_group["lr"] 142 GPA.pai_tracker.add_learning_rate(learning_rate)
Update the learning rate in the tracker.
Parameters
- None
Returns
- None: This function does not return a value.
145def update_param_count(net): 146 """Update the parameter count in the tracker if not already set. 147 148 Parameters 149 ---------- 150 net : torch.nn.Module 151 The neural network model to count parameters for. 152 Returns 153 ------- 154 None 155 """ 156 if len(GPA.pai_tracker.member_vars["param_counts"]) == 0: 157 GPA.pai_tracker.member_vars["param_counts"].append(UPA.count_params(net))
Update the parameter count in the tracker if not already set.
Parameters
- net (torch.nn.Module): The neural network model to count parameters for.
Returns
- None
160def check_input_problems(net, accuracy): 161 """Check for potential input problems in add_validation_score. 162 163 Parameters 164 ---------- 165 net : torch.nn.Module 166 The neural network model to check. 167 accuracy : float, int, or torch.Tensor 168 The accuracy score to validate. 169 170 Returns 171 ------- 172 float 173 The validated accuracy score. 174 175 """ 176 177 # Make sure you are passing in the model and not the dataparallel wrapper 178 if issubclass(type(net), nn.DataParallel): 179 _pai_log("error", "Need to call .module when using add validation score") 180 pdb.set_trace() 181 sys.exit(-1) 182 183 if "module" in net.__dir__(): 184 _pai_log("error", "Need to call .module when using add validation score") 185 pdb.set_trace() 186 sys.exit(-1) 187 188 if not isinstance(accuracy, (float, int)): 189 try: 190 accuracy = accuracy.item() 191 except: 192 _pai_log( 193 "error", 194 f"Scores added for add_validation_score should be float, int, or tensor, yours is a: {type(accuracy)}", 195 ) 196 pdb.set_trace() 197 sys.exit(-1) 198 return accuracy
Check for potential input problems in add_validation_score.
Parameters
- net (torch.nn.Module): The neural network model to check.
- accuracy (float, int, or torch.Tensor): The accuracy score to validate.
Returns
- float: The validated accuracy score.
201def update_running_accuracy(accuracy, epochs_since_cycle_switch): 202 """Add the new accuracy to the tracker. 203 204 Parameters 205 ---------- 206 accuracy : float, int, or torch.Tensor 207 The accuracy score to add. 208 epochs_since_cycle_switch : int 209 The number of epochs since the last cycle switch. 210 211 Returns 212 ------- 213 None 214 215 """ 216 # Only update running_accuracy when neurons are being updated 217 if GPA.pai_tracker.member_vars["mode"] == "n" or GPA.pc.get_learn_dendrites_live(): 218 if epochs_since_cycle_switch < GPA.pc.get_initial_history_after_switches(): 219 if epochs_since_cycle_switch <= 0: 220 GPA.pai_tracker.member_vars["running_accuracy"] = accuracy 221 else: 222 GPA.pai_tracker.member_vars[ 223 "running_accuracy" 224 ] = GPA.pai_tracker.member_vars["running_accuracy"] * ( 225 1 - (1.0 / (epochs_since_cycle_switch + 1)) 226 ) + accuracy * ( 227 1.0 / (epochs_since_cycle_switch + 1) 228 ) 229 else: 230 GPA.pai_tracker.member_vars[ 231 "running_accuracy" 232 ] = GPA.pai_tracker.member_vars["running_accuracy"] * ( 233 1.0 - 1.0 / GPA.pc.get_history_lookback() 234 ) + accuracy * ( 235 1.0 / GPA.pc.get_history_lookback() 236 ) 237 238 GPA.pai_tracker.member_vars["accuracies"].append(accuracy) 239 if GPA.pai_tracker.member_vars["mode"] == "n": 240 GPA.pai_tracker.member_vars["n_accuracies"].append(accuracy) 241 242 if ( 243 GPA.pc.get_drawing_pai() 244 or GPA.pai_tracker.member_vars["mode"] == "n" 245 or GPA.pc.get_learn_dendrites_live() 246 ): 247 GPA.pai_tracker.member_vars["running_accuracies"].append( 248 GPA.pai_tracker.member_vars["running_accuracy"] 249 )
Add the new accuracy to the tracker.
Parameters
- accuracy (float, int, or torch.Tensor): The accuracy score to add.
- epochs_since_cycle_switch (int): The number of epochs since the last cycle switch.
Returns
- None
252def score_beats_current_best(new_score, old_score): 253 """Check if the new score beats the current best score. 254 255 Parameters 256 ---------- 257 new_score : float 258 The new score to compare. 259 old_score : float 260 The old score to compare against. 261 262 Returns 263 ------- 264 bool 265 True if the new score beats the old score, False otherwise. 266 267 Notes 268 ----- 269 Must beat the old score by the margins set in globals for improvement thresholds. 270 271 """ 272 return ( 273 GPA.pai_tracker.member_vars["maximizing_score"] 274 and (new_score * (1.0 - GPA.pc.get_improvement_threshold()) > old_score) 275 and new_score - GPA.pc.get_improvement_threshold_raw() > old_score 276 ) or ( 277 (not GPA.pai_tracker.member_vars["maximizing_score"]) 278 and (new_score * (1.0 + GPA.pc.get_improvement_threshold()) < old_score) 279 and (new_score + GPA.pc.get_improvement_threshold_raw()) < old_score 280 )
Check if the new score beats the current best score.
Parameters
- new_score (float): The new score to compare.
- old_score (float): The old score to compare against.
Returns
- bool: True if the new score beats the old score, False otherwise.
Notes
Must beat the old score by the margins set in globals for improvement thresholds.
283def check_new_best(net, accuracy, epochs_since_cycle_switch): 284 """Check if the new accuracy is a new best. 285 286 Performs saves if new best score is found. 287 288 Parameters 289 ---------- 290 net : torch.nn.Module 291 The neural network model being trained. 292 accuracy : float 293 The accuracy score to check. 294 epochs_since_cycle_switch : int 295 The number of epochs since the last cycle switch. 296 297 Returns 298 ------- 299 None 300 301 """ 302 score_improved = score_beats_current_best( 303 GPA.pai_tracker.member_vars["running_accuracy"], 304 GPA.pai_tracker.member_vars["current_best_validation_score"], 305 ) 306 307 enough_time = ( 308 epochs_since_cycle_switch > GPA.pc.get_initial_history_after_switches() 309 ) or (GPA.pai_tracker.member_vars["switch_mode"] == GPA.pc.DOING_SWITCH_EVERY_TIME) 310 311 if ( 312 score_improved 313 or GPA.pai_tracker.member_vars["current_best_validation_score"] == 0 314 ) and enough_time: 315 316 if GPA.pai_tracker.member_vars["maximizing_score"]: 317 if GPA.pc.get_verbose(): 318 print( 319 f"\n\nGot score of {accuracy:.10f} " 320 f'(average {GPA.pai_tracker.member_vars["running_accuracy"]}, ' 321 f"*{1-GPA.pc.get_improvement_threshold()}=" 322 f'{GPA.pai_tracker.member_vars["running_accuracy"]*(1.0 - GPA.pc.get_improvement_threshold())}) ' 323 f'which is higher than {GPA.pai_tracker.member_vars["current_best_validation_score"]:.10f} ' 324 f"by {GPA.pc.get_improvement_threshold_raw()} so setting epoch to " 325 f'{GPA.pai_tracker.member_vars["num_epochs_run"]}\n\n' 326 ) 327 else: 328 if GPA.pc.get_verbose(): 329 print( 330 f"\n\nGot score of {accuracy:.10f} " 331 f'(average {GPA.pai_tracker.member_vars["running_accuracy"]}, ' 332 f"*{1+GPA.pc.get_improvement_threshold()}=" 333 f'{GPA.pai_tracker.member_vars["running_accuracy"]*(1.0 + GPA.pc.get_improvement_threshold())}) ' 334 f'which is lower than {GPA.pai_tracker.member_vars["current_best_validation_score"]:.10f} ' 335 f'so setting epoch to {GPA.pai_tracker.member_vars["num_epochs_run"]}\n\n' 336 ) 337 338 # Set the new best score 339 GPA.pai_tracker.member_vars["current_best_validation_score"] = ( 340 GPA.pai_tracker.member_vars["running_accuracy"] 341 ) 342 GPA.pai_tracker.member_vars["epoch_last_improved"] = ( 343 GPA.pai_tracker.member_vars["num_epochs_run"] 344 ) 345 if GPA.pc.get_verbose(): 346 print( 347 f'2 epoch improved is {GPA.pai_tracker.member_vars["epoch_last_improved"]}' 348 ) 349 # Immediately update this list before saving so loading will have it correctly 350 GPA.pai_tracker.member_vars["last_improved_accuracies"].append( 351 GPA.pai_tracker.member_vars["epoch_last_improved"] 352 ) 353 # Check if global best 354 is_global_best = score_beats_current_best( 355 GPA.pai_tracker.member_vars["current_best_validation_score"], 356 GPA.pai_tracker.member_vars["global_best_validation_score"], 357 ) 358 359 if ( 360 is_global_best 361 or GPA.pai_tracker.member_vars["global_best_validation_score"] == 0 362 ): 363 if GPA.pc.get_verbose(): 364 print( 365 f"This also beats global best of " 366 f'{GPA.pai_tracker.member_vars["global_best_validation_score"]} so saving' 367 ) 368 GPA.pai_tracker.member_vars["global_best_validation_score"] = ( 369 GPA.pai_tracker.member_vars["current_best_validation_score"] 370 ) 371 GPA.pai_tracker.member_vars["current_n_set_global_best"] = True 372 UPA.save_system(net, GPA.pc.get_save_name(), "best_model") 373 if GPA.pc.get_pai_saves(): 374 UPA.pai_save_system(net, GPA.pc.get_save_name(), "best_model") 375 else: 376 if GPA.pc.get_verbose(): 377 print("Not saving new best because:") 378 if epochs_since_cycle_switch <= GPA.pc.get_initial_history_after_switches(): 379 print( 380 f"Not enough history since switch {epochs_since_cycle_switch} <= " 381 f"{GPA.pc.get_initial_history_after_switches()}" 382 ) 383 elif GPA.pai_tracker.member_vars["maximizing_score"]: 384 print( 385 f"Got score of {accuracy} " 386 f'(average {GPA.pai_tracker.member_vars["running_accuracy"]}, ' 387 f"*{1-GPA.pc.get_improvement_threshold()}=" 388 f'{GPA.pai_tracker.member_vars["running_accuracy"]*(1.0 - GPA.pc.get_improvement_threshold())}) ' 389 f"which is not higher than " 390 f'{GPA.pai_tracker.member_vars["current_best_validation_score"]}' 391 ) 392 else: 393 print( 394 f"Got score of {accuracy} " 395 f'(average {GPA.pai_tracker.member_vars["running_accuracy"]}, ' 396 f"*{1+GPA.pc.get_improvement_threshold()}=" 397 f'{GPA.pai_tracker.member_vars["running_accuracy"]*(1.0 + GPA.pc.get_improvement_threshold())}) ' 398 f"which is not lower than " 399 f'{GPA.pai_tracker.member_vars["current_best_validation_score"]}' 400 ) 401 GPA.pai_tracker.member_vars["last_improved_accuracies"].append( 402 GPA.pai_tracker.member_vars["epoch_last_improved"] 403 ) 404 # If it's the first epoch, save as best anyway 405 if len(GPA.pai_tracker.member_vars["accuracies"]) == 1: 406 if GPA.pc.get_verbose(): 407 print("Saving first model or all models") 408 UPA.save_system(net, GPA.pc.get_save_name(), "best_model") 409 if GPA.pc.get_pai_saves(): 410 UPA.pai_save_system(net, GPA.pc.get_save_name(), "best_model")
Check if the new accuracy is a new best.
Performs saves if new best score is found.
Parameters
- net (torch.nn.Module): The neural network model being trained.
- accuracy (float): The accuracy score to check.
- epochs_since_cycle_switch (int): The number of epochs since the last cycle switch.
Returns
- None
413def process_no_improvement(net): 414 """Handle the case where no improvement is observed. 415 416 If the new dendrite did not improve scores, but its time to switch modes 417 either trigger the end of learning or reset to the previous dendrite 418 to try again. 419 420 Parameters 421 ---------- 422 net : torch.nn.Module 423 The neural network model being trained. 424 425 Returns 426 ------- 427 int 428 The status of restructuring or training completion. 429 torch.nn.Module 430 The potentially modified neural network model. 431 432 """ 433 if GPA.pc.get_verbose(): 434 print( 435 f"Planning to switch to p mode but best beat last: " 436 f'{GPA.pai_tracker.member_vars["current_n_set_global_best"]} ' 437 f"current start lr steps: " 438 f'{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]} ' 439 f"and last maximum lr steps: " 440 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_steps"]} ' 441 f'for rate: {GPA.pai_tracker.member_vars["last_max_learning_rate_value"]:.8f}' 442 ) 443 444 now = datetime.now() 445 dt_string = now.strftime("_%d.%m.%Y.%H.%M.%S") 446 447 if GPA.pc.get_verbose(): 448 print( 449 f'1 saving break {dt_string}_noImprove_lr_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}' 450 ) 451 452 GPA.pai_tracker.save_graphs( 453 f'{dt_string}_noImprove_lr_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}' 454 ) 455 456 if ( 457 GPA.pai_tracker.member_vars["num_dendrite_tries"] 458 < GPA.pc.get_max_dendrite_tries() -1 459 ): 460 _pai_log( 461 "info", 462 f"The newest added dendrites did not improve but current tries " 463 f'{GPA.pai_tracker.member_vars["num_dendrite_tries"] + 1} ' 464 f"is less than max tries {GPA.pc.get_max_dendrite_tries()} " 465 f"so loading last switch and trying new Dendrites.", 466 ) 467 old_tries = GPA.pai_tracker.member_vars["num_dendrite_tries"] 468 # Load best model from previous n mode 469 net = UPA.change_learning_modes( 470 net, 471 GPA.pc.get_save_name(), 472 "best_model", 473 GPA.pai_tracker.member_vars["doing_pai"], 474 ) 475 GPA.pai_tracker.member_vars["num_dendrite_tries"] = old_tries + 1 476 return NETWORK_RESTRUCTURED, net 477 else: 478 _pai_log( 479 "info", 480 f"The newest added dendrites did not improve system and " 481 f'{GPA.pai_tracker.member_vars["num_dendrite_tries"] + 1} > ' 482 f"{GPA.pc.get_max_dendrite_tries()} so returning training_complete.", 483 ) 484 _pai_log("info", "You should now exit your training loop and best_model will be your final model for inference") 485 if not GPA.pc.get_perforated_backpropagation() and GPA.pai_tracker.member_vars["num_dendrites_added"] > 0: 486 _pai_log("info", "For improved results, try perforated backpropagation next time!") 487 488 old_silent = GPA.pc.get_silent() 489 GPA.pc.set_silent(True) 490 UPA.load_system(net, GPA.pc.get_save_name(), "best_model", switch_call=True) 491 GPA.pc.set_silent(old_silent) 492 GPA.pai_tracker.save_graphs() 493 UPA.pai_save_system(net, GPA.pc.get_save_name(), "final_clean") 494 return TRAINING_COMPLETE, net
Handle the case where no improvement is observed.
If the new dendrite did not improve scores, but its time to switch modes either trigger the end of learning or reset to the previous dendrite to try again.
Parameters
- net (torch.nn.Module): The neural network model being trained.
Returns
- int: The status of restructuring or training completion.
- torch.nn.Module: The potentially modified neural network model.
497def process_final_network(net): 498 """When the max number of dendrites has been hit load the best_model and return 499 500 Parameters 501 ---------- 502 net : torch.nn.Module 503 The neural network model being trained. 504 505 Returns 506 ------- 507 torch.nn.Module 508 The final neural network model. 509 """ 510 511 _pai_log("info", f"Last Dendrites were good and this hit the max of {GPA.pc.get_max_dendrites()}") 512 if not GPA.pc.get_perforated_backpropagation() and GPA.pai_tracker.member_vars["num_dendrites_added"] > 0: 513 _pai_log("info", "For improved results, try perforated backpropagation next time!") 514 GPA.pai_tracker.save_graphs("before_final") 515 UPA.load_system(net, GPA.pc.get_save_name(), "best_model", switch_call=True) 516 GPA.pai_tracker.save_graphs() 517 UPA.pai_save_system(net, GPA.pc.get_save_name(), "final_clean") 518 return net
When the max number of dendrites has been hit load the best_model and return
Parameters
- net (torch.nn.Module): The neural network model being trained.
Returns
- torch.nn.Module: The final neural network model.
521def process_scheduler_update(net, accuracy, epochs_since_cycle_switch): 522 """Updates the scheduler 523 524 This increments the scheduler, but if we are automatically sweeping 525 to find the best initial learning rate for a new set of dendrites 526 this function also triggers the network at addition time to 527 try the next value. 528 529 Process for finding best initial learning rate for dendrites: 530 1. Start at default rate 531 2. Learn at that rate until scheduler increments twice 532 3. Save that version, start dendrites at LR current increment - 1 533 4. Repeat 2 and 3 until version has worse final score at set LR 534 5. Load previous model with best accuracy at that LR as initial rate 535 536 Parameters 537 ---------- 538 net : torch.nn.Module 539 The neural network model being trained. 540 accuracy : float 541 The accuracy of the model at the current learning rate. 542 epochs_since_cycle_switch : int 543 The number of epochs since the last cycle switch. 544 545 Returns 546 ------- 547 int 548 The status of restructuring or training completion. 549 torch.nn.Module 550 The potentially modified neural network model. 551 """ 552 553 restructured = False 554 for param_group in GPA.pai_tracker.member_vars["optimizer_instance"].param_groups: 555 learning_rate1 = param_group["lr"] 556 557 if ( 558 type(GPA.pai_tracker.member_vars["scheduler_instance"]) 559 is torch.optim.lr_scheduler.ReduceLROnPlateau 560 ): 561 if ( 562 epochs_since_cycle_switch > GPA.pc.get_initial_history_after_switches() 563 or GPA.pai_tracker.member_vars["mode"] == "p" 564 ): 565 if GPA.pc.get_verbose(): 566 print( 567 f"Updating scheduler with last improved " 568 f'{GPA.pai_tracker.member_vars["epoch_last_improved"]} ' 569 f'from current {GPA.pai_tracker.member_vars["num_epochs_run"]}' 570 ) 571 if GPA.pai_tracker.member_vars["scheduler"] is not None: 572 GPA.pai_tracker.member_vars["scheduler_instance"].step(metrics=accuracy) 573 if ( 574 GPA.pai_tracker.member_vars["scheduler"] 575 is torch.optim.lr_scheduler.ReduceLROnPlateau 576 ): 577 if GPA.pc.get_verbose(): 578 print( 579 f"Scheduler is now at " 580 f'{GPA.pai_tracker.member_vars["scheduler_instance"].num_bad_epochs} bad epochs' 581 ) 582 else: 583 if GPA.pc.get_verbose(): 584 print("Not stepping optimizer since hasnt initialized") 585 586 elif GPA.pai_tracker.member_vars["scheduler"] is not None: 587 if ( 588 epochs_since_cycle_switch > GPA.pc.get_initial_history_after_switches() 589 or GPA.pai_tracker.member_vars["mode"] == "p" 590 ): 591 if GPA.pc.get_verbose(): 592 if hasattr(GPA.pai_tracker.member_vars["scheduler_instance"], '_step_count'): 593 count = GPA.pai_tracker.member_vars["scheduler_instance"]._step_count 594 else: 595 count = GPA.pai_tracker.member_vars["scheduler_instance"].last_epoch 596 597 print( 598 f"Incrementing scheduler to count " 599 f'{count}' 600 ) 601 GPA.pai_tracker.member_vars["scheduler_instance"].step() 602 if ( 603 GPA.pai_tracker.member_vars["scheduler"] 604 is torch.optim.lr_scheduler.ReduceLROnPlateau 605 ): 606 if GPA.pc.get_verbose(): 607 print( 608 f"Scheduler is now at " 609 f'{GPA.pai_tracker.member_vars["scheduler_instance"].num_bad_epochs} bad epochs' 610 ) 611 612 if ( 613 epochs_since_cycle_switch <= GPA.pc.get_initial_history_after_switches() 614 and GPA.pai_tracker.member_vars["mode"] == "n" 615 ): 616 if GPA.pc.get_verbose(): 617 print( 618 f"Not stepping with history {GPA.pc.get_initial_history_after_switches()} " 619 f"and current {epochs_since_cycle_switch}" 620 ) 621 622 for param_group in GPA.pai_tracker.member_vars["optimizer_instance"].param_groups: 623 learning_rate2 = param_group["lr"] 624 625 stepped = False 626 at_last_count = False 627 628 if GPA.pc.get_verbose(): 629 print( 630 f"Checking if at last with scores " 631 f'{len(GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"])}, ' 632 f"count since switch {epochs_since_cycle_switch} " 633 f"and last total lr step count " 634 f'{GPA.pai_tracker.member_vars["initial_lr_test_epoch_count"]}' 635 ) 636 637 # Check if at double or exactly the test count 638 if ( 639 len(GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"]) == 0 640 and epochs_since_cycle_switch 641 == GPA.pai_tracker.member_vars["initial_lr_test_epoch_count"] * 2 642 ) or ( 643 len(GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"]) == 1 644 and epochs_since_cycle_switch 645 == GPA.pai_tracker.member_vars["initial_lr_test_epoch_count"] 646 ): 647 at_last_count = True 648 649 if GPA.pc.get_verbose(): 650 print( 651 f"At last count {at_last_count} with count {epochs_since_cycle_switch} " 652 f'and last LR count {GPA.pai_tracker.member_vars["initial_lr_test_epoch_count"]}' 653 ) 654 655 if learning_rate1 != learning_rate2: 656 stepped = True 657 GPA.pai_tracker.member_vars["current_step_count"] += 1 658 659 if GPA.pc.get_verbose(): 660 print( 661 f"Learning rate just stepped to {learning_rate2:.10e} " 662 f'with {GPA.pai_tracker.member_vars["current_step_count"]} total steps' 663 ) 664 665 if ( 666 GPA.pai_tracker.member_vars["current_step_count"] 667 == GPA.pai_tracker.member_vars["last_max_learning_rate_steps"] 668 ): 669 if GPA.pc.get_verbose(): 670 print( 671 f'{GPA.pai_tracker.member_vars["current_step_count"]} ' 672 f"steps is the max of the last switch mode" 673 ) 674 # Set it when 1->2 gets to 2, not when 0->1 hits 2 as stopping point 675 if ( 676 GPA.pai_tracker.member_vars["current_step_count"] 677 - GPA.pai_tracker.member_vars[ 678 "current_n_learning_rate_initial_skip_steps" 679 ] 680 == 1 681 ): 682 GPA.pai_tracker.member_vars["initial_lr_test_epoch_count"] = ( 683 epochs_since_cycle_switch 684 ) 685 686 if GPA.pc.get_verbose(): 687 print( 688 f"Learning rates were {learning_rate1:.8e} and {learning_rate2:.8e} " 689 f'started with {GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}, ' 690 f'and is now at {GPA.pai_tracker.member_vars["current_step_count"]} ' 691 f'committed {GPA.pai_tracker.member_vars["committed_to_initial_rate"]} ' 692 f"then either this (non zero) or eventually comparing to " 693 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_steps"]} ' 694 f'steps or rate {GPA.pai_tracker.member_vars["last_max_learning_rate_value"]:.8f}' 695 ) 696 697 # If learning rate just stepped, check restart at lower rate 698 if ( 699 (GPA.pai_tracker.member_vars["scheduler"] is not None) 700 and 701 # If potentially might have higher accuracy 702 ( 703 (GPA.pai_tracker.member_vars["mode"] == "n") 704 or GPA.pc.get_learn_dendrites_live() 705 ) 706 and 707 # And learning rate just stepped 708 (stepped or at_last_count) 709 ): 710 711 # If this is the first dendrite addition (last_max_learning_rate_steps == 0), 712 # immediately commit to the initial rate without searching 713 if GPA.pai_tracker.member_vars["last_max_learning_rate_steps"] == 0: 714 if GPA.pc.get_verbose(): 715 print( 716 f"First dendrite addition detected (last_max_learning_rate_steps == 0), " 717 f"immediately committing to initial rate without search" 718 ) 719 GPA.pai_tracker.member_vars["committed_to_initial_rate"] = True 720 GPA.pai_tracker.member_vars["last_max_learning_rate_steps"] = ( 721 GPA.pai_tracker.member_vars["current_step_count"] 722 ) 723 GPA.pai_tracker.member_vars["last_max_learning_rate_value"] = ( 724 learning_rate2 725 ) 726 727 # If hasn't committed to a learning rate for this cycle yet 728 if not GPA.pai_tracker.member_vars["committed_to_initial_rate"]: 729 best_score_so_far = GPA.pai_tracker.member_vars[ 730 "global_best_validation_score" 731 ] 732 733 if GPA.pc.get_verbose(): 734 print( 735 f"In statements to check next learning rate with " 736 f"stepped {stepped} and max count {at_last_count}" 737 ) 738 739 # If no scores saved for this dendrite and initial LR test did second step 740 if len( 741 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"] 742 ) == 0 and ( 743 GPA.pai_tracker.member_vars["current_step_count"] 744 - GPA.pai_tracker.member_vars[ 745 "current_n_learning_rate_initial_skip_steps" 746 ] 747 == 2 748 or at_last_count 749 ): 750 751 restructured = True 752 GPA.pai_tracker.clear_optimizer_and_scheduler() 753 754 # Save system for this initial condition 755 old_global = GPA.pai_tracker.member_vars["global_best_validation_score"] 756 old_accuracy = GPA.pai_tracker.member_vars[ 757 "current_best_validation_score" 758 ] 759 old_counts = GPA.pai_tracker.member_vars["initial_lr_test_epoch_count"] 760 skip1 = GPA.pai_tracker.member_vars[ 761 "current_n_learning_rate_initial_skip_steps" 762 ] 763 764 now = datetime.now() 765 dt_string = now.strftime("_%d.%m.%Y.%H.%M.%S") 766 767 GPA.pai_tracker.save_graphs( 768 f'{dt_string}_PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}' 769 ) 770 771 if GPA.pc.get_test_saves(): 772 UPA.save_system( 773 net, 774 GPA.pc.get_save_name(), 775 f'PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}', 776 ) 777 778 if GPA.pc.get_verbose(): 779 print( 780 f"Saving with initial steps: {dt_string}_PBCount_" 781 f'{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_' 782 f'{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]} ' 783 f"with current best {old_accuracy}" 784 ) 785 786 # Load back at start and try with lower initial learning rate 787 net = UPA.load_system( 788 net, 789 GPA.pc.get_save_name(), 790 f'switch_{len(GPA.pai_tracker.member_vars["switch_epochs"])}', 791 switch_call=True, 792 ) 793 GPA.pai_tracker.member_vars[ 794 "current_n_learning_rate_initial_skip_steps" 795 ] = (skip1 + 1) 796 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"].append( 797 old_accuracy 798 ) 799 GPA.pai_tracker.member_vars["global_best_validation_score"] = old_global 800 GPA.pai_tracker.member_vars["initial_lr_test_epoch_count"] = old_counts 801 802 # If there is one score already, this is first step at next score 803 elif len(GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"]) == 1: 804 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"].append( 805 GPA.pai_tracker.member_vars["current_best_validation_score"] 806 ) 807 808 # If this LR's score was worse than last LR's score 809 lr_score_worse = False 810 if GPA.pai_tracker.member_vars["maximizing_score"]: 811 lr_score_worse = ( 812 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][0] 813 > GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][1] 814 ) 815 else: 816 lr_score_worse = ( 817 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][0] 818 < GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][1] 819 ) 820 821 if lr_score_worse: 822 restructured = True 823 GPA.pai_tracker.clear_optimizer_and_scheduler() 824 825 if GPA.pc.get_verbose(): 826 print( 827 f'Got initial {GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]-1} ' 828 f'step score {GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][0]} ' 829 f'and {GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]} ' 830 f'score at step {GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][1]} ' 831 f"so loading old score" 832 ) 833 834 prior_best = GPA.pai_tracker.member_vars[ 835 "current_cycle_lr_max_scores" 836 ][0] 837 838 now = datetime.now() 839 dt_string = now.strftime("_%d.%m.%Y.%H.%M.%S") 840 841 GPA.pai_tracker.save_graphs( 842 f'{dt_string}_PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}' 843 ) 844 845 if GPA.pc.get_test_saves(): 846 UPA.save_system( 847 net, 848 GPA.pc.get_save_name(), 849 f'PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}', 850 ) 851 852 if GPA.pc.get_verbose(): 853 print( 854 f"Saving with initial steps: {dt_string}_PBCount_" 855 f'{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_' 856 f'{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}' 857 ) 858 859 if GPA.pc.get_test_saves(): 860 net = UPA.load_system( 861 net, 862 GPA.pc.get_save_name(), 863 f'PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]-1}', 864 switch_call=True, 865 ) 866 867 # Save graphs for chosen one 868 now = datetime.now() 869 dt_string = now.strftime("_%d.%m.%Y.%H.%M.%S") 870 871 GPA.pai_tracker.save_graphs( 872 f'{dt_string}_PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}PICKED' 873 ) 874 875 if GPA.pc.get_test_saves(): 876 UPA.save_system( 877 net, 878 GPA.pc.get_save_name(), 879 f'PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}', 880 ) 881 882 if GPA.pc.get_verbose(): 883 print( 884 f"Saving with initial steps: {dt_string}_PBCount_" 885 f'{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_' 886 f'{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}' 887 ) 888 889 GPA.pai_tracker.member_vars["committed_to_initial_rate"] = True 890 GPA.pai_tracker.member_vars["last_max_learning_rate_steps"] = ( 891 GPA.pai_tracker.member_vars["current_step_count"] 892 ) 893 GPA.pai_tracker.member_vars["last_max_learning_rate_value"] = ( 894 learning_rate2 895 ) 896 GPA.pai_tracker.member_vars["current_best_validation_score"] = ( 897 prior_best 898 ) 899 900 if GPA.pc.get_verbose(): 901 print( 902 f"Setting last max steps to " 903 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_steps"]} ' 904 f'and lr {GPA.pai_tracker.member_vars["last_max_learning_rate_value"]}' 905 ) 906 907 else: # Current LR score is better 908 if GPA.pc.get_verbose(): 909 print( 910 f'Got initial {GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]-1} ' 911 f'step score {GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][0]} ' 912 f'and {GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]} ' 913 f'score at step {GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"][1]} ' 914 f"so NOT loading old score and continuing with this score" 915 ) 916 917 if at_last_count: # If this is the last one, set it to be picked 918 restructured = True 919 GPA.pai_tracker.clear_optimizer_and_scheduler() 920 921 now = datetime.now() 922 dt_string = now.strftime("_%d.%m.%Y.%H.%M.%S") 923 924 GPA.pai_tracker.save_graphs( 925 f'{dt_string}_PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}PICKED' 926 ) 927 928 if GPA.pc.get_test_saves(): 929 UPA.save_system( 930 net, 931 GPA.pc.get_save_name(), 932 f'PBCount_{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}', 933 ) 934 935 if GPA.pc.get_verbose(): 936 print( 937 f"Saving with initial steps: {dt_string}_PBCount_" 938 f'{GPA.pai_tracker.member_vars["num_dendrites_added"]}_startSteps_' 939 f'{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}' 940 ) 941 942 GPA.pai_tracker.member_vars["committed_to_initial_rate"] = True 943 GPA.pai_tracker.member_vars["last_max_learning_rate_steps"] = ( 944 GPA.pai_tracker.member_vars["current_step_count"] 945 ) 946 GPA.pai_tracker.member_vars["last_max_learning_rate_value"] = ( 947 learning_rate2 948 ) 949 950 if GPA.pc.get_verbose(): 951 print( 952 f"Setting last max steps to " 953 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_steps"]} ' 954 f'and lr {GPA.pai_tracker.member_vars["last_max_learning_rate_value"]}' 955 ) 956 957 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"] = [] 958 959 elif len(GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"]) == 2: 960 print( 961 "Should never be here. Please let Perforated AI know if this happened." 962 ) 963 pdb.set_trace() 964 965 GPA.pai_tracker.member_vars["global_best_validation_score"] = ( 966 best_score_so_far 967 ) 968 969 else: 970 if GPA.pc.get_verbose(): 971 print( 972 f"Setting last max steps to " 973 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_steps"]} ' 974 f'and lr {GPA.pai_tracker.member_vars["last_max_learning_rate_value"]}' 975 ) 976 GPA.pai_tracker.member_vars["last_max_learning_rate_steps"] += 1 977 GPA.pai_tracker.member_vars["last_max_learning_rate_value"] = learning_rate2 978 if restructured: 979 return NETWORK_RESTRUCTURED, net 980 else: 981 return NO_MODEL_UPDATE, net
Updates the scheduler
This increments the scheduler, but if we are automatically sweeping to find the best initial learning rate for a new set of dendrites this function also triggers the network at addition time to try the next value.
Process for finding best initial learning rate for dendrites:
- Start at default rate
- Learn at that rate until scheduler increments twice
- Save that version, start dendrites at LR current increment - 1
- Repeat 2 and 3 until version has worse final score at set LR
- Load previous model with best accuracy at that LR as initial rate
Parameters
- net (torch.nn.Module): The neural network model being trained.
- accuracy (float): The accuracy of the model at the current learning rate.
- epochs_since_cycle_switch (int): The number of epochs since the last cycle switch.
Returns
- int: The status of restructuring or training completion.
- torch.nn.Module: The potentially modified neural network model.
984class PAINeuronModuleTracker: 985 """ 986 Manager class that tracks all neuron layers and dendrite layers, 987 controls when new dendrites are added, and communicates signals to modules. 988 """ 989 990 def __init__( 991 self, 992 doing_pai, 993 save_name, 994 making_graphs=True, 995 param_vals_setting=-1, 996 values_per_train_epoch=-1, 997 values_per_val_epoch=-1, 998 ): 999 """Initialize the tracker 1000 1001 Parameters 1002 ---------- 1003 doing_pai : bool 1004 Whether or not dendrites should be used. 1005 save_name : str 1006 The base name for saving models and graphs. 1007 making_graphs : bool, optional 1008 Whether or not to generate graphs, by default True. 1009 param_vals_setting : int, optional 1010 Parameter values setting, by default -1. 1011 values_per_train_epoch : int, optional 1012 The number of values to look back for graphing 1013 during training, by default -1 (all values). 1014 values_per_val_epoch : int, optional 1015 The number of values to look back for graphing 1016 during validation, by default -1 (all values). 1017 Returns 1018 ------- 1019 None 1020 """ 1021 1022 # Dict of member vars and their types for saving 1023 self.member_vars = {} 1024 self.member_var_types = {} 1025 1026 # Whether or not PAI will be running 1027 self.member_vars["doing_pai"] = doing_pai 1028 self.member_var_types["doing_pai"] = "bool" 1029 1030 # How many Dendrites have been added 1031 self.member_vars["num_dendrites_added"] = 0 1032 self.member_var_types["num_dendrites_added"] = "int" 1033 1034 # How many Dendrites have been successfully integrated, does not count currently training dendrites 1035 self.member_vars["num_dendrites_integrated"] = 0 1036 self.member_var_types["num_dendrites_integrated"] = "int" 1037 1038 # How many cycles have been run, *2 or *2+1 of the above 1039 self.member_vars["num_cycles"] = 0 1040 self.member_var_types["num_cycles"] = "int" 1041 1042 # Pointers to all neuron wrapped modules 1043 self.neuron_module_vector = [] 1044 1045 # Pointers to all non neuron modules for tracking 1046 self.tracked_neuron_module_vector = [] 1047 1048 # Neuron training or dendrite training mode 1049 self.member_vars["mode"] = "n" 1050 self.member_var_types["mode"] = "string" 1051 1052 # Number of epochs run excluding overwritten epochs 1053 self.member_vars["num_epochs_run"] = -1 1054 self.member_var_types["num_epochs_run"] = "int" 1055 1056 # Number including overwritten epochs 1057 self.member_vars["total_epochs_run"] = -1 1058 self.member_var_types["total_epochs_run"] = "int" 1059 1060 # Last epoch that validation/correlation score was improved 1061 self.member_vars["epoch_last_improved"] = 0 1062 self.member_var_types["epoch_last_improved"] = "int" 1063 1064 # Running validation accuracy 1065 self.member_vars["running_accuracy"] = 0 1066 self.member_var_types["running_accuracy"] = "float" 1067 1068 # True if maxing validation, False if minimizing Loss 1069 self.member_vars["maximizing_score"] = True 1070 self.member_var_types["maximizing_score"] = "bool" 1071 1072 # Mode for switching back and forth between learning modes 1073 self.member_vars["switch_mode"] = GPA.pc.get_switch_mode() 1074 self.member_var_types["switch_mode"] = "int" 1075 1076 # Epoch of the last switch 1077 self.member_vars["last_switch"] = 0 1078 self.member_var_types["last_switch"] = "int" 1079 1080 # Highest validation score from current cycle 1081 self.member_vars["current_best_validation_score"] = 0 1082 self.member_var_types["current_best_validation_score"] = "float" 1083 1084 # Last epoch where the learning rate was updated 1085 self.member_vars["initial_lr_test_epoch_count"] = -1 1086 self.member_var_types["initial_lr_test_epoch_count"] = "int" 1087 1088 # Highest validation score of full run 1089 self.member_vars["global_best_validation_score"] = 0 1090 self.member_var_types["global_best_validation_score"] = "float" 1091 1092 # List of switch epochs 1093 self.member_vars["switch_epochs"] = [] 1094 self.member_var_types["switch_epochs"] = "int array" 1095 1096 # Parameter counts at each network structure 1097 self.member_vars["param_counts"] = [] 1098 self.member_var_types["param_counts"] = "int array" 1099 1100 # List of epochs where switch was made to neuron training 1101 self.member_vars["n_switch_epochs"] = [] 1102 self.member_var_types["n_switch_epochs"] = "int array" 1103 1104 # List of epochs where switch was made to dendrite training 1105 self.member_vars["p_switch_epochs"] = [] 1106 self.member_var_types["p_switch_epochs"] = "int array" 1107 1108 # List of validation accuracies 1109 self.member_vars["accuracies"] = [] 1110 self.member_var_types["accuracies"] = "float array" 1111 1112 # List of epochs where score improved for scheduler updates 1113 self.member_vars["last_improved_accuracies"] = [] 1114 self.member_var_types["last_improved_accuracies"] = "int array" 1115 1116 # List of test accuracy scores registered 1117 self.member_vars["test_accuracies"] = [] 1118 self.member_var_types["test_accuracies"] = "float array" 1119 1120 # List of accuracies registered during neuron training 1121 self.member_vars["n_accuracies"] = [] 1122 self.member_var_types["n_accuracies"] = "float array" 1123 1124 # List of accuracies registered during dendrite training 1125 self.member_vars["p_accuracies"] = [] 1126 self.member_var_types["p_accuracies"] = "float array" 1127 1128 # Running average accuracies from recent epochs 1129 self.member_vars["running_accuracies"] = [] 1130 self.member_var_types["running_accuracies"] = "float array" 1131 1132 # List of additional scores recorded 1133 self.member_vars["extra_scores"] = {} 1134 self.member_var_types["extra_scores"] = "float array dictionary" 1135 1136 # Extra scores not set to be graphed 1137 self.member_vars["extra_scores_without_graphing"] = {} 1138 self.member_var_types["extra_scores_without_graphing"] = ( 1139 "float array dictionary" 1140 ) 1141 1142 # List of test scores 1143 self.member_vars["test_scores"] = [] 1144 self.member_var_types["test_scores"] = "float array" 1145 1146 # Extra scores calculated during neuron training 1147 self.member_vars["n_extra_scores"] = {} 1148 self.member_var_types["n_extra_scores"] = "float array dictionary" 1149 1150 # List of training losses calculated 1151 self.member_vars["training_loss"] = [] 1152 self.member_var_types["training_loss"] = "float array" 1153 1154 # List of learning rates at each epoch 1155 self.member_vars["training_learning_rates"] = [] 1156 self.member_var_types["training_learning_rates"] = "float array" 1157 1158 # Best dendrite scores 1159 self.member_vars["best_scores"] = [] 1160 self.member_var_types["best_scores"] = "float array array" 1161 1162 # Current dendrite scores 1163 self.member_vars["current_scores"] = [] 1164 self.member_var_types["current_scores"] = "float array array" 1165 1166 # Times for neuron training epochs 1167 self.member_vars["n_epoch_times"] = [] 1168 self.member_var_types["n_epoch_times"] = "float array" 1169 1170 # Timing values 1171 self.member_vars["p_epoch_times"] = [] 1172 self.member_var_types["p_epoch_times"] = "float array" 1173 self.member_vars["n_train_times"] = [] 1174 self.member_var_types["n_train_times"] = "float array" 1175 self.member_vars["p_train_times"] = [] 1176 self.member_var_types["p_train_times"] = "float array" 1177 self.member_vars["n_val_times"] = [] 1178 self.member_var_types["n_val_times"] = "float array" 1179 self.member_vars["p_val_times"] = [] 1180 self.member_var_types["p_val_times"] = "float array" 1181 1182 # Setting for tracking timing 1183 self.member_vars["manual_train_switch"] = False 1184 self.member_var_types["manual_train_switch"] = "bool" 1185 1186 # Tracking scores overwritten when reloading best model 1187 self.member_vars["overwritten_extras"] = [] 1188 self.member_var_types["overwritten_extras"] = "float array dictionary array" 1189 self.member_vars["overwritten_vals"] = [] 1190 self.member_var_types["overwritten_vals"] = "float array array" 1191 self.member_vars["overwritten_epochs"] = 0 1192 self.member_var_types["overwritten_epochs"] = "int" 1193 1194 # Setting for determining scores 1195 self.member_vars["param_vals_setting"] = GPA.pc.get_param_vals_setting() 1196 self.member_var_types["param_vals_setting"] = "int" 1197 1198 # Optimizer and scheduler types and instances 1199 self.member_vars["optimizer"] = None 1200 self.member_var_types["optimizer"] = "type" 1201 self.member_vars["scheduler"] = None 1202 self.member_var_types["scheduler"] = "type" 1203 self.member_vars["optimizer_instance"] = None 1204 self.member_var_types["optimizer_instance"] = "empty array" 1205 self.member_vars["scheduler_instance"] = None 1206 self.member_var_types["scheduler_instance"] = "empty array" 1207 1208 # Flag for if the tracker was loaded 1209 self.loaded = False 1210 1211 # flag for 1212 self.member_vars["step_status"] = STEP_CLEARED 1213 self.member_var_types["step_status"] = "int" 1214 1215 1216 # Settings for tracking learning rates 1217 self.member_vars["current_n_learning_rate_initial_skip_steps"] = 0 1218 self.member_var_types["current_n_learning_rate_initial_skip_steps"] = "int" 1219 self.member_vars["last_max_learning_rate_steps"] = 0 1220 self.member_var_types["last_max_learning_rate_steps"] = "int" 1221 self.member_vars["last_max_learning_rate_value"] = -1 1222 self.member_var_types["last_max_learning_rate_value"] = "float" 1223 self.member_vars["current_cycle_lr_max_scores"] = [] 1224 self.member_var_types["current_cycle_lr_max_scores"] = "float array" 1225 self.member_vars["current_step_count"] = 0 1226 self.member_var_types["current_step_count"] = "int" 1227 self.member_vars["committed_to_initial_rate"] = True 1228 self.member_var_types["committed_to_initial_rate"] = "bool" 1229 self.member_vars["best_mean_score_improved_this_epoch"] = 0 1230 self.member_var_types["best_mean_score_improved_this_epoch"] = "int" 1231 1232 # Flag for if current dendrite achieved highest global score 1233 self.member_vars["current_n_set_global_best"] = True 1234 self.member_var_types["current_n_set_global_best"] = "bool" 1235 1236 # Number of tries adding this dendrite count 1237 self.member_vars["num_dendrite_tries"] = 0 1238 self.member_var_types["num_dendrite_tries"] = "int" 1239 1240 # Count of batches per epoch 1241 self.values_per_train_epoch = values_per_train_epoch 1242 self.values_per_val_epoch = values_per_val_epoch 1243 1244 self.save_name = save_name 1245 self.making_graphs = making_graphs 1246 1247 self.start_time = time.time() 1248 self.saved_time = 0 1249 self.start_epoch(internal_call=True) 1250 1251 if GPA.pc.get_verbose(): 1252 print(f'Initializing with switch_mode {self.member_vars["switch_mode"]}') 1253 1254 def to_string(self): 1255 """Convert tracker values to string for saving with safetensors. 1256 1257 Parameters 1258 ---------- 1259 None 1260 1261 Returns 1262 ------- 1263 str 1264 Serialized tracker state suitable for storage in a safetensors field. 1265 """ 1266 1267 full_string = "" 1268 for var in self.member_vars: 1269 full_string += var + "," 1270 if self.member_vars[var] is None: 1271 full_string += "None" 1272 full_string += "\n" 1273 elif self.member_var_types[var] == "bool": 1274 full_string += str(self.member_vars[var]) 1275 full_string += "\n" 1276 elif self.member_var_types[var] in ("int", "float", "string"): 1277 full_string += str(self.member_vars[var]) 1278 full_string += "\n" 1279 elif self.member_var_types[var] == "type": 1280 name = ( 1281 self.member_vars[var].__module__ 1282 + "." 1283 + self.member_vars[var].__name__ 1284 ) 1285 full_string += str(self.member_vars[var]) 1286 full_string += "\n" 1287 elif self.member_var_types[var] == "empty array": 1288 full_string += "[]" 1289 full_string += "\n" 1290 elif self.member_var_types[var] in ("int array", "float array"): 1291 full_string += "\n" 1292 string = "" 1293 for val in self.member_vars[var]: 1294 string += str(val) + "," 1295 # Remove the last comma 1296 string = string[:-1] 1297 full_string += string 1298 full_string += "\n" 1299 elif self.member_var_types[var] == "float array dictionary array": 1300 full_string += "\n" 1301 for array in self.member_vars[var]: 1302 for key in array: 1303 string = key + "," 1304 for val in array[key]: 1305 string += str(val) + "," 1306 # Remove the last comma 1307 string = string[:-1] 1308 full_string += string 1309 full_string += "\n" 1310 full_string += "endkey" 1311 full_string += "\n" 1312 full_string += "endarray" 1313 full_string += "\n" 1314 elif self.member_var_types[var] == "float array dictionary": 1315 full_string += "\n" 1316 for key in self.member_vars[var]: 1317 string = key + "," 1318 for val in self.member_vars[var][key]: 1319 string += str(val) + "," 1320 # Remove the last comma 1321 string = string[:-1] 1322 full_string += string 1323 full_string += "\n" 1324 full_string += "end" 1325 full_string += "\n" 1326 elif self.member_var_types[var] == "float array array": 1327 full_string += "\n" 1328 for array in self.member_vars[var]: 1329 string = "" 1330 for val in array: 1331 string += str(val) + "," 1332 # Remove the last comma 1333 string = string[:-1] 1334 full_string += string 1335 full_string += "\n" 1336 full_string += "end" 1337 full_string += "\n" 1338 else: 1339 print("Did not find a member variable") 1340 pdb.set_trace() 1341 return full_string 1342 1343 def from_string(self, string): 1344 """Load tracker values from string. 1345 1346 Parameters 1347 ---------- 1348 string : str 1349 The string to load from. 1350 1351 Returns 1352 ------- 1353 None 1354 This function does not return a value. 1355 """ 1356 f = io.StringIO(string) 1357 while True: 1358 line = f.readline() 1359 if not line: 1360 break 1361 vals = line.split(",") 1362 var = vals[0] 1363 1364 if self.member_var_types[var] == "bool": 1365 val = vals[1][:-1] 1366 if val == "True": 1367 self.member_vars[var] = True 1368 elif val == "False": 1369 self.member_vars[var] = False 1370 elif val == "1": 1371 self.member_vars[var] = 1 1372 elif val == "0": 1373 self.member_vars[var] = 0 1374 else: 1375 print("Something went wrong with loading") 1376 pdb.set_trace() 1377 elif self.member_var_types[var] == "int": 1378 val = vals[1] 1379 self.member_vars[var] = int(val) 1380 elif self.member_var_types[var] == "float": 1381 val = vals[1] 1382 self.member_vars[var] = float(val) 1383 elif self.member_var_types[var] == "string": 1384 val = vals[1][:-1] 1385 self.member_vars[var] = val 1386 elif self.member_var_types[var] == "type": 1387 # Ignore loading types, tracker should have them set up 1388 continue 1389 elif self.member_var_types[var] == "empty array": 1390 val = vals[1] 1391 self.member_vars[var] = [] 1392 elif self.member_var_types[var] == "int array": 1393 vals = f.readline()[:-1].split(",") 1394 self.member_vars[var] = [] 1395 if vals[0] == "": 1396 continue 1397 for val in vals: 1398 self.member_vars[var].append(int(val)) 1399 elif self.member_var_types[var] == "float array": 1400 vals = f.readline()[:-1].split(",") 1401 self.member_vars[var] = [] 1402 if vals[0] == "": 1403 continue 1404 for val in vals: 1405 self.member_vars[var].append(float(val)) 1406 elif self.member_var_types[var] == "float array dictionary array": 1407 self.member_vars[var] = [] 1408 line2 = f.readline()[:-1] 1409 while line2 != "endarray": 1410 temp = {} 1411 while line2 != "endkey": 1412 vals = line2.split(",") 1413 name = vals[0] 1414 temp[name] = [] 1415 vals = vals[1:] 1416 for val in vals: 1417 temp[name].append(float(val)) 1418 line2 = f.readline()[:-1] 1419 self.member_vars[var].append(temp) 1420 line2 = f.readline()[:-1] 1421 elif self.member_var_types[var] == "float array dictionary": 1422 self.member_vars[var] = {} 1423 line2 = f.readline()[:-1] 1424 while line2 != "end": 1425 vals = line2.split(",") 1426 name = vals[0] 1427 self.member_vars[var][name] = [] 1428 vals = vals[1:] 1429 for val in vals: 1430 self.member_vars[var][name].append(float(val)) 1431 line2 = f.readline()[:-1] 1432 elif self.member_var_types[var] == "float array array": 1433 self.member_vars[var] = [] 1434 line2 = f.readline()[:-1] 1435 while line2 != "end": 1436 vals = line2.split(",") 1437 self.member_vars[var].append([]) 1438 if line2: 1439 for val in vals: 1440 self.member_vars[var][-1].append(float(val)) 1441 line2 = f.readline()[:-1] 1442 else: 1443 print("Did not find a member variable") 1444 1445 pdb.set_trace() 1446 1447 def from_string_debug(self, string): 1448 """Debug function to print tracker values from string without loading them. 1449 1450 Parameters 1451 ---------- 1452 string : str 1453 The string to debug load from. 1454 1455 Returns 1456 ------- 1457 None 1458 This function does not return a value. 1459 """ 1460 f = io.StringIO(string) 1461 print("=== DEBUGGING TRACKER VARIABLES ===") 1462 1463 while True: 1464 line = f.readline() 1465 if not line: 1466 break 1467 vals = line.split(",") 1468 var = vals[0] 1469 1470 print(f"\nVariable: {var}") 1471 print(f"Type: {self.member_var_types.get(var, 'UNKNOWN TYPE')}") 1472 print(f"Current value: {self.member_vars.get(var, 'NOT SET')}") 1473 1474 if self.member_var_types.get(var) == "bool": 1475 val = vals[1][:-1] 1476 print(f"Would set to: {val} -> {val == 'True'}") 1477 1478 elif self.member_var_types.get(var) == "int": 1479 val = vals[1] 1480 print(f"Would set to: {int(val)}") 1481 1482 elif self.member_var_types.get(var) == "float": 1483 val = vals[1] 1484 print(f"Would set to: {float(val)}") 1485 1486 elif self.member_var_types.get(var) == "string": 1487 val = vals[1][:-1] 1488 print(f"Would set to: '{val}'") 1489 1490 elif self.member_var_types.get(var) == "type": 1491 print("Would skip (type loading)") 1492 1493 elif self.member_var_types.get(var) == "empty array": 1494 val = vals[1] 1495 print(f"Would set to: [] (empty array)") 1496 1497 elif self.member_var_types.get(var) == "int array": 1498 vals_line = f.readline()[:-1].split(",") 1499 print(f"Would set to int array with {len(vals_line)} elements:") 1500 if vals_line[0] != "": 1501 print( 1502 f" Elements: {vals_line[:5]}{'...' if len(vals_line) > 5 else ''}" 1503 ) 1504 else: 1505 print(" Empty array") 1506 1507 elif self.member_var_types.get(var) == "float array": 1508 vals_line = f.readline()[:-1].split(",") 1509 print(f"Would set to float array with {len(vals_line)} elements:") 1510 if vals_line[0] != "": 1511 print( 1512 f" Elements: {vals_line[:5]}{'...' if len(vals_line) > 5 else ''}" 1513 ) 1514 else: 1515 print(" Empty array") 1516 1517 elif self.member_var_types.get(var) == "float array dictionary array": 1518 print("Would process float array dictionary array:") 1519 array_count = 0 1520 line2 = f.readline()[:-1] 1521 while line2 != "endarray": 1522 key_count = 0 1523 while line2 != "endkey": 1524 vals_dict = line2.split(",") 1525 name = vals_dict[0] 1526 print( 1527 f" Array {array_count}, Key '{name}': {len(vals_dict)-1} elements" 1528 ) 1529 key_count += 1 1530 line2 = f.readline()[:-1] 1531 print(f" Array {array_count} has {key_count} keys") 1532 array_count += 1 1533 line2 = f.readline()[:-1] 1534 print(f" Total arrays: {array_count}") 1535 1536 elif self.member_var_types.get(var) == "float array dictionary": 1537 print("Would process float array dictionary:") 1538 line2 = f.readline()[:-1] 1539 key_count = 0 1540 while line2 != "end": 1541 vals_dict = line2.split(",") 1542 name = vals_dict[0] 1543 print(f" Key '{name}': {len(vals_dict)-1} elements") 1544 key_count += 1 1545 line2 = f.readline()[:-1] 1546 print(f" Total keys: {key_count}") 1547 1548 elif self.member_var_types.get(var) == "float array array": 1549 print("Would process float array array:") 1550 line2 = f.readline()[:-1] 1551 array_count = 0 1552 while line2 != "end": 1553 if line2: 1554 vals_array = line2.split(",") 1555 print(f" Array {array_count}: {len(vals_array)} elements") 1556 else: 1557 print(f" Array {array_count}: empty") 1558 array_count += 1 1559 line2 = f.readline()[:-1] 1560 print(f" Total arrays: {array_count}") 1561 1562 else: 1563 print(f"UNKNOWN TYPE: {self.member_var_types.get(var, 'NOT FOUND')}") 1564 1565 print("\n=== END DEBUG ===") 1566 1567 def save_tracker_settings(self): 1568 """Save tracker settings for DistributedDataParallel use. 1569 1570 Saves settings in save_name/array_dims.csv 1571 1572 Parameters 1573 ---------- 1574 None 1575 Returns 1576 ------- 1577 None 1578 1579 ----- 1580 Instructions for use are in API customization.md 1581 """ 1582 if not os.path.isdir(self.save_name): 1583 os.makedirs(self.save_name) 1584 f = open(self.save_name + "/array_dims.csv", "w") 1585 for layer in self.neuron_module_vector: 1586 dv = layer.dendrite_module.dendrite_values[0] 1587 shape_str = ",".join(str(s) for s in dv.dendrite_storage_shape.tolist()) 1588 f.write(f"{layer.name},{shape_str}\n") 1589 f.close() 1590 if not GPA.pc.get_silent(): 1591 print("Tracker settings saved.") 1592 print("You may now delete save_tracker_settings") 1593 1594 def initialize_tracker_settings(self): 1595 """Initialize tracker settings from saved file. 1596 1597 This function loads tracker settings from a CSV file and applies them 1598 to the layers the tracker is managing. 1599 1600 Parameters 1601 ---------- 1602 None 1603 1604 Returns 1605 ------- 1606 None 1607 1608 """ 1609 1610 channels = {} 1611 if not os.path.exists(self.save_name + "/array_dims.csv"): 1612 print( 1613 "You must call save_tracker_settings before " 1614 "initialize_tracker_settings" 1615 ) 1616 print("Follow instructions in customization.md") 1617 pdb.set_trace() 1618 f = open(self.save_name + "/array_dims.csv", "r") 1619 for line in f: 1620 parts = line.strip().split(",") 1621 channels[parts[0]] = [int(s) for s in parts[1:]] 1622 for layer in self.neuron_module_vector: 1623 dv = layer.dendrite_module.dendrite_values[0] 1624 dv.setup_arrays(channels[layer.name]) 1625 1626 def set_optimizer_instance(self, optimizer_instance, additional_optimizers=[]): 1627 """Set optimizer instance directly. 1628 1629 Parameters 1630 ---------- 1631 optimizer_instance : object 1632 The optimizer instance to set. 1633 1634 Returns 1635 ------- 1636 None 1637 1638 """ 1639 # This call must be first before the parameters get filtered. 1640 optimizer_instance.zero_grad() 1641 try: 1642 for param_group in optimizer_instance.param_groups: 1643 if ( 1644 param_group["weight_decay"] > 0 1645 and GPA.pc.get_weight_decay_accepted() is False 1646 ): 1647 _pai_log( 1648 "warning", 1649 "For PAI training it is recommended to not use weight decay in your optimizer", 1650 ) 1651 1652 except: 1653 pass 1654 self.member_vars["optimizer_instance"] = optimizer_instance 1655 if GPA.pc.get_perforated_backpropagation(): 1656 TPB.setup_optimizer_pb(self.member_vars["optimizer_instance"]) 1657 for optimizer in additional_optimizers: 1658 TPB.filter_params(optimizer) 1659 1660 def set_optimizer(self, optimizer): 1661 """Set optimizer type to be initialized later 1662 1663 Parameters 1664 ---------- 1665 optimizer : object 1666 The optimizer type to set. 1667 1668 Returns 1669 ------- 1670 None 1671 1672 """ 1673 self.member_vars["optimizer"] = optimizer 1674 1675 def set_scheduler(self, scheduler): 1676 """Set scheduler type to be initialized later 1677 1678 Parameters 1679 ---------- 1680 scheduler : object 1681 The scheduler type to set. 1682 1683 Returns 1684 ------- 1685 None 1686 1687 """ 1688 if scheduler is not torch.optim.lr_scheduler.ReduceLROnPlateau: 1689 if GPA.pc.get_verbose(): 1690 print("Not using ReduceLROnPlateau, this is not recommended") 1691 self.member_vars["scheduler"] = scheduler 1692 1693 def increment_scheduler(self, num_ticks, mode): 1694 """Increment the scheduler a set number of times. 1695 1696 Used for finding best initial learning rate when adding dendrites. 1697 1698 Parameters 1699 ---------- 1700 num_ticks : int 1701 The number of scheduler steps to take. 1702 mode : str 1703 The mode for stepping the scheduler. Options are: 1704 - "step_learning_rate": Step based on improved accuracy epochs 1705 - "increment_epoch_count": Step based on total epoch count 1706 1707 Returns 1708 ------- 1709 current_steps : int 1710 The number of learning rate changes that occurred. 1711 learning_rate1 : float 1712 The final learning rate after stepping. 1713 1714 """ 1715 1716 current_steps = 0 1717 current_ticker = 0 1718 1719 for param_group in GPA.pai_tracker.member_vars[ 1720 "optimizer_instance" 1721 ].param_groups: 1722 learning_rate1 = param_group["lr"] 1723 1724 if GPA.pc.get_verbose(): 1725 print("Using scheduler:") 1726 print(type(self.member_vars["scheduler_instance"])) 1727 1728 while current_ticker < num_ticks: 1729 if GPA.pc.get_verbose(): 1730 print( 1731 f"Lower start rate initial {learning_rate1} " 1732 f'stepping {GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]} times' 1733 ) 1734 1735 if ( 1736 type(self.member_vars["scheduler_instance"]) 1737 is torch.optim.lr_scheduler.ReduceLROnPlateau 1738 ): 1739 if mode == "step_learning_rate": 1740 # Step with counter as last improved accuracy 1741 self.member_vars["scheduler_instance"].step( 1742 metrics=self.member_vars["last_improved_accuracies"][ 1743 GPA.pai_tracker.steps_after_switch() - 1 1744 ] 1745 ) 1746 elif mode == "increment_epoch_count": 1747 # Step with improved epoch counts up to current location 1748 self.member_vars["scheduler_instance"].step( 1749 metrics=self.member_vars["last_improved_accuracies"][ 1750 -((num_ticks - 1) - current_ticker) - 1 1751 ] 1752 ) 1753 else: 1754 self.member_vars["scheduler_instance"].step() 1755 1756 for param_group in GPA.pai_tracker.member_vars[ 1757 "optimizer_instance" 1758 ].param_groups: 1759 learning_rate2 = param_group["lr"] 1760 1761 if learning_rate2 != learning_rate1: 1762 current_steps += 1 1763 learning_rate1 = learning_rate2 1764 if mode == "step_learning_rate": 1765 current_ticker += 1 1766 if GPA.pc.get_verbose(): 1767 print(f"1 step {current_steps} to {learning_rate2}") 1768 1769 if mode == "increment_epoch_count": 1770 current_ticker += 1 1771 1772 return current_steps, learning_rate1 1773 1774 def setup_optimizer(self, net, opt_args, sched_args=None, parameters=None): 1775 """Initialize the optimizer and scheduler when added. 1776 1777 Parameters 1778 ---------- 1779 net : object 1780 The neural network model. 1781 opt_args : dict 1782 The arguments for the optimizer. 1783 sched_args : dict, optional 1784 The arguments for the scheduler, by default None. 1785 1786 Returns 1787 ------- 1788 optimizer : object 1789 The initialized optimizer instance. 1790 scheduler : object or None 1791 The initialized scheduler instance, or None if no scheduler was set. 1792 1793 """ 1794 if "weight_decay" in opt_args and not GPA.pc.get_weight_decay_accepted(): 1795 _pai_log( 1796 "warning", 1797 "For PAI training it is recommended to not use weight decay in your optimizer", 1798 ) 1799 1800 if ("model" not in opt_args.keys()) and "params" not in opt_args.keys(): 1801 print("In setup_optimizer it will be depreciated to not pass in params yourself in the future") 1802 print("please change the settings to include params") 1803 if self.member_vars["mode"] == "n": 1804 if parameters is not None: 1805 opt_args["params"] = parameters 1806 else: 1807 opt_args["params"] = filter(lambda p: p.requires_grad, net.parameters()) 1808 else: 1809 params = UPA.get_pai_network_params(net) 1810 if parameters is not None: 1811 # Filter parameters to only those in params, preserving weight_decay 1812 params_set = set(params) 1813 filtered_params = [] 1814 for param_group in parameters: 1815 filtered_group_params = [p for p in param_group["params"] if p in params_set] 1816 if filtered_group_params: 1817 filtered_params.append({ 1818 "params": filtered_group_params, 1819 "weight_decay": param_group["weight_decay"] 1820 }) 1821 opt_args["params"] = filtered_params 1822 else: 1823 opt_args["params"] = params 1824 elif "params" in opt_args.keys(): 1825 # Check if params is a list of param groups (dicts) or a single param group 1826 params_value = opt_args["params"] 1827 if isinstance(params_value, list) and len(params_value) > 0: 1828 # Check if it's a list of dicts (multiple param groups) or list of tensors (single group) 1829 if isinstance(params_value[0], dict): 1830 # Multiple param groups format: [{"params": [...], "lr": ...}, ...] 1831 # Filter each param group for requires_grad 1832 filtered_param_groups = [] 1833 for param_group in params_value: 1834 filtered_group_params = [p for p in param_group["params"] if p.requires_grad] 1835 if filtered_group_params: 1836 new_group = param_group.copy() 1837 new_group["params"] = filtered_group_params 1838 filtered_param_groups.append(new_group) 1839 opt_args["params"] = filtered_param_groups 1840 else: 1841 # Single param group format: [tensor1, tensor2, ...] or generator 1842 # Filter for requires_grad 1843 opt_args["params"] = [p for p in params_value if p.requires_grad] 1844 elif hasattr(params_value, '__iter__'): 1845 # Handle generators or other iterables 1846 opt_args["params"] = [p for p in params_value if p.requires_grad] 1847 1848 optimizer = self.member_vars["optimizer"](**opt_args) 1849 self.set_optimizer_instance(optimizer) 1850 1851 if self.member_vars["scheduler"] is not None: 1852 # Handle SequentialLR specially 1853 if self.member_vars["scheduler"] is torch.optim.lr_scheduler.SequentialLR: 1854 """ 1855 sched_args should be a dict with "schedulers" (list of tuples) and "milestones" 1856 For example: 1857 sequential_schedArgs = { 1858 "schedulers": [ 1859 (warmup_scheduler_class, warmup_schedArgs), 1860 (main_scheduler_class, main_schedArgs) 1861 ], 1862 "milestones": [switch_epoch] 1863 } 1864 """ 1865 schedulers = [] 1866 milestones = sched_args.get("milestones", []) 1867 scheduler_configs = sched_args.get("schedulers", []) 1868 1869 for scheduler_class, scheduler_args in scheduler_configs: 1870 schedulers.append(scheduler_class(optimizer, **scheduler_args)) 1871 1872 self.member_vars["scheduler_instance"] = torch.optim.lr_scheduler.SequentialLR( 1873 optimizer, schedulers=schedulers, milestones=milestones 1874 ) 1875 else: 1876 self.member_vars["scheduler_instance"] = self.member_vars["scheduler"]( 1877 optimizer, **sched_args 1878 ) 1879 current_steps = 0 1880 1881 for param_group in GPA.pai_tracker.member_vars[ 1882 "optimizer_instance" 1883 ].param_groups: 1884 learning_rate1 = param_group["lr"] 1885 1886 if GPA.pc.get_verbose(): 1887 print( 1888 f"Resetting scheduler with {GPA.pai_tracker.steps_after_switch()} " 1889 f"steps and {GPA.pc.get_initial_history_after_switches()} initial ticks to skip" 1890 ) 1891 1892 # Find setting of previously used learning rate before adding dendrites 1893 if ( 1894 GPA.pai_tracker.member_vars[ 1895 "current_n_learning_rate_initial_skip_steps" 1896 ] 1897 != 0 1898 ): 1899 additional_steps, learning_rate1 = self.increment_scheduler( 1900 GPA.pai_tracker.member_vars[ 1901 "current_n_learning_rate_initial_skip_steps" 1902 ], 1903 "step_learning_rate", 1904 ) 1905 current_steps += additional_steps 1906 1907 if self.member_vars["mode"] == "n" or GPA.pc.get_learn_dendrites_live(): 1908 initial = GPA.pc.get_initial_history_after_switches() 1909 else: 1910 initial = 0 1911 1912 if GPA.pai_tracker.steps_after_switch() > initial: 1913 # Minus extra 1 because this gets called after start epoch 1914 additional_steps, learning_rate1 = self.increment_scheduler( 1915 (GPA.pai_tracker.steps_after_switch() - initial) - 1, 1916 "increment_epoch_count", 1917 ) 1918 current_steps += additional_steps 1919 1920 if GPA.pc.get_verbose(): 1921 print( 1922 f"Scheduler update loop with {current_steps} " 1923 f"ended with {learning_rate1}" 1924 ) 1925 print( 1926 f"Scheduler ended with {current_steps} steps " 1927 f"and lr of {learning_rate1}" 1928 ) 1929 1930 self.member_vars["current_step_count"] = current_steps 1931 return optimizer, self.member_vars["scheduler_instance"] 1932 else: 1933 return optimizer, None 1934 1935 def clear_optimizer_and_scheduler(self): 1936 """Clear the instances for saving. 1937 1938 Parameters 1939 ---------- 1940 None 1941 1942 Returns 1943 ------- 1944 None 1945 This function does not return a value. 1946 """ 1947 self.member_vars["optimizer_instance"] = None 1948 self.member_vars["scheduler_instance"] = None 1949 1950 def switch_time(self): 1951 """Determine if it's time to switch between neuron and dendrite training. 1952 1953 Parameters 1954 ---------- 1955 None 1956 1957 Returns 1958 ------- 1959 bool 1960 True if it's time to switch, False otherwise. 1961 1962 Notes 1963 ----- 1964 Based on current settings and history of scores. 1965 """ 1966 1967 switch_phrase = "No mode, this should never be the case." 1968 switch_number = GPA.pc.get_n_epochs_to_switch() 1969 if self.member_vars["switch_mode"] == GPA.pc.DOING_SWITCH_EVERY_TIME: 1970 switch_phrase = "DOING_SWITCH_EVERY_TIME" 1971 elif self.member_vars["switch_mode"] == GPA.pc.DOING_HISTORY: 1972 switch_phrase = "DOING_HISTORY" 1973 elif self.member_vars["switch_mode"] == GPA.pc.DOING_FIXED_SWITCH: 1974 switch_phrase = "DOING_FIXED_SWITCH" 1975 switch_number = GPA.pc.get_fixed_switch_num() 1976 elif self.member_vars["switch_mode"] == GPA.pc.DOING_NO_SWITCH: 1977 switch_phrase = "DOING_NO_SWITCH" 1978 else: 1979 print( 1980 "A switch mode must be set. Check your settings for GPA.pc.set_switch_mode()." 1981 ) 1982 pdb.set_trace() 1983 if not GPA.pc.get_silent(): 1984 if(GPA.pc.get_perforated_backpropagation()): 1985 print( 1986 f'Checking PAI switch with mode {self.member_vars["mode"]}, ' 1987 f'switch mode {switch_phrase}, epoch {self.member_vars["num_epochs_run"]}, ' 1988 f'last improved epoch {self.member_vars["epoch_last_improved"]}, ' 1989 f'total epochs {self.member_vars["total_epochs_run"]}, ' 1990 f'n: {switch_number}, p: {GPA.pc.get_p_epochs_to_switch()}, ' 1991 f'num_cycles: {self.member_vars["num_cycles"]}' 1992 ) 1993 else: 1994 print( 1995 f'Checking PAI switch with mode {self.member_vars["mode"]}, ' 1996 f'switch mode {switch_phrase}, epoch {self.member_vars["num_epochs_run"]}, ' 1997 f'last improved epoch {self.member_vars["epoch_last_improved"]}, ' 1998 f'total epochs {self.member_vars["total_epochs_run"]}, ' 1999 f'n: {switch_number}, num_cycles: {self.member_vars["num_cycles"]}' 2000 ) 2001 print( 2002 f' Score tracking: current_n_set_global_best={self.member_vars["current_n_set_global_best"]}, ' 2003 f'global_best={self.member_vars["global_best_validation_score"]:.4f}, ' 2004 f'current_best={self.member_vars["current_best_validation_score"]:.4f}' 2005 ) 2006 if GPA.pc.get_perforated_backpropagation(): 2007 # this will fill in epoch last improved 2008 TPB.best_pai_score_improved_this_epoch(self) ## CLOSED ONLY 2009 if self.member_vars["switch_mode"] == GPA.pc.DOING_NO_SWITCH: 2010 if not GPA.pc.get_silent(): 2011 print("Returning False - doing no switch mode") 2012 return False 2013 2014 if self.member_vars["switch_mode"] == GPA.pc.DOING_SWITCH_EVERY_TIME: 2015 if not GPA.pc.get_silent(): 2016 print("Returning True - switching every time") 2017 return True 2018 2019 # Check if we're in the middle of learning rate optimization 2020 # If so, block ALL switch triggers until committed 2021 if GPA.pc.get_verbose(): 2022 print("=== LR Optimization Check ===") 2023 print(f' mode == "n": {self.member_vars["mode"] == "n"}') 2024 print(f" get_learn_dendrites_live(): {GPA.pc.get_learn_dendrites_live()}") 2025 print(f' committed_to_initial_rate: {GPA.pai_tracker.member_vars["committed_to_initial_rate"]}') 2026 print(f" get_dont_give_up_unless_learning_rate_lowered(): {GPA.pc.get_dont_give_up_unless_learning_rate_lowered()}") 2027 print(f' current_n_learning_rate_initial_skip_steps: {self.member_vars["current_n_learning_rate_initial_skip_steps"]}') 2028 print(f' last_max_learning_rate_steps: {self.member_vars["last_max_learning_rate_steps"]}') 2029 print(f' skip_steps < max_steps: {self.member_vars["current_n_learning_rate_initial_skip_steps"] < self.member_vars["last_max_learning_rate_steps"]}') 2030 print(f' scheduler is not None: {self.member_vars["scheduler"] is not None}') 2031 print("=============================") 2032 2033 if ( 2034 ((self.member_vars["mode"] == "n") or GPA.pc.get_learn_dendrites_live()) 2035 and (GPA.pai_tracker.member_vars["committed_to_initial_rate"] is False) 2036 and (GPA.pc.get_dont_give_up_unless_learning_rate_lowered()) 2037 and ( 2038 self.member_vars["current_n_learning_rate_initial_skip_steps"] 2039 <= self.member_vars["last_max_learning_rate_steps"] 2040 ) 2041 and self.member_vars["scheduler"] is not None 2042 ): 2043 if not GPA.pc.get_silent(): 2044 print( 2045 f"Returning False - learning rate optimization in progress. " 2046 f"Not committed yet. Comparing " 2047 f'initial {self.member_vars["current_n_learning_rate_initial_skip_steps"]} ' 2048 f'to last max {self.member_vars["last_max_learning_rate_steps"]}' 2049 ) 2050 return False 2051 2052 if len(self.member_vars["switch_epochs"]) == 0: 2053 this_count = self.member_vars["num_epochs_run"] 2054 else: 2055 this_count = ( 2056 self.member_vars["num_epochs_run"] 2057 - self.member_vars["switch_epochs"][-1] 2058 ) 2059 cap_switch = False 2060 if GPA.pc.get_perforated_backpropagation(): 2061 cap_switch = TPB.check_cap_switch(self, this_count) 2062 2063 if self.member_vars["switch_mode"] == GPA.pc.DOING_HISTORY and ( 2064 ( 2065 (self.member_vars["mode"] == "n") 2066 and ( 2067 self.member_vars["num_epochs_run"] 2068 - self.member_vars["epoch_last_improved"] 2069 >= GPA.pc.get_n_epochs_to_switch() 2070 ) 2071 and this_count 2072 >= GPA.pc.get_initial_history_after_switches() 2073 + GPA.pc.get_n_epochs_to_switch() 2074 ) 2075 or (GPA.pc.get_perforated_backpropagation() and TPB.history_switch(self)) 2076 or cap_switch 2077 ): 2078 if not GPA.pc.get_silent(): 2079 print("Returning True - History and last improved is hit") 2080 return True 2081 2082 if self.member_vars["switch_mode"] == GPA.pc.DOING_FIXED_SWITCH and ( 2083 ( 2084 self.member_vars["total_epochs_run"] % GPA.pc.get_fixed_switch_num() 2085 == GPA.pc.get_fixed_switch_num() - 1 2086 ) 2087 and self.member_vars["num_epochs_run"] 2088 >= GPA.pc.get_first_fixed_switch_num() - 1 2089 ): 2090 if not GPA.pc.get_silent(): 2091 print("Returning True - Fixed switch number is hit") 2092 return True 2093 2094 if not GPA.pc.get_silent(): 2095 print("Returning False - no triggers to switch have been hit") 2096 return False 2097 2098 def steps_after_switch(self): 2099 """Based on settings, return value for steps since a switch. 2100 2101 Different options for param vals setting determine what is returned. 2102 2103 Parameters 2104 ---------- 2105 None 2106 2107 Returns 2108 ------- 2109 int 2110 The number of epochs since the last switch, or total epochs run, 2111 depending on settings. 2112 2113 """ 2114 if self.member_vars["param_vals_setting"] == GPA.pc.PARAM_VALS_BY_TOTAL_EPOCH: 2115 return self.member_vars["num_epochs_run"] 2116 elif ( 2117 self.member_vars["param_vals_setting"] == GPA.pc.PARAM_VALS_BY_UPDATE_EPOCH 2118 ): 2119 return self.member_vars["num_epochs_run"] - self.member_vars["last_switch"] 2120 elif ( 2121 self.member_vars["param_vals_setting"] 2122 == GPA.pc.PARAM_VALS_BY_NEURON_EPOCH_START 2123 ): 2124 if self.member_vars["mode"] == "p": 2125 return ( 2126 self.member_vars["num_epochs_run"] - self.member_vars["last_switch"] 2127 ) 2128 else: 2129 return self.member_vars["num_epochs_run"] 2130 else: 2131 print( 2132 f'{self.member_vars["param_vals_setting"]} is not a valid param vals option' 2133 ) 2134 pdb.set_trace() 2135 2136 def add_pai_neuron_module(self, new_module, initial_add=True): 2137 """Add neuron modules to internal vectors. 2138 2139 Parameters 2140 ---------- 2141 new_module : object 2142 The new module to add. 2143 initial_add : bool, optional 2144 Whether this is the initial addition rather than loading from file 2145 2146 Returns 2147 ------- 2148 None 2149 2150 """ 2151 2152 # If it's a duplicate, ignore the second addition 2153 if new_module in self.neuron_module_vector: 2154 return 2155 self.neuron_module_vector.append(new_module) 2156 if self.member_vars["doing_pai"]: 2157 PA.set_wrapped_params(new_module) 2158 if initial_add: 2159 self.member_vars["best_scores"].append([]) 2160 self.member_vars["current_scores"].append([]) 2161 2162 def add_tracked_neuron_module(self, new_module, initial_add=True): 2163 """Add tracked modules to internal vectors 2164 2165 Parameters 2166 ---------- 2167 new_module : object 2168 The new module to add. 2169 initial_add : bool, optional 2170 Whether this is the initial addition rather than loading from file 2171 2172 Returns 2173 ------- 2174 None 2175 2176 """ 2177 # If it's a duplicate, ignore the second addition 2178 if new_module in self.tracked_neuron_module_vector: 2179 return 2180 self.tracked_neuron_module_vector.append(new_module) 2181 if self.member_vars["doing_pai"]: 2182 PA.set_tracked_params(new_module) 2183 2184 def reset_module_vector(self, net, load_from_restart): 2185 """Clear internal vectors and reset from network. 2186 2187 Parameters 2188 ---------- 2189 net : object 2190 The neural network model. 2191 load_from_restart : bool 2192 Whether loading from a restart file. 2193 2194 Returns 2195 ------- 2196 None 2197 2198 """ 2199 self.neuron_module_vector = [] 2200 self.tracked_neuron_module_vector = [] 2201 this_list = UPA.get_pai_modules(net, 0) 2202 for module in this_list: 2203 self.add_pai_neuron_module(module, initial_add=load_from_restart) 2204 this_list = UPA.get_tracked_modules(net, 0) 2205 for module in this_list: 2206 self.add_tracked_neuron_module(module, initial_add=load_from_restart) 2207 2208 def reset_vals_for_score_reset(self): 2209 """Reset cycle scores for new cycle. 2210 2211 Parameters 2212 ---------- 2213 None 2214 2215 Returns 2216 ------- 2217 None 2218 This function does not return a value. 2219 """ 2220 2221 if GPA.pc.get_find_best_lr(): 2222 self.member_vars["committed_to_initial_rate"] = False 2223 print("Resetting committed to initial rate to False") 2224 # If retaining all dendrties always say that the current dendrites set global best for saving and loading 2225 if GPA.pc.get_retain_all_dendrites(): 2226 self.member_vars["current_n_set_global_best"] = True 2227 self.member_vars["global_best_validation_score"] = 0 2228 else: 2229 self.member_vars["current_n_set_global_best"] = False 2230 2231 # Don't reset global best, but do reset current best 2232 self.member_vars["current_best_validation_score"] = 0 2233 self.member_vars["initial_lr_test_epoch_count"] = -1 2234 2235 def set_dendrite_training(self): 2236 """Signal all layers to start dendrite training. 2237 2238 Parameters 2239 ---------- 2240 None 2241 2242 Returns 2243 ------- 2244 None 2245 This function does not return a value. 2246 """ 2247 if GPA.pc.get_verbose(): 2248 print("Calling set_dendrite_training") 2249 2250 for layer in self.neuron_module_vector[:]: 2251 worked = layer.set_mode("p") 2252 """ 2253 worked is False when a layer was added to the neuron module vector 2254 but then it's never actually been used. This can happen when 2255 you have set a layer to have requires_grad = False or when 2256 you have a module as a member variable but it's not actually 2257 part of the network. Should be moved to be a tracked layer 2258 rather than a neuron layer. 2259 """ 2260 if not worked: 2261 self.neuron_module_vector.remove(layer) 2262 2263 for layer in self.tracked_neuron_module_vector[:]: 2264 worked = layer.set_mode("p") 2265 2266 self.create_new_dendrite_module() 2267 self.member_vars["mode"] = "p" 2268 self.member_vars["current_n_learning_rate_initial_skip_steps"] = 0 2269 2270 if GPA.pc.get_learn_dendrites_live(): 2271 self.reset_vals_for_score_reset() 2272 2273 self.member_vars["last_max_learning_rate_steps"] = self.member_vars[ 2274 "current_step_count" 2275 ] 2276 2277 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"] = [] 2278 GPA.pai_tracker.member_vars["num_cycles"] += 1 2279 2280 2281 def set_neuron_training(self): 2282 """Signal all layers to start neuron training. 2283 2284 Parameters 2285 ---------- 2286 None 2287 2288 Returns 2289 ------- 2290 None 2291 This function does not return a value. 2292 """ 2293 for module in self.neuron_module_vector: 2294 module.set_mode("n") 2295 for module in self.tracked_neuron_module_vector[:]: 2296 module.set_mode("n") 2297 2298 self.member_vars["mode"] = "n" 2299 self.member_vars["num_dendrites_added"] += 1 2300 self.member_vars["current_n_learning_rate_initial_skip_steps"] = 0 2301 self.reset_vals_for_score_reset() 2302 2303 self.member_vars["current_cycle_lr_max_scores"] = [] 2304 if GPA.pc.get_learn_dendrites_live(): 2305 self.member_vars["last_max_learning_rate_steps"] = self.member_vars[ 2306 "current_step_count" 2307 ] 2308 GPA.pai_tracker.member_vars["num_cycles"] += 1 2309 2310 if GPA.pc.get_reset_best_score_on_switch(): 2311 GPA.pai_tracker.member_vars["current_best_validation_score"] = 0 2312 GPA.pai_tracker.member_vars["running_accuracy"] = 0 2313 2314 def start_epoch(self, internal_call=False): 2315 """Perform steps for when a new training epoch is about to begin. 2316 2317 Parameters 2318 ---------- 2319 internal_call : bool, optional 2320 Whether this is an internal call or manual call 2321 2322 Returns 2323 ------- 2324 None 2325 2326 Notes 2327 ----- 2328 If you ever need to call this manually, set internal_call to False. 2329 2330 """ 2331 if self.member_vars["manual_train_switch"] and internal_call: 2332 return 2333 2334 if not internal_call and not self.member_vars["manual_train_switch"]: 2335 self.member_vars["manual_train_switch"] = True 2336 self.saved_time = 0 2337 self.member_vars["num_epochs_run"] = -1 2338 self.member_vars["total_epochs_run"] = -1 2339 2340 end = time.time() 2341 if self.member_vars["manual_train_switch"]: 2342 if self.saved_time != 0: 2343 if self.member_vars["mode"] == "p": 2344 self.member_vars["p_val_times"].append(end - self.saved_time) 2345 else: 2346 self.member_vars["n_val_times"].append(end - self.saved_time) 2347 2348 if self.member_vars["mode"] == "p": 2349 for layer in self.neuron_module_vector: 2350 for m in range(0, GPA.pc.get_global_candidates()): 2351 with torch.no_grad(): 2352 if GPA.pc.get_verbose(): 2353 print(f"Resetting score for {layer.name}") 2354 # Snapshot best_score before reset so we can compute per-epoch improvement 2355 layer.dendrite_module.dendrite_values[ 2356 m 2357 ].epoch_start_best_score.copy_( 2358 layer.dendrite_module.dendrite_values[ 2359 m 2360 ].best_score.detach() 2361 ) 2362 layer.dendrite_module.dendrite_values[ 2363 m 2364 ].best_score_improved_this_epoch = ( 2365 layer.dendrite_module.dendrite_values[ 2366 m 2367 ].best_score_improved_this_epoch 2368 * 0 2369 ) 2370 layer.dendrite_module.dendrite_values[ 2371 m 2372 ].nodes_best_improved_this_epoch = ( 2373 layer.dendrite_module.dendrite_values[ 2374 m 2375 ].nodes_best_improved_this_epoch 2376 * 0 2377 ) 2378 layer.dendrite_module.dendrite_values[ 2379 m 2380 ].nodes_improved_any = ( 2381 layer.dendrite_module.dendrite_values[ 2382 m 2383 ].nodes_improved_any 2384 * 0 2385 ) 2386 if GPA.pc.get_perforated_backpropagation(): 2387 self.member_vars["best_mean_score_improved_this_epoch"] = 0 2388 self.member_vars["num_epochs_run"] += 1 2389 self.member_vars["total_epochs_run"] = ( 2390 self.member_vars["num_epochs_run"] + self.member_vars["overwritten_epochs"] 2391 ) 2392 self.saved_time = end 2393 2394 def stop_epoch(self, internal_call=False): 2395 """Perform steps when a training epoch has completed. 2396 2397 Parameters 2398 ---------- 2399 internal_call : bool, optional 2400 Whether this is an internal call or manual call 2401 2402 Returns 2403 ------- 2404 None 2405 2406 Notes 2407 ----- 2408 If you ever need to call this manually, set internal_call to False. 2409 2410 """ 2411 end = time.time() 2412 if self.member_vars["manual_train_switch"] and internal_call: 2413 return 2414 2415 if self.member_vars["manual_train_switch"]: 2416 if self.member_vars["mode"] == "p": 2417 self.member_vars["p_train_times"].append(end - self.saved_time) 2418 else: 2419 self.member_vars["n_train_times"].append(end - self.saved_time) 2420 else: 2421 if self.member_vars["mode"] == "p": 2422 self.member_vars["p_epoch_times"].append(end - self.saved_time) 2423 else: 2424 self.member_vars["n_epoch_times"].append(end - self.saved_time) 2425 2426 self.saved_time = end 2427 2428 def initialize( 2429 self, 2430 model, 2431 doing_pai=True, 2432 save_name="PAI", 2433 making_graphs=True, 2434 maximizing_score=True, 2435 num_classes=10000, 2436 values_per_train_epoch=-1, 2437 values_per_val_epoch=-1, 2438 zooming_graph=True, 2439 ): 2440 """Setup the tracker with initial settings. 2441 2442 2443 Parameters 2444 ---------- 2445 model : object 2446 The neural network model. 2447 doing_pai : bool, optional 2448 Whether to add dendrites, by default True. 2449 save_name : str, optional 2450 The name under which to save the model. 2451 making_graphs : bool, optional 2452 Whether to make graphs, by default True. 2453 maximizing_score : bool, optional 2454 Whether to maximize the score, by default True. 2455 num_classes : int, optional 2456 The number of classes in the dataset, unused 2457 values_per_train_epoch : int, optional 2458 The number of values to look back for graphing 2459 during training, by default -1 (all values). 2460 values_per_val_epoch : int, optional 2461 The number of values to look back for graphing 2462 during validation, by default -1 (all values). 2463 zooming_graph : bool, optional 2464 Whether to zoom on graphs, by default True. 2465 2466 2467 Returns 2468 ------- 2469 nn.Module 2470 Converted model instance configured for the tracker settings. 2471 """ 2472 model = UPA.convert_network(model) 2473 self.member_vars["doing_pai"] = doing_pai 2474 self.member_vars["maximizing_score"] = maximizing_score 2475 self.save_name = save_name 2476 self.zooming_graph = zooming_graph 2477 self.making_graphs = making_graphs 2478 2479 if not self.loaded: 2480 self.member_vars["running_accuracy"] = (1.0 / num_classes) * 100 2481 2482 self.values_per_train_epoch = values_per_train_epoch 2483 self.values_per_val_epoch = values_per_val_epoch 2484 2485 if GPA.pc.get_testing_dendrite_capacity(): 2486 if not GPA.pc.get_silent(): 2487 print("Running a test of Dendrite Capacity.") 2488 GPA.pc.set_switch_mode(GPA.pc.DOING_SWITCH_EVERY_TIME) 2489 self.member_vars["switch_mode"] = GPA.pc.get_switch_mode() 2490 GPA.pc.set_retain_all_dendrites(True) 2491 GPA.pc.set_max_dendrite_tries(1000) 2492 GPA.pc.set_max_dendrites(1000) 2493 if GPA.pc.get_perforated_backpropagation(): 2494 GPA.pc.set_initial_correlation_batches(1) 2495 else: 2496 if not GPA.pc.get_silent(): 2497 print("Running Dendrite Experiment") 2498 return model 2499 2500 def generate_accuracy_plots(self, ax, save_folder, extra_string): 2501 """ 2502 Generate plots and csvs for accuracy 2503 2504 Parameters 2505 ---------- 2506 ax : object 2507 The matplotlib axis to plot on. 2508 save_folder : str 2509 The folder to save the plots and csvs in. 2510 extra_string : str 2511 An extra string to append to the filenames. 2512 2513 Returns 2514 ------- 2515 None 2516 2517 """ 2518 2519 # If scores are being saved for epochs that get overwritten, plot them 2520 for list_id in range(len(self.member_vars["overwritten_extras"])): 2521 for extra_id in self.member_vars["overwritten_extras"][list_id]: 2522 ax.plot( 2523 np.arange( 2524 len(self.member_vars["overwritten_extras"][list_id][extra_id]) 2525 ), 2526 self.member_vars["overwritten_extras"][list_id][extra_id], 2527 color=_PAI_PALETTE["train"], 2528 alpha=0.35, 2529 ) 2530 ax.plot( 2531 np.arange(len(self.member_vars["overwritten_vals"][list_id])), 2532 self.member_vars["overwritten_vals"][list_id], 2533 color=_PAI_PALETTE["validation"], 2534 alpha=0.35, 2535 ) 2536 2537 # Determine which accuracy vector to use 2538 if GPA.pc.get_drawing_pai(): 2539 accuracies = self.member_vars["accuracies"] 2540 else: 2541 accuracies = self.member_vars["n_accuracies"] 2542 2543 # Get pointer to additional scores being saved 2544 extra_scores = self.member_vars["extra_scores"] 2545 2546 # Plot the main accuracy scores 2547 ax.plot( 2548 np.arange(len(accuracies)), 2549 accuracies, 2550 label="Validation Scores", 2551 color=_PAI_PALETTE["validation"], 2552 ) 2553 ax.plot( 2554 np.arange(len(self.member_vars["running_accuracies"])), 2555 self.member_vars["running_accuracies"], 2556 label="Validation Running Scores", 2557 color=_PAI_PALETTE["validation"], 2558 alpha=0.4, 2559 ) 2560 2561 # Plot additional scores 2562 for extra_index, extra_score in enumerate(extra_scores): 2563 ax.plot( 2564 np.arange(len(extra_scores[extra_score])), 2565 extra_scores[extra_score], 2566 label=extra_score, 2567 color=_pai_extra_score_color(extra_score, extra_index), 2568 ) 2569 2570 plt.title(save_folder + "/" + self.save_name + "Scores") 2571 plt.xlabel("Epochs") 2572 plt.ylabel("Score") 2573 2574 # Add point at epoch last improved and best validation score 2575 if GPA.pc.get_drawing_pai(): 2576 ax.plot( 2577 self.member_vars["epoch_last_improved"], 2578 self.member_vars["global_best_validation_score"], 2579 "o", 2580 color=_PAI_PALETTE["marker_best"], 2581 label="Global best (y)", 2582 ) 2583 ax.plot( 2584 self.member_vars["epoch_last_improved"], 2585 accuracies[self.member_vars["epoch_last_improved"]], 2586 "o", 2587 color=_PAI_PALETTE["marker_improved"], 2588 label="Epoch Last Improved", 2589 ) 2590 else: 2591 if self.member_vars["mode"] == "n": 2592 missed_time = ( 2593 self.member_vars["num_epochs_run"] 2594 - self.member_vars["epoch_last_improved"] 2595 ) 2596 ax.plot( 2597 (len(self.member_vars["n_accuracies"]) - 1) - missed_time, 2598 self.member_vars["n_accuracies"][-(missed_time + 1)], 2599 "o", 2600 color=_PAI_PALETTE["marker_improved"], 2601 label="Epoch Last Improved", 2602 ) 2603 2604 # Generate csv file for the values graphed 2605 pd1 = pd.DataFrame( 2606 {"Epochs": np.arange(len(accuracies)), "Validation Scores": accuracies} 2607 ) 2608 pd2 = pd.DataFrame( 2609 { 2610 "Epochs": np.arange(len(self.member_vars["running_accuracies"])), 2611 "Validation Running Scores": self.member_vars["running_accuracies"], 2612 } 2613 ) 2614 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2615 for extra_score in extra_scores: 2616 pd2 = pd.DataFrame( 2617 { 2618 "Epochs": np.arange(len(extra_scores[extra_score])), 2619 extra_score: extra_scores[extra_score], 2620 } 2621 ) 2622 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2623 extra_scores_without_graphing = self.member_vars[ 2624 "extra_scores_without_graphing" 2625 ] 2626 for extra_score in extra_scores_without_graphing: 2627 pd2 = pd.DataFrame( 2628 { 2629 "Epochs": np.arange( 2630 len(extra_scores_without_graphing[extra_score]) 2631 ), 2632 extra_score: extra_scores_without_graphing[extra_score], 2633 } 2634 ) 2635 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2636 pd1.to_csv( 2637 save_folder + "/" + self.save_name + extra_string + "Scores.csv", 2638 index=False, 2639 ) 2640 del pd1, pd2 2641 2642 # Set y min and max to zoom in on important part of axis 2643 if ( 2644 len(self.member_vars["switch_epochs"]) > 0 2645 and self.member_vars["switch_epochs"][0] > 0 2646 and self.zooming_graph 2647 ): 2648 if GPA.pai_tracker.member_vars["maximizing_score"]: 2649 min_val = np.array( 2650 accuracies[0 : self.member_vars["switch_epochs"][0]] 2651 ).mean() 2652 for extra_score in extra_scores: 2653 min_pot = np.array( 2654 extra_scores[extra_score][ 2655 0 : self.member_vars["switch_epochs"][0] 2656 ] 2657 ).mean() 2658 if min_pot < min_val: 2659 min_val = min_pot 2660 ax.set_ylim(ymin=min_val) 2661 else: 2662 max_val = np.array( 2663 accuracies[0 : self.member_vars["switch_epochs"][0]] 2664 ).mean() 2665 for extra_score in extra_scores: 2666 max_pot = np.array( 2667 extra_scores[extra_score][ 2668 0 : self.member_vars["switch_epochs"][0] 2669 ] 2670 ).mean() 2671 if max_pot > max_val: 2672 max_val = max_pot 2673 ax.set_ylim(ymax=max_val) 2674 2675 ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left") 2676 _pai_grid(ax) 2677 2678 # Draw vertical lines for epochs where a dendrite switch occurred. 2679 # Colour by the phase being entered: dendrite/PAI phase -> teal, 2680 # normal phase -> orange, alternating starting with dendrite. 2681 if GPA.pc.get_drawing_pai() and self.member_vars["doing_pai"]: 2682 for i, switcher in enumerate(self.member_vars["switch_epochs"]): 2683 # Mode always starts at "n" and switch_epochs alternates 2684 # n->p->n->p..., so the first switch enters the dendrite 2685 # phase and every other one alternates from there. 2686 entering_p = (i % 2) == 0 2687 plt.axvline( 2688 x=switcher, 2689 ymin=0, 2690 ymax=1, 2691 color=_PAI_PALETTE["dendrite"] 2692 if entering_p 2693 else _PAI_PALETTE["normal"], 2694 ) 2695 else: 2696 for switcher in self.member_vars["n_switch_epochs"]: 2697 plt.axvline( 2698 x=switcher, ymin=0, ymax=1, color=_PAI_PALETTE["normal"] 2699 ) 2700 2701 def generate_time_plots(self, ax, save_folder, extra_string): 2702 """ 2703 Generate plots and csvs for timing 2704 2705 Parameters 2706 ---------- 2707 ax : object 2708 The matplotlib axis to plot on. 2709 save_folder : str 2710 The folder to save the plots and csvs in. 2711 extra_string : str 2712 An extra string to append to the filenames. 2713 2714 Returns 2715 ------- 2716 None 2717 2718 """ 2719 if self.member_vars["manual_train_switch"]: 2720 ax.plot( 2721 np.arange(len(self.member_vars["n_train_times"])), 2722 self.member_vars["n_train_times"], 2723 label="Normal Epoch Train Times", 2724 color=_PAI_PALETTE["normal"], 2725 ) 2726 ax.plot( 2727 np.arange(len(self.member_vars["p_train_times"])), 2728 self.member_vars["p_train_times"], 2729 label="PAI Epoch Train Times", 2730 color=_PAI_PALETTE["dendrite"], 2731 ) 2732 ax.plot( 2733 np.arange(len(self.member_vars["n_val_times"])), 2734 self.member_vars["n_val_times"], 2735 label="Normal Epoch Val Times", 2736 color=_PAI_PALETTE["normal"], 2737 alpha=0.45, 2738 ) 2739 ax.plot( 2740 np.arange(len(self.member_vars["p_val_times"])), 2741 self.member_vars["p_val_times"], 2742 label="PAI Epoch Val Times", 2743 color=_PAI_PALETTE["dendrite"], 2744 alpha=0.45, 2745 ) 2746 2747 plt.title( 2748 save_folder + "/" + self.save_name + "times (by train() and eval())" 2749 ) 2750 plt.xlabel("Iteration") 2751 plt.ylabel("Epoch Time in Seconds ") 2752 ax.set_ylim(ymin=0) 2753 ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left") 2754 _pai_grid(ax) 2755 2756 pd1 = pd.DataFrame( 2757 { 2758 "Epochs": np.arange(len(self.member_vars["n_train_times"])), 2759 "Normal Epoch Train Times": self.member_vars["n_train_times"], 2760 } 2761 ) 2762 pd2 = pd.DataFrame( 2763 { 2764 "Epochs": np.arange(len(self.member_vars["p_train_times"])), 2765 "PAI Epoch Train Times": self.member_vars["p_train_times"], 2766 } 2767 ) 2768 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2769 2770 pd2 = pd.DataFrame( 2771 { 2772 "Epochs": np.arange(len(self.member_vars["n_val_times"])), 2773 "Normal Epoch Val Times": self.member_vars["n_val_times"], 2774 } 2775 ) 2776 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2777 2778 pd2 = pd.DataFrame( 2779 { 2780 "Epochs": np.arange(len(self.member_vars["p_val_times"])), 2781 "PAI Epoch Val Times": self.member_vars["p_val_times"], 2782 } 2783 ) 2784 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2785 2786 pd1.to_csv( 2787 save_folder + "/" + self.save_name + extra_string + "Times.csv", 2788 index=False, 2789 ) 2790 del pd1, pd2 2791 else: 2792 ax.plot( 2793 np.arange(len(self.member_vars["n_epoch_times"])), 2794 self.member_vars["n_epoch_times"], 2795 label="Normal Epoch Times", 2796 color=_PAI_PALETTE["normal"], 2797 ) 2798 ax.plot( 2799 np.arange(len(self.member_vars["p_epoch_times"])), 2800 self.member_vars["p_epoch_times"], 2801 label="PAI Epoch Times", 2802 color=_PAI_PALETTE["dendrite"], 2803 ) 2804 2805 plt.title( 2806 save_folder + "/" + self.save_name + "times (by train() and eval())" 2807 ) 2808 plt.xlabel("Iteration") 2809 plt.ylabel("Epoch Time in Seconds ") 2810 ax.set_ylim(ymin=0) 2811 ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left") 2812 _pai_grid(ax) 2813 2814 pd1 = pd.DataFrame( 2815 { 2816 "Epochs": np.arange(len(self.member_vars["n_epoch_times"])), 2817 "Normal Epoch Times": self.member_vars["n_epoch_times"], 2818 } 2819 ) 2820 pd2 = pd.DataFrame( 2821 { 2822 "Epochs": np.arange(len(self.member_vars["p_epoch_times"])), 2823 "PAI Epoch Times": self.member_vars["p_epoch_times"], 2824 } 2825 ) 2826 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2827 2828 pd1.to_csv( 2829 save_folder + "/" + self.save_name + extra_string + "Times.csv", 2830 index=False, 2831 ) 2832 del pd1, pd2 2833 2834 if self.values_per_train_epoch != -1 and self.values_per_val_epoch != -1: 2835 ax2 = ax.twinx() # Second axes sharing same x-axis 2836 ax2.set_ylabel("Single Datapoint Time in Seconds") 2837 2838 ax2.plot( 2839 np.arange(len(self.member_vars["n_train_times"])), 2840 np.array(self.member_vars["n_train_times"]) 2841 / self.values_per_train_epoch, 2842 linestyle="dashed", 2843 label="Normal Train Item Times", 2844 color=_PAI_PALETTE["normal"], 2845 ) 2846 ax2.plot( 2847 np.arange(len(self.member_vars["p_train_times"])), 2848 np.array(self.member_vars["p_train_times"]) 2849 / self.values_per_train_epoch, 2850 linestyle="dashed", 2851 label="PAI Train Item Times", 2852 color=_PAI_PALETTE["dendrite"], 2853 ) 2854 ax2.plot( 2855 np.arange(len(self.member_vars["n_val_times"])), 2856 np.array(self.member_vars["n_val_times"]) / self.values_per_val_epoch, 2857 linestyle="dashed", 2858 label="Normal Val Item Times", 2859 color=_PAI_PALETTE["normal"], 2860 alpha=0.45, 2861 ) 2862 ax2.plot( 2863 np.arange(len(self.member_vars["p_val_times"])), 2864 np.array(self.member_vars["p_val_times"]) / self.values_per_val_epoch, 2865 linestyle="dashed", 2866 label="PAI Val Item Times", 2867 color=_PAI_PALETTE["dendrite"], 2868 alpha=0.45, 2869 ) 2870 ax2.tick_params(axis="y") 2871 ax2.set_ylim(ymin=0) 2872 ax2.legend(bbox_to_anchor=(1.05, 1), loc="upper left") 2873 2874 def generate_learning_rate_plots(self, ax, save_folder, extra_string): 2875 """ 2876 Generate plots and csvs for learning rate 2877 2878 Parameters 2879 ---------- 2880 ax : object 2881 The matplotlib axis to plot on. 2882 save_folder : str 2883 The folder to save the plots and csvs in. 2884 extra_string : str 2885 An extra string to append to the filenames. 2886 2887 Returns 2888 ------- 2889 None 2890 2891 """ 2892 ax.plot( 2893 np.arange(len(self.member_vars["training_learning_rates"])), 2894 self.member_vars["training_learning_rates"], 2895 label="learning_rate", 2896 color=_PAI_PALETTE["learning_rate"], 2897 ) 2898 plt.title(save_folder + "/" + self.save_name + "learning_rate") 2899 plt.xlabel("Epochs") 2900 plt.ylabel("learning_rate") 2901 ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left") 2902 _pai_grid(ax) 2903 2904 pd1 = pd.DataFrame( 2905 { 2906 "Epochs": np.arange(len(self.member_vars["training_learning_rates"])), 2907 "learning_rate": self.member_vars["training_learning_rates"], 2908 } 2909 ) 2910 pd1.to_csv( 2911 save_folder + "/" + self.save_name + extra_string + "learning_rate.csv", 2912 index=False, 2913 ) 2914 del pd1 2915 2916 def get_current_pb_scores(self): 2917 """ 2918 Get the latest best PBScore of each dendrite layer, the same numbers 2919 written to the Best PBScores csv. 2920 2921 Returns 2922 ------- 2923 dict 2924 Layer name to score. Empty outside of dendrite scoring phases, 2925 when no candidate dendrites are being scored. 2926 2927 """ 2928 if not self.member_vars["doing_pai"]: 2929 return {} 2930 if not GPA.pc.get_perforated_backpropagation(): 2931 return {} 2932 # Scores only advance while candidate dendrites are being trained 2933 if ( 2934 self.member_vars["mode"] != "p" 2935 and not GPA.pc.get_learn_dendrites_live() 2936 ): 2937 return {} 2938 2939 scores = {} 2940 for layer_id in range(len(self.neuron_module_vector)): 2941 if layer_id >= len(self.member_vars["best_scores"]): 2942 continue 2943 layer_scores = self.member_vars["best_scores"][layer_id] 2944 if len(layer_scores) == 0: 2945 continue 2946 score = layer_scores[-1] 2947 if hasattr(score, "item"): 2948 score = score.item() 2949 score = float(score) 2950 if math.isnan(score) or math.isinf(score): 2951 continue 2952 scores[self.neuron_module_vector[layer_id].name] = score 2953 return scores 2954 2955 def get_current_pb_scores_current(self): 2956 """ 2957 Like :meth:`get_current_pb_scores` but returns the *current* (not best) 2958 PBScore of each dendrite layer - ``current_scores[layer][-1]`` - the 2959 dashed series in ``generate_dendrite_learning_plots``. 2960 2961 Returns 2962 ------- 2963 dict 2964 Layer name to score. Empty outside of dendrite scoring phases. 2965 """ 2966 if not self.member_vars["doing_pai"]: 2967 return {} 2968 if not GPA.pc.get_perforated_backpropagation(): 2969 return {} 2970 if ( 2971 self.member_vars["mode"] != "p" 2972 and not GPA.pc.get_learn_dendrites_live() 2973 ): 2974 return {} 2975 2976 scores = {} 2977 for layer_id in range(len(self.neuron_module_vector)): 2978 if layer_id >= len(self.member_vars["current_scores"]): 2979 continue 2980 layer_scores = self.member_vars["current_scores"][layer_id] 2981 if len(layer_scores) == 0: 2982 continue 2983 score = layer_scores[-1] 2984 if hasattr(score, "item"): 2985 score = score.item() 2986 score = float(score) 2987 if math.isnan(score) or math.isinf(score): 2988 continue 2989 scores[self.neuron_module_vector[layer_id].name] = score 2990 return scores 2991 2992 def generate_dendrite_learning_plots(self, ax, save_folder, extra_string): 2993 """ 2994 Generate dendrite score plots for the tracker. 2995 Also saves csv files associated with the plots. 2996 2997 Parameters 2998 ---------- 2999 ax : matplotlib.axes.Axes 3000 Axis used for plotting dendrite-learning curves. 3001 save_folder : str 3002 Directory where plot images and CSV summaries are written. 3003 extra_string : str 3004 Filename suffix used to distinguish this output set. 3005 3006 Returns 3007 ------- 3008 None 3009 Saves plots and score CSV files to disk. 3010 """ 3011 if self.member_vars["doing_pai"]: 3012 pd1 = None 3013 pd2 = None 3014 num_colors = len(self.neuron_module_vector) 3015 3016 layer_colors = [ 3017 _PAI_DENDRITE_CYCLE[i % len(_PAI_DENDRITE_CYCLE)] 3018 for i in range(num_colors) 3019 ] 3020 3021 for layer_id in range(len(self.neuron_module_vector)): 3022 color = layer_colors[layer_id] 3023 ax.plot( 3024 np.arange(len(self.member_vars["best_scores"][layer_id])), 3025 self.member_vars["best_scores"][layer_id], 3026 label=self.neuron_module_vector[layer_id].name, 3027 color=color, 3028 ) 3029 3030 pd2 = pd.DataFrame( 3031 { 3032 "Epochs": np.arange( 3033 len(self.member_vars["best_scores"][layer_id]) 3034 ), 3035 f"Best ever for all nodes Layer {self.neuron_module_vector[layer_id].name}": self.member_vars[ 3036 "best_scores" 3037 ][ 3038 layer_id 3039 ], 3040 } 3041 ) 3042 3043 if pd1 is None: 3044 pd1 = pd2 3045 else: 3046 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 3047 3048 if len(self.member_vars["current_scores"][layer_id]) != 0: 3049 ax.plot( 3050 np.arange(len(self.member_vars["current_scores"][layer_id])), 3051 self.member_vars["current_scores"][layer_id], 3052 label=f"Current:{self.neuron_module_vector[layer_id].name}", 3053 color=color, 3054 linestyle="--", 3055 ) 3056 3057 pd2 = pd.DataFrame( 3058 { 3059 "Epochs": np.arange( 3060 len(self.member_vars["current_scores"][layer_id]) 3061 ), 3062 f"Best current for all nodes Layer {self.neuron_module_vector[layer_id].name}": self.member_vars[ 3063 "current_scores" 3064 ][ 3065 layer_id 3066 ], 3067 } 3068 ) 3069 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 3070 3071 plt.title(save_folder + "/" + self.save_name + " Best PBScores") 3072 plt.xlabel("Epochs") 3073 plt.ylabel("Best PBScore") 3074 ax.legend( 3075 bbox_to_anchor=(1.05, 1), 3076 loc="upper left", 3077 ncol=max(1, math.ceil(len(self.neuron_module_vector) / 30)), 3078 ) 3079 _pai_grid(ax) 3080 for switcher in self.member_vars["p_switch_epochs"]: 3081 plt.axvline( 3082 x=switcher, ymin=0, ymax=1, color=_PAI_PALETTE["dendrite"] 3083 ) 3084 3085 if self.member_vars["mode"] == "p": 3086 missed_time = ( 3087 self.member_vars["num_epochs_run"] 3088 - self.member_vars["epoch_last_improved"] 3089 ) 3090 plt.axvline( 3091 x=(len(self.member_vars["best_scores"][0]) - (missed_time + 1)), 3092 ymin=0, 3093 ymax=1, 3094 color=_PAI_PALETTE["normal"], 3095 ) 3096 3097 # pd1 here will be none if no PB layers are created 3098 if pd1 is not None: 3099 pd1.to_csv( 3100 save_folder 3101 + "/" 3102 + self.save_name 3103 + extra_string 3104 + "Best PBScores.csv", 3105 index=False, 3106 ) 3107 del pd1, pd2 3108 3109 def generate_extra_csv_files(self, save_folder, extra_string): 3110 """ 3111 Generate additional csvs 3112 3113 Parameters 3114 ---------- 3115 save_folder : str 3116 The folder to save the plots and csvs in. 3117 extra_string : str 3118 An extra string to append to the filenames. 3119 3120 Returns 3121 ------- 3122 None 3123 3124 """ 3125 pd1 = pd.DataFrame( 3126 { 3127 "Switch Number": np.arange(len(self.member_vars["switch_epochs"])), 3128 "Switch Epoch": self.member_vars["switch_epochs"], 3129 } 3130 ) 3131 pd1.to_csv( 3132 save_folder + "/" + self.save_name + extra_string + "switch_epochs.csv", 3133 index=False, 3134 ) 3135 del pd1 3136 3137 pd1 = pd.DataFrame( 3138 { 3139 "Switch Number": np.arange(len(self.member_vars["param_counts"])), 3140 "Param Count": self.member_vars["param_counts"], 3141 } 3142 ) 3143 pd1.to_csv( 3144 save_folder + "/" + self.save_name + extra_string + "param_counts.csv", 3145 index=False, 3146 ) 3147 del pd1 3148 3149 """ 3150 Create best_arch_scores.csv file 3151 When working with dendrites there is a tradeoff between additional param count and score improvement. 3152 This file will help track that tradeoff by recording the best scores for all extra_scores 3153 and extra_scores_without_graphing for each architecture version. 3154 The scores recorded here are from the epoch when the best validation score was found 3155 within each switch_epoch boundary. 3156 """ 3157 switch_counts = len(self.member_vars["switch_epochs"]) 3158 best_valid = [] 3159 associated_params = [] 3160 3161 # Initialize dictionaries to store best scores for each extra score type 3162 best_extra_scores = {} 3163 for score_name in self.member_vars["extra_scores"]: 3164 best_extra_scores[score_name] = [] 3165 for score_name in self.member_vars["extra_scores_without_graphing"]: 3166 best_extra_scores[score_name] = [] 3167 3168 for switch in range(0, switch_counts, 2): 3169 start_index = 0 3170 if switch != 0: 3171 start_index = self.member_vars["switch_epochs"][switch - 1] + 1 3172 end_index = self.member_vars["switch_epochs"][switch] + 1 3173 3174 if GPA.pai_tracker.member_vars["maximizing_score"]: 3175 best_valid_index = start_index + np.argmax( 3176 self.member_vars["accuracies"][start_index:end_index] 3177 ) 3178 else: 3179 best_valid_index = start_index + np.argmin( 3180 self.member_vars["accuracies"][start_index:end_index] 3181 ) 3182 3183 best_valid_score = self.member_vars["accuracies"][best_valid_index] 3184 best_valid.append(best_valid_score) 3185 3186 # Get corresponding scores from all extra_scores 3187 for score_name in self.member_vars["extra_scores"]: 3188 if best_valid_index < len(self.member_vars["extra_scores"][score_name]): 3189 best_extra_scores[score_name].append( 3190 self.member_vars["extra_scores"][score_name][best_valid_index] 3191 ) 3192 else: 3193 best_extra_scores[score_name].append(None) 3194 3195 # Get corresponding scores from all extra_scores_without_graphing 3196 for score_name in self.member_vars["extra_scores_without_graphing"]: 3197 if best_valid_index < len(self.member_vars["extra_scores_without_graphing"][score_name]): 3198 best_extra_scores[score_name].append( 3199 self.member_vars["extra_scores_without_graphing"][score_name][best_valid_index] 3200 ) 3201 else: 3202 best_extra_scores[score_name].append(None) 3203 3204 if self.member_vars["doing_pai"]: 3205 associated_params.append(self.member_vars["param_counts"][switch]) 3206 else: 3207 associated_params.append(self.member_vars["param_counts"][-1]) 3208 3209 # If in neuron training mode but not the very first epoch 3210 if self.member_vars["mode"] == "n" and ( 3211 (len(self.member_vars["switch_epochs"]) == 0) 3212 or ( 3213 self.member_vars["switch_epochs"][-1] + 1 3214 != len(self.member_vars["accuracies"]) 3215 ) 3216 ): 3217 start_index = 0 3218 if len(self.member_vars["switch_epochs"]) != 0: 3219 start_index = self.member_vars["switch_epochs"][-1] + 1 3220 3221 if GPA.pai_tracker.member_vars["maximizing_score"]: 3222 best_valid_index = start_index + np.argmax( 3223 self.member_vars["accuracies"][start_index:] 3224 ) 3225 else: 3226 best_valid_index = start_index + np.argmin( 3227 self.member_vars["accuracies"][start_index:] 3228 ) 3229 3230 best_valid_score = self.member_vars["accuracies"][best_valid_index] 3231 best_valid.append(best_valid_score) 3232 3233 # Get corresponding scores from all extra_scores 3234 for score_name in self.member_vars["extra_scores"]: 3235 if best_valid_index < len(self.member_vars["extra_scores"][score_name]): 3236 best_extra_scores[score_name].append( 3237 self.member_vars["extra_scores"][score_name][best_valid_index] 3238 ) 3239 else: 3240 best_extra_scores[score_name].append(None) 3241 3242 # Get corresponding scores from all extra_scores_without_graphing 3243 for score_name in self.member_vars["extra_scores_without_graphing"]: 3244 if best_valid_index < len(self.member_vars["extra_scores_without_graphing"][score_name]): 3245 best_extra_scores[score_name].append( 3246 self.member_vars["extra_scores_without_graphing"][score_name][best_valid_index] 3247 ) 3248 else: 3249 best_extra_scores[score_name].append(None) 3250 3251 associated_params.append(self.member_vars["param_counts"][-1]) 3252 3253 # Build dataframe with all columns 3254 csv_data = { 3255 "Param Counts": associated_params, 3256 "Max Valid Scores": best_valid, 3257 } 3258 3259 # Add columns for each extra score 3260 for score_name in best_extra_scores: 3261 csv_data[score_name] = best_extra_scores[score_name] 3262 3263 pd1 = pd.DataFrame(csv_data) 3264 pd1.to_csv( 3265 save_folder + "/" + self.save_name + extra_string + "_best_arch_scores.csv", 3266 index=False, 3267 ) 3268 del pd1 3269 3270 def save_graphs(self, extra_string=""): 3271 """ 3272 Save graphs and csvs for all the values the tracker records 3273 3274 Parameters 3275 ---------- 3276 extra_string : str 3277 An extra string to append to the filenames. 3278 3279 Returns 3280 ------- 3281 None 3282 3283 """ 3284 # If running DDP only save with rank 0 3285 if "RANK" in os.environ: 3286 if int(os.environ["RANK"]) != 0: 3287 return 3288 if not self.making_graphs: 3289 return 3290 3291 save_folder = "./" + self.save_name + "/" 3292 3293 plt.ioff() 3294 fig = plt.figure(figsize=(28, 14)) 3295 3296 # Plot with accuracy scores 3297 ax = plt.subplot(221) 3298 self.generate_accuracy_plots(ax, save_folder, extra_string) 3299 3300 # Plot dendrite learning scores 3301 ax = plt.subplot(222) 3302 self.generate_dendrite_learning_plots(ax, save_folder, extra_string) 3303 3304 if GPA.pc.get_drawing_extra_graphs(): 3305 # Plot learning rates for each training epoch 3306 ax = plt.subplot(223) 3307 self.generate_learning_rate_plots(ax, save_folder, extra_string) 3308 3309 # Plot the times for each training epoch 3310 ax = plt.subplot(224) 3311 self.generate_time_plots(ax, save_folder, extra_string) 3312 3313 # Generate extra CSV files 3314 self.generate_extra_csv_files(save_folder, extra_string) 3315 3316 fig.tight_layout() 3317 plt.savefig(save_folder + "/" + self.save_name + extra_string + ".png") 3318 plt.close("all") 3319 3320 def add_loss(self, loss): 3321 """Add loss to tracking vectors. 3322 3323 Parameters 3324 ---------- 3325 loss : float or int 3326 The loss value to add. 3327 3328 Returns 3329 ------- 3330 None 3331 3332 """ 3333 if not isinstance(loss, (float, int)): 3334 loss = loss.item() 3335 self.member_vars["training_loss"].append(loss) 3336 3337 def add_learning_rate(self, learning_rate): 3338 """Add learning rate to tracking vectors. 3339 3340 Parameters 3341 ---------- 3342 learning_rate : float or int 3343 The learning rate value to add. 3344 3345 Returns 3346 ------- 3347 None 3348 3349 """ 3350 if not isinstance(learning_rate, (float, int)): 3351 learning_rate = learning_rate.item() 3352 self.member_vars["training_learning_rates"].append(learning_rate) 3353 3354 def add_extra_score(self, score, extra_score_name): 3355 """Add extra score to tracking vectors. 3356 3357 Parameters 3358 ---------- 3359 score : float or int 3360 The score value to add. 3361 3362 extra_score_name : str 3363 The name of the extra score. 3364 3365 Returns 3366 ------- 3367 None 3368 3369 """ 3370 if not isinstance(score, (float, int)): 3371 try: 3372 score = score.item() 3373 except: 3374 print( 3375 "Scores added for Perforated Backpropagation should be " 3376 "float, int, or tensor, yours is a:" 3377 ) 3378 print(type(score)) 3379 pdb.set_trace() 3380 3381 if GPA.pc.get_verbose(): 3382 print(f"Adding extra score {extra_score_name} of {float(score)}") 3383 3384 if extra_score_name not in self.member_vars["extra_scores"]: 3385 self.member_vars["extra_scores"][extra_score_name] = [] 3386 self.member_vars["extra_scores"][extra_score_name].append(score) 3387 3388 if self.member_vars["mode"] == "n": 3389 if extra_score_name not in self.member_vars["n_extra_scores"]: 3390 self.member_vars["n_extra_scores"][extra_score_name] = [] 3391 self.member_vars["n_extra_scores"][extra_score_name].append(score) 3392 3393 def add_extra_score_without_graphing(self, score, extra_score_name): 3394 """Add extra score without graphing to tracking vectors. 3395 3396 Parameters 3397 ---------- 3398 score : float or int 3399 The score value to add. 3400 3401 extra_score_name : str 3402 The name of the extra score. 3403 3404 Returns 3405 ------- 3406 None 3407 3408 """ 3409 if not isinstance(score, (float, int)): 3410 try: 3411 score = score.item() 3412 except: 3413 print( 3414 "Scores added for Perforated Backpropagation should be " 3415 "float, int, or tensor, yours is a:" 3416 ) 3417 print(type(score)) 3418 print("in add_extra_score_without_graphing") 3419 pdb.set_trace() 3420 3421 if GPA.pc.get_verbose(): 3422 print(f"Adding extra score {extra_score_name} of {float(score)}") 3423 3424 if extra_score_name not in self.member_vars["extra_scores_without_graphing"]: 3425 self.member_vars["extra_scores_without_graphing"][extra_score_name] = [] 3426 self.member_vars["extra_scores_without_graphing"][extra_score_name].append( 3427 score 3428 ) 3429 3430 def add_test_score(self, score, extra_score_name): 3431 """Add test score to tracking vectors. 3432 3433 Parameters 3434 ---------- 3435 score : float or int 3436 The score value to add. 3437 3438 extra_score_name : str 3439 The name of the extra score. 3440 3441 Returns 3442 ------- 3443 None 3444 3445 Notes 3446 ----- 3447 This function is a wrapper around `add_extra_score` that separates 3448 test score for adding to best_arch_scores.csv. 3449 3450 """ 3451 self.add_extra_score(score, extra_score_name) 3452 3453 if not isinstance(score, (float, int)): 3454 try: 3455 score = score.item() 3456 except: 3457 print( 3458 "Scores added for Perforated Backpropagation should be " 3459 "float, int, or tensor, yours is a:" 3460 ) 3461 print(type(score)) 3462 print("in add_test_score") 3463 pdb.set_trace() 3464 3465 if GPA.pc.get_verbose(): 3466 print(f"Adding test score {extra_score_name} of {float(score)}") 3467 self.member_vars["test_scores"].append(score) 3468 3469 def add_validation_score(self, accuracy, net, force_switch=False): 3470 """Function to add the validation score. 3471 3472 This is complex because it determines neuron and dendrite switching. 3473 3474 Parameters 3475 ---------- 3476 accuracy : float or int 3477 The accuracy or loss value to add. 3478 net : object 3479 The neural network model. 3480 force_switch : bool, optional 3481 Whether to force a switch, by default False. 3482 3483 Returns 3484 ------- 3485 net : object 3486 The potentially modified neural network model. 3487 training_complete : bool 3488 Whether training is complete. 3489 restructured : bool 3490 Whether the model has been restructured. 3491 3492 Notes 3493 ----- 3494 WARNING: Do not call self anywhere in this function. When systems 3495 get loaded the actual tracker you are working with can change. 3496 """ 3497 3498 _pai_log("info", f"Adding validation score {accuracy:.8f}") 3499 3500 update_learning_rate() 3501 update_param_count(net) 3502 3503 accuracy = check_input_problems(net, accuracy) 3504 3505 if len(GPA.pai_tracker.member_vars["switch_epochs"]) == 0: 3506 epochs_since_cycle_switch = GPA.pai_tracker.member_vars["num_epochs_run"] 3507 else: 3508 epochs_since_cycle_switch = ( 3509 GPA.pai_tracker.member_vars["num_epochs_run"] 3510 - GPA.pai_tracker.member_vars["switch_epochs"][-1] 3511 ) 3512 3513 update_running_accuracy(accuracy, epochs_since_cycle_switch) 3514 if GPA.pc.get_perforated_backpropagation(): 3515 TPB.update_pb_scores(self) 3516 3517 # Captured before any switch below flips the mode and reloads scores. 3518 # epoch_phase is sampled here too so the emitted epoch event's phase and 3519 # its pb_scores always describe the same side of a switch boundary. 3520 epoch_phase = GPA.pai_tracker.member_vars["mode"] 3521 epoch_pb_scores = self.get_current_pb_scores() 3522 epoch_pb_scores_current = self.get_current_pb_scores_current() 3523 3524 GPA.pai_tracker.stop_epoch(internal_call=True) 3525 3526 # If it is neuron training mode 3527 if ( 3528 GPA.pai_tracker.member_vars["mode"] == "n" 3529 or GPA.pc.get_learn_dendrites_live() 3530 ): 3531 check_new_best(net, accuracy, epochs_since_cycle_switch) 3532 elif GPA.pc.get_perforated_backpropagation(): 3533 TPB.check_best_pai_score_improvement() 3534 3535 # Save the latest model 3536 if GPA.pc.get_test_saves(): 3537 UPA.save_system(net, GPA.pc.get_save_name(), "latest") 3538 if GPA.pc.get_pai_saves(): 3539 UPA.pai_save_system(net, GPA.pc.get_save_name(), "latest") 3540 3541 restructuring_status_value = NO_MODEL_UPDATE 3542 # If it is time to switch based on scores and counter or a manual switch 3543 if GPA.pai_tracker.switch_time() or force_switch: 3544 # If testing dendrite capacity switch after enough dendrites added 3545 if ( 3546 (GPA.pai_tracker.member_vars["mode"] == "n") 3547 and (GPA.pai_tracker.member_vars["num_dendrites_added"] > 2) 3548 and GPA.pc.get_testing_dendrite_capacity() 3549 ): 3550 GPA.pai_tracker.save_graphs() 3551 _pai_log( 3552 "info", 3553 "Successfully added 3 dendrites with GPA.pc.set_testing_dendrite_capacity(True) (default). " 3554 "You may now set that to False and run a real experiment.", 3555 ) 3556 return net, False, True 3557 3558 # If doing neuron training but this dendrite count didn't improve 3559 if ( 3560 (GPA.pai_tracker.member_vars["mode"] == "n") 3561 or GPA.pc.get_learn_dendrites_live() 3562 ) and (GPA.pai_tracker.member_vars["current_n_set_global_best"] is False): 3563 new_restructuring_status_value, net = process_no_improvement(net) 3564 # if this was the final try return that training is complete 3565 if new_restructuring_status_value == TRAINING_COMPLETE: 3566 if _dashboard_emitter is not None: 3567 _dashboard_emitter.emit_run_end( 3568 GPA.pc, 3569 epoch_last_improved=GPA.pai_tracker.member_vars[ 3570 "epoch_last_improved" 3571 ], 3572 global_best_score=GPA.pai_tracker.member_vars[ 3573 "global_best_validation_score" 3574 ], 3575 ) 3576 return net, True, True 3577 else: 3578 restructuring_status_value = update_restructuring_status( 3579 restructuring_status_value, new_restructuring_status_value 3580 ) 3581 # Else if did improve, do a normal switch process 3582 else: 3583 if GPA.pc.get_verbose(): 3584 print( 3585 f"Calling switch_mode with " 3586 f'{GPA.pai_tracker.member_vars["current_n_set_global_best"]}, ' 3587 f'{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}, ' 3588 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_steps"]}, ' 3589 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_value"]},' 3590 f'{GPA.pc.get_max_dendrites()},' 3591 f'{GPA.pai_tracker.member_vars["num_dendrites_added"]},' 3592 f'{GPA.pai_tracker.member_vars["num_dendrite_tries"]},' 3593 ) 3594 import pdb; pdb.set_trace 3595 # If the max number of dendrites has been hit or not doing pai and adding dendtites 3596 # then return rather than adding more 3597 if ( 3598 (GPA.pai_tracker.member_vars["mode"] == "n") 3599 and ( 3600 GPA.pc.get_max_dendrites() 3601 == GPA.pai_tracker.member_vars["num_dendrites_added"] 3602 ) 3603 ) or (GPA.pai_tracker.member_vars["doing_pai"] is False): 3604 if GPA.pc.get_verbose(): 3605 print( 3606 "Max dendrites reached or not doing PAI, finishing training" 3607 ) 3608 net = process_final_network(net) 3609 # Increment integrated if we have dendrites (means they're integrated) 3610 if GPA.pai_tracker.member_vars["num_dendrites_added"] > 0: 3611 GPA.pai_tracker.member_vars["num_dendrites_integrated"] += 1 3612 _pai_log("info", f"Final dendrites successfully integrated! Total integrated: {GPA.pai_tracker.member_vars['num_dendrites_integrated']}") 3613 if _dashboard_emitter is not None: 3614 _dashboard_emitter.emit_dendrite_added( 3615 GPA.pc, 3616 epoch_index=len( 3617 GPA.pai_tracker.member_vars["accuracies"] 3618 ) - 1, 3619 num_dendrites_integrated=GPA.pai_tracker.member_vars[ 3620 "num_dendrites_integrated" 3621 ], 3622 param_count=UPA.count_params(net), 3623 ) 3624 if _dashboard_emitter is not None: 3625 _dashboard_emitter.emit_run_end( 3626 GPA.pc, 3627 epoch_last_improved=GPA.pai_tracker.member_vars[ 3628 "epoch_last_improved" 3629 ], 3630 global_best_score=GPA.pai_tracker.member_vars[ 3631 "global_best_validation_score" 3632 ], 3633 ) 3634 return net, True, True 3635 3636 # Otherwise if its neuron training mode reset the counter of failed dendrites 3637 # Check if we should increment integrated count BEFORE change_learning_modes loads old state 3638 should_increment_integrated = False 3639 if GPA.pai_tracker.member_vars["mode"] == "n": 3640 GPA.pai_tracker.member_vars["num_dendrite_tries"] = 0 3641 if GPA.pc.get_verbose(): 3642 print( 3643 "Adding new dendrites without resetting which means " 3644 "the last ones improved. Resetting num_dendrite_tries" 3645 ) 3646 # Remember to increment after change_learning_modes (which loads old tracker state) 3647 if GPA.pai_tracker.member_vars["num_dendrites_added"] > 0: 3648 should_increment_integrated = True 3649 3650 GPA.pai_tracker.save_graphs( 3651 f'_beforeSwitch_{len(GPA.pai_tracker.member_vars["switch_epochs"])}' 3652 ) 3653 3654 if GPA.pc.get_test_saves(): 3655 UPA.save_system( 3656 net, 3657 GPA.pc.get_save_name(), 3658 f'beforeSwitch_{len(GPA.pai_tracker.member_vars["switch_epochs"])}', 3659 ) 3660 # Copy current best model from this set of dendrites 3661 # If running DDP only copy with rank 0 3662 if "RANK" not in os.environ or int(os.environ["RANK"]) == 0: 3663 shutil.copyfile( 3664 f"{GPA.pc.get_save_name()}/best_model.pt", 3665 f'{GPA.pc.get_save_name()}/best_model_beforeSwitch_{len(GPA.pai_tracker.member_vars["switch_epochs"])}.pt', 3666 ) 3667 3668 if GPA.pc.get_dashboard_debug(): 3669 _pai_log( 3670 "debug", 3671 "[emitter/finding4] before change_learning_modes: " 3672 f'len(accuracies)={len(GPA.pai_tracker.member_vars["accuracies"])} ' 3673 f'num_epochs_run={GPA.pai_tracker.member_vars["num_epochs_run"]}', 3674 ) 3675 net = UPA.change_learning_modes( 3676 net, 3677 GPA.pc.get_save_name(), 3678 "best_model", 3679 GPA.pai_tracker.member_vars["doing_pai"], 3680 ) 3681 restructuring_status_value = NETWORK_RESTRUCTURED 3682 if GPA.pc.get_dashboard_debug(): 3683 _pai_log( 3684 "debug", 3685 "[emitter/finding4] after change_learning_modes: " 3686 f'len(accuracies)={len(GPA.pai_tracker.member_vars["accuracies"])} ' 3687 f'num_epochs_run={GPA.pai_tracker.member_vars["num_epochs_run"]}', 3688 ) 3689 3690 # Now increment after change_learning_modes has loaded the best model 3691 # This ensures the increment persists and doesn't get overwritten 3692 if should_increment_integrated: 3693 GPA.pai_tracker.member_vars["num_dendrites_integrated"] += 1 3694 _pai_log("info", f"Dendrites successfully integrated! Total integrated: {GPA.pai_tracker.member_vars['num_dendrites_integrated']}") 3695 if _dashboard_emitter is not None: 3696 _dashboard_emitter.emit_dendrite_added( 3697 GPA.pc, 3698 epoch_index=len( 3699 GPA.pai_tracker.member_vars["accuracies"] 3700 ) - 1, 3701 num_dendrites_integrated=GPA.pai_tracker.member_vars[ 3702 "num_dendrites_integrated" 3703 ], 3704 param_count=UPA.count_params(net), 3705 ) 3706 3707 # If restructured is true, clear scheduler/optimizer before saving 3708 if restructuring_status_value != NETWORK_RESTRUCTURED: 3709 print( 3710 "Restructured should always be triggered here, let us know if you encounter this situation" 3711 ) 3712 pdb.set_trace() 3713 3714 # Since there is a restructuring optimizer and scheduler must be reinitialized after return 3715 GPA.pai_tracker.clear_optimizer_and_scheduler() 3716 3717 # Save the model from after the switch 3718 UPA.save_system( 3719 net, 3720 GPA.pc.get_save_name(), 3721 f'switch_{len(GPA.pai_tracker.member_vars["switch_epochs"])}', 3722 ) 3723 3724 # If not time to switch and you have a scheduler, perform the update step 3725 elif GPA.pai_tracker.member_vars["scheduler"] is not None: 3726 new_restructuring_status_value, net = process_scheduler_update( 3727 net, accuracy, epochs_since_cycle_switch 3728 ) 3729 restructuring_status_value = update_restructuring_status( 3730 restructuring_status_value, new_restructuring_status_value 3731 ) 3732 3733 GPA.pai_tracker.start_epoch(internal_call=True) 3734 if _dashboard_emitter is not None: 3735 _mv = GPA.pai_tracker.member_vars 3736 _lr = _mv["training_learning_rates"][-1] if _mv["training_learning_rates"] else None 3737 # epoch_index is the committed coordinate: len(accuracies) - 1, 3738 # captured after update_running_accuracy appends and after any reload 3739 # truncation in switch handling. Independent of num_epochs_run. 3740 _epoch_index = len(_mv["accuracies"]) - 1 3741 if GPA.pc.get_dashboard_debug(): 3742 _pai_log( 3743 "debug", 3744 "[emitter/finding4] emit_epoch: " 3745 f"epoch_index={_epoch_index} " 3746 f'true_epoch={_mv["num_epochs_run"]} ' 3747 f'restructured={restructuring_status_value == NETWORK_RESTRUCTURED} ' 3748 f'tail_accuracies={_mv["accuracies"][-4:]}', 3749 ) 3750 # Phase captured at the top of the method (pre-switch), matching the 3751 # point pb_scores were sampled - see Finding 2, ticket 06. 3752 _phase = epoch_phase 3753 # scores: validation (reported), running (checkpointed member var), 3754 # then every add_extra_score name verbatim. 3755 _scores = { 3756 "validation": accuracy, 3757 "running": _mv["running_accuracies"][-1] if _mv["running_accuracies"] else None, 3758 } 3759 for _name, _vals in _mv["extra_scores"].items(): 3760 if _vals: 3761 _scores[_name] = _vals[-1] 3762 _n_times = _mv["n_epoch_times"] or [(_mv["n_train_times"][-1] + _mv["n_val_times"][-1]) if (_mv["n_train_times"] and _mv["n_val_times"]) else None] 3763 _p_times = _mv["p_epoch_times"] or [(_mv["p_train_times"][-1] + _mv["p_val_times"][-1]) if (_mv["p_train_times"] and _mv["p_val_times"]) else None] 3764 _dashboard_emitter.emit_epoch( 3765 GPA.pc, 3766 epoch_index=_epoch_index, 3767 phase=_phase, 3768 scores=_scores, 3769 learning_rate=_lr, 3770 normal_time=_n_times[-1] if _phase == "n" else None, 3771 pai_time=_p_times[-1] if _phase == "p" else None, 3772 pb_scores=epoch_pb_scores, 3773 pb_scores_current=epoch_pb_scores_current, 3774 ) 3775 GPA.pai_tracker.save_graphs() 3776 3777 if restructuring_status_value == NETWORK_RESTRUCTURED: 3778 GPA.pai_tracker.member_vars["epoch_last_improved"] = ( 3779 GPA.pai_tracker.member_vars["num_epochs_run"] 3780 ) 3781 if GPA.pc.get_verbose(): 3782 print( 3783 f"Setting epoch last improved to " 3784 f'{GPA.pai_tracker.member_vars["epoch_last_improved"]}' 3785 ) 3786 3787 now = datetime.now() 3788 dt_string = now.strftime("_%d.%m.%Y.%H.%M.%S") 3789 3790 if GPA.pc.get_verbose(): 3791 print("Not saving restructure right now") 3792 3793 """ 3794 This block of code helped with a save issue with safetensors and huggingface, but it breaks DDP. 3795 Temporarily removing it to avoid DDP issues, but if you encounter save issues try adding it back in. 3796 for param in net.parameters(): 3797 param.data = param.data.contiguous() 3798 """ 3799 if GPA.pc.get_verbose(): 3800 print( 3801 f"Completed adding score. Restructured is {restructuring_status_value}, " 3802 f"\ncurrent switch list is:" 3803 ) 3804 print(GPA.pai_tracker.member_vars["switch_epochs"]) 3805 3806 if _dashboard_emitter is not None and restructuring_status_value == NETWORK_RESTRUCTURED: 3807 _param_count = UPA.count_params(net) 3808 _dashboard_emitter.emit_switch( 3809 GPA.pc, 3810 # switch_epochs was already appended in change_learning_modes, 3811 # so subtract 1 for a 0-based ordinal (matches matplotlib's 3812 # np.arange(len(switch_epochs)) labelling) - Finding 1, ticket 06. 3813 switch_ordinal=len(GPA.pai_tracker.member_vars["switch_epochs"]) - 1, 3814 epoch_index=len(GPA.pai_tracker.member_vars["accuracies"]) - 1, 3815 param_count=_param_count, 3816 switch_type=GPA.pai_tracker.member_vars["mode"], 3817 ) 3818 3819 # Always False for training complete if nothing triggered that training is over 3820 return net, restructuring_status_value, False 3821 3822 def clear_all_processors(self): 3823 """Clear all processors from modules. 3824 3825 Parameters 3826 ---------- 3827 None 3828 3829 Returns 3830 ------- 3831 None 3832 This function does not return a value. 3833 """ 3834 for module in self.neuron_module_vector: 3835 module.clear_processors() 3836 3837 def create_new_dendrite_module(self): 3838 """Add dendrite module to all neuron modules. 3839 3840 Parameters 3841 ---------- 3842 None 3843 3844 Returns 3845 ------- 3846 None 3847 This function does not return a value. 3848 """ 3849 for module in self.neuron_module_vector: 3850 module.create_new_dendrite_module() 3851 3852 def set_create_dendrite_global(self, fn): 3853 """Call set_create_dendrite(fn) on every tracked PAINeuronModule.""" 3854 for module in self.neuron_module_vector: 3855 module.set_create_dendrite(fn) 3856 3857 def set_dendrite_loss_fn_global(self, fn): 3858 """Set the global dendrite loss function used by all dendrite modules.""" 3859 from perforatedbp import modules_pbp as MPB 3860 MPB.dendrite_loss_fn = fn 3861 3862 def apply_pb_grads(self): 3863 """Apply perforated backpropagation gradients to all modules. 3864 3865 Parameters 3866 ---------- 3867 None 3868 3869 Returns 3870 ------- 3871 None 3872 This function does not return a value. 3873 """ 3874 if self.member_vars["mode"] == "p": 3875 for module in self.neuron_module_vector: 3876 module.apply_pb_grads() 3877 3878 def apply_pb_zero(self): 3879 """Apply perforated backpropagation zero gradients to all modules. 3880 3881 Parameters 3882 ---------- 3883 None 3884 3885 Returns 3886 ------- 3887 None 3888 This function does not return a value. 3889 """ 3890 if self.member_vars["mode"] == "p": 3891 for module in self.neuron_module_vector: 3892 module.apply_pb_zero()
Manager class that tracks all neuron layers and dendrite layers, controls when new dendrites are added, and communicates signals to modules.
990 def __init__( 991 self, 992 doing_pai, 993 save_name, 994 making_graphs=True, 995 param_vals_setting=-1, 996 values_per_train_epoch=-1, 997 values_per_val_epoch=-1, 998 ): 999 """Initialize the tracker 1000 1001 Parameters 1002 ---------- 1003 doing_pai : bool 1004 Whether or not dendrites should be used. 1005 save_name : str 1006 The base name for saving models and graphs. 1007 making_graphs : bool, optional 1008 Whether or not to generate graphs, by default True. 1009 param_vals_setting : int, optional 1010 Parameter values setting, by default -1. 1011 values_per_train_epoch : int, optional 1012 The number of values to look back for graphing 1013 during training, by default -1 (all values). 1014 values_per_val_epoch : int, optional 1015 The number of values to look back for graphing 1016 during validation, by default -1 (all values). 1017 Returns 1018 ------- 1019 None 1020 """ 1021 1022 # Dict of member vars and their types for saving 1023 self.member_vars = {} 1024 self.member_var_types = {} 1025 1026 # Whether or not PAI will be running 1027 self.member_vars["doing_pai"] = doing_pai 1028 self.member_var_types["doing_pai"] = "bool" 1029 1030 # How many Dendrites have been added 1031 self.member_vars["num_dendrites_added"] = 0 1032 self.member_var_types["num_dendrites_added"] = "int" 1033 1034 # How many Dendrites have been successfully integrated, does not count currently training dendrites 1035 self.member_vars["num_dendrites_integrated"] = 0 1036 self.member_var_types["num_dendrites_integrated"] = "int" 1037 1038 # How many cycles have been run, *2 or *2+1 of the above 1039 self.member_vars["num_cycles"] = 0 1040 self.member_var_types["num_cycles"] = "int" 1041 1042 # Pointers to all neuron wrapped modules 1043 self.neuron_module_vector = [] 1044 1045 # Pointers to all non neuron modules for tracking 1046 self.tracked_neuron_module_vector = [] 1047 1048 # Neuron training or dendrite training mode 1049 self.member_vars["mode"] = "n" 1050 self.member_var_types["mode"] = "string" 1051 1052 # Number of epochs run excluding overwritten epochs 1053 self.member_vars["num_epochs_run"] = -1 1054 self.member_var_types["num_epochs_run"] = "int" 1055 1056 # Number including overwritten epochs 1057 self.member_vars["total_epochs_run"] = -1 1058 self.member_var_types["total_epochs_run"] = "int" 1059 1060 # Last epoch that validation/correlation score was improved 1061 self.member_vars["epoch_last_improved"] = 0 1062 self.member_var_types["epoch_last_improved"] = "int" 1063 1064 # Running validation accuracy 1065 self.member_vars["running_accuracy"] = 0 1066 self.member_var_types["running_accuracy"] = "float" 1067 1068 # True if maxing validation, False if minimizing Loss 1069 self.member_vars["maximizing_score"] = True 1070 self.member_var_types["maximizing_score"] = "bool" 1071 1072 # Mode for switching back and forth between learning modes 1073 self.member_vars["switch_mode"] = GPA.pc.get_switch_mode() 1074 self.member_var_types["switch_mode"] = "int" 1075 1076 # Epoch of the last switch 1077 self.member_vars["last_switch"] = 0 1078 self.member_var_types["last_switch"] = "int" 1079 1080 # Highest validation score from current cycle 1081 self.member_vars["current_best_validation_score"] = 0 1082 self.member_var_types["current_best_validation_score"] = "float" 1083 1084 # Last epoch where the learning rate was updated 1085 self.member_vars["initial_lr_test_epoch_count"] = -1 1086 self.member_var_types["initial_lr_test_epoch_count"] = "int" 1087 1088 # Highest validation score of full run 1089 self.member_vars["global_best_validation_score"] = 0 1090 self.member_var_types["global_best_validation_score"] = "float" 1091 1092 # List of switch epochs 1093 self.member_vars["switch_epochs"] = [] 1094 self.member_var_types["switch_epochs"] = "int array" 1095 1096 # Parameter counts at each network structure 1097 self.member_vars["param_counts"] = [] 1098 self.member_var_types["param_counts"] = "int array" 1099 1100 # List of epochs where switch was made to neuron training 1101 self.member_vars["n_switch_epochs"] = [] 1102 self.member_var_types["n_switch_epochs"] = "int array" 1103 1104 # List of epochs where switch was made to dendrite training 1105 self.member_vars["p_switch_epochs"] = [] 1106 self.member_var_types["p_switch_epochs"] = "int array" 1107 1108 # List of validation accuracies 1109 self.member_vars["accuracies"] = [] 1110 self.member_var_types["accuracies"] = "float array" 1111 1112 # List of epochs where score improved for scheduler updates 1113 self.member_vars["last_improved_accuracies"] = [] 1114 self.member_var_types["last_improved_accuracies"] = "int array" 1115 1116 # List of test accuracy scores registered 1117 self.member_vars["test_accuracies"] = [] 1118 self.member_var_types["test_accuracies"] = "float array" 1119 1120 # List of accuracies registered during neuron training 1121 self.member_vars["n_accuracies"] = [] 1122 self.member_var_types["n_accuracies"] = "float array" 1123 1124 # List of accuracies registered during dendrite training 1125 self.member_vars["p_accuracies"] = [] 1126 self.member_var_types["p_accuracies"] = "float array" 1127 1128 # Running average accuracies from recent epochs 1129 self.member_vars["running_accuracies"] = [] 1130 self.member_var_types["running_accuracies"] = "float array" 1131 1132 # List of additional scores recorded 1133 self.member_vars["extra_scores"] = {} 1134 self.member_var_types["extra_scores"] = "float array dictionary" 1135 1136 # Extra scores not set to be graphed 1137 self.member_vars["extra_scores_without_graphing"] = {} 1138 self.member_var_types["extra_scores_without_graphing"] = ( 1139 "float array dictionary" 1140 ) 1141 1142 # List of test scores 1143 self.member_vars["test_scores"] = [] 1144 self.member_var_types["test_scores"] = "float array" 1145 1146 # Extra scores calculated during neuron training 1147 self.member_vars["n_extra_scores"] = {} 1148 self.member_var_types["n_extra_scores"] = "float array dictionary" 1149 1150 # List of training losses calculated 1151 self.member_vars["training_loss"] = [] 1152 self.member_var_types["training_loss"] = "float array" 1153 1154 # List of learning rates at each epoch 1155 self.member_vars["training_learning_rates"] = [] 1156 self.member_var_types["training_learning_rates"] = "float array" 1157 1158 # Best dendrite scores 1159 self.member_vars["best_scores"] = [] 1160 self.member_var_types["best_scores"] = "float array array" 1161 1162 # Current dendrite scores 1163 self.member_vars["current_scores"] = [] 1164 self.member_var_types["current_scores"] = "float array array" 1165 1166 # Times for neuron training epochs 1167 self.member_vars["n_epoch_times"] = [] 1168 self.member_var_types["n_epoch_times"] = "float array" 1169 1170 # Timing values 1171 self.member_vars["p_epoch_times"] = [] 1172 self.member_var_types["p_epoch_times"] = "float array" 1173 self.member_vars["n_train_times"] = [] 1174 self.member_var_types["n_train_times"] = "float array" 1175 self.member_vars["p_train_times"] = [] 1176 self.member_var_types["p_train_times"] = "float array" 1177 self.member_vars["n_val_times"] = [] 1178 self.member_var_types["n_val_times"] = "float array" 1179 self.member_vars["p_val_times"] = [] 1180 self.member_var_types["p_val_times"] = "float array" 1181 1182 # Setting for tracking timing 1183 self.member_vars["manual_train_switch"] = False 1184 self.member_var_types["manual_train_switch"] = "bool" 1185 1186 # Tracking scores overwritten when reloading best model 1187 self.member_vars["overwritten_extras"] = [] 1188 self.member_var_types["overwritten_extras"] = "float array dictionary array" 1189 self.member_vars["overwritten_vals"] = [] 1190 self.member_var_types["overwritten_vals"] = "float array array" 1191 self.member_vars["overwritten_epochs"] = 0 1192 self.member_var_types["overwritten_epochs"] = "int" 1193 1194 # Setting for determining scores 1195 self.member_vars["param_vals_setting"] = GPA.pc.get_param_vals_setting() 1196 self.member_var_types["param_vals_setting"] = "int" 1197 1198 # Optimizer and scheduler types and instances 1199 self.member_vars["optimizer"] = None 1200 self.member_var_types["optimizer"] = "type" 1201 self.member_vars["scheduler"] = None 1202 self.member_var_types["scheduler"] = "type" 1203 self.member_vars["optimizer_instance"] = None 1204 self.member_var_types["optimizer_instance"] = "empty array" 1205 self.member_vars["scheduler_instance"] = None 1206 self.member_var_types["scheduler_instance"] = "empty array" 1207 1208 # Flag for if the tracker was loaded 1209 self.loaded = False 1210 1211 # flag for 1212 self.member_vars["step_status"] = STEP_CLEARED 1213 self.member_var_types["step_status"] = "int" 1214 1215 1216 # Settings for tracking learning rates 1217 self.member_vars["current_n_learning_rate_initial_skip_steps"] = 0 1218 self.member_var_types["current_n_learning_rate_initial_skip_steps"] = "int" 1219 self.member_vars["last_max_learning_rate_steps"] = 0 1220 self.member_var_types["last_max_learning_rate_steps"] = "int" 1221 self.member_vars["last_max_learning_rate_value"] = -1 1222 self.member_var_types["last_max_learning_rate_value"] = "float" 1223 self.member_vars["current_cycle_lr_max_scores"] = [] 1224 self.member_var_types["current_cycle_lr_max_scores"] = "float array" 1225 self.member_vars["current_step_count"] = 0 1226 self.member_var_types["current_step_count"] = "int" 1227 self.member_vars["committed_to_initial_rate"] = True 1228 self.member_var_types["committed_to_initial_rate"] = "bool" 1229 self.member_vars["best_mean_score_improved_this_epoch"] = 0 1230 self.member_var_types["best_mean_score_improved_this_epoch"] = "int" 1231 1232 # Flag for if current dendrite achieved highest global score 1233 self.member_vars["current_n_set_global_best"] = True 1234 self.member_var_types["current_n_set_global_best"] = "bool" 1235 1236 # Number of tries adding this dendrite count 1237 self.member_vars["num_dendrite_tries"] = 0 1238 self.member_var_types["num_dendrite_tries"] = "int" 1239 1240 # Count of batches per epoch 1241 self.values_per_train_epoch = values_per_train_epoch 1242 self.values_per_val_epoch = values_per_val_epoch 1243 1244 self.save_name = save_name 1245 self.making_graphs = making_graphs 1246 1247 self.start_time = time.time() 1248 self.saved_time = 0 1249 self.start_epoch(internal_call=True) 1250 1251 if GPA.pc.get_verbose(): 1252 print(f'Initializing with switch_mode {self.member_vars["switch_mode"]}')
Initialize the tracker
Parameters
- doing_pai (bool): Whether or not dendrites should be used.
- save_name (str): The base name for saving models and graphs.
- making_graphs (bool, optional): Whether or not to generate graphs, by default True.
- param_vals_setting (int, optional): Parameter values setting, by default -1.
- values_per_train_epoch (int, optional): The number of values to look back for graphing during training, by default -1 (all values).
- values_per_val_epoch (int, optional): The number of values to look back for graphing during validation, by default -1 (all values).
Returns
- None
1254 def to_string(self): 1255 """Convert tracker values to string for saving with safetensors. 1256 1257 Parameters 1258 ---------- 1259 None 1260 1261 Returns 1262 ------- 1263 str 1264 Serialized tracker state suitable for storage in a safetensors field. 1265 """ 1266 1267 full_string = "" 1268 for var in self.member_vars: 1269 full_string += var + "," 1270 if self.member_vars[var] is None: 1271 full_string += "None" 1272 full_string += "\n" 1273 elif self.member_var_types[var] == "bool": 1274 full_string += str(self.member_vars[var]) 1275 full_string += "\n" 1276 elif self.member_var_types[var] in ("int", "float", "string"): 1277 full_string += str(self.member_vars[var]) 1278 full_string += "\n" 1279 elif self.member_var_types[var] == "type": 1280 name = ( 1281 self.member_vars[var].__module__ 1282 + "." 1283 + self.member_vars[var].__name__ 1284 ) 1285 full_string += str(self.member_vars[var]) 1286 full_string += "\n" 1287 elif self.member_var_types[var] == "empty array": 1288 full_string += "[]" 1289 full_string += "\n" 1290 elif self.member_var_types[var] in ("int array", "float array"): 1291 full_string += "\n" 1292 string = "" 1293 for val in self.member_vars[var]: 1294 string += str(val) + "," 1295 # Remove the last comma 1296 string = string[:-1] 1297 full_string += string 1298 full_string += "\n" 1299 elif self.member_var_types[var] == "float array dictionary array": 1300 full_string += "\n" 1301 for array in self.member_vars[var]: 1302 for key in array: 1303 string = key + "," 1304 for val in array[key]: 1305 string += str(val) + "," 1306 # Remove the last comma 1307 string = string[:-1] 1308 full_string += string 1309 full_string += "\n" 1310 full_string += "endkey" 1311 full_string += "\n" 1312 full_string += "endarray" 1313 full_string += "\n" 1314 elif self.member_var_types[var] == "float array dictionary": 1315 full_string += "\n" 1316 for key in self.member_vars[var]: 1317 string = key + "," 1318 for val in self.member_vars[var][key]: 1319 string += str(val) + "," 1320 # Remove the last comma 1321 string = string[:-1] 1322 full_string += string 1323 full_string += "\n" 1324 full_string += "end" 1325 full_string += "\n" 1326 elif self.member_var_types[var] == "float array array": 1327 full_string += "\n" 1328 for array in self.member_vars[var]: 1329 string = "" 1330 for val in array: 1331 string += str(val) + "," 1332 # Remove the last comma 1333 string = string[:-1] 1334 full_string += string 1335 full_string += "\n" 1336 full_string += "end" 1337 full_string += "\n" 1338 else: 1339 print("Did not find a member variable") 1340 pdb.set_trace() 1341 return full_string
Convert tracker values to string for saving with safetensors.
Parameters
- None
Returns
- str: Serialized tracker state suitable for storage in a safetensors field.
1343 def from_string(self, string): 1344 """Load tracker values from string. 1345 1346 Parameters 1347 ---------- 1348 string : str 1349 The string to load from. 1350 1351 Returns 1352 ------- 1353 None 1354 This function does not return a value. 1355 """ 1356 f = io.StringIO(string) 1357 while True: 1358 line = f.readline() 1359 if not line: 1360 break 1361 vals = line.split(",") 1362 var = vals[0] 1363 1364 if self.member_var_types[var] == "bool": 1365 val = vals[1][:-1] 1366 if val == "True": 1367 self.member_vars[var] = True 1368 elif val == "False": 1369 self.member_vars[var] = False 1370 elif val == "1": 1371 self.member_vars[var] = 1 1372 elif val == "0": 1373 self.member_vars[var] = 0 1374 else: 1375 print("Something went wrong with loading") 1376 pdb.set_trace() 1377 elif self.member_var_types[var] == "int": 1378 val = vals[1] 1379 self.member_vars[var] = int(val) 1380 elif self.member_var_types[var] == "float": 1381 val = vals[1] 1382 self.member_vars[var] = float(val) 1383 elif self.member_var_types[var] == "string": 1384 val = vals[1][:-1] 1385 self.member_vars[var] = val 1386 elif self.member_var_types[var] == "type": 1387 # Ignore loading types, tracker should have them set up 1388 continue 1389 elif self.member_var_types[var] == "empty array": 1390 val = vals[1] 1391 self.member_vars[var] = [] 1392 elif self.member_var_types[var] == "int array": 1393 vals = f.readline()[:-1].split(",") 1394 self.member_vars[var] = [] 1395 if vals[0] == "": 1396 continue 1397 for val in vals: 1398 self.member_vars[var].append(int(val)) 1399 elif self.member_var_types[var] == "float array": 1400 vals = f.readline()[:-1].split(",") 1401 self.member_vars[var] = [] 1402 if vals[0] == "": 1403 continue 1404 for val in vals: 1405 self.member_vars[var].append(float(val)) 1406 elif self.member_var_types[var] == "float array dictionary array": 1407 self.member_vars[var] = [] 1408 line2 = f.readline()[:-1] 1409 while line2 != "endarray": 1410 temp = {} 1411 while line2 != "endkey": 1412 vals = line2.split(",") 1413 name = vals[0] 1414 temp[name] = [] 1415 vals = vals[1:] 1416 for val in vals: 1417 temp[name].append(float(val)) 1418 line2 = f.readline()[:-1] 1419 self.member_vars[var].append(temp) 1420 line2 = f.readline()[:-1] 1421 elif self.member_var_types[var] == "float array dictionary": 1422 self.member_vars[var] = {} 1423 line2 = f.readline()[:-1] 1424 while line2 != "end": 1425 vals = line2.split(",") 1426 name = vals[0] 1427 self.member_vars[var][name] = [] 1428 vals = vals[1:] 1429 for val in vals: 1430 self.member_vars[var][name].append(float(val)) 1431 line2 = f.readline()[:-1] 1432 elif self.member_var_types[var] == "float array array": 1433 self.member_vars[var] = [] 1434 line2 = f.readline()[:-1] 1435 while line2 != "end": 1436 vals = line2.split(",") 1437 self.member_vars[var].append([]) 1438 if line2: 1439 for val in vals: 1440 self.member_vars[var][-1].append(float(val)) 1441 line2 = f.readline()[:-1] 1442 else: 1443 print("Did not find a member variable") 1444 1445 pdb.set_trace()
Load tracker values from string.
Parameters
- string (str): The string to load from.
Returns
- None: This function does not return a value.
1447 def from_string_debug(self, string): 1448 """Debug function to print tracker values from string without loading them. 1449 1450 Parameters 1451 ---------- 1452 string : str 1453 The string to debug load from. 1454 1455 Returns 1456 ------- 1457 None 1458 This function does not return a value. 1459 """ 1460 f = io.StringIO(string) 1461 print("=== DEBUGGING TRACKER VARIABLES ===") 1462 1463 while True: 1464 line = f.readline() 1465 if not line: 1466 break 1467 vals = line.split(",") 1468 var = vals[0] 1469 1470 print(f"\nVariable: {var}") 1471 print(f"Type: {self.member_var_types.get(var, 'UNKNOWN TYPE')}") 1472 print(f"Current value: {self.member_vars.get(var, 'NOT SET')}") 1473 1474 if self.member_var_types.get(var) == "bool": 1475 val = vals[1][:-1] 1476 print(f"Would set to: {val} -> {val == 'True'}") 1477 1478 elif self.member_var_types.get(var) == "int": 1479 val = vals[1] 1480 print(f"Would set to: {int(val)}") 1481 1482 elif self.member_var_types.get(var) == "float": 1483 val = vals[1] 1484 print(f"Would set to: {float(val)}") 1485 1486 elif self.member_var_types.get(var) == "string": 1487 val = vals[1][:-1] 1488 print(f"Would set to: '{val}'") 1489 1490 elif self.member_var_types.get(var) == "type": 1491 print("Would skip (type loading)") 1492 1493 elif self.member_var_types.get(var) == "empty array": 1494 val = vals[1] 1495 print(f"Would set to: [] (empty array)") 1496 1497 elif self.member_var_types.get(var) == "int array": 1498 vals_line = f.readline()[:-1].split(",") 1499 print(f"Would set to int array with {len(vals_line)} elements:") 1500 if vals_line[0] != "": 1501 print( 1502 f" Elements: {vals_line[:5]}{'...' if len(vals_line) > 5 else ''}" 1503 ) 1504 else: 1505 print(" Empty array") 1506 1507 elif self.member_var_types.get(var) == "float array": 1508 vals_line = f.readline()[:-1].split(",") 1509 print(f"Would set to float array with {len(vals_line)} elements:") 1510 if vals_line[0] != "": 1511 print( 1512 f" Elements: {vals_line[:5]}{'...' if len(vals_line) > 5 else ''}" 1513 ) 1514 else: 1515 print(" Empty array") 1516 1517 elif self.member_var_types.get(var) == "float array dictionary array": 1518 print("Would process float array dictionary array:") 1519 array_count = 0 1520 line2 = f.readline()[:-1] 1521 while line2 != "endarray": 1522 key_count = 0 1523 while line2 != "endkey": 1524 vals_dict = line2.split(",") 1525 name = vals_dict[0] 1526 print( 1527 f" Array {array_count}, Key '{name}': {len(vals_dict)-1} elements" 1528 ) 1529 key_count += 1 1530 line2 = f.readline()[:-1] 1531 print(f" Array {array_count} has {key_count} keys") 1532 array_count += 1 1533 line2 = f.readline()[:-1] 1534 print(f" Total arrays: {array_count}") 1535 1536 elif self.member_var_types.get(var) == "float array dictionary": 1537 print("Would process float array dictionary:") 1538 line2 = f.readline()[:-1] 1539 key_count = 0 1540 while line2 != "end": 1541 vals_dict = line2.split(",") 1542 name = vals_dict[0] 1543 print(f" Key '{name}': {len(vals_dict)-1} elements") 1544 key_count += 1 1545 line2 = f.readline()[:-1] 1546 print(f" Total keys: {key_count}") 1547 1548 elif self.member_var_types.get(var) == "float array array": 1549 print("Would process float array array:") 1550 line2 = f.readline()[:-1] 1551 array_count = 0 1552 while line2 != "end": 1553 if line2: 1554 vals_array = line2.split(",") 1555 print(f" Array {array_count}: {len(vals_array)} elements") 1556 else: 1557 print(f" Array {array_count}: empty") 1558 array_count += 1 1559 line2 = f.readline()[:-1] 1560 print(f" Total arrays: {array_count}") 1561 1562 else: 1563 print(f"UNKNOWN TYPE: {self.member_var_types.get(var, 'NOT FOUND')}") 1564 1565 print("\n=== END DEBUG ===")
Debug function to print tracker values from string without loading them.
Parameters
- string (str): The string to debug load from.
Returns
- None: This function does not return a value.
1567 def save_tracker_settings(self): 1568 """Save tracker settings for DistributedDataParallel use. 1569 1570 Saves settings in save_name/array_dims.csv 1571 1572 Parameters 1573 ---------- 1574 None 1575 Returns 1576 ------- 1577 None 1578 1579 ----- 1580 Instructions for use are in API customization.md 1581 """ 1582 if not os.path.isdir(self.save_name): 1583 os.makedirs(self.save_name) 1584 f = open(self.save_name + "/array_dims.csv", "w") 1585 for layer in self.neuron_module_vector: 1586 dv = layer.dendrite_module.dendrite_values[0] 1587 shape_str = ",".join(str(s) for s in dv.dendrite_storage_shape.tolist()) 1588 f.write(f"{layer.name},{shape_str}\n") 1589 f.close() 1590 if not GPA.pc.get_silent(): 1591 print("Tracker settings saved.") 1592 print("You may now delete save_tracker_settings")
Save tracker settings for DistributedDataParallel use.
Saves settings in save_name/array_dims.csv
Parameters
- None
Returns
- None
- -----
- Instructions for use are in API customization.md
1594 def initialize_tracker_settings(self): 1595 """Initialize tracker settings from saved file. 1596 1597 This function loads tracker settings from a CSV file and applies them 1598 to the layers the tracker is managing. 1599 1600 Parameters 1601 ---------- 1602 None 1603 1604 Returns 1605 ------- 1606 None 1607 1608 """ 1609 1610 channels = {} 1611 if not os.path.exists(self.save_name + "/array_dims.csv"): 1612 print( 1613 "You must call save_tracker_settings before " 1614 "initialize_tracker_settings" 1615 ) 1616 print("Follow instructions in customization.md") 1617 pdb.set_trace() 1618 f = open(self.save_name + "/array_dims.csv", "r") 1619 for line in f: 1620 parts = line.strip().split(",") 1621 channels[parts[0]] = [int(s) for s in parts[1:]] 1622 for layer in self.neuron_module_vector: 1623 dv = layer.dendrite_module.dendrite_values[0] 1624 dv.setup_arrays(channels[layer.name])
Initialize tracker settings from saved file.
This function loads tracker settings from a CSV file and applies them to the layers the tracker is managing.
Parameters
- None
Returns
- None
1626 def set_optimizer_instance(self, optimizer_instance, additional_optimizers=[]): 1627 """Set optimizer instance directly. 1628 1629 Parameters 1630 ---------- 1631 optimizer_instance : object 1632 The optimizer instance to set. 1633 1634 Returns 1635 ------- 1636 None 1637 1638 """ 1639 # This call must be first before the parameters get filtered. 1640 optimizer_instance.zero_grad() 1641 try: 1642 for param_group in optimizer_instance.param_groups: 1643 if ( 1644 param_group["weight_decay"] > 0 1645 and GPA.pc.get_weight_decay_accepted() is False 1646 ): 1647 _pai_log( 1648 "warning", 1649 "For PAI training it is recommended to not use weight decay in your optimizer", 1650 ) 1651 1652 except: 1653 pass 1654 self.member_vars["optimizer_instance"] = optimizer_instance 1655 if GPA.pc.get_perforated_backpropagation(): 1656 TPB.setup_optimizer_pb(self.member_vars["optimizer_instance"]) 1657 for optimizer in additional_optimizers: 1658 TPB.filter_params(optimizer)
Set optimizer instance directly.
Parameters
- optimizer_instance (object): The optimizer instance to set.
Returns
- None
1660 def set_optimizer(self, optimizer): 1661 """Set optimizer type to be initialized later 1662 1663 Parameters 1664 ---------- 1665 optimizer : object 1666 The optimizer type to set. 1667 1668 Returns 1669 ------- 1670 None 1671 1672 """ 1673 self.member_vars["optimizer"] = optimizer
Set optimizer type to be initialized later
Parameters
- optimizer (object): The optimizer type to set.
Returns
- None
1675 def set_scheduler(self, scheduler): 1676 """Set scheduler type to be initialized later 1677 1678 Parameters 1679 ---------- 1680 scheduler : object 1681 The scheduler type to set. 1682 1683 Returns 1684 ------- 1685 None 1686 1687 """ 1688 if scheduler is not torch.optim.lr_scheduler.ReduceLROnPlateau: 1689 if GPA.pc.get_verbose(): 1690 print("Not using ReduceLROnPlateau, this is not recommended") 1691 self.member_vars["scheduler"] = scheduler
Set scheduler type to be initialized later
Parameters
- scheduler (object): The scheduler type to set.
Returns
- None
1693 def increment_scheduler(self, num_ticks, mode): 1694 """Increment the scheduler a set number of times. 1695 1696 Used for finding best initial learning rate when adding dendrites. 1697 1698 Parameters 1699 ---------- 1700 num_ticks : int 1701 The number of scheduler steps to take. 1702 mode : str 1703 The mode for stepping the scheduler. Options are: 1704 - "step_learning_rate": Step based on improved accuracy epochs 1705 - "increment_epoch_count": Step based on total epoch count 1706 1707 Returns 1708 ------- 1709 current_steps : int 1710 The number of learning rate changes that occurred. 1711 learning_rate1 : float 1712 The final learning rate after stepping. 1713 1714 """ 1715 1716 current_steps = 0 1717 current_ticker = 0 1718 1719 for param_group in GPA.pai_tracker.member_vars[ 1720 "optimizer_instance" 1721 ].param_groups: 1722 learning_rate1 = param_group["lr"] 1723 1724 if GPA.pc.get_verbose(): 1725 print("Using scheduler:") 1726 print(type(self.member_vars["scheduler_instance"])) 1727 1728 while current_ticker < num_ticks: 1729 if GPA.pc.get_verbose(): 1730 print( 1731 f"Lower start rate initial {learning_rate1} " 1732 f'stepping {GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]} times' 1733 ) 1734 1735 if ( 1736 type(self.member_vars["scheduler_instance"]) 1737 is torch.optim.lr_scheduler.ReduceLROnPlateau 1738 ): 1739 if mode == "step_learning_rate": 1740 # Step with counter as last improved accuracy 1741 self.member_vars["scheduler_instance"].step( 1742 metrics=self.member_vars["last_improved_accuracies"][ 1743 GPA.pai_tracker.steps_after_switch() - 1 1744 ] 1745 ) 1746 elif mode == "increment_epoch_count": 1747 # Step with improved epoch counts up to current location 1748 self.member_vars["scheduler_instance"].step( 1749 metrics=self.member_vars["last_improved_accuracies"][ 1750 -((num_ticks - 1) - current_ticker) - 1 1751 ] 1752 ) 1753 else: 1754 self.member_vars["scheduler_instance"].step() 1755 1756 for param_group in GPA.pai_tracker.member_vars[ 1757 "optimizer_instance" 1758 ].param_groups: 1759 learning_rate2 = param_group["lr"] 1760 1761 if learning_rate2 != learning_rate1: 1762 current_steps += 1 1763 learning_rate1 = learning_rate2 1764 if mode == "step_learning_rate": 1765 current_ticker += 1 1766 if GPA.pc.get_verbose(): 1767 print(f"1 step {current_steps} to {learning_rate2}") 1768 1769 if mode == "increment_epoch_count": 1770 current_ticker += 1 1771 1772 return current_steps, learning_rate1
Increment the scheduler a set number of times.
Used for finding best initial learning rate when adding dendrites.
Parameters
- num_ticks (int): The number of scheduler steps to take.
- mode (str):
The mode for stepping the scheduler. Options are:
- "step_learning_rate": Step based on improved accuracy epochs
- "increment_epoch_count": Step based on total epoch count
Returns
- current_steps (int): The number of learning rate changes that occurred.
- learning_rate1 (float): The final learning rate after stepping.
1774 def setup_optimizer(self, net, opt_args, sched_args=None, parameters=None): 1775 """Initialize the optimizer and scheduler when added. 1776 1777 Parameters 1778 ---------- 1779 net : object 1780 The neural network model. 1781 opt_args : dict 1782 The arguments for the optimizer. 1783 sched_args : dict, optional 1784 The arguments for the scheduler, by default None. 1785 1786 Returns 1787 ------- 1788 optimizer : object 1789 The initialized optimizer instance. 1790 scheduler : object or None 1791 The initialized scheduler instance, or None if no scheduler was set. 1792 1793 """ 1794 if "weight_decay" in opt_args and not GPA.pc.get_weight_decay_accepted(): 1795 _pai_log( 1796 "warning", 1797 "For PAI training it is recommended to not use weight decay in your optimizer", 1798 ) 1799 1800 if ("model" not in opt_args.keys()) and "params" not in opt_args.keys(): 1801 print("In setup_optimizer it will be depreciated to not pass in params yourself in the future") 1802 print("please change the settings to include params") 1803 if self.member_vars["mode"] == "n": 1804 if parameters is not None: 1805 opt_args["params"] = parameters 1806 else: 1807 opt_args["params"] = filter(lambda p: p.requires_grad, net.parameters()) 1808 else: 1809 params = UPA.get_pai_network_params(net) 1810 if parameters is not None: 1811 # Filter parameters to only those in params, preserving weight_decay 1812 params_set = set(params) 1813 filtered_params = [] 1814 for param_group in parameters: 1815 filtered_group_params = [p for p in param_group["params"] if p in params_set] 1816 if filtered_group_params: 1817 filtered_params.append({ 1818 "params": filtered_group_params, 1819 "weight_decay": param_group["weight_decay"] 1820 }) 1821 opt_args["params"] = filtered_params 1822 else: 1823 opt_args["params"] = params 1824 elif "params" in opt_args.keys(): 1825 # Check if params is a list of param groups (dicts) or a single param group 1826 params_value = opt_args["params"] 1827 if isinstance(params_value, list) and len(params_value) > 0: 1828 # Check if it's a list of dicts (multiple param groups) or list of tensors (single group) 1829 if isinstance(params_value[0], dict): 1830 # Multiple param groups format: [{"params": [...], "lr": ...}, ...] 1831 # Filter each param group for requires_grad 1832 filtered_param_groups = [] 1833 for param_group in params_value: 1834 filtered_group_params = [p for p in param_group["params"] if p.requires_grad] 1835 if filtered_group_params: 1836 new_group = param_group.copy() 1837 new_group["params"] = filtered_group_params 1838 filtered_param_groups.append(new_group) 1839 opt_args["params"] = filtered_param_groups 1840 else: 1841 # Single param group format: [tensor1, tensor2, ...] or generator 1842 # Filter for requires_grad 1843 opt_args["params"] = [p for p in params_value if p.requires_grad] 1844 elif hasattr(params_value, '__iter__'): 1845 # Handle generators or other iterables 1846 opt_args["params"] = [p for p in params_value if p.requires_grad] 1847 1848 optimizer = self.member_vars["optimizer"](**opt_args) 1849 self.set_optimizer_instance(optimizer) 1850 1851 if self.member_vars["scheduler"] is not None: 1852 # Handle SequentialLR specially 1853 if self.member_vars["scheduler"] is torch.optim.lr_scheduler.SequentialLR: 1854 """ 1855 sched_args should be a dict with "schedulers" (list of tuples) and "milestones" 1856 For example: 1857 sequential_schedArgs = { 1858 "schedulers": [ 1859 (warmup_scheduler_class, warmup_schedArgs), 1860 (main_scheduler_class, main_schedArgs) 1861 ], 1862 "milestones": [switch_epoch] 1863 } 1864 """ 1865 schedulers = [] 1866 milestones = sched_args.get("milestones", []) 1867 scheduler_configs = sched_args.get("schedulers", []) 1868 1869 for scheduler_class, scheduler_args in scheduler_configs: 1870 schedulers.append(scheduler_class(optimizer, **scheduler_args)) 1871 1872 self.member_vars["scheduler_instance"] = torch.optim.lr_scheduler.SequentialLR( 1873 optimizer, schedulers=schedulers, milestones=milestones 1874 ) 1875 else: 1876 self.member_vars["scheduler_instance"] = self.member_vars["scheduler"]( 1877 optimizer, **sched_args 1878 ) 1879 current_steps = 0 1880 1881 for param_group in GPA.pai_tracker.member_vars[ 1882 "optimizer_instance" 1883 ].param_groups: 1884 learning_rate1 = param_group["lr"] 1885 1886 if GPA.pc.get_verbose(): 1887 print( 1888 f"Resetting scheduler with {GPA.pai_tracker.steps_after_switch()} " 1889 f"steps and {GPA.pc.get_initial_history_after_switches()} initial ticks to skip" 1890 ) 1891 1892 # Find setting of previously used learning rate before adding dendrites 1893 if ( 1894 GPA.pai_tracker.member_vars[ 1895 "current_n_learning_rate_initial_skip_steps" 1896 ] 1897 != 0 1898 ): 1899 additional_steps, learning_rate1 = self.increment_scheduler( 1900 GPA.pai_tracker.member_vars[ 1901 "current_n_learning_rate_initial_skip_steps" 1902 ], 1903 "step_learning_rate", 1904 ) 1905 current_steps += additional_steps 1906 1907 if self.member_vars["mode"] == "n" or GPA.pc.get_learn_dendrites_live(): 1908 initial = GPA.pc.get_initial_history_after_switches() 1909 else: 1910 initial = 0 1911 1912 if GPA.pai_tracker.steps_after_switch() > initial: 1913 # Minus extra 1 because this gets called after start epoch 1914 additional_steps, learning_rate1 = self.increment_scheduler( 1915 (GPA.pai_tracker.steps_after_switch() - initial) - 1, 1916 "increment_epoch_count", 1917 ) 1918 current_steps += additional_steps 1919 1920 if GPA.pc.get_verbose(): 1921 print( 1922 f"Scheduler update loop with {current_steps} " 1923 f"ended with {learning_rate1}" 1924 ) 1925 print( 1926 f"Scheduler ended with {current_steps} steps " 1927 f"and lr of {learning_rate1}" 1928 ) 1929 1930 self.member_vars["current_step_count"] = current_steps 1931 return optimizer, self.member_vars["scheduler_instance"] 1932 else: 1933 return optimizer, None
Initialize the optimizer and scheduler when added.
Parameters
- net (object): The neural network model.
- opt_args (dict): The arguments for the optimizer.
- sched_args (dict, optional): The arguments for the scheduler, by default None.
Returns
- optimizer (object): The initialized optimizer instance.
- scheduler (object or None): The initialized scheduler instance, or None if no scheduler was set.
1935 def clear_optimizer_and_scheduler(self): 1936 """Clear the instances for saving. 1937 1938 Parameters 1939 ---------- 1940 None 1941 1942 Returns 1943 ------- 1944 None 1945 This function does not return a value. 1946 """ 1947 self.member_vars["optimizer_instance"] = None 1948 self.member_vars["scheduler_instance"] = None
Clear the instances for saving.
Parameters
- None
Returns
- None: This function does not return a value.
1950 def switch_time(self): 1951 """Determine if it's time to switch between neuron and dendrite training. 1952 1953 Parameters 1954 ---------- 1955 None 1956 1957 Returns 1958 ------- 1959 bool 1960 True if it's time to switch, False otherwise. 1961 1962 Notes 1963 ----- 1964 Based on current settings and history of scores. 1965 """ 1966 1967 switch_phrase = "No mode, this should never be the case." 1968 switch_number = GPA.pc.get_n_epochs_to_switch() 1969 if self.member_vars["switch_mode"] == GPA.pc.DOING_SWITCH_EVERY_TIME: 1970 switch_phrase = "DOING_SWITCH_EVERY_TIME" 1971 elif self.member_vars["switch_mode"] == GPA.pc.DOING_HISTORY: 1972 switch_phrase = "DOING_HISTORY" 1973 elif self.member_vars["switch_mode"] == GPA.pc.DOING_FIXED_SWITCH: 1974 switch_phrase = "DOING_FIXED_SWITCH" 1975 switch_number = GPA.pc.get_fixed_switch_num() 1976 elif self.member_vars["switch_mode"] == GPA.pc.DOING_NO_SWITCH: 1977 switch_phrase = "DOING_NO_SWITCH" 1978 else: 1979 print( 1980 "A switch mode must be set. Check your settings for GPA.pc.set_switch_mode()." 1981 ) 1982 pdb.set_trace() 1983 if not GPA.pc.get_silent(): 1984 if(GPA.pc.get_perforated_backpropagation()): 1985 print( 1986 f'Checking PAI switch with mode {self.member_vars["mode"]}, ' 1987 f'switch mode {switch_phrase}, epoch {self.member_vars["num_epochs_run"]}, ' 1988 f'last improved epoch {self.member_vars["epoch_last_improved"]}, ' 1989 f'total epochs {self.member_vars["total_epochs_run"]}, ' 1990 f'n: {switch_number}, p: {GPA.pc.get_p_epochs_to_switch()}, ' 1991 f'num_cycles: {self.member_vars["num_cycles"]}' 1992 ) 1993 else: 1994 print( 1995 f'Checking PAI switch with mode {self.member_vars["mode"]}, ' 1996 f'switch mode {switch_phrase}, epoch {self.member_vars["num_epochs_run"]}, ' 1997 f'last improved epoch {self.member_vars["epoch_last_improved"]}, ' 1998 f'total epochs {self.member_vars["total_epochs_run"]}, ' 1999 f'n: {switch_number}, num_cycles: {self.member_vars["num_cycles"]}' 2000 ) 2001 print( 2002 f' Score tracking: current_n_set_global_best={self.member_vars["current_n_set_global_best"]}, ' 2003 f'global_best={self.member_vars["global_best_validation_score"]:.4f}, ' 2004 f'current_best={self.member_vars["current_best_validation_score"]:.4f}' 2005 ) 2006 if GPA.pc.get_perforated_backpropagation(): 2007 # this will fill in epoch last improved 2008 TPB.best_pai_score_improved_this_epoch(self) ## CLOSED ONLY 2009 if self.member_vars["switch_mode"] == GPA.pc.DOING_NO_SWITCH: 2010 if not GPA.pc.get_silent(): 2011 print("Returning False - doing no switch mode") 2012 return False 2013 2014 if self.member_vars["switch_mode"] == GPA.pc.DOING_SWITCH_EVERY_TIME: 2015 if not GPA.pc.get_silent(): 2016 print("Returning True - switching every time") 2017 return True 2018 2019 # Check if we're in the middle of learning rate optimization 2020 # If so, block ALL switch triggers until committed 2021 if GPA.pc.get_verbose(): 2022 print("=== LR Optimization Check ===") 2023 print(f' mode == "n": {self.member_vars["mode"] == "n"}') 2024 print(f" get_learn_dendrites_live(): {GPA.pc.get_learn_dendrites_live()}") 2025 print(f' committed_to_initial_rate: {GPA.pai_tracker.member_vars["committed_to_initial_rate"]}') 2026 print(f" get_dont_give_up_unless_learning_rate_lowered(): {GPA.pc.get_dont_give_up_unless_learning_rate_lowered()}") 2027 print(f' current_n_learning_rate_initial_skip_steps: {self.member_vars["current_n_learning_rate_initial_skip_steps"]}') 2028 print(f' last_max_learning_rate_steps: {self.member_vars["last_max_learning_rate_steps"]}') 2029 print(f' skip_steps < max_steps: {self.member_vars["current_n_learning_rate_initial_skip_steps"] < self.member_vars["last_max_learning_rate_steps"]}') 2030 print(f' scheduler is not None: {self.member_vars["scheduler"] is not None}') 2031 print("=============================") 2032 2033 if ( 2034 ((self.member_vars["mode"] == "n") or GPA.pc.get_learn_dendrites_live()) 2035 and (GPA.pai_tracker.member_vars["committed_to_initial_rate"] is False) 2036 and (GPA.pc.get_dont_give_up_unless_learning_rate_lowered()) 2037 and ( 2038 self.member_vars["current_n_learning_rate_initial_skip_steps"] 2039 <= self.member_vars["last_max_learning_rate_steps"] 2040 ) 2041 and self.member_vars["scheduler"] is not None 2042 ): 2043 if not GPA.pc.get_silent(): 2044 print( 2045 f"Returning False - learning rate optimization in progress. " 2046 f"Not committed yet. Comparing " 2047 f'initial {self.member_vars["current_n_learning_rate_initial_skip_steps"]} ' 2048 f'to last max {self.member_vars["last_max_learning_rate_steps"]}' 2049 ) 2050 return False 2051 2052 if len(self.member_vars["switch_epochs"]) == 0: 2053 this_count = self.member_vars["num_epochs_run"] 2054 else: 2055 this_count = ( 2056 self.member_vars["num_epochs_run"] 2057 - self.member_vars["switch_epochs"][-1] 2058 ) 2059 cap_switch = False 2060 if GPA.pc.get_perforated_backpropagation(): 2061 cap_switch = TPB.check_cap_switch(self, this_count) 2062 2063 if self.member_vars["switch_mode"] == GPA.pc.DOING_HISTORY and ( 2064 ( 2065 (self.member_vars["mode"] == "n") 2066 and ( 2067 self.member_vars["num_epochs_run"] 2068 - self.member_vars["epoch_last_improved"] 2069 >= GPA.pc.get_n_epochs_to_switch() 2070 ) 2071 and this_count 2072 >= GPA.pc.get_initial_history_after_switches() 2073 + GPA.pc.get_n_epochs_to_switch() 2074 ) 2075 or (GPA.pc.get_perforated_backpropagation() and TPB.history_switch(self)) 2076 or cap_switch 2077 ): 2078 if not GPA.pc.get_silent(): 2079 print("Returning True - History and last improved is hit") 2080 return True 2081 2082 if self.member_vars["switch_mode"] == GPA.pc.DOING_FIXED_SWITCH and ( 2083 ( 2084 self.member_vars["total_epochs_run"] % GPA.pc.get_fixed_switch_num() 2085 == GPA.pc.get_fixed_switch_num() - 1 2086 ) 2087 and self.member_vars["num_epochs_run"] 2088 >= GPA.pc.get_first_fixed_switch_num() - 1 2089 ): 2090 if not GPA.pc.get_silent(): 2091 print("Returning True - Fixed switch number is hit") 2092 return True 2093 2094 if not GPA.pc.get_silent(): 2095 print("Returning False - no triggers to switch have been hit") 2096 return False
Determine if it's time to switch between neuron and dendrite training.
Parameters
- None
Returns
- bool: True if it's time to switch, False otherwise.
Notes
Based on current settings and history of scores.
2098 def steps_after_switch(self): 2099 """Based on settings, return value for steps since a switch. 2100 2101 Different options for param vals setting determine what is returned. 2102 2103 Parameters 2104 ---------- 2105 None 2106 2107 Returns 2108 ------- 2109 int 2110 The number of epochs since the last switch, or total epochs run, 2111 depending on settings. 2112 2113 """ 2114 if self.member_vars["param_vals_setting"] == GPA.pc.PARAM_VALS_BY_TOTAL_EPOCH: 2115 return self.member_vars["num_epochs_run"] 2116 elif ( 2117 self.member_vars["param_vals_setting"] == GPA.pc.PARAM_VALS_BY_UPDATE_EPOCH 2118 ): 2119 return self.member_vars["num_epochs_run"] - self.member_vars["last_switch"] 2120 elif ( 2121 self.member_vars["param_vals_setting"] 2122 == GPA.pc.PARAM_VALS_BY_NEURON_EPOCH_START 2123 ): 2124 if self.member_vars["mode"] == "p": 2125 return ( 2126 self.member_vars["num_epochs_run"] - self.member_vars["last_switch"] 2127 ) 2128 else: 2129 return self.member_vars["num_epochs_run"] 2130 else: 2131 print( 2132 f'{self.member_vars["param_vals_setting"]} is not a valid param vals option' 2133 ) 2134 pdb.set_trace()
Based on settings, return value for steps since a switch.
Different options for param vals setting determine what is returned.
Parameters
- None
Returns
- int: The number of epochs since the last switch, or total epochs run, depending on settings.
2136 def add_pai_neuron_module(self, new_module, initial_add=True): 2137 """Add neuron modules to internal vectors. 2138 2139 Parameters 2140 ---------- 2141 new_module : object 2142 The new module to add. 2143 initial_add : bool, optional 2144 Whether this is the initial addition rather than loading from file 2145 2146 Returns 2147 ------- 2148 None 2149 2150 """ 2151 2152 # If it's a duplicate, ignore the second addition 2153 if new_module in self.neuron_module_vector: 2154 return 2155 self.neuron_module_vector.append(new_module) 2156 if self.member_vars["doing_pai"]: 2157 PA.set_wrapped_params(new_module) 2158 if initial_add: 2159 self.member_vars["best_scores"].append([]) 2160 self.member_vars["current_scores"].append([])
Add neuron modules to internal vectors.
Parameters
- new_module (object): The new module to add.
- initial_add (bool, optional): Whether this is the initial addition rather than loading from file
Returns
- None
2162 def add_tracked_neuron_module(self, new_module, initial_add=True): 2163 """Add tracked modules to internal vectors 2164 2165 Parameters 2166 ---------- 2167 new_module : object 2168 The new module to add. 2169 initial_add : bool, optional 2170 Whether this is the initial addition rather than loading from file 2171 2172 Returns 2173 ------- 2174 None 2175 2176 """ 2177 # If it's a duplicate, ignore the second addition 2178 if new_module in self.tracked_neuron_module_vector: 2179 return 2180 self.tracked_neuron_module_vector.append(new_module) 2181 if self.member_vars["doing_pai"]: 2182 PA.set_tracked_params(new_module)
Add tracked modules to internal vectors
Parameters
- new_module (object): The new module to add.
- initial_add (bool, optional): Whether this is the initial addition rather than loading from file
Returns
- None
2184 def reset_module_vector(self, net, load_from_restart): 2185 """Clear internal vectors and reset from network. 2186 2187 Parameters 2188 ---------- 2189 net : object 2190 The neural network model. 2191 load_from_restart : bool 2192 Whether loading from a restart file. 2193 2194 Returns 2195 ------- 2196 None 2197 2198 """ 2199 self.neuron_module_vector = [] 2200 self.tracked_neuron_module_vector = [] 2201 this_list = UPA.get_pai_modules(net, 0) 2202 for module in this_list: 2203 self.add_pai_neuron_module(module, initial_add=load_from_restart) 2204 this_list = UPA.get_tracked_modules(net, 0) 2205 for module in this_list: 2206 self.add_tracked_neuron_module(module, initial_add=load_from_restart)
Clear internal vectors and reset from network.
Parameters
- net (object): The neural network model.
- load_from_restart (bool): Whether loading from a restart file.
Returns
- None
2208 def reset_vals_for_score_reset(self): 2209 """Reset cycle scores for new cycle. 2210 2211 Parameters 2212 ---------- 2213 None 2214 2215 Returns 2216 ------- 2217 None 2218 This function does not return a value. 2219 """ 2220 2221 if GPA.pc.get_find_best_lr(): 2222 self.member_vars["committed_to_initial_rate"] = False 2223 print("Resetting committed to initial rate to False") 2224 # If retaining all dendrties always say that the current dendrites set global best for saving and loading 2225 if GPA.pc.get_retain_all_dendrites(): 2226 self.member_vars["current_n_set_global_best"] = True 2227 self.member_vars["global_best_validation_score"] = 0 2228 else: 2229 self.member_vars["current_n_set_global_best"] = False 2230 2231 # Don't reset global best, but do reset current best 2232 self.member_vars["current_best_validation_score"] = 0 2233 self.member_vars["initial_lr_test_epoch_count"] = -1
Reset cycle scores for new cycle.
Parameters
- None
Returns
- None: This function does not return a value.
2235 def set_dendrite_training(self): 2236 """Signal all layers to start dendrite training. 2237 2238 Parameters 2239 ---------- 2240 None 2241 2242 Returns 2243 ------- 2244 None 2245 This function does not return a value. 2246 """ 2247 if GPA.pc.get_verbose(): 2248 print("Calling set_dendrite_training") 2249 2250 for layer in self.neuron_module_vector[:]: 2251 worked = layer.set_mode("p") 2252 """ 2253 worked is False when a layer was added to the neuron module vector 2254 but then it's never actually been used. This can happen when 2255 you have set a layer to have requires_grad = False or when 2256 you have a module as a member variable but it's not actually 2257 part of the network. Should be moved to be a tracked layer 2258 rather than a neuron layer. 2259 """ 2260 if not worked: 2261 self.neuron_module_vector.remove(layer) 2262 2263 for layer in self.tracked_neuron_module_vector[:]: 2264 worked = layer.set_mode("p") 2265 2266 self.create_new_dendrite_module() 2267 self.member_vars["mode"] = "p" 2268 self.member_vars["current_n_learning_rate_initial_skip_steps"] = 0 2269 2270 if GPA.pc.get_learn_dendrites_live(): 2271 self.reset_vals_for_score_reset() 2272 2273 self.member_vars["last_max_learning_rate_steps"] = self.member_vars[ 2274 "current_step_count" 2275 ] 2276 2277 GPA.pai_tracker.member_vars["current_cycle_lr_max_scores"] = [] 2278 GPA.pai_tracker.member_vars["num_cycles"] += 1
Signal all layers to start dendrite training.
Parameters
- None
Returns
- None: This function does not return a value.
2281 def set_neuron_training(self): 2282 """Signal all layers to start neuron training. 2283 2284 Parameters 2285 ---------- 2286 None 2287 2288 Returns 2289 ------- 2290 None 2291 This function does not return a value. 2292 """ 2293 for module in self.neuron_module_vector: 2294 module.set_mode("n") 2295 for module in self.tracked_neuron_module_vector[:]: 2296 module.set_mode("n") 2297 2298 self.member_vars["mode"] = "n" 2299 self.member_vars["num_dendrites_added"] += 1 2300 self.member_vars["current_n_learning_rate_initial_skip_steps"] = 0 2301 self.reset_vals_for_score_reset() 2302 2303 self.member_vars["current_cycle_lr_max_scores"] = [] 2304 if GPA.pc.get_learn_dendrites_live(): 2305 self.member_vars["last_max_learning_rate_steps"] = self.member_vars[ 2306 "current_step_count" 2307 ] 2308 GPA.pai_tracker.member_vars["num_cycles"] += 1 2309 2310 if GPA.pc.get_reset_best_score_on_switch(): 2311 GPA.pai_tracker.member_vars["current_best_validation_score"] = 0 2312 GPA.pai_tracker.member_vars["running_accuracy"] = 0
Signal all layers to start neuron training.
Parameters
- None
Returns
- None: This function does not return a value.
2314 def start_epoch(self, internal_call=False): 2315 """Perform steps for when a new training epoch is about to begin. 2316 2317 Parameters 2318 ---------- 2319 internal_call : bool, optional 2320 Whether this is an internal call or manual call 2321 2322 Returns 2323 ------- 2324 None 2325 2326 Notes 2327 ----- 2328 If you ever need to call this manually, set internal_call to False. 2329 2330 """ 2331 if self.member_vars["manual_train_switch"] and internal_call: 2332 return 2333 2334 if not internal_call and not self.member_vars["manual_train_switch"]: 2335 self.member_vars["manual_train_switch"] = True 2336 self.saved_time = 0 2337 self.member_vars["num_epochs_run"] = -1 2338 self.member_vars["total_epochs_run"] = -1 2339 2340 end = time.time() 2341 if self.member_vars["manual_train_switch"]: 2342 if self.saved_time != 0: 2343 if self.member_vars["mode"] == "p": 2344 self.member_vars["p_val_times"].append(end - self.saved_time) 2345 else: 2346 self.member_vars["n_val_times"].append(end - self.saved_time) 2347 2348 if self.member_vars["mode"] == "p": 2349 for layer in self.neuron_module_vector: 2350 for m in range(0, GPA.pc.get_global_candidates()): 2351 with torch.no_grad(): 2352 if GPA.pc.get_verbose(): 2353 print(f"Resetting score for {layer.name}") 2354 # Snapshot best_score before reset so we can compute per-epoch improvement 2355 layer.dendrite_module.dendrite_values[ 2356 m 2357 ].epoch_start_best_score.copy_( 2358 layer.dendrite_module.dendrite_values[ 2359 m 2360 ].best_score.detach() 2361 ) 2362 layer.dendrite_module.dendrite_values[ 2363 m 2364 ].best_score_improved_this_epoch = ( 2365 layer.dendrite_module.dendrite_values[ 2366 m 2367 ].best_score_improved_this_epoch 2368 * 0 2369 ) 2370 layer.dendrite_module.dendrite_values[ 2371 m 2372 ].nodes_best_improved_this_epoch = ( 2373 layer.dendrite_module.dendrite_values[ 2374 m 2375 ].nodes_best_improved_this_epoch 2376 * 0 2377 ) 2378 layer.dendrite_module.dendrite_values[ 2379 m 2380 ].nodes_improved_any = ( 2381 layer.dendrite_module.dendrite_values[ 2382 m 2383 ].nodes_improved_any 2384 * 0 2385 ) 2386 if GPA.pc.get_perforated_backpropagation(): 2387 self.member_vars["best_mean_score_improved_this_epoch"] = 0 2388 self.member_vars["num_epochs_run"] += 1 2389 self.member_vars["total_epochs_run"] = ( 2390 self.member_vars["num_epochs_run"] + self.member_vars["overwritten_epochs"] 2391 ) 2392 self.saved_time = end
Perform steps for when a new training epoch is about to begin.
Parameters
- internal_call (bool, optional): Whether this is an internal call or manual call
Returns
- None
Notes
If you ever need to call this manually, set internal_call to False.
2394 def stop_epoch(self, internal_call=False): 2395 """Perform steps when a training epoch has completed. 2396 2397 Parameters 2398 ---------- 2399 internal_call : bool, optional 2400 Whether this is an internal call or manual call 2401 2402 Returns 2403 ------- 2404 None 2405 2406 Notes 2407 ----- 2408 If you ever need to call this manually, set internal_call to False. 2409 2410 """ 2411 end = time.time() 2412 if self.member_vars["manual_train_switch"] and internal_call: 2413 return 2414 2415 if self.member_vars["manual_train_switch"]: 2416 if self.member_vars["mode"] == "p": 2417 self.member_vars["p_train_times"].append(end - self.saved_time) 2418 else: 2419 self.member_vars["n_train_times"].append(end - self.saved_time) 2420 else: 2421 if self.member_vars["mode"] == "p": 2422 self.member_vars["p_epoch_times"].append(end - self.saved_time) 2423 else: 2424 self.member_vars["n_epoch_times"].append(end - self.saved_time) 2425 2426 self.saved_time = end
Perform steps when a training epoch has completed.
Parameters
- internal_call (bool, optional): Whether this is an internal call or manual call
Returns
- None
Notes
If you ever need to call this manually, set internal_call to False.
2428 def initialize( 2429 self, 2430 model, 2431 doing_pai=True, 2432 save_name="PAI", 2433 making_graphs=True, 2434 maximizing_score=True, 2435 num_classes=10000, 2436 values_per_train_epoch=-1, 2437 values_per_val_epoch=-1, 2438 zooming_graph=True, 2439 ): 2440 """Setup the tracker with initial settings. 2441 2442 2443 Parameters 2444 ---------- 2445 model : object 2446 The neural network model. 2447 doing_pai : bool, optional 2448 Whether to add dendrites, by default True. 2449 save_name : str, optional 2450 The name under which to save the model. 2451 making_graphs : bool, optional 2452 Whether to make graphs, by default True. 2453 maximizing_score : bool, optional 2454 Whether to maximize the score, by default True. 2455 num_classes : int, optional 2456 The number of classes in the dataset, unused 2457 values_per_train_epoch : int, optional 2458 The number of values to look back for graphing 2459 during training, by default -1 (all values). 2460 values_per_val_epoch : int, optional 2461 The number of values to look back for graphing 2462 during validation, by default -1 (all values). 2463 zooming_graph : bool, optional 2464 Whether to zoom on graphs, by default True. 2465 2466 2467 Returns 2468 ------- 2469 nn.Module 2470 Converted model instance configured for the tracker settings. 2471 """ 2472 model = UPA.convert_network(model) 2473 self.member_vars["doing_pai"] = doing_pai 2474 self.member_vars["maximizing_score"] = maximizing_score 2475 self.save_name = save_name 2476 self.zooming_graph = zooming_graph 2477 self.making_graphs = making_graphs 2478 2479 if not self.loaded: 2480 self.member_vars["running_accuracy"] = (1.0 / num_classes) * 100 2481 2482 self.values_per_train_epoch = values_per_train_epoch 2483 self.values_per_val_epoch = values_per_val_epoch 2484 2485 if GPA.pc.get_testing_dendrite_capacity(): 2486 if not GPA.pc.get_silent(): 2487 print("Running a test of Dendrite Capacity.") 2488 GPA.pc.set_switch_mode(GPA.pc.DOING_SWITCH_EVERY_TIME) 2489 self.member_vars["switch_mode"] = GPA.pc.get_switch_mode() 2490 GPA.pc.set_retain_all_dendrites(True) 2491 GPA.pc.set_max_dendrite_tries(1000) 2492 GPA.pc.set_max_dendrites(1000) 2493 if GPA.pc.get_perforated_backpropagation(): 2494 GPA.pc.set_initial_correlation_batches(1) 2495 else: 2496 if not GPA.pc.get_silent(): 2497 print("Running Dendrite Experiment") 2498 return model
Setup the tracker with initial settings.
Parameters
- model (object): The neural network model.
- doing_pai (bool, optional): Whether to add dendrites, by default True.
- save_name (str, optional): The name under which to save the model.
- making_graphs (bool, optional): Whether to make graphs, by default True.
- maximizing_score (bool, optional): Whether to maximize the score, by default True.
- num_classes (int, optional): The number of classes in the dataset, unused
- values_per_train_epoch (int, optional): The number of values to look back for graphing during training, by default -1 (all values).
- values_per_val_epoch (int, optional): The number of values to look back for graphing during validation, by default -1 (all values).
- zooming_graph (bool, optional): Whether to zoom on graphs, by default True.
Returns
- nn.Module: Converted model instance configured for the tracker settings.
2500 def generate_accuracy_plots(self, ax, save_folder, extra_string): 2501 """ 2502 Generate plots and csvs for accuracy 2503 2504 Parameters 2505 ---------- 2506 ax : object 2507 The matplotlib axis to plot on. 2508 save_folder : str 2509 The folder to save the plots and csvs in. 2510 extra_string : str 2511 An extra string to append to the filenames. 2512 2513 Returns 2514 ------- 2515 None 2516 2517 """ 2518 2519 # If scores are being saved for epochs that get overwritten, plot them 2520 for list_id in range(len(self.member_vars["overwritten_extras"])): 2521 for extra_id in self.member_vars["overwritten_extras"][list_id]: 2522 ax.plot( 2523 np.arange( 2524 len(self.member_vars["overwritten_extras"][list_id][extra_id]) 2525 ), 2526 self.member_vars["overwritten_extras"][list_id][extra_id], 2527 color=_PAI_PALETTE["train"], 2528 alpha=0.35, 2529 ) 2530 ax.plot( 2531 np.arange(len(self.member_vars["overwritten_vals"][list_id])), 2532 self.member_vars["overwritten_vals"][list_id], 2533 color=_PAI_PALETTE["validation"], 2534 alpha=0.35, 2535 ) 2536 2537 # Determine which accuracy vector to use 2538 if GPA.pc.get_drawing_pai(): 2539 accuracies = self.member_vars["accuracies"] 2540 else: 2541 accuracies = self.member_vars["n_accuracies"] 2542 2543 # Get pointer to additional scores being saved 2544 extra_scores = self.member_vars["extra_scores"] 2545 2546 # Plot the main accuracy scores 2547 ax.plot( 2548 np.arange(len(accuracies)), 2549 accuracies, 2550 label="Validation Scores", 2551 color=_PAI_PALETTE["validation"], 2552 ) 2553 ax.plot( 2554 np.arange(len(self.member_vars["running_accuracies"])), 2555 self.member_vars["running_accuracies"], 2556 label="Validation Running Scores", 2557 color=_PAI_PALETTE["validation"], 2558 alpha=0.4, 2559 ) 2560 2561 # Plot additional scores 2562 for extra_index, extra_score in enumerate(extra_scores): 2563 ax.plot( 2564 np.arange(len(extra_scores[extra_score])), 2565 extra_scores[extra_score], 2566 label=extra_score, 2567 color=_pai_extra_score_color(extra_score, extra_index), 2568 ) 2569 2570 plt.title(save_folder + "/" + self.save_name + "Scores") 2571 plt.xlabel("Epochs") 2572 plt.ylabel("Score") 2573 2574 # Add point at epoch last improved and best validation score 2575 if GPA.pc.get_drawing_pai(): 2576 ax.plot( 2577 self.member_vars["epoch_last_improved"], 2578 self.member_vars["global_best_validation_score"], 2579 "o", 2580 color=_PAI_PALETTE["marker_best"], 2581 label="Global best (y)", 2582 ) 2583 ax.plot( 2584 self.member_vars["epoch_last_improved"], 2585 accuracies[self.member_vars["epoch_last_improved"]], 2586 "o", 2587 color=_PAI_PALETTE["marker_improved"], 2588 label="Epoch Last Improved", 2589 ) 2590 else: 2591 if self.member_vars["mode"] == "n": 2592 missed_time = ( 2593 self.member_vars["num_epochs_run"] 2594 - self.member_vars["epoch_last_improved"] 2595 ) 2596 ax.plot( 2597 (len(self.member_vars["n_accuracies"]) - 1) - missed_time, 2598 self.member_vars["n_accuracies"][-(missed_time + 1)], 2599 "o", 2600 color=_PAI_PALETTE["marker_improved"], 2601 label="Epoch Last Improved", 2602 ) 2603 2604 # Generate csv file for the values graphed 2605 pd1 = pd.DataFrame( 2606 {"Epochs": np.arange(len(accuracies)), "Validation Scores": accuracies} 2607 ) 2608 pd2 = pd.DataFrame( 2609 { 2610 "Epochs": np.arange(len(self.member_vars["running_accuracies"])), 2611 "Validation Running Scores": self.member_vars["running_accuracies"], 2612 } 2613 ) 2614 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2615 for extra_score in extra_scores: 2616 pd2 = pd.DataFrame( 2617 { 2618 "Epochs": np.arange(len(extra_scores[extra_score])), 2619 extra_score: extra_scores[extra_score], 2620 } 2621 ) 2622 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2623 extra_scores_without_graphing = self.member_vars[ 2624 "extra_scores_without_graphing" 2625 ] 2626 for extra_score in extra_scores_without_graphing: 2627 pd2 = pd.DataFrame( 2628 { 2629 "Epochs": np.arange( 2630 len(extra_scores_without_graphing[extra_score]) 2631 ), 2632 extra_score: extra_scores_without_graphing[extra_score], 2633 } 2634 ) 2635 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2636 pd1.to_csv( 2637 save_folder + "/" + self.save_name + extra_string + "Scores.csv", 2638 index=False, 2639 ) 2640 del pd1, pd2 2641 2642 # Set y min and max to zoom in on important part of axis 2643 if ( 2644 len(self.member_vars["switch_epochs"]) > 0 2645 and self.member_vars["switch_epochs"][0] > 0 2646 and self.zooming_graph 2647 ): 2648 if GPA.pai_tracker.member_vars["maximizing_score"]: 2649 min_val = np.array( 2650 accuracies[0 : self.member_vars["switch_epochs"][0]] 2651 ).mean() 2652 for extra_score in extra_scores: 2653 min_pot = np.array( 2654 extra_scores[extra_score][ 2655 0 : self.member_vars["switch_epochs"][0] 2656 ] 2657 ).mean() 2658 if min_pot < min_val: 2659 min_val = min_pot 2660 ax.set_ylim(ymin=min_val) 2661 else: 2662 max_val = np.array( 2663 accuracies[0 : self.member_vars["switch_epochs"][0]] 2664 ).mean() 2665 for extra_score in extra_scores: 2666 max_pot = np.array( 2667 extra_scores[extra_score][ 2668 0 : self.member_vars["switch_epochs"][0] 2669 ] 2670 ).mean() 2671 if max_pot > max_val: 2672 max_val = max_pot 2673 ax.set_ylim(ymax=max_val) 2674 2675 ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left") 2676 _pai_grid(ax) 2677 2678 # Draw vertical lines for epochs where a dendrite switch occurred. 2679 # Colour by the phase being entered: dendrite/PAI phase -> teal, 2680 # normal phase -> orange, alternating starting with dendrite. 2681 if GPA.pc.get_drawing_pai() and self.member_vars["doing_pai"]: 2682 for i, switcher in enumerate(self.member_vars["switch_epochs"]): 2683 # Mode always starts at "n" and switch_epochs alternates 2684 # n->p->n->p..., so the first switch enters the dendrite 2685 # phase and every other one alternates from there. 2686 entering_p = (i % 2) == 0 2687 plt.axvline( 2688 x=switcher, 2689 ymin=0, 2690 ymax=1, 2691 color=_PAI_PALETTE["dendrite"] 2692 if entering_p 2693 else _PAI_PALETTE["normal"], 2694 ) 2695 else: 2696 for switcher in self.member_vars["n_switch_epochs"]: 2697 plt.axvline( 2698 x=switcher, ymin=0, ymax=1, color=_PAI_PALETTE["normal"] 2699 )
Generate plots and csvs for accuracy
Parameters
- ax (object): The matplotlib axis to plot on.
- save_folder (str): The folder to save the plots and csvs in.
- extra_string (str): An extra string to append to the filenames.
Returns
- None
2701 def generate_time_plots(self, ax, save_folder, extra_string): 2702 """ 2703 Generate plots and csvs for timing 2704 2705 Parameters 2706 ---------- 2707 ax : object 2708 The matplotlib axis to plot on. 2709 save_folder : str 2710 The folder to save the plots and csvs in. 2711 extra_string : str 2712 An extra string to append to the filenames. 2713 2714 Returns 2715 ------- 2716 None 2717 2718 """ 2719 if self.member_vars["manual_train_switch"]: 2720 ax.plot( 2721 np.arange(len(self.member_vars["n_train_times"])), 2722 self.member_vars["n_train_times"], 2723 label="Normal Epoch Train Times", 2724 color=_PAI_PALETTE["normal"], 2725 ) 2726 ax.plot( 2727 np.arange(len(self.member_vars["p_train_times"])), 2728 self.member_vars["p_train_times"], 2729 label="PAI Epoch Train Times", 2730 color=_PAI_PALETTE["dendrite"], 2731 ) 2732 ax.plot( 2733 np.arange(len(self.member_vars["n_val_times"])), 2734 self.member_vars["n_val_times"], 2735 label="Normal Epoch Val Times", 2736 color=_PAI_PALETTE["normal"], 2737 alpha=0.45, 2738 ) 2739 ax.plot( 2740 np.arange(len(self.member_vars["p_val_times"])), 2741 self.member_vars["p_val_times"], 2742 label="PAI Epoch Val Times", 2743 color=_PAI_PALETTE["dendrite"], 2744 alpha=0.45, 2745 ) 2746 2747 plt.title( 2748 save_folder + "/" + self.save_name + "times (by train() and eval())" 2749 ) 2750 plt.xlabel("Iteration") 2751 plt.ylabel("Epoch Time in Seconds ") 2752 ax.set_ylim(ymin=0) 2753 ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left") 2754 _pai_grid(ax) 2755 2756 pd1 = pd.DataFrame( 2757 { 2758 "Epochs": np.arange(len(self.member_vars["n_train_times"])), 2759 "Normal Epoch Train Times": self.member_vars["n_train_times"], 2760 } 2761 ) 2762 pd2 = pd.DataFrame( 2763 { 2764 "Epochs": np.arange(len(self.member_vars["p_train_times"])), 2765 "PAI Epoch Train Times": self.member_vars["p_train_times"], 2766 } 2767 ) 2768 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2769 2770 pd2 = pd.DataFrame( 2771 { 2772 "Epochs": np.arange(len(self.member_vars["n_val_times"])), 2773 "Normal Epoch Val Times": self.member_vars["n_val_times"], 2774 } 2775 ) 2776 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2777 2778 pd2 = pd.DataFrame( 2779 { 2780 "Epochs": np.arange(len(self.member_vars["p_val_times"])), 2781 "PAI Epoch Val Times": self.member_vars["p_val_times"], 2782 } 2783 ) 2784 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2785 2786 pd1.to_csv( 2787 save_folder + "/" + self.save_name + extra_string + "Times.csv", 2788 index=False, 2789 ) 2790 del pd1, pd2 2791 else: 2792 ax.plot( 2793 np.arange(len(self.member_vars["n_epoch_times"])), 2794 self.member_vars["n_epoch_times"], 2795 label="Normal Epoch Times", 2796 color=_PAI_PALETTE["normal"], 2797 ) 2798 ax.plot( 2799 np.arange(len(self.member_vars["p_epoch_times"])), 2800 self.member_vars["p_epoch_times"], 2801 label="PAI Epoch Times", 2802 color=_PAI_PALETTE["dendrite"], 2803 ) 2804 2805 plt.title( 2806 save_folder + "/" + self.save_name + "times (by train() and eval())" 2807 ) 2808 plt.xlabel("Iteration") 2809 plt.ylabel("Epoch Time in Seconds ") 2810 ax.set_ylim(ymin=0) 2811 ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left") 2812 _pai_grid(ax) 2813 2814 pd1 = pd.DataFrame( 2815 { 2816 "Epochs": np.arange(len(self.member_vars["n_epoch_times"])), 2817 "Normal Epoch Times": self.member_vars["n_epoch_times"], 2818 } 2819 ) 2820 pd2 = pd.DataFrame( 2821 { 2822 "Epochs": np.arange(len(self.member_vars["p_epoch_times"])), 2823 "PAI Epoch Times": self.member_vars["p_epoch_times"], 2824 } 2825 ) 2826 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 2827 2828 pd1.to_csv( 2829 save_folder + "/" + self.save_name + extra_string + "Times.csv", 2830 index=False, 2831 ) 2832 del pd1, pd2 2833 2834 if self.values_per_train_epoch != -1 and self.values_per_val_epoch != -1: 2835 ax2 = ax.twinx() # Second axes sharing same x-axis 2836 ax2.set_ylabel("Single Datapoint Time in Seconds") 2837 2838 ax2.plot( 2839 np.arange(len(self.member_vars["n_train_times"])), 2840 np.array(self.member_vars["n_train_times"]) 2841 / self.values_per_train_epoch, 2842 linestyle="dashed", 2843 label="Normal Train Item Times", 2844 color=_PAI_PALETTE["normal"], 2845 ) 2846 ax2.plot( 2847 np.arange(len(self.member_vars["p_train_times"])), 2848 np.array(self.member_vars["p_train_times"]) 2849 / self.values_per_train_epoch, 2850 linestyle="dashed", 2851 label="PAI Train Item Times", 2852 color=_PAI_PALETTE["dendrite"], 2853 ) 2854 ax2.plot( 2855 np.arange(len(self.member_vars["n_val_times"])), 2856 np.array(self.member_vars["n_val_times"]) / self.values_per_val_epoch, 2857 linestyle="dashed", 2858 label="Normal Val Item Times", 2859 color=_PAI_PALETTE["normal"], 2860 alpha=0.45, 2861 ) 2862 ax2.plot( 2863 np.arange(len(self.member_vars["p_val_times"])), 2864 np.array(self.member_vars["p_val_times"]) / self.values_per_val_epoch, 2865 linestyle="dashed", 2866 label="PAI Val Item Times", 2867 color=_PAI_PALETTE["dendrite"], 2868 alpha=0.45, 2869 ) 2870 ax2.tick_params(axis="y") 2871 ax2.set_ylim(ymin=0) 2872 ax2.legend(bbox_to_anchor=(1.05, 1), loc="upper left")
Generate plots and csvs for timing
Parameters
- ax (object): The matplotlib axis to plot on.
- save_folder (str): The folder to save the plots and csvs in.
- extra_string (str): An extra string to append to the filenames.
Returns
- None
2874 def generate_learning_rate_plots(self, ax, save_folder, extra_string): 2875 """ 2876 Generate plots and csvs for learning rate 2877 2878 Parameters 2879 ---------- 2880 ax : object 2881 The matplotlib axis to plot on. 2882 save_folder : str 2883 The folder to save the plots and csvs in. 2884 extra_string : str 2885 An extra string to append to the filenames. 2886 2887 Returns 2888 ------- 2889 None 2890 2891 """ 2892 ax.plot( 2893 np.arange(len(self.member_vars["training_learning_rates"])), 2894 self.member_vars["training_learning_rates"], 2895 label="learning_rate", 2896 color=_PAI_PALETTE["learning_rate"], 2897 ) 2898 plt.title(save_folder + "/" + self.save_name + "learning_rate") 2899 plt.xlabel("Epochs") 2900 plt.ylabel("learning_rate") 2901 ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left") 2902 _pai_grid(ax) 2903 2904 pd1 = pd.DataFrame( 2905 { 2906 "Epochs": np.arange(len(self.member_vars["training_learning_rates"])), 2907 "learning_rate": self.member_vars["training_learning_rates"], 2908 } 2909 ) 2910 pd1.to_csv( 2911 save_folder + "/" + self.save_name + extra_string + "learning_rate.csv", 2912 index=False, 2913 ) 2914 del pd1
Generate plots and csvs for learning rate
Parameters
- ax (object): The matplotlib axis to plot on.
- save_folder (str): The folder to save the plots and csvs in.
- extra_string (str): An extra string to append to the filenames.
Returns
- None
2916 def get_current_pb_scores(self): 2917 """ 2918 Get the latest best PBScore of each dendrite layer, the same numbers 2919 written to the Best PBScores csv. 2920 2921 Returns 2922 ------- 2923 dict 2924 Layer name to score. Empty outside of dendrite scoring phases, 2925 when no candidate dendrites are being scored. 2926 2927 """ 2928 if not self.member_vars["doing_pai"]: 2929 return {} 2930 if not GPA.pc.get_perforated_backpropagation(): 2931 return {} 2932 # Scores only advance while candidate dendrites are being trained 2933 if ( 2934 self.member_vars["mode"] != "p" 2935 and not GPA.pc.get_learn_dendrites_live() 2936 ): 2937 return {} 2938 2939 scores = {} 2940 for layer_id in range(len(self.neuron_module_vector)): 2941 if layer_id >= len(self.member_vars["best_scores"]): 2942 continue 2943 layer_scores = self.member_vars["best_scores"][layer_id] 2944 if len(layer_scores) == 0: 2945 continue 2946 score = layer_scores[-1] 2947 if hasattr(score, "item"): 2948 score = score.item() 2949 score = float(score) 2950 if math.isnan(score) or math.isinf(score): 2951 continue 2952 scores[self.neuron_module_vector[layer_id].name] = score 2953 return scores
Get the latest best PBScore of each dendrite layer, the same numbers written to the Best PBScores csv.
Returns
- dict: Layer name to score. Empty outside of dendrite scoring phases, when no candidate dendrites are being scored.
2955 def get_current_pb_scores_current(self): 2956 """ 2957 Like :meth:`get_current_pb_scores` but returns the *current* (not best) 2958 PBScore of each dendrite layer - ``current_scores[layer][-1]`` - the 2959 dashed series in ``generate_dendrite_learning_plots``. 2960 2961 Returns 2962 ------- 2963 dict 2964 Layer name to score. Empty outside of dendrite scoring phases. 2965 """ 2966 if not self.member_vars["doing_pai"]: 2967 return {} 2968 if not GPA.pc.get_perforated_backpropagation(): 2969 return {} 2970 if ( 2971 self.member_vars["mode"] != "p" 2972 and not GPA.pc.get_learn_dendrites_live() 2973 ): 2974 return {} 2975 2976 scores = {} 2977 for layer_id in range(len(self.neuron_module_vector)): 2978 if layer_id >= len(self.member_vars["current_scores"]): 2979 continue 2980 layer_scores = self.member_vars["current_scores"][layer_id] 2981 if len(layer_scores) == 0: 2982 continue 2983 score = layer_scores[-1] 2984 if hasattr(score, "item"): 2985 score = score.item() 2986 score = float(score) 2987 if math.isnan(score) or math.isinf(score): 2988 continue 2989 scores[self.neuron_module_vector[layer_id].name] = score 2990 return scores
Like get_current_pb_scores() but returns the current (not best)
PBScore of each dendrite layer - current_scores[layer][-1] - the
dashed series in generate_dendrite_learning_plots.
Returns
- dict: Layer name to score. Empty outside of dendrite scoring phases.
2992 def generate_dendrite_learning_plots(self, ax, save_folder, extra_string): 2993 """ 2994 Generate dendrite score plots for the tracker. 2995 Also saves csv files associated with the plots. 2996 2997 Parameters 2998 ---------- 2999 ax : matplotlib.axes.Axes 3000 Axis used for plotting dendrite-learning curves. 3001 save_folder : str 3002 Directory where plot images and CSV summaries are written. 3003 extra_string : str 3004 Filename suffix used to distinguish this output set. 3005 3006 Returns 3007 ------- 3008 None 3009 Saves plots and score CSV files to disk. 3010 """ 3011 if self.member_vars["doing_pai"]: 3012 pd1 = None 3013 pd2 = None 3014 num_colors = len(self.neuron_module_vector) 3015 3016 layer_colors = [ 3017 _PAI_DENDRITE_CYCLE[i % len(_PAI_DENDRITE_CYCLE)] 3018 for i in range(num_colors) 3019 ] 3020 3021 for layer_id in range(len(self.neuron_module_vector)): 3022 color = layer_colors[layer_id] 3023 ax.plot( 3024 np.arange(len(self.member_vars["best_scores"][layer_id])), 3025 self.member_vars["best_scores"][layer_id], 3026 label=self.neuron_module_vector[layer_id].name, 3027 color=color, 3028 ) 3029 3030 pd2 = pd.DataFrame( 3031 { 3032 "Epochs": np.arange( 3033 len(self.member_vars["best_scores"][layer_id]) 3034 ), 3035 f"Best ever for all nodes Layer {self.neuron_module_vector[layer_id].name}": self.member_vars[ 3036 "best_scores" 3037 ][ 3038 layer_id 3039 ], 3040 } 3041 ) 3042 3043 if pd1 is None: 3044 pd1 = pd2 3045 else: 3046 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 3047 3048 if len(self.member_vars["current_scores"][layer_id]) != 0: 3049 ax.plot( 3050 np.arange(len(self.member_vars["current_scores"][layer_id])), 3051 self.member_vars["current_scores"][layer_id], 3052 label=f"Current:{self.neuron_module_vector[layer_id].name}", 3053 color=color, 3054 linestyle="--", 3055 ) 3056 3057 pd2 = pd.DataFrame( 3058 { 3059 "Epochs": np.arange( 3060 len(self.member_vars["current_scores"][layer_id]) 3061 ), 3062 f"Best current for all nodes Layer {self.neuron_module_vector[layer_id].name}": self.member_vars[ 3063 "current_scores" 3064 ][ 3065 layer_id 3066 ], 3067 } 3068 ) 3069 pd1 = pd.concat([pd1, pd.DataFrame(pd2)], ignore_index=True) 3070 3071 plt.title(save_folder + "/" + self.save_name + " Best PBScores") 3072 plt.xlabel("Epochs") 3073 plt.ylabel("Best PBScore") 3074 ax.legend( 3075 bbox_to_anchor=(1.05, 1), 3076 loc="upper left", 3077 ncol=max(1, math.ceil(len(self.neuron_module_vector) / 30)), 3078 ) 3079 _pai_grid(ax) 3080 for switcher in self.member_vars["p_switch_epochs"]: 3081 plt.axvline( 3082 x=switcher, ymin=0, ymax=1, color=_PAI_PALETTE["dendrite"] 3083 ) 3084 3085 if self.member_vars["mode"] == "p": 3086 missed_time = ( 3087 self.member_vars["num_epochs_run"] 3088 - self.member_vars["epoch_last_improved"] 3089 ) 3090 plt.axvline( 3091 x=(len(self.member_vars["best_scores"][0]) - (missed_time + 1)), 3092 ymin=0, 3093 ymax=1, 3094 color=_PAI_PALETTE["normal"], 3095 ) 3096 3097 # pd1 here will be none if no PB layers are created 3098 if pd1 is not None: 3099 pd1.to_csv( 3100 save_folder 3101 + "/" 3102 + self.save_name 3103 + extra_string 3104 + "Best PBScores.csv", 3105 index=False, 3106 ) 3107 del pd1, pd2
Generate dendrite score plots for the tracker. Also saves csv files associated with the plots.
Parameters
- ax (matplotlib.axes.Axes): Axis used for plotting dendrite-learning curves.
- save_folder (str): Directory where plot images and CSV summaries are written.
- extra_string (str): Filename suffix used to distinguish this output set.
Returns
- None: Saves plots and score CSV files to disk.
3109 def generate_extra_csv_files(self, save_folder, extra_string): 3110 """ 3111 Generate additional csvs 3112 3113 Parameters 3114 ---------- 3115 save_folder : str 3116 The folder to save the plots and csvs in. 3117 extra_string : str 3118 An extra string to append to the filenames. 3119 3120 Returns 3121 ------- 3122 None 3123 3124 """ 3125 pd1 = pd.DataFrame( 3126 { 3127 "Switch Number": np.arange(len(self.member_vars["switch_epochs"])), 3128 "Switch Epoch": self.member_vars["switch_epochs"], 3129 } 3130 ) 3131 pd1.to_csv( 3132 save_folder + "/" + self.save_name + extra_string + "switch_epochs.csv", 3133 index=False, 3134 ) 3135 del pd1 3136 3137 pd1 = pd.DataFrame( 3138 { 3139 "Switch Number": np.arange(len(self.member_vars["param_counts"])), 3140 "Param Count": self.member_vars["param_counts"], 3141 } 3142 ) 3143 pd1.to_csv( 3144 save_folder + "/" + self.save_name + extra_string + "param_counts.csv", 3145 index=False, 3146 ) 3147 del pd1 3148 3149 """ 3150 Create best_arch_scores.csv file 3151 When working with dendrites there is a tradeoff between additional param count and score improvement. 3152 This file will help track that tradeoff by recording the best scores for all extra_scores 3153 and extra_scores_without_graphing for each architecture version. 3154 The scores recorded here are from the epoch when the best validation score was found 3155 within each switch_epoch boundary. 3156 """ 3157 switch_counts = len(self.member_vars["switch_epochs"]) 3158 best_valid = [] 3159 associated_params = [] 3160 3161 # Initialize dictionaries to store best scores for each extra score type 3162 best_extra_scores = {} 3163 for score_name in self.member_vars["extra_scores"]: 3164 best_extra_scores[score_name] = [] 3165 for score_name in self.member_vars["extra_scores_without_graphing"]: 3166 best_extra_scores[score_name] = [] 3167 3168 for switch in range(0, switch_counts, 2): 3169 start_index = 0 3170 if switch != 0: 3171 start_index = self.member_vars["switch_epochs"][switch - 1] + 1 3172 end_index = self.member_vars["switch_epochs"][switch] + 1 3173 3174 if GPA.pai_tracker.member_vars["maximizing_score"]: 3175 best_valid_index = start_index + np.argmax( 3176 self.member_vars["accuracies"][start_index:end_index] 3177 ) 3178 else: 3179 best_valid_index = start_index + np.argmin( 3180 self.member_vars["accuracies"][start_index:end_index] 3181 ) 3182 3183 best_valid_score = self.member_vars["accuracies"][best_valid_index] 3184 best_valid.append(best_valid_score) 3185 3186 # Get corresponding scores from all extra_scores 3187 for score_name in self.member_vars["extra_scores"]: 3188 if best_valid_index < len(self.member_vars["extra_scores"][score_name]): 3189 best_extra_scores[score_name].append( 3190 self.member_vars["extra_scores"][score_name][best_valid_index] 3191 ) 3192 else: 3193 best_extra_scores[score_name].append(None) 3194 3195 # Get corresponding scores from all extra_scores_without_graphing 3196 for score_name in self.member_vars["extra_scores_without_graphing"]: 3197 if best_valid_index < len(self.member_vars["extra_scores_without_graphing"][score_name]): 3198 best_extra_scores[score_name].append( 3199 self.member_vars["extra_scores_without_graphing"][score_name][best_valid_index] 3200 ) 3201 else: 3202 best_extra_scores[score_name].append(None) 3203 3204 if self.member_vars["doing_pai"]: 3205 associated_params.append(self.member_vars["param_counts"][switch]) 3206 else: 3207 associated_params.append(self.member_vars["param_counts"][-1]) 3208 3209 # If in neuron training mode but not the very first epoch 3210 if self.member_vars["mode"] == "n" and ( 3211 (len(self.member_vars["switch_epochs"]) == 0) 3212 or ( 3213 self.member_vars["switch_epochs"][-1] + 1 3214 != len(self.member_vars["accuracies"]) 3215 ) 3216 ): 3217 start_index = 0 3218 if len(self.member_vars["switch_epochs"]) != 0: 3219 start_index = self.member_vars["switch_epochs"][-1] + 1 3220 3221 if GPA.pai_tracker.member_vars["maximizing_score"]: 3222 best_valid_index = start_index + np.argmax( 3223 self.member_vars["accuracies"][start_index:] 3224 ) 3225 else: 3226 best_valid_index = start_index + np.argmin( 3227 self.member_vars["accuracies"][start_index:] 3228 ) 3229 3230 best_valid_score = self.member_vars["accuracies"][best_valid_index] 3231 best_valid.append(best_valid_score) 3232 3233 # Get corresponding scores from all extra_scores 3234 for score_name in self.member_vars["extra_scores"]: 3235 if best_valid_index < len(self.member_vars["extra_scores"][score_name]): 3236 best_extra_scores[score_name].append( 3237 self.member_vars["extra_scores"][score_name][best_valid_index] 3238 ) 3239 else: 3240 best_extra_scores[score_name].append(None) 3241 3242 # Get corresponding scores from all extra_scores_without_graphing 3243 for score_name in self.member_vars["extra_scores_without_graphing"]: 3244 if best_valid_index < len(self.member_vars["extra_scores_without_graphing"][score_name]): 3245 best_extra_scores[score_name].append( 3246 self.member_vars["extra_scores_without_graphing"][score_name][best_valid_index] 3247 ) 3248 else: 3249 best_extra_scores[score_name].append(None) 3250 3251 associated_params.append(self.member_vars["param_counts"][-1]) 3252 3253 # Build dataframe with all columns 3254 csv_data = { 3255 "Param Counts": associated_params, 3256 "Max Valid Scores": best_valid, 3257 } 3258 3259 # Add columns for each extra score 3260 for score_name in best_extra_scores: 3261 csv_data[score_name] = best_extra_scores[score_name] 3262 3263 pd1 = pd.DataFrame(csv_data) 3264 pd1.to_csv( 3265 save_folder + "/" + self.save_name + extra_string + "_best_arch_scores.csv", 3266 index=False, 3267 ) 3268 del pd1
Generate additional csvs
Parameters
- save_folder (str): The folder to save the plots and csvs in.
- extra_string (str): An extra string to append to the filenames.
Returns
- None
3270 def save_graphs(self, extra_string=""): 3271 """ 3272 Save graphs and csvs for all the values the tracker records 3273 3274 Parameters 3275 ---------- 3276 extra_string : str 3277 An extra string to append to the filenames. 3278 3279 Returns 3280 ------- 3281 None 3282 3283 """ 3284 # If running DDP only save with rank 0 3285 if "RANK" in os.environ: 3286 if int(os.environ["RANK"]) != 0: 3287 return 3288 if not self.making_graphs: 3289 return 3290 3291 save_folder = "./" + self.save_name + "/" 3292 3293 plt.ioff() 3294 fig = plt.figure(figsize=(28, 14)) 3295 3296 # Plot with accuracy scores 3297 ax = plt.subplot(221) 3298 self.generate_accuracy_plots(ax, save_folder, extra_string) 3299 3300 # Plot dendrite learning scores 3301 ax = plt.subplot(222) 3302 self.generate_dendrite_learning_plots(ax, save_folder, extra_string) 3303 3304 if GPA.pc.get_drawing_extra_graphs(): 3305 # Plot learning rates for each training epoch 3306 ax = plt.subplot(223) 3307 self.generate_learning_rate_plots(ax, save_folder, extra_string) 3308 3309 # Plot the times for each training epoch 3310 ax = plt.subplot(224) 3311 self.generate_time_plots(ax, save_folder, extra_string) 3312 3313 # Generate extra CSV files 3314 self.generate_extra_csv_files(save_folder, extra_string) 3315 3316 fig.tight_layout() 3317 plt.savefig(save_folder + "/" + self.save_name + extra_string + ".png") 3318 plt.close("all")
Save graphs and csvs for all the values the tracker records
Parameters
- extra_string (str): An extra string to append to the filenames.
Returns
- None
3320 def add_loss(self, loss): 3321 """Add loss to tracking vectors. 3322 3323 Parameters 3324 ---------- 3325 loss : float or int 3326 The loss value to add. 3327 3328 Returns 3329 ------- 3330 None 3331 3332 """ 3333 if not isinstance(loss, (float, int)): 3334 loss = loss.item() 3335 self.member_vars["training_loss"].append(loss)
Add loss to tracking vectors.
Parameters
- loss (float or int): The loss value to add.
Returns
- None
3337 def add_learning_rate(self, learning_rate): 3338 """Add learning rate to tracking vectors. 3339 3340 Parameters 3341 ---------- 3342 learning_rate : float or int 3343 The learning rate value to add. 3344 3345 Returns 3346 ------- 3347 None 3348 3349 """ 3350 if not isinstance(learning_rate, (float, int)): 3351 learning_rate = learning_rate.item() 3352 self.member_vars["training_learning_rates"].append(learning_rate)
Add learning rate to tracking vectors.
Parameters
- learning_rate (float or int): The learning rate value to add.
Returns
- None
3354 def add_extra_score(self, score, extra_score_name): 3355 """Add extra score to tracking vectors. 3356 3357 Parameters 3358 ---------- 3359 score : float or int 3360 The score value to add. 3361 3362 extra_score_name : str 3363 The name of the extra score. 3364 3365 Returns 3366 ------- 3367 None 3368 3369 """ 3370 if not isinstance(score, (float, int)): 3371 try: 3372 score = score.item() 3373 except: 3374 print( 3375 "Scores added for Perforated Backpropagation should be " 3376 "float, int, or tensor, yours is a:" 3377 ) 3378 print(type(score)) 3379 pdb.set_trace() 3380 3381 if GPA.pc.get_verbose(): 3382 print(f"Adding extra score {extra_score_name} of {float(score)}") 3383 3384 if extra_score_name not in self.member_vars["extra_scores"]: 3385 self.member_vars["extra_scores"][extra_score_name] = [] 3386 self.member_vars["extra_scores"][extra_score_name].append(score) 3387 3388 if self.member_vars["mode"] == "n": 3389 if extra_score_name not in self.member_vars["n_extra_scores"]: 3390 self.member_vars["n_extra_scores"][extra_score_name] = [] 3391 self.member_vars["n_extra_scores"][extra_score_name].append(score)
Add extra score to tracking vectors.
Parameters
- score (float or int): The score value to add.
- extra_score_name (str): The name of the extra score.
Returns
- None
3393 def add_extra_score_without_graphing(self, score, extra_score_name): 3394 """Add extra score without graphing to tracking vectors. 3395 3396 Parameters 3397 ---------- 3398 score : float or int 3399 The score value to add. 3400 3401 extra_score_name : str 3402 The name of the extra score. 3403 3404 Returns 3405 ------- 3406 None 3407 3408 """ 3409 if not isinstance(score, (float, int)): 3410 try: 3411 score = score.item() 3412 except: 3413 print( 3414 "Scores added for Perforated Backpropagation should be " 3415 "float, int, or tensor, yours is a:" 3416 ) 3417 print(type(score)) 3418 print("in add_extra_score_without_graphing") 3419 pdb.set_trace() 3420 3421 if GPA.pc.get_verbose(): 3422 print(f"Adding extra score {extra_score_name} of {float(score)}") 3423 3424 if extra_score_name not in self.member_vars["extra_scores_without_graphing"]: 3425 self.member_vars["extra_scores_without_graphing"][extra_score_name] = [] 3426 self.member_vars["extra_scores_without_graphing"][extra_score_name].append( 3427 score 3428 )
Add extra score without graphing to tracking vectors.
Parameters
- score (float or int): The score value to add.
- extra_score_name (str): The name of the extra score.
Returns
- None
3430 def add_test_score(self, score, extra_score_name): 3431 """Add test score to tracking vectors. 3432 3433 Parameters 3434 ---------- 3435 score : float or int 3436 The score value to add. 3437 3438 extra_score_name : str 3439 The name of the extra score. 3440 3441 Returns 3442 ------- 3443 None 3444 3445 Notes 3446 ----- 3447 This function is a wrapper around `add_extra_score` that separates 3448 test score for adding to best_arch_scores.csv. 3449 3450 """ 3451 self.add_extra_score(score, extra_score_name) 3452 3453 if not isinstance(score, (float, int)): 3454 try: 3455 score = score.item() 3456 except: 3457 print( 3458 "Scores added for Perforated Backpropagation should be " 3459 "float, int, or tensor, yours is a:" 3460 ) 3461 print(type(score)) 3462 print("in add_test_score") 3463 pdb.set_trace() 3464 3465 if GPA.pc.get_verbose(): 3466 print(f"Adding test score {extra_score_name} of {float(score)}") 3467 self.member_vars["test_scores"].append(score)
Add test score to tracking vectors.
Parameters
- score (float or int): The score value to add.
- extra_score_name (str): The name of the extra score.
Returns
- None
Notes
This function is a wrapper around add_extra_score that separates
test score for adding to best_arch_scores.csv.
3469 def add_validation_score(self, accuracy, net, force_switch=False): 3470 """Function to add the validation score. 3471 3472 This is complex because it determines neuron and dendrite switching. 3473 3474 Parameters 3475 ---------- 3476 accuracy : float or int 3477 The accuracy or loss value to add. 3478 net : object 3479 The neural network model. 3480 force_switch : bool, optional 3481 Whether to force a switch, by default False. 3482 3483 Returns 3484 ------- 3485 net : object 3486 The potentially modified neural network model. 3487 training_complete : bool 3488 Whether training is complete. 3489 restructured : bool 3490 Whether the model has been restructured. 3491 3492 Notes 3493 ----- 3494 WARNING: Do not call self anywhere in this function. When systems 3495 get loaded the actual tracker you are working with can change. 3496 """ 3497 3498 _pai_log("info", f"Adding validation score {accuracy:.8f}") 3499 3500 update_learning_rate() 3501 update_param_count(net) 3502 3503 accuracy = check_input_problems(net, accuracy) 3504 3505 if len(GPA.pai_tracker.member_vars["switch_epochs"]) == 0: 3506 epochs_since_cycle_switch = GPA.pai_tracker.member_vars["num_epochs_run"] 3507 else: 3508 epochs_since_cycle_switch = ( 3509 GPA.pai_tracker.member_vars["num_epochs_run"] 3510 - GPA.pai_tracker.member_vars["switch_epochs"][-1] 3511 ) 3512 3513 update_running_accuracy(accuracy, epochs_since_cycle_switch) 3514 if GPA.pc.get_perforated_backpropagation(): 3515 TPB.update_pb_scores(self) 3516 3517 # Captured before any switch below flips the mode and reloads scores. 3518 # epoch_phase is sampled here too so the emitted epoch event's phase and 3519 # its pb_scores always describe the same side of a switch boundary. 3520 epoch_phase = GPA.pai_tracker.member_vars["mode"] 3521 epoch_pb_scores = self.get_current_pb_scores() 3522 epoch_pb_scores_current = self.get_current_pb_scores_current() 3523 3524 GPA.pai_tracker.stop_epoch(internal_call=True) 3525 3526 # If it is neuron training mode 3527 if ( 3528 GPA.pai_tracker.member_vars["mode"] == "n" 3529 or GPA.pc.get_learn_dendrites_live() 3530 ): 3531 check_new_best(net, accuracy, epochs_since_cycle_switch) 3532 elif GPA.pc.get_perforated_backpropagation(): 3533 TPB.check_best_pai_score_improvement() 3534 3535 # Save the latest model 3536 if GPA.pc.get_test_saves(): 3537 UPA.save_system(net, GPA.pc.get_save_name(), "latest") 3538 if GPA.pc.get_pai_saves(): 3539 UPA.pai_save_system(net, GPA.pc.get_save_name(), "latest") 3540 3541 restructuring_status_value = NO_MODEL_UPDATE 3542 # If it is time to switch based on scores and counter or a manual switch 3543 if GPA.pai_tracker.switch_time() or force_switch: 3544 # If testing dendrite capacity switch after enough dendrites added 3545 if ( 3546 (GPA.pai_tracker.member_vars["mode"] == "n") 3547 and (GPA.pai_tracker.member_vars["num_dendrites_added"] > 2) 3548 and GPA.pc.get_testing_dendrite_capacity() 3549 ): 3550 GPA.pai_tracker.save_graphs() 3551 _pai_log( 3552 "info", 3553 "Successfully added 3 dendrites with GPA.pc.set_testing_dendrite_capacity(True) (default). " 3554 "You may now set that to False and run a real experiment.", 3555 ) 3556 return net, False, True 3557 3558 # If doing neuron training but this dendrite count didn't improve 3559 if ( 3560 (GPA.pai_tracker.member_vars["mode"] == "n") 3561 or GPA.pc.get_learn_dendrites_live() 3562 ) and (GPA.pai_tracker.member_vars["current_n_set_global_best"] is False): 3563 new_restructuring_status_value, net = process_no_improvement(net) 3564 # if this was the final try return that training is complete 3565 if new_restructuring_status_value == TRAINING_COMPLETE: 3566 if _dashboard_emitter is not None: 3567 _dashboard_emitter.emit_run_end( 3568 GPA.pc, 3569 epoch_last_improved=GPA.pai_tracker.member_vars[ 3570 "epoch_last_improved" 3571 ], 3572 global_best_score=GPA.pai_tracker.member_vars[ 3573 "global_best_validation_score" 3574 ], 3575 ) 3576 return net, True, True 3577 else: 3578 restructuring_status_value = update_restructuring_status( 3579 restructuring_status_value, new_restructuring_status_value 3580 ) 3581 # Else if did improve, do a normal switch process 3582 else: 3583 if GPA.pc.get_verbose(): 3584 print( 3585 f"Calling switch_mode with " 3586 f'{GPA.pai_tracker.member_vars["current_n_set_global_best"]}, ' 3587 f'{GPA.pai_tracker.member_vars["current_n_learning_rate_initial_skip_steps"]}, ' 3588 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_steps"]}, ' 3589 f'{GPA.pai_tracker.member_vars["last_max_learning_rate_value"]},' 3590 f'{GPA.pc.get_max_dendrites()},' 3591 f'{GPA.pai_tracker.member_vars["num_dendrites_added"]},' 3592 f'{GPA.pai_tracker.member_vars["num_dendrite_tries"]},' 3593 ) 3594 import pdb; pdb.set_trace 3595 # If the max number of dendrites has been hit or not doing pai and adding dendtites 3596 # then return rather than adding more 3597 if ( 3598 (GPA.pai_tracker.member_vars["mode"] == "n") 3599 and ( 3600 GPA.pc.get_max_dendrites() 3601 == GPA.pai_tracker.member_vars["num_dendrites_added"] 3602 ) 3603 ) or (GPA.pai_tracker.member_vars["doing_pai"] is False): 3604 if GPA.pc.get_verbose(): 3605 print( 3606 "Max dendrites reached or not doing PAI, finishing training" 3607 ) 3608 net = process_final_network(net) 3609 # Increment integrated if we have dendrites (means they're integrated) 3610 if GPA.pai_tracker.member_vars["num_dendrites_added"] > 0: 3611 GPA.pai_tracker.member_vars["num_dendrites_integrated"] += 1 3612 _pai_log("info", f"Final dendrites successfully integrated! Total integrated: {GPA.pai_tracker.member_vars['num_dendrites_integrated']}") 3613 if _dashboard_emitter is not None: 3614 _dashboard_emitter.emit_dendrite_added( 3615 GPA.pc, 3616 epoch_index=len( 3617 GPA.pai_tracker.member_vars["accuracies"] 3618 ) - 1, 3619 num_dendrites_integrated=GPA.pai_tracker.member_vars[ 3620 "num_dendrites_integrated" 3621 ], 3622 param_count=UPA.count_params(net), 3623 ) 3624 if _dashboard_emitter is not None: 3625 _dashboard_emitter.emit_run_end( 3626 GPA.pc, 3627 epoch_last_improved=GPA.pai_tracker.member_vars[ 3628 "epoch_last_improved" 3629 ], 3630 global_best_score=GPA.pai_tracker.member_vars[ 3631 "global_best_validation_score" 3632 ], 3633 ) 3634 return net, True, True 3635 3636 # Otherwise if its neuron training mode reset the counter of failed dendrites 3637 # Check if we should increment integrated count BEFORE change_learning_modes loads old state 3638 should_increment_integrated = False 3639 if GPA.pai_tracker.member_vars["mode"] == "n": 3640 GPA.pai_tracker.member_vars["num_dendrite_tries"] = 0 3641 if GPA.pc.get_verbose(): 3642 print( 3643 "Adding new dendrites without resetting which means " 3644 "the last ones improved. Resetting num_dendrite_tries" 3645 ) 3646 # Remember to increment after change_learning_modes (which loads old tracker state) 3647 if GPA.pai_tracker.member_vars["num_dendrites_added"] > 0: 3648 should_increment_integrated = True 3649 3650 GPA.pai_tracker.save_graphs( 3651 f'_beforeSwitch_{len(GPA.pai_tracker.member_vars["switch_epochs"])}' 3652 ) 3653 3654 if GPA.pc.get_test_saves(): 3655 UPA.save_system( 3656 net, 3657 GPA.pc.get_save_name(), 3658 f'beforeSwitch_{len(GPA.pai_tracker.member_vars["switch_epochs"])}', 3659 ) 3660 # Copy current best model from this set of dendrites 3661 # If running DDP only copy with rank 0 3662 if "RANK" not in os.environ or int(os.environ["RANK"]) == 0: 3663 shutil.copyfile( 3664 f"{GPA.pc.get_save_name()}/best_model.pt", 3665 f'{GPA.pc.get_save_name()}/best_model_beforeSwitch_{len(GPA.pai_tracker.member_vars["switch_epochs"])}.pt', 3666 ) 3667 3668 if GPA.pc.get_dashboard_debug(): 3669 _pai_log( 3670 "debug", 3671 "[emitter/finding4] before change_learning_modes: " 3672 f'len(accuracies)={len(GPA.pai_tracker.member_vars["accuracies"])} ' 3673 f'num_epochs_run={GPA.pai_tracker.member_vars["num_epochs_run"]}', 3674 ) 3675 net = UPA.change_learning_modes( 3676 net, 3677 GPA.pc.get_save_name(), 3678 "best_model", 3679 GPA.pai_tracker.member_vars["doing_pai"], 3680 ) 3681 restructuring_status_value = NETWORK_RESTRUCTURED 3682 if GPA.pc.get_dashboard_debug(): 3683 _pai_log( 3684 "debug", 3685 "[emitter/finding4] after change_learning_modes: " 3686 f'len(accuracies)={len(GPA.pai_tracker.member_vars["accuracies"])} ' 3687 f'num_epochs_run={GPA.pai_tracker.member_vars["num_epochs_run"]}', 3688 ) 3689 3690 # Now increment after change_learning_modes has loaded the best model 3691 # This ensures the increment persists and doesn't get overwritten 3692 if should_increment_integrated: 3693 GPA.pai_tracker.member_vars["num_dendrites_integrated"] += 1 3694 _pai_log("info", f"Dendrites successfully integrated! Total integrated: {GPA.pai_tracker.member_vars['num_dendrites_integrated']}") 3695 if _dashboard_emitter is not None: 3696 _dashboard_emitter.emit_dendrite_added( 3697 GPA.pc, 3698 epoch_index=len( 3699 GPA.pai_tracker.member_vars["accuracies"] 3700 ) - 1, 3701 num_dendrites_integrated=GPA.pai_tracker.member_vars[ 3702 "num_dendrites_integrated" 3703 ], 3704 param_count=UPA.count_params(net), 3705 ) 3706 3707 # If restructured is true, clear scheduler/optimizer before saving 3708 if restructuring_status_value != NETWORK_RESTRUCTURED: 3709 print( 3710 "Restructured should always be triggered here, let us know if you encounter this situation" 3711 ) 3712 pdb.set_trace() 3713 3714 # Since there is a restructuring optimizer and scheduler must be reinitialized after return 3715 GPA.pai_tracker.clear_optimizer_and_scheduler() 3716 3717 # Save the model from after the switch 3718 UPA.save_system( 3719 net, 3720 GPA.pc.get_save_name(), 3721 f'switch_{len(GPA.pai_tracker.member_vars["switch_epochs"])}', 3722 ) 3723 3724 # If not time to switch and you have a scheduler, perform the update step 3725 elif GPA.pai_tracker.member_vars["scheduler"] is not None: 3726 new_restructuring_status_value, net = process_scheduler_update( 3727 net, accuracy, epochs_since_cycle_switch 3728 ) 3729 restructuring_status_value = update_restructuring_status( 3730 restructuring_status_value, new_restructuring_status_value 3731 ) 3732 3733 GPA.pai_tracker.start_epoch(internal_call=True) 3734 if _dashboard_emitter is not None: 3735 _mv = GPA.pai_tracker.member_vars 3736 _lr = _mv["training_learning_rates"][-1] if _mv["training_learning_rates"] else None 3737 # epoch_index is the committed coordinate: len(accuracies) - 1, 3738 # captured after update_running_accuracy appends and after any reload 3739 # truncation in switch handling. Independent of num_epochs_run. 3740 _epoch_index = len(_mv["accuracies"]) - 1 3741 if GPA.pc.get_dashboard_debug(): 3742 _pai_log( 3743 "debug", 3744 "[emitter/finding4] emit_epoch: " 3745 f"epoch_index={_epoch_index} " 3746 f'true_epoch={_mv["num_epochs_run"]} ' 3747 f'restructured={restructuring_status_value == NETWORK_RESTRUCTURED} ' 3748 f'tail_accuracies={_mv["accuracies"][-4:]}', 3749 ) 3750 # Phase captured at the top of the method (pre-switch), matching the 3751 # point pb_scores were sampled - see Finding 2, ticket 06. 3752 _phase = epoch_phase 3753 # scores: validation (reported), running (checkpointed member var), 3754 # then every add_extra_score name verbatim. 3755 _scores = { 3756 "validation": accuracy, 3757 "running": _mv["running_accuracies"][-1] if _mv["running_accuracies"] else None, 3758 } 3759 for _name, _vals in _mv["extra_scores"].items(): 3760 if _vals: 3761 _scores[_name] = _vals[-1] 3762 _n_times = _mv["n_epoch_times"] or [(_mv["n_train_times"][-1] + _mv["n_val_times"][-1]) if (_mv["n_train_times"] and _mv["n_val_times"]) else None] 3763 _p_times = _mv["p_epoch_times"] or [(_mv["p_train_times"][-1] + _mv["p_val_times"][-1]) if (_mv["p_train_times"] and _mv["p_val_times"]) else None] 3764 _dashboard_emitter.emit_epoch( 3765 GPA.pc, 3766 epoch_index=_epoch_index, 3767 phase=_phase, 3768 scores=_scores, 3769 learning_rate=_lr, 3770 normal_time=_n_times[-1] if _phase == "n" else None, 3771 pai_time=_p_times[-1] if _phase == "p" else None, 3772 pb_scores=epoch_pb_scores, 3773 pb_scores_current=epoch_pb_scores_current, 3774 ) 3775 GPA.pai_tracker.save_graphs() 3776 3777 if restructuring_status_value == NETWORK_RESTRUCTURED: 3778 GPA.pai_tracker.member_vars["epoch_last_improved"] = ( 3779 GPA.pai_tracker.member_vars["num_epochs_run"] 3780 ) 3781 if GPA.pc.get_verbose(): 3782 print( 3783 f"Setting epoch last improved to " 3784 f'{GPA.pai_tracker.member_vars["epoch_last_improved"]}' 3785 ) 3786 3787 now = datetime.now() 3788 dt_string = now.strftime("_%d.%m.%Y.%H.%M.%S") 3789 3790 if GPA.pc.get_verbose(): 3791 print("Not saving restructure right now") 3792 3793 """ 3794 This block of code helped with a save issue with safetensors and huggingface, but it breaks DDP. 3795 Temporarily removing it to avoid DDP issues, but if you encounter save issues try adding it back in. 3796 for param in net.parameters(): 3797 param.data = param.data.contiguous() 3798 """ 3799 if GPA.pc.get_verbose(): 3800 print( 3801 f"Completed adding score. Restructured is {restructuring_status_value}, " 3802 f"\ncurrent switch list is:" 3803 ) 3804 print(GPA.pai_tracker.member_vars["switch_epochs"]) 3805 3806 if _dashboard_emitter is not None and restructuring_status_value == NETWORK_RESTRUCTURED: 3807 _param_count = UPA.count_params(net) 3808 _dashboard_emitter.emit_switch( 3809 GPA.pc, 3810 # switch_epochs was already appended in change_learning_modes, 3811 # so subtract 1 for a 0-based ordinal (matches matplotlib's 3812 # np.arange(len(switch_epochs)) labelling) - Finding 1, ticket 06. 3813 switch_ordinal=len(GPA.pai_tracker.member_vars["switch_epochs"]) - 1, 3814 epoch_index=len(GPA.pai_tracker.member_vars["accuracies"]) - 1, 3815 param_count=_param_count, 3816 switch_type=GPA.pai_tracker.member_vars["mode"], 3817 ) 3818 3819 # Always False for training complete if nothing triggered that training is over 3820 return net, restructuring_status_value, False
Function to add the validation score.
This is complex because it determines neuron and dendrite switching.
Parameters
- accuracy (float or int): The accuracy or loss value to add.
- net (object): The neural network model.
- force_switch (bool, optional): Whether to force a switch, by default False.
Returns
- net (object): The potentially modified neural network model.
- training_complete (bool): Whether training is complete.
- restructured (bool): Whether the model has been restructured.
Notes
WARNING: Do not call self anywhere in this function. When systems get loaded the actual tracker you are working with can change.
3822 def clear_all_processors(self): 3823 """Clear all processors from modules. 3824 3825 Parameters 3826 ---------- 3827 None 3828 3829 Returns 3830 ------- 3831 None 3832 This function does not return a value. 3833 """ 3834 for module in self.neuron_module_vector: 3835 module.clear_processors()
Clear all processors from modules.
Parameters
- None
Returns
- None: This function does not return a value.
3837 def create_new_dendrite_module(self): 3838 """Add dendrite module to all neuron modules. 3839 3840 Parameters 3841 ---------- 3842 None 3843 3844 Returns 3845 ------- 3846 None 3847 This function does not return a value. 3848 """ 3849 for module in self.neuron_module_vector: 3850 module.create_new_dendrite_module()
Add dendrite module to all neuron modules.
Parameters
- None
Returns
- None: This function does not return a value.
3852 def set_create_dendrite_global(self, fn): 3853 """Call set_create_dendrite(fn) on every tracked PAINeuronModule.""" 3854 for module in self.neuron_module_vector: 3855 module.set_create_dendrite(fn)
Call set_create_dendrite(fn) on every tracked PAINeuronModule.
3857 def set_dendrite_loss_fn_global(self, fn): 3858 """Set the global dendrite loss function used by all dendrite modules.""" 3859 from perforatedbp import modules_pbp as MPB 3860 MPB.dendrite_loss_fn = fn
Set the global dendrite loss function used by all dendrite modules.
3862 def apply_pb_grads(self): 3863 """Apply perforated backpropagation gradients to all modules. 3864 3865 Parameters 3866 ---------- 3867 None 3868 3869 Returns 3870 ------- 3871 None 3872 This function does not return a value. 3873 """ 3874 if self.member_vars["mode"] == "p": 3875 for module in self.neuron_module_vector: 3876 module.apply_pb_grads()
Apply perforated backpropagation gradients to all modules.
Parameters
- None
Returns
- None: This function does not return a value.
3878 def apply_pb_zero(self): 3879 """Apply perforated backpropagation zero gradients to all modules. 3880 3881 Parameters 3882 ---------- 3883 None 3884 3885 Returns 3886 ------- 3887 None 3888 This function does not return a value. 3889 """ 3890 if self.member_vars["mode"] == "p": 3891 for module in self.neuron_module_vector: 3892 module.apply_pb_zero()
Apply perforated backpropagation zero gradients to all modules.
Parameters
- None
Returns
- None: This function does not return a value.