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
10
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
(36)
this model

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