Tutorial: Limpieza de corpus paralelos - mtuoc/tutorials GitHub Wiki

1. Introducción

Tanto si hemos obtenido un corpus paralelo de Internet como si lo hemos creado nosotros mismos será altamente recomendable realizar algunas operaciones de limpieza del corpus. Los corpus paralelos pueden presentar diversos problemas:

  • Presencia de caracteres representados por su entidad html/xml. Por ejemplo, nos podemos encontrar con algunos " o á junto con algunos casos del caracter correspondiente en el corpus.
  • Segmentos vacíos en alguna de las lenguas.
  • Segmentos en lenguas diferentes de las requeridas.
  • Segmentos con un percentage alto de expresiones numéricas.
  • Segmentos con etiquetas XML. Normalmente se entrenan los sistemas con corpus donde se han eliminado estas etiquetas.
  • Errores de codificación de caracteres.
  • Segmentos con caracteres no imprimibles.

y un largo etc.

Además, también es posible que incluso siendo el segmento de partida en la lengua de partida y el de llegada en la lengua de llegada, estos realmente no sean equivalentes de traducción.

Esta semana aprenderemos a realizar diversas operaciones de limpieza en corpus paralelos.

2. Lectura recomendada

Esta semana os propongo la lectura de dos artículos sobre herramientas que realizan operaciones similares a las que utilizaremos nosotros durante esta semana:

Ramírez‐Sánchez, G., Zaragoza-Bernabeu, J., Bañón, M., & Ortiz-Rojas, S. (2020, November). Bifixer and bicleaner: two open-source tools to clean your parallel data. In Proceedings of the 22nd Annual Conference of the European Association for Machine Translation (pp. 291-298).

Zaragoza-Bernabeu, J., Ramírez‐Sánchez, G., Bañón, M., & Ortiz-Rojas, S. (2022, June). Bicleaner AI: Bicleaner goes neural. In Proceedings of the Thirteenth Language Resources and Evaluation Conference (pp. 824-831).

3. Limpieza de corpus

Esta acción se puede llevar a cabo fácilmente con el programa MTUOC-clean-parallel-corpus, que se encuentra en el repositorio https://github.com/mtuoc/MTUOC-clean-parallel-corpus/tree/main. Este programa puede llevar a cabo un gran número de acciones de limpieza diferentes. Para facilitar su uso, la configuración del programa se realiza mediante un archivo yaml (config.yaml, por ejemplo, aunque puede tener cualquier nombre, lo que nos permite tener diversas configuraciones preconfiguradas en archivos diferentes). Recordad que un arvchivo yaml es un archivo de texto y que se puede abrir con cualquier editor de textos). El archivo yaml de configuración contiene la siguiente información:

remove_control_characters: True
remove_non_printable: True
norm_apos: False
norm_unicode: True
remove_tags: True
unescape_html: True
fixencoding: True
remove_empty: True
remove_short: 10
remove_equal: True
remove_NUMPC: 60
remove_URLPC: 60
remove_URL: False
remove_long: False
#False or int
remove_non_latin: False
remove_non_script: False
check_weights: False
escapeforMoses: False
stringFromFile: False
regexFromFile: False
vSL: en
#False or language code en es fr ...
vTL: es
#False or language code en es fr ...
vSetLanguages: False
#False or list separated by ,: en,fr,es
vTNOTL: False
noUPPER: False
verbose: True

Si nos fijamos, este programa tiene una gran cantidad de opciones diferentes. A continuación explicamos brevemente cada una de estas opciones. Una cuestión muy importante es que el programa limpia corpus paralelos que estén en formato de texto tabulado. Cuando se elimina una línia, se elimina tanto el segmento de la lengua 1 como el de la lengua 2, por lo que la paralelización se mantiene.

  • remove_control_characters: True/False, para eliminar los caracteres de control.
  • remove_non_printable: True/False, para eliminar los caracteres no imprimible
  • norm_apos: True/False, convierte cualquier tipo de apóstrofe (' ’ ‘) al apóstrofe estándar plano (')
  • norm_unicode: True/False, normaliza los caracteres unicode a NFC
  • remove_tags: True/False, elimina las etiquetas XML/HTML.
  • unescape_html: True/False, convierte las entidades html a su correspondiente caracter unicode (p.ej. á - á)
  • fixencoding: True/False, intenta corregir los errores en la codificación de caracteres.
  • remove_empty: True/False, elimina toda la línea si uno de los segmentos está vacío.
  • remove_short: número entero, p.e. 10, elimina toda la línea si uno de los segmentos tiene menos de 10 caracteres.
  • remove_equal: True/False, elimina toda la línea si el segento de la lengua 1 es igual al segmento de la lengua 2.
  • remove_NUMPC: número entero, p.e. 60, elimina toda la línea si uno de los segmentos tiene más del 60% de caracteres numéricos.
  • remove_URLPC: número entero, p.e. 60, elimina toda la línea si uno de los segmentos tiene más del 60% de caracteres en URLs.
  • remove_URL: True/False, elimina toda la línea si uno de los segmentos tienes ULRs.
  • remove_long: False o número entero, elimina toda la línea si uno de los segmentos tiene más caracteres de los indicados
  • remove_non_latin: True/False, elimina toda la línea si uno de los segmentos contiene caracteres no latinos.
  • remove_non_script: True/False, elimina toda la línea si uno de los segmentos tiene caracteres que no se corresponen a ningún sistema de escritura.
  • check_weights: True/False, elimina la línia que no contenga un tercer campo con el peso (que tiene que ser un número real válido).
  • escapeforMoses: True/False, elimina toda la línea si uno de los segmentos contiene los caracteres | [ ] < >
  • stringFromFile: False o el nombre de un archivo que contiene cadenas prohibidas, es decir cadenas que obligan a eliminar toda la línea.
  • regexFromFile: False o el nombre de un archivo que contiene expresiones regulares que si se cumplen hacen que se elimine toda la línea:
  • vSL: False o código lengua de dos letras, verifica que la lengua 1 sea la indicada, si no, elimina el segmento.
  • vTL: False o código lengua de dos letras, verifica que la lengua 2 sea la indicada, si no, elimina el segmento.
  • vSetLanguages: False o una série de códigos de lengua de dos letras separados por comas (p.ej. en,fr,es). Limita la lenguas a detectar a las indicadas.
  • vTNOTL: False o código lengua de dos letras, verifica que la lengua 2 no sea la indicada, si lo es, elimina el segmento.
  • noUPPER: True/False, elimina toda la línea si uno de los segmentos está todo en mayúsculas.
  • verbose: True/False, muestra por pantalla las acciones que realiza.

Es muy habitual que para segmentos cortos, la detección de lengua no funcione correctamente, por lo que se debe utilizar la opción de detección de lenguas con precaución. Es decir, la detección de lengua no es fiable para segmentos cortos, y podemos eliminar segmentos correctos. Pero normalmente, si el corpus es de tamaño suficiente, es mejor eliminar cosas correctas que que queden cosas incorrectas.

Para ejecutar el programa símplemente tienes que editar el archivo de configuración para seleccionar la opciones que deseas utilizar y escribir en terminal:

python3 MTUOC-clean-parallel-corpus.py config.yaml corpus-a-limpiar.txt corpus-salida-limpio.txt

Recuerda que puedes poner el nombre que quieras al archivo de configuración. Si utilizas otro archivo, indica el nombre correcto. Lo mismo para los nombres de los corpus, que aquí he puesto hipotéticos.

Este programa si se ejecuta sin parámetros abre una interfaz gráfica sencilla:

python3 MTUOC-clean-parallel-corpus.py

o bien haciendo doble clic sobre el programa .py o sobre el ejecutable para Windows si utilizas este sistema operativo. Se abre la siguiente interfaz:

Utiliza los botones para seleccionar el corpus a limpiar, el corpus limpio resultante y el archivo de configuración a utilizar.

3. Rescoring de corpus

En el apartado anterior hemos visto una serie de operaciones de limpieza para corpus paralelos. Entre estas operaciones, existe la posibilidad de verificar las lenguas de los segmentos para asegurarnos que estas lenguas sean las correctas. Pero entre todas esas operaciones ninguna verifica que el segmento original y el traducido sean realmente equivalentes de traducción. Es posible que las lenguas de los segmentos de partida y de llegada sean correctas, pero que los dos segmentos no sean equivalentes de traducción.

En este apartado explicamos un algoritmo que reverifica las lenguas de partida y de llegada y que, además, comprueba si los segmentos originales y traducidos son equivalentes de traducción. Para hacer esta verificación representa el segmento original y traducido mediante un mismo modelo de sentence embeddings multilingüe. Una vez tenemos estos dos segmentos representados por sus sentence embeddings, dado que se han representado con el mismo modelo multilingüe, estarán dentro del mismo espacio vectoria y podremos calcular alguna medida de similitud, como por ejemplo una basada en la coseno entre los vectores. Si esta medida de similitud es suficientemente alta, podremos deducir que los segmentos son equivalentes de traducción. En cambio, si esta similitud es baja, podremos deducir que no son equivalentes de traducción. No hay un valor concreto que discrimine lo que es traducción o no, y tendremos que tomar algun valor de compromiso entre estar seguros que son equivalentes y no eliminar demasiados segmentos paralelos.

En el siguiente artículos se dan más detalles sobre el funcionamiento de este algoritmo (es una lectura recomendada para aquellos que quieran saber más detalles, pero no es obligatoria para seguir la actividad:

Oliver, A., & Álvarez, S. (2023). Filtering and rescoring the CCMatrix corpus for Neural Machine Translation training. In Proceedings of the 24th Annual Conference of the European Association for Machine Translation (pp. 39-45)

Por defecto, el programa utiliza el modelo de identificación de idiomas de fasttext lid.176.bin. Debes descargar este modelo en el mismo directorio que los programas. Puedes usar cualquier otro modelo de identificación de idiomas de fasttext e incluso entrenar tu propio modelo, como se explica en el apartado 4 de este tutorial.

El archivo requirements.txt contiene los requisitos necesarios para ejecutar los programas.

Es importante instalar la versión de numpy indicada en el archivo requirements.txt. También se recomienda usar un entorno virtual de Python para evitar conflictos con bibliotecas ya instaladas en tu sistema.

Si encuentras el siguiente error:

"ERROR: (<class 'ValueError'>, ValueError('Unable to avoid copy while creating an array as requested.\nIf using np.array(obj, copy=False) replace it with np.asarray(obj) to allow a copy when needed (no behavior change in NumPy 1.x).\nFor more details, see https://numpy.org/devdocs/numpy_2_0_migration_guide.html#adapting-to-changes-in-the-copy-keyword.'), <traceback object at 0x7f78cc533900>)"

Instala la siguiente versión de numpy:

pip install numpy==1.24.4

Los programas se ofrecen en versión en línea de comandos y en versión con una interfaz gráfica sencilla. El proceso se divide en dos pasos:

  • PASO 1: creación del archivo con la información
  • PASO 2: selección de los archivos que cumplen una serie de criterios.

PASO 1: creación del archivo con la información

Este paso en terminla lo realizaremos con el programa MTUOC-PCorpus-rescorer-txt.py. La opción -h muestra la ayuda del programa.

python3 MTUOC-PCorpus-rescorer-txt.py -h
usage: MTUOC-PCorpus-rescorer-txt.py [-h] -i INPUT -o OUTPUT [-SEmodel SEMODEL]
                                     [-LDmodel LDMODEL]

MTUOC-PCorpus-rescorer: a script to score parallel corpora. The parallel corpus file
should be a TSV file with source segment, target segment and, optionally, a score. It
creates a text file that should be used with the companion program MTUOC-PCorpus-selector-
txt.

options:
  -h, --help            show this help message and exit
  -i INPUT, --input INPUT
                        The input parallel corpus file.
  -o OUTPUT, --output OUTPUT
                        The output file with the data.
  -SEmodel SEMODEL      The SentenceTransformer model. Default model: LaBSE
  -LDmodel LDMODEL      The fasttext language detection model. Default model: lid.176.bin

Así, si queremos hacer el rescorer del corpus-entrada.txt, crearemos una archivo con la información que llamaremos corpus-rescore-info.txt (y considerando que dejamos los modelos por defecto y que tenemos lid.176.bin en el mismo directorio).

`python3 MTUOC-PCorpus-rescorer-txt.py -i corpus-entrada.txt -o corpus-rescore-info.txt

Este mismo proceso se puede realizar con la versión visual del programa MTUOC-PCorpus-rescorer-txtGUI (que se ofrece también en versión ejecutable para Windows). Una vez ponemos en marcha el programa aparece la siguiente interfaz que nos permite indicar los mismos parámetros que la versión en terminal.

Cuando finaliza el proceso, que es lento si no disponemos de unidades GPU, el archivo corpus rescorer contiene la siguiente información:

Compare that to the tiny movements involved in online shopping.	Compare eso con los pequeñísimos movimientos que involucran las compras en línea.	en:0.9686876535415649;vi:0.003173261182382703;pt:0.0018109100637957454;id:0.0013031522976234555;ro:0.0012691440060734749	es:0.9898275136947632;pt:0.005487409885972738;en:0.00130145950242877;ast:0.0009181745117530227;ca:0.0007383363554254174	0.9280933141708374

Es decir, (1) el segmento en la lengua 1; (2) el segmento en la lengua 2; (3) la información de detección de lengua del segmento en la lengua 1; (4) la información de detección de lengua del segmento en la lengua 2; (5) el índice de similitud coseno entre los segmentos en las dos lenguas.

Fíjate que la detección de lengua ofrece diferentes lenguas con distintos porcentages de seguridad en la detección.

Once the text file with the rescoring information is created, the program MTUOC-PCorpus-selector-txt.py should be used.

PASO 2: selección de los archivos que cumplen una serie de criterios

Este paso en terminal lo podremos realizar con el programa MTUOC-PCorpus-selector-txt.py. La opció -h muestra la ayuda del programa:

python3 MTUOC-PCorpus-selector-txt.py -h
usage: MTUOC-PCorpus-selector-txt.py [-h] -i INPUT --sl SL --sldc SLDC --tl TL --tldc TLDC
                                     [-m MINSBERT] -o OUTFILE

MTUOC-PCorpus-selector: a script to select parallel segments from a rescorer text file
created with MTUOC_PCorpus-rescorer-txt.

options:
  -h, --help            show this help message and exit
  -i INPUT, --input INPUT
                        The text file resulting from MTUOC-PCorpus-rescorer.py.
  --sl SL               The source language code.
  --sldc SLDC           The minimum source language detection confidence.
  --tl TL               The target language code.
  --tldc TLDC           The minimum target language detection confidence.
  -m MINSBERT, --minSBERT MINSBERT
                        The minimum value for SBERT score.
  -o OUTFILE, --outfile OUTFILE
                        The output file containing the parallel corpus.

Por ejemplo, para seleccionar los segmentos que cumplan un mínimo de 0.75 para la detección de lengua y de mínima similitud coseno, y la lengua SL sea en y la TL es, escxribiremos:

python MTUOC-PCorpus-selector.py -i corpus-rescore-info.txt --sl en --sldc 0.75 --tl es --tldc 0.75 -m 0.75 -o corpus-rescored.txt

En corpus-rescored.txt tendremos los segmentos que cumplen todos los criterios.

Esta misma operación se puede realizar con la versión visual del programa, que al ponerse en marcha muestra la siguiente interfaz que permite indicar los mismos parámetros que la versión en terminal.

4. Entrenamiento de un modelo de detección de lengua

En los ejemplos anteriores hemos utilizado un modelo de detección de lengua que se distribuye con la herramienta fasttext. Este modelo es capaz de detectar entre muchas lenguas diferentes. En estos modelos multilingüe habitualmente las lengas "menores" están menos representadas y ante una oración en dos lenguas parecidas, por ejemplo español y asturiano, el algoritmo tiende a clasificarla como española. Puede resultar de utilidad entrenar un modelo propio que incluya únicamente un conjunto de lenguas de trabajo, especialmente si entre estas lenguas se encuentran lenguas con menos recursos. En estos casos puede ser interesante compenar el tamaño de los corpus utilizados a la lengua menor.

Aquí vamos a explicar cómo entrenar un modelo de fasttext para dos lenguas, pero el procedimiento es el mismo para tratar un número mayor de lenguas. Aquí utilizaremos un corpus pequeño, por ejemplo el Wikimedia para el asturiano y el español, que contiene unos 45.000 segmentos.

Podemos descargar el corpus directamente haciendo:

wget https://object.pouta.csc.fi/OPUS-wikimedia/v20230407/moses/ast-es.txt.zip

y una vez descomprimido haremos los siguientes pasos:

  1. Añadir un prefijo determinado a las oraciones en cada lengua. Este prefijo tiene que ser, en nuestro ejemplo __label__ast para el asturiano y __label_es para el español. Para más lenguas haríamos lo mismo. En Linux podemos añadir un prefijo a cada línea escribiendo:
sed 's/^/__label__ast /' wikimedia.ast-es.ast > wikimedia-prefix-ast.txt
sed 's/^/__label__es /' wikimedia.ast-es.es > wikimedia-prefix-es.txt

Alternativamente podemos utilizar el script addPrefix.py que se distribuye con MTUOC-PCorpus-rescorer, escribiendo:

python3 addPrefix.py wikimedia.ast-es.ast wikimedia-prefix-ast.txt ast
python3 addPrefix.py wikimedia.ast-es.es wikimedia-prefix-es.txt es

Ahora concatenamos y mezclamos los archivos:

cat wikimedia-prefix-ast.txt wikimedia-prefix-es.txt | shuf > corpus-entrenamiento.txt

Y en el corpus-entrenamiento.txt tendremos nuestro corpus de entrenamiento.

  1. Entrenar el modelo:

Para entrenar el modelo se tiene que escribir:

python3 trainFasttextModel.py corpus-entrenamiento.txt modelo-ast-es.bin

El modelo estará en el archivo modelo-ast-es.bin.

Si se dese modificar los parámetros de entrenamiento se puede editar la siguiente linea del script trainFasttextModel.py:

model = fasttext.train_supervised(input=training_data, epoch=25, lr=1.0, wordNgrams=2, verbose=2, minCount=1)

4. La nova normativa ortogràfica de l'IEC per al català

En este apartado explico un proceso específico para el catalán, pero que es posible que tengan que aplicarse procesos similares para otras lenguas. El catalán recientemente (año 2017) ha modificado algunos aspectos de su ortografía. Esto hace que los textos anteriores a 2017 estén con una normativa determinada y que a partir de esta fecha y paulatinamente han ido cambiando a la nueva. Los cambios son pocos pero algunos muy frecuentes, como la eliminación de muchos acentos diacríticos (como dona (mujer) dóna (3a persona singular present idicativo del verbo donar, dar, es decir da; que en la nueva normativa se convierte en dona (sin acento diacrítico) para las dos acepciones). Por lo tanto, en los corpus en catalán es importante asegurarnos que los textos están en la normativa actual. Por suerte, prácticamente todos los cambios se pueden hacer de manera automática. En el repositorio https://github.com/aoliverg/MTUOC-novaIEC se encuentran una serie de scripts que permiten llevar a cabo esta acción.