Вспомогательные функции

Вспомогательные функции

Категория статьи: Нейронные сети

Проект: Shazam своими руками

Некоторые часть кода выполняются многократно, я выделил их в отдельные функции

Всем привет!

В этой статье речь пойдёт о вспомогательных функциях.

Функции для работы с каталогами

В одной из статей я уже упоминал функции для работы с каталогами, добавил еще парочку.

import os
import shutil

# Глобальные переменные
# Максимальная длина сгенерированного имени директории
max_len_dirname = 6
# Максимальная длина сгенерированного имени файла
max_len_filename = 6

def del_create_dir(src_dir):
  """Удаление директории с вложенными файлами и папками
      и создание её снова

  Parameters
  ----------
    src_dir: str
      Директория для удаления и создания
  """
  # Проверяем существует ли директория src_dir
  if os.path.exists(src_dir):
    # Удаляем её и все вложенные файлы и папки
    shutil.rmtree(src_dir)
  # Создаём директорию src_dir
  os.makedirs(src_dir)

def create_dir_if_not_exists(src_dir, echo=False):
  """Создание директории если она не существует

  Parameters
  ----------
    src_dir: str
      Директория для создания

    echo: bool (default=False)
      Флаг вывода сообщения
  """
  # проверяем отсутствует ли директория src_dir
  if not os.path.exists(src_dir):
    # Создаём директорию src_dir
    os.makedirs(src_dir)
    # Если флаг вывода сообщения установлен
    if echo:
      # Выводим сообщение 
      print(f"Создана директория '{src_dir}'")
  # Если директория src_dir уже существует
  else:
    # Проверим флаг вывода сообщения, если он True
    if echo:
      # Выводим сообщение о том, что директория уже существует
      print(f"Директория '{src_dir}' уже существует")

def create_labeled_dir(parent_dir, label):
  """Создание директории с именем соответствующим метке label
      с дополнением нулями спереди
  
  Parameters
  ----------
    parent_dir: str
      Родительская директория

    label: int
      Метка класса на основе которого будет создана директория

  Returns
  -------
    full_dirname: str
      Полный пить к созданной директории
  """
  # Формируем имя директории
  dirname = str(label).zfill(max_len_dirname)
  # Формируем полный пить к директории
  full_dirname = os.path.join(parent_dir, dirname)
  # Создаем директорию если она не создана
  create_dir_if_not_exists(full_dirname)
  # Возвращаем полный пить к созданной директории
  return full_dirname

Pickle

Нарезанные 2х секундные кусочки файлов mp3, особенно когда их становится много, не реально все поместить в оперативную память, для этого нам нужно их сохранить на диске. А загружать будем перед подачей данных для обучения модели. Для сохранения/загрузки объектов в файл воспользуемся библиотекой pickle.

import pickle

def unpickle(filename):
  """Распаковать объект из файла
  
  Parameters
  ----------
    filename: str
      Имя файла

  Returns
  -------
    obj: Объект
      Любой объект
  """
  # Открываем файл для чтения в бинарном режиме
  with open(filename, 'rb') as f:
    # Загружаем данные из файла в объект
    obj = pickle.load(f, encoding='bytes')
    # Закрываем файл
    f.close()
  # Возвращаем объект
  return obj

def dumppickle(filename, obj):
  """Запаковать объект в файл

  Parameters
  ----------
    filename: str
      Имя файла

    obj: Объект
      Любой объект
  """
  # Открываем файл для записи в бинарном режиме
  with open(filename, 'wb') as f:
    # Сохраняем объект в файл
    pickle.dump(obj, f)
    # Закрываем файл
    f.close()

Измерение времени выполнения функции или блока кода

Иногда необходимо оценить время выполнения той или иной операции (итерации), например, для того чтобы примерно понимать общее время выполнение определенного количества итераций. Для этого я подготовил класс с примерами его использования.

from timeit import default_timer

class MeasuringRunTime():
  """Измерение времени выполнения функции или блока кода.

  Example
  -------
    t1 = MeasuringRunTime.Timer()
    t1.Start()
    for i in range(100000000):
      pass

    # Останавливаем таймер, можем посмотреть сколько прошло секунд
    print(t1.Stop())
    # Просматриваем истекшее время
    print(t1.Elapsed())

    def test_fn(a, b):
      for i in range(100000000):
        a += b
      return a

    mrt = MeasuringRunTime()
    print(mrt.Measuring(test_fn, 0, 1))

    # Обвертка прокидывает результат функции и можно дальше работать
    x = mrt.Measuring(test_fn, 1, 1)
    y = mrt.Measuring(test_fn, 2, 1)
    print(x + y)

    mrt.Info()
  """

  class Timer():
    """Класс таймер
    """
    def __init__(self, start=True):
      self.__timer_start = None
      if start:
        self.Start()
      self.__elapsed  = 0
    def Start(self):
      self.__timer_start = default_timer()
    def Elapsed(self):
      if self.__timer_start is not None:
        self.__elapsed = default_timer() - self.__timer_start
      return self.__elapsed
    def Stop(self):
      self.Elapsed()
      self.__timer_start = None
      return self.__elapsed

  def __init__(self):
    self.timers = {}

  def ClearTimer():
    self.timers = {}

  def Info(self):
    for key, val in self.timers.items():
      i = 0
      for item in val:
        print("{:>20} : {}  -  {:.10f} секунд".format(key, i, item.Elapsed()))
        i += 1

  def Measuring(self, fn, *args, **kwargs):
    t = MeasuringRunTime.Timer()
    if fn.__name__ not in self.timers:
      self.timers[fn.__name__] = [t]
    else:
      self.timers[fn.__name__].append(t)
    t.Start()
    ret = fn(*args, **kwargs)
    t.Stop()
    return ret

Функция отображения истории обучения модели

После обучения модели несколько эпох иногда бывает полезно взглянуть на историю обучения модели в графическом виде. По графику видно не возникло ли переобучение модели? Способна ли модель охватить все данные и обучиться? Качество обучаемой и проверочной выборок.

import matplotlib.pyplot as plt

def ShowHistory(h, metrics=["loss"]):
  """Отображение графиков обучения модели.

  Parameters
  ----------
    h: Объект History
      History.history содержит информацию об ошибках и метриках
      на этапах обучения модели.

    metrics: list (default=["loss"])
      Список ошибок и метрик.
  """
  # Для каждой метрики из списка
  for m in metrics:
    # Проверяем есть ли история
    if m in h.history:
      # Рисуем метрику на графике
      plt.plot(h.history[m], label='train')
      # Есть ли история для метрики на проверочном наборе
      if 'val_' + m in h.history:
        # Рисуем метрику на графике
        plt.plot(h.history['val_' + m], label='val')
      # Подписываем ось x
      plt.xlabel('Эпоха обучения')
      # Подписываем ось y
      plt.ylabel(m)
      # добавляем легенду
      plt.legend()
      # Отображаем график
      plt.show()