扩展 - 如何计算两个文本的相似度
1 课程概览
本课扩展讲解如何计算两段文本的相似度,这是常见的面试题。通过切词、统计词汇、计算词频向量、计算距离(如欧氏距离)来衡量文本相似度。对比有无 n-gram 的计算方式。
2 核心概念与定义
- 文本相似度(Text Similarity):衡量两段文本在语义或词汇上的相似程度。
- 欧氏距离(Euclidean Distance):对应维度差值的平方和开平方根。
- 曼哈顿距离(Manhattan Distance):对应维度差值的绝对值之和。
- 词频向量(Word Frequency Vector):以去重词汇为维度,每个句子的词频为值。
3 模型与算法详解
文本相似度计算流程
- 切词:对两段文本进行分词
- 统计词汇:合并去重,生成词汇表
- 计算词频:统计每个句子中各词的出现次数
- 计算距离:使用欧氏距离等公式计算相似度
思路一:无 n-gram(1-gram)
示例:
- 文本1切词:
["我", "是", "黑马", "人"] - 文本2切词:
["你", "是", "黑马"]
步骤:
- 合并去重:
["我", "你", "是", "黑马", "人"](顺序不变) - 统计词频:
- 文本1:
[2, 0, 1, 1, 1]("我"出现2次) - 文本2:
[0, 1, 1, 1, 0]
- 文本1:
- 计算欧氏距离
思路二:有 n-gram(2-gram)
示例:
- 文本1 2-gram:
["我是", "是黑", "黑马", "马人"] - 文本2 2-gram:
["你是", "是黑", "黑马"]
步骤:
- 合并去重
- 统计词频
- 计算距离
距离计算公式对比
| 距离类型 | 公式 |
|---|---|
| 欧氏距离 | $\sqrt{\sum_{i=1}^{n} (x_i - y_i)^2}$ |
| 曼哈顿距离 | $\sum_{i=1}^{n} |
4 数学原理与推导
词频向量
给定词汇表 $V = [v_1, v_2, ..., v_m]$,文本 $T$ 的词频向量为:
$$\vec{f}(T) = [\text{count}(v_1, T), \text{count}(v_2, T), ..., \text{count}(v_m, T)]$$
欧氏距离
$$d(T_1, T_2) = \sqrt{\sum_{i=1}^{m} (f_i(T_1) - f_i(T_2))^2}$$
示例计算
文本1:[2, 0, 1, 1, 1] 文本2:[0, 1, 1, 1, 0]
$$d = \sqrt{(2-0)^2 + (0-1)^2 + (1-1)^2 + (1-1)^2 + (1-0)^2}$$
$$d = \sqrt{4 + 1 + 0 + 0 + 1} = \sqrt{6}$$
5 代码示例
import jieba
import numpy as np
from itertools import chain
# 1. 切词
text1 = "我是黑马人我"
text2 = "你是黑马"
words1 = jieba.lcut(text1) # ['我', '是', '黑马', '人', '我']
words2 = jieba.lcut(text2) # ['你', '是', '黑马']
# 2. 合并去重(保持顺序)
vocab = list(set(chain(words1, words2)))
print("词汇表:", vocab)
# 3. 计算词频向量
def get_freq_vector(words, vocab):
return [words.count(v) for v in vocab]
vec1 = get_freq_vector(words1, vocab)
vec2 = get_freq_vector(words2, vocab)
print("文本1词频:", vec1) # [2, 0, 1, 1, 1]
print("文本2词频:", vec2) # [0, 1, 1, 1, 0]
# 4. 计算欧氏距离
vec1 = np.array(vec1)
vec2 = np.array(vec2)
euclidean_dist = np.sqrt(np.sum((vec1 - vec2) ** 2))
print("欧氏距离:", euclidean_dist) # sqrt(6) ≈ 2.449
# 5. 计算曼哈顿距离
manhattan_dist = np.sum(np.abs(vec1 - vec2))
print("曼哈顿距离:", manhattan_dist)
2-gram 相似度计算
# 2-gram 特征
def create_n_gram(input_list, n=2):
sliced = [input_list[i:] for i in range(n)]
return list(zip(*sliced))
bi_gram1 = create_n_gram(words1, 2)
bi_gram2 = create_n_gram(words2, 2)
# 合并去重
bi_vocab = list(set(chain(bi_gram1, bi_gram2)))
# 计算词频和距离
vec1 = [bi_gram1.count(v) for v in bi_vocab]
vec2 = [bi_gram2.count(v) for v in bi_vocab]
# ... 计算距离
6 重难点与易错提醒
- ❗重点:文本相似度计算流程——切词 → 统计词汇 → 计算词频 → 计算距离。
- ❗重点:欧氏距离 = 对应维度差值平方和开平方根(不是"勾股定理")。
- ⚠️易错:词汇表需去重,且一旦生成顺序不变。
- ⚠️易错:词频统计是每个句子中各词的出现次数,不是 0/1。
- 💡深入理解:添加 n-gram 可以捕捉词组合信息,提升相似度计算准确性。
7 课堂问答精选
Q1:如何计算两段文本的相似度?
A:①切词;②合并去重生成词汇表;③统计每个句子的词频向量;④计算欧氏距离等距离度量。
Q2:欧氏距离如何计算?
A:对应维度差值的平方和开平方根:$d = \sqrt{\sum (x_i - y_i)^2}$。例如 [2,0,1,1,1] 和 [0,1,1,1,0] 的欧氏距离为 $\sqrt{4+1+0+0+1} = \sqrt{6}$。
Q3:有无 n-gram 的区别是什么?
A:无 n-gram(1-gram)每个词独立,词频向量为单个词的计数。有 n-gram(如 2-gram)将相邻词组合作为特征,能捕捉词组合信息,提升相似度计算准确性。
Q4:除了欧氏距离,还有哪些距离度量?
A:曼哈顿距离(对应维度差值绝对值之和)、余弦相似度、杰卡德相似度等。
8 本课小结
- 文本相似度计算流程:切词 → 统计词汇 → 词频向量 → 计算距离。
- 欧氏距离:对应维度差值平方和开平方根。
- 无 n-gram:每个词独立,词频为单词计数。
- 有 n-gram:相邻词组合作为特征,捕捉词组合信息。
- 距离越小,相似度越高。
9 延伸思考
- 余弦相似度相比欧氏距离有什么优势?
- 如何处理词汇表过大导致的维度爆炸?