Spaces:
Build error
Build error
added graph to chart loss_gpt_total rate, added option to prune X number of previous models/states, something else
Browse files- src/utils.py +85 -23
- 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}] [
|
| 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 |
-
|
| 589 |
-
self.
|
|
|
|
|
|
|
|
|
|
| 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=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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,
|