Instructions to use P-s-v/knife-gliner-large-v2.5 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- GLiNER
How to use P-s-v/knife-gliner-large-v2.5 with GLiNER:
from gliner import GLiNER model = GLiNER.from_pretrained("P-s-v/knife-gliner-large-v2.5") - Notebooks
- Google Colab
- Kaggle
knife-gliner-large-v2.5
GLiNER large v2.5 (a 459M DeBERTa-v3-large encoder) fine-tuned to tag knife brands, knife models and knife steels in Reddit comments. From "picked up a Mazaki in white #2, way better than my old Fibrox" it returns Mazaki as a brand, Fibrox as a model and white #2 as a steel.
The labels it was trained on were written by Gemini 3.1 Pro, once, for $9. Nobody checked them by hand, so every score below is agreement with Gemini, not with the truth.
Write-up: https://petervijeh.com/projects/reddit-ner. Run log: https://new.knife.day/blog/fine-tuning-gliner-knife-ner.
Training script and pipeline: https://github.com/pvijeh/reddit-scraper-project (sidecar/train_gliner_modal.py).
Labels
knife brand(Spyderco, Mazaki, Benchmade)knife model(Paramilitary 2, 940, Fibrox)knife steel(MagnaCut, S35VN, white #2)
Results
Validation set: 225 comments held out before the second training run and never changed after.
| Model | F1 vs Gemini labels |
|---|---|
| GLiNER large v2.5, zero-shot | ~0.65 (est.) |
| GLiNER medium v2.5, fine-tuned (209M) | 0.800 |
| This model (run 10) | 0.83 |
An earlier run scored 0.879 on a random split. That number is not the result: on random splits, two drops in F1 that looked like regressions came from which comments landed in the validation set.
Steel names score lower confidence than brands or models. One threshold per class instead of a global 0.45 took steel recall from 0.787 to 0.911. The thresholds used in production:
label_thresholds = {"knife brand": 0.35, "knife model": 0.30, "knife steel": 0.20}
Usage
from gliner import GLiNER
model = GLiNER.from_pretrained("P-s-v/knife-gliner-large-v2.5")
labels = ["knife brand", "knife model", "knife steel"]
thresholds = {"knife brand": 0.35, "knife model": 0.30, "knife steel": 0.20}
text = "picked up a Mazaki in white #2, way better than my old Fibrox"
entities = model.predict_entities(text, labels, threshold=min(thresholds.values()))
entities = [e for e in entities if e["score"] >= thresholds[e["label"]]]
for e in entities:
print(e["text"], "=>", e["label"], round(e["score"], 2))
Training
- 2,025 training examples, 225 validation, GLiNER JSONL format:
tokenized_textplusnerspans. About 30% of examples have no entities. 51 of them are adversarial negatives, comments with generic words like "carbon steel" or "chef knife" and no product names; 510 made F1 worse (0.799). - Tesla T4 on Modal. The 459M encoder fits a T4 only with gradient accumulation. Best checkpoint at epoch 2; every large run overfits after that. The winning run took 24 minutes.
learning_rate = 1e-5
per_device_train_batch_size = 2
gradient_accumulation_steps = 8 # effective batch 16
max_steps = steps_per_epoch * epochs # GLiNER's default max_steps=10000 overrides num_train_epochs
Two bugs cost more than any hyperparameter. GLiNER's tokenize_inputs crashes on some inputs with
the Rust tokenizer's encode_batch; the script replaces it with per-example tokenizer.__call__.
And words_mask is not a mask: it is a 1-based word index per token, 0 on special tokens. Filling
it with ones trains a model that learns nothing, with a flat loss and no error.
Limits
- Silver labels. Where Gemini was wrong, this model is graded right for copying the mistake.
- Trained on eight knife subreddits. Brands it never saw in training (small makers) are the weak spot; no held-out-brand F1 has been measured yet.
- English only. "PM2" is the Spyderco Paramilitary 2 in r/knives and just letters elsewhere.
Cost
$9 of Gemini labels through OpenRouter, about $2.50 of T4 time across ten runs.
- Downloads last month
- -
Model tree for P-s-v/knife-gliner-large-v2.5
Base model
gliner-community/gliner_large-v2.5