Skip to main content

An effective text normalization tool for Vietnamese

Project description

Soe Vinorm - Vietnamese Text Normalization Toolkit

Soe Vinorm is an effective Vietnamese text normalization toolkit designed for use in Text-to-Speech (TTS) and NLP pipelines. It detects and expands non-standard words (NSWs) such as numbers, dates, abbreviations, etc., converting them into their spoken forms. This project is based on the paper Non-Standard Vietnamese Word Detection and Normalization for Text-to-Speech.

Installation

Option 1: Clone the repository (for development)

# Clone the repository
git clone https://github.com/vinhdq842/soe-vinorm.git
cd soe-vinorm

# Install dependencies including development dependencies (using uv)
uv sync --dev

Option 2: Install from PyPI

# Install using uv
uv add soe-vinorm

# Or using pip
pip install soe-vinorm

Option 3: Install from source

# Install directly from GitHub
uv pip install git+https://github.com/vinhdq842/soe-vinorm.git

Usage

Command Line Interface

After installation, you can use the soe-vinorm command directly from the terminal:

# Normalize text from a file
soe-vinorm -i input.txt -o output.txt

# Process with progress bar
soe-vinorm -i input.txt -o output.txt --show-progress

# Use parallel processing (4 workers)
soe-vinorm -i input.txt -o output.txt --n-jobs 4 --show-progress

# Normalization options
soe-vinorm -i input.txt --no-expand-sequence --no-expand-urle

# Read from stdin and write to stdout
echo "Năm 2021" | soe-vinorm

# Show help
soe-vinorm --help

Python API

Basic usage

from soe_vinorm import SoeNormalizer

normalizer = SoeNormalizer()
text = "Từ năm 2021 đến nay, đây là lần thứ 3 Bộ Công an xây dựng thông tư để quy định liên quan đến mẫu hộ chiếu, giấy thông hành."

# Single
result = normalizer.normalize(text)
print(result)
# Output: Từ năm hai nghìn không trăm hai mươi mốt đến nay , đây là lần thứ ba Bộ Công an xây dựng thông tư để quy định liên quan đến mẫu hộ chiếu , giấy thông hành .

# Batch
results = normalizer.batch_normalize([text] * 5, n_jobs=5)
print(results)

Quick function usage

from soe_vinorm import normalize_text

text = "1kg dâu 25 quả, giá 700.000 - Trung bình 30.000đ/quả"
result = normalize_text(text)
print(result)
# Output: một ki lô gam dâu hai mươi lăm quả , giá bảy trăm nghìn - Trung bình ba mươi nghìn đồng trên quả
from soe_vinorm import batch_normalize_texts

texts = [
    "Công trình cao 7,9 m bằng chất liệu đồng nguyên chất với trọng lượng gần 7 tấn, bệ tượng cao 3,6 m",
    "Một trường ĐH tại TP.HCM ghi nhận số nguyện vọng đăng ký vào trường tăng kỷ lục, với trên 178.000.",
    "Theo phương án của Ban Quản lý dự án đường sắt, Bộ Xây dựng, tuyến đường sắt đô thị Thủ Thiêm - Long Thành có chiều dài khoảng 42 km, thiết kế đường đôi, khổ đường 1.435 mm, tốc độ thiết kế 120 km/giờ.",
    "iPhone 16 Pro hiện có giá 999 USD cho phiên bản bộ nhớ 128 GB, và 1.099 USD cho bản 256 GB. Trong khi đó, mẫu 16 Pro Max có dung lượng khởi điểm 256 GB với giá 1.199 USD.",
]

# Process multiple texts in parallel (4 worker processes)
results = batch_normalize_texts(texts, n_jobs=4)

for original, normalized in zip(texts, results):
    print(f"Original: {original}")
    print(f"Normalized: {normalized}")
    print("-" * 50)

Output:

Original: Công trình cao 7,9 m bằng chất liệu đồng nguyên chất với trọng lượng gần 7 tấn, bệ tượng cao 3,6 m
Normalized: Công trình cao bảy phẩy chín mét bằng chất liệu đồng nguyên chất với trọng lượng gần bảy tấn , bệ tượng cao ba phẩy sáu mét
--------------------------------------------------
Original: Một trường ĐH tại TP.HCM ghi nhận số nguyện vọng đăng ký vào trường tăng kỷ lục, với trên 178.000.
Normalized: Một trường Đại học tại Thành phố Hồ Chí Minh ghi nhận số nguyện vọng đăng ký vào trường tăng kỷ lục , với trên một trăm bảy mươi tám nghìn .
--------------------------------------------------
Original: Theo phương án của Ban Quản lý dự án đường sắt, Bộ Xây dựng, tuyến đường sắt đô thị Thủ Thiêm - Long Thành có chiều dài khoảng 42 km, thiết kế đường đôi, khổ đường 1.435 mm, tốc độ thiết kế 120 km/giờ.
Normalized: Theo phương án của Ban Quản lý dự án đường sắt , Bộ Xây dựng , tuyến đường sắt đô thị Thủ Thiêm - Long Thành có chiều dài khoảng bốn mươi hai ki lô mét , thiết kế đường đôi , khổ đường một nghìn bốn trăm ba mươi lăm mi li mét , tốc độ thiết kế một trăm hai mươi ki lô mét trên giờ .
--------------------------------------------------
Original: iPhone 16 Pro hiện có giá 999 USD cho phiên bản bộ nhớ 128 GB, và 1.099 USD cho bản 256 GB. Trong khi đó, mẫu 16 Pro Max có dung lượng khởi điểm 256 GB với giá 1.199 USD.
Normalized: iPhone mười sáu Pro hiện có giá chín trăm chín mươi chín U Ét Đê cho phiên bản bộ nhớ một trăm hai mươi tám ghi ga bai , và một nghìn không trăm chín mươi chín U Ét Đê cho bản hai trăm năm mươi sáu ghi ga bai . Trong khi đó , mẫu mười sáu Pro Max có dung lượng khởi điểm hai trăm năm mươi sáu ghi ga bai với giá một nghìn một trăm chín mươi chín U Ét Đê .
--------------------------------------------------

Normalization options (v0.3)

from soe_vinorm import SoeNormalizer, batch_normalize_texts, normalize_text

# By default, expand_sequence and expand_urle are True
normalizer = SoeNormalizer(expand_sequence=False, expand_urle=False)
text = "iPhone 16 Pro hiện có giá 999 USD cho phiên bản bộ nhớ 128 GB. Liên hệ example@example.com để mua."

# Single
result = normalizer.normalize(text)
print(result)
# Output: iPhone mười sáu Pro hiện có giá chín trăm chín mươi chín USD cho phiên bản bộ nhớ một trăm hai mươi tám ghi ga bai . Liên hệ example@example.com để mua .

result = normalize_text(text, expand_sequence=True, expand_urle=False)
print(result)
# Output: iPhone mười sáu Pro hiện có giá chín trăm chín mươi chín U Ét Đê cho phiên bản bộ nhớ một trăm hai mươi tám ghi ga bai . Liên hệ example@example.com để mua .


# Batch
texts = [text] * 5

results = normalizer.batch_normalize(texts, n_jobs=2, show_progress=True)
print(results)
# Output: ['iPhone mười sáu Pro hiện có giá chín trăm chín mươi chín USD cho phiên bản bộ nhớ một trăm hai mươi tám ghi ga bai . Liên hệ example@example.com để mua .', ...]

results = batch_normalize_texts(texts, n_jobs=2, expand_sequence=False, expand_urle=True)
print(results)
# Output: ['iPhone mười sáu Pro hiện có giá chín trăm chín mươi chín USD cho phiên bản bộ nhớ một trăm hai mươi tám ghi ga bai . Liên hệ i xam pi le a còng i xam pi le chấm com để mua .', ...]

Load from pre-downloaded weights (v0.3)

!git lfs install
!git clone https://huggingface.co/vinhdq842/soe-vinorm model-repo
from soe_vinorm import SoeNormalizer

normalizer = SoeNormalizer(model_path="model-repo")

Approach: Two-stage normalization

Preprocessing & tokenizing

  • Extra spaces, ASCII art, emojis, HTML entities, unspoken words, etc., are removed.
  • A regex-based tokenizer is then used to split the sentence into tokens.

Stage 1: Non-standard word detection

  • A sequence tagger is used to extract non-standard words (NSWs) and categorize them into 18 different types.
  • These NSWs can later be verbalized properly according to their types.
  • The sequence tagger can be any sequence labeling model; this implementation uses a Conditional Random Field due to limited data.

Stage 2: Non-standard word normalization

  • With the NSWs detected in Stage 1 and their respective types, regex-based expanders are applied to produce normalized results.
  • Each NSW type has its own dedicated expander.
  • The normalized results are then inserted back into the original sentence, resulting in the desired normalized sentence.

Other details

  • Foreign NSWs are currently kept as-is.
  • Abbreviation expansion model
    • v0.1: Quantized PhoBERT model combined with a Vietnamese abbreviation dictionary.
    • v0.2: A small neural network, also incorporating a Vietnamese abbreviation dictionary.

Limitations

Due to the inherent characteristics of ML models, i.e., their tendency to learn data-specific quirks (in this case, from newspaper articles), this library can make basic errors on spontaneous text (e.g., arbitrarily composed text without context).

Testing

Run all tests with:

pytest tests

Author

License

MIT License

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

soe_vinorm-0.3.2.tar.gz (196.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

soe_vinorm-0.3.2-py3-none-any.whl (91.1 kB view details)

Uploaded Python 3

File details

Details for the file soe_vinorm-0.3.2.tar.gz.

File metadata

  • Download URL: soe_vinorm-0.3.2.tar.gz
  • Upload date:
  • Size: 196.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.7.19

File hashes

Hashes for soe_vinorm-0.3.2.tar.gz
Algorithm Hash digest
SHA256 f44b93eff1b6c4d94740545799c2fad7f6a6cc6e690916384136510514c91af5
MD5 1eb6480ad81a1e311b801ab8dbd3fb2f
BLAKE2b-256 d34041a92cea819ee3c3ed50838ed865350f802572236b885127bd256c1ce712

See more details on using hashes here.

File details

Details for the file soe_vinorm-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: soe_vinorm-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 91.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.7.19

File hashes

Hashes for soe_vinorm-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 943fc3f598f10e81836ad31d5aa35a8875b31678249b4e7a4a7f8efba788b056
MD5 33dd33fcb040f5a7cab782fc55411347
BLAKE2b-256 fce0940b39b4c0a2b7be11b0c14461848c38d38a6bfd0c1e9aeff10842b2cce7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page