bendavidsteel/Qwen3.5-4B-stance-detection
Qwen3.5-4B-stance-detection
Qwen/Qwen3.5-4B fine-tuned for three-way stance detection: given a document and a target, predict whether the author is favor, against, or neutral toward that target.
Trained with LoRA on a sequence-classification head (the adapter is merged into these weights), as part of the StanceMining pipeline. It is the Qwen3.5 successor to bendavidsteel/Qwen3-4B-stance-detection.
Model details
Usage
The classification head pools the final non-padding token, so inputs must be formatted with the chat template and a generation prompt — the same way the model was trained.
import torch
from transformers import AutoModelForSequenceClassification, AutoTokenizer
repo = "bendavidsteel/Qwen3.5-4B-stance-detection"
tokenizer = AutoTokenizer.from_pretrained(repo)
model = AutoModelForSequenceClassification.from_pretrained(repo, dtype=torch.bfloat16, device_map="auto")
model.eval()
PROMPT = (
"For the following text, determine whether the author's stance is in favor of, "
"against, or neutral toward the target: '{target}'. Consider the language used, "
"any explicit statements of position, and contextual clues that suggest the "
"author's stance. Answer only with 'in favor', 'against', or 'neutral'. \n\n"
"Text: '{text}'"
)
def classify(text, target):
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": PROMPT.format(target=target, text=text)},
]
inputs = tokenizer.apply_chat_template(
messages,
add_generation_prompt=True,
enable_thinking=False,
truncation=True,
max_length=2048,
return_tensors="pt",
return_dict=True,
).to(model.device)
with torch.no_grad():
logits = model(**inputs).logits[0]
probs = logits.softmax(-1)
return model.config.id2label[int(probs.argmax())], probs.tolist()
print(classify("Cycling infrastructure has made my commute so much safer.", "bike lanes"))Prompt templates
The prompt is part of the model's interface — the fine-tune only ever saw inputs in these two forms. Both are also stored in metadata.json in this repo.
Standalone document:
For the following text, determine whether the author's stance is in favor of, against, or neutral toward the target: '{target}'. Consider the language used, any explicit statements of position, and contextual clues that suggest the author's stance. Answer only with 'in favor', 'against', or 'neutral'.
Text: '{text}'Document in a reply chain (used for conversational datasets such as MT-CSD and CTSDT):
For the following text, determine whether the author's stance is in favor of, against, or neutral toward the target: '{target}'. Consider the language used, any explicit statements of position, the chain of parent texts that the author is replying to, and contextual clues that suggest the author's attitude. Answer only with 'in favor', 'against', or 'neutral'.
Parent Document Chain (from oldest to most recent):
{parent_chain}
Text: '{text}'Training data
Fine-tuned on the combined training splits of eight stance datasets:
Training procedure
LoRA adapters on a randomly initialised sequence-classification head, merged into the base weights after training. Hyperparameters from the wandb run autumn-moon-345:
Training ran as three consecutive jobs — an initial run plus two resumptions after crashes — totalling roughly 69 GPU-hours. Final held-out validation loss was 0.575.
Evaluation
Held-out test split of the combined corpus (16,972 examples).
Overall
Per class (derived from the confusion matrix in metadata.json)
Per dataset
Confusion matrix (rows are true labels, columns predicted)
Comparison with the Qwen3 generation
Same training and test splits, same prompts.
Limitations
- The label set is closed and three-way; targets outside the training domains, and stance expressed through heavy irony or in-group reference, remain hard.
- Coverage outside English is limited to Catalan/Spanish independence tweets and French/Italian election tweets, so non-English performance should not be assumed to generalise to other topics.
neutralabsorbs both "no stance" and "discusses the target without taking a side", which differ across the source datasets.- Inputs longer than 2048 tokens were truncated during training and evaluation, despite the larger context window of the base model.
License
Apache 2.0, following the base model.
