The main task of the competition is to reconstruct 45 trojans (triggers), i.e., short multivariate time series segments (3 channels by 75 samples), injected into 45 poisoned models for satellite telemetry forecasting (one trigger per model).
Trojan horse attacks (sometimes called backdoors or triggers) are a significant security threat to AI models, enabling adversaries to manipulate test-time predictions by embedding triggers during training. Triggers to be identified in the competition are short multivariate time series segments having the same number of channels as the input signal (3 channels by 75 samples). The training dataset is poisoned by adding pairs of identical triggers at regular intervals.
The solution uses an optimization-based method to reconstruct trigger patterns injected into poisoned models. The approach consists of several key components:
- Covariance Matrix Adaptation Evolution Strategy (CMA-ES): A evolutionary algorithm that adapts the search distribution based on the success of previous generations
- The optimization searches for trigger patterns (3 channels × 75 samples) that maximize divergence between poisoned and clean predictions
- Wavelet representation: Triggers are represented in the wavelet domain using Daubechies 4 (
db4) wavelets - Compression ratio (0.6): Only 60% of wavelet coefficients are kept, prioritizing low-frequency components
- This reduces the search space dimensionality while preserving important signal characteristics
These were taken from a public notebook (could trace back it to @lennarthaupts)
- 33 candidate patterns are pre-generated:
- Constant values per channel (positive/negative limits)
- Linear ramps
- Step functions (switch patterns)
- Sine waves
- Various combinations across channels
- Divergence score: Measures how much the poisoned model's predictions diverge from clean predictions when the trigger is injected
- Channel-wise normalization: Each channel's contribution is normalized by baseline prediction error to account for varying channel sensitivities
- Tracking penalty (optional): Penalizes triggers that cause predictions to track the input too closely
- Rolling optimization: Tests all possible circular shifts (-74 to +74) of the trigger to find the optimal alignment
- Channel pruning: Evaluates each channel independently and removes those that don't contribute significantly (threshold: 0.0069)
- Regularization: Applies
get_diffto refine the trigger based on actual prediction differences - Channel flipping: Tests flipping the sign of each channel independently to improve the score
- Final triggers are only accepted if they achieve a score above 0.002
- Models failing this threshold default to zero triggers
Create venv
- Linux
python3 -m venv .venv
source .venv/bin/activate- Install dependencies from requirements.txt:
pip install -r requirments.txt- data in the appropriate directory structure like in kaggle notebook in competition:
/kaggle/input/trojan-horse-hunt-in-space/
├── clean_train_data.csv
└── poisoned_models/
├── poisoned_model_1/
│ └── poisoned_model.pt
├── poisoned_model_2/
│ └── poisoned_model.pt
└── ...
- Run the main script:
python main.pyCode here is for all 45 models in one run.
Doing this for all 45 models in one run was time consuming so i did for 5 models at a time in kaggle notebooks. So 9*5=45: total 9 runs.
I will update a separate joblib version too, that does parallel processing.
For more details, background, and citing the competition, refer to the arXiv publication: https://arxiv.org/abs/2506.01849
Source: https://www.kaggle.com/competitions/trojan-horse-hunt-in-space


