1. 为什么使用余弦值相似度?
- 空间维度上两个点之间相似定义
- 在空间维度上两个点之间是有夹角和方向,夹角范围在 [0,180]
- 两个点越相似,方向越接近相同方向,夹角应该越小,越接近0度;
- 两个点意思相反,方向越接近相反方向,夹角应该越大,越接近180度;
- 两个点不相似,方向垂直,夹角应该接近90度;
- 为什么使用余弦值不用正弦值?
- 根据上述相似定义,我们需要找到夹角范围在[0,180]单调性的函数
- 如下图,正弦值夹角范围在 [0,180],不是单调取值 【不符合】
- 余弦值夹角范围在 [0,180],具有单调性
- 夹角为0,应该越相似,夹角值越大(1)
- 夹角为90,应该无相关性,夹角值为0
- 夹角为180,应该意思相反,夹角值越小(-1)
- 总结
- 余弦单调性符合我们在空间维度位置的关系,所以使用余弦相似度 【自我理解】
2. 公式推导
2.1 三角函数余弦公式推导
c
o
s
(
θ
)
=
A
B
2
+
A
C
2
−
B
C
2
2
∗
A
B
∗
A
C
\begin{aligned} {cos}(\theta)&=\frac{{AB}^{2}+{AC}^{2}-{BC}^{2}}{2*AB*AC} \end{aligned}
cos(θ)=2∗AB∗ACAB2+AC2−BC2
- 余弦公式
c o s ( θ ) = A D A C \begin{aligned} {cos}(\theta)=\frac{AD}{AC} \end{aligned} cos(θ)=ACAD - 由勾股定理原理可得
A C 2 = A D 2 + C D 2 ( 1 ) B C 2 = B D 2 + C D 2 ( 2 ) \begin{aligned} {AC}^{2}={AD}^{2}+{CD}^{2}\quad\quad\quad(1)\\ {BC}^{2}={BD}^{2}+{CD}^{2}\quad\quad\quad(2) \end{aligned} AC2=AD2+CD2(1)BC2=BD2+CD2(2) - 公式1-公式2
A C 2 − B C 2 = A D 2 − B D 2 ( 3 ) \begin{aligned} {AC}^{2}-{BC}^{2}&={AD}^{2}-{BD}^{2}\quad\quad\quad(3)\\ \end{aligned} AC2−BC2=AD2−BD2(3) -
A
B
,
A
D
,
B
D
AB,AD,BD
AB,AD,BD之间的关系,
A
D
AD
AD是未知值(但余弦公式需要参数),
A
B
AB
AB是已知值,所以得到
B
D
BD
BD相关公式,把
B
D
BD
BD消掉
A B = A D + B D B D = A B − A D ( 4 ) \begin{aligned} AB&=AD+BD\\ BD&=AB-AD\quad\quad\quad(4) \end{aligned} ABBD=AD+BD=AB−AD(4) - 公式4带入公式3
A C 2 − B C 2 = A D 2 − ( A B − A D ) 2 A C 2 − B C 2 = A D 2 − A B 2 + 2 ∗ A B ∗ A D − A D 2 A C 2 − B C 2 = − A B 2 + 2 ∗ A B ∗ A D A D = A B 2 + A C 2 − B C 2 2 ∗ A B ( 5 ) \begin{aligned} {AC}^{2}-{BC}^{2}&={AD}^{2}-{(AB-AD)}^{2}\\ {AC}^{2}-{BC}^{2}&={AD}^{2}-{AB}^{2}+2*AB*AD-{AD}^{2}\\ {AC}^{2}-{BC}^{2}&=-{AB}^{2}+2*AB*AD\\ AD&=\frac{{AB}^{2}+{AC}^{2}-{BC}^{2}}{2*AB}\quad\quad\quad\quad\quad(5) \end{aligned} AC2−BC2AC2−BC2AC2−BC2AD=AD2−(AB−AD)2=AD2−AB2+2∗AB∗AD−AD2=−AB2+2∗AB∗AD=2∗ABAB2+AC2−BC2(5) - 公式5带入余弦公式
c o s ( θ ) = A D A C = A B 2 + A C 2 − B C 2 2 ∗ A B A C = A B 2 + A C 2 − B C 2 2 ∗ A B ∗ A C \begin{aligned} {cos}(\theta)&=\frac{AD}{AC}\\ &=\frac{\frac{{AB}^{2}+{AC}^{2}-{BC}^{2}}{2*AB}}{AC}\\ &=\frac{{AB}^{2}+{AC}^{2}-{BC}^{2}}{2*AB*AC} \end{aligned} cos(θ)=ACAD=AC2∗ABAB2+AC2−BC2=2∗AB∗ACAB2+AC2−BC2
2.2 三角函数向量余弦公式推导
c o s ( θ ) = a ⃗ ∗ b ⃗ ∥ a ∥ ∗ ∥ b ∥ \begin{aligned} {cos}(\theta)&=\frac{\vec{a}*\vec{b}}{\parallel a\parallel*\parallel b\parallel} \end{aligned} cos(θ)=∥a∥∗∥b∥a∗b
- 向量公式
c ⃗ = a ⃗ − b ⃗ ( 1 ) A C = ∥ b ⃗ ∥ ( 2 ) A B = ∥ a ⃗ ∥ ( 3 ) B C = ∥ c ⃗ ∥ ( 4 ) \begin{aligned} \vec{c}&=\vec{a}-\vec{b}\quad\quad(1)\\ AC &=\parallel \vec{b} \parallel\quad\quad(2)\\ AB &=\parallel \vec{a} \parallel\quad\quad(3)\\ BC &=\parallel \vec{c} \parallel\quad\quad(4)\\ \end{aligned} cACABBC=a−b(1)=∥b∥(2)=∥a∥(3)=∥c∥(4)
-
公式
1
,
2
,
3
,
4
公式1,2,3,4
公式1,2,3,4带入余弦公式
c o s ( θ ) = A B 2 + A C 2 − B C 2 2 ∗ A B ∗ A C c o s ( θ ) = ∥ a ⃗ ∥ 2 + ∥ b ⃗ ∥ 2 − ( ∥ a ⃗ − b ⃗ ∥ ) 2 2 ∗ ∥ a ⃗ ∥ ∗ ∥ b ⃗ ∥ c o s ( θ ) = ∥ a ⃗ ∥ 2 + ∥ b ⃗ ∥ 2 − ∥ a ⃗ ∥ 2 + 2 ∗ a ⃗ ∗ b ⃗ − ∥ b ⃗ ∥ 2 2 ∗ ∥ a ⃗ ∥ ∗ ∥ b ⃗ ∥ c o s ( θ ) = a ⃗ ∗ b ⃗ ∥ a ∥ ∗ ∥ b ∥ \begin{aligned} {cos}(\theta)&=\frac{{AB}^{2}+{AC}^{2}-{BC}^{2}}{2*AB*AC}\\ {cos}(\theta)&=\frac{{\parallel \vec{a} \parallel}^{2}+{\parallel \vec{b} \parallel}^{2}-({\parallel\vec{a}-\vec{b}\parallel)}^{2}}{2*\parallel \vec{a} \parallel*\parallel \vec{b} \parallel}\\ {cos}(\theta)&=\frac{{\parallel \vec{a} \parallel}^{2}+{\parallel \vec{b} \parallel}^{2}-{\parallel \vec{a} \parallel}^{2}+2*\vec{a}*\vec{b}-{\parallel \vec{b} \parallel}^{2}}{2*\parallel \vec{a} \parallel*\parallel \vec{b} \parallel}\\ {cos}(\theta)&=\frac{\vec{a}*\vec{b}}{\parallel a\parallel*\parallel b\parallel} \end{aligned} cos(θ)cos(θ)cos(θ)cos(θ)=2∗AB∗ACAB2+AC2−BC2=2∗∥a∥∗∥b∥∥a∥2+∥b∥2−(∥a−b∥)2=2∗∥a∥∗∥b∥∥a∥2+∥b∥2−∥a∥2+2∗a∗b−∥b∥2=∥a∥∗∥b∥a∗b
3. 余弦相似度代码实现
- 代码来自于书籍:深度学习进阶:自然语言处理
import numpy as np def preprocess(text): """ 语料库预处理 :param text:句子字符串 :return: corpus 是单词ID 列表 word_to_id:是单词到单词 ID 的字典 id_to_word 是单词 ID 到单词的字典 """ text = text.lower().replace('.', ' .') # 单词全为小写 words = text.split(' ') # 以空格分隔 word_to_id = {} id_to_word = {} for word in words: if word not in word_to_id: new_id = len(word_to_id) word_to_id[word] = new_id id_to_word[new_id] = word corpus = np.array([word_to_id[w] for w in words]) return corpus, word_to_id, id_to_word def create_co_matrix(corpus, vocab_size, window_size=1): """ 语料库生成共现矩阵 :param corpus:corpus 是单词 ID 列表 :param vocab_size:词汇个数 :param window_size:窗口大小 :return: 共现矩阵 """ corpus_size = len(corpus) co_matrix = np.zeros((vocab_size, vocab_size), dtype=np.int32) for idx, word_id in enumerate(corpus): for i in range(1, window_size + 1): left_idx = idx - i right_idx = idx + i if left_idx >= 0: left_word_id = corpus[left_idx] co_matrix[word_id, left_word_id] += 1 if right_idx < corpus_size: right_word_id = corpus[right_idx] co_matrix[word_id, right_word_id] += 1 return co_matrix def cos_similarity(x, y, eps=1e-8): """ 余弦相似度函数 :param x:x坐标值 :param y:y坐标值 :param eps:默认值为1e-8,防止分母为0 :return: 余弦相似度值 """ nx = x / (np.sqrt(np.sum(x ** 2)) + eps) ny = y / (np.sqrt(np.sum(y ** 2)) + eps) return np.dot(nx, ny) text = 'I say hello and You say goodbye.' corpus, word_to_id, id_to_word = preprocess(text) print("corpus为:",corpus) print("word_to_id为:",word_to_id) print("id_to_word为:",id_to_word) vocab_size=len(set(corpus)) C=create_co_matrix(corpus, vocab_size, window_size=1) print("共现矩阵为:",C) c0 = C[word_to_id['you']] # you的单词向量 c1 = C[word_to_id['i']] # i的单词向量 print('you和i的相似度为',cos_similarity(c0, c1))