CARDS-Wind-Gemma4-31B
Gemma 4 31B fine-tuned to detect and classify opposition to wind energy under the three-level CARDS-Wind taxonomy. LoRA-trained on human-annotated wind-opposition passages (augmented with Opus-generated reasoning traces and stratified synthetic data), then merged back into the base weights. BF16 checkpoint; multimodal (image + text).
Deploying? Use the FP8 variant
C3DS/CARDS-Wind-Gemma4-31B-FP8โ ~33 GB vs ~62 GB here, near-lossless, fits a single 80GB GPU with room for KV cache.
What it does
Given a passage (news, comment, forum post, council minutes, social media), the model emits a three-level classification:
opposition_detectedโ binary: does the text oppose, dislike, or express skepticism of wind energy? (sarcasm and tonal opposition count)framesโ which of 8 high-level argument frames (N_1โN_8, orN_0fallback) the text invokes.claimsโ the specific assertions (C_*codes, ~58-code hierarchical codebook) argued within those frames.
When opposition_detected is false, frames and claims are empty.
Results
Evaluated on the held-out Wind test set (773 rows, 436 opposition-only; Macro/Micro F1 restricted to gold codes with support โฅ 3, hallucinated codes dropped). Compared against the paper's best fine-tuned Qwen model and two frontier APIs:
| Metric | Gemma4-31B FT (this model) | Windy-Qwen3.5-27B FT | Claude Opus 4.7 | GPT-5.5 |
|---|---|---|---|---|
| Detection โ Precision | 0.871 | 0.877 | 0.896 | 0.927 |
| Detection โ Recall | 0.897 | 0.920 | 0.890 | 0.846 |
| Detection โ F1 | 0.884 | 0.898 | 0.893 | 0.885 |
| Samples F1 โ frames (all rows) | 0.773 | 0.787 | 0.791 | 0.792 |
| Samples F1 โ frames (opp. only) | 0.730 | 0.751 | 0.734 | 0.697 |
| Samples F1 โ claims (all rows) | 0.741 | 0.755 | 0.754 | 0.745 |
| Samples F1 โ claims (opp. only) | 0.674 | 0.694 | 0.667 | 0.614 |
| Macro F1 โ frames (opp. only) | 0.692 | 0.664 | 0.692 | 0.660 |
| Macro F1 โ claims (opp. only) | 0.632 | 0.648 | 0.635 | 0.608 |
| Micro F1 โ frames (opp. only) | 0.788 | 0.796 | 0.788 | 0.696 |
| Micro F1 โ claims (opp. only) | 0.728 | 0.735 | 0.724 | 0.696 |
| Parse failures | 0 / 773 | 0 / 773 | 1 / 773 | 2 / 773 |
Competitive with frontier APIs at a fraction of the inference cost โ best or tied-best Macro F1 on frames (opposition-only) alongside Claude Opus 4.7, and zero parse failures.
Usage
The system prompt and three-level output format are bundled in the FP8 repo as wind_prompts.json. The fine-tune was trained with the slim system instruction โ use slim_system_instruction.
With vLLM
vllm serve C3DS/CARDS-Wind-Gemma4-31B \
--port 8000 \
--max-model-len 8192 \
--enable-prefix-caching \
--served-model-name CARDS-Wind-Gemma4-31B
import json
from huggingface_hub import hf_hub_download
from openai import OpenAI
prompts = json.load(open(hf_hub_download("C3DS/CARDS-Wind-Gemma4-31B-FP8", "wind_prompts.json")))
system_instruction = prompts["slim_system_instruction"]
client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")
def classify(text):
resp = client.chat.completions.create(
model="CARDS-Wind-Gemma4-31B",
messages=[
{"role": "system", "content": system_instruction},
{"role": "user", "content": f"### Text:\n{text}"},
],
temperature=0,
max_tokens=4000,
extra_body={"chat_template_kwargs": {"enable_thinking": True}},
)
return resp.choices[0].message.content
print(classify("These bird-killing turbines will spike our electricity bills and never pay for themselves."))
The model reasons inside <think>โฆ</think> and then emits a YAML block. To parse: take the content after </think> and read the opposition_detected, frames, and claims fields:
opposition_detected: true
frames:
- N_1
claims:
- C_7_0
Thinking must be enabled. Gemma 4 chat templates default
enable_thinkingoff (they pre-fill an empty thought channel); always passchat_template_kwargs={"enable_thinking": true}or the model classifies without its reasoning chain.
Multimodal โ image + text
Gemma 4 is a vision-language model, so this fine-tune also classifies claims depicted in images (memes, screenshots, posters), with or without a caption. Serve with multimodal flags and pass an image_url content part alongside the system prompt:
vllm serve C3DS/CARDS-Wind-Gemma4-31B \
--port 8000 --max-model-len 8192 --trust-remote-code \
--limit-mm-per-prompt image=4 --enable-prefix-caching \
--served-model-name CARDS-Wind-Gemma4-31B
Training
- Base model:
google/gemma-4-31bfamily (Gemma 4,Gemma4ForConditionalGeneration) - Method: LoRA (rank 16, ฮฑ 16, dropout 0) on
q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj, merged into base weights - Dataset:
iRanadheer/wind-opposition-sftโ human-annotated wind-opposition passages augmented with Opus-generated RECoT reasoning traces and stratified synthetic positives / near-miss negatives - Framework: Unsloth + TRL
SFTTrainer - Hyperparameters: 3 epochs,
gradient_accumulation_steps=8,lr=2e-4, cosine schedule, 10 warmup steps,max_seq_length=4096,adamw_8bit,bf16, best checkpoint selected on eval loss
Limitations
- Thinking tokens. Training used
enable_thinking=True. Gemma 4 defaults it off โ always passchat_template_kwargs={"enable_thinking": true}, and reserve token budget for the reasoning trace before the final YAML block. - Domain. Tuned specifically for wind-energy opposition under the CARDS-Wind taxonomy; it is not a general climate-claim classifier (see the CARDS sibling models for that).
Citation
@article{coan2026llm,
title = {Large language model reveals an increase in climate contrarian speech in the United States Congress},
author = {Coan, Travis G. and Malla, Ranadheer and Nanko, Mirjam O. and Kattrup, William and Roberts, J. Timmons and Cook, John and Boussalis, Constantine},
journal = {Communications Sustainability},
volume = {1},
number = {1},
pages = {37},
year = {2026},
publisher = {Nature Publishing Group},
doi = {10.1038/s44458-025-00029-z},
url = {https://www.nature.com/articles/s44458-025-00029-z}
}
License
Apache 2.0, inherited from the base Gemma 4 model.
- Downloads last month
- 22