stixbert / README.md
shidey's picture
Update README.md
e216a1b verified
|
Raw
History Blame Contribute Delete
6.58 kB
---
language: en
license: apache-2.0
library_name: pytorch
pipeline_tag: graph-ml
tags:
- threat-intelligence
- stix
- graph-neural-network
- hgt
- cybersecurity
---
# STIXBert β€” A Graph Transformer on native STIX2.1 schema
**STIXBert** is a Heterogeneous Graph Transformer (HGT) pre-trained with
self-supervised objectives on real-world STIX 2.1 threat-intelligence graphs.
It produces fixed-size embeddings for every node in a STIX bundleβ€”indicators,
malware, attack-patterns, threat-actors, campaigns, and moreβ€”enabling
downstream tasks such as campaign clustering, ATT&CK technique classification,
cross-feed deduplication, infrastructure prediction, and feed quality scoring.
## Model Details
| Property | Value |
|----------|-------|
| Architecture | Heterogeneous Graph Transformer (HGT) |
| Layers | 4 |
| Attention heads | 4 |
| Hidden dimension | 128 |
| Input dimension | 128 |
| Output dimension | 128 |
| Dropout | 0.1 |
| Parameters | **26,916,764** (~102.7 MB fp32) |
| Text encoder | `all-MiniLM-L6-v2` (max 256 tokens) |
| Framework | PyTorch + PyTorch Geometric |
## Pre-training Objectives
STIXBert is trained with three complementary self-supervised losses:
1. **Masked Node Prediction** (weight 1.0) β€”
15% of node features are zeroed; the model
reconstructs them via cosine-similarity loss.
2. **Link Prediction** (weight 1.0) β€” binary
cross-entropy on existing edges vs. sampled negatives.
3. **Temporal Ordering** (weight 0.3) β€” predict
which of two nodes appeared first based on STIX timestamps.
## Training Procedure
### Phase 1 β€” Hyperparameter Search
- **Strategy:** Random search (20 trials)
- **CV:** 5-fold stratified by `node_type`
- **Search epochs per trial:** 5
- **Swept parameters:** `hidden_dim, num_heads, num_layers, lr, batch_size, mask_ratio, dropout`
### Phase 2 β€” Full Training with Best HPs
- **Max epochs:** 200 (early stopping patience=30)
- **Epochs completed:** 164
- **Optimizer:** ADAMW (weight_decay=0.0001)
- **Scheduler:** cosine (warmup=10 epochs, min_lr=1e-06)
- **Gradient clipping:** 1.0
- **Mixed precision:** Yes
- **Class imbalance:** weighted
### Best Hyperparameters (from Phase 1)
| Parameter | Value |
|-----------|-------|
| `batch_size` | 32 |
| `dropout` | 0.1 |
| `hidden_dim` | 256 |
| `lr` | 0.002 |
| `mask_ratio` | 0.1 |
| `num_heads` | 2 |
| `num_layers` | 6 |
### Final Training Losses (Epoch 164)
| Loss | Value |
|------|-------|
| **Total** | 0.4259 |
| Masked Node | 0.0576 |
| Link Prediction | 0.1791 |
| Temporal Ordering | 0.6308 |
## Training Data
| Statistic | Value |
|-----------|-------|
| Total nodes | 9,524 |
| Total edges | 26,079 |
| Node types | 11 |
| Edge types | 18 |
**Data sources:**
- **MITRE ATT&CK** β€” Enterprise, Mobile, ICS (STIX 2.1 bundles from
`raw.githubusercontent.com/mitre-attack/attack-stix-data`)
- **ThreatFox** β€” Recent IOCs exported as STIX 2.1
(`threatfox.abuse.ch/export/json/recent/`)
- **DigitalSide Threat-Intel** β€” Community STIX 2.1 bundles
(`github.com/davidonzo/Threat-Intel`)
**Node types:** attack_pattern, campaign, course_of_action, file, identity, indicator, infrastructure, intrusion_set, malware, tool, vulnerability
**Edge types:** attack_pattern-[revoked_by]->attack_pattern, attack_pattern-[subtechnique_of]->attack_pattern, campaign-[attributed_to]->intrusion_set, campaign-[uses]->attack_pattern, campaign-[uses]->malware, campaign-[uses]->tool, course_of_action-[mitigates]->attack_pattern, indicator-[indicates]->infrastructure, indicator-[indicates]->malware, infrastructure-[communicates_with]->malware, intrusion_set-[revoked_by]->intrusion_set, intrusion_set-[uses]->attack_pattern, intrusion_set-[uses]->malware, intrusion_set-[uses]->tool, malware-[revoked_by]->malware, malware-[revoked_by]->tool, malware-[uses]->attack_pattern, tool-[uses]->attack_pattern
## Intended Uses
| Use Case | Description |
|----------|-------------|
| Campaign clustering | Group malware/indicators by embedding similarity; attribute new IOCs to known campaigns |
| ATT&CK classification | Fine-tune a linear head to predict MITRE ATT&CK tactics from node embeddings |
| Cross-feed deduplication | Identify near-duplicate indicators across feeds via cosine similarity |
| Infrastructure prediction | Predict which infrastructure a malware family will use next |
| Feed quality scoring | Score feed reliability by measuring embedding alignment with ATT&CK ground truth |
## Limitations
- Pre-trained on publicly available threat intel only; may not generalize to
classified or proprietary feeds without fine-tuning.
- Graph structure depends on relationship quality in source data; missing or
incorrect STIX relationships degrade embedding quality.
- Text features are encoded with `all-MiniLM-L6-v2` β€” very long descriptions
are truncated to 256 tokens.
- Node types not seen during pre-training will need the graph rebuilt with
`include_scos=True` or additional SDO types.
## How to Use
```python
import torch
from huggingface_hub import hf_hub_download
# Download model + config
ckpt_path = hf_hub_download(repo_id='shidey/stixbert', filename='stixbert_best.pt')
cfg_path = hf_hub_download(repo_id='shidey/stixbert', filename='config.json')
meta_path = hf_hub_download(repo_id='shidey/stixbert', filename='graph_metadata.json')
import json
with open(cfg_path) as f:
cfg = json.load(f)
with open(meta_path) as f:
meta = json.load(f)
# Rebuild model (paste STIXBert class or import from your code)
model = STIXBert(
node_types=meta['node_types'],
edge_types=[tuple(et) for et in meta['edge_types']],
input_dim=cfg['model']['input_dim'],
hidden_dim=meta['best_hyperparameters'].get('hidden_dim', cfg['model']['hidden_dim']),
num_heads=meta['best_hyperparameters'].get('num_heads', cfg['model']['num_heads']),
num_layers=meta['best_hyperparameters'].get('num_layers', cfg['model']['num_layers']),
dropout=meta['best_hyperparameters'].get('dropout', cfg['model']['dropout']),
)
model.load_state_dict(torch.load(ckpt_path, map_location='cpu', weights_only=False))
model.eval()
# Get embeddings
embeddings = model.get_embeddings(x_dict, edge_index_dict)
```
## Citation
If you use STIXBert in your work, please cite:
```bibtex
@software{stixbert2026,
author = {Dey, Shiladitya},
title = {STIXBert: Self-Supervised STIX Graph Foundation Model},
year = {2026},
url = {https://huggingface.co/shidey/stixbert},
}
```
## Repository
Source code and Colab notebook:
[github.com/sd1977/STIXBert](https://github.com/sd1977/STIXBert)