L'autre jour, j'ai acheté un livre intitulé "Utilisation sur le terrain! Introduction au développement TensorFlow". C'était un livre très facile à comprendre et expliquait un large éventail de la classification d'images au GAN. Le script est un peu ancien et écrit en TensorFlow 1.x, il y a donc de nombreux endroits où il ne fonctionne pas avec l'actuel TensorFlow 2.x, Quant à Keras, il est presque compatible, il peut donc être utilisé presque sans problème après le chapitre 4.
Maintenant que j'ai réellement lu et étudié un livre, je voulais faire une sorte de sortie. Mais ce n'est pas amusant de faire la même chose. Donc, cette fois, je vais utiliser Keras pour faire l'analyse vocale qui m'intéresse personnellement.
Je voulais faire quelque chose comme devenir-yukarin qui transforme ma voix, mais d'abord C'est un peu trop difficile à faire, donc cette fois en guise d'introduction "** Identifions le doubleur à partir de la voix **" J'ai décidé du thème.
Étant donné que la plupart des livres ci-dessus étaient des analyses d'images, j'ai décidé de les mettre en œuvre dans le flux de conversion audio en images et d'apprentissage. Je n'ai jamais appris l'analyse vocale auparavant, donc je pense qu'il y a quelque chose d'étrange à ce sujet. Dans ce cas, veuillez commenter.
Comme je l'ai écrit ci-dessus, cette fois, je prendrai l'approche de la conversion en image (= données bidimensionnelles). Par conséquent, implémentons-le dans le flux suivant.
Cette fois, nous utiliserons l'ensemble de données du site Japan Voice Actor Statistics Society.
Il s'agit d'un ensemble de données dans lequel trois acteurs de la voix (annonceurs), Maki Tsuchiya, Ayako Uemura et Tomonatsu Fujito, ont enregistré 100 lignes dans une pièce silencieuse avec les trois émotions «normal», «joie» et «colère». Il y a un total de 900 données avec 100 lignes x 3 personnes x 3 émotions. (Depuis que je lis Wikipédia à haute voix, quand j'entends exprimer des sentiments de joie et de colère avec des lignes dénuées de sens, je me sens bien en tant qu'acteur.)
Cette fois, faisons un modèle qui peut distinguer ces trois personnes. Les données sont «* .tar.gz», mais elles peuvent être décompressées sous Windows en utilisant un logiciel de décompression.
Convertissons maintenant les données en quelque chose appelé MFCC (Mel Frequency Keptram Coefficient).
Si vous expliquez MFCC en détail, ce sera long, donc je ne l'expliquerai que comme "quantité de fonctionnalités vocales".
Pour convertir des données audio (wav) en une valeur numérique appelée MFCC, utilisez quelque chose appelé librosa
.
pip install librosa
Peut être installé avec.
Tout d'abord, lisons les données.
Par exemple, chargez fujitou_normal_001.wav
.
from matplotlib import pyplot as plt
import librosa
import librosa.display
import os
#Ecrire le chemin des données
WAV_DATA_PATH = os.path.join("Dataset","fujitou_normal","fujitou_normal_001.wav")
x, fs = librosa.load(WAV_DATA_PATH, sr=44100)
librosa.display.waveplot(x, sr=fs, color='blue');
La valeur de retour x de librosa.load
est les données (le format est numpy
), et fs
est le taux d'échantillonnage.
Vous pouvez maintenant importer des données Wav.
Convertissons ces données en MFCC.
mfccs = librosa.feature.mfcc(x, sr=fs)
librosa.display.specshow(mfccs, sr=fs, x_axis='time')
plt.colorbar();
Pour MFCC, l'axe horizontal est le temps et l'axe vertical est la valeur à 20 dimensions.
print(mfccs.shape) # -> (20, 630)
Excluons les données de la première dimension (en bas du graphique). Je n'expliquerai pas cela en détail non plus, mais comme vous pouvez le voir sur le graphique, la plage de données est devenue importante en raison des données de la première dimension, ce qui rend difficile la compréhension des caractéristiques des valeurs au-dessus de la première dimension. ..
mfccs = mfccs[1:]
librosa.display.specshow(mfccs, sr=fs, x_axis='time')
plt.colorbar();
De plus, comme vous pouvez le voir à partir des données wav, il n'y a presque pas de son après 7,5 secondes. Cette zone est également un obstacle, alors coupons-la.
import numpy as np
def cut_silence(wavdata, eps=0.01):
st = 0
gl = len(wavdata)
data = np.abs(wavdata)
threshold = np.max(data) * eps
for i,a in enumerate(data):
if a > threshold:
st = i - 1
break
for i,a in reversed(list(enumerate(data))):
if a > threshold:
gl = i
break
return wavdata[st:gl]
Cette fois, les valeurs «de 0 seconde à la valeur maximale des données x 0,01 ou plus» et «de la fin à la valeur maximale des données x 0,01 ou plus» ont été coupées.
x = cut_silence(x)
librosa.display.waveplot(x, sr=fs, color='blue');
Cela donne au graphique l'aspect suivant:
La conversion en mfccs donne à nouveau:
mfccs = librosa.feature.mfcc(x, sr=fs)
mfccs = mfccs[1:]
librosa.display.specshow(mfccs, sr=fs, x_axis='time')
plt.colorbar();
Avec cela, il était possible de créer des données d'image.
Comme MFCC est un processus qui prend beaucoup de temps, je pense qu'il est plus facile de le sauvegarder en tant que données numpy (* .npy) après avoir utilisé librosa.feature.mfcc
.
Convertissons toutes les données et sauvegardons-les dans des données numpy.
La structure de répertoires suivante est supposée.
.
|-Dataset
| |-fujitou_angry
| | |-fujitou_angry_001.wav
| | |-fujitou_angry_002.wav
| | |-fujitou_angry_003.wav
| | |-...
| |
| |-fujitou_happy
| | |-fujitou_happy_001.wav
| | |-...
| |
| |-...
|
|-ImageData
| |-fujitou_angry
| | |-fujitou_angry_001.npy
| | |-fujitou_angry_002.npy
| | |-fujitou_angry_003.npy
| | |-...
| |
| |-fujitou_happy
| | |-fujitou_happy_001.npy
| | |-...
| |
| |-...
|
Tout d'abord, utilisez ʻos.listdir` pour obtenir le chemin de toutes les données.
import os
import random
DATASET_DIR="Dataset"
wavdatas = []
dirlist = os.listdir(DATASET_DIR)
for d in dirlist:
d = os.path.join(DATASET_DIR, d)
datalist = os.listdir(d)
y = [d[d.find("\\")+1:d.find("_")], d[d.find("_") + 1:]] #Détermination des données de réponse correctes à partir du nom de fichier
datalist = [[os.path.join(d,x), y] for x in datalist]
wavdatas.extend(datalist)
Ensuite, créez un répertoire pour placer des données numpy.
IMAGE_DATA = "ImageData"
dirlist = os.listdir(DATASET_DIR)
for d in dirlist:
os.makedirs(os.path.join(IMAGE_DATA, d), exist_ok=True)
Puis convertissez toutes les données et disons np.save
.
def get_mfcc(datadir):
x, fs = librosa.load(datadir, sr=44100)
x = cut_silence(x)
mfccs = librosa.feature.mfcc(x, sr=fs)
mfccs = mfccs[1:]
return mfccs, x, fs
nn = len(wavdatas)
for i, data in enumerate(wavdatas):
path_list = data[0].split("\\")
path_list[0] = IMAGE_DATA
path_list[2] = path_list[2].replace(".wav", ".npy")
image_path = "\\".join(path_list)
mfcc,x,fs = get_mfcc(data[0])
if i%10 == 0:
print(i, "/", nn)
np.save(image_path, mfcc)
Vous devriez maintenant avoir des données, comme indiqué dans l'image ci-dessous.
Avec cela, les données peuvent être transformées en données bidimensionnelles. Ensuite, formatez les données bidimensionnelles afin qu'elles soient faciles à manipuler.
Commençons par charger les données numpy enregistrées ci-dessus.
IMAGE_DATA = "ImageData"
numpy_datas = []
dirlist = os.listdir(IMAGE_DATA)
for d in dirlist:
d = os.path.join(IMAGE_DATA, d)
datalist = os.listdir(d)
datalist = [[np.load(os.path.join(d,x)), os.path.join(d,x)] for x in datalist]
numpy_datas.extend(datalist)
Premièrement, les données ci-dessus sont comprises entre -200 et 100. Normalisons cela dans la plage 0 ~ 1.
#Obtenez les valeurs maximales et minimales pour l'ensemble des données
data = numpy_datas[0][0]
maximum = np.max(data)
minimum = np.min(data)
for i, data in enumerate(numpy_datas):
M = np.max(data[0])
m = np.min(data[0])
if maximum < M:
maximum = M
if minimum > m:
minimum = m
# 0~Dans la gamme de 1
normalize = lambda x: (x - minimum)/(maximum - minimum)
for i, data in enumerate(numpy_datas):
numpy_datas[i][0] = normalize(data[0])
Les données créées dans la section 2 sont de 19 $ \ fois T $. Ici, $ T $ est le temps (pour être exact, secondes x taux d'échantillonnage). Pour simplement plonger dans un réseau de neurones, il est plus facile de comprendre si toutes les tailles de données sont identiques.
from PIL import Image
import numpy as np
img_datas = []
for i,data in enumerate(numpy_datas):
imgdata = Image.fromarray(data[0])
imgdata = imgdata.resize((512,19))
numpy_datas[i][0] = np.array(imgdata)
Ici, tous ont été convertis en données 512 × 19.
Sauvegardez les données comme dans la section 2.
Commencez par créer un répertoire.
NORMALIZE_DATA = "NormalizeData"
dirlist = os.listdir(DATASET_DIR)
for d in dirlist:
os.makedirs(os.path.join(NORMALIZE_DATA, d), exist_ok=True)
Et enregistrer.
for i, data in enumerate(numpy_datas):
path_list = data[1].split("\\")
path_list[0] = NORMALIZE_DATA
image_path = "\\".join(path_list)
np.save(image_path, data[0])
Apprenons avec l'apprentissage en profondeur
Divisons 900 données en données d'entraînement, données de test et données de vérification.
import numpy as np
import random, os
NORMALIZE_DATA="NormalizeData"
N_TRAIN = 0.8
N_TEST = 0.1
N_VALID = 0.1
train_data = []
test_data = []
valid_data = []
dirlist = os.listdir(NORMALIZE_DATA)
for d in dirlist:
d = os.path.join(NORMALIZE_DATA, d)
datalist = os.listdir(d)
y = [d[d.find("\\")+1:d.find("_")], d[d.find("_") + 1:]] #Détermination des données de réponse correctes à partir du nom de fichier
datalist = [[np.load(os.path.join(d,x)), y, os.path.join(d,x)] for x in datalist]
random.shuffle(datalist)
train_data.extend(datalist[:int(len(datalist)*N_TRAIN)])
test_data.extend(datalist[int(len(datalist)*N_TRAIN): int(len(datalist)*N_TRAIN) + int(len(datalist)*N_TEST)])
valid_data.extend(datalist[int(len(datalist)*N_TRAIN) + int(len(datalist)*N_TEST): ])
random.shuffle(train_data)
random.shuffle(test_data)
random.shuffle(valid_data)
Toutes les données ont été remplacées par des données d'entraînement: données de test: données de vérification = 0,8: 0,1: 0,1. Puisque 900 données sont utilisées, il est de 720: 90: 90. Il est également mélangé deux fois pour une bonne distribution. Après avoir récupéré les données dans un répertoire, elles sont mélangées une fois, puis à nouveau mélangées après avoir rejoint.
--Données d'entrée: train_datas [i] [0]
train_datas [i] [1]
--Nom de l'acteur vocal: train_datas [i] [1] [0]
--Emotions: train_datas [i] [1] [1]
Il a une structure de données comme celle-ci.
Avant d'apprendre avec les keras de tensorflow, convertissons les données d'entrée et de réponse correcte pour une facilité d'utilisation.
Cette fois, train_datas [i] [1] [0]
est la réponse correcte pour classer les noms des acteurs vocaux.
#Convertir en numpy
input_data_train = np.array([train_data[i][0] for i in range(len(train_data))])
input_data_test = np.array([test_data[i][0] for i in range(len(test_data))])
input_data_valid = np.array([valid_data[i][0] for i in range(len(valid_data))])
#Répertorier les bonnes réponses
label_data_train = [train_data[i][1][0] for i in range(len(train_data))]
label_data_test = [test_data[i][1][0] for i in range(len(test_data))]
label_data_valid = [valid_data[i][1][0] for i in range(len(valid_data))]
Modifions les données de réponse correctes en 1-hot.
from tensorflow.keras.utils import to_categorical
label_dict={"tsuchiya": 0, "fujitou":1, "uemura":2}
label_no_data_train = np.array([label_dict[label] for label in label_data_train])
label_no_data_test = np.array([label_dict[label] for label in label_data_test])
label_no_data_valid = np.array([label_dict[label] for label in label_data_valid])
label_no_data_train = to_categorical(label_no_data_train, 3)
label_no_data_test = to_categorical(label_no_data_test, 3)
label_no_data_valid = to_categorical(label_no_data_valid, 3)
Construisons maintenant un modèle pour le réseau neuronal. Je ne connais rien de tel que les règles de fer, alors j'ai décidé de répéter l'opération de convolution et de normalisation pour le moment. La fonction d'activation utilise relu.
from tensorflow.keras.layers import Input, Conv2D, Conv2DTranspose,\
BatchNormalization, Dense, Activation,\
Flatten, Reshape, Dropout
from tensorflow.keras.models import Model
inputs = Input((19,512))
x = Reshape((19,512,1), input_shape=(19,512))(inputs)
x = Conv2D(12, (1,4), strides=(1,2), padding="same")(x)
x = BatchNormalization()(x)
x = Activation("relu")(x)
x = Conv2D(12, (1,4), strides=(1,2), padding="same")(x)
x = BatchNormalization()(x)
x = Activation("relu")(x)
x = Conv2D(12, (2,2), strides=(1,1), padding="same")(x)
x = BatchNormalization()(x)
x = Activation("relu")(x)
x = Conv2D(12, (2,2), strides=(1,1), padding="same")(x)
x = BatchNormalization()(x)
x = Activation("relu")(x)
x = Flatten()(x)
x = Dense(3)(x)
output = Activation("softmax")(x)
model = Model(inputs=inputs, outputs=output)
model.summary()
Le résultat de model.summary () est le suivant.
Model: "functional_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) [(None, 19, 512)] 0
_________________________________________________________________
reshape (Reshape) (None, 19, 512, 1) 0
_________________________________________________________________
conv2d (Conv2D) (None, 19, 256, 12) 60
_________________________________________________________________
batch_normalization (BatchNo (None, 19, 256, 12) 48
_________________________________________________________________
activation (Activation) (None, 19, 256, 12) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 19, 128, 12) 588
_________________________________________________________________
batch_normalization_1 (Batch (None, 19, 128, 12) 48
_________________________________________________________________
activation_1 (Activation) (None, 19, 128, 12) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 19, 128, 12) 588
_________________________________________________________________
batch_normalization_2 (Batch (None, 19, 128, 12) 48
_________________________________________________________________
activation_2 (Activation) (None, 19, 128, 12) 0
_________________________________________________________________
conv2d_3 (Conv2D) (None, 19, 128, 12) 588
_________________________________________________________________
batch_normalization_3 (Batch (None, 19, 128, 12) 48
_________________________________________________________________
activation_3 (Activation) (None, 19, 128, 12) 0
_________________________________________________________________
flatten (Flatten) (None, 29184) 0
_________________________________________________________________
dense (Dense) (None, 3) 87555
_________________________________________________________________
activation_4 (Activation) (None, 3) 0
=================================================================
Total params: 89,571
Trainable params: 89,475
Non-trainable params: 96
_________________________________________________________________
Laissez-les apprendre.
```python
model.compile(
optimizer="adam",
loss="categorical_crossentropy"
)
model.fit(
input_data_train,
label_no_data_train,
batch_size=30,
epochs=50,
validation_data=(input_data_valid, label_no_data_valid)
)
Enfin, faisons une prédiction en utilisant les données de test que nous avons créées en premier.
out = model.predict(input_data_test)
predict = np.argmax(out, axis=1)
answer =np.argmax(label_no_data_test, axis=1)
print("correct:", np.sum(predict == answer), "/", len(predict))
print("rate:", np.sum(predict == answer)/len(predict) * 100, "%")
correct: 90 / 90
rate: 100.0 %
J'ai pu répondre correctement à toutes les questions. Génial.
Je l'ai essayé plusieurs fois, mais c'est environ 90%La précision ci-dessus est sortie. Si vous réglez un peu plus le modèle, ce sera certainement 100%Je pense que je peux y arriver.
Alors, essayons de classer non seulement le nom du doubleur mais aussi l'émotion.
Republier
-Des données d'entrée:
train_datas[i][0]
-Corriger les données de réponse:train_datas[i][1]
-Nom du doubleur:train_datas[i][1][0]
-Sentiments:train_datas[i][1][1]
Detrain_datas[i][1][1]
Essayons de donner des réponses correctes.
Le résultat est,
correct: 88 / 90
rate: 97.77777777777777 %
Et le même résultat a été obtenu.
#Résumé Cette fois, en classant les acteurs de la voix à partir de la voix, nous avons converti la voix en image et utilisé un réseau de neurones utilisant une couche de convolution. Le résultat est 90%Sur ce, je pense que je l'ai bien dit.
À l'avenir, j'aimerais pouvoir distinguer les voix de Yui Ogura en utilisant les voix de l'anime et de la radio.
Recommended Posts