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?" \
}'
