Text Classification
Transformers
ONNX
Safetensors
Transformers.js
modernbert
language-identification
language-detection
lid
multilingual
mmbert
text-embeddings-inference
Instructions to use Horizon-Labs/language-detection-small with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Horizon-Labs/language-detection-small with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="Horizon-Labs/language-detection-small")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForSequenceClassification tokenizer = AutoTokenizer.from_pretrained("Horizon-Labs/language-detection-small") model = AutoModelForSequenceClassification.from_pretrained("Horizon-Labs/language-detection-small", device_map="auto") - Transformers.js
How to use Horizon-Labs/language-detection-small with Transformers.js:
// npm i @huggingface/transformers import { pipeline } from '@huggingface/transformers'; // Allocate pipeline const pipe = await pipeline('text-classification', 'Horizon-Labs/language-detection-small'); - Notebooks
- Google Colab
- Kaggle
Download code/lid/augment_short.py from Horizon-Labs/language-detection-small: direct link, hf CLI and curl.
- Browser
- Download file 2.59 kB
-
https://huggingface.co/Horizon-Labs/language-detection-small/resolve/main/code/lid/augment_short.py
- Command line
-
hf download hf://Horizon-Labs/language-detection-small/code/lid/augment_short.py
-
curl -L -o augment_short.py https://huggingface.co/Horizon-Labs/language-detection-small/resolve/main/code/lid/augment_short.py
2.59 kB
| """Language-ID data v3 = v2 (cleaned) + one extra 10-40 character span from every long training sample. | |
| python lid/augment_short.py V2_DIR OUT | |
| Why: v2 matched GlotLID on 20-40 character prefixes in accuracy (.815 vs .810) but trailed in macro-F1 (.808 vs .819); | |
| short strings (chat messages, queries, titles) are a main use case. Spans are cut at word boundaries for space-separated | |
| scripts; val and eval are unchanged. | |
| v4 (env FILTER_SPANS=1, default): the new spans and the existing samples pass the same script and English filters as | |
| lid/clean_lid.py - in v3, English fragments cut out of code-switched long samples (e.g. Tagalog, Xhosa) were added under | |
| those labels, and short English phrases got low confidence ("Thank you very much for your help" -> eng 0.42). | |
| """ | |
| import os, random, shutil, sys | |
| import pandas as pd | |
| V2, OUT = sys.argv[1:3] | |
| os.makedirs(OUT, exist_ok=True) | |
| rng = random.Random(3) | |
| tr = pd.read_parquet(f"{V2}/train.parquet") | |
| def span(t): | |
| L = rng.randint(10, 40) | |
| if len(t) <= L: | |
| return None | |
| st = rng.randrange(0, len(t) - L + 1); s = t[st:st + L] | |
| if " " in s.strip(): | |
| s = s[s.find(" ") + 1:] if st > 0 else s | |
| s = s[:s.rfind(" ")] if s.rfind(" ") > 4 else s | |
| s = s.strip() | |
| return s if len(s) >= 5 else None | |
| FILTER = os.environ.get("FILTER_SPANS", "1") == "1" | |
| if FILTER: | |
| src = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "clean_lid.py")).read() | |
| ns = {"os": os}; exec(src[src.index("import re, unicodedata"): src.index("ev = pd.read_parquet")], ns) | |
| ok = lambda s, l: not (l.split("_")[-1] != "Latn" and ns["script_frac"](s, l.split("_")[-1]) < 0.5) and \ | |
| not (l.split("_")[-1] == "Latn" and l != "eng_Latn" and ns["english_like"](s)) | |
| before = len(tr) | |
| tr = tr[[ok(t, l) for t, l in zip(tr.text, tr.label)]] # also re-check the existing short spans | |
| print("existing samples removed by filters", before - len(tr), flush=True) | |
| longs = tr[tr.kind == "long"] | |
| extra = pd.DataFrame([(s, l, "short") for s, l in ((span(t), l) for t, l in zip(longs.text, longs.label)) if s and (not FILTER or ok(s, l))], | |
| columns=["text", "label", "kind"]) | |
| out = pd.concat([tr, extra]).drop_duplicates(subset=["text"]).sample(frac=1.0, random_state=0).reset_index(drop=True) | |
| out.to_parquet(f"{OUT}/train.parquet") | |
| for f in ["val.parquet", "labels.json"]: | |
| shutil.copy(f"{V2}/{f}", f"{OUT}/{f}") | |
| shutil.copytree(f"{V2}/eval", f"{OUT}/eval", dirs_exist_ok=True) | |
| print("train", len(tr), "->", len(out), "| short share", round((out.kind == "short").mean(), 3), flush=True) | |