ecker commited on
Commit
bc0d9ab
·
1 Parent(s): 6925ec7

added graph to chart loss_gpt_total rate, added option to prune X number of previous models/states, something else

Browse files
Files changed (2) hide show
  1. src/utils.py +85 -23
  2. src/webui.py +21 -1
src/utils.py CHANGED
@@ -25,6 +25,7 @@ import torchaudio
25
  import music_tag
26
  import gradio as gr
27
  import gradio.utils
 
28
 
29
  from datetime import datetime
30
  from datetime import timedelta
@@ -435,13 +436,14 @@ def compute_latents(voice, voice_latents_chunks, progress=gr.Progress(track_tqdm
435
 
436
  # superfluous, but it cleans up some things
437
  class TrainingState():
438
- def __init__(self, config_path):
439
  self.cmd = ['train.bat', config_path] if os.name == "nt" else ['bash', './train.sh', config_path]
440
 
441
  # parse config to get its iteration
442
  with open(config_path, 'r') as file:
443
  self.config = yaml.safe_load(file)
444
 
 
445
  self.batch_size = self.config['datasets']['train']['batch_size']
446
  self.dataset_path = self.config['datasets']['train']['path']
447
  with open(self.dataset_path, 'r', encoding="utf-8") as f:
@@ -480,9 +482,67 @@ class TrainingState():
480
  self.eta = "?"
481
  self.eta_hhmmss = "?"
482
 
 
 
 
 
 
 
 
 
 
 
 
483
  print("Spawning process: ", " ".join(self.cmd))
484
  self.process = subprocess.Popen(self.cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
485
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
486
  def parse(self, line, verbose=False, buffer_size=8, progress=None ):
487
  self.buffer.append(f'{line}')
488
 
@@ -533,22 +593,7 @@ class TrainingState():
533
  except Exception as e:
534
  pass
535
 
536
- message = f'[{self.epoch}/{self.epochs}, {self.it}/{self.its}, {step}/{steps}] [ETA: {self.eta_hhmmss}] [{self.epoch_rate}, {self.it_rate}] {self.status}'
537
-
538
- """
539
- # I wanted frequently updated ETA, but I can't wrap my noggin around getting it to work on an empty belly
540
- # will fix later
541
-
542
- #self.eta = (self.its - self.it) * self.it_time_delta
543
- self.it_time_deltas = self.it_time_deltas + self.it_time_delta
544
- self.it_taken = self.it_taken + 1
545
- self.eta = (self.its - self.it) * (self.it_time_deltas / self.it_taken)
546
- try:
547
- eta = str(timedelta(seconds=int(self.eta)))
548
- self.eta_hhmmss = eta
549
- except Exception as e:
550
- pass
551
- """
552
 
553
  if lapsed:
554
  self.epoch = self.epoch + 1
@@ -578,15 +623,18 @@ class TrainingState():
578
 
579
  if line.find('INFO: [epoch:') >= 0:
580
  # easily rip out our stats...
581
- match = re.findall(r'\b([a-z_0-9]+?)\b: ([0-9]\.[0-9]+?e[+-]\d+)\b', line)
582
  if match and len(match) > 0:
583
  for k, v in match:
584
- self.info[k] = float(v)
585
 
586
  if 'loss_gpt_total' in self.info:
587
  self.status = f"Total loss at epoch {self.epoch}: {self.info['loss_gpt_total']}"
588
- print(self.status)
589
- self.buffer.append(self.status)
 
 
 
590
  elif line.find('Saving models and training states') >= 0:
591
  self.checkpoint = self.checkpoint + 1
592
 
@@ -598,11 +646,13 @@ class TrainingState():
598
  print(f'{"{:.3f}".format(percent*100)}% {message}')
599
  self.buffer.append(f'{"{:.3f}".format(percent*100)}% {message}')
600
 
 
 
601
  self.buffer = self.buffer[-buffer_size:]
602
  if verbose or not self.training_started:
603
  return "".join(self.buffer)
604
 
605
- def run_training(config_path, verbose=False, buffer_size=8, progress=gr.Progress(track_tqdm=True)):
606
  global training_state
607
  if training_state and training_state.process:
608
  return "Training already in progress"
@@ -614,7 +664,7 @@ def run_training(config_path, verbose=False, buffer_size=8, progress=gr.Progress
614
  unload_whisper()
615
  unload_voicefixer()
616
 
617
- training_state = TrainingState(config_path=config_path)
618
 
619
  for line in iter(training_state.process.stdout.readline, ""):
620
 
@@ -631,6 +681,18 @@ def run_training(config_path, verbose=False, buffer_size=8, progress=gr.Progress
631
  #if return_code:
632
  # raise subprocess.CalledProcessError(return_code, cmd)
633
 
 
 
 
 
 
 
 
 
 
 
 
 
634
  def reconnect_training(verbose=False, buffer_size=8, progress=gr.Progress(track_tqdm=True)):
635
  global training_state
636
  if not training_state or not training_state.process:
 
25
  import music_tag
26
  import gradio as gr
27
  import gradio.utils
28
+ import pandas as pd
29
 
30
  from datetime import datetime
31
  from datetime import timedelta
 
436
 
437
  # superfluous, but it cleans up some things
438
  class TrainingState():
439
+ def __init__(self, config_path, keep_x_past_datasets=0):
440
  self.cmd = ['train.bat', config_path] if os.name == "nt" else ['bash', './train.sh', config_path]
441
 
442
  # parse config to get its iteration
443
  with open(config_path, 'r') as file:
444
  self.config = yaml.safe_load(file)
445
 
446
+ self.dataset_dir = f"./training/{self.config['name']}/"
447
  self.batch_size = self.config['datasets']['train']['batch_size']
448
  self.dataset_path = self.config['datasets']['train']['path']
449
  with open(self.dataset_path, 'r', encoding="utf-8") as f:
 
482
  self.eta = "?"
483
  self.eta_hhmmss = "?"
484
 
485
+ self.losses = {
486
+ 'iteration': [],
487
+ 'loss_gpt_total': []
488
+ }
489
+
490
+
491
+ self.load_losses()
492
+ self.cleanup_old(keep=keep_x_past_datasets)
493
+ self.spawn_process()
494
+
495
+ def spawn_process(self):
496
  print("Spawning process: ", " ".join(self.cmd))
497
  self.process = subprocess.Popen(self.cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
498
 
499
+ def load_losses(self):
500
+ if not os.path.isdir(self.dataset_dir):
501
+ return
502
+
503
+ logs = sorted([f'{self.dataset_dir}/{d}' for d in os.listdir(self.dataset_dir) if d[-4:] == ".log" ])
504
+ infos = {}
505
+ for log in logs:
506
+ with open(log, 'r', encoding="utf-8") as f:
507
+ lines = f.readlines()
508
+ for line in lines:
509
+ if line.find('INFO: [epoch:') >= 0:
510
+ # easily rip out our stats...
511
+ match = re.findall(r'\b([a-z_0-9]+?)\b: +?([0-9]\.[0-9]+?e[+-]\d+|[\d,]+)\b', line)
512
+ if not match or len(match) == 0:
513
+ continue
514
+
515
+ info = {}
516
+ for k, v in match:
517
+ info[k] = float(v.replace(",", ""))
518
+
519
+ if 'iter' in info:
520
+ it = info['iter']
521
+ infos[it] = info
522
+
523
+ for k in infos:
524
+ if 'loss_gpt_total' in infos[k]:
525
+ self.losses['iteration'].append(int(k))
526
+ self.losses['loss_gpt_total'].append(infos[k]['loss_gpt_total'])
527
+
528
+ def cleanup_old(self, keep=2):
529
+ if keep <= 0:
530
+ return
531
+
532
+ models = sorted([ int(d[:-8]) for d in os.listdir(f'{self.dataset_dir}/models/') if d[-8:] == "_gpt.pth" ])
533
+ states = sorted([ int(d[:-6]) for d in os.listdir(f'{self.dataset_dir}/training_state/') if d[-6:] == ".state" ])
534
+ remove_models = models[:-2]
535
+ remove_states = states[:-2]
536
+
537
+ for d in remove_models:
538
+ path = f'{self.dataset_dir}/models/{d}_gpt.pth'
539
+ print("Removing", path)
540
+ os.remove(path)
541
+ for d in remove_states:
542
+ path = f'{self.dataset_dir}/training_state/{d}.state'
543
+ print("Removing", path)
544
+ os.remove(path)
545
+
546
  def parse(self, line, verbose=False, buffer_size=8, progress=None ):
547
  self.buffer.append(f'{line}')
548
 
 
593
  except Exception as e:
594
  pass
595
 
596
+ message = f'[{self.epoch}/{self.epochs}, {self.it}/{self.its}, {step}/{steps}] [{self.epoch_rate}, {self.it_rate}] [Loss at it {self.losses["iteration"][-1]}: {self.losses["loss_gpt_total"][-1]}] [ETA: {self.eta_hhmmss}]'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
597
 
598
  if lapsed:
599
  self.epoch = self.epoch + 1
 
623
 
624
  if line.find('INFO: [epoch:') >= 0:
625
  # easily rip out our stats...
626
+ match = re.findall(r'\b([a-z_0-9]+?)\b: +?([0-9]\.[0-9]+?e[+-]\d+|[\d,]+)\b', line)
627
  if match and len(match) > 0:
628
  for k, v in match:
629
+ self.info[k] = float(v.replace(",", ""))
630
 
631
  if 'loss_gpt_total' in self.info:
632
  self.status = f"Total loss at epoch {self.epoch}: {self.info['loss_gpt_total']}"
633
+
634
+ self.losses['iteration'].append(self.it)
635
+ self.losses['loss_gpt_total'].append(self.info['loss_gpt_total'])
636
+
637
+ verbose = True
638
  elif line.find('Saving models and training states') >= 0:
639
  self.checkpoint = self.checkpoint + 1
640
 
 
646
  print(f'{"{:.3f}".format(percent*100)}% {message}')
647
  self.buffer.append(f'{"{:.3f}".format(percent*100)}% {message}')
648
 
649
+ self.cleanup_old()
650
+
651
  self.buffer = self.buffer[-buffer_size:]
652
  if verbose or not self.training_started:
653
  return "".join(self.buffer)
654
 
655
+ def run_training(config_path, verbose=False, buffer_size=8, keep_x_past_datasets=0, progress=gr.Progress(track_tqdm=True)):
656
  global training_state
657
  if training_state and training_state.process:
658
  return "Training already in progress"
 
664
  unload_whisper()
665
  unload_voicefixer()
666
 
667
+ training_state = TrainingState(config_path=config_path, keep_x_past_datasets=keep_x_past_datasets)
668
 
669
  for line in iter(training_state.process.stdout.readline, ""):
670
 
 
681
  #if return_code:
682
  # raise subprocess.CalledProcessError(return_code, cmd)
683
 
684
+ def get_training_losses():
685
+ global training_state
686
+ if not training_state or not training_state.losses:
687
+ return
688
+ return pd.DataFrame(training_state.losses)
689
+
690
+ def update_training_dataplot():
691
+ global training_state
692
+ if not training_state or not training_state.losses:
693
+ return
694
+ return gr.LinePlot.update(value=pd.DataFrame(training_state.losses))
695
+
696
  def reconnect_training(verbose=False, buffer_size=8, progress=gr.Progress(track_tqdm=True)):
697
  global training_state
698
  if not training_state or not training_state.process:
src/webui.py CHANGED
@@ -508,6 +508,15 @@ def setup_gradio():
508
  training_output = gr.TextArea(label="Console Output", interactive=False, max_lines=8)
509
  verbose_training = gr.Checkbox(label="Verbose Console Output")
510
  training_buffer_size = gr.Slider(label="Console Buffer Size", minimum=4, maximum=32, value=8)
 
 
 
 
 
 
 
 
 
511
  with gr.Tab("Settings"):
512
  with gr.Row():
513
  exec_inputs = []
@@ -720,8 +729,19 @@ def setup_gradio():
720
  training_configs,
721
  verbose_training,
722
  training_buffer_size,
 
723
  ],
724
- outputs=training_output #console_output
 
 
 
 
 
 
 
 
 
 
725
  )
726
  stop_training_button.click(stop_training,
727
  inputs=None,
 
508
  training_output = gr.TextArea(label="Console Output", interactive=False, max_lines=8)
509
  verbose_training = gr.Checkbox(label="Verbose Console Output")
510
  training_buffer_size = gr.Slider(label="Console Buffer Size", minimum=4, maximum=32, value=8)
511
+ training_keep_x_past_datasets = gr.Slider(label="Keep X Previous Datasets", minimum=0, maximum=8, value=0)
512
+
513
+ training_loss_graph = gr.LinePlot(label="Loss Rates",
514
+ x="iteration",
515
+ y="loss_gpt_total",
516
+ title="Loss Rates",
517
+ width=600,
518
+ height=350
519
+ )
520
  with gr.Tab("Settings"):
521
  with gr.Row():
522
  exec_inputs = []
 
729
  training_configs,
730
  verbose_training,
731
  training_buffer_size,
732
+ training_keep_x_past_datasets,
733
  ],
734
+ outputs=[
735
+ training_output,
736
+ ],
737
+ )
738
+ training_output.change(
739
+ fn=update_training_dataplot,
740
+ inputs=None,
741
+ outputs=[
742
+ training_loss_graph,
743
+ ],
744
+ show_progress=False,
745
  )
746
  stop_training_button.click(stop_training,
747
  inputs=None,