Downloads · 30 days
16
7% of all-time downloads
agusnieto77/qwen2.5-1.5b-conflict-classifier
qwen2.5-1.5b-conflict-classifier is a text classification model from agusnieto77. Use it when you need a label for a piece of text. It is set up for peft. The card lists the license as apache-2.0.
Clasificador binario de notas periodísticas en español: CONFLICTO vs NOCONFLICTO para conflicto civil/social — una confrontación o disputa colectiva real con al menos un actor social/civil (trabajadores, estudiantes,…
Downloads · 30 days
16
7% of all-time downloads
All-time downloads
234
Public
Repo size
85.3 MB
Likes
0
Public
Click a slice to open those files.
.safetensors73.9 MB · 87%
From the Hugging Face model README
Clasificador binario de notas periodísticas en español: CONFLICTO vs NO_CONFLICTO para conflicto civil/social — una confrontación o disputa colectiva real con al menos un actor social/civil (trabajadores, estudiantes, vecinos, sindicatos, asociaciones, población civil organizada), o una reacción colectiva concreta frente a una necesidad, agravio o condición social explícita.
Tamaño: 1,54 B del base Qwen/Qwen2.5-1.5B-Instruct + adapter LoRA de 18,5 M. El Hub no muestra Model size en repos que solo publican el adapter (no están los pesos completos).
Fine-tune LoRA de Qwen/Qwen2.5-1.5B-Instruct (revisión fijada
989aa7980e4cf806f80c7fef2b1adb7bc71aa306), entrenado sobre 4.034 notas
periodísticas anotadas manualmente de noticias locales argentinas.
Mismo gold (4.034 notas) y la misma definición operacional v5.
| Modelo | Método | Parámetros | Demo |
|---|---|---|---|
| BETO | Full FT + ventanas | 110 M | vivo · Space |
| Qwen2.5-0.5B | Full SFT | 494 M | vivo · Space |
| Qwen2.5-1.5B | LoRA | 1,54 B + adapter 18,5 M | vivo · Space |
| BETO | Qwen 0.5B | Qwen 1.5B LoRA | |
|---|---|---|---|
| Accuracy OOF | 0.924 | 0.959 | 0.959 |
| Recall CONFLICTO | 0.963 | 0.895 | 0.906 |
| Precisión CONFLICTO | 0.765 | 0.917 | 0.910 |
| FN / FP | 33 / 273 | 94 / 72 | 84 / 80 |
El modelo responde exclusivamente con una de las dos etiquetas. El score es la
probabilidad teacher-forced normalizada por longitud de cada etiqueta (ver
Cómo se calcula el score); se predice CONFLICTO cuando score >= 0.5.
!pip uninstall -y -q torchao
!pip install -q -U peft transformers huggingface_hub accelerate
import math
import torch
from huggingface_hub import hf_hub_download
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
base = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen2.5-1.5B-Instruct", revision="989aa7980e4cf806f80c7fef2b1adb7bc71aa306")
tok = AutoTokenizer.from_pretrained(
"Qwen/Qwen2.5-1.5B-Instruct", revision="989aa7980e4cf806f80c7fef2b1adb7bc71aa306")
model = PeftModel.from_pretrained(base, "agusnieto77/qwen2.5-1.5b-conflict-classifier")
model.eval()
definition = open(
hf_hub_download(
"agusnieto77/qwen2.5-1.5b-conflict-classifier",
"conflict_definition.md",
),
encoding="utf-8",
).read()
LABEL_IDS = {L: tok.encode(L, add_special_tokens=False)
for L in ("CONFLICTO", "NO_CONFLICTO")}
def clasificar(texto: str) -> tuple[str, float]:
system = (
"Eres un clasificador de notas periodísticas.\n\n"
"Tu tarea consiste exclusivamente en determinar si la nota contiene\n"
"un conflicto según la siguiente definición operacional:\n\n"
f"{definition}\n\n"
"Debes responder exclusivamente con una de estas dos etiquetas:\n\n"
"CONFLICTO\n"
"NO_CONFLICTO"
)
messages = [
{"role": "system", "content": system},
{"role": "user", "content": f"Clasifica la siguiente nota:\n\n{texto}"},
]
prompt = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
prompt_ids = tok.encode(prompt, add_special_tokens=False)
# teacher-forced: promediar log-prob por token de cada etiqueta
seqs, labels = [], []
for L in ("CONFLICTO", "NO_CONFLICTO"):
seqs.append(prompt_ids + LABEL_IDS[L]); labels.append(L)
maxlen = max(len(s) for s in seqs)
input_ids = torch.full((2, maxlen), tok.pad_token_id, dtype=torch.long)
attn = torch.zeros((2, maxlen), dtype=torch.long)
for j, s in enumerate(seqs):
input_ids[j, maxlen - len(s):] = torch.tensor(s)
attn[j, maxlen - len(s):] = 1
keep = 6
with torch.no_grad():
logits = model(input_ids, attention_mask=attn, logits_to_keep=keep).logits
mean_lp = {}
for j, L in enumerate(labels):
llen = len(LABEL_IDS[L])
logp = torch.log_softmax(logits[j, keep - llen - 1:keep - 1], dim=-1)
toks = torch.tensor(LABEL_IDS[L])
mean_lp[L] = logp[torch.arange(llen), toks].mean().item()
p_c, p_n = math.exp(mean_lp["CONFLICTO"]), math.exp(mean_lp["NO_CONFLICTO"])
score = p_c / (p_c + p_n)
etiqueta = "CONFLICTO" if score >= 0.5 else "NO_CONFLICTO"
return etiqueta, score
texto_de_la_nota = (
"Los trabajadores del hospital municipal decidieron continuar con el paro "
"de actividades en reclamo de mejoras salariales. El gremio anunció una "
"movilización hacia la Municipalidad y rechazó la propuesta de aumento "
"del Ejecutivo."
)
etiqueta, score = clasificar(texto_de_la_nota)
print(f"{etiqueta} (score = {score:.3f})")
Notebook completo listo para Google Colab: demo_colab.ipynb
El score es teacher-forced con normalización por longitud (el mismo que se
usa en el entrenamiento y la CV): se fuerza cada etiqueta como continuación,
se promedia el log-prob de sus tokens (CONFLICTO = 3 tokens,
NO_CONFLICTO = 4) y se aplica softmax entre ambos promedios:
score = exp(m_C) / (exp(m_C) + exp(m_NC))
La normalización hace comparables secuencias de distinto largo y evita la
confianza exagerada de usar solo el primer token. Se predice CONFLICTO cuando
score >= 0.5.
Validación cruzada de 5 folds sobre las 4.034 notas de entrenamiento (predicciones out-of-fold, umbral fijo 0.5). Matriz de confusión agregada (C, N) × (C, N): TP=811, FN=84, FP=80, TN=3059.
| Métrica | Media | Desvío |
|---|---|---|
| Accuracy | 0.9594 | 0.0043 |
| Precisión CONFLICTO | 0.9104 | 0.0241 |
| Recall CONFLICTO | 0.9064 | 0.0184 |
| F1 CONFLICTO | 0.9081 | 0.0100 |
| Macro F1 | 0.9410 | 0.0062 |
| PR-AUC | 0.9750 | 0.0020 |
| ROC-AUC | 0.9921 | 0.0008 |
Qwen/Qwen2.5-1.5B-Instruct (revisión fijada arriba)Una nota es CONFLICTO si contiene un acto colectivo civil/social ligado a una disputa real entre partes identificables, o a una necesidad, agravio o condición social explícita frente a la cual un colectivo protesta, se rebela o ejerce presión.
Actos que hacen CONFLICTO: medida de fuerza laboral; petición o reclamo formal de un actor colectivo con objeto concreto o contraparte identificada; manifestación popular con reclamo; violencia o acción colectiva en una disputa social; reacción colectiva frente a un delito o abuso; motín carcelario; conflicto resuelto cuando la nota establece el reclamo subyacente.
Casos NO_CONFLICTO: asambleas sin disputa; gestión habitual de entidades civiles; opinión individual; delito común sin reacción social; violencia interpersonal; conflicto puramente militar; cobertura de crisis sin reclamo; asistencia solidaria sin disputa; campaña y retórica electoral; contienda político-institucional ordinaria entre poderes del Estado; marchas conmemorativas, educativas o de concientización sin reclamo.
El clasificador requiere esta definición exacta en el system prompt; usar otra definición cambia el comportamiento. El archivo completo está en conflict_definition.md en este repositorio, y puede descargarse de forma programática:
from huggingface_hub import hf_hub_download
definition = open(
hf_hub_download(
"agusnieto77/qwen2.5-1.5b-conflict-classifier",
"conflict_definition.md",
),
encoding="utf-8",
).read()
Guillermina Laitano, Luciana Nogueira, Nicolás Rabino, Rodrigo Fernández, Ivana Teijón, Agustín Nieto