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.
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.
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.
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.
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');
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.
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();
Bei MFCC ist die horizontale Achse die Zeit und die vertikale Achse der 20-dimensionale Wert.
print(mfccs.shape) # -> (20, 630)
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();
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:
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();
Damit war es möglich, Bilddaten zu erstellen.
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.
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.
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)
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])
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.
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])
Lass uns mit tiefem Lernen lernen
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.
train_datas [i] [0]
--Korrekte Daten: train_datas [i] [1]
train_datas [i] [1] [0]
--Emotionen: train_datas [i] [1] [1]
Es hat eine solche Datenstruktur.
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)
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)
)
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.
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