Like my articles? Feel free to vote for me as ML Writer of the year here . El análisis de sonido es una tarea desafiante, asociada a varias aplicaciones modernas, como análisis de voz, recuperación de información musical, reconocimiento de locutores, análisis de comportamiento y análisis de escenas auditivas para monitoreo de seguridad, salud y medio ambiente. Este artículo proporciona una breve introducción a los conceptos básicos de extracción de de , y de sonido, con ejemplos de demostración en aplicaciones como clasificación de géneros musicales, agrupación de altavoces, clasificación de eventos de audio y detección de actividad de voz. características audio clasificación segmentación Se proporcionan ejemplos de en todos los casos, principalmente a través de la biblioteca . Todos los ejemplos también se proporcionan en repositorio de github. Python pyAudioAnalysis este Con respecto a las metodologías de ML involucradas, este artículo se centra en las funciones de audio hechas a mano y los clasificadores estadísticos tradicionales como SVM. Los métodos de audio profundo se seguirán en un artículo futuro, ya que el presente artículo trata más sobre aprender a extraer características de audio que tengan sentido para sus clasificadores, incluso cuando tenga algunas decenas de muestras de entrenamiento. requisitos previos Antes de profundizar en el reconocimiento de audio, el lector debe conocer los conceptos básicos del manejo de audio y la representación de señales: definición de sonido, muestreo, cuantificación, frecuencia de muestreo, resolución de muestreo y los conceptos básicos de representación de frecuencia. Estos temas se tratan en artículo. este Extracción de funciones de audio: a corto plazo y basada en segmentos Por lo tanto, ya debería saber que una señal de audio está representada por una de con una "resolución de muestra" dada (generalmente 16 bits = 2 bytes por muestra) y con una particular (por ejemplo, 16 KHz = 16000 muestras por segundo). secuencia muestras frecuencia de muestreo Ahora podemos continuar con el siguiente paso: usar estas muestras para los sonidos correspondientes. Por "analizar" podemos referirnos a cualquier cosa, desde: reconocer entre diferentes tipos de sonidos, segmentar una señal de audio en partes homogéneas (por ejemplo, dividir los segmentos sonoros de los no sonoros en una señal de voz) o agrupar archivos de sonido en función de su similitud de contenido. analizar En todos los casos, primero debemos encontrar una manera de pasar de las muestras de datos de audio voluminosas y de bajo nivel a una representación de nivel superior del contenido de audio. Este es el propósito de , la tarea más común e importante en todas las aplicaciones de aprendizaje automático y reconocimiento de patrones. la extracción de características (FE) FE se trata de extraer un conjunto de características que son informativas con respecto a las propiedades deseadas de los datos originales. En nuestro caso, estamos interesados en extraer características de audio que sean capaces de discriminar entre diferentes clases de audio, es decir, diferentes hablantes, eventos, emociones o géneros musicales, dependiendo del subdominio de la aplicación. El concepto más importante de la extracción de características de audio es (o ): esto simplemente significa que la señal de audio se divide en ventanas (o ) a corto plazo. Los marcos pueden superponerse opcionalmente. el uso de ventanas a corto plazo marcos marcos La duración de las tramas suele oscilar entre 10 y 100 milisegundos, según la aplicación y los tipos de señales. Para el caso de no superposición, el del procedimiento de ventana es igual a la de la ventana (también llamado "tamaño"). paso longitud Si, por otro lado, paso < tamaño, entonces los marcos se superponen: por ejemplo, un paso de 10 ms para un tamaño de ventana de 40 ms significa una superposición del 75%. Por lo general, también se aplica una función de ventana (como hamming) a cada cuadro. Para cada fotograma (sea N el número total de fotogramas), extraemos un conjunto de características de audio (a corto plazo). Cuando las características se extraen directamente de los valores de la muestra de audio, se denominan dominio del tiempo. Si las características se calculan sobre los valores de FFT, se denominan características de dominio de frecuencia. Finalmente, las características cepstrales (como los ) son características que se basan en el cepstrum. MFCC Como ejemplo, supongamos que solo extraemos la energía de la señal (la media de los cuadrados de las muestras de audio) y el centroide espectral (el centroide de la magnitud de la FFT). Esto significa que, (o dos secuencias de funciones de igual duración, si lo desea). durante este procedimiento de trama, la señal se representa mediante una secuencia de vectores de características bidimensionales a corto plazo. Entonces, ¿cómo podemos usar estas secuencias de tamaño arbitrario para analizar la señal respectiva? Imagina que quieres construir un clasificador para discriminar entre dos clases de audio, por ejemplo, habla y silencio. Tus datos de entrenamiento iniciales son archivos de audio y las etiquetas de clase correspondientes (una etiqueta de clase por archivo de audio ). Si estos archivos tienen la misma duración, las secuencias de vectores de características a corto plazo correspondientes tendrán la misma longitud. ¿Qué sucede, sin embargo, en el caso general de duraciones arbitrarias? completo ¿Cómo representamos segmentos de audio de tamaño arbitrario? Una solución sería rellenar con cero las secuencias de características hasta la duración máxima del conjunto de datos y luego concatenar las diferentes secuencias de características a corto plazo en un solo vector de características. Pero eso (a) conduciría a una dimensionalidad muy alta (y, por lo tanto, a la necesidad de más muestras de datos para lograr el entrenamiento) y (b) dependería mucho de las posiciones temporales de los valores de las características (ya que cada característica correspondería a una marca de tiempo diferente) . Un enfoque más común seguido en el análisis de audio tradicional es extraer un conjunto de por . estadísticas de características segmento de tamaño fijo Las estadísticas a nivel de segmento extraídas sobre las secuencias de características a corto plazo son las representaciones para cada segmento de tamaño fijo. La representación final de la señal puede ser el promedio a largo plazo de las estadísticas del segmento. ... las estadísticas de características del segmento son la forma más sencilla de hacerlo Como ejemplo, considere una señal de audio de 2,5 segundos. Seleccionamos una ventana a corto plazo de 50 ms y un segmento de 1 segundo. De acuerdo con lo anterior, las secuencias de energía y centroide espectral se extraerán para cada segmento de 1 segundo. La longitud de las secuencias N será igual a 1 / 0.050 = 20. Luego, se extraen los μ y σ de cada secuencia para cada segmento de 1 segundo, como las estadísticas de características del segmento. Estos finalmente se promedian a largo plazo, lo que da como resultado la representación final de la señal. (Tenga en cuenta que el último segmento tiene una longitud de 0,5, por lo que las estadísticas se extraen en un segmento más corto) Notas: Los a corto plazo suelen oscilar entre 10 y 100 mseg. Los cuadros más largos significan mejores representaciones de frecuencia (más muestras para calcular FFT y, por lo tanto, cada contenedor de FFT corresponde a menos Hz). Pero los cuadros más largos también significan una pérdida de resolución temporal, ya que las señales de audio no son estacionarias (considere un cuadro tan largo que encapsule dos eventos de audio diferentes: la resolución de frecuencia sería muy alta, pero ¿qué representarían las características respectivas?) tamaños de trama El paso de promedio a largo plazo de las estadísticas de las características del segmento de una señal (descrito anteriormente) es opcional y generalmente se adopta durante el entrenamiento/prueba de un clasificador de audio. Se utiliza para asignar toda la señal a un solo vector de características. Esto no podría desearse en otra configuración, por ejemplo, cuando estamos interesados en segmentar la señal inicial. Extracción de características de audio: ejemplos de código El ejemplo leer un archivo de audio WAV y extraer secuencias de funciones a corto plazo y trazar la secuencia de energía (solo una de las funciones). Consulte los comentarios en línea para obtener una explicación, junto con estas dos notas: 1 usa pyAudioAnalysis para devuelve la frecuencia de muestreo (Fs) del archivo de audio y una matriz NumPy de las muestras de audio sin procesar. Para obtener la duración en segundos, simplemente hay que dividir el número de muestras por Fs read_audio_file() La función devuelve (a) una matriz de características a corto plazo de 68 x 20, donde 68 es la cantidad de características a corto plazo implementadas en la biblioteca y 20 es la cantidad de fotogramas que caben en los segmentos de 1 segundo (se usa 1 segundo como ventana intermedia en el ejemplo) (b) una lista de 68 cadenas de caracteres que contienen los nombres de cada función implementada en la biblioteca. ShortTermFeatures.feature_extraction() pyAudioAnalysis ShortTermFeatures aF pyAudioAnalysis audioBasicIO aIO numpy np plotly.graph_objs go plotly IPython fs, s = aIO.read_audio_file( ) IPython.display.display(IPython.display.Audio( )) duration = len(s) / float(fs) print( ) win, step = , [f, fn] = aF.feature_extraction(s, fs, int(fs * win), int(fs * step)) print( ) print( ) i, nam enumerate(fn): print( ) time = np.arange( , duration - step, win) energy = f[fn.index( ), :] mylayout = go.Layout(yaxis=dict(title= ), xaxis=dict(title= )) plotly.offline.iplot(go.Figure(data=[go.Scatter(x=time, y=energy)], layout=mylayout)) # Example 1: short-term feature extraction from import as from import as import as import as import import # read audio data from file # (returns sampling freq and signal as a numpy array) "data/object.wav" # play the initial and the generated files in notebook: "data/object.wav" # print duration in seconds: f'duration = seconds' {duration} # extract short-term features using a 50msec non-overlapping windows 0.050 0.050 f' frames, short-term features' {f.shape[ ]} 1 {f.shape[ ]} 0 'Feature names:' for in f' : ' {i} {nam} # plot short-term energy # create time axis in seconds 0 # get the feature whose name is 'energy' 'energy' "frame energy value" "time (sec)" da como resultado (recortado debido a la longitud): duration = seconds frames, short-term features Feature names: :zcr :energy :energy_entropy :spectral_centroid :spectral_spread :spectral_entropy :spectral_flux :spectral_rolloff :mfcc_1 ... :chroma_11 :chroma_12 :chroma_std :delta zcr :delta energy ... :delta chroma_12 :delta chroma_std 1.03 20 68 0 1 2 3 4 5 6 7 8 31 32 33 34 35 66 67 y el siguiente gráfico: El demuestra la característica a corto plazo del centroide espectral. El centroide espectral es simplemente el centroide de la magnitud FFT, normalizado en el rango de frecuencia [0, Fs/2] (por ejemplo, si el centroide espectral = 0,5, esto es igual a Fs/4 medido en Hz). ejemplo 2 pyAudioAnalysis ShortTermFeatures aF pyAudioAnalysis audioBasicIO aIO numpy np plotly.graph_objs go plotly IPython fs, s = aIO.read_audio_file( ) IPython.display.display(IPython.display.Audio( )) duration = len(s) / float(fs) print( ) win, step = , [f, fn] = aF.feature_extraction(s, fs, int(fs * win), int(fs * step)) print( ) time = np.arange( , duration - step, win) energy = f[fn.index( ), :] mylayout = go.Layout(yaxis=dict(title= ), xaxis=dict(title= )) plotly.offline.iplot(go.Figure(data=[go.Scatter(x=time, y=energy)], layout=mylayout)) # Example 2: short-term feature extraction: # spectral centroid of two speakers from import as from import as import as import as import import # read audio data from file # (returns sampling freq and signal as a numpy array) "data/trump_bugs.wav" # play the initial and the generated files in notebook: "data/trump_bugs.wav" # print duration in seconds: f'duration = seconds' {duration} # extract short-term features using a 50msec non-overlapping windows 0.050 0.050 f' frames, short-term features' {f.shape[ ]} 1 {f.shape[ ]} 0 # plot short-term energy # create time axis in seconds 0 # get the feature whose name is 'energy' 'spectral_centroid' "spectral_centroid value" "time (sec)" En este ejemplo, la secuencia del centroide espectral se calcula para una grabación que contiene una muestra del discurso de Donald Trump de 2 segundos, seguida de "¿Qué pasa, doc?" de Bugs Bunny. frase. Es obvio que la secuencia del centroide espectral se puede utilizar para discriminar estos dos altavoces en este ejemplo particular (los valores más altos del centroide espectral corresponden a frecuencias más altas y, por lo tanto, a sonidos "más brillantes"). En total, se extraen 34 funciones a corto plazo en , para cada cuadro, y ShortTermFeatures.feature_extraction() La función también (opcionalmente) extrae las características delta respectivas. En ese caso, el número total de características extraídas para cada cuadro a corto plazo es 68. La lista completa y la descripción de las características a corto plazo se pueden encontrar en el de la biblioteca y en . pyAudioAnalysis wiki esta publicación Los dos primeros ejemplos utilizaron la función para extraer 68 características por cuadro a corto plazo. Como se describe en la sección anterior, en muchos casos, como la clasificación a nivel de segmento, también extraemos estadísticas a nivel de segmento. Esto se logra a través de la función, como se muestra en el : ShortTermFeatures.feature_extraction() MidTermFeatures.mid_feature_extraction() Ejemplo 3 pyAudioAnalysis MidTermFeatures aF pyAudioAnalysis audioBasicIO aIO fs, s = aIO.read_audio_file( ) mt, st, mt_n = aF.mid_feature_extraction(s, fs, * fs, * fs, * fs, * fs) print( ) print( ) print( ) print( ) i, mi enumerate(mt_n): print( ) # Example 3: segment-level feature extraction from import as from import as # read audio data from file # (returns sampling freq and signal as a numpy array) "data/trump_bugs.wav" # get mid-term (segment) feature statistics # and respective short-term features: 1 1 0.05 0.05 f'signal duration seconds' {len(s)/fs} f' -D short-term feature vectors extracted' {st.shape[ ]} 1 {st.shape[ ]} 0 f' -D segment feature statistic vectors extracted' {mt.shape[ ]} 1 {mt.shape[ ]} 0 'mid-term feature names' for in f' : ' {i} {mi} resultados en: signal duration seconds -D short-term feature vectors extracted -D segment feature statistic vectors extracted mid-term feature names :zcr_mean :energy_mean :energy_entropy_mean :spectral_centroid_mean :spectral_spread_mean :spectral_entropy_mean :spectral_flux_mean :spectral_rolloff_mean :mfcc_1_mean ... :delta chroma_9_std :delta chroma_10_std :delta chroma_11_std :delta chroma_12_std :delta chroma_std_std 3.812625 76 68 4 136 0 1 2 3 4 5 6 7 8 131 132 133 134 135 extrae 2 estadísticas, a saber, la y la de cada secuencia de características a corto plazo, utilizando el tamaño de ventana de "mediano plazo" (segmento) proporcionado de 1 segundo para el ejemplo anterior. Dado que la duración de la señal es de 3,8 segundos, y el paso y el tamaño de la ventana a medio plazo es de 1 segundo, esperamos que se creen segmentos a medio plazo y para cada uno de ellos se calculará un vector de estadísticas de características. Además, estas estadísticas de segmento se calculan sobre las secuencias de características a corto plazo de 3,8/0,05 = fotogramas a corto plazo. Además, tenga en cuenta que los nombres de características a mediano plazo también contienen la estadística del segmento, por ejemplo, zcr_mean es la media de la característica a corto plazo con tasa de cruce por cero. MidTermFeatures.mid_feature_extraction() media estándar 4 76 Los primeros 3 ejemplos mostraron cómo podemos extraer características a corto plazo y estadísticas de características a mediano plazo (segmento). Función extrae características de audio para todos los archivos en la carpeta proporcionada, de modo que estos datos se puedan usar para entrenar un clasificador, etc. MidTermFeatures.directory_feature_extraction() Así que en realidad llama para cada archivo WAV y realiza un promedio a largo plazo para pasar de vectores estadísticos de características de segmento a un solo vector de características. Además, esta función es capaz de extraer dos características relacionadas con el ritmo de la música que se adjuntan en las estadísticas del segmento promediado. Como ejemplo, supongamos que queremos analizar una canción de 120 segundos, con una ventana (y paso) a corto plazo de 50 mseg y una ventana (segmento) a medio plazo y paso de 1 segundo. MidTermFeatures.mid_feature_extraction() Los siguientes pasos ocurrirán durante el llamar: MidTermFeatures.directory_feature_extraction() 120 / 0.05 = 2400 Se extraen vectores de características a corto plazo 68-D Se calculan las estadísticas de características 120 136-D (media y estándar de las secuencias vectoriales 68-D) los 120 136-D se promedian a largo plazo para toda la canción y (opcionalmente) se agregan dos características de tiempo (el tiempo debe calcularse en un nivel de archivo ya que necesita información a largo plazo), lo que lleva a un vector de característica final de 138 valores. demuestra el uso de para extraer características a nivel de archivo (promedios de estadísticas de características de segmento) para 20 muestras de música de 2 segundos (archivos WAV separados) de dos categorías de géneros musicales, a saber, música clásica y heavy metal. Para cada uno de los segmentos de canciones de 2 segundos extrae el vector de características 138-D, como se describe anteriormente. Luego seleccionamos trazar 2 de estas características, usando diferentes colores para las dos clases de audio (clásico y metal): El ejemplo 4 MidTermFeatures.directory_feature_extraction() MidTermFeatures.directory_feature_extraction() pyAudioAnalysis MidTermFeatures aF os numpy np plotly.graph_objs go plotly dirs = [ , ] class_names = [os.path.basename(d) d dirs] m_win, m_step, s_win, s_step = , , , features = [] d dirs: f, files, fn = aF.directory_feature_extraction(d, m_win, m_step, s_win, s_step) features.append(f) print(features[ ].shape, features[ ].shape) f1 = np.array([features[ ][:, fn.index( )], features[ ][:, fn.index( )]]) f2 = np.array([features[ ][:, fn.index( )], features[ ][:, fn.index( )]]) plots = [go.Scatter(x=f1[ , :], y=f1[ , :], name=class_names[ ], mode= ), go.Scatter(x=f2[ , :], y=f2[ , :], name=class_names[ ], mode= )] mylayout = go.Layout(xaxis=dict(title= ), yaxis=dict(title= )) plotly.offline.iplot(go.Figure(data=plots, layout=mylayout)) # Example4: plot 2 features for 10 2-second samples # from classical and 10 from metal music from import as import import as import as import "data/music/classical" "data/music/metal" for in 1 1 0.1 0.05 # segment-level feature extraction: for in # get feature matrix for each directory (class) # (each element of the features list contains a # (samples x segment features) = (10 x 138) feature matrix) 0 1 # select 2 features and create feature matrices for the two classes: 0 'spectral_centroid_mean' 0 'energy_entropy_mean' 1 'spectral_centroid_mean' 1 'energy_entropy_mean' # plot 2D features 0 1 0 'markers' 0 1 1 'markers' "spectral_centroid_mean" "energy_entropy_mean" Este ejemplo traza el tamaño de las matrices de características para las dos clases (10 x 138 como se explicó anteriormente) y devuelve la siguiente gráfica de las distribuciones para las dos características seleccionadas (media del centroide espectral y media de la entropía de energía): Se puede ver que las dos características pueden discriminar entre las dos clases con una precisión muy alta (solo una muestra de canción clásica se clasificará incorrectamente con un clasificador lineal simple). En particular, la media de los valores del centroide espectral tiene valores más altos para las muestras de metal, mientras que la media de la entropía de energía tiene valores más altos para las muestras de entropía de energía. Por supuesto, esto es solo una pequeña demostración de una tarea muy simple y con pocas muestras. Como veremos en la siguiente Sección, la clasificación basada en características de audio no siempre es fácil y requiere más de dos características... Clasificación de audio: entrenar el clasificador de audio Habiendo visto cómo extraer vectores de características de audio por cuadro a corto plazo, segmento y para grabaciones completas, ahora podemos proceder a construir modelos supervisados para tareas de clasificación particulares. Todo lo que necesitamos es un conjunto de archivos de audio y las respectivas etiquetas de clase. asume que los archivos de audio están organizados en carpetas y cada carpeta representa una clase de audio diferente. pyAudioAnalysis En el ejemplo de la Sección anterior, hemos visto cómo se diferencian dos características para dos clases de géneros musicales, a partir de respectivos archivos WAV organizados en dos carpetas. muestra cómo se pueden usar las mismas características para entrenar un clasificador SVM simple: cada punto de una cuadrícula en el espacio de características 2D se clasifica luego en cualquiera de las dos clases. Esta es una forma de visualizar la del clasificador. El ejemplo 5 superficie de decisión pyAudioAnalysis MidTermFeatures aF os numpy np sklearn.svm SVC plotly.graph_objs go plotly dirs = [ , ] class_names = [os.path.basename(d) d dirs] m_win, m_step, s_win, s_step = , , , features = [] d dirs: f, files, fn = aF.directory_feature_extraction(d, m_win, m_step, s_win, s_step) features.append(f) f1 = np.array([features[ ][:, fn.index( )], features[ ][:, fn.index( )]]) f2 = np.array([features[ ][:, fn.index( )], features[ ][:, fn.index( )]]) p1 = go.Scatter(x=f1[ , :], y=f1[ , :], name=class_names[ ], marker=dict(size= ,color= ), mode= ) p2 = go.Scatter(x=f2[ , :], y=f2[ , :], name=class_names[ ], marker=dict(size= ,color= ), mode= ) mylayout = go.Layout(xaxis=dict(title= ), yaxis=dict(title= )) y = np.concatenate((np.zeros(f1.shape[ ]), np.ones(f2.shape[ ]))) f = np.concatenate((f1.T, f2.T), axis = ) cl = SVC(kernel= , C= ) cl.fit(f, y) x_ = np.arange(f[:, ].min(), f[:, ].max(), ) y_ = np.arange(f[:, ].min(), f[:, ].max(), ) xx, yy = np.meshgrid(x_, y_) Z = cl.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape) / cs = go.Heatmap(x=x_, y=y_, z=Z, showscale= , colorscale= [[ , ], [ , ]]) mylayout = go.Layout(xaxis=dict(title= ), yaxis=dict(title= )) plotly.offline.iplot(go.Figure(data=[p1, p2, cs], layout=mylayout)) # Example5: plot 2 features for 10 2-second samples # from classical and 10 from metal music. # also train an SVM classifier and draw the respective # decision surfaces from import as import import as from import import as import "data/music/classical" "data/music/metal" for in 1 1 0.1 0.05 # segment-level feature extraction: for in # get feature matrix for each directory (class) # select 2 features and create feature matrices for the two classes: 0 'spectral_centroid_mean' 0 'energy_entropy_mean' 1 'spectral_centroid_mean' 1 'energy_entropy_mean' # plot 2D features 0 1 0 10 'rgba(255, 182, 193, .9)' 'markers' 0 1 1 10 'rgba(100, 100, 220, .9)' 'markers' "spectral_centroid_mean" "energy_entropy_mean" 1 1 0 # train the svm classifier 'rbf' 20 # apply the trained model on the points of a grid 0 0 0.002 1 1 0.002 2 # and visualize the grid on the same plot (decision surfaces) False 0 'rgba(255, 182, 193, .3)' 1 'rgba(100, 100, 220, .3)' "spectral_centroid_mean" "energy_entropy_mean" resultados en: En los dos ejemplos anteriores (4 y 5), las matrices f1 y f2 se pueden usar para nuestra tarea de clasificación, como en cualquier otro caso cuando usamos y la mayoría de las bibliotecas similares de ML: se requiere una matriz X de vectores de características (esto se puede generar fusionando f1 y f2 como se muestra en el Ejemplo 5), junto con una matriz y del mismo número de filas con X, correspondientes a los valores objetivo (las etiquetas de clase). scikit-learn Luego, como con cualquier otra tarea de clasificación, X se puede dividir en entrenamiento y prueba (usando submuestreo aleatorio, validación cruzada o dejar uno fuera) y para cada división de entrenamiento/prueba una métrica de evaluación (como F1, Recall , Precisión, Exactitud o incluso toda la matriz de confusión). Finalmente, informamos la métrica de evaluación general, como el promedio entre todas las divisiones de entrenamiento/prueba (según el método). Este procedimiento se puede seguir como se describe en muchos tutoriales (en mi opinión, se mejor en la página web de scikit-learn), tan pronto como tengamos las funciones de audio, como se describe en los ejemplos anteriores. muestra Alternativamente, proporciona una funcionalidad envuelta que incluye tanto la extracción de características como el entrenamiento del clasificador. esto se hace en función, que pyAudioAnalysis audioTrainTest.extract_features_and_train() (a) función de primeras llamadas que llama , para extraer matrices de características para todas las carpetas dadas de archivos de audio (asumiendo que cada carpeta corresponde a una clase) MidTermFeatures.multi_directory_feature_extraction() , MidTermFeatures.directory_feature_extraction() (b) genera matrices X e y, también conocidas como matriz de características para la tarea de clasificación y las respectivas etiquetas de clase. (c) evalúa el clasificador para diferentes parámetros (por ejemplo, C si se seleccionan clasificadores SVM) (d) devuelve los resultados de evaluación impresos y guarda el mejor modelo en un archivo binario (para que lo use otra función para realizar pruebas, como se muestra más adelante) El siguiente ejemplo entrena un clasificador SVM para la tarea de clasificación de música clásica/metal: pyAudioAnalysis.audioTrainTest extract_features_and_train mt, st = , dirs = [ , ] extract_features_and_train(dirs, mt, mt, st, st, , ) # Example6: use pyAudioAnalysis wrapper # to extract feature and train SVM classifier # for 20 music (10 classical/10 metal) song samples from import 1.0 0.05 "data/music/classical" "data/music/metal" "svm_rbf" "svm_classical_metal" Los resultados finales están aquí: classical metal OVERALL C PRE REC f1 PRE REC f1 ACC f1 best f1 best Acc Confusion Matrix: cla met cla met Selected params: 0.001 79.4 81.0 80.2 80.6 79.0 79.8 80.0 80.0 0.010 77.2 78.0 77.6 77.8 77.0 77.4 77.5 77.5 0.500 76.5 75.0 75.8 75.5 77.0 76.2 76.0 76.0 1.000 88.2 75.0 81.1 78.3 90.0 83.7 82.5 82.4 5.000 100.0 83.0 90.7 85.5 100.0 92.2 91.5 91.4 10.000 100.0 78.0 87.6 82.0 100.0 90.1 89.0 88.9 20.000 100.0 75.0 85.7 80.0 100.0 88.9 87.5 87.3 41.50 8.50 0.00 50.00 5.00000 La matriz de confusión general para el mejor parámetro C (en ese caso C=5), indica que existe (en promedio para todos los experimentos de submuestreo) casi un 9% de probabilidad de que un segmento clásico se clasifique como metal (que, dicho sea de paso, , tiene sentido si recordamos los diagramas de distribución de características que hemos visto anteriormente). es un contenedor que (a) lee todos los archivos de audio organizados en una lista de carpetas y extrae estadísticas de características promediadas a largo plazo (b) luego un clasificador asumiendo que los nombres de las carpetas representan clases de audio. audioTrainTest.extract_features_and_train() de pyAudioAnalysis lib, entrena El modelo entrenado se guardará en (último argumento de la función), junto con los parámetros de extracción de características (tamaños y pasos de ventana a corto plazo y de segmento). Tenga en cuenta que también se crea otro archivo llamado , que almacena los parámetros de normalización, es decir, la media y el estándar utilizados para normalizar las funciones de audio antes del entrenamiento y la prueba. Finalmente, aparte de las SVM, el contenedor es compatible con la mayoría de los clasificadores de scikit-learn, como árboles de decisión y aumento de gradiente. svm_classical_metal svm_classical_metalMEANS Clasificación de audio: aplicar el clasificador de audio Por lo tanto, hemos entrenado un clasificador de audio para distinguir entre dos clases de audio (clásico y metal) en función de los promedios de las estadísticas de características, como se describió anteriormente. Ahora veamos cómo podemos usar el modelo entrenado para la clase de un archivo de audio . Con este fin, vamos a utilizar pyAudioAnalysis' como se muestra en : predecir desconocido audioTrainTest.file_classification() el Ejemplo 7 pyAudioAnalysis audioTrainTest aT files_to_test = [ , , ] f files_to_test: print( ) c, p, p_nam = aT.file_classification(f, , ) print( ) print( ) print() # Example7: use trained model from Example6 # to classify an unknown sample (song) from import as "data/music/test/classical.00095.au.wav" "data/music/test/metal.00004.au.wav" "data/music/test/rock.00037.au.wav" for in f' :' {f} "svm_classical_metal" "svm_rbf" f'P( = )' {p_nam[ ]} 0 {p[ ]} 0 f'P( = )' {p_nam[ ]} 1 {p[ ]} 1 resultados en: data/music/test/classical .au.wav: P(classical= ) P(metal= ) data/music/test/metal .au.wav: P(classical= ) P(metal= ) data/music/test/rock .au.wav: P(classical= ) P(metal= ) .00095 0.6365171558566964 0.3634828441433037 .00004 0.1743387880715576 0.8256612119284426 .00037 0.2757302369241449 0.7242697630758552 Podemos ver que le hemos pedido al clasificador que prediga para tres archivos. El primero fue un segmento de música clásica (obviamente, no se usó en el conjunto de datos de entrenamiento) y, de hecho, la parte posterior estimada de la música clásica fue más alta que la del metal. De igual manera, probamos contra un segmento de metal y se clasificó como metal con un posterior del 86%. Finalmente, el tercer archivo de prueba no pertenece a las dos clases (es un segmento de una canción de rock), sin embargo, el resultado tiene sentido ya que se clasifica en la clase más cercana. audioTrainTest.file_classification() obtiene el modelo entrenado y la ruta de un archivo de audio desconocido y presenta extracción y predicción del clasificador para el archivo desconocido, devolviendo la clase ganadora (prevista), las clases posteriores y los nombres de las clases respectivas. El ejemplo anterior mostró cómo podemos aplicar el clasificador de audio entrenado a un archivo de audio desconocido para predecir su etiqueta de audio. Además de eso, pyAudioAnalysis proporciona la función , que acepta una lista de carpetas, asumiendo que sus nombres base son nombres de clase (como hacemos durante el entrenamiento), y aplica de forma repetitiva un clasificador previamente entrenado en los archivos de audio de cada carpeta. Al final, genera métricas de rendimiento como la matriz de confusión y las curvas ROC: audioTrainTest.evaluate_model_for_folders() pyAudioAnalysis audioTrainTest aT aT.evaluate_model_for_folders([ , ], , , ) # Example8: use trained model from Example6 # to classify audio files organized in folders # and evaluate the predictions, assuming that # foldernames = classes names as during training from import as "data/music/test/classical" "data/music/test/metal" "svm_classical_metal" "svm_rbf" "classical" da como resultado las siguientes cifras de la matriz de confusión, precisión/recuperación/f1 por clase, y curva de precisión/recuperación y curva ROC para una "clase de interés" (aquí hemos proporcionado clásica). Tenga en cuenta que las subparcelas 3 y 4 evalúan el clasificador como un de la clase "clásica" (último argumento). Por ejemplo, el último gráfico muestra la tasa de verdaderos positivos frente a la tasa de falsos positivos, y esto se logra simulando el umbral de la parte posterior de la clase de interés (clásica): a medida que aumenta el umbral de probabilidad, aumentan las tasas de verdaderos positivos y falsos negativos. , la pregunta es: ¿qué tan "empinado" es el aumento de la tasa positiva verdadera? Puede encontrar más información sobre la curva ROC . La curva de precisión/recuperación es equivalente a ROC, pero muestra ambas métricas en el mismo gráfico en el eje y para diferentes umbrales de la parte posterior que se muestra en el eje x (más información ). detector aquí aquí En nuestro ejemplo, podemos ver que para un umbral de probabilidad de, digamos, 0.6 podemos tener una Precisión del 100 % con alrededor del 80 % de Recall para clásico: esto significa que todos los archivos detectados serán de hecho clásicos, mientras que estaremos "perdiendo" casi 1 de cada 5 canciones "clásicas" como metal. Otra nota importante aquí es que . no hay un "mejor" punto de operación del clasificador, eso depende de la aplicación individual Regresión de audio: predicción de valores objetivo continuos La regresión es la tarea de entrenar una función de mapeo desde un espacio de características a una variable objetivo continua (en lugar de una clase discreta). Algunas aplicaciones de regresión de señales de audio incluyen: reconocimiento de emociones de voz y/o música usando clases no discretas (emoción y excitación) y estimación de atributos suaves de la música (por ejemplo, para detectar la "bailabilidad" de Spotify). En este artículo, demostramos cómo se puede usar la regresión para detectar el tono de un segmento de canto coral, sin usar ningún enfoque de procesamiento de señales (por ejemplo, el método de autocorrelación). Con este fin, hemos utilizado parte del , que es un conjunto de grabaciones con las respectivas anotaciones de . Aquí, hemos seleccionado usar este conjunto de datos para producir anotaciones de tono a nivel de segmento: dividimos las grabaciones de canto en segmentos pequeños (0,5 segundos) y para cada segmento, calculamos la media y la desviación estándar del tono (que proporciona el conjunto de datos). ). Estas dos métricas son f0_mean y f0_std respectivamente y son los dos valores de regresión objetivo que se muestran en el siguiente código. A continuación puede escuchar una muestra de 0,5 segundos con una f0 baja y una desviación de f0 baja: Choral Signing Dataset acapella tono y una muestra de 0,5 segundos con una f0 alta y una desviación de f0 alta: Obtener segmentos de 0,5 segundos del de señas corales conduce a miles de muestras, pero para fines de demostración de este artículo, hemos utilizado alrededor de 120 muestras de entrenamiento y 120 de prueba, disponibles en y del repositorio de github. Nuevamente, para demostrar el entrenamiento y las pruebas del modelo de regresión, estamos usando que trata la segmentación de audio de la siguiente manera: Conjunto de datos data/regression/f0/segments_train data/regression/f0/segments_train folders pyAudioAnalysis, dada una ruta que contiene archivos de audio y un conjunto de archivos <tarea_regresión>.csv con el formato <nombre_archivo_audio>, <valor> entrena un modelo de regresión para cada archivo <regression_task>.csv pyAudioAnalysis En nuestro ejemplo, contiene 120 archivos WAV y dos archivos CSV de 120 líneas llamados f0.csv y f0_std.csv. Cada CSV corresponde a una tarea de regresión separada y cada línea del CSV corresponde a la verdad básica del archivo de audio respectivo. Para entrenar, evaluar y guardar los dos modelos de regresión para nuestro ejemplo, se usa el siguiente código: data/regression/f0/segments_train pyAudioAnalysis audioTrainTest aT aT.feature_extraction_train_regression( , , , , , , , ) # Example9: # Train two linear SVM regression models # that map song segments to pitch and pitch deviation # The following function searches for .csv files in the # input folder. For each csv of the format <filename>,<value> # a separate regresion model is trained from import as "data/regression/f0/segments_train" 0.5 0.5 0.05 0.05 "svm" "singing" False Ya que contiene dos CSV, a saber y , el código anterior da como resultado dos modelos: y ( El prefijo se proporciona como un séptimo argumento en la función anterior y se usa para todos los modelos entrenados). data/regression/f0/segments_train f0.csv f0_std.csv singing_f0 singing_f0_std singing Asimismo, este es el resultado del proceso de evaluación ejecutado internamente por la función: audioTrainTest.feature_extraction_train_regression() Analyzing file : data/regression/f0/segments_train/CSD_ER_alto_1.wav_segments_263 .wav Analyzing file : data/regression/f0/segments_train/CSD_ER_alto_1.wav_segments_264 .wav Analyzing file : data/regression/f0/segments_train/CSD_ER_alto_1.wav_segments_301 .wav Analyzing file : data/regression/f0/segments_train/CSD_ER_alto_1.wav_segments_328 .wav Analyzing file : data/regression/f0/segments_train/CSD_ER_alto_1.wav_segments_331 .wav ... ... ... Analyzing file : data/regression/f0/segments_train/CSD_ND_alto_4.wav_segments_383 .wav Analyzing file : data/regression/f0/segments_train/CSD_ND_alto_4.wav_segments_394 .wav Feature extraction complexity ratio: x realtime Regression task f0_std Param MSE T-MSE R-MSE best Selected params: Regression task f0 Param MSE T-MSE R-MSE best Selected params: 1 of 120 .1595 2 of 120 .957 3 of 120 .632 4 of 120 .748 5 of 120 .2835 119 of 120 .483 120 of 120 .315 44.7 0.0010 736.98 10.46 661.43 0.0050 585.38 9.64 573.52 0.0100 522.73 9.17 539.87 0.0500 529.10 7.41 657.36 0.1000 379.13 6.73 541.03 0.2500 361.75 5.09 585.60 0.5000 323.20 3.88 522.12 1.0000 386.30 2.58 590.08 5.0000 782.14 0.99 548.65 10.0000 1140.95 0.47 529.20 0.50000 0.0010 3103.83 44.65 3121.97 0.0050 2772.07 41.38 3098.40 0.0100 2293.79 37.57 2935.42 0.0500 1206.49 19.69 2999.49 0.1000 1012.29 13.94 3115.49 0.2500 839.82 8.64 3147.30 0.5000 758.04 5.62 2917.62 1.0000 689.12 3.53 3087.71 5.0000 892.52 1.07 3061.10 10.0000 1158.60 0.47 2889.27 1.00000 La primera columna de los resultados anteriores representa el parámetro del clasificador evaluado durante el experimento. La segunda columna es el error cuadrático medio (MSE) del parámetro estimado (f0_std y f0 para los dos modelos), medido en los datos de la prueba interna (validación) de cada experimento. La 3.ª columna muestra el MSE de entrenamiento y la 4.ª columna muestra una estimación del modelo aleatorio (que se utilizará como métrica de referencia). Podemos ver que el objetivo f0_std es mucho más difícil de estimar a través de un modelo de regresión: el mejor MSE logrado (320) es ligeramente mejor que el MSE aleatorio promedio (alrededor de 550). Por otro lado, para el objetivo f0, el modelo de regresión entrenado logra un MSE de 680 con un error de referencia de alrededor de 3000. En otras palabras, el modelo logra un aumento del rendimiento de x5 en relación con el modelo aleatorio, mientras que para el f0_std este aumento es alrededor de x1.5. Ahora, una vez que los dos modelos de regresión se entrenan, evalúan y guardan, podemos usarlos para asignar cualquier segmento de audio a f0 o f0_std. demuestra cómo hacer esto usando : Example10 audioTrainTest.file_regression() glob csv os numpy np plotly.graph_objs go plotly pyAudioAnalysis audioTrainTest aT wav_files_to_test = glob.glob( ) ground_truths = {} open( , ) file: reader = csv.reader(file, delimiter = ) row reader: ground_truths[row[ ]] = float(row[ ]) estimated_val, gt_val = [], [] w wav_files_to_test: values, tasks = aT.file_regression(w, , ) os.path.basename(w) ground_truths: estimated_val.append(values[tasks.index( )]) gt_val.append(ground_truths[os.path.basename(w)]) mse = ((np.array(estimated_val) - np.array(gt_val))** ).mean() print( ) p = go.Scatter(x=gt_val, y=estimated_val, mode= ) mylayout = go.Layout(xaxis=dict(title= ), yaxis=dict(title= ), showlegend= ) plotly.offline.iplot(go.Figure(data=[p, go.Scatter(x=[min(gt_val+ estimated_val), max(gt_val+ estimated_val)], y=[min(gt_val+ estimated_val), max(gt_val+ estimated_val)])], layout=mylayout)) # Example10 # load trained regression model for f0 and apply it to a folder # of WAV files and evaluate (use csv file with ground truths) import import import import as import as import from import as # read all files in testing folder: "data/regression/f0/segments_test/*.wav" with 'data/regression/f0/segments_test/f0.csv' 'r' as ',' for in 0 1 for in # for each audio file # get the estimates for all regression models starting with "singing" "singing" "svm" # check if there is ground truth available for the current file if in # ... and append ground truth and estimated values # for the f0 task 'f0' # compute mean square error: 2 f'Testing MSE= ' {mse} # plot real vs predicted results 'markers' "f0 real" "f0 predicted" False En este ejemplo, demostramos cómo se puede utilizar para un conjunto de archivos de un conjunto de datos de prueba. Tenga en cuenta que esta función devuelve decisiones y nombres de tareas para todas las tareas de regresión disponibles que comienzan con el prefijo proporcionado (en nuestro caso, "cantando"). Los resultados se muestran a continuación: podemos ver que los valores real y predicho están bastante cerca para la tarea f0. audioTrainTest.file_regression() MSE=492.7141430351564 Testing audioTrainTest.feature_extraction_train_regression() lee una carpeta de archivos WAV y asume que cada CSV de formato (<ruta>,<valor>) es un archivo de regresión real. Luego extrae características de audio, entrena y guarda el número respectivo de modelos usando un prefijo (proporcionado también como argumento). audioTrainTest.file_regression() lee los modelos guardados y devuelve resultados de regresión previstos para todas las tareas. Acerca de la segmentación de audio Hasta ahora, hemos visto cómo entrenar modelos supervisados que asignan estadísticas de características de audio a nivel de segmento a etiquetas de clase (clasificación de audio) o objetivos de valor real (regresión de audio). Además, hemos visto cómo utilizar estos modelos para predecir la etiqueta de un archivo de audio desconocido, por ejemplo, una expresión verbal o una canción completa o un segmento de una canción. En todos estos casos, la suposición seguida fue que las señales de audio desconocidas . Por ejemplo, una canción pertenece a un género particular, un segmento de canto tiene un valor de tono particular y una expresión verbal tiene una emoción particular. Sin embargo, en las aplicaciones del mundo real, hay muchos casos en los que las señales de audio no son segmentos de contenido homogéneo, sino flujos de audio complejos que contienen muchos segmentos sucesivos de diferentes etiquetas de contenido. Una grabación de un diálogo del mundo real, por ejemplo, es una secuencia de etiquetas de identidades o emociones del hablante. pertenecían a una sola etiqueta Las grabaciones del mundo real no son segmentos de contenido homogéneo sino secuencias de segmentos de diferentes etiquetas. Por esa razón, la de audio es un paso importante del análisis de audio y se trata de segmentar una grabación de audio larga en una secuencia de segmentos que son de contenido homogéneo. La definición de homogeneidad es relativa al dominio de la aplicación: si, por ejemplo, estamos interesados en el reconocimiento del hablante, un segmento se considera homogéneo si pertenece al mismo hablante. segmentación Segmentación de audio: utilizando modelos preentrenados (supervisados) Los algoritmos de segmentación de hTAudio se pueden dividir en dos categorías: (a) supervisados y (b) no supervisados o semisupervisados. La segmentación supervisada se basa en un modelo de segmento preentrenado que puede clasificar segmentos homogéneos. En esta sección, mostraremos cómo lograr la segmentación utilizando un segmento de tamaño fijo simple y un modelo previamente entrenado. Supongamos que ha entrenado un modelo de segmento para distinguir entre las clases S y M. El método de segmentación que se presenta en esta sección es tan simple como eso: divida la grabación de audio en segmentos de tamaño fijo que no se superpongan con la misma longitud que el utilizado para entrenar el modelo. Luego clasifique cada segmento de tamaño fijo de la transmisión de audio usando el modelo entrenado y finalmente combine los segmentos sucesivos que contienen la misma etiqueta de clase. Este proceso se ilustra en el siguiente diagrama. En el Ejemplo 6, habíamos entrenado un modelo que clasifica segmentos de música desconocidos en "metal" y "clásico" (el modelo se guardó en el archivo ). Usemos este modelo para segmentar una grabación de 30 segundos que contiene partes de metal y clásicas (no superpuestas). Esta grabación se almacena en del código del artículo. También, contiene el respectivo archivo de anotación de el formato <start_segment_sec>\t<end_segment_sec>\t<segment_label>. Este es el archivo de verdad del terreno: svm_classical_metal data/music/metal_classical_mix.wav data/music/metal_classical_mix.segment verdad en tierra con classical metal classical metal 0 7.5 7.5 15 15 19 19 29 La funcionalidad de segmentación supervisada de ventana fija se implementa en función , como se muestra en : audioSegmentation.mid_term_file_classification() el Ejemplo 11 pyAudioAnalysis.audioSegmentation mid_term_file_classification, labels_to_segments pyAudioAnalysis.audioTrainTest load_model labels, class_names, _, _ = mid_term_file_classification( , , , , ) print( ) il, l enumerate(labels): print( ) cl, m, s, m_classes, mt_win, mt_step, s_win, s_step, c_beat = load_model( ) print( ) segs, c = labels_to_segments(labels, mt_step) iS, seg enumerate(segs): print( ) # Example 11 # Supervised audio segmentation example: # - Apply model "svm_classical_metal" to achieve fix-sized, supervised audio segmentation # on file data/music/metal_classical_mix.wav # - Function audioSegmentation.mid_term_file_classification() uses pretrained model and applies # the mid-term step that has been used when training the model (1 sec in our case as shown in Example6) # - data/music/metal_classical_mix.segments contains the ground truth of the audio file from import from import "data/music/metal_classical_mix.wav" "svm_classical_metal" "svm_rbf" True "data/music/metal_classical_mix.segments" "\nFix-sized segments:" for in f'fix-sized segment : ' {il} {class_names[int(l)]} # load the parameters of the model (actually we just want the mt_step here): "svm_classical_metal" # print "merged" segments (use labels_to_segments()) "\nSegments:" for in f'segment sec - sec: ' {iS} {seg[ ]} 0 {seg[ ]} 1 {class_names[int(c[iS])]} devuelve una lista de identificadores de etiqueta (uno para cada ventana de segmento de tamaño fijo), una lista de nombres de clase y la matriz de precisión y confusión (si también se proporciona la verdad básica, como en el ejemplo anterior). los list corresponde a segmentos de tamaño fijo de longitud igual al paso de segmento utilizado durante el entrenamiento del modelo (1 segundo en el ejemplo anterior, según el Ejemplo 6). Por eso usamos , para cargar la ventana del segmento directamente desde el archivo del modelo. También, usamos para generar la lista de segmentos finales, basada en la ruta de fusión simple (es decir, concatenar segmentos sucesivos de 1 segundo que tienen la misma etiqueta). audioSegmentation.mid_term_file_classification() labels audioTrainTest.load_model() audioSegmentation.labels_to_segments() El resultado del código anterior es el siguiente (el rojo corresponde a la realidad del terreno y el azul a las etiquetas de los segmentos previstos): Overall Accuracy: Fix-sized segments: fix-sized segment : classical fix-sized segment : classical fix-sized segment : classical fix-sized segment : classical fix-sized segment : classical fix-sized segment : classical fix-sized segment : classical fix-sized segment : metal fix-sized segment : metal fix-sized segment : metal fix-sized segment : metal fix-sized segment : metal fix-sized segment : classical fix-sized segment : metal fix-sized segment : metal fix-sized segment : classical fix-sized segment : classical fix-sized segment : classical fix-sized segment : metal fix-sized segment : metal fix-sized segment : classical fix-sized segment : metal fix-sized segment : classical fix-sized segment : classical fix-sized segment : metal fix-sized segment : metal fix-sized segment : metal fix-sized segment : metal fix-sized segment : metal fix-sized segment : metal Segments: segment sec - sec: classical segment sec - sec: metal segment sec - sec: classical segment sec - sec: metal segment sec - sec: classical segment sec - sec: metal segment sec - sec: classical segment sec - sec: metal segment sec - sec: classical segment sec - sec: metal 0.79 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 0.0 7.0 1 7.0 12.0 2 12.0 13.0 3 13.0 15.0 4 15.0 18.0 5 18.0 20.0 6 20.0 21.0 7 21.0 22.0 8 22.0 24.0 9 24.0 29.0 devuelve la información más "compacta" y útil para el "usuario" final. Además, tenga en cuenta que los de segmentación son: audioSegmentation.labels_to_segments() errores debido a de clasificación del clasificador de (p. ej., los segmentos 22 y 23 se clasifican erróneamente como clásicos cuando su verdadera etiqueta es metal o (a) errores segmentos debido a problemas de : por ejemplo, de acuerdo con la realidad básica, el primer segmento de música clásica termina en 7,5 segundos, mientras que nuestro modelo se aplica cada 1 segundo, por lo que lo mejor que logrará esta metodología de ventana fija es reconocer música clásica hasta 7 u 8 seg. Obviamente, esto se puede manejar a través de un paso más pequeño en la ventana del segmento (es decir, introduciendo una superposición de segmento), sin embargo, esto será con un aumento significativo en las demandas computacionales (se realizarán más predicciones a nivel de segmento). (b) resolución de tiempo Segmentación de audio: sin supervisión Hemos visto cómo, dado un modelo de segmento de audio preentrenado, podemos dividir una grabación de audio en segmentos de contenido homogéneo. Sin embargo, en muchos casos, no somos conscientes del problema de clasificación exacto, o no tenemos los datos para entrenar dicho clasificador. Dichos casos requieren soluciones no supervisadas o semisupervisadas, como se muestra en los casos de uso a continuación: Segmentación musical La extracción de partes estructurales de una pista de música es un caso de uso típico en el que se puede utilizar el análisis de audio no supervisado. Dado que obviamente es bastante difícil tener un clasificador que distinga entre partes de canciones, podemos responder a la pregunta: En el siguiente ejemplo, "Billie Jean" de M. Jackson se utiliza como entrada para el proceso de extracción de características a nivel de segmento descrito anteriormente y se aplica un agrupamiento simple de k-medias en las secuencias de vectores de características resultantes. Luego, los segmentos de cada grupo se concatenan en una grabación artificial y se guardan en archivos de audio. Cada "grabación de grupo" artificial muestra cómo se pueden agrupar las partes de la canción y si esta agrupación tiene algún sentido en términos de estructura musical. El código se muestra en : ¿puedes agrupar segmentos de canciones para que los segmentos del mismo grupo suenen como si pertenecieran a la misma parte de una canción? el Ejemplo 12 os, sklearn.cluster pyAudioAnalysis.MidTermFeatures mid_feature_extraction mT pyAudioAnalysis.audioBasicIO read_audio_file, stereo_to_mono pyAudioAnalysis.audioSegmentation labels_to_segments pyAudioAnalysis.audioTrainTest normalize_features numpy np scipy.io.wavfile wavfile IPython input_file = fs, x = read_audio_file(input_file) mt_size, mt_step, st_win = , , [mt_feats, st_feats, _] = mT(x, fs, mt_size * fs, mt_step * fs, round(fs * st_win), round(fs * st_win * )) (mt_feats_norm, MEAN, STD) = normalize_features([mt_feats.T]) mt_feats_norm = mt_feats_norm[ ].T n_clusters = x_clusters = [np.zeros((fs, )) i range(n_clusters)] k_means = sklearn.cluster.KMeans(n_clusters=n_clusters) k_means.fit(mt_feats_norm.T) cls = k_means.labels_ segs, c = labels_to_segments(cls, mt_step) sp range(n_clusters): count_cl = i range(len(c)): c[i] == sp segs[i, ]-segs[i, ] > : count_cl += cur_x = x[int(segs[i, ] * fs): int(segs[i, ] * fs)] x_clusters[sp] = np.append(x_clusters[sp], cur_x) x_clusters[sp] = np.append(x_clusters[sp], np.zeros((fs,))) print( ) wavfile.write( , fs, np.int16(x_clusters[sp])) IPython.display.display(IPython.display.Audio( )) # Example 12: Unsupervised Music Segmentation # # This example groups of song segments to clusters of similar content import from import as from import from import from import import as import as import # read signal and get normalized segment feature statistics: "data/music/billie_jean.wav" 5 0.5 0.1 0.5 0 # perform clustering 5 for in # save clusters to concatenated wav files # convert flags to segment limits for in 0 for in # for each segment in each cluster (>2 secs long) if and 1 0 2 1 # get the signal and append it to the cluster's signal (followed by some silence) 0 1 # write cluster's signal into a WAV file f'cluster : segments sec total dur' {sp} {count_cl} {len(x_clusters[sp])/float(fs)} f'cluster_ .wav' {sp} f'cluster_ .wav' {sp} El código anterior guarda los sonidos de grupos artificiales en archivos WAV y también los muestra en un mini reproductor en el propio portátil, pero también he subido los sonidos de grupos a YouTube (no se me ocurrió una manera obvia de insertarlos en el artículo) . Entonces, escuchemos los grupos resultantes y veamos si corresponden a partes de canciones homogéneas: Este es claramente el de la canción, repetido dos veces (aunque la segunda vez es mucho más larga ya que incluye más repeticiones sucesivas y un pequeño solo) estribillo El grupo tiene un solo segmento que corresponde a la de la canción. 2 introducción Cluster es el de la canción. 3 pre-estribillo El grupo contiene segmentos de los de la canción (si excluye el segmento pequeño al principio). El quinto grupo no se muestra ya que solo incluía un segmento muy corto casi silencioso al comienzo de la canción. En todos los casos, los grupos representaban (con algunos errores, por supuesto) componentes estructurales de la canción, incluso usando este enfoque muy simple y sin hacer uso de ningún conocimiento supervisado "externo", aparte de características similares, pueden significar contenido musical similar. cuarto versos Los grupos de segmentos de canciones pueden corresponder a elementos estructurales de canciones si se utilizan las funciones de audio apropiadas. Por último, tenga en cuenta que ejecutar el código anterior puede dar como resultado el mismo agrupamiento pero con un orden diferente de ID de clúster (y, por lo tanto, orden en los archivos de audio resultantes). Esto probablemente se deba a la semilla aleatoria k-means. Diarización del hablante Esta es la tarea que, dada una grabación de voz desconocida, responde a la pregunta: "¿quién habla cuándo?". En aras de la simplicidad, supongamos que ya conocemos el número de hablantes en la grabación. ¿Cuál es la forma más sencilla de resolver esta tarea? Obviamente, primero extraiga características de audio a nivel de segmento y luego realice algún tipo de agrupación, con la esperanza de que las agrupaciones resultantes correspondan a las identificaciones de los altavoces. En el siguiente ejemplo (13), usamos exactamente la misma canalización que la que se siguió en el Ejemplo 12, donde agrupamos una canción en sus partes estructurales. Sólo hemos cambiado el tamaño de la ventana del segmento a 2 seg con un paso de 0,1 seg y una ventana de corto plazo más pequeña (50 mseg), ya que las señales de voz se caracterizan, en general, por cambios más rápidos en sus atributos principales, debido a la existencia de fonemas muy diferentes, algunos de los cuales duran apenas unos segundos (en cambio, la nota musical dura varios mseg, incluso en las músicas más rápidas). Entonces, el Ejemplo 13 usa la misma lógica de agrupamiento de vectores de funciones de audio. Esta vez, la señal de entrada es una señal de voz con 4 altavoces (esto se sabe de antemano), por lo que establecemos nuestro tamaño de grupo de kmeans en 4: os, sklearn.cluster pyAudioAnalysis.MidTermFeatures mid_feature_extraction mT pyAudioAnalysis.audioBasicIO read_audio_file, stereo_to_mono pyAudioAnalysis.audioSegmentation labels_to_segments pyAudioAnalysis.audioTrainTest normalize_features numpy np scipy.io.wavfile wavfile IPython input_file = fs, x = read_audio_file(input_file) mt_size, mt_step, st_win = , , [mt_feats, st_feats, _] = mT(x, fs, mt_size * fs, mt_step * fs, round(fs * st_win), round(fs * st_win * )) (mt_feats_norm, MEAN, STD) = normalize_features([mt_feats.T]) mt_feats_norm = mt_feats_norm[ ].T n_clusters = x_clusters = [np.zeros((fs, )) i range(n_clusters)] k_means = sklearn.cluster.KMeans(n_clusters=n_clusters) k_means.fit(mt_feats_norm.T) cls = k_means.labels_ segs, c = labels_to_segments(cls, mt_step) sp range(n_clusters): count_cl = i range(len(c)): c[i] == sp segs[i, ]-segs[i, ] > : count_cl += cur_x = x[int(segs[i, ] * fs): int(segs[i, ] * fs)] x_clusters[sp] = np.append(x_clusters[sp], cur_x) x_clusters[sp] = np.append(x_clusters[sp], np.zeros((fs,))) print( ) wavfile.write( , fs, np.int16(x_clusters[sp])) IPython.display.display(IPython.display.Audio( )) import from import as from import from import from import import as import as import # read signal and get normalized segment feature statistics: "data/diarization_example.wav" 2 0.1 0.05 0.5 0 # perform clustering 4 for in # save clusters to concatenated wav files # convert flags to segment limits for in 0 for in # for each segment in each cluster (>2 secs long) if and 1 0 2 1 # get the signal and append it to the cluster's signal (followed by some silence) 0 1 # write cluster's signal into a WAV file f'speaker : segments sec total dur' {sp} {count_cl} {len(x_clusters[sp])/float(fs)} f'diarization_cluster_ .wav' {sp} f'diarization_cluster_ .wav' {sp} Esta es la grabación inicial. Y estos son los 4 grupos resultantes (los resultados también se escriben en clips de audio en línea en el cuaderno de jupiter nuevamente): En el ejemplo anterior, la agrupación de altavoces (o diarización de altavoces, como solemos llamarla) tuvo bastante éxito con algunos errores al comienzo de los segmentos, principalmente debido a limitaciones de resolución de tiempo (se ha utilizado una ventana de 2 segundos). Por supuesto, este no es siempre el caso: la diarización de los altavoces es una tarea difícil, especialmente si (a) hay mucho ruido de fondo (b) el número de altavoces es desconocido de antemano (c) los altavoces no están equilibrados (por ejemplo, un altavoz habla el 60% del tiempo y otro hablante solo el 0,5% del tiempo). El código de este artículo se proporciona como un cuaderno de notas de Júpiter . en este repositorio de GitHub