ML--数据预处理,降维,特征提取及聚类
ML–数据预处理,降维,特征提取及聚类
主要涉及的知识点有:
- 几种常见的数据预处理工具
- PCA主成分分析用于数据降维
- PCA主成分分析和NMF非负矩阵分解用于特征提取
- 几种常用的聚类算法
一.数据预处理
1.使用StandardScaler进行数据预处理
# 导入numpy
import numpy as np
# 导入画图工具
import matplotlib.pyplot as plt
# 导入数据集生成工具
from sklearn.datasets import make_blobs
X,y=make_blobs(n_samples=40,centers=2,random_state=50,cluster_std=2)
# 用散点图绘制数据点
plt.scatter(X[:,0],X[:,1],c=y,cmap=plt.cm.cool)
plt.show()
[结果分析] 我们在使用make_blobs
函数时,指定了样本数量n_samples
为40,分类centers
为2,随机状态random_state
为50,标准差cluster_std
为2
接下来,我们要使用scikit-learn
的preprocessing
模块对这个手工生成的数据集进行预处理的操作。首先我们先来看第一个方法:StandardScaler
# 导入StandardScaler
from sklearn.preprocessing import StandardScaler
X_1=StandardScaler().fit_transform(X)
plt.scatter(X_1[:,0],X_1[:,1],c=y,cmap=plt.cm.cool)
plt.show()
[结果分析] 图像的数据点的分布情况没有什么不同,但图像的x轴和y轴发生了变化。现在数据所有的特征1的数值都在-2到3之间,而特征2的数值都在-3到2之间。这是因为,StandardScaler
的原理是,将所有数据的特征值转换为均值为0,而方差为1的状态,这样就可以确保数据的"大小"都是一致的,这样更利于模型的训练
2.使用MinMaxScaler进行数据预处理
# 导入MinMaxScaler
from sklearn.preprocessing import MinMaxScaler
X_2=MinMaxScaler().fit_transform(X)
plt.scatter(X_2[:,0],X_2[:,1],c=y,cmap=plt.cm.cool)
plt.show()
[结果分析] 对于我们使用make_blobs
生成的二维数据集,你也可以想象成我们通过MinMaxScaler
把所有的数据压进了一个长和宽的1正方形
3.使用RobustScaler进行数据预处理
还有一种数据转换的方法,和StandardScaler
比较类似,但是它并不是用均值和方差来进行转换,而是使用中位数和四分位数。它会直接把一些异常值踢出去
# 导入RobustScaler
from sklearn.preprocessing import RobustScaler
X_3=RobustScaler().fit_transform(X)
plt.scatter(X_3[:,0],X_3[:,1],c=y,cmap=plt.cm.cool)
plt.show()
4.使用Normalizer进行数据预处理
这种方法将所有样本的特征向量转化为欧几里得距离为1,也就是说,它把数据的分布变成一个半径为1的圆,或者是一个球。Normalizer
通常是在我们只想保留数据特征向量的方向,而忽略其数值的时候使用
# 导入Normalizer
from sklearn.preprocessing import Normalizer
X_4=Normalizer().fit_transform(X)
plt.scatter(X_4[:,0],X_4[:,1],c=y,cmap=plt.cm.cool)
plt.show()
5.通过数据预处理提高模型准确率
# 导入红酒数据集
from sklearn.datasets import load_wine
# 导入MLP神经网络
from sklearn.neural_network import MLPClassifier
# 导入数据集拆分工具
from sklearn.model_selection import train_test_split
wine=load_wine()
X_train,X_test,y_train,y_test=train_test_split(wine.data,wine.target,random_state=62)
print(X_train.shape,X_test.shape)
(133, 13) (45, 13)
下面我们训练数据集来训练一个MLP
神经网络
# 设定MLP神经网络的参数
mlp=MLPClassifier(hidden_layer_sizes=[100,100],max_iter=400,random_state=62)
mlp.fit(X_train,y_train)
print('模型得分:{:.2f}'.format(mlp.score(X_test,y_test)))
模型得分:0.93
E:\Anaconda\envs\mytensorflow\lib\site-packages\sklearn\neural_network\multilayer_perceptron.py:562: ConvergenceWarning: Stochastic Optimizer: Maximum iterations (400) reached and the optimization hasn't converged yet.
% self.max_iter, ConvergenceWarning)
下面我们试着对数据集进行一些预处理的操作
# 使用MinMaxScaler进行数据预处理
scaler=MinMaxScaler()
scaler.fit(X_train)
X_train_pp=scaler.transform(X_train)
X_test_pp=scaler.transform(X_test)
mlp.fit(X_train_pp,y_train)
print('数据预处理后的模型得分:{:.2f}'.format(mlp.score(X_test_pp,y_test)))
数据预处理后的模型得分:1.00
注意 在上面的代码中,我们先用MinMaxScaler
拟合了原始的训练数据集,再用它去转换原始的训练数据集和测试数据集切记不要用它先拟合原始的测试数据集,再去转换测试数据集,这样做就失去了数据转换的意义
二.数据降维
1.PCA主成分分析原理
经过这样的处理之后,数据集从一个散点组成的面变成了一条直线,也就是从二维变成了一维。这就是数据降维的意思,而这里我们用到的方法,称为主成分分析法(Principal Component Analysis,PCA)
2.对数据降维以便于进行可视化
首先为大家展示一下PCA主成分分析法
的使用
# 导入数据预处理工具
from sklearn.preprocessing import StandardScaler
# 对红酒数据集进行预处理
scaler=StandardScaler()
X=wine.data
y=wine.target
X_scaled=scaler.fit_transform(X)
print(X_scaled.shape)
(178, 13)
接下来我们导入PCA
模块并对数据进行处理
# 导入PCA
from sklearn.decomposition import PCA
# 设置主成分数量为2以便我们进行可视化
pca=PCA(n_components=2)
pca.fit(X_scaled)
X_pca=pca.transform(X_scaled)
print(X_pca.shape)
(178, 2)
注意 因为PCA主成分分析法
属于无监督学习算法,所以这里只对X_scaled
进行了拟合,而并没有涉及分类标签y
[结果分析] 数据集的样本数量仍然是178个,而特征变量只剩下2个了
下面我们用经过PCA
降维的数据可视化情况
# 将三个分类中主成分提取出来
X0=X_pca[wine.target==0]
X1=X_pca[wine.target==1]
X2=X_pca[wine.target==2]
plt.scatter(X0[:,0],X0[:,1],c='b',s=60,edgecolors='k')
plt.scatter(X1[:,0],X1[:,1],c='g',s=60,edgecolors='k')
plt.scatter(X2[:,0],X2[:,1],c='r',s=60,edgecolors='k')
plt.legend(wine.target_names,loc='best')
plt.xlabel('component 1')
plt.ylabel('component 2')
plt.show()
[结果分析] 我们可以使用PCA主成分分析法
将数据集的特征向量降至二维,从而轻松进行可视化处理,同时又不会丢失太多的信息
3.原始特征与PCA主成分之间的关系
# 使用主成分绘制热度图
plt.matshow(pca.components_,cmap='plasma')
# 纵轴为主成分数
plt.yticks([0,1],['component 1','component 2'])
plt.colorbar()
# 横轴为原始特征变量
plt.xticks(range(len(wine.feature_names)),wine.feature_names,rotation=60,ha='left')
plt.show()
[结果分析] 颜色由深至浅代表一个从-0.5-0.4的数值。而在两个主成分中,分别涉及了所有的13个特征。如果某个特征对应的数字是正数,说明它和主成分之间是正相关的关系,如果是负数则相反
[技巧] 在scikit-learn中,PCA的n_components不仅可以代表成分的个数,还可以设置为降维之后保留信息的百分比,例如我们希望降维之后保留原特征90%的信息,那么就可以设置n_components为0.9
三.特征提取
我们通过对数据集原来的特征进行转换,生成新的"特征"或者说成分,会比直接使用原始的特征效果要好。我们在引入一个新的名词,称为"数据表达(data representation)"。在数据集极为复杂的情况下,比如图像识别,数据表达就显得十分重要。因为图像是由成千上万个像素组成,每个像素上又有不同的RGB色彩值,所以我们要用到一个新的数据处理方法,称为特征提取(feature extraction)
1.PCA主成分分析法用于特征提取
我们使用一个相对复杂点的数据集–LFW人脸识别数据集
# 导入数据集获取工具
from sklearn.datasets import fetch_lfw_people
# 载人人脸数据集
faces=fetch_lfw_people(min_faces_per_person=20,resize=0.8)
image_shape=faces.images[0].shape
# 将照片打印出来
fig,axes=plt.subplots(3,4,figsize=(12,9),subplot_kw={'xticks':(),'yticks':()})
for target,image,ax in zip(faces.target,faces.images,axes.ravel()):
ax.imshow(image,cmap=plt.cm.gray)
ax.set_title(faces.target_names[target])
plt.show()
接下来,我们在数据未经过处理的情况下,尝试训练一个神经网络,看看效果如何
# 导入神经网络
from sklearn.neural_network import MLPClassifier
# 数据集进行拆分
X_train,X_test,y_train,y_test=train_test_split(faces.data/255,faces.target,random_state=62)
# 训练神经网络
mlp=MLPClassifier(hidden_layer_sizes=[100,100],random_state=62,max_iter=400)
mlp.fit(X_train,y_train)
# 打印模型准确率
print('模型识别准确率:{:.2f}'.format(mlp.score(X_test,y_test)))
模型识别准确率:0.42
接下来我们使用一些方法来提升模型的表现。第一个要用到的就是PCA主成分分析法
中的数据白化功能(data whiten)
那么什么是数据白化呢?虽然每个人的面部特征有很大差异,但如果你从像素级别观察,差距其实就没有那么大了。而且相邻的像素之间有很大的相关性,这样一来,样本特征的输入就是冗余的了,白化的目的就是为了要降低冗余性。所以白化的过程会让样本特征之间的相关性降低,且所有特征具有相同的方差
# 使用白化功能处理人脸数据
pca=PCA(whiten=True,n_components=0.9,random_state=62).fit(X_train)
X_train_whiten=pca.transform(X_train)
X_test_whiten=pca.transform(X_test)
# 打印白化后数据形态
print('白化后数据形态:{}'.format(X_train_whiten.shape))
白化后数据形态:(2202, 107)
下面我们来看看经过白化后神经网络识别的准确率有什么变化
# 使用白化后的数据训练神经网络
mlp.fit(X_train_whiten,y_train)
# 打印模型准确率
print('数据白化后模型识别准确率:{:.2f}'.format(mlp.score(X_test_whiten,y_test)))
数据白化后模型识别准确率:0.55
2.非负矩阵分解用于特征提取
除了PCA
之外,scikit-learn
中还封装了非负矩阵分解(Non-Negative Matrix Factorization,NMF)
。NMF
也是一个无监督学习算法,同样可以用于数据的特征提取
所谓矩阵分解就是把一个矩阵拆解为n个矩阵的乘积。而非负矩阵分解,顾名思义就是原始的矩阵中所有的数值必须大于或等于0,当然分解之后的矩阵中数据也是大于或等于0的
与PCA
不同的是,如果我们降低NMF
成分数量,它会重新生成新的成分,而新的成分和原来的成分是完全不一样的。另外,NMF
中的成分是没有顺序的,这点和PCA
也有所不同。下面我们试一试使用NMF
对LFW
人脸数据集进行特征提取,再重新训练神经网络,看看模型的识别准确率是否有所变化
# 导入NMF
from sklearn.decomposition import NMF
# 使用NMF处理数据
nmf=NMF(n_components=105,random_state=62).fit(X_train)
X_train_nmf=nmf.transform(X_train)
X_test_nmf=nmf.transform(X_test)
# 打印NMF处理后的数据形态
print('NMF处理后数据形态:{}'.format(X_train_nmf.shape))
NMF处理后数据形态:(2202, 105)
注意 和PCA
不同,NMF
的n_components
参数不支持使用浮点数,只能设置为正的整型数
# 使用NMF处理后的数据训练神经网络
mlp.fit(X_train_nmf,y_train)
print('nmf处理后模型准确率:{:.2f}'.format(mlp.score(X_test_nmf,y_test)))
nmf处理后模型准确率:0.53
四.聚类算法
有监督学习主要用于分类和回归,而无监督学习的一个非常重要的用途就是对数据进行聚类。当然了,聚类和分类有一定的相似之处,分类是算法基于已有的标签的数据进行学习并对数据进行分类,而聚类则是在完全没有现有标签下,有算法"猜测"那些数据像是应该堆在一起的,并让算法给不同的"堆"里的数据贴上一个数字标签。我们将重点介绍K均值(K-Means)算法聚类
,凝聚聚类
以及DBSCAN
1.K均值聚类算法
工作原理是:假设我们的数据集中的样本特征不同,像小沙堆一样散布在地上,K均值算法会在小沙堆上插上旗子。而第一遍插的旗子并不能很完美地代表沙堆的分布,所以K均值还要继续,让每个旗子能够插到每个沙堆最佳的位置上,也就是数据点的均值上,这就是K均值算法名字的由来
from sklearn.datasets import make_blobs
# 生成分类数位1的数据集
blobs=make_blobs(random_state=1,centers=1)
X_blobs=blobs[0]
plt.scatter(X_blobs[:,0],X_blobs[:,1],c='r',edgecolors='k')
plt.show()
下面我们使用K均值
来帮助这些数据进行聚类
from sklearn.cluster import KMeans
kmeans=KMeans(n_clusters=3)
kmeans.fit(X_blobs)
x_min,x_max=X_blobs[:,0].min()-0.5,X_blobs[:,0].max()+0.5
y_min,y_max=X_blobs[:,1].min()-0.5,X_blobs[:,1].max()+0.5
xx,yy=np.meshgrid(np.arange(x_min,x_max,.02),np.arange(y_min,y_max,.02))
Z=kmeans.predict(np.c_[xx.ravel(),yy.ravel()])
Z=Z.reshape(xx.shape)
plt.figure(1)
plt.clf()
plt.imshow(Z,interpolation='nearest',extent=(xx.min(),xx.max(),yy.min(),yy.max()),cmap=plt.cm.summer,aspect='auto',origin='lower')
plt.plot(X_blobs[:,0],X_blobs[:,1],'r.',markersize=5)
# 用蓝色叉号代表聚类的中心
centroids=kmeans.cluster_centers_
plt.scatter(centroids[:,0],centroids[:,1],marker='x',s=150,linewidths=3,color='b',zorder=10)
plt.xlim(x_min,x_max)
plt.ylim(y_min,y_max)
plt.xticks(())
plt.yticks(())
plt.show()
[结果分析] 我们指定了K均值的n_clusters
参数是3,所以K均值将数据点聚为3类
看下K均值怎样来表示这些聚类的
# KMeans进行聚类的标签
print("K均值的聚类标签:\n{}".format(kmeans.labels_))
K均值的聚类标签:
[1 1 2 0 0 0 1 1 2 0 1 0 1 2 1 0 0 1 2 2 0 2 1 1 1 1 0 1 1 1 2 2 1 1 0 2 0
2 1 2 0 1 2 2 0 0 0 1 2 1 2 1 0 2 0 0 2 0 0 1 0 2 0 1 2 0 2 2 1 0 0 1 0 0
0 1 0 1 1 2 0 2 0 0 2 1 0 1 2 2 0 1 2 2 0 0 1 0 0 1]
2.凝聚聚类算法
下面我们还是用一个图像来对凝聚聚类算法的工作机制进行说明
# 导入dendrogram和ward工具
from scipy.cluster.hierarchy import dendrogram,ward
# 使用连线的方式进行可视化
linkage=ward(X_blobs)
dendrogram(linkage)
ax=plt.gca()
plt.xlabel("Sample index")
plt.ylabel("Cluster distance")
plt.show()
[结果分析] 凝聚聚类算法是自上而下,不断地合并相似的聚类中心,以便让类别越来越少,同时每个聚类中心的距离也就越来越远。这种逐级生成的聚类方法称为Hierarchy clustering
3.DBSCAN算法
DBSCAN算法的全名称为基于密度的有噪声应用空间聚类(Density-based spatial clustering of applications with noise)
,DBSCAN是通过对特征空间内的密度进行检测,密度大的地方它会认为是一个类,而密度相对小的地方它会认为是一个分界线
from sklearn.cluster import DBSCAN
db=DBSCAN()
clusters=db.fit_predict(X_blobs)
plt.scatter(X_blobs[:,0],X_blobs[:,1],c=clusters,cmap=plt.cm.cool,s=60,edgecolors='k')
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
plt.show()
# 打印聚类个数
print("聚类标签为:\n{}".format(clusters))
聚类标签为:
[-1 0 -1 0 -1 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 0
-1 0 0 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0 0
0 0 0 0 0 0 0 -1 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1
-1 0 0 0 0 -1 0 0 -1 0 0 -1 0 0 0 0 0 0 0 0 -1 0 0 0 -1]
[结果分析] 在DBSCAN中,-1代表该数据点是噪声。我们看到中间深色的数据点密度相对较大,因此DBSCAN把它们归到一类,而外围的浅色的数据点,DBSCAN认为根本不属于任何一类,所以放进了"噪声"这个类别
DBSCAN
两个非常重要的参数:一个是eps
;一个是min_samples
。eps
指定的是考虑划入同一类的样本距离有多远,eps
值设置得越大,则聚类所覆盖的数据点越多,反之则越少。默认情况下eps
的值为0.5
# 设置DBSCAN的eps参数为2
db_1=DBSCAN(eps=2)
clusters_1=db_1.fit_predict(X_blobs)
plt.scatter(X_blobs[:,0],X_blobs[:,1],c=clusters_1,cmap=plt.cm.cool,s=60,edgecolors='k')
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
plt.show()
min_samples
参数指定的是在某个数据点周围,被看成是聚类核心点的个数,min_samples
值越大,则核心数据点越少,噪声也就越多;反之min_sample
值越小,噪声也就越少。默认的min_samples
值是2
# 设置DBSCAN的eps参数为2
db_2=DBSCAN(min_samples=20)
clusters_2=db_2.fit_predict(X_blobs)
plt.scatter(X_blobs[:,0],X_blobs[:,1],c=clusters_2,cmap=plt.cm.cool,s=60,edgecolors='k')
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
plt.show()
DBSCAN
并不需要我们在开始训练算法的时候就指定clusters
的数量,但是通过对eps
和min_samples
参数赋值,相当于间接地指定了clusters
的数量。尤其是eps
参数尤为重要,因为它规定了某一类的范围大小