[PYTHON] En utilisant le RNN avec état de Keras, j'ai essayé de voir s'il pouvait s'agir d'une chanson auto-exploitée basée sur le fichier wav.

Bonjour tous le monde

Google a créé un apprenant de chanson autonome appelé magenta, afin que vous puissiez créer autant de chansons que vous le souhaitez! J'ai pensé, mais ce n'était pas vraiment si simple. Le fichier source d'apprentissage semble être midi, et il semble qu'il n'est pas possible d'apprendre en insérant un fichier enregistré.

C'est pourquoi j'ai essayé diverses choses parce que je ne pouvais pas créer ma propre machine d'apprentissage de chansons autonome qui puisse être créée avec wav. Les derniers articles sont des expériences pour cette fois.

TL;DR

Le référentiel utilise les éléments suivants https://github.com/niisan-tokyo/music_generator

Les fichiers suivants sont utilisés pour l'apprentissage réel. https://github.com/niisan-tokyo/music_generator/blob/master/src/stateful_learn.py

Motivation

Google a publié magenta, un outil de chanson autonome qui utilise le tensorflow. https://magenta.tensorflow.org/ C'est un excellent outil, mais je ne le connais pas un peu car le fichier cible est midi. (Il y en avait beaucoup, mais de nos jours, il y a beaucoup de mp3 ...)

Pour le moment, afin de gérer facilement le fichier enregistré, il semble bon d'utiliser les données de la forme d'onde elle-même comme wav. De plus, python peut gérer wav de manière native, j'ai donc pensé que je devais essayer ceci.

plan

J'ai pensé à utiliser les données brutes de forme d'onde telles quelles pour l'apprentissage, mais c'était totalement inutile, alors j'ai pensé au plan suivant.

En d'autres termes, je pensais que je pourrais faire quelque chose comme ça generator.png Une fois que vous avez une distribution de fréquence de série chronologique, vous pouvez faire de la musique en la transformant inversement en Fourier.

Transformée de Fourier

Dans l'article précédent, le fichier wav renvoyé par la conversion inverse pour la conversion de Fourier à 256 images était audible sans aucun problème. Je pense que cela suffit en tant que quantité de fonctionnalités qui exprime le son lui-même. http://qiita.com/niisan-tokyo/items/764acfeec77d8092eb73

La transformée de Fourier rapide (FFT) avec la bibliothèque numpy vous donnera 256 distributions de fréquences, par exemple dans un intervalle de 256 images. Normalement, la fréquence d'images (images par seconde) d'un fichier audio est de 44100, vous pouvez donc obtenir une distribution de fréquence toutes les 256/44100 = 5,8 (msec). L'idée est que si nous pouvons créer une machine d'apprentissage qui peut générer automatiquement cette transition de temps toutes les 5,8 msec, la musique sera créée automatiquement.

stateful RNN RNN est un réseau neuronal récurrent, qui est un type de réseau qui gère les états continus en se référant au contenu précédemment calculé lors de l'obtention de la sortie de l'entrée. http://qiita.com/kiminaka/items/87afd4a433dc655d8cfd

Lorsqu'il s'agit de RNN dans Keras, il prend généralement plusieurs états consécutifs comme entrées et crée une sortie, mais à chaque entrée, l'état précédent est réinitialisé. Le RNN avec état effectue le traitement suivant tout en conservant l'état après ce traitement précédent. On s'attend à ce que cela permette un traitement en série complexe à intervalles irréguliers.

stateful_rnn.png Cette fois, j'ai pensé qu'il serait possible de créer un générateur avec un flux de chansons en apprenant séquentiellement l'entrée comme la distribution de fréquence au moment actuel et la sortie comme la distribution de fréquence au moment suivant avec RNN avec état.

Préparation à l'apprentissage

Préparation du dossier

Pour les fichiers m4a et mp3, vous pouvez utiliser ffmpeg pour les convertir en wav. http://qiita.com/niisan-tokyo/items/135824905e4a3021d358 J'enregistre ma musique de jeu préférée sur mac et je la recrache en wav.

Créer un jeu de données

Étant donné que l'ensemble de données est créé par transformation de Fourier en wav, vous pouvez essentiellement vous référer au code, mais il y a quelques mises en garde.

def create_test_data(left, right):
    arr = []
    for i in range(0, len(right)-1):
        #Vectorisation des nombres complexes
        temp = np.array([])
        temp = np.append(temp, left[i].real)
        temp = np.append(temp, left[i].imag)
        temp = np.append(temp, right[i].real)
        temp = np.append(temp, right[i].imag)
        arr.append(temp)

    return np.array(arr)

C'est la partie qui crée des données d'entrée en combinant les distributions de fréquence de la source sonore stéréo qui a subi une transformée de Fourier. Ici, les parties réelle et imaginaire de la distribution de fréquence représentée par des nombres complexes sont réinsérées dans des éléments séparés du vecteur. En effet, si vous essayez de calculer avec un nombre complexe, la partie imaginaire sera supprimée. En conséquence, le nombre d'échantillons dans une section est de 256 images, mais la dimension d'entrée réelle est de 1024.

apprendre

Créer un modèle

Le modèle est aussi simple que de connecter trois LSTM et d'insérer enfin la couche entièrement connectée.

model = Sequential()
model.add(LSTM(256,
              input_shape=(1, dims),
              batch_size=samples,
              return_sequences=True,
              activation='tanh',
              stateful=True))
model.add(Dropout(0.5))
model.add(LSTM(256, stateful=True, return_sequences=True, activation='tanh'))
model.add(Dropout(0.3))
model.add(LSTM(256, stateful=True, return_sequences=False, activation='tanh'))
model.add(Dropout(0.2))
model.add(Dense(dims))
model.compile(loss='mse', optimizer='adam')

Maintenant, il existe certaines conditions lors de l'utilisation d'un RNN avec état. Tout d'abord, vous devez spécifier la dimension d'entrée par lot. Ensuite, chaque échantillon du lot précédent et chaque échantillon du lot suivant doivent être continus en série. À titre d'exemple spécifique, s'il existe un premier lot «X_1» et un deuxième lot «X_2», il existe une relation entre le i-ème échantillon $ X_1 [i] $ et $ X_2 [i] $ des deux. Cela signifie qu'il doit y en avoir. Cette fois, nous supposons que le générateur est composé de $ x_ {n + 1} = RNN (x_n) $, et créons le prochain ensemble du même nombre d'états à partir de plusieurs états consécutifs.

model.png En d'autres termes, $ X_2 [i] $ est toujours en avance sur $ X_1 [i] $ par le nombre d'échantillons. Je ne pense pas que ce soit un peu trop bâclé, mais je pense que c'est une machine qui répète le nombre d'échantillons, c'est-à-dire 32 pièces chacune pour créer l'état suivant.

raccord

Maintenant que vous êtes prêt, commençons à apprendre.

for num in range(0, epochs):
    print(num + 1, '/', epochs, ' start')
    for one_data in test:
        in_data = one_data[:-samples]
        out_data = np.reshape(one_data[samples:], (batch_num, dims))
        model.fit(in_data, out_data, epochs=1, shuffle=False, batch_size=samples)

        model.reset_states()
    print(num+1, '/', epochs, ' epoch is done!')

model.save('/data/model/mcreator')

Comme l'ordre des lots est important lors de l'apprentissage, j'essaie de ne pas mélanger les échantillons sur le lot. De plus, l'apprentissage est effectué pour chaque wav, et l'état interne est réinitialisé après un apprentissage une fois.

résultat

Résultat d'apprentissage

Tout d'abord, quand j'essaie de l'apprendre, il semble que l'appareillage progresse d'une manière ou d'une autre.

1 / 10  start
Epoch 1/1
16384/16384 [==============================] - 87s - loss: 1.9879e-04
Epoch 1/1
16384/16384 [==============================] - 84s - loss: 1.9823e-04
Epoch 1/1
16384/16384 [==============================] - 75s - loss: 1.1921e-04
Epoch 1/1
16384/16384 [==============================] - 82s - loss: 2.3389e-04
Epoch 1/1
16384/16384 [==============================] - 80s - loss: 3.7428e-04
Epoch 1/1
16384/16384 [==============================] - 90s - loss: 3.3968e-04
Epoch 1/1
16384/16384 [==============================] - 87s - loss: 5.0188e-04
Epoch 1/1
16384/16384 [==============================] - 76s - loss: 4.9725e-04
Epoch 1/1
16384/16384 [==============================] - 74s - loss: 3.7447e-04
Epoch 1/1
16384/16384 [==============================] - 87s - loss: 4.1855e-04
1 / 10  epoch is done!
2 / 10  start
Epoch 1/1
16384/16384 [==============================] - 82s - loss: 1.9742e-04
Epoch 1/1
16384/16384 [==============================] - 85s - loss: 1.9718e-04
Epoch 1/1
16384/16384 [==============================] - 90s - loss: 1.1876e-04
Epoch 1/1
16384/16384 [==============================] - 104s - loss: 2.3144e-04
Epoch 1/1
16384/16384 [==============================] - 97s - loss: 3.7368e-04
Epoch 1/1
16384/16384 [==============================] - 78s - loss: 3.3906e-04
Epoch 1/1
16384/16384 [==============================] - 87s - loss: 5.0128e-04
Epoch 1/1
16384/16384 [==============================] - 79s - loss: 4.9627e-04
Epoch 1/1
16384/16384 [==============================] - 82s - loss: 3.7420e-04
Epoch 1/1
16384/16384 [==============================] - 90s - loss: 4.1857e-04
2 / 10  epoch is done!
...

Faisons un son en utilisant le modèle terminé.

Génération sonore

Je laisserai le code détaillé à stateful_use.py dans le référentiel, donc si vous n'écrivez que le flux général,

  1. Chargez le modèle généré
  2. Lisez le fichier de musique de départ
  3. Créer des données de séries chronologiques en prédisant le modèle dans une certaine mesure à l'aide de semences,
  4. Créez séquentiellement l'état suivant à partir des données de séries chronologiques générées par vous-même
  5. Après avoir généré des données de série chronologique d'une certaine longueur, créez les données de forme d'onde d'origine à partir de la transformation de Fourier inverse basée sur celles-ci.

La partie générateur est la suivante

#Transformée de Fourier du fichier de départ
Kl = fourier(left, N, samples * steps)
Kr = fourier(right, N, samples * steps)
sample = create_test_data(Kl, Kr)
sample = np.reshape(sample, (samples * steps, 4 * N))
music = []

#Entrez les données de départ dans le modèle=>"Favoriser" l'État
for i in range(steps):
    in_data = np.reshape(sample[i * samples:(i + 1) * samples], (samples, 1, 4 * N))
    model.predict(np.reshape(in_data, (samples, 1, 4 * N)))

#Auto-générer de la musique en substituant séquentiellement les dernières données de sortie dans le modèle dont l'état a été changé avec les données de départ
for i in range(0, frames):
    if i % 50 == 0:
        print('progress: ', i, '/', frames)

    music_data = model.predict(np.reshape(in_data, (samples, 1, 4 * N)))
    music.append(np.reshape(music_data, (samples, 4 * N)))
    in_data = music_data

music = np.array(music)

Les données ainsi obtenues sont transformées inversement de Fourier, converties en espace réel, puis écrites en wav. En regardant la forme d'onde dans l'espace réel, c'est comme suit.

スクリーンショット 2017-06-14 18.11.11.png Un peu plus longue durée スクリーンショット 2017-06-14 18.11.22.png Quand j'écoute ça avec wav, c'est dans un état surréaliste où un son de buzzer avec un ton constant de "boo" est joué tout le temps. .. ..

Loin de la musique, il est devenu une machine mystérieuse qui ne produit qu'un son constant. Quel que soit le type de fichier musical que vous insérez, il produira le même son.

Considération

Je pense que la raison pour laquelle cela n'a pas fonctionné était que la transition du son était si intense que l'apprentissage s'est déroulé dans le sens de la prise de la constante qui minimisait l'erreur. C'est peut-être pourquoi la fluctuation d'erreur est trop faible malgré le système compliqué. Si vous essayez de le faire sans état, vous ne savez pas combien de séquences vous devez prendre, et le nombre de dimensions augmente par le nombre de séquences, ce qui rend difficile l'apprentissage facile. Ou il se peut que le nombre d’apprentissage soit trop petit, mais comme la perte est déjà très faible, cela peut être une idée différente.

Quoi qu'il en soit, nous devons nous améliorer davantage.

Résumé

Si vous pouviez créer une chanson si facilement, ce ne serait pas aussi simple. Cela n'a pas très bien fonctionné, mais je pense que j'ai appris dans une certaine mesure à utiliser python, en particulier la signification de numpy, donc je pense que c'est un bon point.

De plus, je m'en fiche, mais j'ai fini par étudier beaucoup le remodelage de Numpy.

Cette fois est un tel endroit

Postscript

2017/06/18 Les modifications suivantes ont été apportées.

résultat

J'ai la forme d'onde suivante. スクリーンショット 2017-06-18 8.48.25.png スクリーンショット 2017-06-18 8.48.35.png Le bruit de fond est resté comme d'habitude, mais maintenant on dirait qu'il sculpte un certain rythme. De plus, la distribution de fréquence obtenue (la partie réelle) est la suivante. La carte de distribution pour 10 pièces est recouverte de 5 cadres chacune.

N = 256, nombre de neurones LSTM = 256 スクリーンショット 2017-06-18 8.54.15.png N = 1024, nombre de neurones LSTM = 512 スクリーンショット 2017-06-18 8.52.57.png Lorsque N = 1024 et que le nombre de neurones est de 256, c'est la même chose que lorsque N = 256.

Considération

Lorsque le facteur à appliquer a été modifié lors de la transformation de Fourier, la perte a naturellement augmenté. Puisque mse est utilisé pour la perte, la modification du facteur augmente la perte du carré. Cela permet d'observer les changements dans les composants plus petits, ce qui peut avoir une précision améliorée. De plus, en augmentant le nombre de neurones, le pouvoir d'expression a augmenté.

A titre d'amélioration, il est possible d'augmenter le facteur appliqué lors de la transformation de Fourier ou d'augmenter encore le nombre de neurones. Étant donné que le nombre d'époques est de 10 et que le nombre de chansons originales est de 9, il est correct de changer cela, mais comme le changement de perte est faible, je ne pouvais pas voir l'effet de ne pas être bon, alors j'ai pensé que c'était réservé pour le moment.

Recommended Posts

En utilisant le RNN avec état de Keras, j'ai essayé de voir s'il pouvait s'agir d'une chanson auto-exploitée basée sur le fichier wav.
J'ai essayé de synthétiser des fichiers WAV en utilisant Pydub.
J'ai essayé d'utiliser "Syncthing" pour synchroniser des fichiers sur plusieurs PC
J'ai essayé d'utiliser l'API à distance avec GAE / J
[Pythonocc] J'ai essayé d'utiliser la CAO sur un notebook Jupyter
J'ai essayé d'utiliser PySpark de Jupyter 4.x sur EMR
J'ai essayé d'utiliser paramétré
J'ai essayé d'utiliser argparse
J'ai essayé d'utiliser la mimesis
J'ai essayé d'utiliser anytree
J'ai essayé d'utiliser aiomysql
J'ai essayé d'utiliser Summpy
J'ai essayé d'utiliser coturn
J'ai essayé d'utiliser Pipenv
J'ai essayé d'utiliser matplotlib
J'ai essayé d'utiliser "Anvil".
J'ai essayé d'utiliser Hubot
J'ai essayé d'utiliser ESPCN
J'ai essayé d'utiliser openpyxl
J'ai essayé d'utiliser Ipython
J'ai essayé d'utiliser PyCaret
J'ai essayé d'utiliser cron
J'ai essayé d'utiliser ngrok
J'ai essayé d'utiliser face_recognition
J'ai essayé d'utiliser Jupyter
J'ai essayé d'utiliser doctest
J'ai essayé d'utiliser du folium
J'ai essayé d'utiliser jinja2
J'ai essayé d'utiliser du folium
J'ai essayé d'utiliser la fenêtre de temps
[Avec image] J'ai essayé d'utiliser neofetch sur différents OS!
J'ai essayé d'utiliser des données PDF de soins médicaux en ligne basés sur la propagation d'une nouvelle infection à coronavirus
[J'ai essayé d'utiliser Pythonista 3] Introduction
J'ai essayé d'utiliser easydict (mémo).
J'ai essayé la reconnaissance faciale avec Face ++
J'ai essayé d'utiliser RandomForest
J'ai essayé d'utiliser BigQuery ML
J'ai essayé d'utiliser Amazon Glacier
J'ai essayé d'utiliser git inspector
J'ai essayé d'utiliser magenta / TensorFlow
J'ai essayé MLflow sur Databricks
J'ai essayé d'utiliser AWS Chalice
J'ai essayé d'utiliser l'émojinateur Slack
J'ai essayé la validation croisée basée sur le résultat de la recherche de grille avec scikit-learn
J'ai essayé d'utiliser l'API COTOHA (il y a aussi du code sur GitHub)
J'ai essayé de numériser le tampon estampé sur papier en utilisant OpenCV
J'ai essayé de visualiser les données BigQuery à l'aide de Jupyter Lab avec GCP
J'ai essayé de jouer à un jeu Windows en utilisant Steam Play sur Ubuntu 20.04 LTS