Downloads · 30 days
0
nsaroiu/mtg-deck-completion
mtg-deck-completion is a machine learning model from nsaroiu. Use it for the machine learning task on the model card, and read the license before you ship it in a product. The card lists the license as mit.
Given a Commander (and optionally a partial decklist), suggests which other cards belong in the deck. Trained on 122,571 real public Commander decklists from Moxfield (dataset).
Downloads · 30 days
0
Access
Public
Updated Sep 24, 2026
Repo size
120 MB
Likes
0
Public
Click a slice to open those files.
.pt56.5 MB · 47%
From the Hugging Face model README
Given a Commander (and optionally a partial decklist), suggests which other cards belong in the deck. Trained on 122,571 real public Commander decklists from Moxfield (dataset).
Training/evaluation code: github.com/nsaroiu/mtg-deck-completion (the tokenizer + model + training/eval pipeline that produced these weights — not the Moxfield data-collection pipeline or the web UI built on top of this model, which live elsewhere and aren't public).
Every card gets a hybrid embedding: a learned per-card vector, summed with a small MLP over structured features (color identity, mana cost, type, keywords) and a semantic sentence embedding of its oracle text — so even a rarely-seen card starts from a meaningful representation instead of a near-random one.
A deck is treated as an unordered set: a permutation-invariant encoder (mean-pool or self-attention) pools the known cards + commander(s) into one vector, scored against the full card vocabulary via a tied dot product (word2vec-style).
Production inference is an ensemble of two specialized checkpoints, not one model — a single model/loss was tried and found to trade off staple recognition against synergy ranking:
set_transformer_softmax_checkpoint.pt, self-attention
encoder): generates iteratively, a few picks at a time, re-conditioning on
its own prior picks — this is what surfaces specific combo/synergy pieces
rather than a generic "good stuff" pile.deepsets_softmax_checkpoint.pt, mean-pool encoder):
tops up near-universal staples (Sol Ring, Command Tower, ...) to a
realistic target count, since the synergy phase alone under-recommends
them.prune_checkpoint_inject_lowdensity.pt): a
separate discriminator model that re-scores the assembled deck and cuts
the weakest fit, the one step no generation-only model can do.| file | role |
|---|---|
deepsets_softmax_checkpoint.pt | staple-recognition specialist |
set_transformer_softmax_checkpoint.pt | synergy-ranking specialist |
prune_checkpoint_inject_lowdensity.pt | optional deck-pruning pass |
tokenizer.json | card vocabulary + structured features |
tokenizer_text_embeddings.npy | cached oracle-text sentence embeddings |
card_tiers.json | staple / mid-tier / long-tail classification per card |
All three checkpoints share the same card vocabulary and expect
tokenizer.json / tokenizer_text_embeddings.npy alongside them.
Recall@50 / Precision@50 / MRR on held-out real decks, by how much of the deck is already known (mask ratio — low = mostly complete, high = mostly empty), staple-recognition checkpoint, validation split:
| mask ratio | Recall@50 | Precision@50 | MRR |
|---|---|---|---|
| 0.1 (deck mostly complete) | 0.60 | 0.10 | 0.132 |
| 0.5 | 0.51 | 0.44 | 0.068 |
| 0.9 (deck mostly empty) | 0.38 | 0.59 | 0.046 |
Held-out-commander split (commanders never seen in training, testing generalization via card content rather than memorized co-occurrence): Recall@50 0.27–0.41 across the same ratio range — meaningfully above chance, confirming the hybrid content embeddings carry real signal for unfamiliar commanders. Full breakdown by card-popularity tier, plus the synergy/pruning checkpoints' own numbers, in the GitHub repo's eval output.
There's no standalone pip package yet — loading these checkpoints requires
the tokenizer + model code from the GitHub repo above (tokenizer/ and
training/; no other part of that repo is needed to run inference):
git clone https://github.com/nsaroiu/mtg-deck-completion
cd mtg-deck-completion
pip install -r requirements.txt
from huggingface_hub import hf_hub_download
repo = "nsaroiu/mtg-deck-completion"
for f in ["deepsets_softmax_checkpoint.pt", "set_transformer_softmax_checkpoint.pt",
"prune_checkpoint_inject_lowdensity.pt", "tokenizer.json",
"tokenizer_text_embeddings.npy", "card_tiers.json"]:
hf_hub_download(repo_id=repo, filename=f, local_dir=".")
from training.evaluate import load_checkpoint
from training.ensemble import ensemble_complete_deck, load_tiers
from training.train import pick_device
device = pick_device()
staple_model, tok, _ = load_checkpoint("deepsets_softmax_checkpoint.pt", device)
synergy_model, _, _ = load_checkpoint("set_transformer_softmax_checkpoint.pt", device)
tiers = load_tiers(tok, path="card_tiers.json")
# (name, score, source) triples, source in {"synergy", "staple"}
results = ensemble_complete_deck(
["Atraxa, Praetors' Voice"], [], 20, tok, device,
staple_model, synergy_model, tiers,
)
for name, score, source in results:
print(f"{score:.3f} [{source:7}] {name}")
training/ensemble.py is also a CLI covering the same flow shown above
(--commander — repeatable, for partner commanders — --card,
--chunk-size/--temperature for the iterative synergy phase,
--prune-checkpoint to enable the pruning pass). training/complete_deck.py
exposes just one specialist checkpoint at a time, without the ensemble
fusion, if that's all you need.
legal_mask in training/complete_deck.py).
Using the raw logits without that filter can suggest illegal cards.The weights and code in this repository are released under the MIT license. The training data itself is a scrape of public Moxfield decklists with no asserted license (see the dataset card) — if that matters for your use case, that's Moxfield's terms to check, not a constraint this repo imposes.