A fine-tuned version of Qwen3-8B for news media bias detection and neutral rewriting, developed by the Vector Institute as part of the UnBias-Plus project.
Given a news article, the model identifies biased language segments, classifies each segment's bias type and severity, provides neutral replacements, assigns a single article-level severity score, and returns a fully rewritten unbiased version, all in a single structured JSON response.
This is the V2 Instruct variant, trained without chain-of-thought thinking blocks (enable_thinking=False). It produces clean structured JSON directly, making it suitable for production inference via vLLM or other OpenAI-compatible backends.
V2 is trained as a conservative span-level bias annotator: it flags only clear, material bias (while always flagging toxic/dehumanizing language), applies a strict bias-type label precedence, treats attributed/quoted language under the same standard, and changes only flagged text when producing the rewrite.
The exact system prompt used during training is maintained in
src/unbias_plus/prompt.py
(SYSTEM_PROMPT). For faithful results, use that prompt verbatim — a condensed
version is shown below for illustration. The easiest path is to use the
unbias-plus package, which
wires up the prompt, parsing, and offset computation for you.
Using with transformers
fromtransformersimportAutoModelForCausalLM,AutoTokenizerimporttorch,jsonmodel_id="vector-institute/Qwen3-8B-UnBias-Plus-SFT-Instruct-V2"tokenizer=AutoTokenizer.from_pretrained(model_id)model=AutoModelForCausalLM.from_pretrained(model_id,torch_dtype=torch.bfloat16,device_map="auto",)model.eval()# Use the full SYSTEM_PROMPT from src/unbias_plus/prompt.py for best results.SYSTEM_PROMPT="""You are a conservative span-level bias annotator.
Given an article, identify material biased language, assign one article-level
severity score (0-10), and produce a neutral rewrite that changes only flagged
text. Return the result only as a single valid JSON object with the fields
`severity`, `biased_segments`, and `unbiased_text`. Do not output `binary_label`
or `bias_found`. Do not write anything outside the JSON object."""article="Your news article here..."messages=[{"role":"system","content":SYSTEM_PROMPT},{"role":"user","content":f"Analyze the following article for bias and return the result in the required JSON format.\n\nARTICLE:\n{article}"},]inputs=tokenizer.apply_chat_template(messages,tokenize=True,add_generation_prompt=True,enable_thinking=False,return_tensors="pt",return_dict=True,truncation=True,max_length=8192,)withtorch.no_grad():outputs=model.generate(input_ids=inputs["input_ids"].to(model.device),attention_mask=inputs["attention_mask"].to(model.device),max_new_tokens=4096,do_sample=False,temperature=None,top_p=None,pad_token_id=tokenizer.eos_token_id,)new_tokens=outputs[0][inputs["input_ids"].shape[1]:]response=tokenizer.decode(new_tokens,skip_special_tokens=True)result=json.loads(response)
The model emits exactly three fields. binary_label and bias_found are not
produced by the model — they are derived downstream from severity
(severity > 0 ⇒ biased).
{"severity":0,"biased_segments":[{"original":"exact substring from input article","replacement":"neutral alternative phrase (or empty string to delete)","severity":"Low | Medium | High","bias_type":"loaded_language | euphemism | dehumanizing_language | opinion_as_fact | unsupported_generalization | stereotypical_association | sensationalism | informational_bias","reasoning":"short, specific explanation of the linguistic cue"}],"unbiased_text":"Full rewritten neutral article"}
severity is an integer 0–10 (article-level).
Each segment's severity is a string: Low, Medium, or High.
bias_type is exactly one of the eight canonical slugs listed below.
If biased_segments is empty, article severity is 0. If severity > 0, there is at least one segment.
Article-level Severity Scale
Value
Meaning
0
No biased segments
1–5
Limited, low, or moderate bias
6–10
Strong, recurring, or highly distorting bias
Bias Types Detected
V2 assigns exactly one primary bias_type per segment, using the following
label precedence (first applicable label wins):
dehumanizing_language — treats people or groups as less than human, as a threatening mass, or as inherently dangerous (e.g. "vermin", "flood of migrants").
stereotypical_association — assigns traits, roles, or motives to a demographic/social/political/protected group, links identity to suspicion or wrongdoing, or reduces a group to a single function.
sensationalism — hyperbolic, alarmist, or dramatic language that inflates significance (e.g. "bombshell", "catastrophe", "sparks fury").
opinion_as_fact — an unattributed subjective or evaluative judgment presented as fact (e.g. "the policy is a failure").
unsupported_generalization — a sweeping or absolute claim about a group or situation without support (e.g. "everyone knows", "immigrants always").
euphemism — softened or vague wording that minimizes, hides, or downplays harmful facts (e.g. "collateral damage", "enhanced interrogation").
informational_bias — authorial framing that visibly treats one side, source, or claim differently in a way that steers interpretation.
loaded_language — clearly charged, morally weighted, editorial, toxic, abusive, or inflammatory wording not covered by a more specific type (e.g. "thugs", "so-called reform").
Higher recall, longer articles, production deployment
Citation
title={UnBias-Plus: Detect, Explain, and Rewrite Bias},
author={Radwan, Ahmed Y and ElKady, Ahmed and Chaduvula, Sindhuja and Hafez, Mohamed and Krishnan, Amrit and Raza, Shaina},
journal={arXiv preprint arXiv:2606.23412},
year={2026}
}