--- license: apache-2.0 base_model: Qwen/Qwen2.5-1.5B-Instruct library_name: peft tags: - argument-mining - fact-checking - lora - qwen - distillation language: [en] pipeline_tag: text-generation --- # ArgParser-v4 A Qwen-1.5B LoRA that extracts argument structure — claims, premises, citations, and support/attack relations — from political claims and argumentative prose. This is the fourth iteration of a distillation project, and the one that actually works well enough to plug into a real fact-checking pipeline. ## Where this came from I built an argument-aware retrieval pipeline for Politifact-style fact-checking. Given a claim like "Politician X said Y," it retrieves evidence using argument-role-targeted queries and returns a 6-way truth verdict. When the parser was reading gold argument annotations from the LIARArg dataset, this pipeline hit 0.422 6-way F1 versus 0.114 for a flat-RAG baseline. The 0.308 gap was the whole reason to build it. But that gap only means something if a real parser can slot in at inference time. Gold annotations aren't available for actual incoming claims. So the question became: what parser closes that gap? First cut, Phase 2-α, was `gpt-oss-120b` running zero-shot on Cerebras. It closed about 45% of the gap (integration F1 0.254). Real, useful, but calling a 120B cloud model per claim isn't practical for anything resembling deployment. Phase 2-β was the distillation project. Four iterations. Can a small local model preserve most of the gain? ## The four iterations **v1** was Qwen-0.5B, full fine-tune on four argument-mining corpora (AbstRCT, Microtext, CDCP, PERSPECTRUM), 1,494 records, 3 epochs. The smallest reasonable baseline. In-domain comp-F1 averaged 0.108 across the four held-out test sets. High empty rates on some domains (PERSPECTRUM: 91% empty). The point was to get the pipeline plumbing right, not to publish a number. **v2** kept the same data but moved to Qwen-1.5B with LoRA r=16 (α=32, dropout 0.05, target `q_proj,k_proj,v_proj,o_proj`). 3 epochs. In-domain comp-F1: 0.219, roughly double v1. Microtext premise F1 jumped from 0.000 to 0.680. AbstRCT empty rate 75% → 50%. Scale + LoRA + longer training context are the dominant levers here. **v3** was v2's adapter continued for one more epoch after adding a fifth corpus (AAEC, 402 persuasive essays). Marginal in-domain improvement (0.229). PERSPECTRUM actually regressed slightly, which was the first sign that adding more of the same kind of extractive gold hits diminishing returns quickly. Then I tried v3 on the actual LIARArg parse and hit 83% empty rate on the first 64 rows. Killed that run. The lesson was clear: extractive gold from academic argument-mining corpora doesn't teach a small student to handle Politifact-style claims. The distribution gap is too wide. **v4** is this model. Two changes from v3: 1. Fresh adapter, not continual. Clean A/B against v3. 2. I generated 2,123 silver labels on LIARArg train articles using `gpt-oss-120b` via Cerebras. Both the extracted argument structure and the model's Chain-of-Thought reasoning came back (the CoT was in `message.reasoning`, which I captured almost by accident). The training code's target-formatter passes reasoning through as `...{json}` when present, so v4 accidentally became CoT-aware for LIARArg-style inputs — while staying purely extractive on the gold in-domain corpora (where reasoning was empty). This wasn't planned. It just happened, and it turned out to be exactly what was missing. Training details: 3,617 records total, 3 epochs, fresh LoRA adapter, fp16, Adafactor, gradient checkpointing. Batch 1, grad accum 32. About 29 hours on a single GTX 1080 Ti. ## What v4 actually does The load-bearing number is Phase 1 integration on LIARArg — the whole reason to build any of this: | Metric | v4 | Phase 2-α teacher (120B) | Teacher retention | |---|---|---|---| | 6-way F1 | 0.217 | 0.254 | 85% | | 3-way F1 | 0.457 | 0.461 | 99% | | within-1 accuracy | 0.605 | 0.616 | 98% | Flat-RAG baseline for reference: 0.114. v4 beats it by 0.103 and closes 33% of the gold-parser gap using a locally-runnable 1.5B model. LIARArg parse empty rate went from v3's 83% down to 23%. Silver + CoT was the missing piece. In-domain retention is modest — 0.192 comp-F1 averaged across the five training corpora, slightly regressed from v3's 0.229. That's the cost of a fresh 3-epoch run versus v3's effective 4 epochs. The trade was worth it for the cross-domain transfer. ## OOD probes To characterize where v4 stops transferring, I ran three unseen-domain probes: **AMPERSAND** (Chakrabarty et al. 2019, Reddit ChangeMyView). Binary is-argumentative F1 = 0.819 with recall 0.970 on 150 balanced sentences. v4 catches essentially all argumentative content in Reddit debate. The 30% false-positive rate comes from over-flagging fragments and borderline sentences; some of those are arguably right and just disagree with the annotator. **PERSUADE 2.0** (Kaggle Feedback Prize, student argumentative essays). Component F1 macro = 0.193 with 45.7% extraction rate on 25 essays. Claim F1 = 0.351, premise F1 = 0.034. v4 finds about half of PERSUADE's argumentative spans and labels claim-like content reasonably. Premise F1 collapses because PERSUADE's Evidence and Rebuttal categories are much narrower than v4's premise concept. **ECHR** (European Court of Human Rights case briefs). Component F1 = 0.074 with 9.7% extraction rate against a proxy gold derived from ECHR's agent labels. Legal reasoning is structurally distant from anything v4 saw in training. Ordering (Reddit > essays > legal) tracks discourse-register proximity to v4's training data. Predictable, but useful to have quantified. ## Usage ```python from peft import PeftModel from transformers import AutoModelForCausalLM, AutoTokenizer import torch, re, json base_id = "Qwen/Qwen2.5-1.5B-Instruct" adapter_id = "properexit/ArgParser-v4" tok = AutoTokenizer.from_pretrained(base_id) base = AutoModelForCausalLM.from_pretrained( base_id, torch_dtype=torch.float16, device_map="auto" ) model = PeftModel.from_pretrained(base, adapter_id) INSTR = ("Extract all argument components and relations from the text. " "Output strict JSON with claim_components, premise_components, " "citation_components, and relations.") def parse(text, max_new_tokens=1024): prompt = tok.apply_chat_template( [{"role": "user", "content": f"{INSTR}\n\nTEXT:\n{text}"}], tokenize=False, add_generation_prompt=True, ) enc = tok(prompt, return_tensors="pt", truncation=True, max_length=2048).to(model.device) out = model.generate(**enc, max_new_tokens=max_new_tokens, do_sample=False, pad_token_id=tok.pad_token_id, eos_token_id=tok.eos_token_id) raw = tok.decode(out[0, enc["input_ids"].shape[-1]:], skip_special_tokens=True) cleaned = re.sub(r".*?", "", raw, flags=re.DOTALL) m = re.search(r"\{.*\}", cleaned, flags=re.DOTALL) return (json.loads(m.group()) if m else None), raw pred, raw = parse( "The Obama administration is putting Border Patrol agents in a chokehold." ) print(pred) ``` ## Things worth knowing before using this The model emits `...` blocks on political and opinionated inputs (that's where it saw CoT during training). On formal or structured text — legal writing, some scientific abstracts — it goes straight to JSON. Neither is a bug, it just needs handling if you're stripping the raw generation. Long inputs can trigger a relation-generation loop that leaves the JSON unclosed. What happens: the model emits valid `claim_components` and `premise_components` early, then gets stuck emitting `{"src": N, "tgt": M, "type": "support"}` tuples in a repeating pattern until it hits `max_new_tokens`. You never see the closing `}` so strict JSON parsing rejects everything. The workaround is either setting `max_new_tokens` conservatively, or using a lenient parser that pulls each valid `{...}` object out of each section independently and dedupes relations. I use the second approach in the training repo. v4 systematically over-predicts spans on OOD text — precision runs lower than recall in every probe. On borderline sentences it defaults to labeling as claim. Something to keep in mind if downstream consumers care about precision. Fine-grained annotation schemas map imperfectly to v4's binary claim/premise split. PERSUADE's Evidence/Rebuttal distinction and ECHR's Court/Applicant/State agent labels don't translate directly. v4 knows a claim from a premise; it doesn't know PERSUADE's Evidence from PERSUADE's Rebuttal. ## Training summary - Base: `Qwen/Qwen2.5-1.5B-Instruct` - LoRA: r=16, α=32, dropout 0.05, on `q_proj,k_proj,v_proj,o_proj` - Training records: 3,617 (5 gold corpora + 2,123 LIARArg silver) - Epochs: 3, fresh adapter - Optimizer: Adafactor, fp16, gradient checkpointing - Hardware: single NVIDIA GTX 1080 Ti - Wall clock: ~29 h ## License Apache 2.0. Base model (Qwen 2.5) is also Apache 2.0. Use however you want, no warranty.