在机器学习、深度学习等领域,距离(distance)、相似度(similarity) 作为最基本的概念经常出现,它们在 NLP、CV 等领域有重要的应用,而这些概念又大多源于数学领域的度量(metric)、测度(measure)等概念。例如,在机器学习中,通过计算两个用户对应向量之间的距离值大小,有时可以反映这两个用户的相似程度,这在基本的 KNN 算法和 K-means 算法中应用很广泛。
1. 曼哈顿距离
曼哈顿距离,又称出租车距离(出租车从起点不可能直接穿过城镇到终点,只能沿着街道拐角行驶到达终点),表示两个点在标准坐标系上的绝对轴距之和。如下图所示,定义曼哈顿距离的正式意义为固定直角坐标系上两点所形成的线段对轴产生的投影的距离总和。
图中红线代表曼哈顿距离,绿色代表欧氏距离(下文介绍,也称直线距离),而蓝色和黄色代表等价的曼哈顿距离。
具体的数学定义如下:(简单的说就是,每个矩阵中的对位元素相减,取绝对值,再累加)
曼哈顿距离的 Python 实现:
import numpy as np
vector1 = np.mat([[1, -2, 3], [1, 2, 3]])
vector2 = np.mat([[4, 5, -6], [4, 5, 6]])
print(np.sum(np.abs(vector1 - vector2))) # 28
2. 欧氏距离
欧氏距离其实就是 L2 范数,是最易于理解的一种距离计算方法,源自欧氏空间中两点间的距离公式,指在 m 维空间中两个点之间的真实距离,或者向量的自然长度(即该点到原点的距离)。
具体的数学定义如下:(简单的说就是,每个矩阵中的对位元素相减,再平方累加求和后开方)
二维空间:
三维空间:
n 维空间:
欧氏距离的 Python 实现:
import numpy as np
vector1 = np.mat([[1, -2, 3], [1, 2, 3]])
vector2 = np.mat([[4, 5, -6], [4, 5, 6]])
print(np.sqrt((vector1 - vector2) * (vector1 - vector2).T))
# [[11.78982612 1.73205081]
# [ 1.73205081 5.19615242]]
3. 切比雪夫距离
切比雪夫距离是向量空间中的一种度量,二个点之间的距离定义是其各坐标数值差绝对值的最大值。也称为 L∞ 度量,即无穷范数,具体的数学表达式如下:
二维空间:
n 维向量:
等价公式:
切比雪夫距离 Python 实现如下:
from numpy import *
vector1 = np.mat([[1, -2, 3], [1, 2, 3]])
vector2 = np.mat([[4, 5, -6], [4, 5, 6]])
print(np.max(np.abs(vector1 - vector2))) # 9
4. 闵可夫斯基距离
闵可夫斯基距离,简称闵氏距离,从严格意义上讲,闵可夫斯基距离不是一种距离,而是一组距离的定义。
设 n 维空间中有两点坐标 x, y,p为常数,闵式距离定义如下:
实际上:
当 p=1 时,得到绝对值距离,就是曼哈顿距离。
当 p=2 时,得到欧氏距离。
当 ,得到切比雪夫距离。
注意:
(1)闵氏距离与特征参数的量纲有关,有不同量纲的特征参数的闵氏距离常常是无意义的。
(2)闵氏距离没有考虑特征参数间的相关性,没有考虑各个特征参数的分布(期望,方差等)可能是不同的。
5. 汉明距离
汉明距离是信息论中的概念,两个等长字符串之间的汉明距离是两个字符串对应位置的不同字符的个数。换句话说,它就是将一个字符串变换成另外一个字符串所需要替换的字符个数。例如:
1011101 与 1001001 之间的汉明距离是 2。
2143896 与 2233796 之间的汉明距离是 3。
"toned" 与 "roses" 之间的汉明距离是 3。
在信息编码中,一般应使得编码间的汉明距离尽可能的小。
汉明距离的 Python 实现如下:
from numpy import *
vector1 = mat([1, 1, 0, 1, 0, 1, 0, 0, 1, 1])
vector2 = mat([0, 1, 1, 0, 0, 0, 1, 1, 1, 0])
nonzero_str = nonzero(vector1 - vector2)
print(nonzero_str) # (array([0, 0, 0, 0, 0, 0, 0], dtype=int64), array([0, 2, 3, 5, 6, 7, 9], dtype=int64))
# nonzero_str[1] 为对应位置的不同元素所在的索引
print(shape(nonzero_str[1])[0]) # 7
6. 夹角余弦
夹角余弦又称为余弦相似性,是通过计算两个向量的夹角余弦值来评估他们的相似度。余弦相似度将向量根据坐标值,绘制到向量空间中,如最常见的二维空间。
夹角余弦的取值范围为[-1,1],夹角余弦越大,表示两个向量之间的夹角越小。当两个向量的方向重合时,夹角余弦取最大值1;当两个向量的方向完全相反时,夹角余弦取最小值-1。
其具体其数学表达式如下:
两个向量间的余弦值可以通过使用欧几里得点积公式求出:
给定两个属性向量,A和B,其余弦相似性θ由点积和向量长度给出,如下所示:
这里的 分别代表向量A和B的各分量。
夹角余弦的Python实现:
from numpy import *
vector1 = mat([1,2,3])
vector2 = mat([4,5,6])
print dot(vector1,vector2)/(linalg.norm(vector1)*linalg.norm(vector2))
相比欧氏距离,余弦距离更加注重两个向量在方向上的差异。
借助三维坐标系来看下欧氏距离和余弦距离的区别:
从上图可以看出,欧氏距离衡量的是空间各点的绝对距离,跟各个点所在的位置坐标直接相关;而余弦距离衡量的是空间向量的夹角,更加体现在方向上的差异,而不是位置。
欧氏距离和余弦距离各自有不同的计算方式和衡量特征,因此它们适用于不同的数据分析模型:
- 欧氏距离能够体现个体数值特征的绝对差异,所以更多的用于需要从维度的数值大小中体现差异的分析,如使用用户行为指标分析用户价值的相似度或差异。
- 余弦距离更多的是从方向上区分差异,而对绝对的数值不敏感,更多的用于使用用户对内容评分来区分兴趣的相似度和差异,同时修正了用户间可能存在的度量标准不统一的问题(因为余弦距离对绝对数值不敏感)。
7. 杰卡德相似系数与杰卡德距离
杰卡德相似系数用于比较有限样本集之间的相似性与差异性。Jaccard系数值越大,样本相似度越高。
给定两个集合A,B,Jaccard 系数定义为A与B交集的大小与A与B并集的大小的比值,定义如下:
当集合A,B都为空时,J(A,B)定义为1。
与杰卡德相似系数相关的指标叫做杰卡德距离,用于描述集合之间的不相似度。Jaccard 距离越大,样本相似度越低。
公式定义如下:
其中对参差(symmetric difference) ,
杰卡德相似系数与杰卡德距离主要用于:
1. 比较文本相似度,用于文本查重与去重;
2. 计算对象间距离,用于数据聚类等。
3. 推荐系统。与传统相似性度量方法相比,杰卡德方法在基于物品的协同过滤系统中建立评价分析方法,完善了余弦相似性只考虑用户评分而忽略了其他信息量的弊端,特别适合于应用到稀疏度过高的数据中。
杰卡德距离的Python实现:
import numpy as np
import scipy.spatial.distance as dist
matv = np.array([[1, 1, 0, 1, 0, 1, 0, 0, 1], [0, 1, 1, 0, 0, 0, 1, 1, 1]])
print(matv)
# [[1 1 0 1 0 1 0 0 1]
# [0 1 1 0 0 0 1 1 1]]
ds = dist.pdist(matv, 'jaccard')
print(ds) # [0.75]
8. 马氏距离
马氏距离(Mahalanobis distance)是由印度统计学家马哈拉诺比斯(P. C. Mahalanobis)提出的,表示点与一个分布之间的距离。它是一种有效的计算两个未知样本集的相似度的方法。与欧氏距离不同的是,它考虑到各种特性之间的联系(例如:一条关于身高的信息会带来一条关于体重的信息,因为两者是有关联的),并且是尺度无关的,即独立于测量尺度。
假设有 P 个样本向量 x1 ~ xp,对于一个均值记为向量μ:
协方差矩阵记为 S :
则其中样本向量 x 到 u 的马氏距离表示为:
当 为单位阵时马氏距离可以简化为欧氏距离。
马氏距离的目的就是把方差归一化,使得特征之间的关系更加符合实际情况。
马氏距离的优点:
1. 马氏距离不受量纲的影响,两点之间的马氏距离与原始数据的测量单位无关。
2. 由标准化数据和中心化数据(即原始数据与均值之差)计算出的二点之间的马氏距离相同。
3. 马氏距离还可以排除变量之间的相关性的干扰。
马氏距离的缺点:
1. 夸大了变化微小的变量的作用。
2. 马氏距离的计算是不稳定的,主要取决于协方差矩阵(相同的样本在不同的总体样本集中计算得出的结果通常是不同的),这也是马氏距离与欧式距离的最大差异之处。
具体的 Python 实现:
import numpy as np
## 根据公式求解
# 由于马氏距离要求样本数要大于维数,否则无法求协方差矩阵。(此前提在生产中一般都满足)
x = np.random.random(5)
y = np.random.random(5)
# 构建二行五列的数组
X = np.vstack([x, y])
# 0. 对数组转置,转置后的结果为十行五列的数组,表示5个样本,每个样本2个维度
XT = X.T
# 1. 构建两个维度之间协方差矩阵
S = np.cov(X)
# 2. 协方差矩阵的逆矩阵
ST = np.linalg.inv(S)
# 3. 马氏距离计算两个样本之间的距离,此处共有5个样本,两两组合,共有10个距离(排列组合五个中取两个,C 5 2)
# 取当前样本数
samples_num = XT.shape[0]
result_dis = []
# 遍历每个样本,计算样本向量到协方差矩阵的距离
for i in range(0, samples_num):
for j in range(i + 1, samples_num):
# 对应公式中的 x-u
vector_difference = XT[i] - XT[j]
# 计算点积后开方,获取距离
dis = np.sqrt(np.dot(np.dot(vector_difference, ST), vector_difference.T))
result_dis.append(dis)
print(result_dis)
# [0.9005405956748153, 1.9326547704124346, 1.449159387238744, 2.5878730470795053, 2.5813933696678806, 0.9425573033749342, 2.326197750393925, 2.3802959458987134, 2.4656065983430806, 1.3958852659218475]
参考链接
https://www.cnblogs.com/denny402/p/7027954.html
https://blog.csdn.net/bluesliuf/article/details/88862918