Une histoire sur tout, de la collecte de données au développement d'IA et à la publication d'applications Web en Python (3. développement d'IA)

introduction

Cet article est une série. Cliquez ici pour d'autres articles ↓ 0. Design (Qu'est-ce que l'IA pour déterminer la clé?) 1. Collecte de données (exploration) 2. Mise en forme des données (grattage) 4. Développement d'applications Web à l'aide de Django

Cela fait 3-4 mois depuis le développement. Au moment du développement, l'objectif était de déployer, et je n'ai pas passé beaucoup de temps à choisir le modèle essentiel.

Alors cette fois, j'aimerais essayer différentes approches et comparer la précision. J'essaierai également de trouver une précision basée sur des règles sans utiliser l'apprentissage automatique.

Confirmation de tâche

La tâche cette fois est de déterminer la tonalité à partir de la progression d'accords de la chanson. Progression du code? Clé? Veuillez consulter l'article ci-dessous pour plus d'informations.

Une histoire sur tout, de la collecte de données au développement d'IA et à la publication d'applications Web en Python (0. Design)

Confirmation des données

J'ai gratté le nombre de fois où les accords sont apparus et la clé du site de publication de tableaux d'accords appelé J-Total Music. Parmi les chansons affichées, il y a des données pour les chansons (20848 chansons) pour lesquelles la clé pourrait être obtenue.

Cependant, certaines chansons ont peu de codes. Exemple: https://music.j-total.net/data/012si/071_shinohara_tomoe/001.html Étant donné que ces morceaux ne conviennent pas aux données d'entraînement, les données avec un nombre total d'accords apparaissant 20 fois ou moins dans le morceau seront supprimées. En outre, en tant que suppression des valeurs aberrantes, les données avec un nombre total d'accords apparaissant dans la chanson de 250 ou plus sont également supprimées. Dans ce qui suit, nous ferons diverses choses en fonction des ** 20481 données de morceau ** </ u> restantes.

Image de données

C'est comme ça. «key» est la variable expliquée et à droite de «key» la variable explicative. Pour plonger dans le modèle de machine learning, encodez en libellé la clé et changez-la de 0 à 23. D'autres sont utilisés tels quels.

Biais de données (classe)

Il existe 24 types de clés en tout, mais bien sûr, le nombre de données n'est pas pair. Il y a une différence de près de 10 fois entre le plus de touches et le moins de touches. Ce n'est pas autant que des données dites déséquilibrées, mais c'est un peu inquiétant. À propos, le rapport entre la touche majeure et la touche mineure est de ** 149 55: 5526 **, et 73% est la chanson principale </ u>. Si vous regardez le graphique ci-dessus, vous pouvez voir que les mineurs sont regroupés en bas.

Ce que tu veux essayer

Essayez différentes approches pour voir à quel point elles sont précises.

  1. [Classer par règle](#Classifier par règle)
  2. [Classer par apprentissage automatique](#Classer par apprentissage automatique) 2-1. [24 classification](## 2-1.24 classification) 2-2. [Utiliser la connaissance du domaine pour modifier la conception du problème](## 2-2. Utiliser la connaissance du domaine pour modifier la conception du problème)

Indicateurs et données de test pour comparaison

Les indicateurs d'évaluation pour la classification multi-classes sont décrits en détail dans les pages suivantes. https://analysis-navi.com/?p=553

Calculez les trois valeurs suivantes et le taux de réponse correct pour chaque classe.

  • Taux de réponse correct (nombre de réponses correctes / nombre de données)
  • Taux de rappel de macro (moyenne du taux de réponse correcte pour chaque classe)
  • Score Macro F1 (moyenne des scores F1 pour chaque classe)

En ce qui concerne les données de test, «train_test_split» de sklearn prépare 25% du total des données (environ 5000 chansons). La valeur de la moyenne de 5 fois est calculée en tenant compte de la variation de précision en fonction des données. Aussi, comme autre donnée de test, vérifiez le taux de réponse correct avec 90 chansons de flumpool postées sur U-fret. C'est parce que j'aime flumpool et que je connais les clés de toutes les chansons. Ci-après, ces données sont appelées données fp.

résultat

Comme ce sera long, je vais d'abord résumer les résultats.

indice Base de règles 1 Base de règles 2 Retour logistique Machine de vecteur de soutien LightGBM LGBM × LGBM
Taux de réponse correct 0.528 0.613 0.843 0.854 0.857 0.839
Rappel de macro 0.566 0.626 0.836 0.832 0.836 0.826
Score Macro F1 0.567 0.581 0.82 0.827 0.833 0.812
Taux de réponse correct des données fp 0.178 0.611 0.889 0.889 0.911 0.867

LightGBM est le plus précis!

1. Classification fondée sur des règles

Au moment du développement, j'utilisais l'apprentissage automatique parce que ma motivation était "Je veux faire quelque chose avec l'apprentissage automatique pour le moment!", Mais c'est peut-être un problème qui peut être résolu sur une base de règles en premier lieu. Essayez les deux suivants. 1-1. [L'accord le plus fréquemment utilisé dans le morceau (ci-après désigné comme l'accord le plus fréquemment utilisé) est utilisé comme tonalité](## 1-1. L'accord le plus fréquemment utilisé est utilisé comme tonalité) 1-2. [Calculez le nombre total de codes diatoniques pour chaque touche et sortez la clé la plus populaire](## 1-2. Calculez le nombre total de codes diatoniques pour chaque touche)

1-1. Utilisation du code le plus fréquent comme clé

Disons que l'accord le plus utilisé dans la chanson est la clé. Par exemple, si "Dm" est utilisé le plus souvent dans un morceau, la clé de ce morceau est identifiée comme Dm. C'est simple. S'il y a plusieurs codes les plus fréquents, je déciderai de la clé au hasard.

<détails>

code </ summary>

import random


def mode_pred(data):

    
    #Trouvez le code le plus fréquent et enregistrez le nom du code
    tmp_max = -1
    for c in num_cols:
        if tmp_max < data[c]:
            ans = [c]
            tmp_max = data[c]
        elif tmp_max == data[c]:
            ans.append(c)
            
    #S'il y a plusieurs codes les plus fréquents, sélectionnez-les au hasard
    if len(ans) == 1:
        return ans[0]
    else:
        return random.choice(ans)

df['mode_pred'] = df.apply(mode_pred, axis=1)

résultat

  • 5 fois la moyenne Taux de réponse correcte: 0,528 Rappel de macro: 0,556 Score Macro F1: 0,567

  • données fp Taux de réponse correcte: 0,178

<détails>

Taux de légitimité (taux de rappel) pour chaque classe </ summary>

Clé Taux de réponse correct (taux de rappel)
C_minor 0.763
F_minor 0.747
G_minor 0.699
D_minor 0.684
B_minor 0.681
A_minor 0.676
D#/E♭_minor 0.668
C#/D♭_minor 0.663
E_minor 0.663
A#/B♭_minor 0.654
F#/G♭_minor 0.641
G#/A♭_minor 0.611
E_Major 0.522
G_Major 0.504
A_Major 0.496
A#/B♭_Major 0.494
D_Major 0.485
C_Major 0.483
F_Major 0.433
F#/G♭_Major 0.425
B_Major 0.412
C#/D♭_Major 0.408
D#/E♭_Major 0.402
G#/A♭_Major 0.379

En regardant chaque classe, nous pouvons voir que le pourcentage de bonnes réponses en mineur est supérieur à celui en majeur. Si vous le comparez avec votre propre connaissance du domaine, vous pouvez en quelque sorte le comprendre. Les données fp sont inutiles, n'est-ce pas? .. .. C'est probablement parce qu'il y a beaucoup de chansons en major.

1-2. Calculez le nombre total de codes diatoniques pour chaque touche

Comme je l'ai écrit dans Article précédent, lorsque je détermine la clé à partir du tableau d'accords En regardant le code utilisé, il est déterminé que «c'est cette clé parce que le code diatonique de cette clé est souvent utilisé». Implémentons cette méthode. Plus précisément, le flux est le suivant.

  1. Obtenez le nombre total d'occurrences du code diatonique pour chaque touche à partir des données du morceau.
  2. Utilisez la clé avec le plus grand total obtenu en 1. comme clé du morceau (s'il y a plusieurs touches, décidez au hasard)

Étant donné que l'instruction for est tournée 24 fois (le nombre de touches) sur chaque ligne, le processus prend un temps considérable. Il a fallu environ 20 minutes au total pour calculer la moyenne de 5 fois. .. ..

<détails>

code </ summary>

def diatonic_pred(data):
    tmp_max = -1
    #Trouvez le nombre total d'occurrences du code diatonique pour chaque touche
    for key, cols in diatonic_dict.items():
        sum_value = data[cols].sum()
        if tmp_max < sum_value:
            ans = [key]
            tmp_max = sum_value
        elif tmp_max == sum_value:
            ans.append(key)
    #La discrimination
    if len(ans) == 1:
        return ans[0]
    else:
        return random.choice(ans)

tqdm_notebook.pandas()
df['diatonic_pred'] = df.progress_apply(diatonic_pred, axis=1)

résultat

  • 5 fois la moyenne Taux de réponse correcte: 0,613 Taux de réponses correctes moyen pour chaque classe: 0,626 Valeur F: 0,581

  • données fp Taux de réponse correcte: 0,611

<détails>

Taux de légitimité (taux de rappel) pour chaque classe </ summary>

Clé Taux de réponse correct (taux de rappel)
F_minor 0.711
G_minor 0.702
C_minor 0.688
A#/B♭_minor 0.688
A_minor 0.67
D_minor 0.667
G_Major 0.651
F#/G♭_minor 0.649
B_minor 0.649
E_minor 0.633
C#/D♭_minor 0.632
G#/A♭_minor 0.615
F_Major 0.614
G#/A♭_Major 0.614
A#/B♭_Major 0.61
B_Major 0.61
D#/E♭_Major 0.607
F#/G♭_Major 0.604
E_Major 0.596
D_Major 0.586
D#/E♭_minor 0.579
A_Major 0.572
C_Major 0.566
C#/D♭_Major 0.504

La précision moyenne est supérieure à la discrimination par le code le plus fréquent. La valeur maximale du taux de réponse correcte est d'environ 70%, ce qui est à peu près la même pour les deux méthodes, mais il existe une différence considérable dans la valeur minimale. Le taux de réponse correcte le plus bas pour le code le plus fréquent était de 38%, mais cette méthode est de 50%. De plus, comme auparavant, la précision de classification des chansons en mineur est élevée.

Jetons un coup d'œil à la matrice de confusion ici. Curieusement, certaines clés sont mal classées d'environ 20% pour chaque clé. Ceci est parallèle, qui est une clé avec presque le même code utilisé. Puisque cette méthode de discrimination utilise le nombre de fois où le code a été utilisé, je pense qu'il est naturel de le classer à tort comme une clé parallèle.

Le taux de réponse correcte pour les données fp a considérablement augmenté par rapport à la fois précédente, mais il est d'environ 60%. J'ai examiné les données de classification erronée, mais la plupart d'entre elles ont été mal classées en parallèle. Il s'avère que cette méthode ne fonctionne pas pour la classification des tons parallèles.

2. Classification par apprentissage automatique

Maintenant que vous connaissez la précision basée sur des règles, il est temps d'essayer l'apprentissage automatique. J'essaierai ici aussi quelques méthodes. 2-1. [Discriminate as 24 class classification](## 2-1.24 Class classification) 2-2. [Utiliser la connaissance du domaine pour modifier la conception du problème](## 2-2. Utiliser la connaissance du domaine pour modifier la conception du problème)

Classement 2-1. 24

Il est traité comme un problème de classification de 24 classes avec obéissance. La méthode est la ** régression logistique ** en tant que représentant de l'algorithme de séparation linéaire classique, ** la machine vectorielle de support ** en tant que représentant de l'algorithme de séparation non linéaire, et est la plus puissante en termes de haute précision, de vitesse d'apprentissage élevée et de peu de prétraitement. Utilisez le ** LightGBM ** haut de gamme. Je veux comparer les méthodes, je m'abstiendrai donc d'ajuster les paramètres flashy.

Divisez les données avec train_test_split et plongez-les dans le modèle tel quel. Il n'ajuste pas les hyper paramètres, mais il est défini sur class_weight = balancé. En faisant cela, il sera pondéré par «nombre d'échantillons de la classe correspondante / (nombre de classes * nombre total d'échantillons)».

Retour logistique

Pour le moment, j'ai essayé de jouer avec les paramètres du terme de régularisation, mais comme il n'y avait pas de grande différence, je m'entraîne par défaut. <détails>

code </ summary>

for seed in [1, 4, 9, 16, 25]:
    X_train, X_test, y_train, y_test = train_test_split(df[num_cols], df['target_key'], random_state=seed)
    lr = LogisticRegression(class_weight='balanced')
    lr.fit(X_train, y_train)
    y_pred = lr.predict(X_test)

Machine de vecteur de soutien

En guise de test, j'ai entraîné la fonction du noyau avec le noyau rbf, méthode de classification one-to-other. Cependant, il a fallu près d'une heure pour apprendre et la précision était décevante, avec une moyenne d'environ 30%. Puisqu'il est préférable pour les machines vectorielles de support de standardiser les variables, nous avons décidé de mesurer la précision avec la standardisation des variables + noyau rbf + 1 par rapport à une autre méthode de classification. En normalisant, le temps d'exécution est devenu des ordres de grandeur plus rapides, et j'ai été impressionné. Cependant, un apprentissage prend environ 2-3 minutes, donc le temps d'exécution est plus lent que les deux autres.

<détails>

code </ summary>

from sklearn.svm import SVC
from sklearn.multiclass import OneVsRestClassifier
from sklearn.preprocessing import StandardScaler

#1 vs autre classification
svc = SVC(kernel='rbf', class_weight='balanced', verbose=True)
ovr = OneVsRestClassifier(svc)
#Standardisation
sc = StandardScaler()

for seed in [1, 4, 9, 16, 25]:
    X_train, X_test, y_train, y_test = train_test_split(sc.fit_transform(X), y, random_state=seed)
    ovr.fit(X_train, y_train)
    y_pred = ovr.predict(X_test)

LightGBM Je l'ai exécuté avec les paramètres par défaut.

<détails>

code </ summary>

import lightgbm as lgbm

for seed in [1, 4, 9, 16, 25]:
    X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=seed)
    clf = lgbm.LGBMClassifier(class_weight='balanced')
    clf.fit(X_train, y_train)
    y_pred = clf.predict(X_test)

résultat

Retour logistique Machine de vecteur de soutien LightGBM
Taux de réponse correct 0.843 0.854 0.857
Rappel de macro 0.836 0.832 0.836
Score Macro F1 0.820 0.827 0.833

Bien qu'il n'y ait pas de grande différence de précision, le résultat est que LightGBM est le plus précis. Cependant, du point de vue du temps d'exécution, c'était comme LightGBM >> Régression logistique >>> Support vector machine. Le temps requis pour 5 apprentissages était inférieur à 1 minute pour LightGBM, alors qu'il a fallu environ 10 minutes pour la machine vectorielle de support. Il faut du temps pour apprendre une fois, donc je sens que je ne peux pas facilement ajuster les paramètres.

Ensuite, regardons le pourcentage de réponses correctes pour chaque classe.

<détails>

Taux de légitimité (taux de rappel) pour chaque classe </ summary>

Clé Retour logistique Machine de vecteur de soutien LightGBM
C_Major 0.838 0.866 0.856
C_minor 0.883 0.885 0.837
C#/D♭_Major 0.825 0.859 0.878
C#/D♭_minor 0.809 0.748 0.755
D_Major 0.84 0.875 0.871
D_minor 0.851 0.814 0.827
D#/E♭_Major 0.841 0.842 0.869
D#/E♭_minor 0.808 0.782 0.761
E_Major 0.871 0.897 0.9
E_minor 0.844 0.84 0.842
F_Major 0.851 0.857 0.87
F_minor 0.881 0.827 0.836
F#/G♭_Major 0.805 0.828 0.847
F#/G♭_minor 0.793 0.751 0.791
G_Major 0.857 0.872 0.872
G_minor 0.861 0.849 0.832
G#/A♭_Major 0.86 0.865 0.866
G#/A♭_minor 0.773 0.704 0.725
A_Major 0.849 0.874 0.887
A_minor 0.826 0.83 0.833
A#/B♭_Major 0.822 0.853 0.867
A#/B♭_minor 0.823 0.796 0.777
B_Major 0.815 0.847 0.855
B_minor 0.847 0.815 0.804

On peut voir que la régression logistique a un pourcentage de réponses correctes plus élevé pour les clés mineures que les deux autres.

Ensuite, traçons le taux de réponse correct pour chaque clé avec chaque méthode à l'aide d'un diagramme de moustaches.

Le taux minimum de réponse correcte pour toutes les méthodes est de 70% ou plus. Cependant, on peut lire que la variation (gamme) de précision de la régression logistique est plus petite que celle des autres méthodes. LightGBM était le meilleur de chaque index, mais vous pouvez voir que la plage est assez inégale.

À propos, en ce qui concerne le taux de réponse correct des données fp, toutes les méthodes avaient un taux de réponse correcte d'environ 80/90 chansons. La plupart des erreurs étaient en parallèle (d'autres étaient des chansons transposées, etc.).

2-2. Modifier la conception du problème en utilisant la connaissance du domaine

Dans le but de "déterminer la clé" cette fois, nous modifierons la conception du problème en utilisant la connaissance du domaine.

Il existe 24 types de touches que vous souhaitez distinguer, mais les 24 types n'ont pas tous des caractéristiques complètement différentes. Pour chaque touche, il n'y a qu'une seule touche avec des caractéristiques similaires (en particulier, la même mélodie et des sons et accords similaires utilisés). Cela s'appelle parallel. Par exemple, la clé parallèle de do (do majeur) est Am (si mineur). Il y a toujours une correspondance entre majeur et mineur. Regardons en fait le tableau des accords.

Et ça? Le code utilisé est assez similaire, n'est-ce pas? Parmi les 24 types de touches, il existe 12 types de telles combinaisons de touches.


Sur la base de ce qui précède, le jugement est rendu comme suit.

  1. Effectuer 12 classifications en parallèle
  2. Classification binaire du majeur ou du mineur. Déterminez la clé en fonction du résultat de 1.

C'est difficile à comprendre, donc si vous donnez un exemple

  • 1 résultat de classification est "C (Do majeur) ou Am (La mineur)", 2 résultat de classification est "majeur" → ** la clé est C (Do majeur) **
  • 1 résultat de classification est "F (fa majeur) ou Dm (ré mineur)", 2 résultat de classification est "mineur" → ** la clé est Dm (ré mineur) **

L'image est que la discrimination est divisée en deux parties comme celle-ci. Les deux modèles utilisent LightGBM.

<détails>

code </ summary>

model_1 = lgbm.LGBMClassifier(class_weight='balanced')
model_2 = lgbm.LGBMClassifier(class_weight='balanced')

key_answer = df['key']
diatonic_answer = df['diatonic_type']
type_answer = df['key_type']
X = df[num_cols]
y1 = df['diatonic_type_int']

for seed in [1, 4, 9, 16, 25]:
    #Classification en 12 classes (classification parallèle)
    X_train, X_test, y1_train, y1_test = train_test_split(X, y1, random_state=seed)
    model_1.fit(X_train, y1_train)
    y1_pred = model_1.predict(X_test)
    #Chaîne de caractères (C_Major@A_retour au mineur)
    y1_pred_str = le_d.inverse_transform(y1_pred)
    
    #Classification binaire avec les mêmes données (majeures ou mineures)
    train_index = y1_train.index
    test_index = y1_test.index
    
    y2_train = type_answer[train_index]
    y2_test = type_answer[test_index]
    
    model_2.fit(X_train, y2_train)
    y2_pred = model_2.predict(X_test)
    
    #Intégrer les résultats de la classification en 12 classes et de la classification binaire
    y_pred = []
    for y1_, y2_ in zip(y1_pred_str, y2_pred):
        if y2_ == 1:
            ans = y1_.split('@')[0]
        else:
            ans = y1_.split('@')[1]
        y_pred.append(ans)
    
    y_test = key_answer[test_index]

résultat

  • 5 fois la moyenne Taux de réponse correcte: 0,839 Rappel de macro: 0,826 Note Macro F1: 0,812

  • données fp Taux de réponse correcte: 0,867

Taux de réponse correct pour chaque classe
Clé Taux de réponse correct (taux de rappel)
C_Major 0.848
C_minor 0.843
C#/D♭_Major 0.858
C#/D♭_minor 0.853
D_Major 0.83
D_minor 0.825
D#/E♭_Major 0.84
D#/E♭_minor 0.836
E_Major 0.82
E_minor 0.815
F_Major 0.797
F_minor 0.787
F#/G♭_Major 0.811
F#/G♭_minor 0.803
G_Major 0.746
G_minor 0.686
G#/A♭_Major 0.775
G#/A♭_minor 0.764
A_Major 0.884
A_minor 0.875
A#/B♭_Major 0.909
A#/B♭_minor 0.89
B_Major 0.869
B_minor 0.864

Le résultat de la moyenne 5 fois est un peu inférieur au résultat de LightGBM avec 24 classifications. La précision de prédiction de la première étape (12 classifications de classes résumées en parallèle) était bonne à environ 93%, mais le taux global de réponse correcte a diminué dans la deuxième étape en majeur ou mineur. En ce qui concerne les données fp, la classification erronée de la tonalité parallèle représentait la plupart des données. Cependant, il y avait des chansons sur lesquelles je n'ai pas fait beaucoup d'erreurs dans le classement des 24 classes.

Résumé

J'ai essayé diverses choses, mais le résultat était qu'il valait mieux le prédire comme une classification de 24 classes docilement avec LightGBM. Après tout, Light GBM est incroyable. LightGBM n'est pas qu'une question de précision. ** La vitesse d'apprentissage est rapide sans aucun problème **. Par conséquent, je pense que c'est un gros avantage que le nombre d'essais puisse être augmenté par rapport à d'autres modèles tels que l'ajustement des paramètres.

Ce n'est pas que les méthodes autres que LightGBM soient mauvaises. Par exemple, la base de règles 1 a un pourcentage élevé de réponses correctes en mineur, et nous avons constaté que le code le plus fréquent est utile pour classer les mineurs. La règle de base 2 a confirmé la validité de mon hypothèse. Dans la régression logistique, il a été constaté que la variation du taux de réponse correcte pour chaque classe était faible. Il y a encore de la place pour le réglage des paramètres sur la machine à vecteurs de support, de sorte que le taux de réponse correct peut augmenter en fonction du réglage. La dernière prédiction en deux étapes avec une conception de problème différente peut donner de bons résultats selon le modèle et les paramètres.

Donc, pour le moment, j'aimerais utiliser LightGBM, qui peut facilement produire une grande précision, mais je prendrai le temps d'ajuster les paramètres. À ce moment-là, j'écrirai à nouveau l'article. C'était une phrase enfantine, mais merci de l'avoir lue.

Recommended Posts