虽然向量 (vector)加法、减法和标量乘法改变了向量的位置或尺度,但点积 (也称作标量积或内积)提供了一种“乘法”方式,将两个向量相乘,得到一个单一的标量。这个标量描述了两个向量方向之间的关联,使其成为测量相似度以及理解特征空间中几何关联的重要方法。
定义与计算
对于R n \mathbb{R}^n R n 中的两个向量 (vector)a = [ a 1 , a 2 , . . . , a n ] a = [a_1, a_2, ..., a_n] a = [ a 1 , a 2 , ... , a n ] 和b = [ b 1 , b 2 , . . . , b n ] b = [b_1, b_2, ..., b_n] b = [ b 1 , b 2 , ... , b n ] ,它们的点积,表示为a ⋅ b a \cdot b a ⋅ b 或a T b a^T b a T b ,通过对应元素相乘并求和得到:
a ⋅ b = ∑ i = 1 n a i b i = a 1 b 1 + a 2 b 2 + ⋯ + a n b n a \cdot b = \sum_{i=1}^{n} a_i b_i = a_1 b_1 + a_2 b_2 + \dots + a_n b_n a ⋅ b = ∑ i = 1 n a i b i = a 1 b 1 + a 2 b 2 + ⋯ + a n b n
结果总是一个标量,而非向量。
例子:
设 a = [ 1 , 2 , 3 ] a = [1, 2, 3] a = [ 1 , 2 , 3 ] 且 b = [ − 2 , 0 , 4 ] b = [-2, 0, 4] b = [ − 2 , 0 , 4 ] 。
它们的点积为:
a ⋅ b = ( 1 × − 2 ) + ( 2 × 0 ) + ( 3 × 4 ) = − 2 + 0 + 12 = 10 a \cdot b = (1 \times -2) + (2 \times 0) + (3 \times 4) = -2 + 0 + 12 = 10 a ⋅ b = ( 1 × − 2 ) + ( 2 × 0 ) + ( 3 × 4 ) = − 2 + 0 + 12 = 10
在Python中使用NumPy,可以通过numpy.dot函数、NumPy数组的dot方法或@运算符(Python 3.5+)有效计算点积。
import numpy as np
a = np.array([1, 2, 3])
b = np.array([-2, 0, 4])
# 方法 1: np.dot()
dot_product_1 = np.dot(a, b)
# 方法 2: .dot() 方法
dot_product_2 = a.dot(b)
# 方法 3: @ 运算符
dot_product_3 = a @ b
print(f"使用 np.dot 的点积: {dot_product_1}")
print(f"使用 .dot 方法的点积: {dot_product_2}")
print(f"使用 @ 运算符的点积: {dot_product_3}")
# 预期输出:所有方法均为 10
几何解释:向量 (vector)间的夹角
点积有一个强大的几何解释,它与两个向量(当它们首尾相接时)之间的夹角θ \theta θ 相关:
a ⋅ b = ∣ ∣ a ∣ ∣ 2 ∣ ∣ b ∣ ∣ 2 cos ( θ ) a \cdot b = ||a||_2 ||b||_2 \cos(\theta) a ⋅ b = ∣∣ a ∣ ∣ 2 ∣∣ b ∣ ∣ 2 cos ( θ )
这里,∣ ∣ a ∣ ∣ 2 ||a||_2 ∣∣ a ∣ ∣ 2 和∣ ∣ b ∣ ∣ 2 ||b||_2 ∣∣ b ∣ ∣ 2 是向量a a a 和b b b 的欧几里得 (L 2 L_2 L 2 ) 范数(模长)。此公式将代数计算(∑ a i b i \sum a_i b_i ∑ a i b i )与几何(长度和向量间的夹角)联系起来。
我们可以重新排列此公式来求夹角:
cos ( θ ) = a ⋅ b ∣ ∣ a ∣ ∣ 2 ∣ ∣ b ∣ ∣ 2 \cos(\theta) = \frac{a \cdot b}{||a||_2 ||b||_2} cos ( θ ) = ∣∣ a ∣ ∣ 2 ∣∣ b ∣ ∣ 2 a ⋅ b
θ = arccos ( a ⋅ b ∣ ∣ a ∣ ∣ 2 ∣ ∣ b ∣ ∣ 2 ) \theta = \arccos\left(\frac{a \cdot b}{||a||_2 ||b||_2}\right) θ = arccos ( ∣∣ a ∣ ∣ 2 ∣∣ b ∣ ∣ 2 a ⋅ b )
点积的符号描述了夹角θ \theta θ :
a ⋅ b > 0 a \cdot b > 0 a ⋅ b > 0 : cos ( θ ) > 0 \cos(\theta) > 0 cos ( θ ) > 0 ,所以0 ∘ ≤ θ < 90 ∘ 0^\circ \le \theta < 90^\circ 0 ∘ ≤ θ < 9 0 ∘ (锐角)。向量大致指向相同方向。
a ⋅ b = 0 a \cdot b = 0 a ⋅ b = 0 : cos ( θ ) = 0 \cos(\theta) = 0 cos ( θ ) = 0 ,所以θ = 90 ∘ \theta = 90^\circ θ = 9 0 ∘ 。向量是正交的 (垂直的)。正交性是线性代数和机器学习 (machine learning)中的一个重要性质,常表示独立性或分离。
a ⋅ b < 0 a \cdot b < 0 a ⋅ b < 0 : cos ( θ ) < 0 \cos(\theta) < 0 cos ( θ ) < 0 ,所以90 ∘ < θ ≤ 180 ∘ 90^\circ < \theta \le 180^\circ 9 0 ∘ < θ ≤ 18 0 ∘ (钝角)。向量大致指向相反方向。
测量相似度:余弦相似度
几何解释直接引出了机器学习 (machine learning)中向量 (vector)间相似度的一个常用度量:余弦相似度 。它测量了两个非零向量间夹角的余弦值。
余弦相似度 ( a , b ) = cos ( θ ) = a ⋅ b ∣ ∣ a ∣ ∣ 2 ∣ ∣ b ∣ ∣ 2 \text{余弦相似度}(a, b) = \cos(\theta) = \frac{a \cdot b}{||a||_2 ||b||_2} 余弦相似度 ( a , b ) = cos ( θ ) = ∣∣ a ∣ ∣ 2 ∣∣ b ∣ ∣ 2 a ⋅ b
余弦相似度的取值范围是 -1 到 1:
1: 向量指向完全相同的方向(最大相似度)。
0: 向量正交(无方向相似度)。
-1: 向量指向相反方向(方向上的最大不相似度)。
这种度量广泛应用于自然语言处理(NLP)等领域,以比较文档嵌入 (embedding)或词向量,以及在推荐系统中查找相似用户或物品,因为它侧重于向量的朝向(方向)而非其模长。例如,两篇讨论相同主题的文档,它们的向量表示可能指向相似方向,即便其中一篇文档比另一篇长得多(向量模长更大)。
import numpy as np
# 例子:文档向量(例如,词频)
doc1 = np.array([2, 1, 0, 3]) # 词 A, B, C, D 的计数
doc2 = np.array([4, 2, 1, 6]) # 相似主题,更长的文档
doc3 = np.array([0, 0, 5, 1]) # 不同主题
def cosine_similarity(vec1, vec2):
dot_prod = vec1 @ vec2
norm1 = np.linalg.norm(vec1) # 默认是 L2 范数
norm2 = np.linalg.norm(vec2)
if norm1 == 0 or norm2 == 0:
return 0 # 如果向量为零,避免除以零
return dot_prod / (norm1 * norm2)
sim_1_2 = cosine_similarity(doc1, doc2)
sim_1_3 = cosine_similarity(doc1, doc3)
print(f"doc1 和 doc2 之间的相似度: {sim_1_2:.4f}") # 应该接近 1
print(f"doc1 和 doc3 之间的相似度: {sim_1_3:.4f}") # 应该低得多
# 预期输出(约):
# doc1 和 doc2 之间的相似度: 0.9990
# doc1 和 doc3 之间的相似度: 0.1705
向量 (vector)投影
点积的另一个主要用途是计算一个向量在另一个向量上的投影 。想象将光源垂直照射到向量b b b 上;向量a a a 在包含b b b 的线上投下的影子就是a a a 在b b b 上的向量投影。这个投影描述了向量a a a 在向量b b b 方向上的“量”。
向量a a a 在向量b b b 上的投影。紫色虚线向量proj_b(a)是向量a a a 沿向量b b b 方向的分量。
有两个相关量:
标量投影: 这是向量投影的长度 。它是“影子”的带符号模长。从三角学 (∣ ∣ a ∣ ∣ 2 cos ( θ ) ||a||_2 \cos(\theta) ∣∣ a ∣ ∣ 2 cos ( θ ) ) 和点积公式 (a ⋅ b = ∣ ∣ a ∣ ∣ 2 ∣ ∣ b ∣ ∣ 2 cos ( θ ) a \cdot b = ||a||_2 ||b||_2 \cos(\theta) a ⋅ b = ∣∣ a ∣ ∣ 2 ∣∣ b ∣ ∣ 2 cos ( θ ) ) 中,我们得到:
向量 a 在 b 上的标量投影 = ∣ ∣ a ∣ ∣ 2 cos ( θ ) = a ⋅ b ∣ ∣ b ∣ ∣ 2 \text{向量 } a \text{ 在 } b \text{ 上的标量投影} = ||a||_2 \cos(\theta) = \frac{a \cdot b}{||b||_2} 向量 a 在 b 上的标量投影 = ∣∣ a ∣ ∣ 2 cos ( θ ) = ∣∣ b ∣ ∣ 2 a ⋅ b
符号指示投影方向与b b b 方向相同(+)或相反(-)。
向量投影: 这是表示投影的实际向量 。为了得到这个向量,我们将标量投影(长度)乘以b b b 方向的单位向量。b b b 方向上的单位向量是b ∣ ∣ b ∣ ∣ 2 \frac{b}{||b||_2} ∣∣ b ∣ ∣ 2 b 。
proj b ( a ) = ( 标量投影 ) × ( 向量 b 的单位向量 ) \text{proj}_b(a) = (\text{标量投影}) \times (\text{向量 } b \text{ 的单位向量}) proj b ( a ) = ( 标量投影 ) × ( 向量 b 的单位向量 )
proj b ( a ) = ( a ⋅ b ∣ ∣ b ∣ ∣ 2 ) b ∣ ∣ b ∣ ∣ 2 = a ⋅ b ∣ ∣ b ∣ ∣ 2 2 b \text{proj}_b(a) = \left( \frac{a \cdot b}{||b||_2} \right) \frac{b}{||b||_2} = \frac{a \cdot b}{||b||_2^2} b proj b ( a ) = ( ∣∣ b ∣ ∣ 2 a ⋅ b ) ∣∣ b ∣ ∣ 2 b = ∣∣ b ∣ ∣ 2 2 a ⋅ b b
由于∣ ∣ b ∣ ∣ 2 2 = b ⋅ b ||b||_2^2 = b \cdot b ∣∣ b ∣ ∣ 2 2 = b ⋅ b ,一个常用的替代形式是:
proj b ( a ) = ( a ⋅ b b ⋅ b ) b \text{proj}_b(a) = \left( \frac{a \cdot b}{b \cdot b} \right) b proj b ( a ) = ( b ⋅ b a ⋅ b ) b
投影在需要将向量分解为分量的算法中非常有用,例如用于生成正交基的Gram-Schmidt正交化过程,并且它们被隐式应用于像主成分分析(PCA)这样的降维方法中,其中数据被投影到方差较大的方向上。
计算例子:
设 a = [ 2 , 3 ] a = [2, 3] a = [ 2 , 3 ] 且 b = [ 4 , 0 ] b = [4, 0] b = [ 4 , 0 ] 。
a ⋅ b = ( 2 ) ( 4 ) + ( 3 ) ( 0 ) = 8 a \cdot b = (2)(4) + (3)(0) = 8 a ⋅ b = ( 2 ) ( 4 ) + ( 3 ) ( 0 ) = 8
b ⋅ b = ( 4 ) ( 4 ) + ( 0 ) ( 0 ) = 16 b \cdot b = (4)(4) + (0)(0) = 16 b ⋅ b = ( 4 ) ( 4 ) + ( 0 ) ( 0 ) = 16
∣ ∣ b ∣ ∣ 2 = 16 = 4 ||b||_2 = \sqrt{16} = 4 ∣∣ b ∣ ∣ 2 = 16 = 4
向量a a a 在b b b 上的标量投影 = a ⋅ b ∣ ∣ b ∣ ∣ 2 = 8 4 = 2 = \frac{a \cdot b}{||b||_2} = \frac{8}{4} = 2 = ∣∣ b ∣ ∣ 2 a ⋅ b = 4 8 = 2 。
向量a a a 在b b b 上的向量投影 = ( a ⋅ b b ⋅ b ) b = ( 8 16 ) [ 4 , 0 ] = 1 2 [ 4 , 0 ] = [ 2 , 0 ] = \left( \frac{a \cdot b}{b \cdot b} \right) b = \left( \frac{8}{16} \right) [4, 0] = \frac{1}{2} [4, 0] = [2, 0] = ( b ⋅ b a ⋅ b ) b = ( 16 8 ) [ 4 , 0 ] = 2 1 [ 4 , 0 ] = [ 2 , 0 ] 。
这直观上讲得通:向量 a = [ 2 , 3 ] a=[2, 3] a = [ 2 , 3 ] 在 x 轴方向上(即 b = [ 4 , 0 ] b=[4, 0] b = [ 4 , 0 ] 的方向)有一个长度为 2 的分量。
import numpy as np
a = np.array([2, 3])
b = np.array([4, 0])
# 标量投影
scalar_proj = (a @ b) / np.linalg.norm(b)
# 向量投影
vector_proj = ( (a @ b) / (b @ b) ) * b
# 使用标量投影计算向量投影的替代方法:
# unit_b = b / np.linalg.norm(b)
# vector_proj_alt = scalar_proj * unit_b
print(f"向量 a: {a}")
print(f"向量 b: {b}")
print(f"向量 a 在 b 上的标量投影: {scalar_proj:.4f}")
print(f"向量 a 在 b 上的向量投影: {vector_proj}")
# 预期输出:
# 向量 a 在 b 上的标量投影: 2.0000
# 向量 a 在 b 上的向量投影: [2. 0.]
点积和投影是非常实用的方法。它们使我们能够从简单的向量运算转向分析向量间的几何关联、测量相似度以及将向量分解为有特定意义的分量,所有这些都是在机器学习 (machine learning)中处理数据时的常见操作。