Dark Fleet Detection β€” CNN + LSTM + RNN Fusion

A multimodal deep learning framework for detecting dark fleet vessels in the Indian Ocean using Sentinel-1 SAR imagery and Global Fishing Watch vessel detection data.

Model Description

This repository contains five trained model files for a three-branch dark fleet detection pipeline:

File Branch Description
cnn_branch.keras CNN MobileNetV2 SAR vessel density classifier
lstm_best.keras LSTM Bidirectional LSTM dark vessel detector
rnn_best.keras RNN SimpleRNN positional anomaly detector
lstm_scaler.pkl LSTM/RNN StandardScaler for feature normalisation
fusion_meta_model.pkl Fusion Logistic regression meta-learner

Performance

Branch AUC Accuracy Precision Recall
CNN 0.941 87.4% 91.0% 81.4%
LSTM 0.911 85.1% 68.1% 80.0%
RNN 0.927 86.0% 69.8% 82.2%
Fusion 0.926 88.0% 78.0% 72.0%

Training Data

  • CNN branch: SARscope dataset (6,735 Sentinel-1 SAR image patches, COCO format)
  • LSTM/RNN branches: Global Fishing Watch Sentinel-1 SAR vessel detections, filtered to Indian Ocean (lat βˆ’30Β° to 30Β°N, lon 40Β° to 100Β°E), March 2026 β€” 16,795 records, 1,617 vessel sequences

How to Use

Install dependencies

pip install tensorflow scikit-learn joblib huggingface_hub

Load models

from huggingface_hub import hf_hub_download
import tensorflow as tf
import joblib
import numpy as np

REPO = "neural-shubh/dark-fleet-models"

# Load all models
cnn_model    = tf.keras.models.load_model(hf_hub_download(REPO, "cnn_branch.keras"))
lstm_model   = tf.keras.models.load_model(hf_hub_download(REPO, "lstm_best.keras"))
rnn_model    = tf.keras.models.load_model(hf_hub_download(REPO, "rnn_best.keras"))
scaler       = joblib.load(hf_hub_download(REPO, "lstm_scaler.pkl"))
fusion_model = joblib.load(hf_hub_download(REPO, "fusion_meta_model.pkl"))

print("All models loaded βœ…")

Run inference on a vessel sequence

# Vessel sequence β€” shape (1, 2, 12)
# Features: lat, lon, presence_score, length_m, fishing_score,
#           matching_score, hour, dayofweek,
#           lat_diff, lon_diff, speed_proxy, impossible_speed

sequence = np.array([[
    [12.5, 72.3, 0.99, 180.0, 0.02, 0.0, 2.0, 5.0, 0.0, 0.0, 0.0, 0.0],
    [12.8, 74.1, 0.97, 180.0, 0.02, 0.0, 2.1, 5.0, 0.3, 1.8, 0.002, 1.0]
]], dtype=np.float32)

# Scale features
sequence_scaled = scaler.transform(
    sequence.reshape(-1, 12)
).reshape(1, 2, 12)

# Predict
lstm_score   = lstm_model.predict(sequence_scaled, verbose=0)[0][0]
rnn_score    = rnn_model.predict(sequence_scaled, verbose=0)[0][0]
fusion_score = fusion_model.predict_proba([[lstm_score, rnn_score]])[0][1]

print(f"LSTM score:   {lstm_score:.4f}")
print(f"RNN score:    {rnn_score:.4f}")
print(f"Fusion score: {fusion_score:.4f}")
print(f"Verdict: {'DARK VESSEL ⚠️' if fusion_score > 0.5 else 'Normal βœ…'}")

Architecture

SAR Imagery ──► CNN Branch (MobileNetV2) ──► Spatial Triage Score

GFW Vessel  ──► LSTM Branch (BiLSTM 64β†’32)   ──► Dark Score ──► Logistic
Sequences   ──► RNN Branch (SimpleRNN 64β†’32) ──► Dark Score ──► Regression ──► Risk Score

Limitations

  • Labels derived from AIS matching status, not independently verified dark fleet behaviour
  • Sophisticated spoofing (AIS transmitting but position falsified) would evade detection
  • Trained on single-month GFW snapshot β€” most vessels appear only once, limiting sequence length to 2
  • Intended as analyst support tool, not autonomous enforcement mechanism

Citation

If you use these models in your research, please cite:

@misc{darkfleet2026,
  author = {Shubh},
  title = {Monitoring Dark Fleets and Maritime Domain Awareness: 
           A Multimodal CNN-LSTM-RNN Framework for the Indian Ocean Region},
  year = {2026},
  publisher = {Hugging Face},
  url = {https://huggingface.co/neural-shubh/dark-fleet-models}
}

Links

Downloads last month
63
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support