# 分词词表模型

> 预训练模型（llmlingua2、bert_base_uncased、wiki_tocken、gemma2b）、自定义语料训练模型、HuggingFace 分词器与 Lindera 模型。

---

LLMS 索引： [llms.txt](/zh/llms.txt)

---

在 `pg_tokenizer` 中，**模型（Model）** 负责将字符串词元（或原始文本）映射为整数词元 ID（`INT[]`）。你可以直接选用四个内置预训练模型、在 PostgreSQL 表上训练自定义词表模型、导入任意 HuggingFace `tokenizer.json`，或者使用 Lindera 形态学分析模型。

## 1. 内置预训练模型 {#1-built-in-pretrained-models}

| 模型名称 | 来源 / 架构 | 是否需要 `text_analyzer`？ | 内存占用 | 适用场景 |
| :--- | :--- | :--- | :--- | :--- |
| **`llmlingua2`** | [`microsoft/llmlingua-2-xlm-roberta-large-meetingbank`](https://huggingface.co/microsoft/llmlingua-2-xlm-roberta-large-meetingbank)（XLM-RoBERTa 多语言子词模型） | 否（禁止） | ~200 MB（默认预加载） | 开箱即用的多语言通用文本分词，无需训练自定义词表。 |
| **`bert_base_uncased`** | [`google-bert/bert-base-uncased`](https://huggingface.co/google-bert/bert-base-uncased)（WordPiece，30,522 词表） | 否（禁止） | ~15 MB | 通用英文文本；启动极快且词表紧凑。 |
| **`wiki_tocken`** | [`Snowolf/wiki-tocken`](https://huggingface.co/Snowolf/wiki-tocken)（内置英文维基百科词元词典） | **是（必需）** | ~30 MB | 配合自定义词干提取器、停用词与同义词使用的英文全文检索。 |
| **`gemma2b`** | [`google/gemma-2b`](https://huggingface.co/google/gemma-2b)（SentencePiece BPE，256k 词表） | 否（禁止） | ~100 MB | 代码、技术标识符以及多语言大语言模型工作负载。 |

### 示例：使用 `bert_base_uncased` 或 `llmlingua2` {#example-using-bert_base_uncased-or-llmlingua2}

```sql
SELECT create_tokenizer('bert_tok', $$
model = "bert_base_uncased"
$$);

SELECT tokenize('VectorChord-BM25 brings Block-WeakAnd to PostgreSQL.', 'bert_tok');
```

---

## 2. 自定义语料库模型（`create_custom_model`） {#2-custom-corpus-models-create_custom_model}

为了获得最高的 BM25 相关性精度与最小的内存占用，你可以直接基于自己的业务表列构建**自定义词表模型**。不同于大语言模型子词分词器（会将罕见的领域专有名词切碎为多个子词元），自定义模型会为 `text_analyzer` 产出的每个唯一归一化词元分配独立的 1:1 整数 ID，且在内存中仅占用几兆字节。

### 方式 A：一步到位辅助函数（`create_custom_model_tokenizer_and_trigger`） {#option-a-one-step-helper-create_custom_model_tokenizer_and_trigger}

该便捷函数会一步完成创建自定义模型、创建分词器、为所有存量行回填目标 `bm25vector` 列，并安装一个 `BEFORE INSERT OR UPDATE` 触发器——当数据行发生变化时，自动学习新词元并更新 `bm25vector` 列：

```sql
-- 1. 定义文本分析流水线
SELECT create_text_analyzer('my_analyzer', $$
character_filters = ["to_lowercase"]
pre_tokenizer = "unicode_segmentation"
token_filters = [
    "skip_non_alphanumeric",
    { stopwords = "nltk_english" },
    { stemmer = "english_porter2" },
]
$$);

-- 2. 创建模型与分词器、回填向量并挂载触发器
SELECT create_custom_model_tokenizer_and_trigger(
    tokenizer_name     => 'my_tokenizer',
    model_name         => 'my_model',
    text_analyzer_name => 'my_analyzer',
    table_name         => 'documents',
    source_column      => 'passage',
    target_column      => 'embedding'
);
```

### 方式 B：分步创建（不使用触发器的 `create_custom_model`） {#option-b-step-by-step-create_custom_model}

如果你不希望挂载自动触发器（或者希望自行控制何时计算并回填 `embedding` 列）：

```sql
-- 1. 基于现有表列构建自定义词表模型
SELECT create_custom_model('my_model', $$
table = "documents"
column = "passage"
text_analyzer = "my_analyzer"
$$);

-- 2. 创建引用该分析器与自定义模型的分词器
SELECT create_tokenizer('my_tokenizer', $$
text_analyzer = "my_analyzer"
model = "my_model"
$$);

-- 3. 手动回填或刷新词频向量列
UPDATE documents SET embedding = tokenize(passage, 'my_tokenizer');
```

---

## 3. 导入 HuggingFace 分词器（`create_huggingface_model`） {#3-importing-huggingface-tokenizers-create_huggingface_model}

你可以使用 `create_huggingface_model` 将任意 HuggingFace [`tokenizers`](https://github.com/huggingface/tokenizers) 的 JSON 配置文件（`tokenizer.json`）导入 PostgreSQL。

在 `psql` 中，可以使用 `\set` 直接从 HuggingFace Hub 下载 `tokenizer.json` 到变量中并传给 `create_huggingface_model`：

```sql
\set content `wget -q -O - https://huggingface.co/google-bert/bert-base-uncased/resolve/main/tokenizer.json`
SELECT create_huggingface_model('bert_import', :'content');

SELECT create_tokenizer('bert_import_tok', $$
model = "bert_import"
$$);
```

也可以直接将 `tokenizer.json` 文件的完整内容作为美元符号引用的字符串传入：

```sql
SELECT create_huggingface_model('bge_m3_model', $$
{
  "version": "1.0",
  ...
}
$$);

SELECT create_tokenizer('bge_m3_tok', $$
model = "bge_m3_model"
$$);
```

---

## 4. Lindera 形态学分词模型（`create_lindera_model`） {#4-lindera-morphological-models-create_lindera_model}

对于日文、韩文、中文等需要形态学词典分析的东亚语言，`pg_tokenizer` 集成了 [`lindera`](https://github.com/lindera/lindera)。

> [!NOTE]
> 如果你从源码编译 `pg_tokenizer`，请在编译时通过 `--features` 启用所需的 Lindera 词典特性：
> ```bash
> cargo pgrx install --release --features "lindera-ipadic lindera-ipadic-neologd lindera-unidic lindera-ko-dic lindera-cc-cedict"
> ```

以下完整示例展示了基于 `ipadic` 词典配置日文叠字符（Iteration Mark）规范化、NFKC Unicode 规范化、字符映射、复合词合并、数词归一化、日文停用词性（Stop-tags）过滤、片假名词干提取以及变音符号移除：

```sql
SELECT create_lindera_model('japanese_ipadic', $$
[segmenter]
mode = "normal"
  [segmenter.dictionary]
  kind = "ipadic"

[[character_filters]]
kind = "japanese_iteration_mark"
  [character_filters.args]
  normalize_kanji = true
  normalize_kana = true

[[character_filters]]
kind = "unicode_normalize"
  [character_filters.args]
  kind = "nfkc"

[[character_filters]]
kind = "mapping"
  [character_filters.args.mapping]
  "リンデラ" = "Lindera"

[[token_filters]]
kind = "japanese_compound_word"
  [token_filters.args]
  kind = "ipadic"
  tags = [
    "名詞,数",
    "名詞,接尾,助数詞",
  ]
  new_tag = "名詞,数"

[[token_filters]]
kind = "japanese_number"
  [token_filters.args]
  tags = ["名詞,数"]

[[token_filters]]
kind = "japanese_stop_tags"
  [token_filters.args]
  tags = [
    "接続詞",
    "助詞",
    "助詞,格助詞",
    "助詞,格助詞,一般",
    "助詞,格助詞,引用",
    "助詞,格助詞,連語",
    "助詞,係助詞",
    "助詞,副助詞",
    "助詞,間投助詞",
    "助詞,並立助詞",
    "助詞,終助詞",
    "助詞,副助詞／並立助詞／終助詞",
    "助詞,連体化",
    "助詞,副詞化",
    "助詞,特殊",
    "助動詞",
    "記号",
    "記号,一般",
    "記号,読点",
    "記号,句点",
    "記号,空白",
    "記号,括弧閉",
    "その他,間投",
    "フィラー",
    "非言語音",
  ]

[[token_filters]]
kind = "japanese_katakana_stem"
  [token_filters.args]
  min = 3

[[token_filters]]
kind = "remove_diacritical_mark"
  [token_filters.args]
  japanese = false
$$);

SELECT create_tokenizer('jp_tok', $$
model = "japanese_ipadic"
$$);
```

---

## 5. 模型预加载（`add_preload_model`） {#5-model-preloading-add_preload_model}

大型模型（如约 200 MB 的 `llmlingua2`、约 100 MB 的 `gemma2b`、`japanese_ipadic`）在每个新建的 PostgreSQL 后端进程首次查询时，需要数秒钟时间和可观的内存来加载。通过在 `shared_preload_libraries` 启动阶段预加载模型，PostgreSQL 只需在 postmaster 主进程中加载一次模型，即可通过写时复制（Copy-on-Write）内存在所有后端连接之间共享（`llmlingua2` 默认已加入预加载列表）：

```sql
-- 将模型加入预加载列表（持久化保存在 $PGDATA/pg_tokenizer/preload_models）
SELECT add_preload_model('my_model');

-- 查看当前已配置的预加载模型列表（默认为 {llmlingua2}）
SELECT list_preload_models();

-- 从预加载列表中移除模型（例如不使用 llmlingua2 时可释放约 200 MB 内存）
SELECT remove_preload_model('llmlingua2');
```

调用 `add_preload_model(...)` 后（请确保已在 `postgresql.conf` 中设置 `shared_preload_libraries = 'pg_tokenizer.so'`），重启 PostgreSQL 即可使预加载对所有后端连接生效。
