Skip to content

paksopi/Language-Detection-Analysis

Repository files navigation

Language Detection Engine Evaluation

tests License: MIT Python 3.12

A proof-of-concept comparing language-detection engines — lingua, langdetect, pycld2, fasttext, and langid — on short-text EN / MY / ID (English / Malay / Indonesian) classification, and combining them into a weighted voting ensemble.

Naming: EN/MY/ID are the language labels used in prose and table headers below; en/ms/id are their BCP-47 ISO codes, used in code font when referring to a literal model output value.

Dataset

The main evaluation dataset is data/test_case_7_enmyid.txt — 1,273 English/Malay/Indonesian cases in LANG | text format, filtered from the 5-language test_case_7.txt (2,036 cases, which also includes Chinese and Tamil). Cases are organized into five word-count buckets so accuracy and speed can be measured per bucket per language, not just overall:

Bucket n Description
1 word 677 50% shared/formal roots (stress-tests EN/MY/ID ambiguity), 50% exclusive/colloquial (tests distinct dialect routing)
2 words 399 Same 50/50 shared-root / colloquial split as bucket 1
3–7 words 94 Authentic Bahasa Rojak, code-switching patterns
8–16 words 73 Localized slang, multi-word educational phrases
17–50 words 30 Full sentences, educational content, paragraphs

Earlier, smaller test cases (test_case_1.txt through test_case_6.txt) are kept in data/ for history — see the archived reports in reports/archive/ for the runs that used them. Labels are scored as exact-match BCP 47 codes (en, ms, id).

Results

Evaluated on 1,273 EN/MY/ID short-text cases (test_case_7_enmyid.txt). Current production baseline (langdetect alone) scores 29.1% overall accuracy and cannot detect Malay at all (0.0% MY). The best-ALL-accuracy config, S2 Weighted Voting (lingua-high + openlid-v3

  • pycld2), reaches 70.8% overall accuracy — +41.7 pp over production — while running 8.6–115.5× faster per call. That 70.8% figure comes with a trade-off worth reading before you pick a config: S2 Weighted's own Malay (MY) accuracy is 43.7%, below both lingua-high running alone (55.8%) and the S1 Two-Stage Weighted alternative (56.5%). Choose S2 Weighted if ID accuracy/latency/simplicity matter more than MY protection; choose S1 Two-Stage Weighted if MY protection matters more (only 0.5 pp behind on ALL). Full decision matrix in §9 of the report.
Strategy Ensemble EN MY ID ALL
langdetect (current production) 42.8% 0.0% 44.3% 29.1%
lingua-high (individual) 92.1% 55.8% 57.9% 68.8%
S1 two_stage_weighted ld+li+py (2-stage) 92.4% 56.5% 61.4% 70.3%
S2 weighted (best ALL/ID; MY trade-off, see above) ol+li+py 92.1% 43.7% 76.0% 70.8%

The 70.8%/56.5% figures above also depend on this dataset's bucket composition (53% single-word cases) — see src/benchmark/reweight_by_real_distribution.py and §12b of the report for what these numbers become under more realistic query-length distributions.

ROC curves for all seven models

pycld2 is the most discriminative single model by AUC (0.97), but its raw accuracy alone is low (§1 of the report) — it needs the other voters to convert that discriminative power into correct calls. Full ROC methodology in §1.5 of the report.

Speed & complexity (Scenario 1 vs. Scenario 2)

Scenario 2 (recommended) wins on steady-state latency but costs more to deploy:

Dimension S1 (lingua+langdetect+pycld2) S2 (lingua+openlid-v3+pycld2)
Per-request latency (1-word text) 5.5216 ms 0.0478 ms (115.5× faster)
Per-request latency (17–50 words) 2.1027 ms 0.2435 ms (8.6× faster)
Model artifact size ~2.3 MB 1.2 GB (openlid-v3.bin)
Cold-start load time ~0.242 s ~1.155 s (~4.8× slower)
Routing logic needed Two-stage voting required to protect MY accuracy Single-stage vote, no routing

Since detection runs synchronously before any downstream NLP stage, per-request latency dominates in production; the 1.2 GB artifact and slower cold start are one-time costs paid at deploy/restart, not per request. Full breakdown in §10 of the report.

Layout

All code lives under src/, grouped by purpose:

Folder Contents
src/voting/ Core voting-ensemble logic and evaluation scripts (accuracy, kappa, AUC, reproducibility, two-stage voting) for Scenario 1 (lingua-high+langdetect+pycld2)
src/voting/strategies/ Scenario 2 (lingua-high+openlid-v3+pycld2) voting logic, organized by technique: hard.py, soft.py, weighted.py, two_stage.py, plus the run_s2_*.py report-generation scripts. Formerly src/voting/scenario2/ — see the module docstring in src/voting/strategies/__init__.py for the old-name → new-name mapping used by the report's "S1"/"S2" terminology
src/benchmark/ Single-engine benchmark script producing confusion matrices and ROC curves; also reweight_by_real_distribution.py, which recomputes reported accuracy under a different query-length distribution than the test set's own bucket mix
src/examples/ Earlier standalone demo/comparison scripts (lingua vs. langdetect, iterative versions) — kept for history, not part of the main pipeline
models/ Pretrained language-ID model binaries (lid.176.ftz, openlid-v3.bin)
data/ Test-case text files used as evaluation input
results/ Generated artifacts: logs/, logs_scenario2/ (Scenario 2 output; directory name predates the strategies/ rename and is left as-is since it's just generated data), calibration/, confusion_matrix/, roc_curve/ — all reproducible by re-running the scripts above
reports/ Written analysis of results — see reports/language_detection_ensemble_evaluation.md for the current canonical report. Superseded drafts/earlier runs live in reports/archive/.
tests/ Pytest suite: unit tests for the pure, model-free functions in src/voting/core.py, plus one end-to-end test that runs a small fixture dataset through the full S1 and S2 pipelines — see Tests below for exactly what the CI badge does and doesn't cover

Setup

Requires Python 3.12pycld2, langid, and statsmodels don't yet have prebuilt wheels for newer Python versions. requirements.txt installs fasttext-wheel rather than the official fasttext package, since fasttext has no prebuilt Windows wheel and fails to compile from source on newer MSVC/pybind11 toolchains — fasttext-wheel is a drop-in, prebuilt-wheel fork; the import name is still import fasttext.

python3.12 -m venv .venv
.venv\Scripts\activate      # Windows
pip install -r requirements.txt
pip install -e .             # registers voting/benchmark as importable packages

models/openlid-v3.bin (1.2 GB) is excluded from this repo via .gitignore — it's too large for a normal git push. Download it separately from Hugging Face and place it at models/openlid-v3.bin before running any openlid-v3 scripts.

Usage

Run the main benchmark:

python src/benchmark/benchmark.py [path/to/test_case.txt]

Run the voting ensemble evaluation:

python src/voting/voting_main.py

Scripts write logs/plots into results/, keyed by test case, and default to data/test_case_7_enmyid.txt if no input file is given.

Tests

python -m pytest tests/

What the tests CI badge actually validates (.github/workflows/tests.yml runs this same command on Ubuntu, Python 3.12, on every push/PR to main):

Coverage What it checks What it does NOT check
tests/test_core.py Pure, model-free functions in src/voting/core.py — bucketing, vote-counting arithmetic, path numbering, accuracy aggregation Never loads a real model; doesn't touch the actual voting/scoring pipeline
tests/test_pipeline_e2e.py Runs tests/fixtures/e2e_fixture.txt (18 hand-labeled cases) through the real S1 pipeline (lingua-high+langdetect+pycld2) and, when models/openlid-v3.bin is present, the real S2 pipeline (lingua-high+openlid-v3+pycld2); asserts accuracy stays within an expected range Not a re-validation of the report's 1,273-case statistical findings — it's a regression guard against pipeline-wiring bugs (broken imports, wrong argument order, a vote function silently returning "unknown"). The S2 half is skipped in CI, because models/openlid-v3.bin (1.2 GB) is excluded via .gitignore and not fetched by the workflow — CI only ever exercises the S1 half of this file

In short: passing CI means the vote-counting logic and the S1 pipeline wiring are intact. It does not mean the 70.8%/56.5%/etc. accuracy figures in the report have been re-verified — those come from the one-off scripts in src/voting/ and src/voting/strategies/ run manually against the full dataset, not from anything CI runs automatically.

Model attribution

models/lid.176.ftz is Facebook/Meta's pretrained fastText language-identification model, redistributed here unmodified. It is licensed under CC BY-SA 3.0, trained on data from Wikipedia, Tatoeba, and SETimes. See fastText's language-identification docs for details. models/openlid-v3.bin is excluded from this repo via .gitignore (see Setup) and is not redistributed here.

References

Language detection libraries evaluated

Evaluation methods and metrics

About

a language-detection engine benchmark and weighted voting ensemble (Lingua, OpenLID, pycld2, fastText, langdetect) optimized for short-text English, Malay, and Indonesian (EN/MS/ID) classification.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages