CNN zur Phonemerkennung


7

Ich studiere derzeit dieses Papier , in dem CNN für die Phonemerkennung unter Verwendung der visuellen Darstellung von Log-Mel-Filterbänken und eines begrenzten Gewichtsverteilungsschemas angewendet wird.

Die Visualisierung von Log-Mel-Filterbänken ist eine Möglichkeit, die Daten darzustellen und zu normalisieren. Sie schlagen vor, als Spektogramm mit RGB-Farben zu visualisieren. Das Beste, was ich mir einfallen lassen könnte, wäre, es mit einer matplotlibsFarbkarte zu zeichnen cm.jet. Sie (als Papier) schlagen auch vor, dass jeder Rahmen mit seinen [statischen Delta Delta_ Delta] Filterbank-Energien gestapelt werden sollte. Das sieht so aus:

Geben Sie hier die Bildbeschreibung ein

Die Eingabe der besteht aus einem Bildfeld von 15 Frames, die [statische Delta Delta_Detlta] Eingabeform wäre (40,45,3)

Die begrenzte Gewichtsverteilung besteht darin, die Gewichtsverteilung auf einen bestimmten Filterbankbereich zu beschränken, da Sprache in verschiedenen Frequenzbereichen unterschiedlich interpretiert wird. Daher würde eine vollständige Gewichtsverteilung bei normaler Faltung nicht funktionieren.

Ihre Implementierung einer begrenzten Gewichtsverteilung besteht darin, die Gewichte in der Gewichtsmatrix zu steuern, die jeder Faltungsschicht zugeordnet sind. Sie wenden also eine Faltung auf die gesamte Eingabe an. Das Papier wendet nur eine Faltungsschicht an, da die Verwendung mehrerer Schichten die Lokalität der aus der Faltungsschicht extrahierten Merkmalskarten zerstören würde. Der Grund, warum sie Filterbank-Energien anstelle des normalen MFCC-Koeffizienten verwenden, liegt darin, dass DCT die Lokalität der Filterbank-Energien zerstört.

Geben Sie hier die Bildbeschreibung ein

Anstatt die der Faltungsschicht zugeordnete Gewichtsmatrix zu steuern, entscheide ich mich, das CNN mit mehreren Eingaben zu implementieren. Daher besteht jeder Eingang aus einem (kleiner Filterbankbereich, total_frames_with_deltas, 3). So heißt es beispielsweise auf dem Papier, dass eine Filtergröße von 8 gut sein sollte, und ich habe einen Filterbankbereich von 8 festgelegt. Daher hat jedes kleine Bildfeld die Größe (8,45,3). Jedes der kleinen Bildfelder wird mit einem Schiebefenster mit einem Schritt von 1 extrahiert - es gibt also eine große Überlappung zwischen jedem Eingang - und jeder Eingang hat seine eigene Faltungsschicht.

Geben Sie hier die Bildbeschreibung ein

(input_3, input_3, input3, sollte input_1, input_2, input_3 ... gewesen sein)

Auf diese Weise ist es möglich, mehrere Faltungsschichten zu verwenden, da die Lokalität kein Problem mehr darstellt, da sie innerhalb eines Filterbankbereichs angewendet wird. Dies ist meine Theorie.

Das Papier gibt es nicht explizit an, aber ich denke, der Grund, warum sie die Phonemerkennung für mehrere Frames durchführen, besteht darin, dass ein Teil des linken und rechten Kontexts vorhanden ist, sodass nur der mittlere Frame vorhergesagt / trainiert wird. In meinem Fall setzen die ersten 7 Frames das linke Kontextfenster - der mittlere Frame wird trainiert und die letzten 7 Frames setzen das rechte Kontextfenster. Bei mehreren Frames wird also nur ein Phonem als Mitte erkannt.

Mein neuronales Netzwerk sieht derzeit so aus:

def model3():

    #stride = 1
    #dim = 40
    #window_height = 8
    #splits = ((40-8)+1)/1 = 33
    next(test_generator())
    next(train_generator(batch_size))

    kernel_number = 200#int(math.ceil(splits))
    list_of_input = [Input(shape = (window_height,total_frames_with_deltas,3)) for i in range(splits)]
    list_of_conv_output = []
    list_of_conv_output_2 = []
    list_of_conv_output_3 = []
    list_of_conv_output_4 = []
    list_of_conv_output_5 = []
    list_of_max_out = []
    for i in range(splits):
        #list_of_conv_output.append(Conv2D(filters = kernel_number , kernel_size = (15,6))(list_of_input[i]))
        #list_of_conv_output.append(Conv2D(filters = kernel_number , kernel_size = (window_height-1,3))(list_of_input[i]))
        list_of_conv_output.append(Conv2D(filters = kernel_number , kernel_size = (window_height,3), activation = 'relu')(list_of_input[i]))
        list_of_conv_output_2.append(Conv2D(filters = kernel_number , kernel_size = (1,5))(list_of_conv_output[i]))
        list_of_conv_output_3.append(Conv2D(filters = kernel_number , kernel_size = (1,7))(list_of_conv_output_2[i]))
        list_of_conv_output_4.append(Conv2D(filters = kernel_number , kernel_size = (1,11))(list_of_conv_output_3[i]))
        list_of_conv_output_5.append(Conv2D(filters = kernel_number , kernel_size = (1,13))(list_of_conv_output_4[i]))
        #list_of_conv_output_3.append(Conv2D(filters = kernel_number , kernel_size = (3,3),padding='same')(list_of_conv_output_2[i]))
        list_of_max_out.append((MaxPooling2D(pool_size=((1,11)))(list_of_conv_output_5[i])))

    merge = keras.layers.concatenate(list_of_max_out)
    print merge.shape
    reshape = Reshape((total_frames/total_frames,-1))(merge)

    dense1 = Dense(units = 1000, activation = 'relu',    name = "dense_1")(reshape)
    dense2 = Dense(units = 1000, activation = 'relu',    name = "dense_2")(dense1)
    dense3 = Dense(units = 145 , activation = 'softmax', name = "dense_3")(dense2)
    #dense4 = Dense(units = 1, activation = 'linear', name = "dense_4")(dense3)


    model = Model(inputs = list_of_input , outputs = dense3)
    model.compile(loss="categorical_crossentropy", optimizer="SGD" , metrics = [metrics.categorical_accuracy])

    reduce_lr=ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, verbose=1, mode='auto', epsilon=0.001, cooldown=0)
    stop  = EarlyStopping(monitor='val_loss', min_delta=0, patience=5, verbose=1, mode='auto')

    print model.summary()

    raw_input("okay?")
    hist_current = model.fit_generator(train_generator(batch_size),
                        steps_per_epoch=10,
                        epochs = 10000,
                        verbose = 1,
                        validation_data = test_generator(),
                        validation_steps=1)
                        #pickle_safe = True,
                        #workers = 4)

Also .. jetzt kommt das Problem ..

Ich habe das Netzwerk trainiert und konnte nur eine Validierungsgenauigkeit von höchstens 0,17 erhalten, und die Genauigkeit nach vielen Epochen beträgt 1,0. Geben Sie hier die Bildbeschreibung ein (Handlung wird gerade gemacht)

fester Rahmen:

Geben Sie hier die Bildbeschreibung ein (Handlung wird noch gemacht)

Ich bin mir nicht sicher, warum ich keine besseren Ergebnisse erhalte. Warum diese hohe Fehlerrate? Ich verwende den TIMIT-Datensatz, den auch die anderen verwenden. Warum erhalte ich dann schlechtere Ergebnisse?

Und entschuldigen Sie den langen Beitrag - ich hoffe, weitere Informationen zu meiner Entwurfsentscheidung könnten hilfreich sein - und helfen Sie zu verstehen, wie ich das Papier verstanden habe und wie ich es angewendet habe, um herauszufinden, wo mein Fehler liegen würde.


1
Könnten Sie bitte die Eingabeform (40,45,3) erklären? Ich denke, es sollte (40,15,3) sein, dh (Anzahl der Filterbänke, Kontext (links + rechts), 3 (statisch + Delta + Delta_Delta)).
Arduinolover

Ich denke du hast Recht 3 Frames. aber 45 insgesamt seit 15x3 = 45.
Carlton Banks

Könnten Sie versuchen, die Ausgänge erneut zu veröffentlichen? Übrigens benutzt du Kaldi?
Arduinolover

@arduinolover ja ich benutze kaldi .. und es läuft gerade. fbank-Energien werden mit dem Kaldi-Framework extrahiert.
Carlton Banks

Bitte gehen Sie dieses Tutorial auf CNN für die Spracherkennung durch github.com/botonchou/libdnn/wiki/…
arduinolover

Antworten:


2

Könnte Ihre Netzwerkstruktur sein:

Das Papier besagt, dass ihr Experiment durchgeführt wird mit:

conv
pool
dense
dense
dense(softmax)

Also so etwas für fws:

def fws():
    #Input shape: (batch_size,40,45,3)
    #output shape: (1,15,50)
    # number of unit in conv_feature_map = splitd

    filter_size = 8
    pooling_size = 28
    stride_step = 2
    pool_splits = ((splits - pooling_size)+1)/2
    conv_featur_map = []
    pool_feature_map = []
    print "Printing shapes"


    list_of_input = [Input(shape = (window_height,total_frames_with_deltas,3)) for i in range(splits)]


    #convolution
    shared_conv = Conv2D(filters = 150, kernel_size = (filter_size,45), activation='relu')
    for i in range(splits):
        conv_featur_map.append(shared_conv(list_of_input[i]))

    #Pooling
    input = Concatenate()(conv_featur_map)
    input = Reshape((splits,-1))(input)
    pooled = MaxPooling1D(pool_size = pooling_size, strides = stride_step)(input)

    #reshape = Reshape((3,-1))(pooled)

    #fc
    dense1 = Dense(units = 1000, activation = 'relu',    name = "dense_1")(pooled)
    dense2 = Dense(units = 1000, activation = 'relu',    name = "dense_2")(dense1)
    dense3 = Dense(units = 50 , activation = 'softmax', name = "dense_3")(dense2)

Wenn Sie die Position der for-Schleife in fws ändern (2 Zeilen nach oben verschieben), wird sie zu lws (plus einige Anpassungen mit der Pooling-Ebene).


Es sieht viel einfacher aus als das, was ich wollte ...
Carlton Banks
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.