本文目录导读:
- 基于字面匹配(适合短文本、关键词匹配)
- 基于词向量与语义(适合中长文本、理解语义)
- 面向长文档(适合篇章级比较)
- 中文文本的特殊处理
- 如何选择?(决策指南)
- 代码示例(Python,使用 Sentence-BERT)
计算文本相似度的方法有很多,选择哪种取决于文本的长度(短文本 vs 长文档)、语言(中文 vs 英文)以及对语义理解的要求(字面匹配 vs 语义相似)。
下面从传统方法到深度学习/大模型方法,分类介绍几种主流方式:
基于字面匹配(适合短文本、关键词匹配)
这类方法不考虑词义,只计算词汇重叠或字符重叠的比例。
编辑距离(Levenshtein Distance)
- 原理:把一个字符串变成另一个字符串所需的最少编辑操作(插入、删除、替换)次数。
- 计算:距离越小,相似度越高,可以归一化:
相似度 = 1 - (编辑距离 / 最大长度)。 - 适用:拼写纠错、精准匹配、短文本(如地址、姓名)。
- 示例:
“abc”与“abd”编辑距离为 1(替换 c→d)。
Jaccard 相似度
- 原理:计算两个集合(如分词后的词语集合)的交集大小除以并集大小。
- 公式:
J(A, B) = |A ∩ B| / |A ∪ B| - 适用:去重、关键词重叠度检查。
- 示例:句子A:
“我喜欢猫”→ 词集{我,喜欢,猫};句子B:“我不喜欢狗”→{我,不喜欢,狗},交集{我},并集{我,喜欢,猫,不喜欢,狗},Jaccard = 1/5 = 0.2。
余弦相似度(基于 TF-IDF)
- 原理:将文本转为向量(每个词用词频或TF-IDF权重表示),计算两个向量夹角的余弦值,值越接近1,方向越一致。
- 公式:
cos(A, B) = (A·B) / (|A| * |B|) - 适用:长文档(如文章、论文)的比较,能过滤常见词。
- 缺点:无法处理同义词(“电脑”和“计算机”会被视为完全不同)。
基于词向量与语义(适合中长文本、理解语义)
这类方法引入预训练的神经网络,能理解“猫”和“狗”都是宠物。
Word2Vec / GloVe 词向量平均
- 原理:将每个词映射到一个稠密向量(如300维),然后将句子中所有词的向量取平均,得到句子向量,再计算余弦相似度。
- 优点:比TF-IDF效果好,能捕捉部分语义。
- 缺点:词向量平均会丢失顺序信息;对否定词不敏感(“不好”可能会被平均成中性)。
句子嵌入模型(Sentence-BERT / SimCSE)
- 原理:专门用于将整个句子转化为高维语义向量,使用预训练的BERT或RoBERTa模型,通过对比学习训练,使得语义相似的句子向量距离更近。
- 计算:输入句子A和句子B,直接输出两个向量,计算余弦相似度。
- 适用:目前工业界最常用方法,适合短文本匹配(如问答、搜索query与标题匹配)。
- 示例:模型会判定“今天天气真好”和“今天是个好天气”相似度很高。
BERT 交叉编码器(Cross-Encoder)
- 原理:不单独生成向量,而是将两段文本拼接成一个序列输入BERT,由模型直接输出一个0到1的相似度分数。
- 优点:精度最高,能捕捉词序、否定、指代等复杂关系。
- 缺点:速度慢,需要两两配对输入,不适合大规模检索(可先用Bi-Encoder检索粗排,再用Cross-Encoder精排)。
- 适用:精排、语义蕴涵判断(Entailment)。
面向长文档(适合篇章级比较)
LSA / LDA 主题模型
- 原理:将文档表示为“主题分布”(如:60%体育,30%科技,10%娱乐),然后计算两个主题分布的KL散度或余弦相似度。
- 适用:新闻聚类、长文档主题相似度。
BM25(信息检索经典算法)
- 原理:基于概率检索模型,计算文档与查询的相关度,考虑了词频、逆文档频率、文档长度归一化。
- 适用:搜索引擎召回阶段,计算查询与文档的关键词匹配程度。
- 特点:不是对称的(query与doc相似度 ≠ doc与query相似度)。
中文文本的特殊处理
中文不像英文天然有空格分词,因此计算前通常需要:
- 分词:使用
jieba、HanLP、PKUSeg等工具。“我喜欢北京天安门” →“我 / 喜欢 / 北京 / 天安门”。 - 去除停用词:去掉“的、了、是、在”等无意义高频词。
- 词性标注或实体识别(可选):对关键实体(人名、地名)赋予更高权重。
如何选择?(决策指南)
| 场景 | 推荐方法 | 原因 |
|---|---|---|
| 拼写纠错、DNA序列 | 编辑距离 | 精确到字符级 |
| 短文本去重(如评论) | Jaccard / SimHash | 速度快,适合海量数据 |
| 搜索引擎召回 | BM25 | 经典、高效、可解释性强 |
| 开放域问答匹配 | Sentence-BERT | 语义理解好,速度快 |
| 法律/金融合同比对 | 编辑距离 + 实体对齐 | 需要字面上精确匹配改动点 |
| 高精度语义相似度 | BERT Cross-Encoder | 精度最高,但速度慢 |
| 大规模聚类 | Sentence-BERT + 余弦距离 | 先编码,再聚类 |
代码示例(Python,使用 Sentence-BERT)
这是目前最实用的方案之一:
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
# 1. 加载预训练模型(会自动下载)
# 中文推荐:'shibing624/text2vec-base-chinese' 或 'BAAI/bge-small-zh-v1.5'
model = SentenceTransformer('shibing624/text2vec-base-chinese')
# 2. 定义句子
sentences = [
"我喜欢看科幻电影",
"我对科幻片很感兴趣",
"我今天吃了火锅"
]
# 3. 生成向量
embeddings = model.encode(sentences)
# 4. 计算相似度
sim_12 = cosine_similarity([embeddings[0]], [embeddings[1]])[0][0] # 结果 ≈ 0.85
sim_13 = cosine_similarity([embeddings[0]], [embeddings[2]])[0][0] # 结果 ≈ 0.30
print(f"句子1与句子2 相似度: {sim_12:.2f}")
print(f"句子1与句子3 相似度: {sim_13:.2f}")
如果你的需求是简单的字面匹配,用 Jaccard 或 TF-IDF 余弦;如果你需要理解“同义词”和“语义”,用 Sentence-BERT 或 BERT Cross-Encoder(前者速度快,后者精度高)。
标签: 计算方法