Getting started

This walkthrough trains a small acoustic model from the audio included in the repository. You do not need to write Python. Use Terminal on macOS or Linux; native Windows cannot run the training pipeline. Windows users can install a Linux environment using Microsoft’s WSL instructions and work inside its Linux terminal. WSL is not a separately tested pstrain platform. See platform support for the precise limits.

Install the current checkout

You need Git, Python 3.11–3.13, and a C/C++ compiler for this walkthrough. Python 3.12 is a suitable choice; these Python versions are covered by CI. If Python is missing, install one of these versions from the official Python downloads. If python3 --version reports an older version, use the installed version explicitly—for example, replace python3 with python3.13 in the version check and the -m venv command below. After activation, continue using python as shown.

On macOS, install Apple’s Command Line Tools with xcode-select --install if needed; see Apple’s tools page. On Debian/Ubuntu, the relevant system packages are git, build-essential, python3-dev, and python3-venv; check that your distribution’s python3 is new enough. Install missing system tools before continuing.

Copy these commands into your terminal, one line at a time:

python3 --version
git clone https://github.com/lenzo-ka/pstrain.git
cd pstrain
python3 -m venv .venv
source .venv/bin/activate
python -m pip install ".[test]"
pstrain info

The .venv directory keeps pstrain’s Python dependencies separate from your other software. Activation makes python and pstrain refer to that environment; see Python’s virtual-environment guide. The install command builds the bundled native code and installs the evaluation metrics extra (test). It obtains CMake 3.25 or newer and other Python build requirements automatically; you do not need a separate cmake --build step. The first installation needs internet access for dependencies and the pinned PocketSphinx source and can take several minutes.

In pstrain info, check that C library: available appears. If compilation fails, check the compiler prerequisite and the first error in pip’s output. If pstrain is not found, activate .venv again. In a new terminal, return to this checkout and run source .venv/bin/activate. Run deactivate when finished.

This installs the checked-out source, not the published PyPI release. After updating the checkout, repeat python -m pip install ".[test]" to rebuild it. For development and editable installs, see the development guide.

Train a small model

Stay in the checkout directory, with the environment active. The following uses the bundled mini Arctic corpus, so it needs no corpus download. Choose a new output directory; demo below must not already contain another project.

pstrain train demo \
  --audio tests/fixtures/mini_arctic/wav \
  --prompts tests/fixtures/mini_arctic/transcription.txt \
  --dictionary tests/fixtures/mini_arctic/dictionary.dict \
  --phoneset tests/fixtures/mini_arctic/phoneset.txt \
  --filler-dict tests/fixtures/mini_arctic/filler.dict \
  --target ci-1g -j 1

The backslash continues a command onto the next line; keep it as the last character on each continued line. ci-1g requests a small context-independent model with one Gaussian per state, and -j 1 uses one training worker. Success ends with Status: trained and prints the model directory: demo/shared/models/ci-1g/default. A pass summary may say converged=False: that means the configured iteration limit was reached, not that convergence was demonstrated. This tiny run checks the workflow, not recognition quality.

Decode its held-out utterance and package the model:

pstrain test ci-1g --project-dir demo
pstrain package ci-1g --project-dir demo

The test command should report 1/1 decoded and prints WER (word error rate); a high value is expected for this tiny corpus and model, and insertions can make WER exceed 100%. It builds a language model from the training transcript. These results are not the Arctic benchmark. The package command prints its output files under demo/packages/ci-1g/, including the acoustic model, dictionaries, and pstrain-package.json manifest.

To continue through the larger default model, reuse the prepared project:

pstrain build cd-8g --project-dir demo -j 1

Then use cd-8g in the test and package commands above. For your own corpus, replace the input paths in pstrain train and use a new project directory. Read input formats first: utterance IDs must match the audio paths, and prompt words must match the pronunciation dictionary. Run pstrain train --help for options, including explicit resume behavior.

Install a published release instead

If you already have your own corpus and want a release rather than this checkout, create a separate environment in a new directory:

mkdir pstrain-work
cd pstrain-work
python3 -m venv .venv
source .venv/bin/activate
python -m pip install "pstrain[test]"
pstrain info
pstrain train --help

A compatible wheel includes the native library; when no wheel matches your Python/platform, pip builds from source and needs the compiler prerequisites above. The repository’s mini corpus is not part of the installed package. Release commands and fixes can lag this checkout: consult the installed --help and the release’s documentation rather than assuming equal behavior from the version number alone.

For a longer guided notebook, pstrain tutorial copies the bundled tutorial into the current directory. Opening it requires a Jupyter installation; its setup cells describe additional dependencies and corpus downloads. It is optional and separate from the terminal walkthrough above.

Further workflows

The lower-level setup, validate-project, split, features, and flat commands are available for decomposed workflows. Each accepts --help.

Packages created before the pstrain-package.json marker was introduced are recognizable as legacy packages, but replacement now requires explicit consent. The training pipeline deliberately does not set that consent automatically: doing so would make a legacy pipeline directory indistinguishable from a hand-assembled decoder package with the same structure. Before rerunning a package target against pre-marker output, move dist/models/<model>-<profile> aside so it remains recoverable. Alternatively, after inspecting the directory, replace it explicitly with the equivalent package command:

pstrain package cd-8g --project-dir my_project \
    --out my_project/dist/models \
    --name cd-8g-default \
    --overwrite

Subsequent pipeline runs recognize the marker written by that command and do not require another opt-in.

Supplying an existing train/test split

To preserve a corpus’s canonical partition or your own held-out set, create all four Sphinx-format files before running pstrain split or pstrain build:

my_project/experiments/default/etc/train.fileids
my_project/experiments/default/etc/test.fileids
my_project/experiments/default/etc/train.transcription
my_project/experiments/default/etc/test.transcription

Each .fileids file contains one utterance ID per line. Its matching .transcription uses one of the accepted transcript forms and contains the same IDs in exactly the same order, with the transcript text from etc/all.transcription. Together, train and test must partition all.transcription exactly, may not overlap, and every ID must have a matching audio/<fileid>.wav (nested file IDs are supported).

When all four files are supplied, they are authoritative: pstrain validates but does not rewrite or reorder them. Any mismatch, omission, overlap, transcript change, or missing audio is an error. If the files are absent, the existing automatic 95/5 seeded split remains the default.

Python API

from pstrain.api import setup_project, validate_project, create_model
from pathlib import Path

# Set up a new project
result = setup_project(
    project_dir=Path("my_project"),
    transcription_path=Path("transcripts.txt"),
    dictionary_path=Path("dictionary.dict"),
)

# Validate the project
report = validate_project(Path("my_project"))
if not report.is_valid:
    print(report.summary())

# Create a model
model = create_model("ci", config="baseline")
print(f"Model: {model.display_name}")
print(f"Default topn: {model.default_topn}")