词汇/表达差异-6-n-gram分布距离

1.基本原理

"N-gram分布距离"的核心思想是:将两个字符串各自拆解成若干个长度为 n 的连续片段(即n-gram),然后通过比较这些片段的频率分布来计算它们的差异程度。这属于一种"无对齐"的字符串比较方法。

一句话定义
n-gram 分布距离 = 比较两个文本在"局部序列模式"上的统计分布是否一致

"这些局部片段(n-gram)出现的概率分布像不像?"

n-gram指的是文本中连续的n个字符或词汇(如单字符的1-gram、双字符的2-gram(bigram)、三字符的3-gram(trigram))。n-gram分布距离的核心思想是:

两个文本的相似程度,取决于它们的n-gram集合的出现频率分布差异------分布越接近,距离越小,相似度越高。

常见的n-gram分布距离包括余弦距离、Jaccard距离、KL散度、卡方距离 等,其中基于频率的余弦距离基于集合的Jaccard距离是最常用的两种形式。

2.算法步骤

(1)n-gram的两种类型
类型 定义 示例(文本:"苹果手机") 适用场景
字符级n-gram 以字符为单位切分连续n个字符 1-gram:{苹,果,手,机};2-gram:{苹果,果手,手机} 短文本、拼写纠错、跨语言文本匹配
词汇级n-gram 以分词后的词汇为单位切分连续n个词汇 1-gram:{苹果,手机};2-gram:{苹果手机} 长文本、语义匹配、文档相似度计算
(2)n-gram分布的表示

对文本提取n-gram后,通常转换为两种向量形式:

  • 布尔向量(存在型):仅记录n-gram是否出现(对应集合,用于Jaccard距离);
  • 频率向量(计数型) :记录每个n-gram的出现次数/频率(对应分布,用于余弦、KL散度等)。
    对每个 n-gram 计数并归一化:
    pT(g)=count(g)∑g′count(g′)p_T(g) = \frac{\text{count}(g)}{\sum_{g'} \text{count}(g')}pT(g)=∑g′count(g′)count(g)

得到一个 离散概率分布

步骤3:选择距离度量,计算分布差异

根据需求选择距离指标,计算两个频率向量的差异:

  • Jaccard距离(基于布尔向量):1 - 交集大小/并集大小 = 1 - 1/5 = 0.8;
  • 余弦距离(基于频率向量):1 - 余弦相似度 = 1 - (1×1)/(√3×√3) = 1 - 1/3 ≈ 0.667。
距离 本质
L1 / L2 几何差异
Cosine 分布方向
KL / JS 信息损失
Jaccard 模式重叠
Wasserstein 质量迁移

3.优缺点适用场景

特点 说明
✅ 核心优点 捕捉局部模式 :对字符串的局部连续变化(如拼写错误、词缀变化)比较敏感 计算相对高效 :与动态规划类的编辑距离相比,计算复杂度通常更低,尤其在大规模文本预处理中 无需对齐 :不要求逐字符/词匹配,对词序调换有一定容忍度。 可调粒度:通过 n 控制敏感度(n=2~5 最常用)。
❌ 主要缺点 忽略全局语义 :本质上仍是基于表面形式的统计,不理解深层语义。 忽略全局结构 :较长的n-gram才能捕捉较长依赖,但会导致特征空间爆炸和稀疏性问题。 维度灾难:n值增大时,所有可能n-gram的数量呈指数级增长。
🛠️ 典型使用场景 文本分类与聚类 :通过比较文档的n-gram分布来度量相似性,用于新闻分类、作者识别等。 拼写检查与模糊匹配 :因为对局部修改(增删改)敏感,可用于单词纠错或模糊搜索。 生物信息学 :用于比较DNA、RNA或蛋白质序列,进行同源性分析或分类。 剽窃检测:通过比较文本片段的n-gram分布来识别相似或重复内容。。

4.主流库推荐

实现组合 核心库 适用场景 性能 易用性 核心优势
NLTK + SciPy/NumPy NLTK(n-gram提取)、SciPy(距离计算) 通用文本处理、小数据量 接口友好,学习成本低
RapidFuzz + 自定义n-gram RapidFuzz(距离计算)、自定义Python函数(n-gram提取) 批量文本匹配、高性能需求 极快 距离计算基于C++,批量处理高效
spaCy + scikit-learn spaCy(分词/ngram)、sklearn(向量/距离) 词汇级n-gram、工业级NLP任务 支持词性标注、实体识别等预处理
Polyfuzz Polyfuzz(集成n-gram+匹配) 文本模糊匹配的端到端任务 开箱即用,支持多种匹配策略
手动实现(Python) 纯Python函数 学习场景、定制化需求 灵活可

5.使用

场景1:通用文本处理(字符/词汇级n-gram,小数据量)→ 首选NLTK + SciPy

NLTK提供了便捷的n-gram提取工具,SciPy/NumPy可高效计算距离,是学习和小数据量场景的首选。

使用示例

python 复制代码
import nltk
from nltk.util import ngrams
import numpy as np
from scipy.spatial.distance import cosine

# 下载nltk的必要数据(首次使用需执行)
# nltk.download('punkt')

# 1. 定义文本和n值
s1 = "kitten"
s2 = "sitting"
n = 2  # 二元字符n-gram

# 2. 提取字符级n-gram
def get_ngram_features(text, n):
    # 生成n-gram列表
    ngram_list = list(ngrams(text, n))
    # 构建n-gram到频率的字典
    ngram_freq = {}
    for gram in ngram_list:
        key = ''.join(gram)
        ngram_freq[key] = ngram_freq.get(key, 0) + 1
    return ngram_freq

# 提取特征
freq1 = get_ngram_features(s1, n)
freq2 = get_ngram_features(s2, n)

# 3. 构建统一的n-gram向量
all_ngrams = list(set(freq1.keys()) | set(freq2.keys()))
vec1 = np.array([freq1.get(gram, 0) for gram in all_ngrams])
vec2 = np.array([freq2.get(gram, 0) for gram in all_ngrams])

# 4. 计算余弦距离(n-gram分布距离的一种)
cos_sim = 1 - cosine(vec1, vec2)
cos_dist = cosine(vec1, vec2)
print(f"n-gram余弦相似度:{cos_sim:.4f},余弦距离:{cos_dist:.4f}")
# 输出:n-gram余弦相似度:0.3651,余弦距离:0.6349
场景2:批量文本匹配(高性能需求)→ 首选RapidFuzz + 自定义n-gram

RapidFuzz的C++底层实现了多种距离计算(如Jaccard、余弦),结合自定义的n-gram提取函数,可实现批量文本的高效匹配。

使用示例

python 复制代码
import rapidfuzz
from rapidfuzz import distance
from nltk.util import ngrams

# 1. 提取n-gram并转换为集合(用于Jaccard距离)
def get_ngram_set(text, n):
    ngram_list = list(ngrams(text, n))
    return set([''.join(gram) for gram in ngram_list])

# 2. 定义文本和候选列表
query = "北大"
candidates = ["北京大学", "北京师范大学", "清华大学", "南京大学"]
n = 2  # 二元字符n-gram

# 3. 批量计算n-gram的Jaccard距离
matches = []
query_ngram = get_ngram_set(query, n)
for cand in candidates:
    cand_ngram = get_ngram_set(cand, n)
    # 计算Jaccard相似度
    sim = distance.Jaccard.similarity(list(query_ngram), list(cand_ngram))
    matches.append((cand, sim))

# 排序输出
matches.sort(key=lambda x: x[1], reverse=True)
print("批量匹配结果:", matches)
# 输出:[('北京大学', 0.5), ('北京师范大学', 0.2), ('清华大学', 0.0), ('南京大学', 0.0)]
场景3:工业级NLP任务(词汇级n-gram)→ 首选spaCy + scikit-learn

spaCy提供了更专业的分词和文本预处理功能,适合词汇级n-gram的提取,结合scikit-learn的向量和距离计算,可处理复杂的NLP任务。

使用示例

python 复制代码
import spacy
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics.pairwise import cosine_similarity

# 加载spaCy的中文模型(首次使用需下载:python -m spacy download zh_core_web_sm)
nlp = spacy.load("zh_core_web_sm")

# 1. 定义文本
texts = ["苹果手机的性能很好", "苹果电脑的性能不错", "华为手机的续航很强"]

# 2. 分词函数(基于spaCy)
def tokenize(text):
    doc = nlp(text)
    return [token.text for token in doc if not token.is_stop and not token.is_punct]

# 3. 提取词汇级2-gram并构建频率向量
vectorizer = CountVectorizer(ngram_range=(2, 2), tokenizer=tokenize)
X = vectorizer.fit_transform(texts)

# 4. 计算余弦相似度(分布距离)
sim_matrix = cosine_similarity(X)
print("词汇级n-gram余弦相似度矩阵:\n", sim_matrix)
# 输出:[[1.         0.28867513 0.        ]
#        [0.28867513 1.         0.        ]
#        [0.         0.         1.        ]]
场景4:端到端文本模糊匹配→ 首选Polyfuzz

Polyfuzz集成了n-gram、TF-IDF、词嵌入等多种特征,可直接实现文本的模糊匹配,开箱即用。

使用示例

python 复制代码
from polyfuzz import PolyFuzz

# 1. 定义查询和候选
query = ["北大"]
candidates = ["北京大学", "北京师范大学", "清华大学", "南京大学"]

# 2. 使用n-gram模型匹配
model = PolyFuzz("NGram")
model.match(query, candidates)

# 3. 输出结果
print(model.get_matches())
# 输出:包含匹配的文本、相似度等信息
相关推荐
偶信科技5 小时前
自容式水听器是什么?偶信科技为您解答
人工智能·科技·偶信科技·ocean·自容式水听器·海洋仪器·海洋设备
Yolo566Q5 小时前
环境多介质逸度模型实践技术与典型案例【代码】应用
python
饕餮争锋5 小时前
pip install 报错This environment is externally managed
开发语言·python·pip
superman超哥5 小时前
仓颉语言导入语句使用深度解析
c语言·开发语言·c++·python·仓颉
躺柒5 小时前
读人机沟通法则:理解数字世界的设计与形成04机器是不完整的
大数据·人工智能·ai·人机交互·人机对话
xu_yule5 小时前
算法基础-多源最短路
c++·算法·多源最短路
Hcoco_me5 小时前
cv::contourArea &&鞋带公式
人工智能·rnn·lstm
小付爱coding5 小时前
MCP官方调试工具
java·人工智能
Amelia1111115 小时前
day33
python