1、sklearn中的降维算法
PCA使用的信息量衡量指标,就是样本方差,又称可解释性方 差,方差越大,特征所带的信息量越多。
(1)PCA
我们希望能够找出一种办法来帮助我们衡量特征上所带的信息量,让我们在降维的过程中,能够即减少特征的数量,又保留大部分有效信息——将那些带有重复信息的特征合并,并删除那些带无效信息的特征等等——逐渐创造出能够代表原特征矩阵大部分信息的,特征更少的,新特征矩阵。因此,在降维中,PCA使用的信息量衡量指标,就是样本方差,又称可解释性方差,方差越大,特征所带的信息量越多。 有空可以补充一下这个知识点 将为过程的主要步骤如下:
(2)SVD
(3)思考
PCA和特征选择技术都是特征工程的一部分,它们有什么不同?
特征工程中有三种方式:特征提取,特征创造和特征选择。仔细观察上面的降维例子和上周我们讲解过的特征 选择,你发现有什么不同了吗?
特征选择是从已存在的特征中选取携带信息最多的,选完之后的特征依然具有可解释性,我们依然知道这个特 征在原数据的哪个位置,代表着原数据上的什么含义。
而PCA,是将已存在的特征进行压缩,降维完毕后的特征不是原本的特征矩阵中的任何一个特征,而是通过某 些方式组合起来的新特征。通常来说,在新的特征矩阵生成之前,我们无法知晓PCA都建立了怎样的新特征向 量,新特征矩阵生成之后也不具有可读性,我们无法判断新特征矩阵的特征是从原数据中的什么特征组合而 来,新特征虽然带有原始数据的信息,却已经不是原数据上代表着的含义了。以PCA为代表的降维算法因此是 特征创造(feature creation,或feature construction)的一种。
可以想见,PCA一般不适用于探索特征和标签之间的关系的模型(如线性回归),因为无法解释的新特征和标 签之间的关系不具有意义。在线性回归模型中,我们使用特征选择。
2、重要参数n_components
n_components是我们降维后需要的维度,即降维后需要保留的特征数量,降维流程中第二步里需要确认的k值, 一般输入[0, min(X.shape)]范围中的整数。K是一个需要我们人为去确认的超参数,并且我们设定的数字会影响到模型的表现。就达不到降维的效果,如果留下的特征太少,那新特征向量可能无法容纳原始数据集中的大部分信息,因此,n_components既不能太大也不能太小。那怎么办呢?
可以先从我们的降维目标说起:如果我们希望可视化一组数据来观察数据分布,我们往往将数据降到三维以下,很 多时候是二维,即n_components的取值为2。
迷你实例(鸢尾花降维可视化)
from sklearn.decomposition import PCA
from matplotlib import pyplot as plt
from sklearn.datasets import load_iris
import pandas as pd
import numpy as np
iris = load_iris()
x = iris.data
y = iris.target
pca = PCA(n_components=2)
pca = pca.fit(x)
result = pca.transform(x)
x_dr = PCA(n_components=2).fit_transform(x)
plt.figure()
plt.scatter(x_dr[y == 0, 0], x_dr[y == 0, 1], color='red', label=iris.target_names[0])
plt.scatter(x_dr[y == 1, 0], x_dr[y == 1, 1], color='black', label=iris.target_names[1])
plt.scatter(x_dr[y == 2, 0], x_dr[y == 2, 1], color='orange', label=iris.target_names[2])
plt.title('pca of IRIS dataset')
plt.legend()
plt.show()
colors = ['red', 'black', 'yellow']
for i in range(3):
plt.scatter(x_dr[y == i, 0], x_dr[y == i, 1], alpha=0.7, color=colors[i],label=iris.target_names[i])
plt.legend()
plt.title('PCA of IRIS dataset')
plt.show()
print(pca.explained_variance_ )
print(pca.explained_variance_ratio_)
print(pca.explained_variance_ratio_.sum())
pca_line = PCA().fit(x)
plt.plot([1, 2, 3, 4], np.cumsum(pca_line.explained_variance_ratio_))
plt.xticks([1, 2, 3, 4])
plt.xlabel('number of components after dimension reduction')
plt.ylabel('cumulative explained variance ratio')
plt.show()
最大似然估计自选超参数
pca = PCA(n_components='mle')
pca_mle = pca.fit(x)
x_mle = pca_mle.transform(x)
按信息量占比选超参数
pca_f = PCA(n_components=0.97, svd_solver='full')
pca_f.fit(x)
x_f = pca_f.transform(x)
print(pca_f.explained_variance_ratio_)
3、PCA中的SVD
重要参数svd_solver 与 random_state
参数svd_solver是在降维过程中,用来控制矩阵分解的一些细节的参数。有四种模式可选:“auto”, “full”, “arpack”,“randomized”,默认”auto"。
“auto”:基于X.shape和n_components的默认策略来选择分解器:如果输入数据的尺寸大于500x500且要提 取的特征数小于数据最小维度min(X.shape)的80%,就启用效率更高的”randomized“方法。否则,精确完整 的SVD将被计算,截断将会在矩阵被分解完成后有选择地发生。
“full”:从scipy.linalg.svd中调用标准的LAPACK分解器来生成精确完整的SVD,适合数据量比较适中,计算时 间充足的情况,生成的精确完整的SVD的结构为: “arpack”:从scipy.sparse.linalg.svds调用ARPACK分解器来运行截断奇异值分解(SVD truncated),分解时就将特征数量降到n_components中输入的数值k,可以加快运算速度,适合特征矩阵很大的时候,但一般用于 特征矩阵为稀疏矩阵的情况,此过程包含一定的随机性。截断后的SVD分解出的结构为: “randomized”,通过Halko等人的随机方法进行随机SVD。在"full"方法中,分解器会根据原始数据和输入的 n_components值去计算和寻找符合需求的新特征向量,但是在"randomized"方法中,分解器会先生成多个随机向量,然后一一去检测这些随机向量中是否有任何一个符合我们的分解需求,如果符合,就保留这个随 机向量,并基于这个随机向量来构建后续的向量空间。这个方法已经被Halko等人证明,比"full"模式下计算快 很多,并且还能够保证模型运行效果。适合特征矩阵巨大,计算量庞大的情况。
重要属性components_ 通常来说,在新的特征矩阵生成之前,我们无法知晓PCA都建立了怎样的新特征向量,新 特征矩阵生成之后也不具有可读性,我们无法判断新特征矩阵的特征是从原数据中的什么特征组合而来,新特征虽 然带有原始数据的信息,却已经不是原数据上代表着的含义了。
但是其实,在矩阵分解时,PCA是有目标的:在原有特征的基础上,找出能够让信息尽量聚集的新特征向量。
如果原特征矩阵是图像,V(k,n)这 个空间矩阵也可以被可视化的话,我们就可以通过两张图来比较,就可以看出新特征空间究竟从原始数据里提取了什么重要的信息。
人脸识别案例
from sklearn.decomposition import PCA
from sklearn.datasets import fetch_lfw_people
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
faces = fetch_lfw_people(min_faces_per_person=60)
x = faces.data
fig, axes = plt.subplots(4, 5, figsize=(8, 4), subplot_kw={'xticks': [], 'yticks': []})
for i, ax in enumerate(axes.flat):
ax.imshow(faces.images[i, :, :], cmap='gray')
pca = PCA(150).fit(x)
v = pca.components_
'''
[[-0.00579708 -0.00595363 -0.00615767 ... -0.00813068 -0.00789127
-0.00791125]
[-0.00586497 -0.00604798 -0.00647989 ... -0.00849601 -0.00826257
-0.00840366]
[-0.00622997 -0.00658633 -0.00714348 ... -0.00897475 -0.0085009
-0.0089165 ]
...
[-0.00270353 -0.00276129 -0.00370097 ... -0.01046781 -0.00896865
-0.00820496]
[-0.00298345 -0.003292 -0.00394306 ... -0.01013859 -0.00875235
-0.00805416]
[-0.00329591 -0.00362476 -0.00457148 ... -0.01000113 -0.00901094
-0.00813919]]
'''
'''
[-0.00579718 -0.00595365 -0.00615768 ... -0.01000111 -0.00901092
-0.00813918]
'''
fig, axes = plt.subplots(3, 8, figsize=(8, 4), subplot_kw={'xticks': [], 'yticks': []})
for i, ax in enumerate(axes.flat):
ax.imshow(v[i, :].reshape(62, 47), cmap='gray')
plt.show()
提取的主要特征有光线和眼睛
4、重要接口inverse_transform
from sklearn.datasets import fetch_lfw_people
from sklearn.decomposition import PCA
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
faces = fetch_lfw_people(min_faces_per_person=60)
x = faces.data
pca = PCA(150)
x_dr = pca.fit_transform(x)
x_inverse = pca.inverse_transform(x_dr)
fig, ax = plt.subplots(2, 10, figsize=(10, 2.5), subplot_kw={"xticks": [], "yticks": []})
for i in range(10):
ax[0, i].imshow(faces.images[i, :, :], cmap='binary_r')
ax[1, i].imshow(x_inverse[i].reshape(62, 47), cmap='binary_r')
plt.show()
逆转换之后的图像并没有原来的图像清洗,是因为在经过PVC降维之后去掉了一些噪音,只提取了重要的特征,所以在逆转的过程中并不能逆转已经去掉的信息。
案例:用PCA做噪音过滤
from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
digits = load_digits()
def plot_digits(data):
fig, axes = plt.subplots(4, 10, figsize=(10, 4), subplot_kw={'xticks': [], 'yticks': []})
for i, ax in enumerate(axes.flat):
ax.imshow(data[i].reshape(8, 8), cmap="binary")
plt.show()
plot_digits(digits.data)
给手写数字加噪音,取出一部分服从正太分布的、方差为2的数据
rng = np.random.RandomState(42)
digits_noise = rng.normal(digits.data, 2)
加了噪音之后非常模糊
利用pca提取使得特征解释率在0.5上的特征即可,经过主要特征的特区进行逆转,逆转之后的图片只体现主要的信息,会稍微清晰一些
pca = PCA(0.5, svd_solver='full').fit(digits_noise)
x_dr = pca.transform(digits_noise)
print(x_dr.shape)
x_inverse = pca.inverse_transform(x_dr)
5、重要接口,参数和属性总结
6、案例:PCA对手写数字数据集的降维
from sklearn.decomposition import PCA
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.neighbors import KNeighborsClassifier as KNN
from sklearn.model_selection import cross_val_score
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import time
data = pd.read_csv('digit recognizor.csv')
x = data.iloc[:, 1:]
y = data.iloc[:, 0]
pca_line = PCA().fit(x)
plt.figure(figsize=(20, 5))
plt.plot(np.cumsum(pca_line.explained_variance_ratio_))
plt.xlabel('number of components after dimension reduction')
plt.ylabel('cumulative explained variance ratio')
先通过解释率累加图来寻找一个合理的维度,因为785维实属太高维了! 我们一般拐点处的维度,大概看起来就是在1-101之间
scores = []
for i in range(1, 101, 10):
x_dr = PCA(i).fit_transform(x)
once = cross_val_score(RFC(n_estimators=10, random_state=0), x_dr, y, cv=5).mean()
scores.append(once)
plt.figure(figsize=(10, 8))
plt.plot(range(1, 101, 10), scores)
plt.show()
再次缩小范围
start_time = time.time()
scores = []
for i in range(15, 30):
x_dr = PCA(i).fit_transform(x)
once = cross_val_score(RFC(n_estimators=10, random_state=0), x_dr, y, cv=5).mean()
scores.append(once)
plt.figure(figsize=(10, 8))
plt.plot(range(15, 30), scores)
end_time = time.time()
print(end_time-start_time)
plt.show()
于是,取维度26作为我们降维之后的降维之后的新特征维度,并输入随机森林中
x_dr = PCA(26).fit_transform(x)
score = cross_val_score(RFC(n_estimators=100, random_state=0), x_dr, y, cv=5).mean()
print(score)
此时,我们想到了KNN比随机森林更适合跑这个数据集,所以我们换一个算法再次验证
x_dr = PCA(26).fit_transform(x)
score_knn = cross_val_score(KNN(), x_dr, y, cv=5).mean()
print(score_knn)
因为这时的KNN()里面是默认值,所以我们可以寻找一个更好的参数
x_dr = PCA(26).fit_transform(x)
score_1 = []
for i in range(10):
once = cross_val_score(KNN(i+1), x_dr, y, cv=5).mean()
score_1.append(once)
plt.figure(figsize=(10, 8))
plt.plot(range(10), score_1)
plt.show()
此时,KNN里面的参数取2和4都试试
x_dr = PCA(26).fit_transform(x)
score_knn = cross_val_score(KNN(3), x_dr, y, cv=5).mean()
print(score_knn)
x_dr = PCA(26).fit_transform(x)
score_r = cross_val_score(KNN(5), x_dr, y, cv=5).mean()
print(score_knn)
发现结果都不如默认的参数,那就还是使用默认参数。
|