将越南语地址字符串规范化到 2025 年行政区划标准形式。
基于 Transformer seq2seq 模型 + 省份约束 Beam Search,候选库含 18.7 万条规范地址。
本仓库为 Ascend910 NPU 适配版:
inference.py已迁移到torch_npu后端,并自带 FastAPI 服务化接口。 由maggie_Ha完成昇腾适配。

| 组件 | 版本 (实测) |
|---|---|
| Python | 3.11.14 |
| torch | 2.9.0 |
| torch-npu | 2.9.0.post1 |
| CANN | 8.5.1 |
| 设备 | Ascend910 (Atlas 800 A2) ×2 |
| fastapi / uvicorn / pydantic | 0.123.10 / 0.46.0 / 2.13.3 |
# 1) 安装 CANN 驱动与 toolkit (root)
bash Ascend-cann-toolkit_8.5.1_linux-aarch64.run --install
# 2) 安装 Python 依赖
pip install -r requirements.txt
# 3) 验证 NPU
npu-smi info
python -c "import torch, torch_npu; print('NPU:', torch.npu.device_count())"cd /opt/atomgit/vn-address-normalizer
python inference.py "p tan dinh q1 tphcm"输出示例 (中文日志 + 耗时):
2026-08-21 09:43:11 | INFO | [VN-Norm] 后端 = Ascend NPU (npu:0)
2026-08-21 09:43:11 | INFO | [VN-Norm] 模型已搬移到 Ascend NPU
2026-08-21 09:43:11 | INFO | [VN-Norm] NPU 预热完成
2026-08-21 09:43:11 | INFO | [VN-Norm] CLI 推理: 输入 = 'p tan dinh q1 tphcm'
2026-08-21 09:43:12 | INFO | [VN-Norm] canonical='Phường Tân Định, Thành phố Hồ Chí Minh',
valid=True, 耗时=1027.4 ms, backend=Ascend NPU# 默认 8088 端口
python inference.py --serve 8088
# 或指定地址
python inference.py --serve 8088 --host 0.0.0.0curl http://127.0.0.1:8088/health
# {"status":"ok","backend":"Ascend NPU","device":"npu:0",
# "vocab_size_src":287,"vocab_size_tgt":269,"canonicals":187817}curl -X POST http://127.0.0.1:8088/normalize \
-H "Content-Type: application/json" \
-d '{"text":"p tan dinh q1 tphcm"}'返回:
{
"canonical": "Phường Tân Định, Thành phố Hồ Chí Minh",
"valid": true,
"confidence": -272.6974,
"province": "Thành phố Hồ Chí Minh",
"ward_hint": "tan dinh",
"search_space": 170,
"latency_ms": 1067.7,
"backend": "Ascend NPU",
"device": "npu:0"
}python test_inference.py # 全部测试 (CLI + API + HTTP)
python test_inference.py --cli # 仅 CLI
python test_inference.py --api # 仅 API + 基准 + HTTP测试结果会写入 test_results.json。
from inference import normalize, BACKEND, DEVICE
print(f"后端 = {BACKEND} ({DEVICE})")
result = normalize("p tan dinh q1 tphcm")
print(result["canonical"]) # "Phường Tân Định, Thành phố Hồ Chí Minh"
print(result["valid"]) # True
print(result["latency_ms"]) # 1029.3
# 含 diacritics 输入
result = normalize("Phường Ba Đình, Quận 1, TP.HCM")
print(result["canonical"])| 字段 | 类型 | 说明 |
|---|---|---|
canonical | str | 规范化地址,未匹配时为空 |
valid | bool | 是否在标准地址库中 |
confidence | float | log-prob 分数 (越大越可信) |
province | str | 解析出的省份,None 表示未识别 |
ward_hint | str | ward slug 提示 |
search_space | int | trie 候选数 |
latency_ms | float | 本次推理 wall-clock 耗时 (ms) |
backend | str | "Ascend NPU" 或 "CPU" |
device | str | torch.device 字符串 |
| 输入 | 输出 |
|---|---|
p tan dinh q1 tphcm | Phường Tân Định, Thành phố Hồ Chí Minh |
Phuong Ba Dinh Ha Noi | Phường Ba Đình, Thành phố Hà Nội |
Xa Cu Chi TP HCM | Xã Củ Chi, Thành phố Hồ Chí Minh |
P. Bến Nghé Q.1 HCM | Phường Sài Gòn, Thành phố Hồ Chí Minh (pre-2025 重命名) |
phuong 14 quan 10 tphcm | (空 — 数字 ward 已被废弃) |
duong le loi phuong ben nghe q1 tphcm | Phường Sài Gòn, Thành phố Hồ Chí Minh |
测试机环境:Ascend910 ×2 / CANN 8.5.1 / torch 2.9.0+cpu / torch-npu 2.9.0.post1
| 指标 | 数值 |
|---|---|
| 冷启动 (模型加载 + trie 构建 + 预热) | ~25 s |
| 单次推理 — min | 867.9 ms |
| 单次推理 — p50 | 915.4 ms |
| 单次推理 — mean | 910.21 ms |
| 单次推理 — p95 | 952.0 ms |
| 单次推理 — max | 994.1 ms |
| numbered ward 拒收 (短路返回) | ~22 ms |
| 1-candidate trie (直查) | ~150 ms |
| HTTP 全链路 RTT (8 用例均值) | ~785 ms |
模型非常小 (26M),beam search 大部分时间花在 CPU trie 遍历上。NPU 主要承担 encoder/decoder 的矩阵乘法。 在小模型 + 大 trie 的场景下,NPU 加速比相对有限,但服务依然能跑在低延迟稳定区间。
| 改动点 | 原 CPU 版 | NPU 适配版 |
|---|---|---|
| 设备探测 | 默认 CPU | torch.npu.is_available() 自动选择 NPU;不可用时回退 CPU |
| 后端导入 | import torch | import torch_npu |
| 模型加载 | m.load_state_dict(...) | m.to(torch.device("npu:0")) |
encode() 内部 | torch.arange(L) 默认 CPU | torch.arange(L, device=src.device) |
step() 内部 | generate_square_subsequent_mask 默认 CPU | 同上,显式 device=tgt.device |
| Beam Search | 输入张量在 CPU | 全部 .to(device) 到 NPU |
| 首次推理 | 无预热 | 启动时跑一次空 encoder,触发 NPU 编译 |
| 服务化 | 无 | FastAPI /health + /normalize (默认 8088) |
| 日志 | logging 模块,中文 INFO |
适配后的 inference.py 仍保留原始 CPU 推理路径(自动回退),可作为参考实现对比。
vn-address-normalizer/
├── README.md ← 本文件
├── config.json ← 模型超参 (与 model_v3_final/ 同名,优先用)
├── inference.py ← ★ NPU 适配 + 服务化推理入口
├── test_inference.py ← ★ 端到端测试 (CLI / API / HTTP / 基准)
├── build_vocab.py ← ★ 一次性脚本:从语料构造 src/tgt_vocab.json
├── generate_screenshots.py ← ★ 一次性脚本:生成 3 张交付截图 + 水印
├── requirements.txt ← ★ Python 依赖清单
├── test_results.json ← ★ 测试输出 (运行 test_inference.py 后生成)
├── .fonts/
│ └── wqy-microhei.ttc ← 中文字体 (用于截图渲染)
├── assets/ ← ★ 交付截图 (maggie_Ha 水印)
│ ├── npu_device_call.png
│ ├── model_result.png
│ └── agent_workflow.png
└── model_v3_final/
├── model.safetensors ← 模型权重 (26M)
├── config.json ← 模型超参
├── src_vocab.json ← ★ 由 build_vocab.py 生成 (287 字符)
├── tgt_vocab.json ← ★ 由 build_vocab.py 生成 (269 字符)
├── clean_canonicals.json
└── legacy_ward_idx.json仓库原版
inference.py引用了model_v3_final/src_vocab.json与tgt_vocab.json,但发布包漏发。build_vocab.py按语料自动构造这两个文件, 与权重维度 (SRC_VOCAB=287, TGT_VOCAB=269) 严格对齐。
Seq2Seq Transformer (26M 参数)
推理 pipeline
训练数据
inference.py 用纯神经网络,不依赖规则 FST 引擎
(后者需要 vietnam_provinces 包与更重的索引)。vn-address-normalizer (CPU 原版 inference.py, 187K 地址库)Ascend910 / CANN 8.5.1)assets/npu_device_call.png assets/model_result.png assets/agent_workflow.pngtest_results.json