KWJA Japanese analyzer on Apple Silicon MLX — full 14-head port with byte-reproducible weight converter
Python
0
35 commits
updated Oct 13, 2026
Complete MLX port of KWJA (Kyoto-Waseda Japanese Analyzer).
All three modules running on Metal GPU with full accuracy.
sentences(), sent_seg B/I)w·GELU(Ws·hi+Wt·hj), topk-4 + cycle/single-root decode, types D/P/A/I (parent_index = base-phrase index, -1 = root)m.cohesion)result.discourse)analyze_document(texts): lexical per sentence, merged-window cross-sentence cohesionfrom kwja_mlx import Analyzer
analyzer = Analyzer(quantize="q8") # q8 recommended: fast + accurate
# Quick mode (segmentation + POS + NER, no reading)
morphemes = analyzer.tokenize("東京都庁に行きました")
for m in morphemes:
print(f"{m.surface}/{m.pos}")
# 東京/名詞 都庁/名詞 に/助詞 行き/動詞 ました/接尾辞
# Full mode (+ reading/lemma/canon via T5, + dependency/cohesion)
result = analyzer.analyze("東京都庁に行きました")
for m in result.morphemes:
print(f"{m.surface}/{m.pos} [{m.reading}] parent={m.parent_index}/{m.dep_type} {m.cohesion}")
# 東京/名詞 [とうきょう] 都庁/名詞 [とちょう] に/助詞 [に] 行き/動詞 [いき] ました/接尾辞 [ました]
# Document mode (cross-sentence coreference)
results = analyzer.analyze_document(["田中さんが来た。", "彼は笑った。"])
# 笑った ガ→彼 (flat cross-sentence index)
# Sentence split
analyzer.sentences("こんにちは。さようなら。") # → [["こ","ん","に","ち","は","。"], [...]]
Tested on Apple Silicon (M-series Mac).
| Variant | Speed (sent/sec) | Segmentation F1 | POS Accuracy |
|---|---|---|---|
| FP32 | 78 | 1.000 | baseline |
| FP16 | ~150 | 1.000 | 1.000 |
| Q8 | ~170 | 1.000 | 1.000 |
| Q4 | ~180 | 0.997 | 1.000 |
| Variant | Speed (sent/sec) | Output Match |
|---|---|---|
| FP32 | 5.9 | baseline |
| Q8 | ~8 | ✓ identical |
| Q4 | ~9 | ✓ identical |
| Backend | Quick Mode | Full Mode |
|---|---|---|
| PyTorch CPU | ~10 sent/sec | ~3 sent/sec |
| PyTorch MPS | ~30 sent/sec | ~8 sent/sec |
| MLX FP32 | 78 sent/sec | 5.9 sent/sec |
| MLX Q8 | ~170 sent/sec | ~8 sent/sec |
Input text
↓
[char module] DeBERTa-v2-base-japanese-char-wwm (22K vocab)
→ Word segmentation (per-char B/I tagging)
↓
[word module] DeBERTa-v2-base-japanese (32K vocab)
→ POS tagging + NER (first-subword pooling → classification)
↓
[seq2seq module] T5-base (32K vocab, encoder-decoder)
→ Reading, lemma, canonical form (autoregressive generation)
↓
Output: Morpheme(surface, pos, ne, reading, lemma, canon)
mlx >= 0.20
numpy
safetensors
transformers
huggingface_hub
sentencepiece
Weights from kevinzhow/kwja-mlx (auto-downloaded on first use).
Based on KWJA v2.4 checkpoints from Kyoto University:
ku-nlp/deberta-v2-base-japanese-char-wwmku-nlp/deberta-v2-base-japaneseretrieva-jp/t5-base-longscripts/convert_kwja_to_mlx.py rebuilds the .mlx.safetensors artifacts
from the official Kyoto .ckpt files — verified byte-identical to
kevinzhow/kwja-mlx (char 214/214, word 264/264, seq2seq 282/282 tensors,
2026-10-13):
python scripts/convert_kwja_to_mlx.py --model-size base --module all --out ./converted
Needs torch + safetensors (CPU is fine). Conversion is key-rename only,
plus one transpose: char conv (O,I,K) → (O,K,I).
scripts/convert_kwja_to_mlx.py.openmed.mlx.convert.@InProceedings{Ueda2023a,
author = {Nobuhiro Ueda and Kazumasa Omura and Takashi Kodama and Hirokazu Kiyomaru and Yugo Murawaki and Daisuke Kawahara and Sadao Kurohashi},
title = {KWJA: A Unified Japanese Analyzer Based on Foundation Models},
booktitle = {Proceedings of the 61st Annual Meeting of the Association for Computational Linguistics: System Demonstrations},
year = {2023},
address = {Toronto, Canada},
}
MIT (same as KWJA)
KWJA Japanese analyzer on Apple Silicon MLX — full 14-head port with byte-reproducible weight converter
Python
0
35 commits
updated Oct 13, 2026
Complete MLX port of KWJA (Kyoto-Waseda Japanese Analyzer).
All three modules running on Metal GPU with full accuracy.
sentences(), sent_seg B/I)w·GELU(Ws·hi+Wt·hj), topk-4 + cycle/single-root decode, types D/P/A/I (parent_index = base-phrase index, -1 = root)m.cohesion)result.discourse)analyze_document(texts): lexical per sentence, merged-window cross-sentence cohesionfrom kwja_mlx import Analyzer
analyzer = Analyzer(quantize="q8") # q8 recommended: fast + accurate
# Quick mode (segmentation + POS + NER, no reading)
morphemes = analyzer.tokenize("東京都庁に行きました")
for m in morphemes:
print(f"{m.surface}/{m.pos}")
# 東京/名詞 都庁/名詞 に/助詞 行き/動詞 ました/接尾辞
# Full mode (+ reading/lemma/canon via T5, + dependency/cohesion)
result = analyzer.analyze("東京都庁に行きました")
for m in result.morphemes:
print(f"{m.surface}/{m.pos} [{m.reading}] parent={m.parent_index}/{m.dep_type} {m.cohesion}")
# 東京/名詞 [とうきょう] 都庁/名詞 [とちょう] に/助詞 [に] 行き/動詞 [いき] ました/接尾辞 [ました]
# Document mode (cross-sentence coreference)
results = analyzer.analyze_document(["田中さんが来た。", "彼は笑った。"])
# 笑った ガ→彼 (flat cross-sentence index)
# Sentence split
analyzer.sentences("こんにちは。さようなら。") # → [["こ","ん","に","ち","は","。"], [...]]
Tested on Apple Silicon (M-series Mac).
| Variant | Speed (sent/sec) | Segmentation F1 | POS Accuracy |
|---|---|---|---|
| FP32 | 78 | 1.000 | baseline |
| FP16 | ~150 | 1.000 | 1.000 |
| Q8 | ~170 | 1.000 | 1.000 |
| Q4 | ~180 | 0.997 | 1.000 |
| Variant | Speed (sent/sec) | Output Match |
|---|---|---|
| FP32 | 5.9 | baseline |
| Q8 | ~8 | ✓ identical |
| Q4 | ~9 | ✓ identical |
| Backend | Quick Mode | Full Mode |
|---|---|---|
| PyTorch CPU | ~10 sent/sec | ~3 sent/sec |
| PyTorch MPS | ~30 sent/sec | ~8 sent/sec |
| MLX FP32 | 78 sent/sec | 5.9 sent/sec |
| MLX Q8 | ~170 sent/sec | ~8 sent/sec |
Input text
↓
[char module] DeBERTa-v2-base-japanese-char-wwm (22K vocab)
→ Word segmentation (per-char B/I tagging)
↓
[word module] DeBERTa-v2-base-japanese (32K vocab)
→ POS tagging + NER (first-subword pooling → classification)
↓
[seq2seq module] T5-base (32K vocab, encoder-decoder)
→ Reading, lemma, canonical form (autoregressive generation)
↓
Output: Morpheme(surface, pos, ne, reading, lemma, canon)
mlx >= 0.20
numpy
safetensors
transformers
huggingface_hub
sentencepiece
Weights from kevinzhow/kwja-mlx (auto-downloaded on first use).
Based on KWJA v2.4 checkpoints from Kyoto University:
ku-nlp/deberta-v2-base-japanese-char-wwmku-nlp/deberta-v2-base-japaneseretrieva-jp/t5-base-longscripts/convert_kwja_to_mlx.py rebuilds the .mlx.safetensors artifacts
from the official Kyoto .ckpt files — verified byte-identical to
kevinzhow/kwja-mlx (char 214/214, word 264/264, seq2seq 282/282 tensors,
2026-10-13):
python scripts/convert_kwja_to_mlx.py --model-size base --module all --out ./converted
Needs torch + safetensors (CPU is fine). Conversion is key-rename only,
plus one transpose: char conv (O,I,K) → (O,K,I).
scripts/convert_kwja_to_mlx.py.openmed.mlx.convert.@InProceedings{Ueda2023a,
author = {Nobuhiro Ueda and Kazumasa Omura and Takashi Kodama and Hirokazu Kiyomaru and Yugo Murawaki and Daisuke Kawahara and Sadao Kurohashi},
title = {KWJA: A Unified Japanese Analyzer Based on Foundation Models},
booktitle = {Proceedings of the 61st Annual Meeting of the Association for Computational Linguistics: System Demonstrations},
year = {2023},
address = {Toronto, Canada},
}
MIT (same as KWJA)