CoolFace
Modelpublic

diego-florez/Qwen2.5-7B-Comment-Sexuality-Reasoner

sourceHugging Faceapache-2.0updated 2d agoView on Hugging Face
0likes419downloads
Model Card

Qwen2.5-7B-Comment-Sexuality-Reasoner

A Spanish-language, context-aware reasoning model fine-tuned from Qwen/Qwen2.5-7B-Instruct for analyzing social-media comments in relation to image context, topic, and comment author information.

The model classifies comments as non-sexualized, sexualized, or neutral, and also returns a short Spanish reasoning and a probability for each of the three classes.

Model Description

Qwen2.5-7B-Comment-Sexuality-Reasoner is a fine-tuned version of Qwen2.5-7B-Instruct adapted to a specific social-media analysis task in Spanish.

The model receives contextual information associated with an image together with one or more comments and analyzes each comment individually.

The task is context-aware: the model evaluates the meaning and intent of a comment in relation to the provided image context and topic, rather than relying only on individual words.

Summary

  • —Base model: Qwen/Qwen2.5-7B-Instruct
  • —Fine-tuning method: QDoRA (DoRA adapter on a 4-bit quantized base model)
  • —Training framework: Unsloth + TRL
  • —Recommended inference framework: vLLM
  • —Output language: Spanish (Castilian Spanish)

Intended Use

The model is intended for:

  • —Research on Spanish social-media comments.
  • —Context-aware classification of potentially sexualized comments.
  • —Dataset annotation and weak/semi-supervised labeling.
  • —Analysis of reasoning produced by language models for social-media moderation tasks.

The model should be considered a research model, not a production-ready moderation system.

Input

The model expects structured information containing:

  • —image_context: textual description of the associated image.
  • —topic: topic associated with the image/content.
  • —comment_author: author identifier.
  • —author_gender: author gender metadata (Male, Female or Not identifiable).
  • —comments: one or more comments to analyze.

Comments are grouped by:

text
image_id + comment_author

This allows multiple comments from the same author on the same image to be provided together as additional context, while each comment is still classified independently.

Labels

  • —non-sexualized: The comment is based on the image context and/or expresses admiration, support, or humor without sexual connotations or hidden intentions.
  • —sexualized: The comment expresses desire, objectification, or an explicit or implicit sexual insinuation toward the person, outside the context of the image.
  • —neutral: Multiple interpretations are possible and there is insufficient evidence to decide between them, or there is insufficient context.

Output

For each input comment, the model returns a JSON object with:

  • —label
  • —reasoning (in Spanish)
  • —non_sexualized_probability
  • —sexualized_probability
  • —neutral_probability

The output is a JSON array with exactly one object per input comment, in the same order as the input. The three probabilities are intended to sum to 1.00, and the label should correspond to the class with the highest probability.

Training

Data

The training data was built for this specific task from Spanish social-media comments, each with an image description, topic, author metadata, label, reasoning and class probabilities.

  1. 1.LLM annotation (weak supervision): 9,962 comments were annotated by Qwen2.5-32B-Instruct-AWQ with few-shot examples taken from a human-labeled gold set.
  2. 2.Bias analysis: the annotator was evaluated against the human gold set to identify systematic errors by topic, author gender, doubt expressions in the reasoning, and over/under-confident probability ranges.
  3. 3.Targeted human correction: 344 comments flagged by those filters were reviewed and corrected by hand (label, reasoning and probabilities).
  4. 4.Phase splits (no overlap between phases):
PhaseSourceCommentsnon-sexualizedsexualizedneutral
Phase 1LLM-annotated8,61881.5%14.2%4.3%
Phase 2344 human-corrected + 1,000 LLM-annotated (stratified)1,34476.3%12.1%11.6%

The gold set used for evaluation was never used for training.

Procedure

Training was performed in two phases on top of the same adapter:

  • —Phase 1 (classification): learns the three-class task from the large LLM-annotated set. A 3% validation split was used and the best checkpoint was selected by validation loss.
  • —Phase 2 (reasoning): refines the reasoning style with the human-corrected set, using a lower learning rate so that the classification ability learned in Phase 1 is preserved.
HyperparameterPhase 1Phase 2
Epochs3 (best checkpoint at step 600 of 1,029)2 (112 steps)
Learning rate1.5e-44e-5
Batch size2424
Scheduler / warmupcosine / 0.1cosine / 0.1
Max grad norm0.50.5

Adapter configuration: DoRA with rsLoRA, r=64, lora_alpha=128, lora_dropout=0.05, applied to q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj and down_proj (162.9M trainable parameters, 2.09% of the model). Base model loaded in 4-bit, max_seq_length=2048, NVIDIA A100 80GB.

The final adapter was merged with the base model and saved in 16-bit format for inference.

Evaluation

The model was evaluated on a held-out, human-annotated gold set of 448 comments (342 non-sexualized, 54 sexualized, 52 neutral).

The main comparison is against Qwen2.5-32B-Instruct-AWQ (the model that annotated the training data), evaluated zero-shot with the same prompt. Both models were run with vLLM on an NVIDIA A100 (Google Colab).

The benchmark is task-specific and should not be interpreted as a general-purpose LLM benchmark.

Classification

MetricQwen2.5-32B-Instruct-AWQQwen2.5-7B-Comment-Sexuality-Reasoner
Accuracy42.19%70.54%
Weighted F149.24%70.53%
Macro F137.93%46.98%
Inference time (448 comments)4m 30s35s

Weighted F1 weights each class by its number of examples and is dominated by non-sexualized (76% of the test set). Macro F1 weights the three classes equally and is more demanding on the minority classes.

Per class:

ClassModelPrecisionRecallF1
non-sexualized32B0.9130.3980.554
7B0.8360.8330.835
sexualized32B0.3180.5000.388
7B0.3020.3520.325
neutral32B0.1210.5000.195
7B0.2730.2310.250

The 32B model over-predicts neutral (214 predictions for 52 true cases), which explains most of its accuracy gap. The fine-tuned 7B model predicts each class in a proportion close to the real one, but has lower recall than the 32B model on sexualized.

Probability quality

Brier score per class (lower is better):

ClassQwen2.5-32B-Instruct-AWQQwen2.5-7B-Comment-Sexuality-Reasoner
neutral0.13800.1078
non-sexualized0.26050.2075
sexualized0.13850.1569
Mean0.17900.1574

The 7B model is well calibrated for non-sexualized in the high-probability range, but it is overconfident on `sexualized`: when it assigns ~0.95 to sexualized, only ~28% of those comments are actually sexualized.

Reasoning quality

MetricQwen2.5-32B-Instruct-AWQQwen2.5-7B-Comment-Sexuality-Reasoner
Lexical diversity: TTR0.8590.822
Lexical diversity: MTLD86.7555.73
Cosine similarity to human reasoning, correct predictions0.6730.645
Cosine similarity to human reasoning, all predictions0.6450.656
Cosine similarity to label definition (mean of 3 classes)0.6110.624

Cosine similarity was computed with BAAI/bge-m3 embeddings.

LLM-as-a-judge

Reasoning quality was also evaluated with Mistral-Small-24B-Instruct-2501-AWQ as judge, from a different model family to avoid self-preference bias. For each of 447 comments, the judge received the image context, the comment, the human label and reasoning, and the outputs of five candidate models (the 32B model and four variants of the 7B model), shown in random order. It scored every candidate from 0 to 5 before selecting a winner for each criterion.

CriterionQwen2.5-32B-Instruct-AWQQwen2.5-7B-Comment-Sexuality-Reasoner
Best answer191108
Most similar to human reasoning16889
Richest reasoning29055
Self-contradictory reasoning (lower is better)163

Counts do not sum to 447 because the table only shows two of the five candidates. The "best answer" criterion prioritizes reasoning quality over label correctness, which is why the judge often prefers the 32B model despite its lower accuracy.

Summary

  • —The fine-tuned 7B model clearly outperforms the 32B model on classification (70.5% vs 42.2% accuracy; 47.0% vs 37.9% macro F1) while being ~7.7x faster.
  • —It has a lower mean Brier score, its reasoning is more aligned with the label definitions, and it contradicts itself far less often (3 vs 16).
  • —The 32B model produces richer and more varied reasoning, is preferred by the judge on reasoning quality, and has higher recall on sexualized.

Bias, Risks and Limitations

  • —The model was fine-tuned on a specific Spanish-language social-media dataset and may not generalize to other languages, cultural contexts, platforms or domains.
  • —The model relies on the supplied image_context and topic. Errors or biases in the image-to-text description can propagate to the final classification.
  • —Author gender shortcut: although the prompt states that author_gender must never be sufficient on its own, the model's reasoning for sexualized predictions frequently relies on the author being male. Accuracy is 57.0% on comments by male authors vs 92.9% on comments by female authors.
  • —Implicit sexualization: masked or implicit sexualized comments, especially when the image context is already sensual (e.g. lingerie modeling), are frequently missed.
  • —Overconfidence on `sexualized`: all false sexualized predictions on the test set have a probability of 0.9 or higher.
  • —The probabilities are written by the model as part of its output, not token-level probabilities. They should not be interpreted as statistically calibrated probabilities and should be validated before use in high-stakes systems.
  • —With 52–54 test examples in each minority class, metrics for neutral and sexualized have high variance.
  • —This is a research model and has not been validated as a safety-critical content moderation system. Human review is recommended for ambiguous or consequential decisions.

How to Use

Installation

bash
pip install transformers==4.45.2 unsloth==2026.8.16 torch==2.4.0 vllm==0.6.3.post1 trl==1.10.0 peft==0.19.1

Inference environment: Python 3.12.13, NVIDIA A100-SXM4-40GB. The model is expected to run on smaller GPUs such as an NVIDIA L4 or T4, although memory requirements and inference speed depend on the configuration and workload.

Loading the model

python
from vllm import LLM

MODEL_PATH = "diego-florez/Qwen2.5-7B-Comment-Sexuality-Reasoner"

llm = LLM(
    model=MODEL_PATH,
    dtype="float16",
    gpu_memory_utilization=0.90,
    max_model_len=6144,
    trust_remote_code=True,
)

tokenizer_vllm = llm.get_tokenizer()

Prompt

Use the full prompt below: it is the same one used during training.

python
import json

SYSTEM_MESSAGE = (
    "Eres un experto razoneando y etiquetando comentarios "
    "en redes sociales. "
    "Responde SOLO JSON válido en español castellano."
)

TASK_INSTRUCTIONS = """
TASK:

Tu tarea es analizar comentarios relacionando con:
- image_context
- topic
- author_gender

Para cada comentario de la lista devuelve:
- label
- reasoning
- non_sexualized_probability
- sexualized_probability
- neutral_probability

IMPORTANTE:
- Usa SIEMPRE español castellano.
- No uses inglés en el reasoning.
- Usa el contexto completo antes de decidir.
- Si existen varios comentarios del mismo autor para la misma imagen, utilízalos como contexto adicional para interpretar la intención de cada comentario, pero clasifica cada comentario de forma individual.

Requisitos de las probabilidades:
- Las tres deben sumar exactamente 1.00
- El label debe corresponder a la categoría con mayor probabilidad
- Usa toda la escala, no solo valores predeterminados

DEFINICIÓN DE LABELS:
non_sexualized: Comentario basado en el contexto de la imagen y/o que expresa admiración, apoyo o humor, sin connotaciones ni segundas intenciones.
sexualized: Comentario que expresa deseo, objetificación o insinuación explícita o enmascarada hacia la persona, fuera de contexto de la imagen.
neutral: Existen varias interpretaciones posibles y no hay evidencia suficiente para decidir entre ellas o falta contexto suficiente en el comentario.

EVITA SESGOS:
- El author_gender aporta contexto, pero nunca debe ser suficiente por sí solo para decidir la clasificación.
- El topic no implica sexualización. Comparar siempre comentario y contexto.
- Si durante el razonamiento existen varias interpretaciones posibles y no hay evidencia suficiente para favorecer claramente una de ellas, prioriza la categoría neutral.
- Reserva probabilidades superiores a 0.90 para casos con evidencia clara y directa.
- El reasoning debe explicar qué evidencias del comentario y del contexto justifican la clasificación.
- Evalúa la intención del comentario en relación con el contexto de la imagen, no únicamente las palabras utilizadas.

# FORMATO DE SALIDA (ARRAY JSON)
[
  {
    "label": "sexualized | neutral | non-sexualized",
    "reasoning": "explicación en español",
    "non_sexualized_probability": 0.XX,
    "sexualized_probability": 0.XX,
    "neutral_probability": 0.XX
  }
]

Cada elemento del array "comments" corresponde a un comentario independiente.
La salida debe ser un único array JSON que contenga exactamente un objeto por cada elemento del array "comments", respetando el mismo orden.
"""


def build_user_message(grouped_row):
    comments_list = [{"comment_text": ctext} for ctext in grouped_row["comment_text"]]

    input_data = {
        "image_context": grouped_row["image_context"],
        "topic": grouped_row["topic"],
        "comment_author": grouped_row["comment_author"],
        "author_gender": grouped_row["author_gender"],
        "num_comments": len(comments_list),
        "comments": comments_list,
    }

    return f"""INPUT DATA:
{json.dumps(input_data, ensure_ascii=False, indent=2)}

{TASK_INSTRUCTIONS}"""


def build_full_prompt(grouped_row):
    messages = [
        {"role": "system", "content": SYSTEM_MESSAGE},
        {"role": "user", "content": build_user_message(grouped_row)},
    ]
    return tokenizer_vllm.apply_chat_template(
        messages,
        tokenize=False,
        add_generation_prompt=True,
    )

Grouping comments

df is a dataframe with one row per comment and the columns comment_id, comment_text, comment_author, author_gender, image_id, image_context and topic.

python
grouped = (
    df
    .groupby(["image_id", "comment_author"])
    .agg({
        "image_context": "first",
        "topic": "first",
        "author_gender": "first",
        "comment_id": list,
        "comment_text": list,
    })
    .reset_index()
)

Running inference

python
from vllm import SamplingParams

prompts = []
sampling_params = []

for _, row in grouped.iterrows():
    n = len(row["comment_id"])
    prompts.append(build_full_prompt(row))
    sampling_params.append(
        SamplingParams(
            temperature=0.0,
            repetition_penalty=1.05,
            max_tokens=min(512, n * 384),
        )
    )

outputs = llm.generate(prompts, sampling_params, use_tqdm=True)

Parsing the output

Each JSON object in the response is parsed individually, so a single malformed object does not invalidate the rest of the group. Predictions are aligned with the original comment_id values by position, never by any identifier generated by the model.

python
import re
import pandas as pd

def parse_objects(text):
    parsed = []
    for match in re.findall(r"\{[^{}]*\}", text):
        try:
            parsed.append(json.loads(match))
        except json.JSONDecodeError:
            parsed.append(None)
    return parsed

rows = []
for (_, group), output in zip(grouped.iterrows(), outputs):
    objects = parse_objects(output.outputs[0].text)
    for i, comment_id in enumerate(group["comment_id"]):
        obj = objects[i] if i < len(objects) and objects[i] else {}
        rows.append({
            "comment_id": comment_id,
            "pred_label": obj.get("label", "parse_error"),
            "reasoning": obj.get("reasoning"),
            "non_sexualized_probability": obj.get("non_sexualized_probability"),
            "sexualized_probability": obj.get("sexualized_probability"),
            "neutral_probability": obj.get("neutral_probability"),
        })

predictions = pd.DataFrame(rows)

Example

Input for one group (image_id + comment_author):

json
{
  "image_context": "Mujer posando en ropa interior",
  "topic": "Modeling lingerie",
  "comment_author": "@test_user",
  "author_gender": "Male",
  "num_comments": 2,
  "comments": [
    {"comment_text": "Qué guapa"},
    {"comment_text": "Te amo ❤️"}
  ]
}

Output format (one object per comment, in the same order):

json
[
  {
    "label": "non-sexualized",
    "reasoning": "El comentario expresa admiración general.",
    "non_sexualized_probability": 0.75,
    "sexualized_probability": 0.15,
    "neutral_probability": 0.1
  },
  {
    "label": "sexualized",
    "reasoning": "Expresa atracción directa hacia la persona.",
    "non_sexualized_probability": 0.1,
    "sexualized_probability": 0.8,
    "neutral_probability": 0.1
  }
]

This example illustrates the input and output format used during training; it is not an actual model prediction.

Model Files

The repository contains the complete merged 16-bit model, its configuration and the tokenizer files. All weight shards listed in the safetensors index are required.

License

This model is released under the Apache License 2.0, consistent with the licensing of the Qwen2.5-7B-Instruct base model.

Please review the base model's license and terms before using the model in downstream applications.

Citation

If you use this model or the associated dataset/pipeline in research, please cite the corresponding project repository once it is published.