iRanadheer commited on
Commit
9bc4a66
·
verified ·
1 Parent(s): 88795e4

Add model card

Browse files
Files changed (1) hide show
  1. README.md +178 -0
README.md ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ language:
4
+ - en
5
+ base_model:
6
+ - Qwen/Qwen3.5-9B
7
+ pipeline_tag: text-generation
8
+ library_name: transformers
9
+ tags:
10
+ - climate
11
+ - climate-change
12
+ - climate-discourse
13
+ - classification
14
+ - qwen3
15
+ - fine-tuned
16
+ - cards
17
+ - image-text-to-text
18
+ - multimodal
19
+ - vision-language
20
+ datasets:
21
+ - C3DS/cards_sft_dataset
22
+ ---
23
+
24
+ # CARDS-Qwen3.5-9B
25
+
26
+ Fine-tuned **Qwen3.5-9B** for classification of climate-contrarian claims using the **CARDS taxonomy** from Coan et al. (2025).
27
+
28
+ This is a **merged** checkpoint: a LoRA adapter (rank 16) trained on the CARDS SFT dataset has been merged back into the base weights for direct loading with `transformers`, vLLM, or any standard inference engine.
29
+
30
+ ## Results
31
+
32
+ Evaluated on the held-out CARDS test set (1,436 samples, Level 1, `min_support ≥ 3`):
33
+
34
+ | Metric | Qwen3.5-9B (base) | Qwen3.5-4B FT | **Qwen3.5-9B FT** | Qwen3.5-27B FT | Claude Opus 4.6 |
35
+ |---|---|---|---|---|---|
36
+ | Samples F1 | 0.721 | 0.838 | **0.872** | 0.884 | 0.893 |
37
+ | Macro F1 | 0.629 | 0.632 | **0.663** | 0.766 | 0.751 |
38
+ | Micro F1 | 0.775 | 0.828 | **0.862** | 0.877 | 0.881 |
39
+ | Precision | 0.866 | 0.840 | **0.875** | 0.879 | 0.863 |
40
+ | Recall | 0.701 | 0.816 | **0.849** | 0.874 | 0.900 |
41
+ | Parse failures | 247 / 1436 | 1 / 1436 | **0 / 1436** | 0 / 1436 | 0 / 1436 |
42
+
43
+ - Fine-tuning lifts samples F1 from 0.721 (base) to 0.872 (+0.151).
44
+ - Zero parse failures on 1,436 test items — the model reliably emits the YAML format.
45
+ - Sweet-spot for deployment cost vs accuracy: ≈ 0.012 below the 27B FT and ≈ 0.021 below Opus 4.6 on samples F1, at a fraction of the size.
46
+ - Per-level breakdown: L1 0.872 / L2 0.840 / L3 0.813 samples F1.
47
+
48
+ ## Usage
49
+
50
+ ### With vLLM
51
+
52
+ ```bash
53
+ vllm serve C3DS/CARDS-Qwen3.5-9B \
54
+ --port 8000 \
55
+ --max-model-len 4096 \
56
+ --dtype bfloat16 \
57
+ --enable-prefix-caching \
58
+ --served-model-name CARDS-Qwen3.5-9B
59
+ ```
60
+
61
+ The system prompt (`slim_system_instruction`) and the user-message suffix (`cot_trigger`) the model was trained with are bundled in this repo as [`cards_prompts.json`](./cards_prompts.json) — self-contained, with the CARDS taxonomy already inlined.
62
+
63
+ ```python
64
+ import json
65
+ from huggingface_hub import hf_hub_download
66
+ from openai import OpenAI
67
+
68
+ prompts = json.load(open(hf_hub_download("C3DS/CARDS-Qwen3.5-9B", "cards_prompts.json")))
69
+ slim_system_instruction = prompts["slim_system_instruction"]
70
+ cot_trigger = prompts["cot_trigger"]
71
+
72
+ client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")
73
+
74
+ def classify(text):
75
+ resp = client.chat.completions.create(
76
+ model="CARDS-Qwen3.5-9B",
77
+ messages=[
78
+ {"role": "system", "content": slim_system_instruction},
79
+ {"role": "user", "content": f"### Text:\n{text}\n\n{cot_trigger}"},
80
+ ],
81
+ temperature=0,
82
+ max_tokens=4000,
83
+ )
84
+ return resp.choices[0].message.content
85
+
86
+ print(classify("These are only a few renewable energy technologies at work"))
87
+ ```
88
+
89
+ The model produces a reasoning trace inside `<think>…</think>` followed by a YAML `categories:` block listing predicted CARDS codes. To parse: take the content after `</think>` and read the `categories:` list.
90
+
91
+ For an FP8-quantized variant (~9 GB on disk, no measurable accuracy loss) see [`C3DS/CARDS-Qwen3.5-9B-FP8`](https://huggingface.co/C3DS/CARDS-Qwen3.5-9B-FP8).
92
+
93
+
94
+ ### Multimodal — image + text
95
+
96
+ The base Qwen3.5/3.6 family supports image inputs via the OpenAI-compatible
97
+ `image_url` content part, and this fine-tune preserves that capability — pass
98
+ the system prompt below alongside an image (with or without caption text) and
99
+ the model will classify the depicted claim under the CARDS taxonomy.
100
+
101
+ Serve vLLM with multimodal flags enabled:
102
+
103
+ ```bash
104
+ vllm serve C3DS/CARDS-Qwen3.5-9B \
105
+ --port 8000 \
106
+ --max-model-len 8192 \
107
+ --trust-remote-code \
108
+ --limit-mm-per-prompt image=4 \
109
+ --enable-prefix-caching \
110
+ --served-model-name CARDS-Qwen3.5-9B
111
+ ```
112
+
113
+ ```python
114
+ import base64, json, mimetypes
115
+ from pathlib import Path
116
+ from huggingface_hub import hf_hub_download
117
+ from openai import OpenAI
118
+
119
+ prompts = json.load(open(hf_hub_download("C3DS/CARDS-Qwen3.5-9B", "cards_prompts.json")))
120
+ slim_system_instruction = prompts["slim_system_instruction"]
121
+ cot_trigger = prompts["cot_trigger"]
122
+
123
+ def image_part(path):
124
+ p = Path(path)
125
+ mime = mimetypes.guess_type(p)[0] or "image/png"
126
+ b64 = base64.b64encode(p.read_bytes()).decode()
127
+ return {"type": "image_url", "image_url": {"url": f"data:{mime};base64,{b64}"}}
128
+
129
+ client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")
130
+
131
+ resp = client.chat.completions.create(
132
+ model="CARDS-Qwen3.5-9B",
133
+ messages=[
134
+ {"role": "system", "content": slim_system_instruction},
135
+ {"role": "user", "content": [
136
+ {"type": "text", "text": "Read the image (and any caption below) and classify the climate claim it makes."},
137
+ image_part("screenshot.png"),
138
+ {"type": "text", "text": f"### Caption:\n<optional caption>\n\n{cot_trigger}"},
139
+ ]},
140
+ ],
141
+ temperature=0,
142
+ max_tokens=4000,
143
+ )
144
+ print(resp.choices[0].message.content)
145
+ ```
146
+
147
+ ## Training
148
+
149
+ - **Base model:** `Qwen/Qwen3.5-9B`
150
+ - **Method:** LoRA (rank 16, α 16, dropout 0) on `q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj`, then merged into base weights
151
+ - **Dataset:** [`C3DS/cards_sft_dataset`](https://huggingface.co/datasets/C3DS/cards_sft_dataset) (`sft` config — RECoT chat messages)
152
+ - **Framework:** Unsloth + TRL `SFTTrainer`
153
+ - **Hyperparameters:** 3 epochs, `per_device_train_batch_size=1`, `gradient_accumulation_steps=8`, `lr=2e-4`, cosine schedule, 10 warmup steps, `max_seq_length=4096`, `adamw_8bit`, `bf16`
154
+ - **Hardware:** 1× NVIDIA H200
155
+ - **Checkpoint selection:** best via `load_best_model_at_end=True`
156
+
157
+ ## Limitations
158
+
159
+ - **Macro F1 on rare labels.** Rare level-3 claims (under 10 training examples) trail Claude Opus by a wider margin than common claims, reflecting the long-tailed CARDS distribution.
160
+ - **Thinking tokens.** Training used `enable_thinking=True`. Either parse output after `</think>`, or disable thinking at inference via `chat_template_kwargs={"enable_thinking": false}`. Reserve token budget for the reasoning trace before the final YAML block.
161
+
162
+ ## Citation
163
+
164
+ ```bibtex
165
+ @article{coan2025cards,
166
+ title = {Large language model reveals an increase in climate contrarian speech in the United States Congress},
167
+ author = {Coan, Travis G. and Malla, Ranadheer and Nanko, Mirjam O. and Kattrup, William and Roberts, J. Timmons and Cook, John and Boussalis, Constantine},
168
+ journal = {Communications Sustainability},
169
+ volume = {1},
170
+ pages = {37},
171
+ year = {2025},
172
+ doi = {10.1038/s44458-025-00029-z}
173
+ }
174
+ ```
175
+
176
+ ## License
177
+
178
+ Apache 2.0, inherited from Qwen3.5-9B.