Ir al contenido principal

Ajuste fino de Qwen3.5 Small con QLoRA para clasificar noticias

Aprende a ajustar Qwen3.5-0.8B con QLoRA usando una configuración ligera que mejora notablemente el rendimiento en clasificación de noticias.
Actualizado 17 sept 2026  · 8 min leer

Explora con IA

ChatGPTClaudePerplexity

Qwen3.5 Small es un modelo muy práctico para ajustes finos ligeros: está pensado para prototipado, adaptación a tareas concretas y flujos de trabajo de investigación. Combinado con QLoRA, podemos ajustarlo sobre un modelo base cuantizado en 4 bits y congelado, haciendo el proceso mucho más eficiente en memoria y accesible en hardware limitado.

En este tutorial, configuraremos el entorno, cargaremos el modelo y el conjunto de datos AG News, evaluaremos el modelo base y después ajustaremos Qwen3.5 Small para clasificación de noticias. Cerraremos comparando los resultados antes y después del entrenamiento, y por último guardaremos y publicaremos el adaptador LoRA en Hugging Face para reutilizarlo. 

Te recomiendo realizar el itinerario de aprendizaje Hugging Face Fundamentals si aún no estás familiarizado con su funcionamiento.

¿Qué es Qwen3.5 Small?

Antes de entrar en el código, conviene entender el modelo con el que vamos a trabajar. Qwen3.5 es una familia de modelos fundacionales y la serie "Small" hace referencia a su línea ligera, en concreto los modelos con menos de 10.000 millones de parámetros. Para este tutorial usaremos la versión de 0,8B.

Aun teniendo solo 800 millones de parámetros, Qwen3.5-0.8B es muy capaz y extremadamente eficiente. Cargado en precisión de 4 bits, requiere menos de 2 GB de VRAM. Esto lo hace ideal para portátiles de consumo, entornos gratuitos en la nube como Kaggle y despliegues ligeros en el borde.

Además, la arquitectura Qwen3.5 admite una ventana de contexto enorme de 262K y multimodalidad nativa. Aunque aquí nos centramos estrictamente en clasificación de texto, los conceptos fundamentales pueden ampliarse fácilmente a tareas de visión más adelante. El modelo también incluye un modo de razonamiento híbrido, pero lo mantendremos desactivado para maximizar la velocidad de inferencia en esta tarea sencilla de etiquetado.

En conjunto, Qwen3.5-0.8B es una opción muy práctica para prototipar. Permite probar flujos de ajuste fino de forma rápida y a bajo coste, manteniendo un buen rendimiento aguas abajo.

1. Configura el entorno en Kaggle

En este tutorial usaremos un cuaderno de Kaggle como entorno de entrenamiento. Kaggle nos da acceso a GPUs, lo que lo convierte en una plataforma cómoda para entrenar, ajustar y evaluar el modelo. Antes de ejecutar el cuaderno, crea uno nuevo, abre los ajustes de Session y selecciona GPU P100 como acelerador. 

A continuación, configuraremos el entorno, añadiremos un token de Hugging Face a Kaggle e importaremos las librerías de Python que usaremos en todo el tutorial.

Crea un token de acceso de Hugging Face

Antes de ejecutar el cuaderno, crea un token de acceso de Hugging Face desde los ajustes de tu cuenta. Ve a Settings, abre Access Tokens y genera uno nuevo. Si quieres publicar el adaptador LoRA ajustado en el Hub de Hugging Face al final, asegúrate de que el token tenga permisos de escritura.

Hugging Face Access Token Page

Añade el token a los secrets de Kaggle

Tras generar el token, abre tu cuaderno de Kaggle y añádelo como secreto. En el editor del cuaderno, ve a Add-ons, luego Secrets y crea un nuevo secreto llamado HUGGINGFACE_TOKEN. Pega ahí el valor de tu token para que el cuaderno pueda acceder a él de forma segura sin exponerlo en el código.

Kaggle Secrets setup

Instala las librerías necesarias

Ahora podemos instalar las librerías principales para este flujo. Estos paquetes cubren carga de modelos, cuantización, ajuste eficiente en parámetros, manejo de datos y evaluación.

!pip install -q -U transformers accelerate
!pip install -q peft trl bitsandbytes datasets scikit-learn

Autentícate con Hugging Face

Después, cargamos el token de Hugging Face desde los secrets de Kaggle y lo usamos para autenticarnos. Esto da al cuaderno acceso al modelo y nos permitirá subir más tarde el adaptador entrenado.

from huggingface_hub import login
from kaggle_secrets import UserSecretsClient

user_secrets = UserSecretsClient()
login(token=user_secrets.get_secret("HUGGINGFACE_TOKEN"))

Importa las librerías de Python

Por último, importamos las librerías de Python que usaremos para cargar el modelo, entrenar, procesar el conjunto de datos y evaluar.

import torch
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model
from trl import SFTTrainer, SFTConfig
from transformers import TrainingArguments
from sklearn.metrics import accuracy_score, f1_score
from tqdm.notebook import tqdm

2. Carga el modelo Qwen3.5 y el conjunto de datos AG News

Con el entorno listo, ya podemos cargar el modelo base y preparar el conjunto de datos para el ajuste fino. 

En esta sección cargaremos Qwen3.5-0.8B en precisión de 4 bits para que el entrenamiento sea más eficiente en memoria, configuraremos el tokenizador y cargaremos un subconjunto pequeño de AG News para entrenamiento y evaluación.

Carga Qwen3.5-0.8B con cuantización de 4 bits

Empezamos cargando Qwen3.5-0.8B con cuantización de 4 bits usando BitsAndBytes. Esto reduce el uso de memoria y hace el modelo más manejable en un cuaderno de Kaggle.

model_id = "Qwen/Qwen3.5-0.8B"

bnb_config = BitsAndBytesConfig(
  load_in_4bit=True,
  bnb_4bit_quant_type="nf4",
  bnb_4bit_compute_dtype=torch.bfloat16
)

model = AutoModelForCausalLM.from_pretrained(
  model_id,
  quantization_config=bnb_config,
  device_map="auto",
  dtype=torch.bfloat16
)

Carga el tokenizador

A continuación cargamos el tokenizador del mismo modelo. También establecemos el token de relleno como el de fin de secuencia para que el modelo gestione correctamente el padding durante el entrenamiento y la generación.

tokenizer = AutoTokenizer.from_pretrained(model_id)
tokenizer.pad_token = tokenizer.eos_token

model.config.pad_token_id = tokenizer.eos_token_id
model.generation_config.pad_token_id = tokenizer.eos_token_id

Carga el conjunto de datos AG News

Por último, cargamos el conjunto de datos AG News y creamos divisiones de entrenamiento y prueba más pequeñas para este tutorial. Usar un subconjunto acelera el flujo de trabajo y aun así nos da datos suficientes para evaluar el efecto del ajuste fino.

dataset = load_dataset("ag_news")

train_dataset = dataset["train"].select(range(2500))
test_dataset = dataset["test"].select(range(200))

3. Formato del prompt y evaluación de referencia

Antes de ajustar, debemos definir cómo verá el modelo la tarea y medir su rendimiento en estado original. 

En esta sección crearemos un prompt con instrucciones para clasificar noticias, extraeremos predicciones de la salida del modelo, haremos una inferencia de muestra y evaluaremos el modelo base en el conjunto de prueba antes del entrenamiento.

Crea el prompt de instrucciones

Empezamos definiendo los nombres de las etiquetas y construyendo un prompt que pida al modelo clasificar un artículo. El prompt está redactado para que el modelo devuelva solo la etiqueta numérica, lo que facilita el análisis de la salida en la evaluación.

labels = ['World','Sports','Business','Sci/Tech']

def build_prompt(text):

    return f"""
Classify the news article.

Article:
{text}

Return ONLY the number of the correct label.

0 = World
1 = Sports
2 = Business
3 = Sci/Tech

Answer:
"""

Extrae predicciones de la salida del modelo

Creamos después una pequeña función auxiliar para extraer la etiqueta predicha del texto generado. Esta función elimina cualquier token de razonamiento adicional y devuelve la clase final como entero.

import re

def extract_label(text):

   text = re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL)
   matches = re.findall(r"[0-3]", text)

   if matches:
       return int(matches[-1])

   return -1

Ejecuta una inferencia de ejemplo

Antes de evaluar el modelo en todo el conjunto de prueba, lo probamos con un único ejemplo. Así podemos inspeccionar el artículo, comprobar la etiqueta correcta y ver qué predice el modelo antes del ajuste fino.

sample = train_dataset[10]

sample_text = sample["text"]
sample_label = sample["label"]

print("Ground truth label:", sample_label)
print("Sample text:")
print(sample_text[:300])

La salida muestra que la etiqueta real es 2, que corresponde a Business.

Ground truth label: 2
Sample text:
Oil and Economy Cloud Stocks' Outlook  NEW YORK (Reuters) - Soaring crude prices plus worries  about the economy 
and the outlook for earnings are expected to  hang over the stock market next week during the depth of the  summer doldrums.

Después, formateamos el prompt, se lo pasamos al modelo y generamos una predicción.

prompt = build_prompt(sample_text)

inputs = tokenizer(prompt, return_tensors="pt").to("cuda")

with torch.inference_mode():
    outputs = model.generate(
        **inputs,
        max_new_tokens=5,
        do_sample=False,
        pad_token_id=tokenizer.eos_token_id
    )

prediction = tokenizer.decode(outputs[0], skip_special_tokens=True)

print("Model prediction (before training):")
print(extract_label(prediction))

El modelo también predice 2, así que clasifica correctamente este ejemplo. Esto nos confirma rápidamente que el formato del prompt y la canalización de predicción funcionan como esperamos.

Model prediction (before training):
2

Evalúa el modelo base antes del ajuste fino

Ahora evaluamos el modelo base en el conjunto de prueba para establecer una referencia. Esto nos da un punto de comparación antes y después del ajuste fino.

def evaluate_model(dataset):

    preds = []
    refs = []

    model.eval()

    for example in tqdm(dataset):

        text = example["text"]
        label = example["label"]

        prompt = build_prompt(text)

        inputs = tokenizer(prompt, return_tensors="pt", truncation=True)
        inputs = {k: v.to("cuda") for k, v in inputs.items()}

        with torch.inference_mode():

            outputs = model.generate(
                **inputs,
                max_new_tokens=5,
                do_sample=False,
                pad_token_id=tokenizer.eos_token_id
            )

        prediction = tokenizer.decode(outputs[0], skip_special_tokens=True)

        pred_label = extract_label(prediction)

        preds.append(pred_label)
        refs.append(label)

    acc = accuracy_score(refs, preds)
    f1 = f1_score(refs, preds, average="weighted")

    print("Accuracy:", acc)
    print("F1:", f1)


print('Pre-fine-tuning evaluation')
evaluate_model(test_dataset)

La salida informa de una exactitud de 0.52 y un F1 ponderado de 0.4589. Estos resultados de referencia muestran que el modelo ya tiene cierta capacidad para la tarea, aunque hay margen claro de mejora mediante ajuste fino.

Pre-fine-tuning evaluation
Accuracy: 0.52
F1: 0.45889163289429247

4. Ajusta Qwen3.5 con QLoRA

Con la referencia establecida, podemos ajustar el modelo a la tarea de clasificación de noticias. En esta sección daremos formato a los datos de entrenamiento, configuraremos los adaptadores LoRA, definiremos la configuración de entrenamiento, entrenaremos el modelo y guardaremos el adaptador final para su uso posterior.

Prepara los datos de entrenamiento

Primero, formateamos cada ejemplo de entrenamiento para que siga la misma estructura de prompt usada en inferencia. Añadimos la etiqueta correcta al final del prompt para que el modelo aprenda a generar la respuesta adecuada durante el ajuste fino.

def format_train(example):
    prompt = build_prompt(example['text'])
    answer = str(example['label'])
    return {'text': prompt + ' ' + answer}

train_dataset = train_dataset.map(format_train)

Configura los adaptadores LoRA

Después definimos la configuración de LoRA. Esto indica al modelo qué capas adaptar durante el ajuste fino, permitiéndonos entrenar un pequeño conjunto de parámetros adicionales en lugar de actualizar todo el modelo.

lora_config = LoraConfig(
    r=16,
    lora_alpha=16,
    lora_dropout=0,
    bias='none',
    task_type='CAUSAL_LM',
    target_modules=[
        'q_proj','k_proj','v_proj','o_proj',
        'gate_proj','up_proj','down_proj'
    ]
)

Define la configuración de entrenamiento

A continuación, establecemos los argumentos de entrenamiento para el ajuste supervisado. Estos parámetros controlan cuántos ejemplos se procesan a la vez, cómo se acumulan los gradientes, cuánto dura el entrenamiento y dónde se guardan los archivos de salida.

training_args = SFTConfig(
    output_dir="./qwen35-lora",
    per_device_train_batch_size=8,
    gradient_accumulation_steps=2,
    learning_rate=2e-4,
    num_train_epochs=1,
    logging_steps=10,
    bf16=True,
    dataset_text_field="text",
    packing=False,
)

Entrena el modelo

Ahora inicializamos el entrenador con el modelo, el conjunto de datos, la configuración de LoRA, los ajustes de entrenamiento y el tokenizador. Luego iniciamos el ajuste fino sobre el conjunto de entrenamiento formateado de AG News.

trainer = SFTTrainer(
    model=model,
    train_dataset=train_dataset,
    peft_config=lora_config,
    args=training_args,
    processing_class=tokenizer
)
trainer.train()

Salida: 

TrainOutput(global_step=157, training_loss=1.9094241804378047, metrics={'train_runtime': 616.477, 'train_samples_per_second': 4.055, 'train_steps_per_second': 0.255, 'total_flos': 1041693973114368.0, 'train_loss': 1.9094241804378047})

Guarda el adaptador entrenado

Cuando termine el entrenamiento, guardamos el adaptador LoRA y el tokenizador. Así podremos recargar el adaptador ajustado más tarde para evaluar, compartir o publicar en Hugging Face.

trainer.model.save_pretrained("qwen35-small-news-class")
tokenizer.save_pretrained("qwen35-small-news-class")

5. Evalúa el modelo ajustado

Tras el entrenamiento, recargamos el modelo base y acoplamos el adaptador LoRA guardado. Así podemos ejecutar la evaluación con los pesos ajustados y comparar los resultados con la referencia anterior.

Recarga el modelo base y acopla el adaptador LoRA

Primero cargamos de nuevo el modelo base Qwen3.5 y luego acoplamos el adaptador LoRA ajustado que guardamos en el directorio de trabajo de Kaggle. Después, ponemos el modelo en modo de evaluación e inspeccionamos el resumen de parámetros entrenables.

from transformers import AutoModelForCausalLM
base_model = AutoModelForCausalLM.from_pretrained(
    model_id,
    device_map="auto",
    dtype=torch.bfloat16
)


from peft import PeftModel

model = PeftModel.from_pretrained(
    base_model,
    "/kaggle/working/qwen35-small-news-class"
)

model.eval()
model.print_trainable_parameters()

La salida muestra que no hay parámetros entrenables en esta fase porque el entrenamiento ya ha concluido y ahora usamos el adaptador guardado para inferencia y evaluación.

trainable params: 0 || all params: 758,782,784 || trainable%: 0.0000

Evalúa el modelo ajustado

A continuación ejecutamos de nuevo la misma función de evaluación sobre el conjunto de prueba. Así medimos cuánto ha mejorado el modelo tras el ajuste fino.

print('Post-fine-tuning evaluation')
evaluate_model(test_dataset)

La salida informa de una exactitud de 0.865 y un F1 ponderado de 0.8661. Comparado con la referencia, se aprecia una gran mejora tras el ajuste fino.

Post-fine-tuning evaluation
Accuracy: 0.865
F1: 0.8660926258753361

Compara los resultados

Ya podemos comparar el rendimiento antes y después del ajuste fino. Tanto la exactitud como el F1 mejoran de forma sustancial, lo que muestra que el ajuste con QLoRA ayuda al modelo a adaptarse muy bien a la tarea de clasificación de noticias.

Métrica

Antes del entrenamiento

Después del entrenamiento

Exactitud

0.52

0.865

F1

0.4589

0.866

Como ves, ajustar Qwen3.5 Small con QLoRA mejora de forma notable el rendimiento en clasificación.

6. Publica el modelo en Hugging Face

Una vez completada la evaluación, podemos subir el adaptador ajustado y el tokenizador a Hugging Face. Así será más fácil reutilizarlo, compartirlo y cargarlo en futuros proyectos sin repetir todo el entrenamiento.

model.push_to_hub("kingabzpro/qwen35-small-news-class")
tokenizer.push_to_hub("kingabzpro/qwen35-small-news-class")

Cuando termine la subida, el modelo estará disponible en:

Model:
https://huggingface.co/kingabzpro/qwen35-small-news-class

Fine-tuned Qwen 3.5 LoRA on Hugging Face

Fuente: kingabzpro/qwen35-small-news-class

El cuaderno de Kaggle de este tutorial también está disponible. Te recomiendo usarlo solo si te surge algún problema al reproducir los pasos anteriores. También puedes clonarlo y ejecutarlo en tu propio sistema, pero recuerda actualizar el nombre de usuario y el token de Hugging Face para que coincidan con tu cuenta.

7. Conclusión

En este tutorial, ajustamos Qwen3.5-0.8B con QLoRA para clasificación de noticias y logramos un gran resultado con una configuración muy ligera. Tras solo una época y un subconjunto pequeño de datos, la exactitud pasó de 0.52 a 0.865 y el F1 ponderado pasó de 0.4589 a 0.8661. 

Para un modelo tan pequeño, eso es lo más sorprendente: no necesitamos un modelo enorme, ni un dataset inmenso, ni entrenamientos largos para lograr buenos resultados en tareas posteriores.

Lo mejor es lo práctica que resulta esta configuración. Qwen describe el modelo de 0.8B como idóneo para prototipado y ajuste fino específico de tareas, y QLoRA mantiene bajo el uso de memoria al entrenar adaptadores sobre un modelo base de 4 bits congelado. Eso significa que no es solo un flujo en la nube: también es realista para probar en GPUs de portátiles y equipos de consumo.

Otro motivo por el que este modelo me entusiasma es que la familia Qwen3.5 admite ajuste fino de texto y de visión. Así que, aunque aquí lo usamos para clasificación de texto, la misma idea puede extenderse a clasificación de imágenes, de imagen a texto y de texto a texto. Esto convierte a un modelo pequeño como este en una opción realmente práctica cuando quieres probar ideas rápido, ajustar a bajo coste y mantener un despliegue ligero.

También te recomiendo echar un vistazo a otros recursos, como nuestra guía de Qwen3.5 y el tutorial para ejecutar Qwen3.5 en local con una sola GPU.

FAQs

¿Por qué usar QLoRA (cuantización en 4 bits) en lugar de ajuste completo para un modelo de 0.8B?

Aunque 0.8B se considera "pequeño" en términos de LLM, actualizar por completo los 800 millones de parámetros sigue exigiendo mucha VRAM y cómputo. QLoRA congela el modelo base en precisión de 4 bits y solo entrena un conjunto muy pequeño de pesos de adaptador encima. Esto reduce drásticamente la huella de memoria, permitiéndote entrenar cómodamente en una GPU P100 gratuita de Kaggle o en un portátil estándar sin quedarte sin memoria.

Qwen3.5 admite un nuevo modo "thinking". ¿Debería usarlo para tareas de clasificación?

Qwen3.5-0.8B admite de forma nativa razonamiento híbrido, pero funciona en modo "no-thinking" por defecto. Aunque puedes activar sus capacidades de razonamiento, tareas sencillas de clasificación como AG News no requieren un razonamiento complejo paso a paso. Mantener desactivado el modo de razonamiento es mucho más rápido y eficiente en tokens para un etiquetado directo.

¿Cómo afecta la enorme ventana de contexto de 262K de Qwen3.5 a este script de ajuste fino?

Aunque el modelo base admite hasta 262.144 tokens, ajustar con documentos masivos agotará rápidamente la memoria de la GPU de Kaggle. Para clasificación de noticias, el texto es corto, así que no supone un problema. Si planeas adaptar este script a tareas con documentos largos (como resumir libros enteros), tendrás que activar el checkpointing de gradientes y reducir el tamaño de lote.


Abid Ali Awan's photo
Author
Abid Ali Awan
LinkedIn
Twitter

Soy un científico de datos certificado que disfruta creando aplicaciones de aprendizaje automático y escribiendo blogs sobre ciencia de datos. Actualmente me centro en la creación de contenidos, la edición y el trabajo con grandes modelos lingüísticos.

Temas
Inteligencia Artificial

Cursos destacados de DataCamp

Curso

Ajuste fino con Llama 3

2 h
4.1K
Ajusta Llama para tareas personalizadas con TorchTune y aprende técnicas para un ajuste eficiente, como la cuantificación.
Ver detallesRight Arrow
Empezar Curso
Ver másRight Arrow
Relacionado

Tutorial

Cómo ajustar GPT 3.5: Liberar todo el potencial de la IA

Explore GPT-3.5 Turbo y descubra el potencial transformador del ajuste fino. Aprenda a personalizar este modelo de lenguaje avanzado para aplicaciones especializadas, mejore su rendimiento y comprenda los costes asociados, la seguridad y las consideraciones de privacidad.
Moez Ali's photo

Moez Ali

11 min

Tutorial

DCLM-7B de Apple: Configuración, Ejemplo de uso, Ajuste fino

Empieza a utilizar el gran modelo de lenguaje DCLM-7B de Apple y aprende a configurarlo, utilizarlo y ajustarlo para tareas específicas.
Dimitri Didmanidze's photo

Dimitri Didmanidze

9 min

Tutorial

Ajuste fino de GPT-3 mediante la API OpenAI y Python

Libere todo el potencial de GPT-3 mediante el ajuste fino. Aprenda a utilizar la API de OpenAI y Python para mejorar este modelo de red neuronal avanzado para su caso de uso específico.
Zoumana Keita 's photo

Zoumana Keita

12 min

Tutorial

Ajuste fino de LLaMA 2: Guía paso a paso para personalizar el modelo de lenguaje grande

Aprende a ajustar Llama-2 en Colab utilizando nuevas técnicas para superar las limitaciones de memoria y computación y hacer más accesibles los grandes modelos lingüísticos de código abierto.
Abid Ali Awan's photo

Abid Ali Awan

12 min

Tutorial

Guía para principiantes de LlaMA-Factory WebUI: Ajuste de los LLM

Aprende a afinar los LLM en conjuntos de datos personalizados, evaluar el rendimiento y exportar y servir modelos sin problemas utilizando el marco de trabajo de bajo/ningún código de LLaMA-Factory.
Abid Ali Awan's photo

Abid Ali Awan

12 min

Tutorial

Tutorial FLAN-T5: Guía y puesta a punto

Una guía completa para afinar un modelo FLAN-T5 para una tarea de respuesta a preguntas utilizando la biblioteca de transformadores, y ejecutando la inferencia optmizada en un escenario del mundo real.
Zoumana Keita 's photo

Zoumana Keita

15 min

Ver MásVer Más