---
title: entity_dedup
canonical_url: "https://www.modelscope.cn/models/Fu496735182/entity_dedup"
md_url: "https://www.modelscope.cn/models/Fu496735182/entity_dedup.md"
repository: Fu496735182/entity_dedup
chinese_name: "实体判重与合并命名"
last_updated: 2026-10-14
pipeline_tag: entity-typing
tasks:
  - entity-typing
parameters: 8.4M
tensor_type:
  - F32
library_name:
  - safetensors
downloads: 3
stars: 0
---

# entity_dedup

> entity_dedup - Fu496735182 在 ModelScope 开源的模型。同一实体在不同对话中可能具有不同名称，导致信息重复或分散。基于BERT 二分类 + Qwen3.5-2B LoRA 命名的推理项目。输入两个候选实体，判断是否合并；合并时生成名称并合并描述。本项目包含推理代码及选定的 BERT、LoRA 适配器权重，不包含Qwen 基座权重，需要单独准备Qwen3.5-2B基座。

- **Repository**: Fu496735182/entity_dedup
- **Tasks**: entity-typing
- **Parameters**: 8.4M
- **Downloads**: 3
- **Stars**: 0
- **Last updated**: 2026-10-14

Source: https://www.modelscope.cn/models/Fu496735182/entity_dedup

---

# Entity Dedup · 实体判重与合并命名

同一实体在不同对话中可能具有不同名称，导致信息重复或分散。基于BERT 二分类 + Qwen3.5-2B LoRA 命名的推理项目。输入两个候选实体，判断是否合并；合并时生成名称并合并描述。本项目包含推理代码及选定的 BERT、LoRA 适配器权重，不包含Qwen 基座权重，需要单独准备Qwen3.5-2B基座。

## 模型流程

```mermaid
flowchart TD
    IN["输入 JSON：A / B"] --> VALID["字段校验与白名单"]
    VALID --> RULE["现有推理规则：号码拦截优先，其次强制合并"]
    RULE -->|未命中| BERT["BERT：名称、类型、别名"]
    BERT --> SEM["CLS 768 → MLP 128 → 64 维"]
    VALID --> FEAT["Python：32 维硬特征"]
    FEAT --> SCALE["长度及计数 log1p + 标准化；其余保持原值"]
    SEM --> CONCAT["拼接 96 维 → MLP 128 → 1 → Sigmoid"]
    SCALE --> CONCAT
    CONCAT --> AVG["A/B 与 B/A 概率平均"]
    AVG --> DECISION["阈值判断，默认 0.5"]
    RULE -->|命中| DECISION
    DECISION -->|不合并| DROP["should_merge=false；合并字段为 null"]
    DECISION -->|合并| PROMPT["名称 + 可选描述；清洗描述日志时间戳"]
    PROMPT --> LORA["Qwen3.5-2B + LoRA：仅生成名称"]
    LORA --> PY["Python：别名并集、描述直接拼接"]
    PY --> OUT["输出 JSON"]
```

## 目录

```text
.
├── config.json                 # 相对路径以配置文件所在目录为基准
├── predict.py                  # JSON / JSONL 推理入口
├── verify_artifacts.py         # 模型文件 SHA-256 校验
├── entity_merge/                # 独立推理实现、硬特征、词表和 prompt
├── models/
│   ├── bert/                   # 完整分类器、tokenizer、特征元数据
│   ├── lora/                   # LoRA 适配器及配套 tokenizer
│   └── manifest.json           # 模型文件摘要
├── examples/                   # 虚构示例
└── tests/                      # 无须权重的接口与规则测试
```

## 安装与配置

已验证环境为 Python 3.13.5、NVIDIA GPU、torch 2.9.0+cu128。先按设备环境安装 CUDA 版 PyTorch，再安装依赖。项目默认离线加载，不自动下载基座。

```bash
python -m pip install -r requirements.txt
python verify_artifacts.py
```

编辑 `config.json` 的 `lora.base_model_path`，指向完整的 **Qwen3.5-2B** 模型目录，目录中须包含 config、权重及其索引。可使用绝对路径，也可使用相对于配置文件的路径。例如 Linux 使用 `/opt/models/Qwen3.5-2B`，Windows 使用 `C:/models/Qwen3.5-2B`。

```json
{
  "bert": {
    "checkpoint_dir": "models/bert",
    "device": "cuda:0",
    "threshold": 0.5,
    "batch_size": 16
  },
  "lora": {
    "base_model_path": "models/Qwen3.5-2B",
    "adapter_dir": "models/lora",
    "device": "cuda:0",
    "dtype": "bfloat16",
    "attn_implementation": "sdpa",
    "max_new_tokens": 96
  }
}
```

`threshold` 允许 0–1，概率大于等于阈值预测合并。LoRA 按需加载，全部不合并时不会加载基座。两个模型常驻同一进程；显存不足时可以让 BERT 使用 `cpu`，LoRA 仍用 GPU。纯 CPU 使用时将两处 device 改为 `cpu`、LoRA dtype 改为 `float32`，速度较慢。`batch_size` 用于 BERT 两种顺序的计算，当前 CLI 按候选对顺序推理。

## 输入格式

单条 JSON 对象、JSON 数组均支持；输入文件扩展名为 `.jsonl` 时逐行解析。可选 `sample_id` 原样回传，不进入模型。

```json
{
  "sample_id": "demo-merge",
  "A": {
    "name": "蓝色收纳箱",
    "entity_type": "物品设备",
    "aliases": ["储物箱"],
    "description": "蓝色的收纳箱"
  },
  "B": {
    "name": "带盖收纳箱",
    "entity_type": "物品设备",
    "aliases": ["储物箱", "带盖箱"],
    "description": "有盖子的收纳箱"
  }
}
```

| 字段 | 类型 | 约束 |
| --- | --- | --- |
| A / B | object | 必填 |
| name | string | 必填，非空 |
| entity_type | string | 必填，非空，保持业务类别名称 |
| aliases | string[] | 可省略，默认 `[]` |
| description | string / null | 可省略，只进入 LoRA |
| sample_id | string | 可省略，调用方请求标识 |

其他字段不传入模型，尤其不使用 `description_summary`、标签、审核答案或召回分数。

## 输出格式

合并示例：

```json
{
  "sample_id": "demo-merge",
  "should_merge": true,
  "merge_score": 0.9,
  "bert_probability": 0.9,
  "decision_source": "bert",
  "name_containment": 0,
  "new_name": "蓝色带盖收纳箱",
  "merged_aliases": ["储物箱", "带盖箱"],
  "description_merge": "蓝色的收纳箱；有盖子的收纳箱"
}
```

| 字段 | 说明 |
| --- | --- |
| should_merge | 最终是否合并 |
| merge_score | BERT 合并概率；规则拦截为 0，规则合并为 1。规则值不是模型置信度 |
| bert_probability | 实际 BERT 概率；规则跳过模型时为 null |
| decision_source | `bert`、`number_guard`、`action_wrapper` 或 `lead_customer` |
| name_containment | 原始名称相等或互为子字符串为 1，否则为 0；仅诊断标签 |
| new_name | LoRA 生成的名称；不合并时 null |
| merged_aliases | Python 计算的别名并集；不合并时 null |
| description_merge | Python 拼接原始描述；不合并时 null |

## 执行推理

在项目根目录运行：

```bash
python predict.py --config config.json --input examples/input.json --output outputs/result.json
```

JSONL 示例：

```bash
python predict.py --config config.local.json --input requests.jsonl --output outputs/results.jsonl
```

输出形状随输入：对象对应对象、数组对应数组、JSONL 对应 JSONL。已有结果文件会被覆盖；只有整批成功后才原子替换文件，失败时保留原有文件并返回非零退出码。不会将错误生成结果当成“不合并”。日志写入终端，不进入结果 JSON。

Python 调用：

```python
from entity_merge.runtime import load_pipeline

pipeline = load_pipeline("config.json")  # 进程内初始化一次并复用
result = pipeline.predict({
    "A": {"name": "蓝色收纳箱", "entity_type": "物品设备", "aliases": []},
    "B": {"name": "带盖收纳箱", "entity_type": "物品设备", "aliases": []}
})
```

输出名称可能存在遗漏，单行格式校验不代表语义完整性。此包提供推理接口，不将历史实验指标视为当前端到端链路的上线评估。

## 验证

```bash
python -m unittest discover -s tests -v
python verify_artifacts.py
```

单元测试覆盖不合并跳过 LoRA、合并字段、别名去重、描述保留、日志时间戳清洗、规则优先级、包含关系仍走 LoRA、输入白名单及生成失败行为。真实权重加载和端到端测试需要安装依赖并提供基座。

交付前已在真实 GPU 上通过随包示例完成一次端到端推理，实际结果见 [`examples/output.actual.json`](examples/output.actual.json)。
