Auto Encoderによる可視化

Auto Encoderを用いた説明変数間の特徴の可視化

説明変数の特徴はデータ分析をする上でいくつかのヒントを持っています。もしくは次元削減を行ってから解析を行う方が効率的な場合もあります。
Auto Encoderには初期値の決定や可視化などいくつかの利用方法や派生があると思いますが、今回は入力層と出力層と一つの中間層からなるニューラルネットワークモデルを用いてデータの説明変数を2つの中間層ユニットで表現することで可視化を行います。
Auto Encoderによる可視化の例について見ていきたいと思います。

Requirements

For this tutorial, you will need the following modules

  • numpy 1.13.1
  • matplotlib 2.0.2
  • scikit-learn 0.18.2
In [1]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from sklearn.preprocessing import LabelBinarizer
from sklearn.datasets import load_iris, fetch_mldata

import renom as rm
from renom.optimizer import Adam
from renom.cuda import set_cuda_active
set_cuda_active(False)

データのクラス毎のデータ数

データを読み込んでそれぞれのクラスごとのデータ数を示します

In [2]:
iris = load_iris()
X = iris.data
y = iris.target

X = X.astype(np.float32)
y = y.astype(np.float32)

left = []
height = []
label = []
uniq_label = list(set(y))
for i in range(0, len(uniq_label)):
    label.append(uniq_label[i])
    left.append(uniq_label[i])
    height.append(len(y[y==uniq_label[i]]))
plt.bar(left, height, color="black", tick_label=label, align="center")
plt.xlabel("Label")
plt.ylabel("Samples")
plt.show()
../../../_images/notebooks_visualization_autoencoder_visualization_notebook_4_0.png

Modelの定義

Auto Encoderのモデルを定義し、predict関数として可視化の実装を行います。
中間層のユニットは2に設定します。
Auto Encoderは入力と出力が同じデータになっており、入力データを一度中間層で2次元で表現させてから出力層で入力データと同じものを復元できるように学習を行います。そのため上手く学習できた場合は次元数の多い入力データ数をより少ない次元数で表すことができるということを意味しています。
一般的に説明変数同士で相関を持つ場合はAuto Encoderによって入力データの簡潔な表現を学習することができます。例えば自然言語を用いたニュース記事分類を考えてもサッカーや野球、ゴール、得点などは共通した文書に出てくることが多いと思いますので、そのようなスポーツというような上位概念を学習出来る可能性があるということです。
In [3]:
lb = LabelBinarizer().fit(y)
N = len(X)
class AutoEncoder(rm.Model):
    def __init__(self):
        super(AutoEncoder, self).__init__()
        self.layer1 = rm.Dense(2)
        self.layer2 = rm.Dense(4)

    def forward(self, X):
        t1 = self.layer1(X)
        out = self.layer2(t1)
        return out

    def predict(self, X, y):
        t1 = self.layer1(X)
        uniq_label = list(set(y))
        for i in range(0, len(uniq_label)):
            plt.scatter(t1[y==uniq_label[i], :][:, 0], t1[y==uniq_label[i], :][:, 1], color=cm.get_cmap("tab20").colors[i], label=str(uniq_label[i]), alpha=0.5)
        plt.legend()
        plt.show()
        out = self.layer2(t1)
        return out

Run the Model

しかしAuto Encoderにはランダム性があり、初期値によって結果が異なる可能性があります。
学習するたびに結果も変わりますし、時には可視化したときの形が大きくことなるときもあります。
Auto Encoderを2回続けて初期化して可視化を行いたいと思います。これが1回目です。
In [4]:
model = AutoEncoder()
optimizer = Adam()

batch = 64
epoch = 100
for i in range(epoch):
    for j in range(N//batch):
        train_batch = X[j*batch : (j+1)*batch]
        with model.train():
            z = model.forward(train_batch)
            loss = rm.mse(z, train_batch)
        loss.grad().update(optimizer)
    if i%5 == 0:
        print("epoch %2d train_loss:%f" % (i, loss))

pred = model.predict(X, y)
epoch  0 train_loss:66.602905
epoch  5 train_loss:63.385071
epoch 10 train_loss:60.402035
epoch 15 train_loss:57.676476
epoch 20 train_loss:55.197670
epoch 25 train_loss:52.943356
epoch 30 train_loss:50.887455
epoch 35 train_loss:49.004417
epoch 40 train_loss:47.271103
epoch 45 train_loss:45.667252
epoch 50 train_loss:44.175373
epoch 55 train_loss:42.780369
epoch 60 train_loss:41.469101
epoch 65 train_loss:40.230038
epoch 70 train_loss:39.052925
epoch 75 train_loss:37.928589
epoch 80 train_loss:36.848644
epoch 85 train_loss:35.805466
epoch 90 train_loss:34.792000
epoch 95 train_loss:33.801750
../../../_images/notebooks_visualization_autoencoder_visualization_notebook_8_1.png

Run the Model

これが2回目です。

In [5]:
model = AutoEncoder()
optimizer = Adam()

batch = 64
epoch = 100
for i in range(epoch):
    for j in range(N//batch):
        train_batch = X[j*batch : (j+1)*batch]
        with model.train():
            z = model.forward(train_batch)
            loss = rm.mse(z, train_batch)
        loss.grad().update(optimizer)
    if i%5 == 0:
        print("epoch %2d train_loss:%f" % (i, loss))

pred = model.predict(X, y)
epoch  0 train_loss:53.558716
epoch  5 train_loss:51.806366
epoch 10 train_loss:50.138603
epoch 15 train_loss:48.602467
epoch 20 train_loss:47.198566
epoch 25 train_loss:45.918667
epoch 30 train_loss:44.751385
epoch 35 train_loss:43.684692
epoch 40 train_loss:42.707012
epoch 45 train_loss:41.807610
epoch 50 train_loss:40.976612
epoch 55 train_loss:40.204933
epoch 60 train_loss:39.484173
epoch 65 train_loss:38.806442
epoch 70 train_loss:38.164253
epoch 75 train_loss:37.550442
epoch 80 train_loss:36.958046
epoch 85 train_loss:36.380226
epoch 90 train_loss:35.810226
epoch 95 train_loss:35.241344
../../../_images/notebooks_visualization_autoencoder_visualization_notebook_10_1.png
今回は3クラスで4つの説明変数を持つirisデータセットを用いて可視化を行いました。irisはクラス数も説明変数も少なく、分類も簡単なケースです。
そこでより複雑なケースに対してAuto Encoderによってどのような可視化がされるのかについて見るために10クラスで784の説明変数を持つMNISTデータセットを用いて可視化を行っていきます。
In [6]:
mnist = fetch_mldata("MNIST original", data_home=".")
X = mnist.data / 255
y = mnist.target

X = X.astype(np.float32)
y = y.astype(np.float32)

left = []
height = []
label = []
uniq_label = list(set(y))
for i in range(0, len(uniq_label)):
    label.append(uniq_label[i])
    left.append(uniq_label[i])
    height.append(len(y[y==uniq_label[i]]))
plt.clf()
plt.bar(left, height, color="black", tick_label=label, align="center")
plt.xlabel("Label")
plt.ylabel("Samples")
plt.show()
../../../_images/notebooks_visualization_autoencoder_visualization_notebook_12_0.png

MNIST用のモデル作成

In [7]:
lb = LabelBinarizer().fit(y)
N = len(X)
class AutoEncoder(rm.Model):
    def __init__(self):
        super(AutoEncoder, self).__init__()
        self.layer1 = rm.Dense(2)
        self.layer2 = rm.Dense(784)

    def forward(self, X):
        t1 = self.layer1(X)
        out = self.layer2(t1)
        return out

    def predict(self, X, y):
        t1 = self.layer1(X)
        uniq_label = list(set(y))
        for i in range(0, len(uniq_label)):
            plt.scatter(t1[y==uniq_label[i], :][:, 0], t1[y==uniq_label[i], :][:, 1], color=cm.get_cmap("tab20").colors[i], label=str(uniq_label[i]), alpha=0.5)
        plt.legend()
        plt.show()
        out = self.layer2(t1)
        return out

Run the Model

MNIST用のAuto Encoderによる可視化の計算

In [8]:
model = AutoEncoder()
optimizer = Adam()

batch = 64
epoch = 100
for i in range(epoch):
    for j in range(N//batch):
        train_batch = X[j*batch : (j+1)*batch]
        with model.train():
            z = model.forward(train_batch)
            loss = rm.mse(z, train_batch)
        loss.grad().update(optimizer)
    if i%5 == 0:
        print("epoch %2d train_loss:%f" % (i, loss))

pred = model.predict(X, y)
epoch  0 train_loss:18.098236
epoch  5 train_loss:19.707720
epoch 10 train_loss:18.138201
epoch 15 train_loss:18.016071
epoch 20 train_loss:17.998529
epoch 25 train_loss:17.979641
epoch 30 train_loss:17.986599
epoch 35 train_loss:17.984344
epoch 40 train_loss:18.061237
epoch 45 train_loss:18.016376
epoch 50 train_loss:18.029505
epoch 55 train_loss:18.093777
epoch 60 train_loss:18.066256
epoch 65 train_loss:18.138508
epoch 70 train_loss:18.124315
epoch 75 train_loss:18.271906
epoch 80 train_loss:18.221874
epoch 85 train_loss:18.161594
epoch 90 train_loss:18.229214
epoch 95 train_loss:18.162909
../../../_images/notebooks_visualization_autoencoder_visualization_notebook_16_1.png
上で見たように多くのクラス数と次元数を持つデータに対してはAuto Encoderによる可視化で上手く分離させることはできませんでした。
PCAやt-SNEと違ってAuto Encoderは入力を出力としてもう一度再現をするためによりよい入力の簡潔な表現を学習します。
Auto Encoderによる結果は例えば全結合ニューラルネットワークでどれだけ分類が出来そうかのある程度の指標や説明変数同士の関連を見る上で有用な手法であります。
しかし一方でPCAやt-SNEなどといった手法も合わせて考えたときにどの手法が一番ベストかどうかを決めるのは難しいです。どの手法もそれぞれ注目しているデータの特徴があり、それに合わせた可視化手法を用いることでこれまで解明されなかったデータの特徴を見つけられるのではないかと考えています。