主要内容
- 求数据的前N个主成分
- 高维数据向低维数据进行映射
降维 不是把某个维度删除不要了,而是一次次通过PCA找出当前数据的最佳维度所对应的向量w。
问题:
在之前我们操作了一大堆,不知到是在干什么,那么我们现在说一说PCA降维的目标是什么?
我们在之前的例子中数据集是只有两个特征的,也就是只有两列(二维),我们只需要降维到一维即可,那么找一次就能找出其最符合得向量w。
而对于实际操作中,数据不可能只有两列,是有多个维度的。那么当我们要降维到某个维度时,不可能一次就可以降到,我们只有在原始数据中第一次使用PCA降维找到最主要的主成分,然后整理数据,在第一次降维的基础上,找出第二个主成分,一次类推,直到降到我们想要的维度。
PCA降维的流程:
- 对数据归0操作
- 使用梯度上升法找出最佳w
- 修改数据(原有数据X - 数据在上一个主成分上的分量X(project))
- 重复2,3操作
1.求数据的前N个主成分
求出第一主成分以后,如何求出下一个主成分?
1.1 修改数据 X-->X`分析
首先数据进行改变,将数据在第一个主成分上的分量去掉,去掉的步骤以及图示如下:
- 第一个主成分上的分量:OA向量。
- OA向量的计算:先计算OA向量的模,由于OD是单位向量,也就是OB () OD = |OA|。然后|OA| () OD = OA。
- 最后。OB - OA = OC 。我们就将点B转为点C了。
在新的数据上求第一主成分w,对数据修改得到的X`,是X中的所有样本都去除了第一主成分上的分量得到的结果,求第二主成分,只要在新的数据上,重新求一下第一主成分。
1.2 代码实现
1.定义函数以及求第一主成分w,与之前一样。
import numpy as np
import matplotlib.pyplot as plt
X = np.empty((100, 2))
# 将X的第0列设置为0-100的随机小数
X[:,0] = np.random.uniform(0., 100., size=100)
# 第2列数据 = 0.75 * X[:,0] + 3 + noise
X[:,1] = 0.75 * X[:,0] + 3. + np.random.normal(0, 10., size=100)
# 归0
def demean(X):
return X - np.mean(X, axis=0)
X_mean = demean(X)
def f(X,w):
return np.sum(X.dot(w)**2) / len(X)
def df_math(X,w):
gradient = (X.T.dot(X.dot(w))) * 2 / len(X)
return gradient
def df_debug(X,w,epsilon=0.0001):
gradient = np.empty(len(w))
for i in range(len(w)):
w_1 = w.copy()
w_1[i] += epsilon
w_2 = w.copy()
w_2[1] -=epsilon
gradient[i] = (f(X,w_1) - f(X,w_2)) /(2 *epsilon)
return gradient
# 单位向量w
def direction(w):
return w/np.linalg.norm(w)
def gradient_ascent(df, X, initial_w, eta, n_iters = 1e4, epsilon=1e-8):
w = direction(initial_w)
i_iter = 0
while i_iter < n_iters:
gradient = df(X,w)
last = w
w = w + eta * gradient
w = direction(w)
if abs(f(X,w) - f(X,last)) < epsilon:
break
i_iter +=1
return w
initial_w = np.random.random(X.shape[1])
eta = 0.01
w = gradient_ascent(df_math,X, initial_w, eta)
print(w) # [0.79084381 0.61201803]
2.计算X`:
.X`的每一列计算公式:
使用for循环遍历赋值。
2. 使用向量的方式:
把上图中的i都去掉,变成了X - (X (*) w) * w
。
其中X为m*n的矩阵,w为一个含有n个元素的一维向量,要进行点乘,所以要将w转换为n行1列的二维矩阵,点乘结果为m行1列的列向量,之后乘以w,结果为m行n列的矩阵X(project)。
# 修改数据X--》X2
X2 = np.empty(X.shape) # 创建与X形状相同的X2
for i in range(len(X)): # 计算X2的每一项
X2[i] = X[i] - X[i].dot(w) * w
plt.scatter(X2[:,0], X2[:,1])
plt.show()
# 向量方式计算X2
X2 = X - X.dot(w.reshape(-1, 1)) * w
plt.scatter(X2[:,0], X2[:,1])
plt.show()
X`数据:
其中:
X_project就是的每个样本点在第一主成分向量上映射的点。
X3 = np.empty(X.shape)
for i in range(len(X)):
X3[i] = X[i].dot(w) * w
plt.scatter(X3[:,0], X3[:,1])
plt.show()
# 向量方式计算X_project
X_project = X.dot(w.reshape(-1, 1)) * w
plt.scatter(X2[:,0], X2[:,1])
plt.show()
X(project):原数据在其主成分上的映射点:
3. 对X`进行主成分分析
那么再次对X`进行主成分分析,也就是找出X`中所有样本点的主成分,X`中所有样本点如上上图所示,求对应的w
w2 = gradient_ascent(df_math,X2,initial_w,eta)
print(w2)
# [-0.640744 0.7677546]
plt.plot([0,w2[0]*100],[0,w2[1]*100],color='r')
plt.show()
X`对应w2:
这样的话哦我们呢就求出来2个主成分w和w2了,但是一次一次调用太麻烦了,所以我们定义一个函数来调用。
4.定义函数,循环调用
为了方便我们定义一个函数,在函数中循环调用梯度上升函数,实现求多个主成分。
# 梯度上升法
def gradient_ascent(df, X, initial_w, eta, n_iters = 1e4, epsilon=1e-8):
w = direction(initial_w)
i_iter = 0
while i_iter < n_iters:
gradient = df(X,w)
last = w
w = w + eta * gradient
w = direction(w)
if abs(f(X,w) - f(X,last)) < epsilon:
break
i_iter +=1
return w
def first_n_components(df,n ,X, initial_w, eta, n_iters = 1e4, epsilon=1e-8):
# 复制数据
X_pca = X.copy()
# 归0数据
X_pca = demean(X_pca)
res = []
for i in range(n):
w = gradient_ascent(df, X_pca, initial_w, eta,n_iters,epsilon)
res.append(w)
X_pca = X_pca - X_pca.dot(w.reshape(-1,1)) * w
return res
w_list = first_n_components(df_math ,2 , X ,initial_w ,eta)
print(w_list)
# [array([0.78231485, 0.6228832 ]), array([-0.62287792, 0.78231905])]
那么这样的话,我们想算几个w就算几个w了。
所有代码:
import numpy as np
X = np.empty((100, 2))
# 将X的第0列设置为0-100的随机小数
X[:,0] = np.random.uniform(0., 100., size=100)
# 第2列数据 = 0.75 * X[:,0] + 3 + noise
X[:,1] = 0.75 * X[:,0] + 3. + np.random.normal(0, 10., size=100)
# 归0
def demean(X):
return X - np.mean(X, axis=0)
# X_mean = demean(X)
def f(X,w):
return np.sum(X.dot(w)**2) / len(X)
def df_math(X,w):
gradient = (X.T.dot(X.dot(w))) * 2 / len(X)
return gradient
def df_debug(X,w,epsilon=0.0001):
gradient = np.empty(len(w))
for i in range(len(w)):
w_1 = w.copy()
w_1[i] += epsilon
w_2 = w.copy()
w_2[1] -=epsilon
gradient[i] = (f(X,w_1) - f(X,w_2)) /(2 *epsilon)
return gradient
# 单位向量w
def direction(w):
return w/np.linalg.norm(w)
def gradient_ascent(df, X, initial_w, eta, n_iters = 1e4, epsilon=1e-8):
w = direction(initial_w)
i_iter = 0
while i_iter < n_iters:
gradient = df(X,w)
last = w
w = w + eta * gradient
w = direction(w)
if abs(f(X,w) - f(X,last)) < epsilon:
break
i_iter +=1
return w
def first_n_components(df,n ,X, initial_w, eta, n_iters = 1e4, epsilon=1e-8):
# 复制数据
X_pca = X.copy()
# 归0数据
X_pca = demean(X_pca)
# 存放每次循环的w
res = []
for i in range(n):
# 调用梯度上升计算w
w = gradient_ascent(df, X_pca, initial_w, eta,n_iters,epsilon)
# 加入reslist中
res.append(w)
# 修改X_pca
X_pca = X_pca - X_pca.dot(w.reshape(-1,1)) * w
return res
initial_w = np.random.random(X.shape[1])
eta = 0.01
w_list = first_n_components(df_math ,3 , X ,initial_w ,eta)
print(w_list)
# [array([0.75222449, 0.65890691]), array([-0.65890421, 0.75222686])]
2. 高维数据向低维数据进行映射
对于一个数据集X来说,这个X
有m
行n
列,代表有m
个样本n
个特征,通过我们前面学习的主成分分析法,假设我们已经求出了针对这个数据来说的前k
个主成分,每一个主成分对应一个单位方向,用W来表示,W也是一个矩阵,他有k
行,代表我们求出的前K
个主成分,每一行有n
列,代表每一个主成分的坐标轴应该是有n
个元素的,也对应数据X特征有n
个。
我们的主成分分析法主要就是将数据从一个坐标系转化成了另外一个坐标系,原来这个坐标系有n
个维度,现在这个坐标系也应该有n
个维度,只不过对于转化的坐标系来说,我们取出来前k
个,这k
个方向更加重要。这k
个维度对X数据集的数据影响最大。
如何将我们的样本X从n维转化成k维呢,回忆们之前学到的,对于一个X样本,与一个W进行点乘,其实就是讲一个样本映射到了w这个坐标轴,得到的模。
如果讲这一个样本和这k个w分别做点乘,得到的就是这一个样本在这k个方向上做映射后每一个方向上的大小,这k个元素合在一起,就是一个样本在这k个维度上所对应的向量,就代表这一个样本映射到新的k个轴所代表的坐标系上相应的这个样本的大小。
X
的每个样本乘以W(k)
的每一行主成分W1--Wn
,(1*n (*) n*k = 1*k)
,那得到的k
个数组成的向量,就是样本1映射到Wk
这个坐标系上得到的k维的向量,由于k<n
,所以我们就完成了一个样本从n
维到k
维的映射,这个过程依次类推从样本1到样本m都这么做,我们就将m个样本都从N
维映射到了k
维-----其实我们就是做了一个乘法X·WT
(为什么是转置呢,因为我们是拿X的每一行去和W的每一行做点乘的,但是矩阵乘法规定是拿X的每一行和W的每一列做乘法)。
X(k)
中每一行都是每个样本通过降维后产生所对应的k维向量,该向量中的元素有7
个,因为我们通过主成分分析只取了前k个维度。
我们得到新的降维后的矩阵Xk以后,是可以通过和Wk想乘回复回来的,但是由于我们在降维的过程中丢失了一部分信息,这时及时回复回来也和原来的矩阵不一样了,但是这个从数据角度成立的。
2.1 代码实现 从高维数据向低维数据的映射
PCA 降维的基本原理:找到另外一个坐标系,这个坐标系每一个轴依次可以表达原来的样本他们的重要程度,也就是称为所有的主成分,我们取得前k个最重要的主成分,就可以将所有的样本映射到这k个轴上,获得一个低维的数据信息。
1.手工代码实现
class PCA:
def __init__(self,n_components):
# 主成分数量
self.n_components = n_components
# 主成分W
self.components = None
def fit(self,X,eta=0.01,n_iters=1e4,epsilon=1e-8):
def demean(X):
'''归0'''
return X - np.mean(X, axis=0)
def f(w,X):
'''效用函数f'''
return np.sum((X.dot(w)**2))/len(X)
def df_math(X, w):
'''公式法求梯度'''
gradient = (X.T.dot(X.dot(w))) * 2 / len(X)
return gradient
def df_debug(X, w, epsilon=0.0001):
'''debug求梯度'''
gradient = np.empty(len(w))
for i in range(len(w)):
w_1 = w.copy()
w_1[i] += epsilon
w_2 = w.copy()
w_2[1] -= epsilon
gradient[i] = (f(X, w_1) - f(X, w_2)) / (2 * epsilon)
return gradient
def direction(w):
"""计算单位向量w"""
return w / np.linalg.norm(w)
def first_componet(df,X, inital_w, eta, n_iters, epsilon):
'''梯度上升法'''
w = direction(inital_w)
cur_iter = 0
while cur_iter < n_iters:
gradient = df(X,w)
last_w = w
w = w + eta * gradient
## 注意1:每次求单位向量
w = direction(w)
if abs(f(w, X) - f(last_w, X)) < epsilon:
break
cur_iter = cur_iter + 1
return w
'''数据准备,参数设置'''
X_pca = demean(X)
self.components = np.empty((self.n_components,X_pca.shape[1]))
# 求多少个w就遍历多少次
for i in range(self.n_components):
init_w = np.random.random(X_pca.shape[1])
# 调用函数,返回w
w = first_componet(df_math,X_pca,init_w,eta,n_iters,epsilon)
# 将第i行的所有数据设为w
self.components[i,:] = w
# 计算下一个X_pca
X_pca = X_pca - X_pca.dot(w.reshape(-1,1)) * w
return self
def transform(self,X):
'''计算X数据集经过主成分分析后(降维后)的数据'''
return X.dot(self.components.T)
def inverse_rainsform(self,X):
'''将降维后的数据映射回原数据(有损失)'''
return X.dot(self.components)
def __repr__(self):
return f'PCA{self.n_components}'
使用数据测试:
X = np.empty((100, 2))
X[:,0] = np.random.uniform(0., 100., size=100)
X[:,1] = 0.75 * X[:,0] + 3. + np.random.normal(0, 10., size=100)
pca = PCA(1)
a = pca.fit(X)
print(a)
# PCA2
print(pca.components)
# [[ 0.77144557 0.63629532]
# [-0.6362932 0.77144731]]
X_reduce = pca.transform(X)
print(X_reduce.shape)
# (100, 1)
X_restore = pca.inverse_rainsform(X_reduce)
print(X_restore.shape)
# (100, 2)
import matplotlib.pyplot as plt
plt.scatter(X[:,0],X[:,1])
plt.scatter(X_restore[:,0],X_restore[:,1])
plt.show()
可以看出:
蓝色点为原数据X,橙色点为经过PCA降维后再映射回相同维度的数据点,两个数据是有差别的,所以,再降维后升维的过程中,会有数据损失。
2. sklearn中的PCA
1.使用sklearn中的PCA操作X
from sklearn.decomposition import PCA
import numpy as np
X = np.empty((100, 2))
X[:,0] = np.random.uniform(0., 100., size=100)
X[:,1] = 0.75 * X[:,0] + 3. + np.random.normal(0, 10., size=100)
# pca降维
pca = PCA(1)
pca.fit(X)
print(pca)
# PCA(n_components=1)
# W
print(pca.components_) # [[0.76636326 0.64240747]]
X_reduction = pca.transform(X)
print(X_reduction.shape)
# (100, 1)
X_restore = pca.inverse_transform(X_reduction)
print(X_restore.shape)
# (100, 2)
import matplotlib.pyplot as plt
plt.scatter(X[:,0],X[:,1])
plt.scatter(X_restore[:,0],X_restore[:,1])
plt.show()
2.使用真实数据集测试sklearn中的PCA
我们使用手写数字识别数据集继续宁测试,目的就是降维前后效率变快了没有以及正确率怎么样
?
from sklearn import datasets
'''1.导入数据集'''
digits = datasets.load_digits()
X = digits.data
y = digits.target
print(X.shape)
# (1797, 64)
'''2.分割数据集'''
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=666)
print(X_train.shape)
# (1347, 64)
'''3.先使用KNN算法训练测试一下'''
from sklearn.neighbors import KNeighborsClassifier
import time
knn = KNeighborsClassifier()
strat = time.time()
knn.fit(X_train,y_train)
end = time.time()
score = knn.score(X_test,y_test)
print(score)
# 0.9866666666666667
print(end - strat) # 0.014959573745727539
'''4.使用PCA对数据进行降维'''
from sklearn.decomposition import PCA
pca = PCA(2)
pca.fit(X_train)
print(pca)
# PCA(n_components=2)
# 将训练数据和测试数据降到2维
X_train_reduction = pca.transform(X_train)
X_test_reduction = pca.transform(X_test)
print(X_test_reduction.shape) # (450, 2)
print(X_train_reduction.shape) # (1347, 2)
'''5.使用降维后的数据进行KNN测试'''
start = time.time()
knn.fit(X_train_reduction,y_train)
end = time.time()
score = knn.score(X_test_reduction,y_test)
print(score) # 0.6066666666666667
print(end - start) # 0.0009965896606445312
由此可见:
在程序中我们把数据从64维降到2维后,效率变快了很多,降维之前使用KNN算法(KNN算法的效率最慢)的运行时间大约为15ms
,降维后运行时间大概1ms
,但是弊端时正确率从0.98
下降到了0.60
,因为我们通过PCA降维后只取了前两个维度,只通过这两个维度能使数据训练达到60%
的正确率,为了提高正确率,可以将维度增大,但随之而来的就是效率变慢。
3.主成分解释数据百分比
explained_variance_ratio
-解释方差的比例 。
0.14566817 代表第一个主成分可以解释14%的原数据。
0.13735469 代表第二个主成分可以解释13%的原数据。
两个主成分加起来可以解释百分之27的原数据,而其他的信息丢失了,可以使用explained_variance
这个参数来查看每个主成分所解释的原数据,来判断要取多少个主成分
'''查看每个主成分解释数据百分比'''
evr = pca.explained_variance_ratio_
print(evr)
# [0.14566817 0.13735469]
print(pca.explained_variance_)
# [175.90066519 165.86177754]
到这里我们说为了要求正确率和效率同时达到最好的效果,我们需要找到主成分个数的最佳值k。这k个主成分不仅要保证能解释足够多的数据,还要保证效率。
代码:
首先我们先求出所有主成分能解释数据的比率:
# 取与数据集维度相同的主成分,也就是主成分的最大值
pca1 = PCA(X.shape[1])
pca1.fit(X_train)
print(pca1.explained_variance_ratio_)
# [1.45668166e-01 1.37354688e-01 1.17777287e-01 8.49968861e-02
# 5.86018996e-02 5.11542945e-02 4.26605279e-02 3.60119663e-02
# 3.41105814e-02 3.05407804e-02 2.42337671e-02 2.28700570e-02
# 1.80304649e-02 1.79346003e-02 1.45798298e-02 1.42044841e-02
# 1.29961033e-02 1.26617002e-02 1.01728635e-02 9.09314698e-03
# 8.85220461e-03 7.73828332e-03 7.60516219e-03 7.11864860e-03
# 6.85977267e-03 5.76411920e-03 5.71688020e-03 5.08255707e-03
# 4.89020776e-03 4.34888085e-03 3.72917505e-03 3.57755036e-03
# 3.26989470e-03 3.14917937e-03 3.09269839e-03 2.87619649e-03
# 2.50362666e-03 2.25417403e-03 2.20030857e-03 1.98028746e-03
# 1.88195578e-03 1.52769283e-03 1.42823692e-03 1.38003340e-03
# 1.17572392e-03 1.07377463e-03 9.55152460e-04 9.00017642e-04
# 5.79162563e-04 3.82793717e-04 2.38328586e-04 8.40132221e-05
# 5.60545588e-05 5.48538930e-05 1.08077650e-05 4.01354717e-06
# 1.23186515e-06 1.05783059e-06 6.06659094e-07 5.86686040e-07
# 1.71368535e-33 7.44075955e-34 7.44075955e-34 7.15189459e-34]
这就是所有主成分所能解释数据集的比率,可以看到后百年数据能解释的数据很小,基本可以忽略了,但是该取多少个主成分最好呢?
我们可以通过画图来看一看:
绘制曲线观察取前i个主成分的时候,所能解释的原数据比例
x_data = [i for i in range(X_train.shape[1])]
y_data = [np.sum(pca1.explained_variance_ratio_[:i+1]) for i in range(X_train.shape[1])]
plt.plot(x_data,y_data)
plt.show()
可以看到该函数类似于对数函数的曲线,x轴越大,增长越慢。
我们希望sklearn中的PCA算法支持传入一个小于1的数来表示能解释多少比例的主成分。
例如想解释95%的数据,应该取多少个主成分?
pca2 = PCA(0.95)
pca2.fit(X_train)
print(pca2.n_components_)
# 28
取28个主成分,能够解释95%的数据。
将数据降维到28维后,使用KNN算法进行测试
X_train_reduction = pca2.transform(X_train)
X_test_reduction = pca2.transform(X_test)
knn = KNeighborsClassifier()
knn.fit(X_train_reduction,y_train)
score = knn.score(X_test_reduction,y_test)
print(score)
# 0.98
正确率还可以。
3.使用PCA对数据进行降维可视化
pca3 = PCA(2)
pca3.fit(X)
X_reduction = pca.transform(X)
print(X_reduction.shape)
# (1797, 2)
for i in range(10):
# 画出y = i时所有的点
plt.scatter(X_reduction[y == i,0],X_reduction[y == i,1])
plt.show()
4.MNIST
使用MNIST加强版手写数字的例子:
使用kNN
sklearn 封装的KNeighborsClassifier,在fit过程中如果数据集较大,会以树结构的过程进行存储,以加快knn的预测过程,但是会导致fit过程变慢 没有进行数据归一化,是因为这里的每个维度都标示的是每个像素点的亮度,他们的尺度是相同的,这个时候比较两个样本之间的距离是有意义的
import time
import numpy as np
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA
from sklearn.neighbors import KNeighborsClassifier
'''1.加载数据集'''
dataset = fetch_openml("mnist_784",version=1,cache=True)
# print(dataset.keys())
# dict_keys(['data', 'target', 'frame', 'categories', 'feature_names', 'target_names', 'DESCR', 'details', 'url'])
# print(dataset.data.shape)
# (70000, 784)
'''2.分割数据集'''
X_train,X_test,y_train,y_test = train_test_split(dataset.data,dataset.target)
# print(X_train.shape) # (52500, 784)
# print(y_train.shape) # (52500,)
# print(X_test.shape) # (17500, 784)
# print(y_test.shape) # (17500,)
'''3.转换数据集的数据类型-->float'''
X_train = X_train.astype(np.float)
y_train = y_train.astype(np.float)
X_test = X_test.astype(np.float)
y_test = y_test.astype(np.float)
'''4.使用KNN进行训练'''
knn = KNeighborsClassifier()
start = time.time()
knn.fit(X_train,y_train)
end = time.time()
score = knn.score(X_test,y_test)
print(score) #
print(end - start) # 时间很长,不做演示
'''5.pca降维'''
pca = PCA(0.9)
pca.fit(X_train)
X_train_reduction = pca.transform(X_train)
X_test_reduction = pca.transform(X_test)
'''6.再次使用KNN进行测试'''
knn1 = KNeighborsClassifier()
start = time.time()
knn1.fit(X_train_reduction,y_train)
end = time.time()
score = knn1.score(X_test_reduction,y_test)
print(score) # 0.9749142857142857
print(end - start)
# 1.4979944229125977ms
使用PCA进行降维后的数据集进行训练,不光时间变短了,准确度也变高了 这是因为PCA的过程中,不仅仅是进行了降维,还在降维的过程中将数据包含的噪音给消除了 这使得我们可以更加好的,更加准确的拿到我们数据集对应的特征,从而使得准确率大大提高
降维的过程可以理解成是去噪。
2.2 PCA降噪
1.生成数据,加上噪音
from sklearn import datasets
import numpy as np
from sklearn.decomposition import PCA
'''1.加载数据'''
digits = datasets.load_digits()
X = digits.data
y = digits.target
print(X.shape) # (1797, 64)
'''2.给数据集X 加入噪音'''
noisy_digits = X + np.random.normal(0, 4, size=X.shape)
2.取出适量样本,画图显示
'''3.取出X中的每个数字10个样本'''
# 取出数字为0的前10个样本
zero_digits = X[y==0,:][:10]
for num in range(1,10):
zero_digits = np.vstack([zero_digits,noisy_digits[y==num,:][:10]])
print(zero_digits.shape)
# (100, 64)
'''4.画图'''
import matplotlib.pyplot as plt
def plot_digits(data):
# 10行10列的子图,并且去除x,y的刻度,返回主图和子图(二维数组10*10)
fig, axes = plt.subplots(10, 10, figsize=(10, 10),
subplot_kw={'xticks':[], 'yticks':[]},
gridspec_kw=dict(hspace=0.1, wspace=0.1))
for i, ax in enumerate(axes.flat):
# 将传进来的每一个数据项转为8*8
ax.imshow(data[i].reshape(8, 8),
cmap='binary', interpolation='nearest',
# 0-16的范围内
clim=(0, 16))
plt.show()
plot_digits(zero_digits)
3.使用PCA降维(去噪)后,再显示
'''5.PCA降噪'''
pca = PCA(0.5)
pca.fit(noisy_digits)
print(pca.n_components_) # 12
zero_digits = X[y==0,:][:10]
for num in range(1,10):
zero_digits = np.vstack([zero_digits,noisy_digits[y==num,:][:10]])
# 将数据PCA降噪后再转回原来的维度
components = pca.transform(zero_digits)
filtered_digits = pca.inverse_transform(components)
plot_digits(filtered_digits)