{"id":1572,"date":"2017-11-29T17:01:23","date_gmt":"2017-11-29T16:01:23","guid":{"rendered":"http:\/\/blog.mozilla.org\/press-es\/?p=1572"},"modified":"2017-11-29T17:04:35","modified_gmt":"2017-11-29T16:04:35","slug":"un-viaje-a-una-tasa-de-error-de-palabra-de-menos-del-10","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/press-es\/2017\/11\/29\/un-viaje-a-una-tasa-de-error-de-palabra-de-menos-del-10\/","title":{"rendered":"Un viaje a una tasa de error de palabra de menos del 10%"},"content":{"rendered":"<p>En Mozilla creemos que las interfaces de voz ser\u00e1n una pieza clave en la interacci\u00f3n entre las personas y sus dispositivos en el futuro. Hoy nos complace anunciar el lanzamiento de <strong>nuestro <a href=\"https:\/\/github.com\/mozilla\/DeepSpeech\" target=\"_blank\" rel=\"noopener\"><u>modelo de reconocimiento de voz de c\u00f3digo abierto<\/u><\/a><\/strong> para que cualquiera pueda desarrollar experiencias comunicativas interesantes.<\/p>\n<p>El equipo de Machine Learning de Mozilla Research ha estado trabajando en <strong>un motor de reconocimiento de voz autom\u00e1tico de c\u00f3digo abierto<\/strong> basado en los trabajos Deep Speech (<u><a href=\"https:\/\/arxiv.org\/abs\/1412.5567\" target=\"_blank\" rel=\"noopener\">1<\/a>, <a href=\"https:\/\/arxiv.org\/abs\/1512.02595\" target=\"_blank\" rel=\"noopener\">2<\/a><\/u>) publicados por Baidu. Uno de los principales objetivos desde el principio fue lograr una tasa de error de menos del 10% en las transcripciones en Word. Hemos progresado mucho: <strong>nuestra tasa de error de palabra en las pruebas en LibriSpeech es del 6,5%, lo que no solo logra nuestro objetivo inicial, sino que nos acerca a un rendimiento humano<\/strong>.<\/p>\n<p>Este blogpost es una descripci\u00f3n general de los esfuerzos del equipo y finaliza con una explicaci\u00f3n m\u00e1s detallada de la \u00faltima pieza del rompecabezas: el decodificador de CTC.<\/p>\n<p><strong>La arquitectura<\/strong><\/p>\n<p>Deep Speech es una red neuronal recurrente profunda (<a href=\"https:\/\/en.wikipedia.org\/wiki\/Recurrent_neural_network\" target=\"_blank\" rel=\"noopener\">RNN<\/a>) a nivel de caracteres, entrenable y de extremo a extremo. En t\u00e9rminos m\u00e1s asequibles, es una red neuronal profunda con capas recurrentes que transforma las caracter\u00edsticas de audio en entradas y salidas de caracteres directamente: transcripciones de audio. Se puede entrenar usando un aprendizaje desde cero supervisado, sin ninguna &#8220;fuente de inteligencia&#8221; externa, como un conversor de grafema a fonema o de alineamiento forzado en la entrada.<\/p>\n<p><a href=\"https:\/\/blog.mozilla.org\/press-es\/files\/2017\/11\/deepspeech-diagram.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-1579\" src=\"https:\/\/blog.mozilla.org\/press-es\/files\/2017\/11\/deepspeech-diagram-600x600.gif\" alt=\"\" width=\"600\" height=\"600\" srcset=\"https:\/\/blog.mozilla.org\/press-es\/files\/2017\/11\/deepspeech-diagram-600x600.gif 600w, https:\/\/blog.mozilla.org\/press-es\/files\/2017\/11\/deepspeech-diagram-160x160.gif 160w, https:\/\/blog.mozilla.org\/press-es\/files\/2017\/11\/deepspeech-diagram-252x252.gif 252w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/a><\/p>\n<p>Esta animaci\u00f3n muestra c\u00f3mo fluyen los datos a trav\u00e9s de la red. En la pr\u00e1ctica, en lugar de procesar fragmentos de la entrada de audio de forma individual, procesamos todos los fragmentos a la vez.<\/p>\n<p>La red tiene cinco capas: la entrada se sirve de tres capas totalmente conectadas, seguidas de una capa RNN bidireccional y, finalmente, una capa totalmente conectada. Las capas completamente conectadas ocultas usan la activaci\u00f3n <a href=\"https:\/\/en.wikipedia.org\/wiki\/Rectifier_%28neural_networks%29\" target=\"_blank\" rel=\"noopener\"><u>ReLU<\/u><\/a>. La capa RNN usa celdas LSTM con activaci\u00f3n de tanh.<\/p>\n<p>El resultado de la red es una matriz de probabilidades de caracteres en el tiempo: en otras palabras, a cada paso en el tiempo la red genera una probabilidad para cada car\u00e1cter del alfabeto, que representa la probabilidad de que ese car\u00e1cter corresponda a lo que se dice en el audio en ese momento. La funci\u00f3n de <a href=\"http:\/\/www.cs.toronto.edu\/~graves\/icml_2006.pdf\" target=\"_blank\" rel=\"noopener\"><u>p\u00e9rdida de CTC<\/u><\/a>\u00a0 considera todas las alineaciones del audio y la transcripci\u00f3n al mismo tiempo, lo que nos permite maximizar la probabilidad de que se prediga la transcripci\u00f3n correcta sin preocuparse por la alineaci\u00f3n. Finalmente, lo probamos usando el <u><a href=\"https:\/\/arxiv.org\/abs\/1412.6980\" target=\"_blank\" rel=\"noopener\">optimizador Adam<\/a>.<\/u><\/p>\n<p><strong>Los datos<\/strong><\/p>\n<p>El aprendizaje supervisado requiere datos, montones y montones de ellos. Entrenar a un modelo como Deep Speech requiere miles de horas de audio etiquetado, y obtener y preparar esta informaci\u00f3n puede ser mucho trabajo, incluso m\u00e1s que implementar la red y la l\u00f3gica de entrenamiento.<\/p>\n<p>Comenzamos descargando corpus de voz disponibles gratuitamente como <a href=\"http:\/\/www-lium.univ-lemans.fr\/en\/content\/ted-lium-corpus\" target=\"_blank\" rel=\"noopener\"><u>TED-LIUM<\/u><\/a> y <a href=\"http:\/\/www.openslr.org\/12\/\" target=\"_blank\" rel=\"noopener\"><u>LibriSpeech<\/u><\/a>, as\u00ed como adquiriendo corpus de pago como <a href=\"https:\/\/catalog.ldc.upenn.edu\/LDC2004S13\" target=\"_blank\" rel=\"noopener\"><u>Fisher<\/u><\/a> y <a href=\"https:\/\/catalog.ldc.upenn.edu\/ldc97s62\" target=\"_blank\" rel=\"noopener\">Switchboard<\/a>. Escribimos importadores en Python para los diferentes conjuntos de datos que convierten los archivos de audio a WAV, dividimos el audio y limpiamos la transcripci\u00f3n de caracteres innecesarios como la puntuaci\u00f3n y los acentos. Finalmente, almacenamos los datos pre-procesados en archivos CSV que se pueden usar para introducir los datos en la red.<\/p>\n<p>El uso de corpus de voz existentes nos permiti\u00f3 comenzar a trabajar r\u00e1pidamente en el modelo. Pero para lograr excelentes resultados, necesit\u00e1bamos mucha m\u00e1s informaci\u00f3n. Tuvimos que ser creativos. Pensamos que tal vez este tipo de datos de voz ya existir\u00edan ah\u00ed fuera, en los archivos de la gente, as\u00ed que nos comunicamos con emisoras de radio y TV p\u00fablicas, departamentos de estudio de idiomas de universidades y, b\u00e1sicamente, con cualquier persona que pudiera haber etiquetado datos de voz para compartirlos. A trav\u00e9s de este esfuerzo, pudimos duplicar con creces la cantidad de datos para entrenamiento con los que trabajar, lo que ahora es suficiente para entrenar un modelo en ingl\u00e9s de alta calidad.<\/p>\n<p>Tener un corpus de voz de alta calidad a disposici\u00f3n del p\u00fablico no solo nos ayuda a avanzar en nuestro propio motor de reconocimiento de voz. Con el tiempo permitir\u00e1 una amplia innovaci\u00f3n, porque los desarrolladores, las start-ups y los investigadores podr\u00e1n entrenar y experimentar con diferentes arquitecturas y modelos para diferentes idiomas. Podr\u00eda ayudar a democratizar el acceso a Deep Learning para aquellos que no pueden pagar miles de horas de datos para entrenamiento (casi todo el mundo).<\/p>\n<p>Para construir un corpus de voz que fuera gratuito, de c\u00f3digo abierto y lo suficientemente grande como para crear productos significativos, trabajamos con el equipo Open Innovation de Mozilla y lanzamos el <a href=\"https:\/\/voice.mozilla.org\/\" target=\"_blank\" rel=\"noopener\"><u>proyecto Common Voice<\/u><\/a> para recopilar y validar las contribuciones verbales de voluntarios de todo el mundo. Hoy, el equipo lanza una gran colecci\u00f3n de datos de voz de <a href=\"https:\/\/creativecommons.org\/choose\/zero\/\" target=\"_blank\" rel=\"noopener\"><u>dominio p\u00fablico<\/u><\/a>. Obt\u00e9n m\u00e1s informaci\u00f3n sobre el lanzamiento en el blog<a href=\"https:\/\/medium.com\/mozilla-open-innovation\" target=\"_blank\" rel=\"noopener\"> <u>Open Innovation Medium<\/u>.<\/a><\/p>\n<p><strong>El hardware<\/strong><\/p>\n<p>Deep Speech tiene m\u00e1s de 120 millones de par\u00e1metros, y entrenar a un modelo de esta envergadura es una tarea muy costosa desde el punto de vista inform\u00e1tico: se necesitan muchas GPU si no quieres tener que esperar los resultados de por vida. Estudiamos el entrenamiento en la nube, pero no funcion\u00f3 en lo financiero: el hardware dedicado se paga por s\u00ed solo si se entrena mucho. Por el contrario, la nube es una buena forma de hacer exploraciones r\u00e1pidas de hiperpar\u00e1metros, as\u00ed que tenlo en cuenta.<\/p>\n<p>Comenzamos con una sola m\u00e1quina con cuatro GPU Titan X Pascal, y luego compramos otros dos servidores con 8 Titan XPs. Ejecutamos las dos m\u00e1quinas GPU de 8 como un cl\u00faster, y la m\u00e1quina GPU de 4 anterior se qued\u00f3 aparte para ejecutar experimentos m\u00e1s peque\u00f1os y probar cambios de c\u00f3digo que requieren m\u00e1s potencia de procesamiento que la que tienen nuestras m\u00e1quinas de desarrollo. Esta configuraci\u00f3n es bastante eficiente, y para nuestras jornadas de entrenamiento m\u00e1s largas podremos pasar de cero a un buen modelo en aproximadamente una semana.<\/p>\n<p>Configurar el entrenamiento distribuido con TensorFlow fue un proceso arduo. Aunque cuenta con las herramientas distribuidas de entrenamiento m\u00e1s desarrolladas en el marco del <em>Deep learning<\/em>, lograr que las cosas funcionen realmente sin fallos y aprovechar al m\u00e1ximo la potencia de procesamiento adicional es complicado. Nuestra configuraci\u00f3n actual funciona gracias a los incre\u00edbles esfuerzos de <a href=\"https:\/\/github.com\/tilmankamp\" target=\"_blank\" rel=\"noopener\"><u>Tilman Kamp<\/u><\/a>, quien soport\u00f3 largas batallas con TensorFlow, <a href=\"https:\/\/slurm.schedmd.com\" target=\"_blank\" rel=\"noopener\"><u>Slurm<\/u><\/a> e incluso con el kernel de Linux hasta que todo empez\u00f3 a funcionar.<\/p>\n<p><strong>Poni\u00e9ndolo todo junto<\/strong><\/p>\n<p>En este punto, tenemos dos trabajos para guiarnos, un modelo implementado basado en esos trabajos, los datos resultantes y el hardware necesario para el proceso de entrenamiento. Result\u00f3 que replicar los resultados de un trabajo no es tan simple. La gran mayor\u00eda de los trabajos no especifican todos los hiperpar\u00e1metros que utilizan, si es que especifican alguno. Esto significa que se debe dedicar mucho tiempo y energ\u00eda a realizar b\u00fasquedas de hiperpar\u00e1metros para encontrar un buen conjunto de valores. Nuestras pruebas iniciales con valores elegidos mediante una combinaci\u00f3n de aleatoriedad e intuici\u00f3n no fueron ni siquiera cercanas a las citadas en el documento, probablemente debido a peque\u00f1as diferencias en la arquitectura; por un lado, usamos celdas LSTM (<a href=\"https:\/\/en.wikipedia.org\/wiki\/Long_short-term_memory\" target=\"_blank\" rel=\"noopener\"><u>memoria a largo-corto plazo<\/u><\/a>) en lugar de celdas GRU (unidad recurrente cerrada). Pasamos mucho tiempo realizando b\u00fasquedas binarias en las tasas de abandono, redujimos la tasa de aprendizaje, cambiamos la forma en la que se iniciaban los pesos y tambi\u00e9n experimentamos con el tama\u00f1o de las capas ocultas. Todos estos cambios nos acercaron bastante a nuestro objetivo deseado de menos del 10% de la Tasa de Error de Palabras, pero no nos llevaron hasta all\u00ed.<\/p>\n<p>Una pieza que faltaba en nuestro c\u00f3digo supuso una optimizaci\u00f3n importante: la integraci\u00f3n de nuestro modelo de lenguaje en el decodificador. La forma en que funciona el decodificador CTC (Clasificaci\u00f3n Temporal de Conexiones) es tomando la matriz de probabilidad como si fuera un resultado del modelo y repas\u00e1ndolo, buscando la secuencia de texto m\u00e1s probable de acuerdo con la matriz de probabilidad. Si en el paso de tiempo 0 la letra &#8220;C&#8221; es la m\u00e1s probable, y en el paso de tiempo 1 la letra &#8220;A&#8221; es la m\u00e1s probable, y en el paso de tiempo 2 la letra &#8220;T&#8221; es la m\u00e1s probable, entonces la transcripci\u00f3n dada por el decodificador m\u00e1s simple posible ser\u00e1 &#8220;CAT&#8221;.<\/p>\n<p><a href=\"https:\/\/blog.mozilla.org\/press-es\/files\/2017\/11\/Gato.jpg\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-large wp-image-1580\" src=\"https:\/\/blog.mozilla.org\/press-es\/files\/2017\/11\/Gato-600x344.jpg\" alt=\"\" width=\"600\" height=\"344\" srcset=\"https:\/\/blog.mozilla.org\/press-es\/files\/2017\/11\/Gato-600x344.jpg 600w, https:\/\/blog.mozilla.org\/press-es\/files\/2017\/11\/Gato-252x144.jpg 252w, https:\/\/blog.mozilla.org\/press-es\/files\/2017\/11\/Gato-768x440.jpg 768w, https:\/\/blog.mozilla.org\/press-es\/files\/2017\/11\/Gato.jpg 900w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/a><\/p>\n<p>Esta es una forma bastante eficiente de decodificar las probabilidades que genera el modelo en una secuencia de caracteres, pero tiene un defecto importante: solo tiene en cuenta el resultado de la red, lo que significa que solo tiene en cuenta la informaci\u00f3n del audio. Cuando un audio tiene dos transcripciones igualmente probables (piensa en &#8220;Nobel&#8221; frente a &#8220;novel&#8221;, &#8220;vaca&#8221; vs &#8220;baca&#8221;), el modelo solo puede intentar adivinar cu\u00e1l es la correcta. Esto est\u00e1 lejos de ser perfecto: si las primeras palabras de una oraci\u00f3n son &#8220;El cient\u00edfico recibi\u00f3 un premio&#8221;, podemos estar bastante seguros de que la siguiente palabra ser\u00e1 &#8220;Nobel&#8221; en lugar de &#8220;novel&#8221;. Responder a ese tipo de preguntas es el trabajo de un modelo de lenguaje, y si pudi\u00e9ramos integrar un modelo de lenguaje en la fase de decodificaci\u00f3n de nuestro modelo, podr\u00edamos obtener mucho mejores resultados.<\/p>\n<p>Cuando intentamos abordar este problema por primera vez, nos encontramos con un par de barreras en TensorFlow: primero, no expone su funcionalidad de puntuaci\u00f3n de luz (<em>beam scoring<\/em>) en la API de Python (probablemente por razones de rendimiento); y segundo, las probabilidades de registro producidas por la funci\u00f3n de p\u00e9rdida de CTC eran (\u00bfson?) <u><a href=\"https:\/\/github.com\/tensorflow\/tensorflow\/issues\/6034\" target=\"_blank\" rel=\"noopener\">inv\u00e1lidas<\/a>. <\/u>Decidimos solucionar el problema creando algo as\u00ed como un corrector ortogr\u00e1fico en su lugar: revisar la transcripci\u00f3n y valorar si hay peque\u00f1as modificaciones que pudieran hacer que aumentara la probabilidad de que esa transcripci\u00f3n sea v\u00e1lida en ingl\u00e9s, de acuerdo con el modelo de idioma. Esto tuvo buenos resultados al corregir peque\u00f1os errores en el resultado, pero a medida que nos acercamos cada vez m\u00e1s a nuestro objetivo de tasa de error, nos dimos cuenta de que no iba a ser suficiente. Tendr\u00edamos que aceptarlo y escribir algo de C++.<\/p>\n<p><strong>Beam scoring con un modelo de lenguaje<\/strong><\/p>\n<p>Integrar el modelo de lenguaje en el decodificador implica consultar el modelo de lenguaje cada vez que evaluamos una adici\u00f3n a la transcripci\u00f3n. Volviendo al ejemplo anterior, al analizar si queremos elegir &#8220;Nobel&#8221; o &#8220;novel&#8221; para la siguiente palabra despu\u00e9s de &#8220;El cient\u00edfico recibi\u00f3 un premio&#8221;, consultamos el modelo de lenguaje y usamos esa puntuaci\u00f3n como un peso para clasificar las transcripciones candidatas. Ahora podemos utilizar la informaci\u00f3n no solo del audio, sino tambi\u00e9n de nuestro modelo de lenguaje para decidir qu\u00e9 transcripci\u00f3n es m\u00e1s adecuada. El algoritmo se describe en <a href=\"https:\/\/arxiv.org\/abs\/1408.2873\" target=\"_blank\" rel=\"noopener\"><u>este trabajo<\/u><\/a> de Hannun y otros.<\/p>\n<p>Afortunadamente, TensorFlow tiene un punto de extensi\u00f3n en su decodificador de b\u00fasqueda de CTC que permite al usuario suministrar su propio marcador de luz (<em>beam scorer<\/em>). Esto significa que todo lo que tienes que hacer es escribir el marcador de luz que consulta el modelo de idioma y conectarlo. En nuestro caso, quer\u00edamos que esa funcionalidad estuviera expuesta en nuestro c\u00f3digo Python, por lo que tambi\u00e9n lo expusimos como una operaci\u00f3n TensorFlow personalizada que puede cargarse usando <u><a href=\"https:\/\/www.tensorflow.org\/api_docs\/python\/tf\/load_op_library\" target=\"_blank\" rel=\"noopener\">tf.load_op_library.<\/a><\/u><\/p>\n<p>Conseguir que todo esto funcionara con nuestra configuraci\u00f3n requiri\u00f3 bastante esfuerzo, desde luchar con el sistema de compilaci\u00f3n Bazel durante horas, hasta asegurarse de que todo el c\u00f3digo fuera capaz de manejar la entrada Unicode de forma consistente y depurar el propio marcador de luz. El sistema requiere que muchas piezas trabajen juntas:<\/p>\n<ul>\n<li>El modelo de lenguaje en s\u00ed (utilizamos <a href=\"http:\/\/kheafield.com\/code\/kenlm\/\" target=\"_blank\" rel=\"noopener\"><u>KenLM <\/u><\/a>para compilar y consultar).<\/li>\n<li>Un \u00e1rbol digital (<a href=\"https:\/\/en.wikipedia.org\/wiki\/Trie\" target=\"_blank\" rel=\"noopener\"><em>trie<\/em><\/a>) de todas las palabras de nuestro vocabulario.<\/li>\n<li>Un archivo de alfabeto que mapea las etiquetas enteras generadas por la red para convertirlas en caracteres.<\/li>\n<\/ul>\n<p>Aunque agregar todas estas partes m\u00f3viles hace que nuestro c\u00f3digo sea m\u00e1s dif\u00edcil de modificar y aplicar a diferentes casos de uso (como otros idiomas), ofrece grandes beneficios: nuestra tasa de error de palabra en el conjunto de prueba de LibriSpeech pas\u00f3 del 16% al 6,5%, lo que no solo logra nuestro objetivo inicial, pero nos acerca a un desempe\u00f1o a nivel humano (5.83% seg\u00fan el trabajo de Deep Speech 2). En un MacBook Pro, usando la GPU, el modelo puede crear inferencias en un factor de tiempo real de alrededor de x0,3, y alrededor de x1,4 solo en la CPU (un factor de tiempo real de x1 significa que puede transcribir 1 segundo de audio en 1 segundo).<\/p>\n<p>Hemos vivido un viaje incre\u00edble para llegar a este momento: \u00a1el lanzamiento inicial de nuestro modelo! En el futuro, queremos lanzar un modelo que sea lo suficientemente r\u00e1pido como para ejecutarse en un dispositivo m\u00f3vil o una Raspberry Pi.<\/p>\n<p>Si este tipo de trabajo te parece interesante o \u00fatil, visita <a href=\"http:\/\/github.com\/mozilla\/DeepSpeech\" target=\"_blank\" rel=\"noopener\"><u>nuestro repositorio en GitHub<\/u><\/a> y nuestro <a href=\"https:\/\/discourse.mozilla.org\/c\/deep-speech\" target=\"_blank\" rel=\"noopener\"><u>Discourse channel<\/u><\/a>. Tenemos una comunidad cada vez mayor de colaboradores y estamos ansiosos por ayudarte a crear y publicar un modelo para tu idioma.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>En Mozilla creemos que las interfaces de voz ser\u00e1n una pieza clave en la interacci\u00f3n entre las personas y sus dispositivos en el futuro. Hoy nos complace anunciar el lanzamiento &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/press-es\/2017\/11\/29\/un-viaje-a-una-tasa-de-error-de-palabra-de-menos-del-10\/\">Read more<\/a><\/p>\n","protected":false},"author":593,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":true,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[290452,289113,31466,289112],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/press-es\/wp-json\/wp\/v2\/posts\/1572"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/press-es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/press-es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/press-es\/wp-json\/wp\/v2\/users\/593"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/press-es\/wp-json\/wp\/v2\/comments?post=1572"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/press-es\/wp-json\/wp\/v2\/posts\/1572\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/press-es\/wp-json\/wp\/v2\/media?parent=1572"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/press-es\/wp-json\/wp\/v2\/categories?post=1572"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/press-es\/wp-json\/wp\/v2\/tags?post=1572"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}