一个算法小白在拿到数据的第一步可能会按照下面的流程进行:
| 清洗数据 | => | 划分训练集/测试集 | => | 上模型 | => | 得到结果 | => | 胡搞 |

作为一个稍有经验的算法工程师,也许应该更加注重前期对数据的分析,因为——每次操作数据运行模型烧的都是钱啊!因此在前期对数据/特征的分析其实还是挺重要的,这一篇文章总结一下我常用的数据降维可视化方案:使用t-SNE降维分析特征。

降维算法

常用的降维算法有PCA和LDA,但是这两个算法的降维可视化效果并不是很好(一般说的降维可视化都是将数据降维到2-3维),因此就有了SNE算法以及其优化版本t-SNE算法。

关于t-SNE算法原理解释这里先挖个坑,以后自己重新整理一遍,感兴趣的读者可以先到这里了解一下。

t-SNE降维代码

t-SNE降维的原理复杂,但是sklearn帮我们实现了它,其调用特别简单:

from sklearn.manifold import TSNE
import matplotlib.pyplot as plt

# 使用下面的代码做t-SNE降维,得到的降维结果保存在X_embedded中
X_embedded = TSNE(n_components=2).fit_transform(X)

# 使用下面的代码绘制降维结果即可
plt.scatter(X_embedded[:, 0], X_embedded[:, 1])

示例代码及效果展示

t-SNE能够将高维数据降至2-3维,同时还能保持数据原本的簇的形式,便于分析数据(在有的数据中,PCA的降维可视化效果也不错)。下面的示例程序借助sklearn中的make_blobs函数生成簇状的仿真数据,来测试在高纬度成簇的数据使用t-SNE降维之后的效果:

from sklearn.datasets import make_blobs
from sklearn.manifold import TSNE
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

def get_data(n_samples, n_features):
    X, y = make_blobs(n_samples=n_samples, n_features=n_features, random_state=7)
    return X, y

def plot_subplot(X, y):
    x_min, x_max = X.min(0), X.max(0)
    X_norm = (X - x_min) / (x_max - x_min)

    X0_0, X0_1 = X_norm[y == 0, 0], X_norm[y == 0, 1]
    X1_0, X1_1 = X_norm[y == 1, 0], X_norm[y == 1, 1]
    X2_0, X2_1 = X_norm[y == 2, 0], X_norm[y == 2, 1]

    for i in range(len(X0_0)):
        plt.text(X0_0[i], X0_1[i], '0', color='red')
    for i in range(len(X1_0)):
        plt.text(X1_0[i], X1_1[i], '1', color='green')
    for i in range(len(X2_0)):
        plt.text(X2_0[i], X2_1[i], '2', color='blue')

fig = plt.figure(figsize=(15, 25))
idx = 1
line = 5
for i in range(line):
    X, y = get_data(500, i + 2)
    plt.subplot(line, 3, idx)
    plot_subplot(X, y)
    plt.title("Full dimension(" + str(X.shape[1]) + ')')
    idx += 1
    
    X_embedded = TSNE(n_components=2).fit_transform(X)
    plt.subplot(line, 3, idx)
    plot_subplot(X_embedded, y)
    plt.title("TSNE result")
    idx += 1
    
    X_embedded = PCA(n_components=2).fit_transform(X)
    plt.subplot(line, 3, idx)
    plot_subplot(X_embedded, y)
    plt.title("PCA result")
    idx += 1

结果如下图所示,可见原本在高纬度可分的数据在使用PCA或者t-SNE降维之后其仍然是可分的。

数据可视化画图次方 数据可视化算法_PCA


下面加大点难度,让数据中混杂一部分随机的不可分数据,再来看看效果如何,首先看代码:

from sklearn.datasets import make_blobs, make_gaussian_quantiles
from sklearn.manifold import TSNE
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

def get_data(n_samples, n_features):
    X, y = make_blobs(n_samples=n_samples, n_features=n_features, random_state=7)
    X_random, y_random = make_gaussian_quantiles(n_samples=n_samples, n_features=n_features, random_state=7)
    y_random += (max(y) + 1)
    X = np.concatenate([X, X_random])
    y = np.concatenate([y, y_random])
    return X, y

def plot_subplot(X, y):
    x_min, x_max = X.min(0), X.max(0)
    color_list = ['red', 'green', 'blue', 'yellow', 'pink', 'gray']
    X_norm = (X - x_min) / (x_max - x_min)

    for i in range(max(y)):
        X0, X1 = X_norm[y == i, 0], X_norm[y == i, 1]
        for j in range(len(X0)):
            plt.text(X0[j], X0[j], str(i), color=color_list[i])
   

fig = plt.figure(figsize=(15, 25))
idx = 1
line = 5
for i in range(line):
    X, y = get_data(500, i + 2)
    plt.subplot(line, 3, idx)
    plot_subplot(X, y)
    plt.title("Full dimension(" + str(X.shape[1]) + ')')
    idx += 1
    
    X_embedded = TSNE(n_components=2).fit_transform(X)
    plt.subplot(line, 3, idx)
    plot_subplot(X_embedded, y)
    plt.title("TSNE result")
    idx += 1
    
    X_embedded = PCA(n_components=2).fit_transform(X)
    plt.subplot(line, 3, idx)
    plot_subplot(X_embedded, y)
    plt.title("PCA result")
    idx += 1

其效果是这样的~其实这个例子举得不是特别好,因为make_gaussian_quantiles得到的数据并不是特别的随机,也就是说有三个类的数据是非常接近的(就是下图中粉红色黄色的那一团)。

数据可视化画图次方 数据可视化算法_数据可视化画图次方_02


另外,在实际的场景中的二分类的数据可能有下面这样奇特的场景:属于一个类的数据的可视化结果并不是成一个簇,而是会有多个不同的簇。在这种奇怪的场景下你就会发现,书上教的不够用了!这时候就需要动用自己的聪明才智各显神通了。