File size: 9,894 Bytes
d8b4e68
 
b78d8df
 
 
 
 
 
 
 
 
 
d8b4e68
b78d8df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5254c54
b78d8df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5254c54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b78d8df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5254c54
 
 
 
 
 
 
 
 
 
b78d8df
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
---
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
   `<think>...</think>{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 = "iamjayeshc/ArgParser-v4-Qwen1.5B"

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"<think>.*?</think>", "", 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)
```
Prompt:

Extract all argument components and relations from the text.

Return strict JSON with:
- claim_components
- premise_components
- citation_components
- relations

Output

{
  "claim_components": [],
  "premise_components": [],
  "citation_components": [],
  "relations": []
}

## Things worth knowing before using this

The model emits `<think>...</think>` 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

## Acknowledgements

This repository is maintained by Jayesh Choudhari.

The ArgParser research project was developed collaboratively, and this repository mirrors the final model with permission from the original collaborator.

Citation

A formal preprint is planned. Until then, please cite this Hugging Face repository if you use the model in your work.

## License

Apache 2.0. Base model (Qwen 2.5) is also Apache 2.0. Use however you
want, no warranty.