如有错误,恳请指出。
以下内容将使用一个非常经典的手写数字数据集来进行降维与可视化展示。在原始的数据中,手写数字的维度是64,因为其像素表示是64,以下内容将分别使用不同的算法对其降维到2~3维并展示。所以分别可以实现2D可视化展示降维效果与3D可视化降维效果。
文章目录
首先导入必要的工具包:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
1. t-SNE降维效果
1.1 2D可视化
from sklearn import datasets
from sklearn import preprocessing
from sklearn.manifold import TSNE
# 数据集导入
X, y = datasets.load_digits(return_X_y=True)
# t-SNE降维处理
tsne = TSNE(n_components=2, verbose=1 ,random_state=42)
result = tsne.fit_transform(X)
# 归一化处理
scaler = preprocessing.MinMaxScaler(feature_range=(-1,1))
result = scaler.fit_transform(result)
# 颜色设置
color = ['#FFFAFA', '#BEBEBE', '#000080', '#87CEEB', '#006400',
'#00FF00', '#4682B4', '#D02090', '#8B7765', '#B03060']
# 可视化展示
plt.figure(figsize=(10, 10))
plt.title('t-SNE process')
# plt.xlim((-1.1, 1.1))
# plt.ylim((-1.1, 1.1))
# for i in range(len(result)):
# plt.text(result[i,0], result[i,1], str(y[i]),
# color=color[y[i]], fontdict={'weight': 'bold', 'size': 9})
plt.scatter(result[:,0], result[:,1], c=y, s=10)
输出:
[t-SNE] Computing 91 nearest neighbors...
[t-SNE] Indexed 1797 samples in 0.000s...
[t-SNE] Computed neighbors for 1797 samples in 0.116s...
[t-SNE] Computed conditional probabilities for sample 1000 / 1797
[t-SNE] Computed conditional probabilities for sample 1797 / 1797
[t-SNE] Mean sigma: 11.585657
[t-SNE] KL divergence after 250 iterations with early exaggeration: 61.555897
[t-SNE] KL divergence after 1000 iterations: 0.743767
1.2 3D可视化
from sklearn.manifold import TSNE
from sklearn import preprocessing
from sklearn import datasets
# 导入数据
X, y = datasets.load_digits(return_X_y=True)
# t-SNE降维处理
tsne = TSNE(n_components=3, verbose=1 ,random_state=42)
result = tsne.fit_transform(X)
# 归一化处理
scaler = preprocessing.MinMaxScaler(feature_range=(-1,1))
result = scaler.fit_transform(result)
# 3D可视化展示
fig = plt.figure(figsize=(14, 14))
ax = fig.add_subplot(projection='3d')
ax.set_title('t-SNE process')
ax.scatter(result[:,0], result[:,1], result[:,2] , c=y, s=10)
输出:
[t-SNE] Computing 91 nearest neighbors...
[t-SNE] Indexed 1797 samples in 0.000s...
[t-SNE] Computed neighbors for 1797 samples in 0.119s...
[t-SNE] Computed conditional probabilities for sample 1000 / 1797
[t-SNE] Computed conditional probabilities for sample 1797 / 1797
[t-SNE] Mean sigma: 11.585657
[t-SNE] KL divergence after 250 iterations with early exaggeration: 61.700397
[t-SNE] KL divergence after 1000 iterations: 0.615485
可以看见,t-SNE无论是将数据降维到2维或者是3维,其可视化的效果都是比较好的,可以明显的看出来数据之间的分布是有一定距离的。
2. PCA降维效果
2.1 2D可视化
from sklearn.decomposition import PCA
from sklearn.preprocessing import MinMaxScaler
from sklearn import datasets
# 颜色设置
color = ['#FFFAFA', '#BEBEBE', '#000080', '#87CEEB', '#006400',
'#00FF00', '#4682B4', '#D02090', '#8B7765', '#B03060']
# 导入数据
X, y = datasets.load_digits(return_X_y=True)
# PCA降维(一种矩阵分解的方法)
pca = PCA(n_components=2, random_state=42)
result = pca.fit_transform(X)
# 归一化处理
scaler = MinMaxScaler(feature_range=(-1., 1.), copy=True)
result = scaler.fit_transform(result)
# 可视化展示
plt.figure(figsize=(10, 10))
plt.xlim((-1.1, 1.1))
plt.ylim((-1.1, 1.1))
plt.title('PCA process')
for i in range(len(result)):
plt.text(result[i,0], result[i,1], str(y[i]),
color=color[y[i]], fontdict={'weight': 'bold', 'size': 9})
2.2 3D可视化
from sklearn.decomposition import PCA
from sklearn.preprocessing import MinMaxScaler
from sklearn import datasets
# 颜色设置
color = ['#FFFAFA', '#BEBEBE', '#000080', '#87CEEB', '#006400',
'#00FF00', '#4682B4', '#D02090', '#8B7765', '#B03060']
# 导入数据
X, y = datasets.load_digits(return_X_y=True)
# PCA降维(一种矩阵分解的方法)
pca = PCA(n_components=3, random_state=42)
result = pca.fit_transform(X)
# 归一化处理
scaler = MinMaxScaler(feature_range=(-2., 2.), copy=True)
result = scaler.fit_transform(result)
# 可视化展示
fig = plt.figure(figsize=(14, 14))
ax = fig.add_subplot(111, projection='3d')
# 标题,范围,标签设置
ax.set_title('PCA process')
ax.set_xlim((-2.1, 2.1))
ax.set_ylim((-2.1, 2.1))
ax.set_zlim((-2.1, 2.1))
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# 绘制散点图(直接在对应位置上画出标签值)
for i in range(len(result)):
ax.text(result[i, 0], result[i, 1], result[i, 2], str(y[i]),
color=color[y[i]], fontdict={'weight': 'bold', 'size': 9})
3. KernelPCA降维效果
3.1 2D可视化
from sklearn.decomposition import KernelPCA
from sklearn.preprocessing import MinMaxScaler
# 导入数据
X, y = datasets.load_digits(return_X_y=True)
# PCA降维(一种矩阵分解的方法)
pca = KernelPCA(n_components=2, random_state=42)
result = pca.fit_transform(X)
# 归一化处理
scaler = MinMaxScaler(feature_range=(-1, 1), copy=True)
result = scaler.fit_transform(result)
# 可视化展示
plt.figure(figsize=(10, 10))
plt.title('KernelPCA process')
plt.scatter(result[:,0], result[:,1], c=y, s=10)
3.2 3D可视化
from sklearn.decomposition import KernelPCA
from sklearn.preprocessing import MinMaxScaler
from sklearn import datasets
# 颜色设置
color = ['#000000', '#BEBEBE', '#000080', '#87CEEB', '#006400',
'#00FF00', '#4682B4', '#D02090', '#8B7765', '#104E8B']
# 导入数据
X, y = datasets.load_digits(return_X_y=True)
# PCA降维(一种矩阵分解的方法)
kernel_pca = KernelPCA(n_components=3, random_state=42)
result = kernel_pca.fit_transform(X)
# 归一化处理
scaler = MinMaxScaler(feature_range=(-2., 2.), copy=True)
result = scaler.fit_transform(result)
# 可视化展示
fig = plt.figure(figsize=(14, 14))
ax = fig.add_subplot(111, projection='3d')
# 标题,范围,标签设置
ax.set_title('KernelPCA process')
ax.set_xlim((-2.1, 2.1))
ax.set_ylim((-2.1, 2.1))
ax.set_zlim((-2.1, 2.1))
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# 绘制散点图(直接在对应位置上画出标签值)
for i in range(len(result)):
ax.text(result[i, 0], result[i, 1], result[i, 2], str(y[i]),
color=color[y[i]], fontdict={'weight': 'bold', 'size': 9})
4. MDS降维效果
4.1 2D可视化
from sklearn.manifold import MDS
from sklearn.preprocessing import MinMaxScaler
# 导入数据
X, y = datasets.load_digits(return_X_y=True)
# 数据降维
mds = MDS(n_components=2, verbose=1, random_state=42)
result = mds.fit_transform(X)
# 归一化处理
scaler = MinMaxScaler(feature_range=(-1, 1), copy=True)
result = scaler.fit_transform(result)
# 可视化展示
plt.figure(figsize=(10, 10))
plt.title('MDS process')
plt.scatter(result[:,0], result[:,1], c=y, s=10)
4.2 3D可视化
from sklearn.manifold import MDS
from sklearn.preprocessing import MinMaxScaler
# 导入数据
X, y = datasets.load_digits(return_X_y=True)
# 数据降维
mds = MDS(n_components=3, verbose=1, random_state=42)
result = mds.fit_transform(X)
# 归一化处理
scaler = MinMaxScaler(feature_range=(-1, 1), copy=True)
result = scaler.fit_transform(result)
# 可视化展示
fig = plt.figure(figsize=(14, 14))
ax = fig.add_subplot(projection='3d')
ax.set_title('MDS process')
ax.scatter(result[:,0], result[:,1], result[:,2] , c=y, s=10)
5. SpectralEmbedding降维效果
5.1 2D可视化
from sklearn.manifold import SpectralEmbedding
from sklearn.preprocessing import MinMaxScaler
# 导入数据
X, y = datasets.load_digits(return_X_y=True)
# 数据降维
sp = SpectralEmbedding(n_components=2, random_state=42)
result = sp.fit_transform(X)
# 归一化处理
scaler = MinMaxScaler(feature_range=(-1, 1), copy=True)
result = scaler.fit_transform(result)
# 可视化展示
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111)
ax.set_title('SpectralEmbedding process')
ax.scatter(result[:,0], result[:,1], c=y, s=10)
5.2 3D可视化
from sklearn.manifold import SpectralEmbedding
from sklearn.preprocessing import MinMaxScaler
# 导入数据
X, y = datasets.load_digits(return_X_y=True)
# 数据降维
sp = SpectralEmbedding(n_components=3, random_state=42)
result = sp.fit_transform(X)
# 归一化处理
scaler = MinMaxScaler(feature_range=(-1, 1), copy=True)
result = scaler.fit_transform(result)
# 可视化展示
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(projection='3d')
ax.set_title('SpectralEmbedding process')
ax.scatter(result[:,0], result[:,1], result[:,2] , c=y, s=10)
6. 总结
通过可视化的比较,可以明显的看得出来,t-SNE的降维效果是最好的,那么如果使用t-SNE降维后再使用一些机器学习的分类方法能否可以超越深度学习的方法呢?这里我做了一个实验,详细见:t-SNE原理介绍与对手写数字MNIST的可视化结果,最后的答案效果是不好的。
线性降维方法包括PCA、LDA时间消耗较少,但是这种线性降维方法会丢失高维空间中的非线性结构信息。相比较而言,非线性降维方法(这里没有提到KPCA和KLDA,有兴趣的可以试一试这两类非线性降维方法)中的流形学习方法可以很好的保留高维空间中的非线性结构信息。虽然典型的流形学习是非监督的方法,但是也存在一些有监督方法的变体。
在进行数据降维时,我们一定要弄清楚我们降维的目的,是为了进行特征提取,使得之后的模型解释性更强或效果提升,还是仅仅为了可视化高维数据。在降维的方法的选择上,我们也要尽量平衡时间成本和降维效果。
另外,在降维时需要注意以下几点:
- 降维之前,所有特征的尺度是一致的;
- 重构误差可以用于寻找最优的输出维度𝑑d(此时降维不只是为了可视化),随着维度𝑑d的增加,重构误差将会减小,直到达到实现设定的阈值;
- 噪音点可能会导致流形出现“短路”,即原本在流形之中很容易分开的两部分被噪音点作为一个“桥梁”连接在了一起;
- 某种类型的输入数据可能导致权重矩阵是奇异的,比如在数据集有超过两个样本点是相同的,或者样本点被分在了不相交的组内。在这种情况下,特征值分解的实现solver='arpack’将找不到零空间。解决此问题的最简单的办法是使用solver='dense’实现特征值分解,虽然dense可能会比较慢,但是它可以用在奇异矩阵上。除此之外,我们也可以通过理解出现奇异的原因来思考解决的办法:如果是由于不相交集合导致,则可以尝试n_neighbors增大;如果是由于数据集中存在相同的样本点,那么可以尝试去掉这些重复的样本点,只保留其中一个。
这部分的总结来自参考资料2.
参考资料: