Construyendo un Agente de IA Nativo con C y Llama.cpp (III)

Con el modelo y el contexto creado, vamos a una parte muy interesante que es la Tokenización, un resumen muy simple es pasar el texto a números, para poder realizar la inferencia.

Convertiremos la pregunta a IDs y el modelo LLM nos dará IDs que deberemos destokenizar para convertirlo a texto.

const vocab_t *vocab = llama_model_get_vocab(modelo);  // Cargamos el vocabulario del modelo.

// Debemos crear un 

Cada modelo tendrá su propio vocabulario por eso lo primero que hacemos es descargar el vocabulario del modelo.

El texto que le pasamos a tokenizar debe ser un PROMPT que tendrá palabras claves, que nos permite definir el rol del sistema que indica como debe comportarse, el del usuario que es nuestra pregunta y en la respuesta tendremos el rol assistant.

<|begin_of_text|>: Indica el inicio del documento.

<|start_header_id|>system<|end_header_id|>: Aquí empieza el ROL.

<|start_header_id|>user<|end_header_id|>: Aquí empieza la pregunta.

<|eot_id|>: Fin de turno (lo que hablábamos antes).

Para pasar nuestro mensaje a este formato usaremos la función «llama_chat_apply_template»

// Preparamos prompt con su rol de system y user
const char *rolSystem = "La forma de comunicarme es en Español";
mensajeChat_t mensajes[] = {
      {"system", rolSystem },
      {"user",   prompt}
};

llama_chat_apply_template(llama_model_chat_template(modelo, NULL), mensajes,2, true, prompt_formateado, bytes_prompt_final);
prompt_formateado[bytes_prompt_final] = '\0'; // Asegurar cierre de cadena de C
		
llama_tokenize(vocab,prompt_formateado,strlen(prompt_formateado),prompt_tokens,n_tokens,false,true)

En el código se puede ver con más detalle ya que deberemos crear con malloc el espacio de memoria suficiente para contener el prompt. Hasta este momento solo hemos pasado nuestra pregunta con el rol que utilizaremos a IDs, ahora deberemos realizr la inferencia pero antes debemos crear el «sampler» que nos permitirá definir como seleccionar las posibles opciones.

paramSampler_t sparams = llama_sampler_chain_default_params(); 
sampler_t *miSampler = llama_sampler_chain_init(sparams);
// Definimos la forma de elegir  
llama_sampler_chain_add(miSampler, llama_sampler_init_greedy()); // Elige la mas exata
llama_sampler_chain_add(miSampler, llama_sampler_init_min_p(0.05f, 1)); // Min-P
llama_sampler_chain_add(miSampler, llama_sampler_init_temp(0.8f));   //Temperatura
llama_sampler_chain_add(miSampler, llama_sampler_init_dist(LLAMA_DEFAULT_SEED)); // Distribución

Tenemos varias funciones de selección que pueden ser:

Función de InicializaciónParámetros Clave¿Qué hace exactamente?¿Cuándo usarlo? (Caso de uso)
llama_sampler_init_greedy()NingunoBúsqueda codiciosa: Elige siempre la palabra con el porcentaje más alto, ignorando la creatividad o el azar.Para tareas que requieren respuestas exactas e idénticas siempre (programación, matemáticas, JSON).
llama_sampler_init_temp(float temp)temp: Nivel de caos (ej. 0.7f).Temperatura: Escala las probabilidades. Una temperatura alta nivela el terreno (da oportunidad a palabras raras); una baja las distancia más.Es el ecualizador global. 0.2 para máxima lógica, 0.8 o 1.0 para chats creativos o escritura.
llama_sampler_init_top_k(int32_t k)k: Número de palabras (ej. 40).Top-K: Ordena las palabras de mayor a menor probabilidad y elimina todas las que queden por debajo del puesto k.Para evitar que el modelo elija palabras completamente absurdas o rotas fuera del top inicial.
llama_sampler_init_top_p(float p, size_t min_keep)p: Porcentaje (ej. 0.95f), min_keep: Mínimo a mantener.Top-P (Nucleus): Suma los porcentajes de las palabras más probables hasta llegar al p por ciento (ej. 95%) y descarta el resto.Es dinámico. Si el modelo está seguro, el Top-P se encoge automáticamente; si duda, se expande.
llama_sampler_init_min_p(float p, size_t min_keep)p: Escala respecto al líder (ej. 0.05f), min_keep: Mínimo.Min-P: Elimina cualquier palabra cuya probabilidad sea menor que un porcentaje del token ganador (ej. menos del 5% del líder).El sampler moderno más recomendado. Limpia el «ruido» de forma mucho más limpia que Top-P y Top-K.
llama_sampler_init_repetition(int32_t penalty_last_n, float penalty_repeat, float penalty_freq, float penalty_present)penalty_last_n: Ventana de memoria (ej. 64), penalty_repeat: Fuerza (ej. 1.1f).Penalización de repetición: Revisa los últimos n tokens escritos y les baja la probabilidad si el modelo intenta usarlos otra vez.¡Esencial! Evita que el modelo se quede atrapado en bucles infinitos repitiendo la misma frase una y otra vez.
llama_sampler_init_dist(uint32_t seed)seed: Semilla aleatoria (ej. 1234 o un número basado en el tiempo).Distribución: Es el encargado de tirar el dado final sobre las opciones supervivientes para elegir el token definitivo.Va siempre al final de la cadena (a menos que uses Greedy). Permite que la misma pregunta tenga respuestas variadas.

Ahora ya podremos generar nuestro batch y realizar la inferencia, mediante lo que llamamos bucle de inferencia que enviara al modelo la pregunta y recibira la respuesta que tendremos que decodificar para que la veamos como texto.

llama_batch miBatch = llama_batch_get_one(prompt_tokens, n_tokens);  // Crea batch

Bucle
   llama_decode(contexto, miBatch) // LLama a la inferencia.
   new_token_id = llama_sampler_sample(miSampler, contexto, -1);  
   if (llama_vocab_is_eog(vocab, new_token_id)) { break; }      // Esto es el fin de la generación

   llama_token_to_piece(vocab, new_token_id, buf, sizeof(buf), 0, true);
   buf[n] = 0;
   printf("%s", buf);    // Imprime segun vamos recibiendo
   // Prepara el siguiente lote con TOKEN sample
   miBatch = llama_batch_get_one(&new_token_id, 1);

No debemos olvidarnos antes de terminar nuestro programa de limpiar la memoria creada.

 printf("\n--- 9. Limpia memoria ---\n");
 llama_sampler_free(miSampler);
 free(prompt_tokens);
 free(prompt_formateado); 
 llama_free(contexto);
 llama_model_free(modelo);
 llama_backend_free();

Ya tenemos la programación básica, en el próximo hablaremos de como mantener contexto, que se podra ver en el otro ejemplo que explicaremos que es «Agente«

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Información básica sobre protección de datos Ver más

  • Responsable: Javier.
  • Finalidad:  Moderar los comentarios.
  • Legitimación:  Por consentimiento del interesado.
  • Destinatarios y encargados de tratamiento:  No se ceden o comunican datos a terceros para prestar este servicio. El Titular ha contratado los servicios de alojamiento web a Hostinger.es que actúa como encargado de tratamiento.
  • Derechos: Acceder, rectificar y suprimir los datos.
  • Información Adicional: Puede consultar la información detallada en la Política de Privacidad.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos. Contiene enlaces a sitios web de terceros con políticas de privacidad ajenas que podrás aceptar o no cuando accedas a ellos. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Ver
Privacidad