training_callbacks.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import logging
  2. from dash.dependencies import Input as DashInput, Output as DashOutput, State as DashState
  3. from dash import callback_context, dcc
  4. from utils.file_utils import ensure_directory_exists, list_files_in_directory, update_training_time
  5. from threading import Thread
  6. import time
  7. import os
  8. import importlib.util
  9. import json
  10. from utils.training_interface import TrainerInterface
  11. # Configure logging to file
  12. logger = logging.getLogger(__name__)
  13. logging.basicConfig(filename='trainer.log', encoding='utf-8', level=logging.DEBUG)
  14. TRAINING_HOTWORD_DIR = './training/hotword/'
  15. MODEL_DIR = './model/hotword/'
  16. BACKGROUND_DIR = './training/background/'
  17. SAMPLER_DIR = './training/sampler/'
  18. training_status = "Not started"
  19. training_start_time = None
  20. training_thread = None
  21. training_info = {}
  22. # Global variables for progress tracking
  23. current_progress = 0
  24. current_loss = 0.0
  25. current_accuracy = 0.0
  26. total_epochs = 0
  27. total_batches = 0
  28. def load_trainer_modules():
  29. trainer_modules = {}
  30. trainer_dir = './trainer_scripts/'
  31. for file_name in os.listdir(trainer_dir):
  32. if file_name.endswith('.py'):
  33. module_name = file_name[:-3]
  34. file_path = os.path.join(trainer_dir, file_name)
  35. spec = importlib.util.spec_from_file_location(module_name, file_path)
  36. module = importlib.util.module_from_spec(spec)
  37. spec.loader.exec_module(module)
  38. for attr_name in dir(module):
  39. attr = getattr(module, attr_name)
  40. if isinstance(attr, type) and issubclass(attr, TrainerInterface) and attr != TrainerInterface:
  41. trainer_modules[module_name] = attr
  42. return trainer_modules
  43. trainer_modules = load_trainer_modules()
  44. def register_training_callbacks(app):
  45. @app.callback(
  46. [DashOutput("start-training-hotword", "disabled"),
  47. DashOutput("optimize-training-hotword", "disabled"),
  48. DashOutput("training-status", "children"),
  49. DashOutput("training-timer", "children"),
  50. DashOutput("training-chart", "figure"),
  51. DashOutput("training-progress", "value")],
  52. [DashInput("start-training-hotword", "n_clicks"),
  53. DashInput("optimize-training-hotword", "n_clicks"),
  54. DashInput("stop-training-hotword", "n_clicks"),
  55. DashInput('interval-component', 'n_intervals')],
  56. [DashState('url', 'pathname'),
  57. DashState("training-method-dropdown", "value"),
  58. DashState("dropout-rate", "value"),
  59. DashState("num-epochs", "value"),
  60. DashState("batch-size", "value")]
  61. )
  62. def manage_training(n_clicks_start, n_clicks_optimize, n_clicks_stop, n_intervals, pathname, training_method, dropout_rate, num_epochs, batch_size):
  63. global training_status, training_start_time, training_thread, training_info, logger
  64. global current_progress, current_loss, current_accuracy, total_epochs, total_batches
  65. model_name = pathname.split('/')[-1]
  66. ctx = callback_context
  67. if not os.path.exists(MODEL_DIR + model_name):
  68. os.mkdir(MODEL_DIR + model_name)
  69. if not ctx.triggered:
  70. logger.error("Callback context triggered is empty.")
  71. return False, False, f"Status: {training_status}", "", {}, 0
  72. triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]
  73. if triggered_id == "start-training-hotword" and n_clicks_start:
  74. training_status = "Preprocessing"
  75. training_start_time = time.time()
  76. training_info[model_name] = {"accuracy": [], "loss": [], "epochs": 0}
  77. if training_method in trainer_modules:
  78. trainer_class = trainer_modules[training_method]
  79. trainer = trainer_class()
  80. else:
  81. logger.error(f"Unsupported training method: {training_method}")
  82. return False, False, f"Status: {training_status}", "", {}, 0
  83. trainer.set_model_path(MODEL_DIR + model_name + "/")
  84. trainer.set_training_directories([TRAINING_HOTWORD_DIR + model_name + "/", TRAINING_HOTWORD_DIR + "systemcommand/"])
  85. trainer.set_sample_directory(SAMPLER_DIR)
  86. trainer.set_background_directory(BACKGROUND_DIR)
  87. trainer.set_dropout_rate(dropout_rate)
  88. trainer.set_epochs(num_epochs)
  89. trainer.set_batch_size(batch_size)
  90. training_thread = Thread(target=trainer.start_new)
  91. training_thread.start()
  92. logger.warning(f"Started training thread for model: {model_name}")
  93. return True, True, f"Status: {training_status}", f"Started at: {time.ctime(training_start_time)}", {}, 0
  94. elif triggered_id == "optimize-training-hotword" and n_clicks_optimize:
  95. training_status = "Optimizing"
  96. training_start_time = time.time()
  97. training_info[model_name] = {"accuracy": [], "loss": [], "epochs": 0}
  98. if training_method in trainer_modules:
  99. trainer_class = trainer_modules[training_method]
  100. trainer = trainer_class()
  101. else:
  102. logger.error(f"Unsupported training method: {training_method}")
  103. return False, False, f"Status: {training_status}", "", {}, 0
  104. trainer.set_model_path(MODEL_DIR + model_name + "/")
  105. trainer.set_training_directories([TRAINING_HOTWORD_DIR + model_name + "/", TRAINING_HOTWORD_DIR + "systemcommand/"])
  106. trainer.set_sample_directory(SAMPLER_DIR)
  107. trainer.set_background_directory(BACKGROUND_DIR)
  108. trainer.set_dropout_rate(dropout_rate)
  109. trainer.set_epochs(num_epochs)
  110. trainer.set_batch_size(batch_size)
  111. training_thread = Thread(target=trainer.start)
  112. training_thread.start()
  113. logger.warning(f"Started optimization thread for model: {model_name}")
  114. return True, True, f"Status: {training_status}", f"Started at: {time.ctime(training_start_time)}", {}, 0
  115. elif triggered_id == "stop-training-hotword" and n_clicks_stop:
  116. training_status = "Stopped"
  117. logger.debug(f"Stopped training for model: {model_name}")
  118. return False, False, f"Status: {training_status}", "", {}, 0
  119. elif triggered_id == "interval-component" and training_start_time and training_status in ["Training", "Optimizing"]:
  120. elapsed_time = time.time() - training_start_time
  121. figure = {
  122. "data": [
  123. {"x": list(range(len(training_info[model_name]["accuracy"]))), "y": training_info[model_name]["accuracy"], "type": "line", "name": "Accuracy", "line": {"color": "blue"}},
  124. {"x": list(range(len(training_info[model_name]["loss"]))), "y": training_info[model_name]["loss"], "type": "line", "name": "Loss", "line": {"color": "green"}},
  125. ],
  126. "layout": {
  127. "title": "Training Progress",
  128. "xaxis": {"title": "Epoch"},
  129. "yaxis": {"title": "Value", "range": [0, 2]},
  130. "height": 200,
  131. "width": 500,
  132. "plot_bgcolor": "darkgray",
  133. "gridcolor": "lightgray"
  134. }
  135. }
  136. progress = current_progress
  137. training_info[model_name]["accuracy"].append(current_accuracy)
  138. training_info[model_name]["loss"].append(current_loss)
  139. training_info[model_name]["epochs"] = total_epochs
  140. return True, True, f"Status: {training_status}", f"Training time: {int(elapsed_time // 60)} minutes {int(elapsed_time % 60)} seconds", figure, progress
  141. training_data = list_files_in_directory(f"{TRAINING_HOTWORD_DIR}/{model_name}", extension='.wav')
  142. if training_status in ["Training", "Optimizing"] or not training_data:
  143. return True, True, f"Status: {training_status}", "", {}, 0
  144. return False, False, f"Status: {training_status}", "", {}, 0
  145. app.layout.children.append(dcc.Interval(
  146. id='interval-component',
  147. interval=1*1000, # in milliseconds
  148. n_intervals=0
  149. ))