天天看點

皮爾遜相關系數和餘弦相似度

先看看二者定義,給定兩個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​)

  1. 餘弦相似度:

    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=1n​ai2​

    ​×∑i=1n​bi2​

    ​∑i=1n​ai​×bi​​

  2. 皮爾遜相關系數:

    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ˉ=n1​i=1∑n​ai​,bˉ=n1​i=1∑n​bi​

兩式對比,可見皮爾遜相關系數的計算是先對向量每一分量減去分量均值,再求餘弦相似度。這一操作稱為中心化(将特征向量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
           

不過要注意的是,皮爾遜相關系數隻對線性相關有效,并不是所有衡量特征重要性都可以這麼用,需要因地制宜。

繼續閱讀