crop-burn-detector-v2

Fine-tuned LFM2.5-VL-450M (Liquid AI) for detecting crop residue burning in Sentinel-2 satellite imagery over northern India.

Given a pair of Sentinel-2 tiles — RGB (B4-B3-B2) and SWIR (B12-B8-B4) — covering a 5km × 5km agricultural area, the model outputs a structured JSON burn assessment. It was trained on 1,098 labelled image pairs and evaluated against the base model on 178 held-out test tiles.


Benchmark — Base vs Fine-tuned

Evaluated on 178 clear-sky test tiles (held-out split of crop-burn-detection-labeled).

Model burn_detected burn_severity smoke_visible veg_phase Overall
Base LFM2.5-VL-450M (no fine-tune) 73.6% 11.8% 0.0% 24.2% 21.9%
crop-burn-detector-v2 (ours) 73.6% 53.9% 100.0% 46.6% 74.8%
Δ — +42.1pp +100pp +22.4pp +52.9pp

Primary metric (mean of burn_detected + burn_severity): 42.7% → 63.8% (+21.1pp)

The base model produces syntactically valid JSON on every sample but collapses severity predictions to a single dominant class and outputs 0% accuracy on smoke visibility and image quality fields. Fine-tuning on domain-specific Sentinel-2 pairs corrects the severity distribution and fully unlocks the smoke and quality heads.


Model details

Base model LiquidAI/LFM2.5-VL-450M
Method LoRA (rank 32, alpha 32, dropout 0.05)
Trainable params 16.3M / 465M (3.5%)
Target modules q/k/v/out_proj, fc1/fc2, w1/w2/w3, linear_1/2 (multimodal projector)
Training samples 1,098
Epochs 5
Learning rate 2e-5 (cosine + 5% warmup)
Effective batch 16 (batch=1, grad_accum=16)
Precision BF16
Loss Class-balanced cross-entropy (no-burn class weight ×1.86)
GPU NVIDIA RTX A4000 (16 GB VRAM)
Training time ~16 minutes
ONNX export munish0838/parali-v1-onnx

Output schema

{
  "burn_detected": true,
  "burn_severity": "moderate",
  "active_smoke_visible": false,
  "vegetation_phase": "post_harvest",
  "image_quality_limited": false
}
Field Type Values
burn_detected bool true / false
burn_severity string none / low / moderate / high
active_smoke_visible bool true / false
vegetation_phase string pre_harvest / harvest_in_progress / post_harvest / fallow
image_quality_limited bool true / false (cloud cover, missing data)

Inference

from transformers import AutoProcessor, AutoModelForImageTextToText
from PIL import Image
import torch, json

model = AutoModelForImageTextToText.from_pretrained(
    "munish0838/crop-burn-detector-v2",
    torch_dtype=torch.bfloat16,
    device_map="auto",
    trust_remote_code=True,
)
processor = AutoProcessor.from_pretrained(
    "munish0838/crop-burn-detector-v2",
    trust_remote_code=True,
)

rgb_image  = Image.open("tile_rgb.png")   # Sentinel-2 B4-B3-B2 composite
swir_image = Image.open("tile_swir.png")  # Sentinel-2 B12-B8-B4 composite

SYSTEM_PROMPT = (
    "You are an expert in analyzing Sentinel-2 satellite imagery for crop residue "
    "burning detection in northern India. You analyze pairs of RGB and SWIR composites "
    "of 5km tiles and produce structured JSON assessments."
)

USER_PROMPT = """The following two images show the same 5km × 5km tile of agricultural land.

Image 1 is the RGB composite (B4-B3-B2, natural color).
Image 2 is the SWIR composite (B12-B8-B4 false color), which highlights vegetation moisture, burn scars, and active fires.

In the SWIR composite:
  - Bright green = healthy standing crop (high NIR)
  - Brownish / pinkish-brown = harvested stubble or bare soil
  - Charcoal black or dark brownish-red with rectangular edges = burn scars
  - White or bright cyan = cloud cover

Analyze both images and output exactly this JSON with no additional text:

{
  "burn_detected": <true|false>,
  "burn_severity": <"none"|"low"|"moderate"|"high">,
  "active_smoke_visible": <true|false>,
  "vegetation_phase": <"pre_harvest"|"harvest_in_progress"|"post_harvest"|"fallow">,
  "image_quality_limited": <true|false>
}"""

messages = [
    {"role": "system", "content": SYSTEM_PROMPT},
    {"role": "user", "content": [
        {"type": "image", "image": rgb_image},
        {"type": "image", "image": swir_image},
        {"type": "text",  "text": USER_PROMPT},
    ]},
]

inputs = processor.apply_chat_template(
    messages,
    add_generation_prompt=True,
    return_tensors="pt",
    return_dict=True,
    tokenize=True,
).to(model.device)

with torch.no_grad():
    out = model.generate(**inputs, max_new_tokens=256, do_sample=False)

input_len  = inputs["input_ids"].shape[1]
new_tokens = out[:, input_len:]
raw = processor.batch_decode(new_tokens, skip_special_tokens=True)[0].strip()

result = json.loads(raw)
print(result)
# {'burn_detected': True, 'burn_severity': 'moderate', 'active_smoke_visible': False,
#  'vegetation_phase': 'post_harvest', 'image_quality_limited': False}

Dataset

Raw imagery munish0838/crop-burn-detection-raw
Labelled (used for training) munish0838/crop-burn-detection-labeled
Total samples 1,370 (1,103 train / 267 test)
Coverage 55 districts · 5 Indian states · 22–31.7°N, 73.6–83.7°E
Date range March – November 2025
Tile size 5km × 5km Sentinel-2 tiles
Imagery source DPhi SimSat API → ESA Copernicus

Why this model exists

Official Indian government fire monitoring relies on VIIRS and MODIS satellites that pass at fixed times (10:30 AM, 1:30 PM). By 2025, over 90% of large crop fires in Punjab were being lit after 3 PM — after every government satellite had passed. A 2025 CEEW field study found 169 burnt fields in a single district over two days; the government's system detected 7.

Sentinel-2 burn scars persist for days after a fire. This model reads those scars — alongside live NASA FIRMS hotspots — to give enforcement teams a complete picture regardless of when the fire happened.

This is part of Parali, a real-time crop burn detection platform for northern India.

Downloads last month
89
Safetensors
Model size
0.4B params
Tensor type
BF16
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for munish0838/crop-burn-detector-v2

Finetuned
(22)
this model

Dataset used to train munish0838/crop-burn-detector-v2