不同的数据集之间可能会存在关联,如一方变大,另一方也随之变大或者变小。例如,大多数图像的像素点是高度相关的。图像上的一个像素与其周围的像素点在很大概率上会类似。而”相关性“十分适合用于描述这种关系,描述相关性的统计量能够清晰地体现出数据特征之间有何关联。
在传统的机器学习领域,我们并不希望不同特征之间有很强的关联性,因为这样不但难以提供新的信息,还会对模型产生干扰。因此,在传统的机器学习领域,通常需要对获得的数据进行特征工程,其中一项目的就是去吃相关性的影响。然而在深度学习领域,由于深度网络本身会为输入学习新的特征表示,因此并不严格要求对输入去除相关性。
无论是传统的机器学习还是深度神经网络,作为描述和探索数据集的一环,对特征间相关性的分析都值得研究和理解。下面主要介绍两种相关性。
# 皮尔森相关性
皮尔森相关系数是位于-1到1的实数值r。它描述的是两个特征之间线性相关的强度。注意这里是线性相关性,如果两个特征之间存在非线性相关关系,则皮尔森相关系数不能够很好地体现出来。
r越接近1,则两个特征越正线性相关;r越接近-1,则两个特征越负线性相关;如果r接近0,则两个特征之间没有明显的线性关系。
皮尔森相关系数的计算公式如下:
其中X和Y是两个样本,E(.)为期望。对应的python计算代码如下:
```
import numpy as np
def pearson(x,y):
exy = (x*y).mean()
ex = x.mean()
ey = y.mean()
exx = (x*x).mean()
ex2 = x.mean()**2
eyy = (y*y).mean()
ey2 = y.mean()**2
return (exy - ex*ey) / ( np.sqrt(exx - ex2) * np.sqrt(eyy - ey2) )
```
下面我们用python实验来进一步学习,首先生成三组数据
```
import numpy as np
import matplotlib.pylab as plt
np.random.seed(8675309)
N = 100
x = np.linspace(0,1,N) + (np.random.random(N)-0.5)
y = np.random.random(N)*x
z = -0.1*np.random.random(N)*x
```
其中x为在0-1线段上叠加了噪声的结果,y和z分别为跟踪x的数据,其中y为在x的基础上叠加了在区间[0,1)的噪声,z为在y的基础上乘了一个负的系数。绘制图像如下:
之后我们调用前面定义的pearson函数和numpy中自带的计算相关性的函数来计算皮尔森相关系数:
```
print("pearson(x,y):", pearson(x,y))
print("pearson(x,z):", pearson(x,z))
print("pearson(y,z):", pearson(y,z))
print()
d = np.vstack((x,y,z))
print(np.corrcoef(d))
print()
```
结果如下:
从中可以看到上面定义的函数计算基本上是准确的,且x与y之间存在正相关性;x与z之间则存在较强的负相关性;相应地,y与z之间也存在一定的负相关性。
之后我们对比一下一张图片中不同行之间的相关性
```
from sklearn.datasets import load_sample_image
china = load_sample_image('china.jpg')
a = china[230,:,1].astype("float64")
b = china[231,:,1].astype("float64")
c = china[400,:,1].astype("float64")
d = np.random.random(640)
print("china(a,b): ", pearson(a,b))
print("china(a,c): ", pearson(a,c))
print("china(a,d): ", pearson(a,d))
print()
```
输出结果如下:
对比结果可以发现,图像的第230行与231行具有较强的正相关性,但是230行与400行之间的相关性较弱。这也证明了我们前面的判断。
# 斯皮尔曼相关性
皮尔森相关性衡量的数据之间的线性关系,而斯皮尔曼相关性则是衡量的输入之间是否存在单调的关联性。斯皮尔曼相关系数是及v特征取值的次序而非特征取值本身进行计算的,范围为[-1,1]。当两个特征中都不包含重复值时,其定义如下:
其中,n为样本容量,d表示随机变量X和Y中对应元素的次序差值。对应的计算代码如下:
```
#spearman
def spearman(x,y):
n = len(x)
t = x[np.argsort(x)]
rx = []
for i in range(n):
rx.append(np.where(x
== t)[0][0])
rx = np.array(rx, dtype="float64")
t = y[np.argsort(y)]
ry = []
for i in range(n):
ry.append(np.where(y == t)[0][0])
ry = np.array(ry, dtype="float64")
d = rx - ry
return 1.0 - (6.0/(n*(n*n-1)))*(d**2).sum()
```
我们首先调用np.argsort(x)对x排序为t,之后根据每个x判断其在t中的排序位置并将其存入rx。同理,对y做类似的处理,最后就是根据公式进行计算得到斯皮尔曼相关系数。
下面我们针对一条斜线和sigmoid函数分别计算他们之间的皮尔森和斯皮尔曼相关性来看看两者有什么区别。
```
a = np.linspace(-20,20,1000)
b = 1.0 / (1.0 + np.exp(-a))
print(pearson(a,b))
print(spearman(a,b))
```
输出结果如下:
结果表明皮尔森相关系数约为0.9,斯皮尔曼相关系数为1.0。这是因为随着a的增加,b也会增加,但是增加的幅度未必相同。而斯皮尔曼相关系数则能很好地捕捉这种非线性的相关关系,从而能够更好地帮助我们判断哪些特征应该丢弃,哪些特征应该保留。