您当前的位置: 首页 >  机器学习

凌云时刻

暂无认证

  • 0浏览

    0关注

    1437博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

机器学习笔记(十三):主成分分析法(PCA)

凌云时刻 发布时间:2020-07-04 15:30:00 ,浏览量:0

凌云时刻 · 技术

导读:这篇笔记主要讲解机器学习中经常用到的降维算法PCA。

作者 | 计缘

来源 | 凌云时刻(微信号:linuxpk)

前言

在机器学习的实际使用中,我们都希望有足够多的样本数据,并且有足够的特征来训练我们的模型,所以高维特征数据是经常会用到的,但是高维特征数据同样会带来一些问题:

  • 机器学习算法收敛速度下降。

  • 特征难于分辨,很难第一时间认识某个特征代表的意义。

  • 会产生冗余特征,增加模型训练难度,比如说某一品牌型号汽车的特征数据,有从中国采集的,也有从国外采集的,那么就会产生公里/小时和英里/小时这种特征,但其实这两个特征代表的意义是一样的。

  • 无法通过可视化对训练数据进行综合分析。

以上问题都是高维特征数据带来的普遍问题,所以将高维特征数据降为低维特征数据就很重要了。这篇笔记主要讲解机器学习中经常用到的降维算法PCA。

PCA是英文Principle Component Analysis的缩写,既主成分分析法。该算法能从冗余特征中提取主要成分,在不太损失模型质量的情况下,提升了模型训练速度。

理解PCA算法降维的原理

我们从二维降一维的场景来理解PCA降维的原理。上面的图示显示了一个二维的特征坐标,横坐标是特征1,纵座标是特征2。图中的五个点就表示了五条特征数据。我们先来想一下最简单粗暴的降维方式就是丢弃掉其中一个特征。

如上图中显示,将特征2抛弃,这里大家先注意一下这五个点落在特征1轴上的间距。

或者如上图所示抛弃特征1,大家再注意一下这五个点落在特征2轴上的间距。能很明显的发现,抛弃特征2,落在特征1轴上的五个点之间间距比较大,并且分布均匀。而抛弃特征1,落在特征2轴上的五个点之间间距大多都比较小,并且分布不均匀。

就这两种简单粗暴的降维方式而言,哪种更好一些呢?这里我们先来看看方差的概念,方差描述的是随机数据的离散程度,也就是离期望值(不严谨的说,期望值等同于均值)的距离。所以方差越大,数据的离散程度越高,约分散,离均值的距离越大。方差越小,数据的离散程度越小,约聚合,离均值的距离约小。那么大家可以想想作为机器学习算法训练的样本数据,每组特征应该尽可能的全,在该特征的合理范围内尽可能的广,这样才能更高的代表真实性,也就是每组特征数据的方差应该尽可能的大才好。所以就上面两种情况来看,抛弃特征2的降维方式更好一些。

但是简单粗暴的完全丢弃一个特征自然是不合理的,这极大的影响了训练出模型的正确性。所以,按照方差最大的理论,我们应该再找到一个特征向量,使样本数据落在这个特征向量后的方差最大,那么这个特征向量代表的特征就是我们需要的降维后的特征。这就是支撑PCA算法的理论之一。

如上图所示,降维后的特征方差明显大于抛弃特征1或抛弃特征2后的方差。

我们在使用PCA之前首先需要对样本数据进行特征去均值化,也就是将每个特征的值减去该维特征的平均值。去均值化的目的是去除均值对数据变化的影响,也就是避免第一主成分收到均值的影响。

在第二篇笔记中,我们提到过方差,它的公式为:

   

‍‍那么当数据去均值化后,公式中的   就成了0,所以去均值后的方差为:

   

‍‍此时   就是降维后的特征,我们记‍‍为   ,那‍‍么降维后特征值的方差公式为:

   

‍‍‍‍因为在高维特征下,   和   都‍‍是向量,所以求方差时候应该对他们取模‍‍‍‍:

   

所以我们就是要求上面这个公式的最大值。那么首先如何求得‍‍这个   呢?我们来具体看一下:

 

如上图所示,蓝色向量是特征值原始维度的向量,   黑色向量就是我们要寻求的新维度的向量,绿色的点就是蓝色点在新维度上的投影点,红色向量的长度就是投影点的特征值。下面我们先来看看初高中数学中学过的知识。

首先向量的点乘有代数定义,也有几何定义。在几何定义下,两个向量的点乘为这两个向量的长度相乘,再乘以这两个向量夹角的余弦:

   

所以从上图来看:

   

因为我们需要的   ‍‍只是方向,所以它可以是一个方向向量,既长度为1,所以上面的公式就变为:

   

然后由三角函数可知,夹角的余弦等于邻边除以斜边。上图中   角的斜边就是   ,邻边就是   ,所以:

   

此时我们就知道了,我们要求得的红色向量的长度,即:

   

代入上面的方差公式为:

   

因为两个向量的点乘是一个标量,所以最终公式为:

   

那么我们的目标就是求   向量,使得上面的这个公式最大。上一篇笔记我们讲了用梯度下降法求函数极小值,那么这里我们就要用到梯度上升法求函数的极大值。

使用梯度上升法解决主成分分析问题

我们先将上面的公式展开(w是一个列向量):

   

既然是梯度上升,那么第一步当然是求梯度了,这和梯度下降是一样的,结合第五篇笔记中的梯度推导可得,上面公式的梯度为:

   

下面对上面的公式再进行向量化,这里我再推导一遍,首先我们将   看成是一个1行m列的行矩阵中的元素:

   

然后将它和一个m行n列的矩阵相乘:

    

 

因为X是一个m行n列矩阵,w是一个n行1列的矩阵,所以X乘w是一个m行1列的矩阵,上面我们将其转换为了1行m列的矩阵,所以上面的公式简写为   ,相乘后的结果是一个1行n列的矩阵:

   

那我们的梯度是一个n行1列的矩阵,所以将上面的矩阵再做转置:

   

所以最终主成分分析的梯度向量化后为:

   

 代码实现PCA梯度上升

首先我们构建样本数据,其中有100条样本数据,每条样本数据中有2个特征:

import numpy as np
import matplotlib.pyplot as plt

# 构建样本数据
# 构建一个100行2列的矩阵
X = np.empty((100, 2))
# 第一个特征为0到100的随机分布
X[:, 0] = np.random.uniform(0., 100., size=100)
# 第二个特征和第一个特征有一定线性关系,并且增加了0到10的正态分布的噪音
X[:, 1] = X[:, 0] * 0.75 + 3. + np.random.normal(0, 10, size=100)
# 将特征绘制出来
plt.scatter(X[:, 0], X[:, 1])
plt.show()

 

接下来根据上文中讲到,下一步要对样本数据的每一个特征进行均值归0操作,也就是每一列的元素减去这一列所有元素的均值:

 

def demean(X):
	# 对矩阵X在横轴方向上求均值,既求每一列的均值
	return X - np.mean(X, axis=0)

# 均值归0化后的样本数据
X_demean = demean(X)

plt.scatter(X_demean[:, 0], X_demean[:, 1])
plt.show()

可以看到均值归0化后,样本数据的分布形态是没有变化的,但是坐标轴往右上移动了,既0点现在在样本数据的中间。下面来定义目标函数和梯度函数:

 

# 目标函数
def f(X, w):
	return np.sum(X.dot(w)**2) / len(X)

# 梯度函数
def df(X, w):
	return X.T.dot(X.dot(x)) / 2 * len(X)

在上面的公式推导的过程中提到过,我们期望的向量ww是一个单位向量,所以在代码实现计算的时候需要将传入的初始向量ww和计算后的新ww向量都转换为单位向量(向量的单位向量为该向量除以该向量的模):

def direction(w):
	# np.linalg.norm(w)为求向量的模
	return w / np.linalg.norm(w)

接下来的梯度上升计算和梯度下降计算基本是大同小异的:

 

# 参数传入样本矩阵,初始向量,步长,查找循环次数,两次方差的最小差值
def gradient_ascent(X, initial_w, eta, n_iters=1e4, different=1e-8):
	# 将向量转换为单位向量
	w = direction(initial_w)
	i_iters = 0

	while i_iters < n_iters:
		gradient = df(X, w)
		last_w = w
		w = w + eta * gradient
		w = direction(w)
		if(abs(f(X, w) - f(X, last_w)) < different):
			break

		i_iters += 1

	return w

在使用我们定义的方法前,有两点需要注意的是,一点是在PCA中,初始向量ww不能为0,因为方差公式里ww在分子,所以如果为0了,那么方差始终为0,所以每次我们随机初始化一个不为0的向量。另外一点是在PCA中我们不对样本数据做归一化或标准化处理,因为一旦做了归一化处理,样本数据的方差就变成了1,自然求不到最大方差了。

 

# 初始化随机向量
initial_w = np.random.random(X.shape[1])
# 设置步长
eta = 0.01
# 梯度上升
w = gradient_ascent(X_demean, initial_w, eta)
# 绘制w向量
plt.scatter(X_demean[:, 0], X_demean[:, 1])
plt.plot([0, w[0]*30], [0, w[1]*30], color='r')
plt.show()

这样我们就求出了样本数据的第一个降维到的向量,我们称为样本的第一主成分。

 求数据的其他主成分

在上节中我们使用的样本数据是在二维空间的,求出的第一主成分其实可以看作是将坐标轴旋转后横轴或纵轴,我们降维后的数据其实是新的坐标轴上某个轴的分量,那么另外一个分量自然是降维到垂直于第一主成分向量的向量,既新坐标轴的另外一个轴。该向量是第一主成分向量的正交线。那么下面我们来看一下第一主成分的正交线如何求:

从上图可以看到,   就是第一主成分向量w的正交线,由向量的加减法可得:

   

因为上文推导过:

   

   

所以得:

这就相当于原始样本数据减去投影到第一主成分上的数据,我们对去掉第一主成分数据的样本数据再求第一主成分数据,那么就相当于在求原始样本数据的第二主成分了,以此类推就可以求得样本数据的n个主成分。

下面我们来用代码实现,首先我们算出样本数据在新坐标轴上的另一个分量,根据上面推导出的公式可得:

X2 = X - X.dot(w).reshape(-1, 1) * w

# 绘制X2及第一主成分向量w
plt.scatter(X2[:, 0], X2[:, 1])
plt.plot([0, w[0]*30], [0, w[1]*30], color='r')
plt.show()

首先可以看到,当样本数据去掉第一主成分数据后,另一个分量的数据其实就是在正交于第一主成分向量的轴上,所以所有点都在一条直线上。

然后将之前的gradient_ascent方法改个名称,因为它就是求第一主成分的方法,所以改名为first_component,然后求出X2的第一主成分:

def first_component(X, initial_w, eta, n_iters=1e4, different=1e-8):
	w = direction(initial_w)
	i_iters = 0

	while i_iters < n_iters:
		gradient = df(X, w)
		last_w = w
		w = w + eta * gradient
		w = direction(w)
		if(abs(f(X, w) - f(X, last_w)) < different):
			break

		i_iters += 1

	return w

w2 = first_component(X2, initial_w, eta)

由向量的正交定理知道,垂直的向量点乘结果为0,所以我们来验证一下w和w2之间的点乘结果:

  

w.dot(w2)
# 结果
3.2666630511712924e-10

可以看到结果基于趋近于0。

下面我们封装一个计算n个主成分的方法:

 

def first_n_component(n, X, eta=0.01, n_iters=1e4, different=1e-8):

	# 拷贝原始样本数据
	X_pca = X.copy()
	# 对样本数据进行均值归一化
	X_pca = demean(X_pca)
	# 存储结果数组
	res = []
	# 希望计算几个主成分就循环几次
	for i in range(n):
		# 每次随机一个初始向量
		initial_w = np.random.random(X_pca.shape[1])
		# 通过获取主成分方法计算出主成分向量
		w = first_component(X_pca, initial_w, eta)
		res.append(w)

		# 每次从原始样本数据中除去主成分数据
		X_pca = X_pca - X_pca.dot(w).reshape(-1, 1) * w

	return res

first_n_component(2, X)
# 结果
[array([ 0.77899988,  0.62702407]), array([-0.62702407,  0.77899988])]

  

 

END

往期精彩文章回顾

机器学习笔记(十二):随机梯度下降

机器学习笔记(十一):优化梯度公式

机器学习笔记(十):梯度下降

机器学习笔记(九):多元线性回归

机器学习笔记(八):线性回归算法的评测标准

机器学习笔记(七):线性回归

机器学习笔记(六):数据归一化

机器学习笔记(五):超参数

机器学习笔记(四):kNN算法

机器学习笔记(三):NumPy、Matplotlib、kNN算法

长按扫描二维码关注凌云时刻

每日收获前沿技术与科技洞见

关注
打赏
1663816507
查看更多评论
立即登录/注册

微信扫码登录

0.4163s