Atualmente, estou estudando este artigo , no qual a CNN é aplicada para reconhecimento de fonemas usando representação visual de bancos de filtros de registros de mel e esquema de compartilhamento de peso limitado.
A visualização dos bancos de filtros de log mel é uma maneira de representar e normalizar os dados. Eles sugerem visualizar como um espectograma com cores RGB, que o mais próximo que eu poderia chegar seria matplotlibs
plotá- lo usando o mapa de cores cm.jet
. Eles (sendo o papel) também sugerem que cada quadro seja empilhado com suas energias do banco de filtros [static delta delta_delta]. É assim:
A entrada consistir em um patch de imagem de 15 quadros definido [formato delta estático delta_detlta] seria (40,45,3)
O compartilhamento de peso limitado consiste em limitar o compartilhamento de peso a uma área específica do banco de filtros, pois a fala é interpretada de maneira diferente em diferentes áreas de frequência, portanto, um compartilhamento de peso total conforme a convolução normal se aplica não funcionaria.
Sua implementação do compartilhamento limitado de pesos consiste em controlar os pesos na matriz de pesos associados a cada camada convolucional. Então eles aplicam uma convolução na entrada completa. O artigo aplica apenas uma camada convolucional, pois o uso de múltiplas destrói a localidade dos mapas de recursos extraídos da camada convolucional. A razão pela qual eles usam as energias do banco de filtros em vez do coeficiente normal do MFCC é porque o DCT destrói a localidade das energias dos bancos de filtros.
Em vez de controlar a matriz de peso associada à camada de convolução, optei por implementar a CNN com várias entradas. portanto, cada entrada consiste em um (pequeno intervalo de banco de filtros, total_frames_with_deltas, 3). Assim, por exemplo, o artigo declara que um tamanho de filtro de 8 deve ser bom, então decidi um intervalo de banco de filtros de 8. Portanto, cada pequeno retalho de imagem é de tamanho (8,45,3). Cada parte da pequena imagem é extraída com uma janela deslizante com um passo de 1 - para que haja muita sobreposição entre cada entrada - e cada entrada tenha sua própria camada convolucional.
(entrada_3, entrada_3, entrada3, deveria ter sido entrada_1, entrada_2, entrada_3 ...)
Dessa maneira, é possível usar várias camadas convolucionais, já que a localidade não é mais um problema, uma vez que é aplicada dentro de uma área de banco de filtros, essa é a minha teoria.
O artigo não afirma explicitamente, mas acho que a razão pela qual eles reconhecem fonemas em vários quadros é ter alguns dos contextos esquerdo e direito, de modo que apenas o quadro intermediário esteja sendo previsto / treinado. Portanto, no meu caso, os 7 primeiros quadros definem a janela de contexto esquerda - o quadro do meio está sendo treinado e os últimos 7 quadros são a janela de contexto correta. Assim, considerando vários quadros, apenas um fonema será reconhecido como sendo o meio.
Minha rede neural atualmente se parece com isso:
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)
Então .. agora vem a questão ..
Eu tenho treinado a rede e só consegui obter uma validação_ precisão de 0,17, e a precisão após várias épocas acaba sendo 1,0. (A plotagem está sendo feita no momento)
quadro fixo:
(trama ainda está sendo feita)
Não sei por que não estou obtendo melhores resultados. Por que essa alta taxa de erros? Estou usando o conjunto de dados TIMIT que os outros também usam. Então, por que estou obtendo resultados piores?
E desculpe pelo longo post - espero que mais informações sobre minha decisão de design possam ser úteis - e ajude a entender como eu entendi o artigo e como eu me inscrevi ajudaria a identificar onde estaria meu erro.
fonte
Respostas:
Pode ser sua estrutura de rede:
O artigo afirma que seu experimento é realizado usando:
Então, algo assim para fws:
Alterar a posição do loop for em fws (movendo-o 2 linhas para cima) torna-o em lws (além de alguns ajustes com a camada de pool).
fonte