f
fbybl/sampurner-bengali-muril
模型介绍
文件和版本
Pull Requests
讨论
分析

SampurNER_Bengali_MuRIL 孟加拉语命名实体识别 · 昇腾 NPU 部署

简介

这个模型是孟加拉语 NER 的实用化部署产物。它源自 google/muril-large-cased 在 SampurNER 孟加拉语语料上的微调,能把一句孟加拉语拆成词级标签,识别出人名、 地名、机构、设施、事件、艺术类作品等 133 种 IOB2 实体。为了让它在昇腾 910 NPU 上跑起来,我们走了 vLLM-Ascend 的 token_classify 池化路线,最终对外暴露 一个标准的 OpenAI 风格 HTTP 接口,调用方只需发一个 POST 请求即可拿到逐 token 的实体标签。

技术上它就是一个标准的 BERT-large encoder:24 层、1024 维隐层、16 头注意力、 约 5.9 亿参数、WordPiece 词表 19.7 万。因为 vLLM 本身就注册了 BertForTokenClassification,且 vllm-ascend 已经带上了 BERT 的 worker 补丁, 整个适配过程不需要改动任何推理框架源码。

验证环境

适配与验证都在一台昇腾 910 设备上完成,关键版本如下:

  • CANN 8.5.1 / Python 3.11
  • vLLM 0.18.0 + vllm-ascend 0.8.0
  • torch / torch_npu 2.5.x
  • 单卡运行,tensor-parallel-size 1

推理服务占用 chip1(逻辑卡 13),通过 ASCEND_RT_VISIBLE_DEVICES=1 隔离, 避免与同机其他适配任务抢占资源。

服务启动

把模型权重放到本地目录后,用下面这条命令把服务拉起来:

export ASCEND_RT_VISIBLE_DEVICES=1
vllm serve /opt/atomgit/models/sampurner_20260823 \
  --load-format safetensors \
  --dtype bfloat16 \
  --max-model-len 512 \
  --max-num-seqs 8 \
  --port 8095 --host 127.0.0.1 \
  --enforce-eager --runner pooling \
  --tensor-parallel-size 1 \
  --gpu-memory-utilization 0.1 \
  --served-model-name sampurner \
  --trust-remote-code \
  --no-enable-prefix-caching \
  --no-enable-chunked-prefill

服务正常起来后,日志里会出现 Supported tasks: ['token_classify'] 和 Starting vLLM server on http://127.0.0.1:8095 两行关键信息,看到它们就说明 引擎已经就绪。

Smoke 验证

先确认服务在听:

curl -sf http://127.0.0.1:8095/v1/models

然后发一条推理请求,记得在请求体里带上任务类型:

curl -s http://127.0.0.1:8095/pooling \
  -H 'Content-Type: application/json' \
  -d '{"model":"sampurner","input":"ঢাকায় আগুন লাগলে মানুষ ক্ষতিগ্রস্ত হয়","task":"token_classify"}'

返回体里 data[0].data 是一个二维数组,行数等于 token 数,每行是 133 个 类别的概率分布。用仓库里的 inference.py 可以直接看到解析后的实体:

python3 inference.py --text "ঢাকায় আগুন লাগলে মানুষ ক্ষতিগ্রস্ত হয়"

输出类似这样——"ঢাকায়" 被标成地名,其余词都是 O:

ঢাকায়       -> B-location-GPE
আগুন         -> O
...
[实体] ঢাকায়  => location-GPE

性能参考

用 8 个并发线程压测,服务能稳定吃到 137 req/s 左右;单条短句的响应时间基本 在 25ms 上下。拿 502 个 token 的长文本对比 CPU 与 NPU:CPU 上 fp32 推理要 5 秒多,NPU 上 bf16 只要 75ms,提速接近 70 倍。推理期间 npu-smi 看到 chip1 的 AICore 在 11% 左右,HBM 占用约 4.2GB——对一个大 encoder 来说很轻量。

精度测评

手工挑了 4 句有代表性的孟加拉语,覆盖地名、人名、机构名,逐词核对结果:

  • ঢাকায় আগুন লাগলে মানুষ ক্ষতিগ্রস্ত হয় → 识别出地名 ঢাকায়
  • রবীন্দ্রনাথ ঠাকুর কলকাতায় জন্মগ্রহণ করেন → 人名 রবীন্দ্রনাথ ঠাকুর、地名 কলকাতায়
  • টাটা মোটরস ঢাকায় নতুন শাখা খুলেছে → 机构 টাটা মোটরস、地名 ঢাকায়
  • আগরতলায় বিক্ষোভে অন্তত ১০ জন আহত হয়েছেন → 地名 আগরতলায়

四句全部命中。因为走的是 argmax + eval 模式,同一句话重复请求输出完全一致, 结果可复现。

注意事项

实践里踩过的几个坑,列出来供参考:

  1. 请求必须显式传 "task":"token_classify"——光在启动参数里指定 --runner pooling 是不够的。
  2. --dtype 只认 bfloat16/float32,传 bf16 会直接报参数错误。
  3. 权重所在盘是 SFS Turbo,对稀疏文件支持不佳,预分配大文件要用真实写零。
  4. 多窗口并行适配时,记得用独立端口(这里 8095)和 ASCEND_RT_VISIBLE_DEVICES 指定空闲卡,别去动别人的进程。
  5. pooling 服务要加 --no-enable-chunked-prefill,否则行为不符合预期。