[PYTHON] J'ai essayé de classer la musique en majeur / mineur sur Neural Network

L'autre jour, j'ai examiné le code d'un simple réseau neuronal récurrent, et de l'idée de vouloir en faire quelque chose, j'ai eu l'idée de classer la musique en majeur / mineur. .. Bien sûr, je ne suis pas familier avec la musique, alors j'ai commencé par rechercher les majeurs et les mineurs sur Wikipedia.

Tone (cho, key) est l'un des termes musicaux. Lorsqu'une mélodie ou un accord est composé en association avec une note centrale, la musique est dite tonale. Au sens étroit, dans la musique occidentale traditionnelle, il existe deux clés connues, la clé majeure et la clé mineure, qui sont composées de sons de toute l'échelle (échelle diatonique). Le son de la est le son central.

La définition de base est la suivante, mais comme je l'ai appris au primaire et au collège, la majeure est une chanson qui sonne «brillante et énergique», tandis que la mineure est une chanson qui sonne «sombre et lourde». J'ai cherché à savoir si cela pouvait être classé par programme.

Un peu plus d'explications sur les touches majeures et mineures

La note centrale est également appelée la note fondamentale (ci-après dénommée la clé de base), mais la plus connue est le "Do majeur" avec "C" comme clé de base, le soi-disant "doremi fasolaside". (Au Japon, on l'appelle aussi "Do majeur".) Le mineur qui commence par le son de La est "A minor" ("A minor"). Je citerai ces deux échelles de wikipedia.

Fig. C major scale C_major_scale.png

Fig. A minor scale A_minor_scale.png

Comprenez vous? Dans ces deux échelles, l'échelle est écrite sur une partition simple sans symbole pointu ni symbole plat sur le côté droit du symbole des aigus (Otamajakushi). En d'autres termes, do majeur et la mineur sont constitués de «sons constituants identiques». (Les éléments sont les mêmes.) La différence entre les deux est de savoir si la touche de base est "C" ou "A". (Un mineur est décalé vers le bas.) En passant, il semble que la relation entre deux gammes avec les mêmes sons constitutifs soit appelée "tonalité parallèle".

Maintenant, lorsque nous traitons de l'échelle de la musique, nous attribuons des numéros à chaque son.

Fig. Key mapping music_keyboard.png

La figure ci-dessus montre le clavier pour une octave, mais les nombres entiers tels que 3, 4, 5 ... sont attribués dans l'ordre à partir du "C" le plus à gauche (do). Les numéros sont également attribués là où il y a des touches noires, donc si vous ne regardez que les touches blanches, vous verrez une séquence de nombres légèrement irrégulière, mais nous procéderons à la programmation de cette manière.

Paramètres du problème et génération de jeux de données

J'ai mentionné qu'il y a des "Do majeur" et "La mineur" comme typiques en majeur et mineur, mais comme cela seul est ennuyeux en tant que problème de classification, nous traitons 5 majors, 5 mineurs, un total de 10 types d'échelles musicales. , Le problème à diviser grossièrement en majeur (majeur) et mineur (mineur) a été défini.

Nous avons préparé les 10 types d'échelles suivants. Il y a 5 majors et 5 mineurs. Do majeur, La mineur (ces deux sont parallèles), Sol majeur, Mi mineur (ces deux sont parallèles), Ré majeur, si mineur (ces deux sont parallèles), La majeur, fa dièse mineur (ces deux sont parallèles), Mi majeur, Do dièse mineur (ces deux sont parallèles).

Un programme a été utilisé pour sélectionner au hasard parmi ces 10 types de gammes et générer des morceaux (séquences sonores). Tout d'abord, nous avons préparé une constante qui suit les règles de la clé.

 scale_names = ['Cmj', 'Gmj', 'Dmj', 'Amj', 'Emj', 'Amn', 'Emn', 'Bmn', 'Fsmn', 'Csmn']

cmj_set = [3, 5, 7, 8, 10, 12, 14]
cmj_base = [3, 15]
amn_base = [12, 24]

gmj_set = [3, 5, 7, 9, 10, 12, 14]
gmj_base = [10, 22]
emn_base = [7, 19]

dmj_set = [4, 5, 7, 9, 10, 12, 14]
dmj_base = [5, 17]
bmn_base = [14, 26]

amj_set = [4, 5, 7, 9, 11, 12, 14] 
amj_base = [12, 24]
fsmn_base = [9, 21]

emj_set = [4, 6, 7, 9, 11, 12, 14] 
emj_base = [7, 19]
csmn_base = [4, 16]

scale_names est une chaîne d'échelles. Ensuite, préparez une liste de sons constituants composée de sept entiers (pour une octave). Par exemple, réglez le son constitutif "Doremi Fasorashi" en do majeur sur [3, 5, 7, 8, 10, 12, 14] en vous référant à la carte des touches de la figure ci-dessus. Puisqu'il s'agit d'une octave, le son une octave plus haut peut être calculé comme «[15, 17, 19, 20, 22, 24, 26]» en ajoutant «12» à cet élément de liste. Définissez également la clé de base pour chaque échelle. Soit la clé de base de do majeur "do" et "do" une octave plus haut, comme cmj_base = [3, 15].

Ensuite, en mineur, comme mentionné ci-dessus, do majeur et la mineur sont dans un ton parallèle, et les sons constitutifs sont les mêmes. Seule la clé de base d'un mineur est définie comme ʻamn_base = [12, 24] (le son de" la "). Plus tard, lors de la génération d'un morceau (séquence) de la mineur, reportez-vous à la liste de sons constitutifs cmj_set` de do majeur. Etant donné qu'une telle relation de "ton parallèle" est utilisée, les constantes de définition d'échelle suivantes (liste dans la liste) sont préparées et utilisées à l'avance.

scale_db = [
    [cmj_set, cmj_base, amn_base],
    [gmj_set, gmj_base, emn_base],
    [dmj_set, dmj_base, bmn_base],
    [amj_set, amj_base, fsmn_base],
    [emj_set, emj_base, csmn_base]
    ]

Vient ensuite la génération de l'ensemble de données, qui est exprimé en pseudo-code comme suit.

# Begin
   # 0 ..Générez 9 nombres aléatoires et répondez'key'Decider.
    key_index = np.random.randint(10)
    
    #avoir été décidé'key'Extrayez la liste pour une octave des notes constituantes et la liste des clés de base de.
    myset, mybase = (scale_db[][], scale_db[][])
    #Étendez l'échelle à 2 octaves.
    myscale2 = prep_2x_scale(myset)
    
    #De longueur de séquence'for'boucle
    for i in range(m_len):
        if i == 0:    #Le premier son est Base Key (une octave plus haut)
          cur_key = base[1]
        else:         #Le second son et les suivants sont dirigés de manière aléatoire.
            direct = np.random.randint(7)
            if t < 3 :
Dans la liste des échelles, sélectionnez une note inférieure d'une unité à la note précédente.
            if t < 4 :
Sélectionnez le même son que précédemment.
            else:
Dans la liste des échelles, sélectionnez une note supérieure de 1 à la note précédente.
                
# Vérifiez comment la séquence se termine
    if last_ley in base:    #Le dernier son est Base Key?
        proper = True
Adoptez comme données.
    else 
        proper = False
Cette séquence est abandonnée car elle ne se termine pas bien.

# End

De cette manière, des nombres aléatoires sont utilisés pour générer une séquence de nombres qui indiquent la clé du son. Toute longueur de séquence et tout nombre peuvent être générés, et l'exemple de sortie est le suivant. (Cette fois, la longueur de la séquence est de 20.)

21, 19, 17, 19, 21, 19, 17, 16, 14, 16, 17, 19, 21, 19, 21, 23, 21, 21, 19, 21, Fsmn
16, 14, 16, 14, 16, 14, 12, 11, 12, 14, 12, 14, 12, 14, 16, 14, 12, 14, 16, 16, Csmn
26, 24, 24, 24, 22, 22, 24, 22, 24, 22, 24, 22, 22, 21, 22, 24, 24, 22, 24, 26, Bmn
21, 23, 21, 19, 21, 23, 24, 24, 26, 26, 24, 23, 21, 19, 21, 19, 21, 23, 23, 21, Fsmn
24, 26, 26, 24, 22, 20, 22, 20, 19, 19, 17, 15, 14, 15, 17, 15, 17, 15, 14, 12, Amn
 ...

De cette façon, une séquence d'entiers et un ensemble d'étiquettes de touches (chaînes de caractères) sont émis, mais dans la séquence de la première ligne, cela commence par le son de basse 21 de `` F sharp minor '' et se termine par le même son 21. Vous pouvez le vérifier. De plus, dans la séquence "A minor" sur la 5ème ligne, il peut être confirmé qu'elle commence par le son de basse 24 et se termine par le son de basse 12 une octave en dessous.

(Supplément)

Le problème posé cette fois est de classer majeur (sensation lumineuse) et mineur (sensation sombre), mais il est certain de jouer réellement et de se demander si les données générées par le programme ci-dessus sont appropriées. Je pense. J'ai également sorti l'iPad et appuyé sur le clavier avec l'application GarageBand, mais c'était assez difficile à jouer pour que je puisse comprendre le ton (clair / sombre), alors j'ai abandonné immédiatement. (J'aurais peut-être dû utiliser le format standard midi et jouer automatiquement, mais je n'ai ni les compétences ni le courage de le faire. Cependant, pour les 10 types de Key Scales que j'ai traités cette fois, j'ai appuyé sur les touches (limité). Cependant, j'ai confirmé qu'il faisait clair / sombre.)

Modèle de réseau neuronal (étude préliminaire avec MLP)

Avant d'essayer le réseau neuronal récurrent (RNN), j'ai d'abord examiné ce qui se passe avec le modèle MLP (Multi-layer Perceptron). Le modèle doit considérer chaque séquence de sons d'une longueur prédéterminée comme un nombre indépendant et entrer celui-ci dans le nombre d'unités de réseau pour obtenir une sortie. Puisqu'il est possible de composer deux majors (majeur et mineur) à partir des mêmes sons constitutifs, on s'attendait à ce que la précision de la classification ne s'améliore pas avec ce modèle qui n'utilise pas de séquences.

La configuration de cette couche de modèle est la suivante.

class HiddenLayer(object):

(Omis)

class ReadOutLayer(object):

(Omis)

h_layer1 = HiddenLayer(input=x, n_in=seq_len, n_out=40)            #Couche cachée 1
h_layer2 = HiddenLayer(input=h_layer1.output(), n_in=40, n_out=40) #Couche cachée 2
o_layer = ReadOutLayer(input=h_layer2.output(), n_in=40, n_out=1)  #Couche de sortie

Il s'agit d'un modèle MLP avec deux couches cachées et enfin une couche de sortie, pour un total de trois couches. (Cette fois, la longueur de la séquence est définie sur seq_len = 20.) La figure ci-dessous montre l'état du calcul effectué avec ce modèle.

Fig. Loss & Accuracy by MLP model (RMSProp) mlp_learn1.png

La ligne rouge est le coût et la ligne bleue est la précision de classification des données de train. C'est un calcul vibrationnel probablement parce que le paramétrage de l'hyper (ou processus de régularisation) n'était pas approprié, mais la précision finale est de 0,65. Puisqu'il s'agit d'un problème de classification binaire, si vous lancez un dé ou effectuez un tirage au sort et le classifiez de manière appropriée, la précision est de 0,50, ce qui est une précision légèrement améliorée par rapport à cette ligne de base. L'impression est que ce n'était pas aussi grave que ce à quoi je m'attendais.

Au début du calcul, j'étais préoccupé par la partie où la perte et la précision stagnaient et le point où le calcul oscillait, donc le résultat du calcul en changeant l'optimiseur est montré dans la figure ci-dessous.

Fig. Loss & Accuracy by MLP model (Gradient Descent) mlp_learn2.png

La vibration du calcul a disparu, mais la stagnation au début du calcul persiste. La précision est légèrement améliorée à environ 0,67.

Je l'ai calculé avec RNN (Elman Net) ...

Ensuite, le calcul a été effectué en utilisant Elman Net, qui est le RNN (Recurrent Nueral Network) favori et simple. La partie principale de ce modèle est le code suivant.

class simpleRNN(object):
    #   members:  slen  : state length
    #             w_x   : weight of input-->hidden layer
    #             w_rec : weight of recurrnce 
    def __init__(self, slen, nx, nrec, ny):
        self.len = slen
        self.w_h = theano.shared(
            np.asarray(np.random.uniform(-.1, .1, (nx)),
            dtype=theano.config.floatX)
        )
        self.w_rec = theano.shared(
            np.asarray(np.random.uniform(-.1, .1, (nrec)),
            dtype=theano.config.floatX)
        )
        self.w_o = theano.shared(
            np.asarray(np.random.uniform(-1., .1, (ny)),
            dtype=theano.config.floatX)
        )
        self.b_h = theano.shared(
            np.asarray(0., dtype=theano.config.floatX)            
        )
        self.b_o = theano.shared(
            np.asarray(0., dtype=theano.config.floatX)
        )
    
    def state_update(self, x_t, s0):
        # this is the network updater for simpleRNN
        def inner_fn(xv, s_tm1, wx, wr, wo, bh, bo):
            s_t = xv * wx + s_tm1 * wr + bh
            y_t = T.nnet.sigmoid(s_t * wo + bo)
            
            return [s_t, y_t]
        
        w_h_vec = self.w_h[0]
        w_rec_vec = self.w_rec[0]
        w_o = self.w_o[0]
        b_h = self.b_h
        b_o = self.b_o
        
        [s_t, y_t], updates = theano.scan(fn=inner_fn,
                        sequences=[x_t],
                        outputs_info=[s0, None],
                        non_sequences=[w_h_vec, w_rec_vec, w_o, b_h, b_o]
        )
        return y_t
(Omis)

    net = simpleRNN(seq_len, 1, 1, 1)
    y_t = net.state_update(x_t, s0)
    y_hypo = y_t[-1]
    prediction = y_hypo > 0.5
    
    cross_entropy = T.nnet.binary_crossentropy(y_hypo, y_)
    

Reportez-vous à la figure pour obtenir des explications.

Fig. Simple RNN structure RNN0_model.png

La figure montre la configuration développée par ordre chronologique sur la base de la méthode BPTT (Back propagation through time). Les données de séquence sonore sont entrées dans ce modèle sous la forme [X1, X2, X3, ..., Xn]. Celle-ci est pondérée puis sortie vers la couche cachée S, la récursive est calculée et enfin la série [Y1, Y2, Y3, ..., Yn] est sortie. La sortie de la dernière unité Yn de cette série Y passe par la fonction d'activation pour obtenir un nombre binaire (0 ou 1).

J'ai essayé d'exécuter le calcul avec espoir, mais le résultat était décevant.

Fig. Loss & Accuracy by 1st RNN model (RMSProp) rnn0_learn.png

Peu de progrès ont été réalisés et la précision finale était de 0,58, ce qui n'est pas très différent de la performance zéro de 0,5. (Cela n'a pas fonctionné même si je l'ai changé pour l'optimiseur ou joué avec les hyper paramètres.)

Je soupçonnais que la cause était que seul [Yn] dans la séquence de sortie était référencé et que les informations restantes [Y1 .. Yn-1] étaient ignorées. Par conséquent, nous avons examiné l'amélioration du modèle.

Modèle amélioré RNN (couche de sortie ajoutée)

Afin de se référer à toutes les valeurs de sortie de la séquence [Y1, Y2, ..., Yn], nous avons décidé de les pondérer pour créer un signal de classification.

Fig. Simple RNN + Read-out Layer structure RNN1_model.PNG

Le code est créé en insérant la partie couche de sortie du modèle MLP.

class simpleRNN(object):
    #   members:  slen  : state length
    #             w_x   : weight of input-->hidden layer
    #             w_rec : weight of recurrnce 
    def __init__(self, slen, nx, nrec, ny):

(Omis)
    
    def state_update(self, x_t, s0):
    
(Omis)

class ReadOutLayer(object):                 # <====Classe supplémentaire
    def __init__(self, input, n_in, n_out):
        self.input = input
        
        w_o_np = 0.05 * (np.random.standard_normal([n_in,n_out]))
        w_o = theano.shared(np.asarray(w_o_np, dtype=theano.config.floatX))
        b_o = theano.shared(
            np.asarray(np.zeros(n_out, dtype=theano.config.floatX))
        )
       
        self.w = w_o
        self.b = b_o
        self.params = [self.w, self.b]
    
    def output(self):
        linarg = T.dot(self.input, self.w) + self.b
        self.output = T.nnet.sigmoid(linarg)  

        return self.output
        
(Omis)

    net = simpleRNN(seq_len, 1, 1, 1)
    y_t = net.state_update(x_t, s0)
    y_tt = T.transpose(y_t)
    ro_layer = ReadOutLayer(input=y_tt, n_in=seq_len, n_out=1)  # <====ajouter à
    
    y_hypo = (ro_layer.output()).flatten()
    prediction = y_hypo > 0.5
    
    cross_entropy = T.nnet.binary_crossentropy(y_hypo, y_)
    
(Omis)

La situation dans laquelle le calcul a été exécuté est la suivante.

Fig. Loss & Accuracy by 2nd RNN model (RMSProp) rnn1_learn.png

Au fur et à mesure que l'apprentissage progressait, la précision finale s'est améliorée à 0,73. On pense que la raison est que les informations de la séquence de sortie ont été extraites avec succès comme prévu et que le degré de flexibilité (flexibilité) dans le processus d'apprentissage a augmenté parce que le nombre de poids a augmenté et le degré de liberté du réseau a augmenté. ing.

Cependant, avec une précision de 0,73, elle est inférieure à la valeur initialement attendue. (Je pensais à la précision de classification de 0,9 + comme cible.) Il est peut-être possible d'améliorer encore la précision en étudiant et en améliorant le mouvement de chaque poids, mais cette fois, j'aimerais le terminer.

Cette fois, j'ai utilisé des données musicales artificielles créées par un programme avec des nombres aléatoires, mais je pense que cela peut aussi affecter la faible précision de cette heure. (N'y a-t-il pas des règles plus compliquées dans la musique réelle?) Si des données, etc. peuvent être obtenues, je voudrais classer la mélodie faite par les humains en majeure / mineure. (Vous devrez peut-être étudier un peu plus le solfège.)

Références (site Web)

Recommended Posts

J'ai essayé de classer la musique en majeur / mineur sur Neural Network
J'ai essayé de prédire le genre de musique à partir du titre de la chanson sur le réseau neuronal récurrent
J'ai essayé de résumer quatre méthodes d'optimisation de réseau neuronal
J'ai essayé de mettre en œuvre le modèle de base du réseau neuronal récurrent
J'ai essayé de classer le texte en utilisant TensorFlow
J'ai essayé d'améliorer la précision de mon propre réseau neuronal
J'ai essayé de classer les boules de dragon par adaline
J'ai essayé de déboguer.
Introduction à la création d'IA avec Python! Partie 3 J'ai essayé de classer et de prédire les images avec un réseau de neurones convolutifs (CNN)
J'ai essayé d'implémenter Mine Sweeper sur un terminal avec python
Implémentez un réseau de neurones feedforward dans Chainer pour classer les documents
J'ai essayé d'installer scrapy sur Anaconda et je n'ai pas pu
J'ai essayé de classer les voix des acteurs de la voix
J'ai essayé d'apprendre PredNet
J'ai essayé d'organiser SVM.
J'ai essayé d'utiliser "Syncthing" pour synchroniser des fichiers sur plusieurs PC
J'ai essayé d'implémenter PCANet
J'ai essayé de classer MNIST par GNN (avec PyTorch géométrique)
J'ai essayé de réintroduire Linux
J'ai essayé de présenter Pylint
J'ai essayé de résumer SparseMatrix
J'ai essayé de changer le script python de 2.7.11 à 3.6.0 sur Windows10
J'ai essayé de lancer le cluster ipython au minimum sur AWS
J'ai essayé un réseau de neurones convolutifs (CNN) avec un tutoriel TensorFlow sur Cloud9-Classification des images manuscrites-
jupyter je l'ai touché
J'ai essayé d'implémenter StarGAN (1)
J'ai fait un générateur de réseau neuronal qui fonctionne sur FPGA
J'ai essayé MLflow sur Databricks
J'ai créé mon propre réseau de neurones à propagation directe à 3 couches et j'ai essayé de comprendre le calcul en profondeur.
J'ai essayé de créer un environnement serveur qui fonctionne sous Windows 10
J'ai essayé de créer un environnement de MkDocs sur Amazon Linux
J'ai essayé Python! ] Puis-je publier sur Kaggle sur iPad Pro?
J'ai essayé de numériser le tampon estampé sur papier en utilisant OpenCV
J'ai essayé d'enregistrer une station sur la plateforme IoT "Rimotte"
J'ai essayé de démarrer avec Bitcoin Systre le week-end
J'ai essayé de visualiser les données BigQuery à l'aide de Jupyter Lab avec GCP
J'ai essayé d'afficher l'interface graphique sur Mac avec le système X Window
J'ai essayé de résumer les remarques de tout le monde sur le slack avec wordcloud (Python)
J'ai essayé AdaNet pour les données de table
J'ai essayé d'implémenter Deep VQE
J'ai essayé de créer l'API Quip
J'ai essayé de toucher Python (installation)
J'ai essayé d'expliquer l'ensemble de données de Pytorch
J'ai essayé l'authentification vocale Watson (Speech to Text)
J'ai essayé de mettre en œuvre un réseau de neurones à deux couches
J'ai essayé Cython sur Ubuntu sur VirtualBox
J'ai touché l'API de Tesla
J'ai essayé de m'organiser à propos de MCMC.
J'ai essayé d'implémenter Realness GAN
Comment installer Music 21 sur Windows
J'ai essayé de déplacer le ballon
J'ai essayé d'estimer la section.
J'ai essayé de comprendre attentivement la fonction d'apprentissage dans le réseau de neurones sans utiliser la bibliothèque d'apprentissage automatique (deuxième moitié)
[Python] J'ai essayé de visualiser la nuit du chemin de fer de la galaxie avec WordCloud!
J'ai essayé d'utiliser jpholidayp sur proxy pour exécuter cron uniquement en semaine
J'ai écrit un diagramme de configuration du système avec des diagrammes sur Docker
Touches de karaoké assorties ~ J'ai essayé de le mettre sur Laravel ~ <en route>
J'ai essayé d'installer Docker sur Windows10 Home mais cela n'a pas fonctionné