Hoy vamos a crear una versión custom de Llama 3.2 mediante la técnica de Fine-tunning usando Google Colab cómo máquina de entrenamiento.
Primero tenemos que ir a Google Colab y crear un nuevo cuaderno. https://colab.research.google.com/
Una vez creado vamos a ir a Entorno de ejecución o Runtime y pulsamos en cambiar tipo de entorno de ejecución:
Elegimos T4 GPU
Y ahora vamos a configurar el entorno.
Para ello escribimos en el cuaderno:
!pip install unsloth
En el siguiente párrafo de código:
from unsloth import FastLanguageModel from unsloth.chat_templates import get_chat_template import torch max_seq_length = 2048 dtype = None load_in_4bit = True model, tokenizer = FastLanguageModel.from_pretrained( model_name = "unsloth/Llama-3.2-1B-bnb-4bit", max_seq_length = max_seq_length, dtype = dtype, load_in_4bit = load_in_4bit, ) # Cargar el template de chat tokenizer = get_chat_template( tokenizer, # El tokenizer cargado chat_template="llama-3.1" # En este caso usamos el template de llama 3.1 )
Aquí tenemos la lista de modelos: https://huggingface.co/unsloth
En mi caso uso: unsloth/Llama-3.2-1B-bnb-4bit también se puede usar unsloth/Llama-3.2-3B-Instruct
Ahora ejecutamos:
Una vez ejecutado, está todo correcto y el modelo descargado.
Podemos seguir con la configuración de fine-tune:
model = FastLanguageModel.get_peft_model( model, r = 16, target_modules = ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj",], lora_alpha = 16, lora_dropout = 0, bias = "none", use_gradient_checkpointing = "unsloth", random_state = 3407, use_rslora = False, loftq_config = None, )
Ahora para el conjunto de entrenamiento vamos a utilizar el formato Llama 3.1.
Ahora creamos los datos de entrenamiento con este formato:
{ "conversations":[ {"role": "user", "content": "¿Pregunta?"}, {"role": "assistant", "content": "Respuesta..."} ] }
Quedando asi:
# Ejemplo de datos de entrenamiento training_data = [ { "conversations": [ {"role": "user", "content": "¿Cómo hago un pedido?"}, {"role": "assistant", "content": "Para hacer un pedido, selecciona los productos que desees, agrégales al carrito y sigue los pasos para completar el pago."} ] }, { "conversations": [ {"role": "user", "content": "¿Cuánto tarda en llegar mi pedido?"}, {"role": "assistant", "content": "El tiempo de entrega es de 3 a 5 días hábiles."} ] }, { "conversations": [ {"role": "user", "content": "¿Qué métodos de pago aceptan?"}, {"role": "assistant", "content": "Aceptamos tarjetas de crédito, PayPal y transferencia bancaria."} ] }, { "conversations": [ {"role": "user", "content": "¿Puedo cancelar mi pedido?"}, {"role": "assistant", "content": "Sí, puedes cancelar tu pedido dentro de las 24 horas después de realizarlo."} ] }, { "conversations": [ {"role": "user", "content": "¿Cómo puedo rastrear mi pedido?"}, {"role": "assistant", "content": "Recibirás un correo con el número de seguimiento una vez que se haya enviado tu pedido."} ] } ] # Convertir el ejemplo a un dataset import pandas as pd # Convertir la lista de conversaciones a un DataFrame df = pd.DataFrame(training_data) dataset = df.to_dict(orient='records') from datasets import Dataset formatted_dataset = Dataset.from_dict({ "conversations": [convo["conversations"] for convo in dataset] # Solo extraemos la lista de conversaciones })
Con esta plantilla podemos ir completando nuestros datos de entrenamiento.
La transformamos a formato Hugging Face:
from unsloth.chat_templates import standardize_sharegpt # Estandarizar el dataset standardized_dataset = standardize_sharegpt(formatted_dataset)
Ahora vamos a crear un bucle que aplicará el template esperado a los datos de entrenamiento, de esta forma nos permitirá entrenar el modelo:
from unsloth.chat_templates import get_chat_template from transformers import AutoTokenizer def formatting_prompts_func(examples): texts = [] for convo in examples["conversations"]: convo_text = "" try: for turn in convo: role = turn["role"] content = turn["content"] # Crear el texto usando el formato deseado text = f"<|{role}|>{content}<|endoftext|>" convo_text += text + " " texts.append(convo_text.strip()) except Exception as e: print(f"Error procesando la conversación: {convo}, error: {e}") return {"text": texts} # Formatear el dataset formatted_dataset = standardized_dataset.map(formatting_prompts_func, batched=True) print(formatted_dataset) print(formatted_dataset[:5]["conversations"]) # Muestra las primeras 5 entradas del dataset print(formatted_dataset[:5]["text"]) #Muestra el formato text
Ahora aplicamos el entrenador:
from trl import SFTTrainer from transformers import TrainingArguments, DataCollatorForSeq2Seq from unsloth import is_bfloat16_supported # Configuración del entrenador trainer = SFTTrainer( model=model, # Asegúrate de que tienes tu modelo cargado tokenizer=tokenizer, train_dataset=formatted_dataset, # Dataset formateado dataset_text_field="text", max_seq_length=512, # Ajusta esto según sea necesario data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer), dataset_num_proc=2, packing=False, args=TrainingArguments( per_device_train_batch_size=2, gradient_accumulation_steps=4, warmup_steps=5, num_train_epochs=1, # Establece esto para el número de épocas que desees learning_rate=2e-4, fp16=not is_bfloat16_supported(), bf16=is_bfloat16_supported(), logging_steps=1, optim="adamw_8bit", weight_decay=0.01, lr_scheduler_type="linear", seed=3407, output_dir="outputs", ), )
Ajustamos el formato:
from unsloth.chat_templates import train_on_responses_only trainer = train_on_responses_only( trainer, instruction_part = "<|start_header_id|>user<|end_header_id|>\n\n", response_part = "<|start_header_id|>assistant<|end_header_id|>\n\n", ) tokenizer.decode(trainer.train_dataset[2]["input_ids"])
Y finalmente entrenamos
trainer_stats = trainer.train()
Ollama format:
!curl -fsSL https://ollama.com/install.sh | sh
Guardar el modelo a formato Ollama:
Con este fichero podemos transformar a distintos formatos:
model.save_pretrained_gguf("model", tokenizer,)
Ahora arrancamos el servidor Ollama:
import subprocess subprocess.Popen(["ollama", "serve"]) import time time.sleep(3) # Wait for a few seconds for Ollama to load!
Generar el archivo Modelfile:
print(tokenizer._ollama_modelfile)
Ahora nos imprimirá el Modelfile, debemos modificar la ruta y copiar el contenido para guardarlo dentro de:
model/Modelfile
En mi caso queda así:
FROM ./model/unsloth.Q8_0.gguf TEMPLATE """{{ if .Messages }} {{- if or .System .Tools }}<|start_header_id|>system<|end_header_id|> {{- if .System }} {{ .System }} {{- end }} {{- if .Tools }} You are a helpful assistant with tool calling capabilities. When you receive a tool call response, use the output to format an answer to the orginal use question. {{- end }} {{- end }}<|eot_id|> {{- range $i, $_ := .Messages }} {{- $last := eq (len (slice $.Messages $i)) 1 }} {{- if eq .Role "user" }}<|start_header_id|>user<|end_header_id|> {{- if and $.Tools $last }} Given the following functions, please respond with a JSON for a function call with its proper arguments that best answers the given prompt. Respond in the format {"name": function name, "parameters": dictionary of argument name and its value}. Do not use variables. {{ $.Tools }} {{- end }} {{ .Content }}<|eot_id|>{{ if $last }}<|start_header_id|>assistant<|end_header_id|> {{ end }} {{- else if eq .Role "assistant" }}<|start_header_id|>assistant<|end_header_id|> {{- if .ToolCalls }} {{- range .ToolCalls }}{"name": "{{ .Function.Name }}", "parameters": {{ .Function.Arguments }}}{{ end }} {{- else }} {{ .Content }}{{ if not $last }}<|eot_id|>{{ end }} {{- end }} {{- else if eq .Role "tool" }}<|start_header_id|>ipython<|end_header_id|> {{ .Content }}<|eot_id|>{{ if $last }}<|start_header_id|>assistant<|end_header_id|> {{ end }} {{- end }} {{- end }} {{- else }} {{- if .System }}<|start_header_id|>system<|end_header_id|> {{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|> {{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|> {{ end }}{{ .Response }}{{ if .Response }}<|eot_id|>{{ end }}""" PARAMETER stop "<|start_header_id|>" PARAMETER stop "<|end_header_id|>" PARAMETER stop "<|eot_id|>" PARAMETER stop "<|eom_id|>" PARAMETER temperature 1.5 PARAMETER min_p 0.1
Ahora podemos importarlo a ollama:
!ollama create unsloth_model -f ./nuevo_modelo/Modelfile
Una vez importado, podemos realizarle consultas:
!curl http://localhost:11434/api/generate -d '{ \ "model": "unsloth_model", \ "stream": false, \ "prompt": "¿Cuánto tarda en llegar mi pedido?" \ }'
NOTA: Para que funcione mejor, debemos entrenarlo con bastantes datos.
Publicar modelo en huggingface:
- Primero debes obtener un token: https://huggingface.co/settings/tokens
- Después enejugar este código:
# https://huggingface.co/settings/tokens for a token! model.push_to_hub_gguf("hf/model", tokenizer, token = "")
Guardar en diferentes formatos:
16bit GGUF:
model.save_pretrained_gguf("model", tokenizer, quantization_method = "f16") model.push_to_hub_gguf("hf/model", tokenizer, quantization_method = "f16", token = "")
q4_k_m GGUF:
model.save_pretrained_gguf("model", tokenizer, quantization_method = "q4_k_m") model.push_to_hub_gguf("hf/model", tokenizer, quantization_method = "q4_k_m", token = "")
Guardar el Modelo Usando Múltiples Opciones de Cuantización:
model.push_to_hub_gguf( "hf/model", # Change hf to your username! tokenizer, quantization_method = ["q4_k_m", "q8_0", "q5_k_m",], token = "", )
*En token debes especificar el token de Huggin Face
Una vez entrenado podemos guardarlo de la siguiente forma:
# Guardar el modelo entrenado y el tokenizer output_dir = "./output_dir" # Directorio donde se guardará el modelo trainer.save_model(output_dir) # Guarda el modelo tokenizer.save_pretrained(output_dir) # Guarda el tokenizer # También puedes guardar la configuración del modelo directamente model.config.save_pretrained(output_dir) # Guarda la configuración del modelo
Descargar modelo creado:
Comprimimos el modelo:
!zip -r nuevo_modelo.zip nuevo_modelo/
Lo descargamos:
from google.colab import files files.download('nuevo_modelo.zip')
Ahora podemos utilizarlo por ejemplo en Ollama con Docker compose: https://devcodelight.com/ollama-con-llama-3-2-en-docker/
Primero descomprimimos el zip descargado en la carpeta ./models
Despues levantamos el docker compose si no lo tenemos levantado:
docker compose up -d
Y ahora cargamos el modelo:
ollama create unsloth_model -f ./nuevo_modelo/Modelfile
Y genera el modelo llamado unsloth_model (podemos llamarlo como queramos).
Finalmente podemos ejecutarlo:
!curl http://localhost:11434/api/generate -d '{ \ "model": "unsloth_model", \ "stream": false, \ "prompt": "¿Cuánto tarda en llegar mi pedido?" \ }'