在机器学习的过程中,经常会遇到需要考察具有某些特征的两个样本之间相似程度的情况。计相似程度的计算可以使用距离算法或是相关系数,直观来说,距离和相关系数的变化呈负相关,距离越小,相关系数越大,越相似,反之亦然。
接下来记录几种简单实用的距离算法和相关系数,以n维空间中的两个点 x(x1,x2,...,xn) , y(y1,y2,...,yn) 为例,并给出python实现。
距离算法
欧几里得距离(Euclidean Distance)
公式:
定义:欧几里得距离是欧氏空间中两点间“普通”(即直线)距离。没什么好说的。
#输入:x,y为维度相同的包含各特征值的list
#输出:输入值x,y的欧几里得距离
def dis_euc(x, y):
return sum([(x[i]-y[i])**2 for i in range(len(x))])**0.5
曼哈顿距离(Manhattan distance)
公式:
定义:曼哈顿距离的正式意义为L1-距离或城市区块距离,也就是在欧几里得空间的固定直角坐标系上两点所形成的线段对轴产生的投影的距离总和。
曼哈顿距离的命名原因是从规划为方型建筑区块的城市(如曼哈顿)间,最短的行车路径而来。一张图可以直观的说明它与欧氏距离的关系:
图中的绿线为欧几里得距离,即为两点之间的直线长度;红、黄、蓝三条线均为两点之间的城市区块路径,三条线的长度相等,即为曼哈顿距离。
#输入:x,y为维度相同的包含各特征值的list
#输出:输入值x,y的欧几里得距离
def dis_man(x, y):
return sum([abs(x[i]-y[i]) for i in range(len(x)])
明可夫斯基距离(minkowski distance)
公式:
定义:明氏距离是欧氏空间中的一种测度,被看做是欧氏距离和曼哈顿距离的一种推广。
p取1或2时的明氏距离是最为常用的,p=2即为欧氏距离,而p=1时则为曼哈顿距离。当p取无穷时的极限情况下,可以得到切比雪夫距离。
#输入:x,y为维度相同的包含各特征值的list,p为int
#输出:输入值x,y的明可夫斯基距离
def dis_man(x, y, p):
return sum([(x[i]-y[i])**p for i in range(len(x)])**(1/p)
这样求出来的距离值,越相似的样本,距离值越小,完全一致则为0,相差越大则值越大。但在统计学中,用相关系数来考察两个样本的相关程度时有如下的约定:
当相关系数为0时,X和Y两变量无关系。
当X的值增大(减小),Y值增大(减小),两个变量为正相关,相关系数在0.00与1.00之间。
当X的值增大(减小),Y值减小(增大),两个变量为负相关,相关系数在-1.00与0.00之间。
相关性 | 负 | 正 |
---|---|---|
无 | −0.09 to 0.0 | 0.0 to 0.09 |
弱 | −0.3 to −0.1 | 0.1 to 0.3 |
中 | −0.5 to −0.3 | 0.3 to 0.5 |
强 | −1.0 to −0.5 | 0.5 to 1.0 |
类比于上述约定,我们也可以将欧几里得距离求出的值转化为类似相关系数的形式,Python实现如下:
#输入:x,y为维度相同的包含各特征值的list
#输出:输入值x,y的欧几里得距离评价
def sim_distance(x, y):
dist = sum([(x[i]-y[i])**2 for i in range(len(x))])**0.5
return 1 / (1+dist)
欧氏距离(或者说所有的明氏距离)都有两个明显的缺点:
没有考虑到各维度分量数值分布的差别,数量级小的维度在数量级大的维度面前体现不出其差异性。
对于各维度的分量一视同仁,均将其视作单纯的数值进行计算,没有考虑到其代表的单位或是方向;
举两个栗子:
栗子一:
Metacritic 是一家专门的对影视、游戏、音乐做评分的网站,他的评分有两套体系:1、Metascore:网站综合各权威媒体评论文章做出的打分,数值从1-100; 2、Userscore:网站用户评分的均值,数值从1-10。以两个最近上市的游戏为例:Watch Dogs 2的Metascore为74,Userscore为7.7;Planet Coaster的Metascore为84,Userscore为8.1。现在要考察媒体评分和用户评价之间的差别,可以得到两个样本点M(74, 84),U(7.7, 8.1)。直观的来看不难发现其实两套评价体系对这两款游戏的评价是差不多的,但如果去计算M和U的欧氏距离,dist_euc(M, U)=100.78,大了去了!
那么该如何解决数量级大的维度对结果具有绝对影响呢?很自然的想法就是对每个维度进行标准化,使每个维度差值对结果的贡献(或者说权重)相差不多,这就是标准化欧式距离:
标准化欧几里得距离(Standardized Euclidean Distance)
公式:
定义:对每个维度做标准化之后再求欧式距离,如果将标准差的倒数看成一个权重,也可称之为加权欧氏距离(Weighted Euclidean distance)。
#输入:x,y为维度相同的包含各特征值的list
#输出:输入值x,y的标准化欧式距离
def dis_seuc(x, y):
sd = [(((x[i]-(x[i]+y[i])/2)**2 + (y[i]-(x[i]+y[i])/2)**2 )/ 2)**0.5 for i in range(len(x))]
return sum([((x[i]-y[i])/sd[i])**2 for i in range(len(x))])**0.2
再算一下, dist_seuc(M, U) = 1.5157165665103982,好像媒体和玩家的评分差别也没那么大 : )
栗子二:
假如时代广场街头采访路人关于数据挖掘的话题,得到了下面三个回答:
1. A: “Yeah I like datamining, it’s so cool~”
2. B: “Datamining? Never heard of it.”
3. C: “I’m learning datamining. Datamining is more than just cool, it’s the coolest thing ever.”
对三个字符串进行分词、词干提取、去除stopwords后,得到三个list,取”like”, “datamining”, “never”, “cool”为特征维度,以词频为权值可以得到下表:
name | like | datamining | never | cool |
---|---|---|---|---|
A | 1 | 1 | 0 | 1 |
B | 0 | 1 | 1 | 0 |
C | 0 | 2 | 0 | 2 |
也就是三个向量:
A = [1, 1, 0, 1]
B = [0, 1, 1, 0]
C = [0, 2, 0, 2]
直观的看句子我们能感觉到A和C的意思比较接近,A和B说的意思差远了。这时如果我们使用曼哈顿距离来计算,则dist(A, B) = 3, dist(A, C) = 3。嗯……好像哪里不太对劲吧。这就是因为距离函数无法表征向量方向上的变化,解决这个问题的方法,就是使用余弦相似度来衡量向量的距离。
相关系数
余弦相似度(Cosine similarity)
公式: