Experimental setup, feature extraction and training

This notebook sets up the experiments in the 10-fold cross validation scheme for all parameters to be tested, extracts the features (either pitch distribution or pitch-class distribution) and computes the training models


In [1]:
import os
import itertools
import json
import numpy as np
from morty.extras.foldgenerator import FoldGenerator

Setup the cluster

Important: ipyparallel executes the code in where the engines have started. So you should start the cluster in the folder of this notebook to avoid errativ behaviour


In [2]:
# ipyparallel
import ipyparallel

# get the clients
clients = ipyparallel.Client()
print(clients.ids)

# create a direct view into the engines
dview = clients.direct_view()

with dview.sync_imports():
    from dlfm_code.trainer import compute_recording_distributions
    from dlfm_code.trainer import train_single
    from dlfm_code.trainer import train_multi


[0, 1, 2, 3, 4, 5, 6, 7]
importing compute_recording_distributions from dlfm_code.trainer on engine(s)
importing train_single from dlfm_code.trainer on engine(s)
importing train_multi from dlfm_code.trainer on engine(s)

Set the dataset paths and define the parameters


In [3]:
# paths
data_path = os.path.join('.', 'data')
dataset_path = os.path.join(data_path, 'otmm_makam_recognition_dataset')

# training parameters
distribution_types = ["pd", "pcd"]
step_sizes = [7.5, 15.0, 25.0, 50.0, 100.0]
kernel_widths = [0, 7.5, 15.0, 25.0, 50.0, 100.0]
model_types = ['single', 'multi']

# load annotation
annotations = json.load(open(os.path.join(dataset_path, 'annotations.json')))

Create the stratifies folds in 10-fold cross validation


In [4]:
# divide the data into stratified 10-fold
cemil_death = 1916  # use Tanburi Cemil Bey's death year for reproducability
folds = FoldGenerator.stratified_k_fold(
    os.path.join(dataset_path, 'data'), annotations, n_folds=10, random_state=cemil_death)

# add index to folds for saving the training model later
folds = list(enumerate(folds))

json.dump(folds, open(os.path.join(data_path, 'folds.json'), 'w'), indent=4)

Feature extraction

Here we compute the two distributions for each recording. The first is a histogram, which will be used to accumulate the "single data point per mode" model and the other is a probabilty density function (i.e. the histogram normalized by the sum of the values), which is directly used in the "multi data point per mode" model in the next step


In [5]:
# compute the distribution per recording for all combinations
fcombs = list(itertools.product(
        step_sizes, kernel_widths, distribution_types, annotations, 
        [dataset_path], [data_path]))
# ignore combinations in which kernel_width is three times less than the step_size
fcombs = [c for c in fcombs if c[1] == 0 or 3 * c[1] >= c[0]]
fcombs = np.array(fcombs).transpose().tolist()

feature_result = dview.map_sync(compute_recording_distributions, *fcombs)

Training

  • Trains the "single data point per mode" model from the extracted histograms
  • Trains the "multi data point per mode" model from the extracted probability density functions

In [6]:
# get all parameter combinations
tcombs = list(
    itertools.product(step_sizes, kernel_widths, distribution_types, folds, [data_path]))
# ignore combinations in which kernel_width is three times less than the step_size
tcombs = [c for c in tcombs if c[1] == 0 or 3 * c[1] >= c[0]]
tcombs = np.array(tcombs).transpose().tolist()

single_result = dview.map_sync(train_single, *tcombs)
multi_result = dview.map_sync(train_multi, *tcombs)