人工智能之语言领域 自然语言处理 第九章 文本相似度计算

人工智能之语言领域

第九章 文本相似度计算


文章目录

  • 人工智能之语言领域
  • [前言 文本相似度计算](#前言 文本相似度计算)
    • [9.1 文本相似度概述](#9.1 文本相似度概述)
      • [9.1.1 相似度的核心定义与应用场景](#9.1.1 相似度的核心定义与应用场景)
      • [9.1.2 相似度与距离的关系](#9.1.2 相似度与距离的关系)
    • [9.2 传统相似度计算方法](#9.2 传统相似度计算方法)
      • [9.2.1 基于字符串的方法](#9.2.1 基于字符串的方法)
        • [(1)编辑距离(Levenshtein Distance)](#(1)编辑距离(Levenshtein Distance))
        • [(2)Jaccard 相似度](#(2)Jaccard 相似度)
      • [9.2.2 基于向量的方法:TF-IDF 向量余弦相似度](#9.2.2 基于向量的方法:TF-IDF 向量余弦相似度)
    • [9.3 现代相似度计算方法](#9.3 现代相似度计算方法)
      • [9.3.1 基于词嵌入的相似度计算](#9.3.1 基于词嵌入的相似度计算)
      • [9.3.2 基于预训练模型的句嵌入相似度](#9.3.2 基于预训练模型的句嵌入相似度)
      • [9.3.3 双塔模型在相似度计算中的应用](#9.3.3 双塔模型在相似度计算中的应用)
    • [9.4 实战案例:智能问答中的问句匹配与文本检索](#9.4 实战案例:智能问答中的问句匹配与文本检索)
      • 任务目标
      • 完整实现流程
        • [Step 1: 准备 FAQ 数据库](#Step 1: 准备 FAQ 数据库)
        • [Step 2: 构建句嵌入索引](#Step 2: 构建句嵌入索引)
        • [Step 3: 用户查询匹配](#Step 3: 用户查询匹配)
        • [Step 4: 可视化相似度(可选)](#Step 4: 可视化相似度(可选))
      • 系统优化建议
    • 补充:方法对比总结
    • 小结
  • 资料关注

前言 文本相似度计算

文本相似度计算是自然语言处理(NLP)中一项基础而关键的技术,旨在量化两段文本在语义或形式上的接近程度。它是智能搜索、问答系统、推荐引擎、抄袭检测等应用的核心支撑。本章将系统介绍文本相似度的定义、传统与现代计算方法,并通过智能问答中的问句匹配实战案例完整演示端到端流程。


9.1 文本相似度概述

9.1.1 相似度的核心定义与应用场景

文本相似度 :衡量两个文本片段在字面形式语义内容 上的相近程度,取值通常在 [0, 1] 区间(0=完全不相似,1=完全相同)。

典型应用场景:
场景 任务 相似度作用
智能问答(QA) 用户问句 vs FAQ库 找最匹配的标准问题
搜索引擎 查询 vs 网页 排序相关文档
推荐系统 用户历史 vs 候选内容 推荐相似商品/文章
文本去重 新闻/论文比对 检测重复或抄袭
对话系统 用户输入 vs 对话状态 判断意图是否一致

🌰 示例:

问句1:"怎么重置我的密码?"

问句2:"忘记密码了怎么办?"

→ 语义高度相似,应匹配同一答案


9.1.2 相似度与距离的关系

距离(Distance) 越小 → 相似度(Similarity) 越大。常见转换关系:

距离度量 相似度转换 特点
余弦距离 sim = 1 - distance 或直接用 cosine_similarity 忽略向量长度,关注方向
欧氏距离 sim = 1 / (1 + distance) 受向量尺度影响大
Jaccard 距离 `sim = 1 - distance = A∩B

文本1
向量化
文本2
距离度量
余弦距离
欧氏距离
Jaccard距离
相似度 = cos(θ)
相似度 = 1/(1+d)
相似度 = |A∩B|/|A∪B|

余弦相似度 是 NLP 中最常用的相似度度量,因其对文本长度不敏感。


9.2 传统相似度计算方法

9.2.1 基于字符串的方法

(1)编辑距离(Levenshtein Distance)
  • 定义:将一个字符串转换为另一个所需的最少单字符编辑操作(插入、删除、替换)次数。
  • 相似度转换:sim = 1 - edit_distance / max(len(s1), len(s2))
python 复制代码
def levenshtein_distance(s1, s2):
    if len(s1) < len(s2):
        return levenshtein_distance(s2, s1)
    if len(s2) == 0:
        return len(s1)
    previous_row = list(range(len(s2) + 1))
    for i, c1 in enumerate(s1):
        current_row = [i + 1]
        for j, c2 in enumerate(s2):
            insertions = previous_row[j + 1] + 1
            deletions = current_row[j] + 1
            substitutions = previous_row[j] + (c1 != c2)
            current_row.append(min(insertions, deletions, substitutions))
        previous_row = current_row
    return previous_row[-1]

s1, s2 = "kitten", "sitting"
dist = levenshtein_distance(s1, s2)
sim = 1 - dist / max(len(s1), len(s2))
print(f"编辑距离: {dist}, 相似度: {sim:.3f}")  # 0.571

⚠️ 缺陷:无法捕捉语义("car" 和 "automobile" 距离大)

(2)Jaccard 相似度
  • 将文本视为词集合,计算交集与并集之比。
  • 适用于关键词匹配场景。
python 复制代码
def jaccard_similarity(s1, s2):
    set1, set2 = set(s1.split()), set(s2.split())
    intersection = set1 & set2
    union = set1 | set2
    return len(intersection) / len(union) if union else 0

s1 = "如何重置密码"
s2 = "密码忘了怎么重置"
print("Jaccard相似度:", jaccard_similarity(s1, s2))  # 0.4(按字切分更高)

💡 中文建议按分词后处理。


9.2.2 基于向量的方法:TF-IDF 向量余弦相似度

步骤

  1. 构建语料库 TF-IDF 矩阵
  2. 将两文本表示为 TF-IDF 向量
  3. 计算余弦相似度
python 复制代码
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

corpus = [
    "如何重置我的账户密码",
    "忘记密码了怎么办",
    "怎样登录我的邮箱",
    "手机无法开机"
]

# 拟合 TF-IDF
vectorizer = TfidfVectorizer(token_pattern=r'\S+')  # 中文需先分词
# 简化:按字切分
corpus_char = [" ".join(list(s)) for s in corpus]
tfidf_matrix = vectorizer.fit_transform(corpus_char)

# 计算相似度
query = " ".join(list("密码忘记了怎么找回"))
query_vec = vectorizer.transform([query])
cos_sim = cosine_similarity(query_vec, tfidf_matrix).flatten()

for i, score in enumerate(cos_sim):
    print(f"'{corpus[i]}' 相似度: {score:.3f}")
# 输出:第一句和第二句得分高

✅ 优点:简单高效,适合关键词匹配

❌ 缺点:忽略语序和语义("北京到上海" vs "上海到北京" 被视为不同)


9.3 现代相似度计算方法

9.3.1 基于词嵌入的相似度计算

使用 Word2Vec/GloVe/FastText 获取词向量,再聚合为句向量。

常用聚合策略:
  • 平均池化(Mean Pooling):所有词向量求均值
  • TF-IDF 加权平均:高频通用词权重低
  • SIF(Smooth Inverse Frequency):去除主成分噪声
python 复制代码
import numpy as np
from gensim.models import Word2Vec

# 假设已训练好 Word2Vec 模型
model = Word2Vec.load("your_w2v_model.bin")

def sentence_embedding(words, model):
    vectors = [model.wv[w] for w in words if w in model.wv]
    return np.mean(vectors, axis=0) if vectors else np.zeros(model.vector_size)

s1 = ["密码", "重置"]
s2 = ["忘记", "密码", "怎么办"]
emb1 = sentence_embedding(s1, model)
emb2 = sentence_embedding(s2, model)

from sklearn.metrics.pairwise import cosine_similarity
sim = cosine_similarity([emb1], [emb2])[0][0]
print("词嵌入相似度:", sim)

⚠️ 仍存在局限:无法处理一词多义("苹果"在不同上下文)


9.3.2 基于预训练模型的句嵌入相似度

Sentence-BERT(SBERT) 是当前 SOTA 方案,专为句子相似度设计。

  • 微调 BERT,使语义相近句子的嵌入距离更近
  • 支持直接计算余弦相似度
python 复制代码
from sentence_transformers import SentenceTransformer

# 加载中文 SBERT 模型
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
# 或中文专用:'shibing624/text2vec-base-chinese'

sentences = [
    "如何重置密码?",
    "密码忘了怎么找回?",
    "我的手机坏了"
]

embeddings = model.encode(sentences)
sim_matrix = cosine_similarity(embeddings)

print("相似度矩阵:")
print(sim_matrix.round(3))
# 输出:前两句相似度 >0.8,与第三句 <0.2

🌐 中文推荐模型:

  • shibing624/text2vec-base-chinese
  • WangZeJun/sentence-bert-base-chinese

9.3.3 双塔模型在相似度计算中的应用

双塔架构(Dual Encoder) 是工业级大规模检索的标准方案。

  • Query 塔:编码用户查询
  • Doc 塔:编码候选文档
  • 离线计算:Doc 嵌入可预先计算并索引
  • 在线匹配:仅需计算 Query 嵌入,与 Doc 库做 ANN 搜索

用户查询
Query Encoder
文档1
Doc Encoder
文档2
向量索引
ANN 搜索
Top-K 相似文档

优势

  • 在线推理快(O(1) 复杂度)
  • 支持十亿级文档检索

实现工具

  • 向量索引:FAISS(Facebook)、Annoy(Spotify)
  • 模型训练:使用对比学习(Contrastive Loss)或三元组损失(Triplet Loss)
python 复制代码
# 使用 FAISS 进行快速相似检索
import faiss
import numpy as np

# 假设 doc_embeddings 是 [N, 768] 的文档嵌入矩阵
index = faiss.IndexFlatIP(768)  # 内积(等价于余弦,若向量已归一化)
index.add(doc_embeddings)

# 查询
query_emb = model.encode(["如何重置密码?"])
D, I = index.search(query_emb, k=3)  # 返回 Top-3
print("最相似文档ID:", I[0])
print("相似度得分:", D[0])  # 内积值 ∈ [-1, 1]

9.4 实战案例:智能问答中的问句匹配与文本检索

任务目标

构建一个 FAQ 问答系统:

  • 输入:用户自然语言问句
  • 输出:最匹配的标准问题及答案

完整实现流程

Step 1: 准备 FAQ 数据库
python 复制代码
faq = {
    "如何重置密码?": "请访问登录页面,点击'忘记密码'链接。",
    "怎么修改手机号?": "进入个人中心 -> 账户安全 -> 修改手机号。",
    "退款流程是什么?": "提交退款申请后,3个工作日内处理。"
}
standard_questions = list(faq.keys())
Step 2: 构建句嵌入索引
python 复制代码
from sentence_transformers import SentenceTransformer
import faiss

# 加载模型
model = SentenceTransformer('shibing624/text2vec-base-chinese')

# 编码标准问题
doc_embeddings = model.encode(standard_questions)

# 构建 FAISS 索引(L2 距离)
index = faiss.IndexFlatL2(doc_embeddings.shape[1])
index.add(np.array(doc_embeddings, dtype='float32'))
Step 3: 用户查询匹配
python 复制代码
def get_answer(user_query, top_k=1):
    # 编码查询
    query_emb = model.encode([user_query])
    # 搜索
    D, I = index.search(np.array(query_emb, dtype='float32'), top_k)
    # 返回最相似问题及答案
    best_idx = I[0][0]
    similarity = 1 - D[0][0] / 4  # 粗略归一化(L2最大距离≈2)
    if similarity > 0.5:  # 阈值过滤
        question = standard_questions[best_idx]
        return faq[question], similarity
    else:
        return "抱歉,未找到相关问题。", similarity

# 测试
user_input = "密码忘记了怎么办?"
answer, score = get_answer(user_input)
print(f"用户问: {user_input}")
print(f"匹配度: {score:.3f}")
print(f"回答: {answer}")
Step 4: 可视化相似度(可选)
python 复制代码
import matplotlib.pyplot as plt

scores = []
for q in standard_questions:
    emb = model.encode([q])
    D, _ = index.search(np.array(emb, dtype='float32'), 1)
    scores.append(1 - D[0][0]/4)

plt.bar(standard_questions, scores)
plt.title("各标准问题与用户的匹配度")
plt.xticks(rotation=45)
plt.show()

系统优化建议

  • 负采样:训练时加入困难负例(如语义相近但不匹配的问题)
  • 多轮交互:若 Top-1 置信度低,提供多个候选让用户选择
  • 领域微调:在客服对话日志上继续微调 SBERT

补充:方法对比总结

方法 语义理解 速度 适用场景
编辑距离 ⚡️ 拼写纠错、短文本
Jaccard ⚡️ 关键词匹配
TF-IDF + 余弦 ⚡️ 中小规模检索
词嵌入平均 ⚡️ 无监督场景
SBERT ✅✅ 高精度匹配
双塔 + FAISS ✅✅ ⚡️⚡️ 大规模在线服务

小结

文本相似度计算从早期的字符串匹配发展到今天的深度语义模型,已成为智能系统不可或缺的组件。TF-IDF 仍是轻量级应用的首选,而 Sentence-BERT + 向量检索 是当前工业界高精度场景的标准方案。在实际项目中,应根据数据规模、延迟要求、语义深度 选择合适技术栈。未来,相似度计算将与大语言模型(LLM)多模态嵌入个性化表示深度融合,迈向更智能、更精准的语义匹配。


资料关注

咚咚王

《Python 编程:从入门到实践》

《利用 Python 进行数据分析》

《算法导论中文第三版》

《概率论与数理统计(第四版) (盛骤) 》

《程序员的数学》

《线性代数应该这样学第 3 版》

《微积分和数学分析引论》

《(西瓜书)周志华-机器学习》

《TensorFlow 机器学习实战指南》

《Sklearn 与 TensorFlow 机器学习实用指南》

《模式识别(第四版)》

《深度学习 deep learning》伊恩·古德费洛著 花书

《Python 深度学习第二版(中文版)【纯文本】 (登封大数据 (Francois Choliet)) (Z-Library)》

《深入浅出神经网络与深度学习 +(迈克尔·尼尔森(Michael+Nielsen)》

《自然语言处理综论 第 2 版》

《Natural-Language-Processing-with-PyTorch》

《计算机视觉-算法与应用(中文版)》

《Learning OpenCV 4》

《AIGC:智能创作时代》杜雨 +&+ 张孜铭

《AIGC 原理与实践:零基础学大语言模型、扩散模型和多模态模型》

《从零构建大语言模型(中文版)》

《实战 AI 大模型》

《AI 3.0》

相关推荐
研究点啥好呢2 小时前
每日GitHub热门项目推荐 | 2026年3月9日
人工智能·ai·自动化·github·openclaw
itwangyang5202 小时前
GitHub Push Protection 报错解决指南(检测到 Token / Secret)
人工智能·python·github
燃于AC之乐2 小时前
OpenClaw“小龙虾”深度解析:60天碾压Linux的AI智能体,从原理到搞定本地部署【Windows系统 + 接入飞书】
linux·人工智能·飞书·openclaw·小龙虾
AlphaNil2 小时前
.NET + AI 跨平台实战系列(四):本地化部署——使用Ollama运行开源多模态模型
人工智能·开源·.net
lihuayong2 小时前
混合检索架构:为什么BM25与向量搜索缺一不可
人工智能·全文检索·向量检索·混合检索
犀思云2 小时前
解构网络复杂性:基于 FusionWAN NaaS 的确定性架构工程实践与流量编排深度指南
网络·人工智能·机器人·智能仓储·专线
安逸sgr2 小时前
【Agent 架构设计】记忆系统深度解析:从 RAG 到 Hindsight 的演进之路!
人工智能·microsoft·大模型·claude·cursor
万少2 小时前
用龙虾 openclaw 拆解网络爆文-说不定你也可以参考写一个
人工智能
wal13145202 小时前
OpenClaw教程补充内容——如何进行飞书Bot的配置
人工智能·飞书·openclaw