Tras el auge de la IA, el ritmo de iteración tecnológica ha alcanzado un nivel sin precedentes. Los obstáculos anteriores ahora parecen tener soluciones viables. Este artículo sirve como guía "NMT 101". Al presentar nuestro proyecto, también guía a los lectores paso a paso a través del proceso de ajuste de un modelo de traducción existente para admitir un idioma de bajos recursos que no está incluido en los modelos multilingües convencionales.
Antecedentes: el dongxiang como lengua de bajos recursos
El dongxiang es una lengua minoritaria hablada en la provincia china de Gansu y está clasificada como vulnerable por el Atlas de las lenguas del mundo en peligro de la UNESCO. A pesar de ser ampliamente hablado en las comunidades locales, el dongxiang carece del apoyo institucional y digital del que disfrutan las lenguas de altos recursos. Antes de sumergirse en el proceso de formación, resulta útil comprender brevemente el idioma en sí. Dongxiang, como su nombre indica, es la lengua materna del pueblo Dongxiang. Descendiente de grupos de Asia Central que emigraron a Gansu durante la dinastía Yuan, la comunidad Dongxiang tiene raíces lingüísticas estrechamente ligadas al mongol medio. Desde la perspectiva del sistema de escritura, Dongxiang ha experimentado una estandarización relativamente reciente. Desde la década de 1990, con la promoción gubernamental, el idioma ha adoptado gradualmente una ortografía oficial basada en el latín, utilizando las 26 letras del alfabeto inglés y delimitando palabras por espacios en blanco.
Aunque todavía está clasificado en la familia de lenguas mongólicas, debido a la coexistencia prolongada con comunidades de habla mandarín a lo largo de la historia, la lengua tiene un tesoro de préstamos léxicos del chino (mandarín). Dongxiang no muestra ninguna inflexión tensa manifiesta ni género gramatical, lo que puede ser una ventaja para simplificar nuestro entrenamiento modelo.
Puede encontrar más información sobre el idioma dongxiang y sus hablantes en nuestro sitio web, que alberga una introducción oficial en inglés publicada por el gobierno chino.
Nuestro modelo: cómo utilizar el sistema de traducción
Construimos nuestro sistema de traducción sobre NLLB-200-distilled-600M, un modelo de traducción automática neuronal multilingüe lanzado por Meta como parte del proyecto No Language Left Behind (NLLB). Nos inspiramos en el trabajo de David Dale. Sin embargo, las actualizaciones continuas de la biblioteca Transformers han dificultado la aplicación del enfoque original. En nuestras propias pruebas, volver a versiones anteriores (por ejemplo, transformadores ≤ 4,33) a menudo provocaba conflictos con otras dependencias. A la luz de estas limitaciones, proporcionamos una lista completa de bibliotecas en el archivo de requisitos de GitHub de nuestro proyecto.txt para su referencia.
Nuestro modelo se ajustó en 42.868 pares de oraciones bilingües dongxiang-chino. El corpus de capacitación combina materiales disponibles públicamente con recursos seleccionados internamente proporcionados por socios del gobierno local, todos los cuales fueron procesados y limpiados con anticipación. La capacitación se llevó a cabo utilizando Adafactor, un optimizador de memoria eficiente muy adecuado para modelos de transformadores grandes. Con la arquitectura refinada, el proceso de ajuste completo se puede completar en menos de 12 horas en una sola GPU NVIDIA A100. Todas las configuraciones de entrenamiento, hiperparámetros y configuraciones experimentales se documentan en dos cuadernos de entrenamiento de Jupyter. En lugar de depender de un único modelo bidireccional, entrenamos dos modelos de dirección específica para admitir la traducción Dongxiang-chino y chino-Dongxiang. Dado que NLLB ya está entrenado previamente en chino, el entrenamiento conjunto en condiciones de desequilibrio de datos tiende a favorecer la dirección más fácil o más dominante. Como resultado, las mejoras en el desempeño en el lado de bajos recursos (Dongxiang) suelen ser limitadas. Sin embargo, NLLB admite la traducción bidireccional en un solo modelo y un enfoque sencillo es alternar direcciones de traducción a nivel de lote.
Aquí están los enlaces a nuestro repositorio y sitio web.
Repositorio GitHub
Sitio web alojado en GitHub
El modelo también está disponible públicamente en Hugging Face.
Chino → Dongxiang
Dongxiang → Chino
Capacitación modelo: canalización reproducible paso a paso
Antes de seguir este proceso para construir el modelo, asumimos que el lector tiene un conocimiento básico de Python y conceptos fundamentales en el procesamiento del lenguaje natural. Para los lectores que puedan estar menos familiarizados con estos temas, los cursos de Andrew Ng son una puerta de entrada muy recomendable. Personalmente, también comencé mi propio camino en este campo a través de su curso.
Paso 1: procesamiento de conjuntos de datos bilingües
La primera etapa del entrenamiento del modelo se centra en la construcción de un conjunto de datos bilingüe. Si bien a menudo se pueden obtener corpus paralelos para los principales idiomas aprovechando los recursos existentes extraídos de la web, los datos del dongxiang-chino siguen siendo difíciles de adquirir. Para respaldar la transparencia y la reproducibilidad, y con el consentimiento de los custodios de datos relevantes, hemos publicado tanto el corpus sin procesar como una versión normalizada en nuestro repositorio de GitHub. El conjunto de datos normalizado se produce a través de un sencillo proceso de preprocesamiento que elimina el exceso de espacios en blanco, estandariza la puntuación y garantiza una separación clara entre los scripts. El texto dongxiang está restringido a caracteres latinos, mientras que el texto chino contiene sólo caracteres chinos.
A continuación se muestra el código utilizado para el preprocesamiento:
importar re importar pandas como pd def split_lines(s: str): si "\n" en s y "n" no en s: líneas = s.split("\n") else: líneas = s.splitlines() líneas = [ln.strip().strip("'").strip() for ln en líneas si ln.strip()] devolver líneas def clean_dxg(s: str) -> str: s = re.sub(r"[^A-Za-zs,.?]", " ", s) s = re.sub(r"s+", " ", s).strip() s = re.sub(r"[,.?]+$", "", s) return s def clean_zh(s: str) -> str: s = re.sub(r"[^u4e00-u9fff,。?]", "", s) s = re.sub(r"[,。?]+$", "", s) return s def make_pairs(raw: str) -> pd.DataFrame: líneas = split_lines(raw) pares =[]para i en rango(0, len(líneas) – 1, 2): dxg = clean_dxg(líneas[i]) zh = clean_zh(líneas[i+1]) si dxg o zh: pares.append({"Dongxiang": dxg, "Chinese": zh}) return pd.DataFrame(pairs, columns=["Dongxiang", "Chinese"])
En la práctica, se prefieren los pares bilingües de nivel de oración a las entradas de nivel de palabra, y las oraciones excesivamente largas se dividen en segmentos más cortos. Esto facilita una alineación interlingüística más confiable y conduce a un entrenamiento de modelos más estable y eficiente. No se deben insertar entradas aisladas del diccionario en las entradas de capacitación. Sin el contexto circundante, el modelo no puede inferir roles sintácticos ni aprender cómo interactúan las palabras con los tokens circundantes.
Cuando los datos paralelos son limitados, una alternativa común es generar oraciones fuente sintéticas a partir de datos monolingües del idioma de destino y emparejarlas con los originales para formar corpus pseudoparalelos. Esta idea fue popularizada por Rico Sennrich, cuyo trabajo sobre retrotraducción sentó las bases para muchos proyectos de NMT. Los datos sintéticos generados por LLM son otro enfoque viable. Trabajos anteriores han demostrado que los datos sintéticos generados por LLM son eficaces en la construcción de sistemas de traducción para purépecha, una lengua indígena hablada en México.
Paso 2: preparación del tokenizador
Antes de que un modelo de traducción automática neuronal pueda digerir el texto, debe convertirse en tokens. Los tokens son unidades discretas, normalmente a nivel de subpalabra, que sirven como símbolos de entrada básicos para las redes neuronales. Usar palabras completas como unidades atómicas no es práctico, ya que genera vocabularios excesivamente extensos y un rápido crecimiento en la dimensionalidad del modelo. Además, las representaciones a nivel de palabras tienen dificultades para generalizarse a palabras raras o no vistas, mientras que la tokenización de subpalabras permite a los modelos componer representaciones para formas de palabras novedosas.
La documentación oficial de NLLB ya proporciona ejemplos estándar que demuestran cómo se maneja la tokenización. Debido a la fuerte capacidad multilingüe de NLLB, los sistemas de escritura más utilizados pueden tokenizarse de manera razonable y estable. En nuestro caso, adoptar el tokenizador multilingüe NLLB predeterminado (basado en Unigram) fue suficiente para procesar el texto Dongxiang.
La mejor forma de determinar si el tokenizador debe volver a entrenarse es mediante dos criterios. La primera es la cobertura: la aparición frecuente de tokens desconocidos () indica vocabulario o manejo de caracteres insuficiente. En nuestra muestra de 300 sentencias Dongxiang, la tasa es cero, lo que sugiere una cobertura total bajo el preprocesamiento actual. El segundo criterio es la fertilidad de subpalabras, definida como el número promedio de tokens de subpalabra generados por palabra delimitada por espacios en blanco. En las 300 muestras, las oraciones tienen un promedio de 6,86 palabras y 13,48 tokens, lo que corresponde a una fertilidad de aproximadamente 1,97. Este patrón sigue siendo consistente en toda la distribución, sin evidencia de fragmentación excesiva en oraciones más largas.
En general, NLLB demuestra un comportamiento sólido incluso en lenguajes nunca antes vistos. Como resultado, el reentrenamiento del tokenizador generalmente es innecesario a menos que el idioma de destino emplee un sistema de escritura muy poco convencional o incluso carezca de soporte Unicode. Volver a entrenar un tokenizador SentencePieza también tiene implicaciones para la capa de incrustación. Los nuevos tokens comienzan sin incrustaciones previamente entrenadas y deben inicializarse utilizando valores aleatorios o un promedio simple.
Paso 3: Registro de identificación de idioma
En sistemas prácticos de traducción automática como Google Translate, los idiomas de origen y de destino deben especificarse explícitamente. NLLB adopta el mismo supuesto. La traducción se rige por una etiqueta de idioma explícita, denominada src_lang y tgt_lang, que determina cómo se codifica y genera el texto dentro del modelo. Cuando un lenguaje queda fuera del alcance predefinido de NLLB, primero debe registrarse explícitamente, junto con la correspondiente expansión de la capa de incrustación del modelo. La capa de incrustación asigna tokens discretos a representaciones vectoriales continuas, lo que permite que la red neuronal procese y aprenda patrones lingüísticos en forma numérica.
En nuestra implementación, se agrega una etiqueta de idioma personalizada al tokenizador como un token especial adicional, que le asigna una ID de token única. Luego se cambia el tamaño de la matriz de incorporación de tokens del modelo para acomodar el vocabulario ampliado. El vector de incrustación asociado con la nueva etiqueta de idioma se inicializa a partir de una distribución normal centrada en cero con una pequeña varianza, escalada en 0,02. Si el lenguaje recién introducido está estrechamente relacionado con un lenguaje compatible existente, su incorporación a menudo se puede entrenar sobre el espacio de representación existente. Sin embargo, la similitud lingüística por sí sola no garantiza un aprendizaje por transferencia eficaz. Las diferencias en los sistemas de escritura pueden afectar la tokenización. Un ejemplo bien conocido es el moldavo, que es lingüísticamente idéntico al rumano pero está escrito en escritura latina, mientras que en la llamada República de Moldavia de Pridnestrovia está escrito en cirílico. A pesar de la estrecha relación lingüística, la diferencia en la escritura introduce distintos patrones de tokenización.
El código utilizado para registrar un nuevo idioma se presenta aquí.
def fix_tokenizer(tokenizer, new_lang: str): old = list(tokenizer.additional_special_tokens) si new_lang no está en old: tokenizer.add_special_tokens( {"additional_special_tokens": old + [new_lang]}) return tokenizer.convert_tokens_to_ids(new_lang) fix_tokenizer(tokenizer,"sce_Latn") # nos registramos Dongxiang como sce_Latn, y debe agregarse a la última # salida 256204 print(tokenizer.convert_ids_to_tokens([256100,256204])) print(tokenizer.convert_tokens_to_ids(['lao_Laoo','sce_Latn'])) # salida ['lao_Laoo', 'sce_Latn'] [256100, 256204] model = AutoModelForSeq2SeqLM.from_pretrained("facebook/nllb-200-distilled-600M") model.resize_token_embeddings(len(tokenizer)) new_id = fix_tokenizer(tokenizer, "sce_Latn") embed_dim = modelo.modelo.peso.compartido.tamaño(1) modelo.modelo.peso.compartido.datos[new_id] = torch.randn(embed_dim) * 0.02
Paso 4: Entrenamiento modelo
Ajustamos el modelo de traducción utilizando el optimizador Adafactor, un algoritmo de optimización con eficiencia de memoria diseñado para modelos de secuencia a secuencia a gran escala. El programa de entrenamiento comienza con 500 pasos de calentamiento, durante los cuales la tasa de aprendizaje aumenta gradualmente hasta 1e-4 para estabilizar la optimización temprana y evitar picos repentinos de gradiente. Luego, el modelo se entrena para un total de 8000 pasos de optimización, con 64 pares de oraciones por paso de optimización (lote). La longitud máxima de la secuencia se establece en 128 tokens y el recorte de gradiente se aplica con un umbral de 1,0.
Inicialmente planeamos adoptar una parada anticipada. Sin embargo, debido al tamaño limitado del corpus bilingüe, casi todos los datos bilingües disponibles se utilizaron para el entrenamiento, dejando solo más de una docena de pares de oraciones reservados para las pruebas. En estas condiciones, no se disponía de un conjunto de validación de tamaño suficiente. Por lo tanto, aunque nuestro código base de GitHub incluye marcadores de posición para la detención anticipada, este mecanismo no se utilizó activamente en la práctica.
A continuación se muestra una instantánea de los hiperparámetros clave utilizados en el entrenamiento.
optimizador = Adafactor( [p para p en model.parameters() si p.requires_grad], scale_parameter=False, relativo_step=False, lr=1e-4, clip_threshold=1.0,weight_decay=1e-3,) tamaño_de_lote = 64 longitud_máxima = 128 pasos_de_entrenamiento = 8000 pasos_de_calentamiento = 500
También vale la pena señalar que, en el diseño de la función de pérdida, adoptamos una estrategia de entrenamiento computacionalmente eficiente. El modelo recibe oraciones fuente tokenizadas como entrada y genera la secuencia objetivo de forma incremental. En cada paso, el token predicho se compara con el token de referencia correspondiente en la oración objetivo y el objetivo de entrenamiento se calcula utilizando la pérdida de entropía cruzada a nivel de token.
loss = model(**x, etiquetas=y.input_ids).loss # El pseudocódigo a continuación ilustra el mecanismo subyacente de la función de pérdida para cada lote: x = tokenize(source_sentences) # entrada: tokens del idioma de origen y = tokenize(target_sentences) # destino: tokens de traducción de referencia predicciones = model.forward(x) # predecir distribuciones del siguiente token loss = cross_entropy(predictions, y) # comparar con tokens de referencia propagación hacia atrás (pérdida) update_model_parameters()
Esta formulación en realidad conlleva una suposición implícita: que la traducción de referencia representa la única respuesta correcta y que el resultado del modelo debe alinearse con ella token por token. Bajo este supuesto, cualquier desviación de la referencia se trata como un error. Incluso cuando una predicción transmite la misma idea utilizando diferentes palabras, sinónimos o una estructura de oración alterada.
La falta de coincidencia entre la supervisión a nivel de token y la corrección a nivel de significado es particularmente problemática en lenguajes de bajos recursos y morfológicamente flexibles. En la etapa de capacitación, este problema se puede aliviar relajando la estricta alineación a nivel de token y tratando múltiples oraciones objetivo parafraseadas como referencias igualmente válidas. En la etapa de inferencia, en lugar de seleccionar el resultado de mayor probabilidad, se puede generar un conjunto de traducciones candidatas y reclasificarlas utilizando criterios semánticamente informados (por ejemplo, chrF).
Paso 5: Evaluación del modelo
Una vez construido el modelo, el siguiente paso es examinar qué tan bien se traduce. La calidad de la traducción está determinada no sólo por el modelo en sí, sino también por cómo se configura el proceso de traducción en el momento de la inferencia. Según el marco NLLB, el idioma de destino debe especificarse explícitamente durante la generación. Esto se hace a través del parámetro force_bos_token_id, que ancla la salida al idioma deseado. La longitud de salida se controla mediante dos parámetros. El primero es la asignación mínima de producción (a), que garantiza una cantidad base de tokens que el modelo puede generar. El segundo es un factor de escala (b), que determina cómo crece la longitud máxima de salida en proporción a la longitud de entrada. El número máximo de tokens generados se establece como una función lineal de la longitud de entrada, calculada como a + b × input_length. Además, max_input_length limita la cantidad de tokens de entrada que lee el modelo.
Esta función potencia la traducción Dongxiang → Chino.
importar antorcha desde transformadores importar AutoModelForSeq2SeqLM, AutoTokenizer dispositivo = "cuda" if torch.cuda.is_available() else "cpu" MODEL_DIR3 = "/content/drive/MyDrive/my_nllb_CD_model" tokenizer3 = AutoTokenizer.from_pretrained(MODEL_DIR3) model3 = AutoModelForSeq2SeqLM.from_pretrained(MODEL_DIR3).to(dispositivo) model3.eval() def Translate3(text, src_lang="zho_Hans", tgt_lang="sce_Latn", a=16, b=1.5, max_input_length=1024, **kwargs): tokenizer3.src_lang = src_lang inputs = tokenizer3(texto, return_tensors="pt", padding=True, truncation=True, max_length=max_input_length).to(model3.device) resultado = model3.generate( **inputs, force_bos_token_id=tokenizer3.convert_tokens_to_ids(tgt_lang), max_new_tokens=int(a + b * inputs.input_ids.shape[1]), **kwargs ) salidas = tokenizer3.batch_decode(resultado, skip_special_tokens=True) devuelve salidas
Luego, la calidad del modelo se evalúa utilizando una combinación de métricas de evaluación automática y juicio humano. En el aspecto cuantitativo, informamos métricas de traducción automática estándar como BLEU y ChrF++. Las puntuaciones BLEU se calcularon utilizando el estándar BLEU-4, que mide la superposición de n-gramas a nivel de palabra desde unigramas hasta cuatro gramos y los combina utilizando una media geométrica con penalización por brevedad. ChrF++ se calculó sobre n-gramas a nivel de carácter y se informó como una puntuación F. Cabe señalar que la evaluación actual es preliminar. Debido a la disponibilidad limitada de datos en esta etapa inicial, las puntuaciones de BLEU y ChrF++ se calcularon en solo unas pocas docenas de pares de oraciones reservadas. Nuestro modelo logró los siguientes resultados:
Dongxiang → Chino (DX→ZH)
AZUL-4: 44,00
ChrF++: 34,3
Chino → Dongxiang (ZH→DX)
AZUL-4: 46,23
ChrF++: 59,80
Las puntuaciones BLEU-4 superiores a 40 generalmente se consideran sólidas en entornos de bajos recursos, lo que indica que el modelo captura la estructura de la oración y las opciones léxicas clave con una precisión razonable. Se espera una puntuación chrF++ más baja en la dirección Dongxiang → Chino y no necesariamente indica una calidad de traducción deficiente, ya que el chino permite una variación sustancial a nivel superficial en la elección de palabras y la estructura de las oraciones, lo que reduce la superposición a nivel de caracteres con una única traducción de referencia.
Paralelamente, los evaluadores bilingües con fluidez en ambos idiomas informaron que el modelo funciona de manera confiable en oraciones simples, como aquellas que siguen estructuras básicas sujeto-verbo-objeto. El rendimiento se degrada en oraciones más largas y complejas. Si bien estos resultados son alentadores, también indican que aún es necesario seguir mejorando.
Paso 6: Implementación
En la etapa actual, implementamos el proyecto a través de una configuración liviana alojando la documentación y la interfaz de demostración en GitHub Pages, mientras publicamos los modelos entrenados en Hugging Face. Este enfoque permite el acceso público y la participación de la comunidad sin incurrir en costos de infraestructura adicionales. Los detalles sobre la implementación basada en GitHub y el alojamiento del modelo Hugging Face siguen la documentación oficial proporcionada por GitHub Pages y Hugging Face Hub, respectivamente.
Este script carga un modelo compatible con Hugging Face entrenado localmente.
import os from huggingface_hub import HfApi, HfFolder # Cargar el token de acceso de Hugging Face token = os.environ.get("HF_TOKEN") HfFolder.save_token(token) # Ruta al directorio local que contiene los artefactos del modelo entrenado local_dir = "/path/to/your/local_model_directory" # ID del repositorio de Target Hugging Face Hub en el formato: nombre de usuario/nombre_repo repo_id = "your_username/your_model_name" # Cargue todo el directorio del modelo en Hugging Face Model Hub api = HfApi() api.upload_folder(folder_path=local_dir, repo_id=repo_id, repo_type="model",)
Después del lanzamiento del modelo, se implementa una interfaz basada en Gradio como Hugging Face Space y se integra en el sitio GitHub Pages del proyecto. En comparación con la autoimplementación basada en Docker, el uso de Hugging Face Spaces con Gradio evita el costo de mantener una infraestructura de nube dedicada.
Reflexión
A lo largo del proyecto, la preparación de datos, no la capacitación de modelos, dominó la carga de trabajo general. El tiempo dedicado a limpiar, validar y alinear los datos de Dongxiang y China superó con creces el tiempo necesario para ajustar el modelo en sí. Sin la participación del gobierno local y el apoyo de hablantes nativos y bilingües, completar este trabajo no habría sido posible. Desde una perspectiva técnica, este desequilibrio pone de relieve un problema más amplio de representación en la PNL multilingüe. Los idiomas de bajos recursos, como el dongxiang, están subrepresentados no debido a una complejidad lingüística inherente, sino porque los datos necesarios para respaldarlos son costosos de obtener y dependen en gran medida de la experiencia humana.
En esencia, este proyecto digitaliza un diccionario bilingüe impreso y construye un sistema de traducción básico. Para una comunidad de menos de un millón de personas, estos pasos incrementales desempeñan un papel enorme a la hora de garantizar que el idioma no quede excluido de las tecnologías lingüísticas modernas. Finalmente, ¡tomemos un momento para apreciar el impresionante paisaje del condado autónomo de Dongxiang!
Contacto
Este artículo fue escrito conjuntamente por Kaixuan Chen y Bo Ma, compañeros de clase en el Departamento de Estadística de la Universidad de Carolina del Norte – Chapel Hill. Kaixuan Chen actualmente está cursando una maestría en la Universidad Northwestern, mientras que Bo Ma está cursando una maestría en la Universidad de California, San Diego. Ambos autores están abiertos a oportunidades profesionales.
Si está interesado en nuestro trabajo o desea conectarse, no dude en comunicarse con:
Proyecto GitHub: https://github.com/dongxiangtranslationproject
Kaixuan Chen: [correo electrónico protegido]
Bo Ma: [correo electrónico protegido]