CoolFace
Modelpublic

howarudo/gemma-3-12b-it-semeval2026-task9-polarization-lora

sourceHugging Facegemmaupdated 2mo agoView on Hugging Face
0likes6downloads
Model Card

gemma-3-12b-it SALSA LoRA — multilingual polarization detection (SemEval-2026 Task 9, Subtask 1)

A LoRA adapter over `unsloth/gemma-3-12b-it` for binary polarization detection across 22 languages, trained for SemEval-2026 Task 9 Subtask 1.

The adapter is trained SALSA-style: rather than decoding free text, the model is scored by reading the logits of the two class tokens ("0" and "1") at a single <ANSWER> position, and the softmax over just those two logits is the class probability. This makes inference one forward pass per example and gives a calibrated probability you can threshold.

Usage

The prompt format matters — the adapter was trained on this exact string, and deviating from it (wrapping <ANSWER> in an assistant turn, or skipping the 800-character truncation) shifts the distribution away from what it saw.

python
import torch
from unsloth import FastLanguageModel

MAX_LENGTH, TEXT_MAX_LENGTH = 1024, 800

TASK_TEMPLATE = """Does the following text contain polarization?
A text is polarized if it incites division, hatred, or stereotyping towards other groups.

Answer in format:
<ANSWER>#Number</ANSWER>
where the number is one of the following:
0 - No
1 - Yes

The text:
<TEXT>
{text}
</TEXT>
"""

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="howarudo/gemma-3-12b-it-semeval2026-task9-polarization-lora",
    max_seq_length=MAX_LENGTH,
    dtype=None,
    load_in_4bit=False,
)
FastLanguageModel.for_inference(model)

def polarization_prob(text: str) -> float:
    messages = [{"role": "user", "content": TASK_TEMPLATE.format(text=str(text)[:TEXT_MAX_LENGTH])}]
    prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) + "<ANSWER>"
    enc = tokenizer(prompt, return_tensors="pt", truncation=True,
                    max_length=MAX_LENGTH, add_special_tokens=False).to(model.device)

    class_ids = torch.tensor([tokenizer.encode(c, add_special_tokens=False)[0] for c in ["0", "1"]],
                             device=model.device)
    # Autocast is required: the Gemma3 attention path computes in fp32 and
    # projects through bf16 weights.
    with torch.inference_mode(), torch.autocast("cuda", dtype=torch.bfloat16):
        logits = model(**enc).logits[0, -1, :]
    return torch.softmax(logits.index_select(0, class_ids).float(), dim=-1)[1].item()

print(polarization_prob("Your text here."))   # P(polarized)

For batched scoring, right-pad the sequences and read the logits at each row's own last non-padding position — padding sits after the answer token, so under causal attention it cannot influence the class logits.

License

Governed by the Gemma Terms of Use, inherited from the base model.