Dicen que una de las habilidades que mejor manejan las IAs conversacionales es los idiomas. Tras trastear un tiempo con ChatGPT, no puedo estar más de acuerdo. Así que me pregunté si habría herramientas que me ayudaran con traducciones de texto usando archivos con contenido estructurado como los archivos PO.
Los archivos con la extensión .PO nos permiten gestionar las traducciones de WordPress, sus temas, extensiones, así como a otras aplicaciones y herramientas. Al buscar herramientas de traducción de archivos PO que usaran ChatGPT, encontré Gpto-po de ryanhex53. Me sirvió para ver el potencial y lanzarme a desarrollar mi propia herramienta con la que probar a fondo las distintas opciones de la API de OpenAI.
Tras realizar pruebas el resultado fue toda una sorpresa.
Consideraciones previas al uso de ChatGPT
Utilizaremos la API de OpenAI para traducir con ChatGPT. Esta API funciona con su propia bolsa de USD y su uso se tarifica a través de tokens. En su versión gratuita el uso de los tokens está limitado.
Este artículo es bastante denso y conviene aclarar previamente ciertos aspectos o conceptos. La lista a continuación nos permitirá avanzar más fluidamente por el artículo y servirá como referencia en caso de duda:
- Token: unidad mínima en la que los modelos de lenguaje ( GPT’s) de OpenAI dividen una palabra para luego poder tarificarla. No es uniforme en todos los modelos de ChatGPT, ni la misma palabra representa siempre el mismo número de tokens en el mismo modelo.
Si quieres saber más:
- Coste: Traducir con la API de OpenAI tiene un coste por token. Esto puede influir en el modelo de ChatGPT que queramos hacer servir.
- ChatGPT usa un modelo de lenguaje creado por OpenAI. La API que utilizaremos es la propia de OpenAI.
- gptpo es una aplicación ejecutable en línea de comandos diseñada para gestionar traducciones de texto de archivos en formato PO, utilizando el modelos de lenguaje de inteligencia artificial de GPT (Generative Pre-trained Transformer).
Traduciendo con «Chat Completions API»
Chat Completions API, es una de las maneras que ofrece la API de openAI para acceder a ChatGPT de OpenAI. Para realizar experimentos, encontré en github la aplicación «gpt-po», una aplicación de línea de comandos desarrollada en JavaScript. «gpt-po» se conecta a ChatGPT de OpenAI y realiza llamadas usando el proceso de «Chat Completion». Es similar a abrir un chat, proporcionar instrucciones para que asuma el rol de traductor («prompting»), y enviar uno a uno los mensajes del archivo PO para traducir. Más abajo se describe el proceso paso a paso.
Todo apuntaba hacia el éxito, hacia una revolución…, pero el resultado fueron traducciones imprecisas, inconsistentes y con errores graves:
- Algunas traducciones directamente las interpretaba, y algunas en formato de pregunta, directamente las respondía saltándose las instrucciones.
- No respetaba la puntuación a final de frases, mucho menos los espacios en blanco.
- Imposible dar con resultados consistentes en el tiempo.
- Además, las traducciones que realizaba del Inglés al Catalán, en la comunidad WordPress, están sujetas a un buen número de reglas. Tenemos incluso un Glosario que impone cierta rigidez y que ChatGPT a la fecha no era capaz de interpretar.
- Tampoco podía excederme en la longitud del «prompt», pues la modalidad «Chat completion» requiere que envíes el «prompt» para cada pregunta (en nuestro caso cada mensaje a traducir). Cuanto más extenso el prompt, más lento se hacia el proceso y más se encarecían los costes de traducción.
Tras lidiar con varios prompts y distintos modelos (gpt-3.5-turbo, gpt-4-1106-preview) abandoné mis experimentos.
Los GPT Assistants ¡Un gran avance!
En noviembre de 2023, durante el evento OpenAI Devday, se presentaron los Assistants, una nueva forma de contextualizar el trabajo con ChatGPT. El acceso a estos Assistants , se realiza a través de una interfaz de programación de aplicaciones (API).
Todo un avance que podría ayudar a solventar los numerosos problemas con los que me había encontrado hasta el momento.
En pocas palabras, con los Assistants puedes ajustar un prompt para luego reciclarlo a lo largo del chat. Y algo muy útil, puedes adjuntar archivos para enriquecer el prompt y el posterior proceso.
Lo primero que hice fue construir un Assistant en el propio panel de OpenAI.
Le di una buena lista de instrucciones y le adjunté un Glosario en formato texto para que lo pudiera usar para supervisar sus propias traducciones.
Y los resultados, mucho mejor.
El «Assistant» resultante está publicado y lo podéis probar en este enlace https://chat.openai.com/g/g-AtqPosD0C-enca-translator
Usando Assistants de OpenAI para traducir archivos PO
Tras los resultados obtenidos con el Assistant, volví a recuperar las esperanzas, así que me propuse incorporar esta nueva modalidad al traductor gpt-po.
Hice una copia en GitHub del traductor, bueno un fork al que llamé «gptpo«, y al poco tiempo ya tenía incorporado «Assistants» para probarlo contra un archivo PO y un buen volumen de mensajes.
Valorando la traducción de archivos PO con Assistant
Tras evaluar el resultado de la traducción, la valoración del uso de Assistant fue: «no está mal», podría llegar a ser útil.
Efectivamente: no era un mal resultado, era un avance, pero me encontré con:
- Imprecisiones, muchas imprecisiones e inconsistencias. Nuevamente los espacios y signos de puntuación a final de cadena no se respetaban.
- No siempre seguía las reglas del glosario.
- Lentitud. Para traducir 300 cadenas (con un volumen de unos 2900 tokens) necesitaba casi 1 hora. Tal vez fuera el uso del modelo
gpt-4-1106-preview
, o la carga añadida de supervisión de las traducciones para seguir las reglas del glosario que le adjunté en forma de archivo. - Los costes se dispararon. Los tokens con el modelo
gpt-4
son 20 veces más caros. No obstante, el hecho de no enviar el prompt cada vez y poder preservar el contexto (thread) pensé que podía ayudar. El resultado no parecía estar de acuerdo con mi teoría, la ventana de contexto se hizo muy grande. Para generar 2909 tokens de traducción necesitó 169546 tokens de contexto:
De nuevo el resultado me pareció estar lejos de lo esperado. Tal vez se me escapara algún detalle de implementación. Algo parecía no estar bien, y me dispuse a releer a fondo los manuales de la API.
Mejores traducciones con Fine-Tuning de ChatGPT
No se cómo se me había pasado por alto, el fine-tuning me pareció al instante la opción interesante para intentar acotar las imprecisiones y sobretodo para darle indicaciones concretas al modelo.
El fine-tuning permite ajustar y mejorar el rendimiento del modelo específicamente para una tarea concreta, lo que ayuda a reducir imprecisiones y proporcionar indicaciones precisas.
En concreto, el fine-tuning de ChatGPT es un proceso en el que se ajusta y personaliza el modelo de inteligencia artificial ChatGPT para que se adapte mejor a un conjunto específico de datos o necesidades. Es como enseñarle a ChatGPT a ser mejor en un área específica mediante la práctica con ejemplos relevantes.
En la Documentación, en el apartado de fine-tuning se dice:
El «fine-tuning» o «afinado» de los modelos de OpenAI puede hacerlos mejores para aplicaciones específicas, pero requiere una cuidadosa inversión de tiempo y esfuerzo.
Con la emoción, no consideré la inversión de tiempo y esfuerzo como algo importante, así que decidí ajustar un modelo sin pensarlo mucho.
Podéis encontrar información detallada en la documentación de OpenAI, y también más adelante, en este mismo post, un paso a paso del proceso.
Mejorando «gptpo» con el «fine-tuning» de ChatGPT
Para facilitar el proceso añadí los comandos para hacer «fine-tuning» a la aplicación «gptpo». De esta manera con «gptpo» podemos afinar un modelo, para luego usarlo en la traducción de archivos PO.
gptpo es una aplicación ejecutable en línea de comandos que usa el motor Node.js, y que está diseñada para gestionar traducciones de archivos PO utilizando modelos de lenguaje de OpenAI a través de su API.
Instalar gptpo para gestionar traducciones
Si no dispones de Node.js en tu ordenador. En la página oficial encontrarás los instaladores: https://nodejs.org/en/download
Una vez disponemos de Node.js, ya podemos instalar la aplicación gptpo con el comando:
sudo npm install -g @sitamet/gptpo
Para más detalles puedes consultar el «Readme» del repositorio de github: https://github.com/sitamet/gptpo
A partir de ahora, ya tienes a tu disposición el comando gptpo. Verifica que todo ha ido bien probándolo con un simple gptpo --help
~ » gptpo --help
Usage: @sitamet/gptpo [options] [command]
Translation command line tool for gettext (po) files that supports pre-translation and chat-gpt translations with assitant and fine tunning of models.
Options:
-V, --version output the version number
-h, --help display help for command
Commands:
translate [options] translate po file with completions (default command)
translate-assistant [options] translate po file with assistant
fine-tuning [options] launch a new fine-tune task to customize your OpenAI model
fine-tuning-jobs [options] list fine-tune jobs
preprocess [options] update po from previous translated po files (origin po will incorporate translations from previous po files)
help [command] display help for command
Necesitarás una API Key de OpenAI
Para usar gptpo y hacer traducciones usando la API de OpenAI, es preciso autorizar las llamadas con una llave única. Para obtener la API Key, dirígete a https://platform.openai.com/api-keys
Identifícate y luego en la página API keys usa el boton «+ Create new secret key»:
Cada vez que usemos gptpo
para traducir, tendremos que aportar nuestra API key. Para evitar tener que «arrastrar» la API key en todas las llamadas del comando, la guardaremos en una variable de entorno:
export OPENAI_API_KEY=sk-F***********************************vGL
Traducir un archivo PO con los modelos de OpenAI
Aunque acabaremos traduciendo con un modelo «tuneado», empezaremos usando uno de los modelos estándar de OpenAI. La idea es recolectar todos los errores que comete el «modelo» para ajustarlo a continuación mediante «fine-tuning».
OpenAI pone distintos límites y precios a cada uno de sus modelos.
Más información sobre modelos disponibles en https://platform.openai.com/docs/models/overview
En mi caso me decanté por usar gpt-3.5-turbo-1106, por dos motivos:
- El objetivo es poder traducir volumenes elevados de cadenas sin tener que pagar mucho por ello.
- Al usarlo con fine-tunning y con un tamaño de contexto no muy amplio, 3.5-turbo-1106 podía equiparar los resultados de gpt-4 e incluso ser más veloz.
Aunque este escenario puede cambiar conforme OpenAI va entregando nuevos modelos.
Guardar el modelo en variable de entorno
Al igual que con la API Key, para no arrastrar el modelo en las llamadas al comando gptpo, lo guardamos en una variable de entorno:
export OPENAI_API_MODEL=gpt-3.5-turbo-1106
Definir un «system prompt» conciso
Es importante remarcar que, a diferencia del «system prompt» que construiriamos usando un modelo estándar, es decir: un «prompt» refinado, extenso detallado,… como vamos a trabajar con «fine-tuning», vamos a usar un «system prompt» conciso. Y ya nos ocuparemos de pulir y dar forma a las respuestas usando el afinamiento o «fine-tuning».
Guardaremos el texto de nuestro «system prompt» en systemprompt.txt
en el directorio de trabajo desde donde vayamos lanzar el comando gptpo:
cd demo
echo 'You are a machine translating English to Catalan text.' > systemprompt.txt
Traducir un archivo PO con gptpo
Si queremos traducir las cadenas de texto de `test.po`, como ya tenemos las variables de entorno definidas, el comando a lanzar es relativamente sencillo:
gptpo translate --po ./test.po --verbose
░░░░░░░░░░░░░░░░░░░░ 0% 0/1
==> The plugin will load as a regular plugin
==> El plugin es carregarà com un plugin regular.
████████████████████ 100% 1/1 done.
El parámetro --verbos
e muestra sobre la marcha las cadenas que va traduciendo
Traducir un archivo PO con un modelo afinado
Al traducir cadenas usando el modelo estándar y un prompt escueto, daremos con un montón de casos que tendremos que corregir. Es el momento de perfilar el modelo para que responda adecuadamente.
El fine-tuning consiste en aportar un conjunto de «entradas» y «respuestas deseadas», para que el modelo se ajuste a nuestras especificaciones
Más información en: https://platform.openai.com/docs/guides/fine-tuning
Fine-tuning con la ayuda de gptpo
Aunque dispones de un apartado en la web de OpenAI especifico para trabajar el fine-tuning, decidí incorporar los comandos en la aplicación gptpo y así poder operar desde la línea de comando. Más adelante veremos cómo usarlo.
El primer paso para un fine-tuning, consiste en elaborar el archivo de tipo JSONL con todos los mensajes a usar para ajustar el modelo.
Construir un dataset para afinar el modelo
El «afinado» consiste en enviar un conjunto de datos o «dataset» donde pondremos el texto a traducir y el resultado que deseemos, un número finito de veces, contemplando todos los casos donde el modelo no responde al resultado que deseamos.
Para construir el modelo de datos o «dataset», dispones de información detallada de cómo preparar tu «dataset» en esta sección de la documentación. Pero no es nada complicado, más adelante lo verás claro con el ejemplo que te mostramos.
Cosas a tener en cuenta:
- El archivo se escribe en un formato especifico JSON Lines que debemos respetar.
- El prompt actuará como un «disparador». Es como esa frase que usan los hipnotizadores para pasar a controlar la mente del hipnotizado. Elegiremos una frase corta, significativa y la repetiremos siempre igual.
- Cada línea en el archivo es un mensaje, donde encontraremos secciones etiquetadas por «role».
- Tenemos 3 roles:
- system: aquí va el «prompt» e indicaremos siempre la misma frase.
- «user»: aquí va directamente el texto que nosotros queremos traducir,
- «assistant»: aquí viene el «afinado» pues indicaremos con exactitud cómo debería ser la traducción.
- No te limites a unos pocos mensajes. La documentación menciona un mínimo de 10. En mi caso envié alrededor de 150. El propósito es reforzar el modelo con ejemplos bien hechos y haciendo hincapié en todos los casos donde ChatGPT comete errores e imprecisiones.
- Utiliza el chat con su modelo estándar para ver dónde se dan errores y trabajar específicamente con esos errores para perfilar el modelo.
Para construir el archivo de tuning o afinado, con la ayuda de un editor de texto, prepara un archivo de texto y construye lo que sería un molde. Así podrás concentrarte en el «entrenamiento», pues solo tendrás que ir rellenando los agujeros con los mensajes de «user» y «assistant»:
{"messages": [{"role": "system", "content": "You are a machine translating English to Catalan text."}, {"role": "user", "content": ""}, {"role": "assistant", "content": ""}]}
{"messages": [{"role": "system", "content": "You are a machine translating English to Catalan text."}, {"role": "user", "content": ""}, {"role": "assistant", "content": ""}]}
{"messages": [{"role": "system", "content": "You are a machine translating English to Catalan text."}, {"role": "user", "content": ""}, {"role": "assistant", "content": ""}]}
{"messages": [{"role": "system", "content": "You are a machine translating English to Catalan text."}, {"role": "user", "content": ""}, {"role": "assistant", "content": ""}]}
{"messages": [{"role": "system", "content": "You are a machine translating English to Catalan text."}, {"role": "user", "content": ""}, {"role": "assistant", "content": ""}]}
{"messages": [{"role": "system", "content": "You are a machine translating English to Catalan text."}, {"role": "user", "content": ""}, {"role": "assistant", "content": ""}]}
{"messages": [{"role": "system", "content": "You are a machine translating English to Catalan text."}, {"role": "user", "content": ""}, {"role": "assistant", "content": ""}]}
...
A continuación, con paciencia y precisión iremos rellenando línea a línea insertando el campo «content» de «user» y «assistant»:
{"messages": [{"role": "system", "content": "You are a machine translating English to Catalan text."}, {"role": "user", "content": "File does not exist."}, {"role": "assistant", "content": "El fitxer no existeix."}]}
{"messages": [{"role": "system", "content": "You are a machine translating English to Catalan text."}, {"role": "user", "content": "Skipping malware scan "}, {"role": "assistant", "content": "Omet l'escaneig de malware "}]}
{"messages": [{"role": "system", "content": "You are a machine translating English to Catalan text."}, {"role": "user", "content": "Stop Ignoring"}, {"role": "assistant", "content": "Deixa d’ignorar"}]}
{"messages": [{"role": "system", "content": "You are a machine translating English to Catalan text."}, {"role": "user", "content": "No IPs blocked yet."}, {"role": "assistant", "content": "Encara no hi ha IPs blocades."}]}
{"messages": [{"role": "system", "content": "You are a machine translating English to Catalan text."}, {"role": "user", "content": "Error Activating 2FA"}, {"role": "assistant", "content": "Error en activar el 2FA"}]}
...
Lanzar una tarea de afinado
Para indicarle a OpenAI que nos «entrene» el modelo con los mensajes de fine-tuning-ca.jsonl
indicaremos:
gptpo fine-tuning --suffix cat01 --file ./fine-tuning-ca.json
El --suffix
nos ayudará a versionar el modelo resultante. Si más adelante refinamos modelos préviamente ajustados, el versionado nos ayudará a ordenar la colección de modelos resultante.
Ver el estado del afinado de mi modelo
Tras ejecutar el comando de fine-tuning, OpenAI lo pone en cola y te avisa al cabo de unos minutos por correo electrónico
Con la ayuda de gptpo podemos listar los modelos y ver el estado de la tarea de afinado de la siguiente manera:
gptpo fine-tuning-jobs
[
{
object: 'fine_tuning.job',
id: 'ftjob-2u8sMZaK82tKXpmIr9Rt1iXM',
model: 'gpt-3.5-turbo-1106',
created_at: 1704570847,
finished_at: 1704571377,
fine_tuned_model: 'ft:gpt-3.5-turbo-1106:wetopi:cat01:8e7AgzNb',
organization_id: 'org-iMMKhtTcRklst7guwU9LzPr6',
result_files: [ 'file-bg27iX6hYBIN2rCtDlkkZon6' ],
status: 'succeeded',
validation_file: null,
training_file: 'file-7vDQosGoZ48fni1ekm0TBJE3',
hyperparameters: { n_epochs: 3, batch_size: 1, learning_rate_multiplier: 2 },
trained_tokens: 12555,
error: null
}
]
El resultado es un objeto JSON . Entre los datos relevantes tenemos:
- model: el modelo de entrada que queremos ajutar.
- status: nos indica el estado del proceso de fine-tuning.
- fine_tuned_model: el nombre del modelo resultante del fine-tuning.
Recuerda que puedes seguir ajustando un modelo previamente ya afinado.
Si los resultados de traducción todavía arrojan imprecisiones y errores, envía un nuevo dataset al modelo préviamente afinado para seguir ajustándolo.
En el panel de control de OpenAI tambien podemos ver el resultado del fine-tuning del modelo:
Traducir un archivo po con gptpo usando un modelo afinado
Ya tenemos un modelo afinado al que OpenAI a dado el nombre de ft:gpt-3.5-turbo-1106:wetopi:cat01:8e7AgzNb
El comando que usaremos en gptpo no cambia. Eso si tendremos que indicarle el nuevo modelo. Para ello podemos pasarle al comando el nombre del nuevo modelo como un parámetro --model
=ft:gpt… o como hemos hecho con anterioridad, guardarlo en la variable de entorno OPENAI_API_MODEL
export OPENAI_API_MODEL=ft:gpt-3.5-turbo-1106:wetopi:cat01:8e7AgzNb
Y lanzar como antes el comando gptpo translate
cd demo
gptpo translate --po ./test.po --verbose
░░░░░░░░░░░░░░░░░░░░ 0% 0/1
==> The plugin will load as a regular plugin
==> L'extensió es carregarà com una extensió
████████████████████ 100% 1/1 done.
Valorando el resultado de la traducción
Tras entrenar el modelo con un total de 145 mensajes cuidadosamente elegidos, evalué el modelo «tuneado» lanzando la traducción de 1050 cadenas.
La valoración final es muy positiva. El modelo ajustado, es capaz de centrarse en el tono; es extricto en cuanto a respetar el código, las puntuaciones, los espacios en blanco a final de texto.
Comparando con el modelo estándar, el resultado es sorprendente. La traducción es casi perfecta en la semántica, el tono y persona empleados. Eso sí: sigue siendo imprescindible revisar el resultado. En algunos casos inventa términos, o expresa mal conceptos técnicos. En mi caso reentrené el modelo varias veces para ir ajustando los errores.
Análisis de costes
El resultado es bueno, pero ¿a qué coste?
El fine-tuning de 145 mensajes consumió un total de 12.555 tokens lo que representó un coste de 0,10$
Para la traducción de los 1.050 mensajes, se utilizaron 87.418 tokens costando 0,37$ (he descontado el fine-tuning del dia 6/1)
Traducción de archivos PO a partir de textos anteriormente traducidos
Al margen de poder obtener buenas traducciones, una de las funcionalidades más deseadas es el poder aprovechar traducciones previamente ya realizadas y validadas. Aquí el comando gptpo
también nos será de gran ayuda.
En tu directorio de trabajo crea un subdirectorio y llámalo p.ej previous
. Dentro, guarda todos los archivos «po» que puedan tener relación con la temática que estas abordando en tus traducciones.
gptpo intentará rellenar todas las traducciones pendientes con las coincidencias que pueda encontrar en todos los archivos:
cd demo
gptpo preprocess --po my-plugin.po --prev ./previous
Preprocessing PO file: my-plugin.po
· Get translations from: previous/wordpress-tm.po
· Get translations from: previous/wordpress_plugins-tm.po
· Preprocessed 2 files, translated 31 messages.
Para finalizar
Abordé este proyecto como un experimento para ayudar en la traducción durante las jornadas de colaboración WordPress dentro del marco Five for the Future que los viernes llevamos a cabo entre amigos. El resultado, gptpo está disponible para que lo puedas usar, tirar, desbrozar, refactorizar, … Y si te apetece colaborar para mejorarlo, tu ayuda será siempre bienvenida.
¿No tienes una cuenta en wetopi?
Incluye servidores desarrollo Gratis.
Sin tarjeta de crédito.
Estos posts te podrían interesar