langid-go is built to serve as a highly concurrent, zero-allocation inference engine. It executes pre-compiled model structures but delegates model training and raw compilation tasks to Python.
This document explains the technical reasons behind this architectural decision and walks you through the step-by-step workflow of training, converting, and executing custom language models.
To keep the Go runtime lightweight, secure, and free from external dependencies or floating-point precision drift, the following decisions were made:
Model training is a multi-stage statistical operation requiring corpus directory scanning, byte-level sliding window tokenization, Shannon information-gain calculations, n-gram optimization, and Aho-Corasick DFA state-machine generation. These tasks currently rely heavily on the Python scientific stack (numpy and scipy). Native Go training remains planned on the roadmap as suitable native libraries are identified.
The original models generated by langid.py are base64-encoded, bz2-compressed Python 2 pickle files. Parsing Python pickles in Go is highly insecure (vulnerable to arbitrary code execution), slow, and fragile due to language version mismatch.
Instead, custom models are compiled using the reference Python pipeline and converted into highly optimized, type-safe Go .lidg binary files.
The diagram below outlines the path a model takes from a raw training corpus to production inference in Go:
graph TD
Corpus["Raw Text Corpus<br/>/corpus/{lang}/*.txt"] -->|1. train.py<br/>Python SciPy Stack| Pickle["Legacy Pickle Model<br/>my_custom.model"]
Pickle -->|2. convert_model.py<br/>Go Binary Serialization| Lidg["Type-Safe Binary Model<br/>my_custom.lidg"]
subgraph Go Production Deployment
Lidg -->|3a. Programmatic<br/>langid.LoadModel| App["Go Microservice / Identifier"]
Lidg -->|3b. Command Line<br/>langid -m| CLI["langid CLI Tool"]
end
style Lidg fill:#2a9d8f,stroke:#264653,stroke-width:2px,color:#fff
Use the official Python training toolkit available in the original langid.py repository: langid.py/langid/train.
Organize your training corpus in directories where each subdirectory is named after its ISO language code:
/corpus
├── en/
│ ├── doc1.txt
│ └── doc2.txt
└── es/
├── doc1.txt
└── doc2.txt
Run the training pipeline:
python3 path/to/langid.py/langid/train/train.py -m /path/to/output_dir /path/to/corpusThis process generates a legacy model file (e.g., my_custom.model).
Convert the legacy model into the optimized .lidg binary file using the conversion utility provided in the langid-go repository:
python3 scripts/convert_model.py my_custom.model model/my_custom.lidg(This utility deserializes the Python data arrays, verifies structural integrity, and packs the DFA state transitions, language maps, and probability tables into a flat, type-safe binary structure).
Load and run the model within your application using the package-level LoadModel function:
package main
import (
"fmt"
"log"
"github.com/ilpy20/langid-go"
)
func main() {
// Load the custom model
id, err := langid.LoadModel("model/my_custom.lidg")
if err != nil {
log.Fatalf("failed to load custom model: %v", err)
}
// Classify a string using the custom classifier instance
res, err := id.IdentifyString("This is classified using our custom model.")
if err != nil {
log.Fatalf("failed to classify: %v", err)
}
fmt.Printf("Language: %s (Log Score: %.2f)\n", res.Language, res.Score)
}Instruct the CLI executable to use your custom model by passing its path via the -m or --model flag:
./langid -m model/my_custom.lidg <<< "This text will be classified by your custom model"