先看看二者定义,给定两个n维向量A,B:
A = ( a 1 , a 2 , … , a n ) A = (a_1, a_2, \ldots ,a_n) A=(a1,a2,…,an)
B = ( b 1 , b 2 , … , b n ) B = (b_1, b_2, \ldots ,b_n) B=(b1,b2,…,bn)
-
余弦相似度:
c o s ( θ ) = A ⋅ B ∥ A ∥ ⋅ ∥ B ∥ = ∑ i = 1 n a i × b i ∑ i = 1 n a i 2 × ∑ i = 1 n b i 2 cos(\theta) = {A \cdot B \over \Vert A \Vert \cdot \Vert B \Vert } = \frac{ \sum_{i=1}^n a_i \times b_i }{ \sqrt {\sum_{i=1}^n a_i^2} \times \sqrt {\sum_{i=1}^n b_i^2}} cos(θ)=∥A∥⋅∥B∥A⋅B=∑i=1nai2
×∑i=1nbi2
∑i=1nai×bi
-
皮尔逊相关系数:
p e a r s o n _ r = ∑ i = 1 n ( a i − a ˉ ) × ( b i − b ˉ ) ∑ i = 1 n ( a i − a ˉ ) 2 × ∑ i = 1 n ( b i − b ˉ ) 2 pearson\_r = \frac{ \sum_{i=1}^n (a_i - \bar a ) \times (b_i - \bar b) }{ \sqrt {\sum_{i=1}^n (a_i - \bar a )^2} \times \sqrt {\sum_{i=1}^n (b_i - \bar b)^2}} pearson_r=∑i=1n(ai−aˉ)2
×∑i=1n(bi−bˉ)2
∑i=1n(ai−aˉ)×(bi−bˉ)
其中:
a ˉ = 1 n ∑ i = 1 n a i , b ˉ = 1 n ∑ i = 1 n b i \bar a = {1 \over n} \sum_{i=1}^n a_i, \bar b = {1 \over n} \sum_{i=1}^n b_i aˉ=n1i=1∑nai,bˉ=n1i=1∑nbi
两式对比,可见皮尔逊相关系数的计算是先对向量每一分量减去分量均值,再求余弦相似度。这一操作称为中心化(将特征向量X根据 x ˉ \bar x xˉ 移动)。
这么做有什么好处呢?比如小明和小刚爱看电影,他们都看过《夏洛特烦恼》、《羞羞的铁拳》和《西虹市首富》,两个人也都在豆瓣上给这几部电影打了分,分值如下表:
夏洛特烦恼 | 羞羞的铁拳 | 西虹市首富 | |
---|---|---|---|
小明 | 9.6 | 7.5 | 5.8 |
小刚 | 8.3 | 5.6 | 2.4 |
如果豆瓣的程序员小哥哥想评判小明和小刚的兴趣是否相似(以此来给他们推荐类似的电影或投放相同广告),先初始化两个特征向量:
import numpy as np
a_scores = np.array([9.6, 7.5, 5.8])
b_scores = np.array([8.3, 5.6, 2.4])
试下余弦相似度:
from sklearn.metrics.pairwise import cosine_similarity
cosine_similarity([a_scores], [b_scores])
output:
array([[0.9760924]])
两个人的余弦相似度是0.9760924,还不错,再试试皮尔逊相关系数:
from scipy.stats import pearsonr
pearsonr(a_scores, b_scores)[0]
output:
0.9940012359465
效果更好!
余弦相似度除了可以比较样本间相似度,也可用于判断特征的重要性,网上比较多的例子即是评判sklearn库的iris(鸢尾花)数据集特征,在此一起总结下。
加载数据集:
from scipy.stats import pearsonr
from sklearn.datasets import load_iris
# 加载数据集
iris = load_iris()
看数据前10行,每行包含鸢尾花的4个特征(花萼、花瓣的长宽):
iris.data[:10]
output:
array([[5.1, 3.5, 1.4, 0.2],
[4.9, 3. , 1.4, 0.2],
[4.7, 3.2, 1.3, 0.2],
[4.6, 3.1, 1.5, 0.2],
[5. , 3.6, 1.4, 0.2],
[5.4, 3.9, 1.7, 0.4],
[4.6, 3.4, 1.4, 0.3],
[5. , 3.4, 1.5, 0.2],
[4.4, 2.9, 1.4, 0.2],
[4.9, 3.1, 1.5, 0.1]])
看看label前10行,都是1个品种:
iris.target[:10]
output:
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
每个样本有4维特征,通过计算各特征与label的皮尔逊相关系数,即可得到各特征的重要性,所以需要依次取出iris.data的每一列特征,怎么取呢?sklearn.datasets中的数据集已经帮我们处理好了。iris是sklearn.utils.Bunch类的实例,其中iris.data.T就是iris.data的转置,看看其前10行(由于样本过多,这里只显示10个):
iris.data.T[:, :10]
output:
array([[5.1, 4.9, 4.7, 4.6, 5. , 5.4, 4.6, 5. , 4.4, 4.9],
[3.5, 3. , 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1],
[1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4, 1.5],
[0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1]])
下面就可以计算特征重要性了:
from scipy.stats import pearsonr
for feature in iris.data.T:
# 计算皮尔逊相关系数
print(pearsonr(feature, iris.target)[0])
output:
0.7825612318100814
-0.4194462002600275
0.9490425448523336
0.9564638238016173
不过要注意的是,皮尔逊相关系数只对线性相关有效,并不是所有衡量特征重要性都可以这么用,需要因地制宜。