CUBE SUGAR CONTAINER

技術系のこと書きます。

Python: UMAP を使ってみる

UMAP (Uniform Manifold Approximation and Projection) は次元削減手法のひとつ。 似た手法としては t-SNE (t-distributed Stochastic Neighbor Embedding) があるけど、それよりも高速らしい。 公式のベンチマークが以下で紹介されていて、t-SNE に比べるとスケーラビリティに優れていることが伺える。

umap-learn.readthedocs.io

使った環境は次のとおり。

$ sw_vers 
ProductName:    Mac OS X
ProductVersion: 10.14.6
BuildVersion:   18G5033
$ python -V                          
Python 3.7.7
$ pip list | grep -i umap            
umap-learn                0.4.4

もくじ

下準備

$ pip install umap-learn scikit-learn

Digit データセットを次元削減してみる

scikit-learn に付属している数字の画像データセットを使って動作を確認してみよう。 UMAP は scikit-learn に準拠したインターフェースを提供している。 以下のサンプルコードでは、元が 8 x 8 ピクセルから成る 64 次元の画像データを、UMAP で 2 次元にまで削減している。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import numpy as np
from matplotlib import pyplot as plt
from sklearn import datasets
import umap


def main():
    # データセットを読み込む
    dataset = datasets.load_digits()
    X, y = dataset.data, dataset.target

    # 次元削減する
    mapper = umap.UMAP(random_state=0)
    embedding = mapper.fit_transform(X)

    # 結果を二次元でプロットする
    embedding_x = embedding[:, 0]
    embedding_y = embedding[:, 1]
    for n in np.unique(y):
        plt.scatter(embedding_x[y == n],
                    embedding_y[y == n],
                    label=n)

    # グラフを表示する
    plt.grid()
    plt.legend()
    plt.show()


if __name__ == '__main__':
    main()

上記を保存して実行する。

$ python digitsumap.py

すると、次のようなグラフが得られる。

t-SNE をはじめて使ったときも思ったけど、教師なしでここまで同じラベルのデータがまとまるのは不思議。

t-SNE も試してみる

一応、比較対象として t-SNE も試しておく。 以下のサンプルコードは、基本的に先ほどのコードで umap.UMAP()sklearn.manifold.TSNE に変えただけ。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import numpy as np
from matplotlib import pyplot as plt
from sklearn import datasets
from sklearn import manifold


def main():
    # データセットを読み込む
    dataset = datasets.load_digits()
    X, y = dataset.data, dataset.target

    # 次元削減する
    mapper = manifold.TSNE(random_state=0)
    embedding = mapper.fit_transform(X)

    # 結果を二次元でプロットする
    embedding_x = embedding[:, 0]
    embedding_y = embedding[:, 1]
    for n in np.unique(y):
        plt.scatter(embedding_x[y == n],
                    embedding_y[y == n],
                    label=n)

    # グラフを表示する
    plt.grid()
    plt.legend()
    plt.show()


if __name__ == '__main__':
    main()

上記を保存して実行する。

$ digitstsne.py

すると、次のようなグラフが得られる。

おそらく学習に使うパラメータにもよるだろうけど、UMAP の方がぎゅっとまとまってる感じかな。

いじょう。