En esta entrada se recoge la implementación de un detector de actividad de voz a través de una red neuronal artificial entrenada mediante propagación hacia atrás.
Este tipo de redes neuronales es ideal para el reconocimiento de patrones, de modo que entrenamos el sistema con vectores de características representativos de pasajes de silencio (ruido de fondo) y pasajes de voz. Para ello caracterizamos una trama de audio mediante 12 coeficientes cepstrales MFCC (los primeros 13 coeficientes salvo el primero, ya que este está relacionado con la energía de la trama y en este caso no es representativo) y un coeficiente de entropía, computada como:
$H = -\sum_{k=1}^N p_{k}\log p_{k}$
Donde $N$ es el número de muestras del módulo del espectro de la trama en consideración y $p_{k}$ representa el valor k-ésimo de la función densidad de probabilidad calculada a partir de dicho espectro, definiéndose como:
$p_{k} = \frac{S_{k}}{\sum_{j}^N S_{j}}$
Con $S$ el módulo del espectro de la trama.
La red neuronal que he construido para solventar el problema utiliza tramas de 128 muestras (donde consideramos la señal aproximadamente estacionaria) para un audio muestreado a 8kHz y está diseñada para usar 10 capas ocultas. Dicha red está entrenada con 20 vectores de características representativos de silencio y otros 22 vectores representativos de voz. El resto de parámetros se extrae del código MatLab incluido a continuación.
Obsérvese un ejemplo de un fragmento de audio al que se le eliminan los silencios mediante esta implementación de VAD. El funcionamiento es bastante bueno. El programa va consumiendo tramas de 128 muestras a 8kHz, clasificándolas como voz o silencio gracias a la red neuronal previamente entrenada. Recomiendo sacar del código la parte del entrenamiento de la red neuronal, a fin de no estar inicializándola constantemente cada vez que se llame a la función.
Audio de entrada.
Audio de salida con silencios eliminados.
Esta herramienta que he construido está pensada para funcionar previamente a la detección del pitch, a fin de poder eliminar las partes sin voz para no obtener falsas estimaciones.
Se puede ver el código a continuación, así como descargar mi fichero con patrones de entrenamiento haciendo clic aquí.
% Detector de actividad de voz basado en red neuronal
% Autor: Iván López Espejo.
function [y,sil] = vadet(x,fm)
% Remuestreo a 8kHz.
x = resample(x,8000,fm);
x = x/max(abs(x));
% Definición de algunos parámetros.
N = 128;
% Inicialización y entrenamiento de la red neuronal.
nump = 2; % Número de clases.
% Patrones de entrenamiento.
load Patrones
P = Patrones;
% Salidas.
T = zeros(2,42);
T(1,21:42) = ones(1,22);
T(2,1:20) = ones(1,20);
S1 = 10; % Número de capas ocultas.
S2 = nump; % Número de capas de salida (= al número de clases).
[R,Q] = size(P);
epochs = 10000; % Número de iteraciones.
goal_err = 5e-5; % Error objetivo.
a = 0.7; % Define el rango de variables aleatorias.
b = -0.7;
W1 = a + (b-a) * rand(S1,R); % Pesos entre la entrada y las neuronas ocultas.
W2 = a + (b-a) * rand(S2,S1); % Pesos entre las neuronas ocultas y las de salida.
b1 = a + (b-a) * rand(S1,1); % Pesos entre la entrada y las neuronas ocultas.
b2 = a + (b-a) * rand(S2,1); % Pesos entre las neuronas ocultas y las de salida.
n1 = W1 * P;
A1 = logsig(n1);
n2 = W2 * A1;
A2 = logsig(n2);
e = A2 - T;
error = 0.5 * mean(mean(e.*e));
nntwarn off
for itr = 1:epochs
if error <= goal_err
break;
else
for i = 1:Q
df1 = dlogsig(n1,A1(:,i));
df2 = dlogsig(n2,A2(:,i));
s2 = -2 * diag(df2) * e(:,i);
s1 = diag(df1)* W2' * s2;
W2 = W2 - 0.1 * s2 * A1(:,i)';
b2 = b2 - 0.1 * s2;
W1 = W1 - 0.1 * s1 * P(:,i)';
b1 = b1 - 0.1 * s1;
A1(:,i) = logsig(W1*P(:,i),b1);
A2(:,i) = logsig(W2*A1(:,i),b2);
end
e = T - A2;
error = 0.5 * mean(mean(e.*e));
disp(sprintf('Iteración: %5d mse: %12.6f%',itr,error));
mse(itr) = error;
end
end
threshold = 0.9; % Umbral del sistema (umbral más alto = mayor precisión).
TrnOutput = real(A2>threshold);
disp('Resultado del entrenamiento: ')
disp(TrnOutput)
% Inicialización del audio de salida.
y = [];
sil = [];
for j = 1:N:length(x)-N
coefs = calCoeficientes(x(j:j+N-1))';
% Clasificamos el frame en voz o silencio.
n1 = W1 * coefs;
A1 = logsig(n1);
n2 = W2 * A1;
A2test = logsig(n2);
resultado = real(A2test>threshold);
if resultado(1) == 1 && resultado(2) == 0
y = [y; x(j:j+N-1)];
else
sil = [sil; x(j:j+N-1)];
end
end
% Autor: Iván López Espejo.
function [y,sil] = vadet(x,fm)
% Remuestreo a 8kHz.
x = resample(x,8000,fm);
x = x/max(abs(x));
% Definición de algunos parámetros.
N = 128;
% Inicialización y entrenamiento de la red neuronal.
nump = 2; % Número de clases.
% Patrones de entrenamiento.
load Patrones
P = Patrones;
% Salidas.
T = zeros(2,42);
T(1,21:42) = ones(1,22);
T(2,1:20) = ones(1,20);
S1 = 10; % Número de capas ocultas.
S2 = nump; % Número de capas de salida (= al número de clases).
[R,Q] = size(P);
epochs = 10000; % Número de iteraciones.
goal_err = 5e-5; % Error objetivo.
a = 0.7; % Define el rango de variables aleatorias.
b = -0.7;
W1 = a + (b-a) * rand(S1,R); % Pesos entre la entrada y las neuronas ocultas.
W2 = a + (b-a) * rand(S2,S1); % Pesos entre las neuronas ocultas y las de salida.
b1 = a + (b-a) * rand(S1,1); % Pesos entre la entrada y las neuronas ocultas.
b2 = a + (b-a) * rand(S2,1); % Pesos entre las neuronas ocultas y las de salida.
n1 = W1 * P;
A1 = logsig(n1);
n2 = W2 * A1;
A2 = logsig(n2);
e = A2 - T;
error = 0.5 * mean(mean(e.*e));
nntwarn off
for itr = 1:epochs
if error <= goal_err
break;
else
for i = 1:Q
df1 = dlogsig(n1,A1(:,i));
df2 = dlogsig(n2,A2(:,i));
s2 = -2 * diag(df2) * e(:,i);
s1 = diag(df1)* W2' * s2;
W2 = W2 - 0.1 * s2 * A1(:,i)';
b2 = b2 - 0.1 * s2;
W1 = W1 - 0.1 * s1 * P(:,i)';
b1 = b1 - 0.1 * s1;
A1(:,i) = logsig(W1*P(:,i),b1);
A2(:,i) = logsig(W2*A1(:,i),b2);
end
e = T - A2;
error = 0.5 * mean(mean(e.*e));
disp(sprintf('Iteración: %5d mse: %12.6f%',itr,error));
mse(itr) = error;
end
end
threshold = 0.9; % Umbral del sistema (umbral más alto = mayor precisión).
TrnOutput = real(A2>threshold);
disp('Resultado del entrenamiento: ')
disp(TrnOutput)
% Inicialización del audio de salida.
y = [];
sil = [];
for j = 1:N:length(x)-N
coefs = calCoeficientes(x(j:j+N-1))';
% Clasificamos el frame en voz o silencio.
n1 = W1 * coefs;
A1 = logsig(n1);
n2 = W2 * A1;
A2test = logsig(n2);
resultado = real(A2test>threshold);
if resultado(1) == 1 && resultado(2) == 0
y = [y; x(j:j+N-1)];
else
sil = [sil; x(j:j+N-1)];
end
end


1 comentarios:
Hola,
Mi nombre es Manuel Rodrigues Lima.
Me interesó el algoritmo VAD que implementaste, pero desafortunadamente el fichero de patrones de entrenamiento se encuentra caído y no puedo descargarlo. Será posible que me lo envíes al mail o lo resubas a otro sitio? Te estaría muy agradecido,
Saludos y gracias,
Manuel
E-mail: ikario [ARROBA@] [GMAIL].[COM]
Publicar un comentario en la entrada