[PYTHON] Sprachverarbeitung durch tiefes Lernen: Lassen Sie uns anhand der Stimme herausfinden, wer der Sprecher ist

Vorwort

Neulich habe ich ein Buch mit dem Titel "Use in the field! Einführung in die TensorFlow-Entwicklung" gekauft. Es war ein sehr leicht verständliches Buch und erklärte ein breites Spektrum von der Bildklassifizierung bis zur GAN. Das Skript ist etwas alt und in TensorFlow 1.x geschrieben. Es gibt also viele Stellen, an denen es mit dem aktuellen TensorFlow 2.x nicht funktioniert. Keras ist nahezu kompatibel, sodass es nach Kapitel 4 nahezu problemlos bedient werden kann.

Nachdem ich tatsächlich ein Buch gelesen und studiert habe, wollte ich eine Art Ausgabe machen. Aber es macht keinen Spaß, dasselbe zu tun. Dieses Mal werde ich Keras verwenden, um die Sprachanalyse durchzuführen, an der ich persönlich interessiert bin.

Ich wollte so etwas wie Werden-Yukarin machen, das meine Stimme verwandelt, aber zuerst Es ist etwas zu schwierig, also diesmal als Einführung "** Lassen Sie uns anhand der Stimme herausfinden, wer der Synchronsprecher ist **" Ich habe mich für das Thema entschieden.

Da es sich bei den meisten der oben genannten Bücher um Bildanalysen handelte, habe ich mich entschlossen, sie in den Fluss der Umwandlung von Audio in Bilder und des Lernens zu implementieren. Ich habe noch nie etwas über Sprachanalyse gelernt, daher denke ich, dass etwas Seltsames daran ist. In diesem Fall bitte kommentieren.

Umgebung

Gliederung

Wie ich oben geschrieben habe, werde ich diesmal den Ansatz der Konvertierung in ein Bild (= zweidimensionale Daten) verfolgen. Lassen Sie es uns daher im folgenden Ablauf implementieren.

  1. Sammeln Sie Daten
  2. Konvertieren Sie Audio in MFCC
  1. Bilddaten formatieren und anpassen
  2. Erstellen und trainieren Sie ein Modell

Implementierung

1. Sammeln Sie Daten

Dieses Mal verwenden wir den Datensatz der Website Japan Voice Actor Statistics Society.

Dies ist ein Datensatz, in dem drei Sprecher (Ansager), Maki Tsuchiya, Ayako Uemura und Tomonatsu Fujito, 100 Zeilen in einem stillen Raum mit den drei Emotionen "normal", "Freude" und "Wut" aufzeichneten. Es gibt insgesamt 900 Daten mit 100 Zeilen x 3 Personen x 3 Emotionen. (Weil ich Wikipedia vorlese, wenn ich höre, wie ich Gefühle der Freude und des Zorns mit bedeutungslosen Linien ausdrücke, fühle ich mich als Synchronsprecher erstaunlich)

Lassen Sie uns dieses Mal ein Modell erstellen, das diese drei Personen unterscheiden kann. Die Daten sind "* .tar.gz", können jedoch unter Windows mithilfe einer Dekomprimierungssoftware dekomprimiert werden.

2. Konvertieren Sie Audio in MFCC

Lassen Sie uns nun die Daten in etwas konvertieren, das als MFCC (Mel Frequency Keptram Coefficient) bezeichnet wird. Wenn Sie MFCC im Detail erklären, wird es lang sein, daher werde ich es nur als "Sprachfunktionsmenge" erklären. Verwenden Sie zum Konvertieren von Audiodaten (wav) in einen numerischen Wert namens MFCC den Namen "librosa".

pip install librosa

Kann mit installiert werden.

2.1 Daten lesen

Lassen Sie uns zuerst die Daten lesen. Laden Sie beispielsweise fujitou_normal_001.wav.

from matplotlib import pyplot as plt
import librosa
import librosa.display
import os

#Schreiben Sie den Datenpfad
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');

wavdata.png

Der Rückgabewert x von "librosa.load" sind die Daten (Format ist "numpy") und "fs" ist die Abtastrate. Jetzt können Sie Wav-Daten importieren.

2.2 Umstellung auf MFCC

Lassen Sie uns diese Daten in MFCC konvertieren.

mfccs = librosa.feature.mfcc(x, sr=fs)
librosa.display.specshow(mfccs, sr=fs, x_axis='time')
plt.colorbar();

mfcc.png

Bei MFCC ist die horizontale Achse die Zeit und die vertikale Achse der 20-dimensionale Wert.

print(mfccs.shape) # -> (20, 630)

2.3 Datenformung

Lassen Sie uns die Daten der ersten Dimension (am unteren Rand des Diagramms) ausschließen. Ich werde dies auch nicht im Detail erklären, aber wie Sie aus der Grafik sehen können, ist der Datenbereich aufgrund der Daten in der ersten Dimension groß geworden, was es schwierig macht, die Merkmale von Werten über der ersten Dimension zu erfassen. ..

mfccs = mfccs[1:]
librosa.display.specshow(mfccs, sr=fs, x_axis='time')
plt.colorbar();

mfcc_skip.png

Wie Sie aus den WAV-Daten ersehen können, ist nach 7,5 Sekunden fast kein Ton mehr zu hören. Dieser Bereich ist auch ein Hindernis, also lasst es uns abschneiden.

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]

Dieses Mal wurden die Werte "von 0 Sekunden bis zum Maximalwert der Daten x 0,01 oder mehr" und "vom Ende bis zum Maximalwert der Daten x 0,01 oder mehr" geschnitten.

x = cut_silence(x)
librosa.display.waveplot(x, sr=fs, color='blue');

Dadurch sieht das Diagramm folgendermaßen aus: wav_cut.png

Die erneute Konvertierung in mfccs ergibt:

mfccs = librosa.feature.mfcc(x, sr=fs)
mfccs = mfccs[1:]
librosa.display.specshow(mfccs, sr=fs, x_axis='time')
plt.colorbar();

mfcc_skip_cut.png

Damit war es möglich, Bilddaten zu erstellen.

2.4. Konvertieren und speichern Sie alle Daten

Da MFCC ein Prozess ist, der viel Zeit in Anspruch nimmt, ist es meiner Meinung nach einfacher, ihn nach Verwendung von librosa.feature.mfcc als numpy-Daten (* .npy) zu speichern. Konvertieren Sie alle Ihre Daten und speichern Sie sie in numpy Daten. Die folgende Verzeichnisstruktur wird angenommen.

.
|-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
|   |   |-...
|   |
|   |-...
|

Verwenden Sie zuerst os.listdir, um den Pfad aller Daten abzurufen.

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:]] #Ermittlung der richtigen Antwortdaten aus dem Dateinamen
    datalist = [[os.path.join(d,x), y] for x in datalist]
    wavdatas.extend(datalist)

Erstellen Sie als Nächstes ein Verzeichnis zum Platzieren von Numpy-Daten.

IMAGE_DATA = "ImageData"

dirlist = os.listdir(DATASET_DIR)
for d in dirlist:
    os.makedirs(os.path.join(IMAGE_DATA, d), exist_ok=True)

Dann konvertieren Sie alle Daten und lassen Sie uns "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)

Sie sollten jetzt Daten haben, wie in der Abbildung unten gezeigt.

directory.png

3. Bilddaten formatieren und anpassen

Damit können die Daten in zweidimensionale Daten umgewandelt werden. Als nächstes formatieren wir die zweidimensionalen Daten so, dass sie einfach zu handhaben sind.

3.1 Daten lesen

Laden wir zunächst die oben gespeicherten Numpy-Daten.

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)

3.2. Normalisierung

Erstens liegen die obigen Daten im Bereich von -200 bis 100. Normalisieren wir dies auf den Bereich 0 ~ 1.

#Holen Sie sich die Maximal- und Minimalwerte für die gesamten Daten
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~Im Bereich von 1
normalize = lambda x: (x - minimum)/(maximum - minimum)
for i, data in enumerate(numpy_datas):
    numpy_datas[i][0] = normalize(data[0])

3.3 Richten Sie die Größe der Daten aus

Die in Abschnitt 2 erstellten Daten haben die Größe von $ 19 \ mal T $. Hier ist $ T $ die Zeit (genauer Sekunden x Abtastrate). Um einfach in ein neuronales Netzwerk einzutauchen, ist es einfacher zu verstehen, ob alle Datengrößen gleich sind.

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)

Hier wurden alle in 512 × 19 Daten konvertiert.

3.4. Speichern von Bilddaten

Speichern Sie die Daten wie in Abschnitt 2.

Erstellen Sie zunächst ein Verzeichnis.

NORMALIZE_DATA = "NormalizeData"

dirlist = os.listdir(DATASET_DIR)
for d in dirlist:
    os.makedirs(os.path.join(NORMALIZE_DATA, d), exist_ok=True)

Und speichern.

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])

4. Erstellen und trainieren Sie ein Modell

Lass uns mit tiefem Lernen lernen

4.1 Trennung von Trainingsdaten und Testdaten

Teilen wir 900 Daten in Trainingsdaten, Testdaten und Verifizierungsdaten auf.

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:]] #Ermittlung der richtigen Antwortdaten aus dem Dateinamen
    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)

Alle Daten wurden in Trainingsdaten geändert: Testdaten: Verifizierungsdaten = 0,8: 0,1: 0,1. Da 900 Daten verwendet werden, ist dies 720: 90: 90. Es wird auch zweimal gemischt, um eine gute Verteilung zu gewährleisten. Nachdem die Daten in einem Verzeichnis gespeichert wurden, werden sie einmal gemischt und schließlich nach dem Beitritt erneut gemischt.

Es hat eine solche Datenstruktur.

4.2 Konvertieren Sie Eingaben und korrigieren Sie die Daten für das Training

Bevor wir mit Keras des Tensorflusses lernen, konvertieren wir die Eingabe und korrigieren die Antwortdaten, um die Verwendung zu vereinfachen. Dieses Mal sind "train_datas [i] [1] [0]" die richtigen Antwortdaten, um die Namen der Sprecher zu klassifizieren.

#In numpy konvertieren
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))])

#Listen Sie die richtigen Antwortdaten auf
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))]

Lassen Sie uns die richtigen Antwortdaten in 1-hot ändern.

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)

4.3 Modellbau / Lernen

Lassen Sie uns nun ein Modell für das neuronale Netzwerk erstellen. Ich kenne nichts wie eiserne Regeln, deshalb habe ich beschlossen, die Faltungsoperation und Normalisierung vorerst zu wiederholen. Die Aktivierungsfunktion verwendet 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()

Das Ergebnis von model.summary () ist wie folgt.

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
_________________________________________________________________

Lass sie lernen.

```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)
)

4.4.Genauigkeitsvorhersage mit Testdaten

Lassen Sie uns abschließend anhand der zuerst erstellten Testdaten eine Vorhersage treffen.

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 %

Ich konnte alle Fragen richtig beantworten. Toll.

Ich habe es mehrmals versucht, aber es ist ungefähr 90%Die obige Genauigkeit ist herausgekommen. Wenn Sie das Modell etwas weiter einstellen, werden es definitiv 100 sein%Ich denke ich kann es schaffen.

4.5.Klassifizierung von Emotionen

Versuchen wir also, nicht nur den Namen des Synchronsprechers, sondern auch die Emotion zu klassifizieren.

Erneut veröffentlichen

-Eingabedaten: train_datas[i][0] -Richtige Antwortdaten: train_datas[i][1] -Name des Synchronsprechers: train_datas[i][1][0] -Gefühle: train_datas[i][1][1]

Vontrain_datas[i][1][1]Versuchen wir es als richtige Antwortdaten. Ergebnis ist,

correct: 88 / 90
rate: 97.77777777777777 %

Und das gleiche Ergebnis wurde erhalten.

#Zusammenfassung Dieses Mal haben wir durch Klassifizieren von Sprachschauspielern von Sprache die Stimme in ein Bild umgewandelt und ein neuronales Netzwerk unter Verwendung einer Faltungsschicht verwendet. Das Ergebnis ist 90%Damit habe ich es wohl gut gesagt.

In Zukunft möchte ich in der Lage sein, die Stimmen von Yui Ogura anhand der Stimmen von Anime und Radio zu unterscheiden.

Recommended Posts

Sprachverarbeitung durch tiefes Lernen: Lassen Sie uns anhand der Stimme herausfinden, wer der Sprecher ist
Bestimmen, ob es mein Kind ist, anhand des Bildes eines Shiba-Hundes durch tiefes Lernen (3) Visualisierung durch Grad-CAM
Bestimmen Sie anhand des Bildes des Shiba-Hundes anhand des Bildes des Shiba-Hundes, ob es sich um ein Kind handelt
[Deep Learning von Grund auf neu] Ich habe die Affine-Ebene implementiert
Deep Learning aus den mathematischen Grundlagen Teil 2 (während der Teilnahme)
Othello ~ Aus der dritten Zeile von "Implementation Deep Learning" (4) [Ende]
Die Kopiermethode von pandas.DataFrame ist standardmäßig Deep Copy
Deep Learning von Grund auf neu
(Deep Learning) Ich habe Bilder von der Flickr-API gesammelt und versucht, durch Transferlernen mit VGG16 zu unterscheiden
[Deep Learning von Grund auf neu] Implementieren Sie die Backpropagation-Verarbeitung in einem neuronalen Netzwerk mithilfe der Fehler-Back-Propagation-Methode
[Teil 4] Verwenden Sie Deep Learning, um das Wetter anhand von Wetterbildern vorherzusagen
[Teil 1] Verwenden Sie Deep Learning, um das Wetter anhand von Wetterbildern vorherzusagen
[Teil 3] Verwenden Sie Deep Learning, um das Wetter anhand von Wetterbildern vorherzusagen
Erstellen Sie eine KI, die Zuckerbergs Gesicht mit tiefem Lernen identifiziert learning (Datenlernen)
[Teil 2] Verwenden Sie Deep Learning, um das Wetter anhand von Wetterbildern vorherzusagen
[Deep Learning von Grund auf neu] Informationen zu den Ebenen, die für die Implementierung der Backpropagation-Verarbeitung in einem neuronalen Netzwerk erforderlich sind
Deep Learning von Grund auf 1-3 Kapitel
Lassen Sie uns von der Linie suchen
Beurteilung, ob es sich um mein Kind handelt oder nicht, nach dem Bild des Shiba-Hundes durch tiefes Lernen (2) Datenerhöhung, Transferlernen, Feinabstimmung
Lua-Version Deep Learning von Grund auf neu Teil 6 [Inferenzverarbeitung für neuronale Netze]
Bewerten Sie die Genauigkeit des Lernmodells durch einen Kreuztest von scikit learn