介绍
如今,使用具有数百个(甚至数千个)特征的数据集变得非常普遍。如果要素的数量变得与存储在数据集中的观测值的数量相似(甚至更大!),则很可能导致机器学习模型过度拟合。为了避免此类问题,有必要应用正则化或降维技术(特征提取)。在机器学习中,数据集的维数等于用来表示数据集的变量数。
使用正则化无疑可以帮助降低过度拟合的风险,但是使用特征提取技术也可以带来其他类型的优势,例如:
特征提取旨在通过从现有特征中创建新特征(然后丢弃原始特征)来减少数据集中的特征数量。然后,这些新的简化功能集应该能够汇总原始功能集中包含的大多数信息。这样,可以从原始集合的组合中创建原始特征的摘要版本。
减少数据集中特征数量的另一种常用技术是特征选择。特征选择和特征提取之间的区别在于,特征选择的目的是对数据集中现有特征的重要性进行排名,并丢弃次要的特征(不创建新特征)。
在本文中,将引导如何使用Kaggle蘑菇分类数据集作为示例来应用特征提取技术。目标是通过查看给定的特征来尝试预测蘑菇是否有毒。这篇文章中使用的所有代码都可以在Kaggle和GitHub帐户上找到。
https://www.kaggle.com/uciml/mushroom-classification
https://www.kaggle.com/pierpaolo28/feature-extraction?scriptVersionId=21698205
https://github.com/pierpaolo28/Artificial-Intelligence-Projects/blob/master/Features%20Analysis/feature-extraction.ipynb
首先,需要导入所有必需的库。
import timeimport numpy as npimport pandas as pdimport matplotlib.pyplot as pltfrom matplotlib.pyplot import figureimport seaborn as snsfrom sklearn import preprocessingfrom sklearn.preprocessing import LabelEncoderfrom sklearn.preprocessing import StandardScalerfrom sklearn.model_selection import train_test_splitfrom sklearn.metrics import classification_report,confusion_matrixfrom sklearn.ensemble import RandomForestClassifier
下图显示了本示例中将使用的数据集。
图1:蘑菇分类数据集
在将这些数据输入到机器学习模型之前,决定将数据划分为特征(X)和标签(Y),然后对所有分类变量进行一次热编码。
X = df.drop(['class'], axis = 1)Y = df['class']X = pd.get_dummies(X, prefix_sep='_')Y = LabelEncoder().fit_transform(Y)X = StandardScaler().fit_transform(X)
后来,决定创建一个函数(forest_test)将输入数据分为训练集和测试集,然后训练和测试随机森林分类器。
def forest_test(X, Y): X_Train, X_Test, Y_Train, Y_Test = train_test_split(X, Y, test_size = 0.30, random_state = 101) start = time.process_time() trainedforest = RandomForestClassifier(n_estimators=700).fit(X_Train,Y_Train) print(time.process_time() - start) predictionforest = trainedforest.predict(X_Test) print(confusion_matrix(Y_Test,predictionforest)) print(classification_report(Y_Test,predictionforest))
现在,可以使用整个数据集使用此函数,然后在使用简化版本而不是整个数据集时相继使用它来比较这些结果。
forest_test(X, Y)
如下所示,使用所有功能训练随机森林分类器可在约2.2s的训练时间内获得100%的准确性。在以下每个示例中,每个模型的训练时间都将打印在每个摘要的第一行,以供您参考。
2.2676709799999992[[1274 0] [ 0 1164]] precision recall f1-score support 0 1.00 1.00 1.00 1274 1 1.00 1.00 1.00 1164 accuracy 1.00 2438 macro avg 1.00 1.00 1.00 2438weighted avg 1.00 1.00 1.00 2438
特征提取
主成分分析(PCA)
PCA是最常用的线性降维技术之一。使用PCA时,将原始数据作为输入,并尝试找到可以最好地总结原始数据分布的输入特征的组合,从而减小其原始尺寸。PCA可以通过查看方对距离来最大化方差并最小化重构误差来实现此目的。在PCA中,原始数据被投影到一组正交轴中,并且每个轴都按重要性顺序排序。
PCA是一种无监督的学习算法,因此它并不关心数据标签,而只关心变化。在某些情况下,这可能导致数据分类错误。
在此示例中,将首先在整个数据集中执行PCA,以将数据缩小为二维,然后构造一个具有新功能及其各自标签的数据框。
from sklearn.decomposition import PCA pca = PCA(n_components=2)X_pca = pca.fit_transform(X)PCA_df = pd.DataFrame(data = X_pca, columns = ['PC1', 'PC2'])PCA_df = pd.concat([PCA_df, df['class']], axis = 1)PCA_df['class'] = LabelEncoder().fit_transform(PCA_df['class'])PCA_df.head()
图2:PCA数据集
使用新创建的数据框,现在可以在2D散点图中绘制数据分布。
figure(num=None, figsize=(8, 8), dpi=80, facecolor='w', edgecolor='k') classes = [1, 0]colors = ['r', 'b']for clas, color in zip(classes, colors): plt.scatter(PCA_df.loc[PCA_df['class'] == clas, 'PC1'], PCA_df.loc[PCA_df['class'] == clas, 'PC2'], c = color) plt.xlabel('Principal Component 1', fontsize = 12)plt.ylabel('Principal Component 2', fontsize = 12)plt.title('2D PCA', fontsize = 15)plt.legend(['Poisonous', 'Edible'])plt.grid()
图3:2D PCA可视化
现在,可以重复相同的过程,而是保留3个尺寸并使用Plotly创建动画(可以与下面的动画进行交互!)。
使用PCA时,还可以使用explicit_variance_ratio_Scikit-learn函数来探索保留了多少原始数据差异。一旦计算出方差比,就可以继续创建精美的可视化图。
使用由PCA构造的3个要素集(而不是整个数据集)再次运行随机森林分类器,可以达到98%的分类精度,而仅使用2个要素即可达到95%的精度。
pca = PCA(n_components=3,svd_solver='full')X_pca = pca.fit_transform(X)print(pca.explained_variance_) forest_test(X_pca, Y)
[10.31484926 9.42671062 8.35720548]2.769664902999999[[1261 13] [ 41 1123]] precision recall f1-score support 0 0.97 0.99 0.98 1274 1 0.99 0.96 0.98 1164 accuracy 0.98 2438 macro avg 0.98 0.98 0.98 2438weighted avg 0.98 0.98 0.98 2438
此外,使用二维数据集,现在还可以可视化随机森林使用的决策边界,以便对每个不同的数据点进行分类。
from itertools import product X_Reduced, X_Test_Reduced, Y_Reduced, Y_Test_Reduced = train_test_split(X_pca, Y, test_size = 0.30, random_state = 101)trainedforest = RandomForestClassifier(n_estimators=700).fit(X_Reduced,Y_Reduced) x_min, x_max = X_Reduced[:, 0].min() - 1, X_Reduced[:, 0].max() + 1y_min, y_max = X_Reduced[:, 1].min() - 1, X_Reduced[:, 1].max() + 1xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1), np.arange(y_min, y_max, 0.1))Z = trainedforest.predict(np.c_[xx.ravel(), yy.ravel()])Z = Z.reshape(xx.shape)plt.contourf(xx, yy, Z,cmap=plt.cm.coolwarm, alpha=0.4)plt.scatter(X_Reduced[:, 0], X_Reduced[:, 1], c=Y_Reduced, s=20, edgecolor='k')plt.xlabel('Principal Component 1', fontsize = 12)plt.ylabel('Principal Component 2', fontsize = 12)plt.title('Random Forest', fontsize = 15)plt.show()
图4:PCA随机森林决策边界
独立成分分析(ICA)
ICA是一种线性降维方法,将独立成分的混合作为输入数据,旨在正确识别每个成分(删除所有不必要的噪声)。如果两个输入特征的线性和非线性相关性都等于零[1],则可以认为它们是独立的。
独立成分分析通常在医学应用中使用,例如EEG和fMRI分析,以将有用信号与无用信号分开。
作为ICA应用程序的一个简单示例,考虑一个音频注册,其中有两个不同的人在说话。例如,使用ICA,可以尝试识别注册中的两个不同的独立组件(两个不同的人)。这样,可以使我们的无监督学习算法在对话中的不同说话者之间识别。
使用ICA,现在可以再次将数据集简化为三个特征,使用随机森林分类器测试其准确性并绘制结果。
from sklearn.decomposition import FastICA ica = FastICA(n_components=3)X_ica = ica.fit_transform(X) forest_test(X_ica, Y)
2.8933812039999793[[1263 11] [ 44 1120]] precision recall f1-score support 0 0.97 0.99 0.98 1274 1 0.99 0.96 0.98 1164 accuracy 0.98 2438 macro avg 0.98 0.98 0.98 2438weighted avg 0.98 0.98 0.98 2438
从下面的动画中,可以看到,即使PCA和ICA得出相同的精度结果,它们仍可以构造两个不同的3维空间分布。
线性判别分析(LDA)
LDA是监督学习降维技术和机器学习分类器。
LDA的目的是最大程度地增加每个类的均值之间的距离,并最大程度地减少类本身的散布。因此,LDA在班级内部和班级之间用作度量。这是一个不错的选择,因为在较低维度的空间中投影数据时,最大化每个类别的均值之间的距离会导致更好的分类结果(由于减少了不同类别之间的重叠)。
使用LDA时,假设输入数据遵循高斯分布(在这种情况下),因此将LDA应用于非高斯数据可能会导致较差的分类结果。
在此示例中,将运行LDA将数据集简化为一个特征,测试其准确性并绘制结果。
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis lda = LinearDiscriminantAnalysis(n_components=1) # run an LDA and use it to transform the featuresX_lda = lda.fit(X, Y).transform(X)print('Original number of features:', X.shape[1])print('Reduced number of features:', X_lda.shape[1])
Original number of features: 117Reduced number of features: 1
由于数据分布严格遵循高斯分布,因此LDA的表现非常好,在这种情况下,使用随机森林分类器可以达到100%的准确性。
forest_test(X_lda, Y)
1.2756952610000099[[1274 0] [ 0 1164]] precision recall f1-score support 0 1.00 1.00 1.00 1274 1 1.00 1.00 1.00 1164 accuracy 1.00 2438 macro avg 1.00 1.00 1.00 2438weighted avg 1.00 1.00 1.00 2438
正如在本节开头提到的那样,LDA也可以用作分类器。因此,现在可以测试在这种情况下LDA分类器的性能。
X_Reduced, X_Test_Reduced, Y_Reduced, Y_Test_Reduced = train_test_split(X_lda, Y, test_size = 0.30, random_state = 101) start = time.process_time()lda = LinearDiscriminantAnalysis().fit(X_Reduced,Y_Reduced)print(time.process_time() - start)predictionlda = lda.predict(X_Test_Reduced)print(confusion_matrix(Y_Test_Reduced,predictionlda))print(classification_report(Y_Test_Reduced,predictionlda))
0.008464782999993758[[1274 0] [ 2 1162]] precision recall f1-score support 0 1.00 1.00 1.00 1274 1 1.00 1.00 1.00 1164 accuracy 1.00 2438 macro avg 1.00 1.00 1.00 2438weighted avg 1.00 1.00 1.00 2438
最后,现在可以形象地看到我们的两个类的分布看起来像创建一维数据的分布图。
图5:LDA类分离
局部线性嵌入(LLE)
到目前为止,已经考虑了PCA和LDA等方法,它们在不同特征之间存在线性关系的情况下确实能够很好地执行,现在将继续考虑如何处理非线性情况。
局部线性嵌入是基于流形学习的降维技术。歧管是D维尺寸的对象,它嵌入到更高维的空间中。流形学习的目的是使该对象在其原始D维度上可表示,而不是在不必要的更大空间中表示。
用于解释机器学习中的流形学习的一个典型示例是Swiss Roll流形(图6)。作为输入,获得了一些数据,该数据具有类似于一卷纸的分布(在3D空间中),然后可以对其展开,以便将数据缩小为二维空间。
流形学习算法的一些示例包括:Isomap,局部线性嵌入,修改的局部线性嵌入,Hessian特征映射等。
图6:流形学习[2]
现在,将在示例中逐步指导如何实现LLE。根据Scikit-learn文档[3]:
局部线性嵌入(LLE)寻求数据的低维投影,以保留局部邻域内的距离。可以将其视为一系列局部主成分分析,将其进行全局比较以找到最佳的非线性嵌入。
现在,可以在数据集上运行LLE,以将数据维数减少到3维,测试总体准确性并绘制结果。
from sklearn.manifold import LocallyLinearEmbedding embedding = LocallyLinearEmbedding(n_components=3)X_lle = embedding.fit_transform(X) forest_test(X_lle, Y)
2.578125[[1273 0] [1143 22]] precision recall f1-score support 0 0.53 1.00 0.69 1273 1 1.00 0.02 0.04 1165 micro avg 0.53 0.53 0.53 2438 macro avg 0.76 0.51 0.36 2438weighted avg 0.75 0.53 0.38 2438
t分布随机邻居嵌入(t-SNE)
t-SNE是非线性降维技术,通常用于可视化高维数据集。t-SNE的一些主要应用是自然语言处理(NLP),语音处理等…
t-SNE通过最小化由输入特征在原始高维空间中的成对概率相似性与其在缩减的低维空间中的等效特征构成的分布之间的差异来工作。然后t-SNE利用Kullback-Leiber(KL)散度来测量两个不同分布的不相似性。然后使用梯度下降将KL散度最小化。
使用t-SNE时,高维空间使用高斯分布建模,而低维空间则使用学生的t分布建模。这样做是为了避免由于转换成低维空间而导致的相邻点距离分布的不平衡。
现在,准备使用TSNE,并将数据集简化为3个要素。
from sklearn.manifold import TSNE start = time.process_time()tsne = TSNE(n_components=3, verbose=1, perplexity=40, n_iter=300)X_tsne = tsne.fit_transform(X)print(time.process_time() - start)
[t-SNE] Computing 121 nearest neighbors...[t-SNE] Indexed 8124 samples in 0.139s...[t-SNE] Computed neighbors for 8124 samples in 11.891s...[t-SNE] Computed conditional probabilities for sample 1000 / 8124[t-SNE] Computed conditional probabilities for sample 2000 / 8124[t-SNE] Computed conditional probabilities for sample 3000 / 8124[t-SNE] Computed conditional probabilities for sample 4000 / 8124[t-SNE] Computed conditional probabilities for sample 5000 / 8124[t-SNE] Computed conditional probabilities for sample 6000 / 8124[t-SNE] Computed conditional probabilities for sample 7000 / 8124[t-SNE] Computed conditional probabilities for sample 8000 / 8124[t-SNE] Computed conditional probabilities for sample 8124 / 8124[t-SNE] Mean sigma: 2.658530[t-SNE] KL divergence after 250 iterations with early exaggeration: 65.601128[t-SNE] KL divergence after 300 iterations: 1.909915143.984375
通过可视化结果功能的分布,我们可以清楚地看到,即使在缩小的空间中进行了转换,我们的数据也是如何很好地分离的。
使用t-SNE缩减子集测试随机森林准确性,证实了现在类可以轻松分离了。
forest_test(X_tsne, Y)
2.6462027340000134[[1274 0] [ 0 1164]] precision recall f1-score support 0 1.00 1.00 1.00 1274 1 1.00 1.00 1.00 1164 accuracy 1.00 2438 macro avg 1.00 1.00 1.00 2438weighted avg 1.00 1.00 1.00 2438
自动编码器
自动编码器是一类机器学习算法,可以用作降维技术。自动编码器与其他降维技术之间的主要区别在于,自动编码器使用非线性变换将数据从高维度投影到低维度。
存在不同类型的自动编码器,例如:
在此示例中,将从构建基本的自动编码器开始(图7)。自动编码器的基本体系结构可以分为两个主要组件:
如果所有输入要素彼此独立,则自动编码器将特别难以编码和解码以将输入数据输入低维空间。
图7:自动编码器架构[4]
可以使用Keras API在Python中实现自动编码器。在这种情况下,在编码层中指定要减少输入数据的要素数量(对于本例3)。从下面的代码片段可以看出,自动编码器将X(我们的输入功能)作为功能和标签(X,Y)。
对于此示例,决定将ReLu用作编码阶段的激活函数并将Softmax用作解码阶段。如果不使用非线性激活函数,那么自动编码器将尝试使用线性变换来减少输入数据(因此,得到的结果类似于使用PCA的结果)。
from keras.layers import Input, Densefrom keras.models import Model input_layer = Input(shape=(X.shape[1],))encoded = Dense(3, activation='relu')(input_layer)decoded = Dense(X.shape[1], activation='softmax')(encoded)autoencoder = Model(input_layer, decoded)autoencoder.compile(optimizer='adam', loss='binary_crossentropy') X1, X2, Y1, Y2 = train_test_split(X, X, test_size=0.3, random_state=101) autoencoder.fit(X1, Y1, epochs=100, batch_size=300, shuffle=True, verbose = 30, validation_data=(X2, Y2)) encoder = Model(input_layer, encoded)X_ae = encoder.predict(X)
现在,可以重复与前面的示例类似的工作流程,这次使用简单的自动编码器作为特征提取技术。
forest_test(X_ae, Y)
1.734375[[1238 36] [ 67 1097]] precision recall f1-score support 0 0.95 0.97 0.96 1274 1 0.97 0.94 0.96 1164 micro avg 0.96 0.96 0.96 2438 macro avg 0.96 0.96 0.96 2438weighted avg 0.96 0.96 0.96 2438
参考书目
[1]通过独立成分分析(ICA),在纸空间中深入研究降维。访问以下网址:
https://blog.paperspace.com/dimension-reduction-with-independent-components-analysis/
[2]具有流形雕刻的迭代非线性降维,ResearchGate。访问网址:
https://www.researchgate.net/publication/220270207_Iterative_Non-linear_Dimensionality_Reduction_with_Manifold_Sculpting
[3]流形学习,Scikit学习文档。可通过以下网址访问:
https://scikit-learn.org/stable/modules/manifold.html#targetText=Manifold%20learning%20is%20an%20approach,sets%20is%20only%20artificially%20high
[4]可变自动编码器很漂亮,Comp Three Inc.史蒂文·弗洛雷斯(Steven Flores)。访问网址:http://www.compthree.com/blog/autoencoder/