ナード戦隊データマン

データサイエンスを用いて悪と戦うぞ

教師なし学習で画像分類をする

教師なし学習の主な目的はEDAです。しかし、類似画像を教師なし学習で表示するようなサービスもあります。ここでは、教師なし学習であるKmeans, AgglomerativeClustering, DBSCANを比較します。

顔画像データの準備

まず、利用するデータをインポートしてデータについて少々理解を得ていきましょう。

In[1]:

%matplotlib inline
from sklearn.datasets import fetch_lfw_people
import matplotlib.pyplot as plt

people = fetch_lfw_people(min_faces_per_person=20, resize=0.7)

image_shape = people.images[0].shape
fix, axis = plt.subplots(2, 5, figsize=(15, 8), subplot_kw={'xticks':(), 'yticks':()})
for target, image, ax in zip(people.target, people.images, axis.ravel()):
    ax.imshow(image)
    ax.set_title(people.target_names[target])

Out[1]: download.png

上のような顔画像データをインポートしています。顔画像のそれぞれの人物のサイズを見てみます。

In[2]:

import numpy as np
counts = np.bincount(people.target)

for i, (count, name) in enumerate(zip(counts, people.target_names)):
    print("{0:25} {1:3}".format(name, count), end='   ')
    if (i+1) % 3 == 0:
        print()

Out[2]:

Alejandro Toledo           39   Alvaro Uribe               35   Amelie Mauresmo            21   
Andre Agassi               36   Angelina Jolie             20   Ariel Sharon               77   
Arnold Schwarzenegger      42   Atal Bihari Vajpayee       24   Bill Clinton               29   
Carlos Menem               21   Colin Powell              236   David Beckham              31   
Donald Rumsfeld           121   George Robertson           22   George W Bush             530   
Gerhard Schroeder         109   Gloria Macapagal Arroyo    44   Gray Davis                 26   
Guillermo Coria            30   Hamid Karzai               22   Hans Blix                  39   
Hugo Chavez                71   Igor Ivanov                20   Jack Straw                 28   
Jacques Chirac             52   Jean Chretien              55   Jennifer Aniston           21   
Jennifer Capriati          42   Jennifer Lopez             21   Jeremy Greenstock          24   
Jiang Zemin                20   John Ashcroft              53   John Negroponte            31   
Jose Maria Aznar           23   Juan Carlos Ferrero        28   Junichiro Koizumi          60   
Kofi Annan                 32   Laura Bush                 41   Lindsay Davenport          22   
Lleyton Hewitt             41   Luiz Inacio Lula da Silva  48   Mahmoud Abbas              29   
Megawati Sukarnoputri      33   Michael Bloomberg          20   Naomi Watts                22   
Nestor Kirchner            37   Paul Bremer                20   Pete Sampras               22   
Recep Tayyip Erdogan       30   Ricardo Lagos              27   Roh Moo-hyun               32   
Rudolph Giuliani           26   Saddam Hussein             23   Serena Williams            52   
Silvio Berlusconi          33   Tiger Woods                23   Tom Daschle                25   
Tom Ridge                  33   Tony Blair                144   Vicente Fox                32   
Vladimir Putin             49   Winona Ryder               24   

偏りが大きいので、サイズを50に制限します。

In[3]:

from sklearn.decomposition import PCA
mask = np.zeros(people.target.shape, dtype=np.bool)
for target in np.unique(people.target):
    mask[np.where(people.target == target)[0][:50]] = 1
X_people = people.data[mask]
y_people = people.target[mask]

X_people = X_people / 255

pca = PCA(n_components=100, whiten=True, random_state=0)
X_pca = pca.fit_transform(X_people)

DBSCAN

まず、DBSCANを試してみます。近接度epsを設定する必要がありますが、クラスタ数は自動的に決定されます。

In[4]:

from sklearn.cluster import AgglomerativeClustering,DBSCAN,KMeans
for eps in [1, 3, 5, 7, 9, 11, 13, 15, 17]:
    print("eps:{}".format(eps))
    dbscan = DBSCAN(eps=eps, min_samples=3)
    labels = dbscan.fit_predict(X_pca)
    print("Clusters present:{}".format(np.unique(labels)))
    print("Clusters size:{}".format(np.bincount(labels + 1)))
    print()

Out[4]:

eps:1
Clusters present:[-1]
Clusters size:[2063]

eps:3
Clusters present:[-1]
Clusters size:[2063]

eps:5
Clusters present:[-1]
Clusters size:[2063]

eps:7
Clusters present:[-1  0  1  2  3  4  5  6  7  8  9 10 11 12]
Clusters size:[2003    4   14    7    4    3    3    4    4    3    3    5    3    3]

eps:9
Clusters present:[-1  0  1  2]
Clusters size:[1306  751    3    3]

eps:11
Clusters present:[-1  0]
Clusters size:[ 413 1650]

eps:13
Clusters present:[-1  0]
Clusters size:[ 120 1943]

eps:15
Clusters present:[-1  0]
Clusters size:[  31 2032]

eps:17
Clusters present:[-1  0]
Clusters size:[   6 2057]

-1はノイズを表しています。まともにクラスタが出力されているのはeps=7だけのようです。

In[5]:

dbscan = DBSCAN(eps=7, min_samples=3)
labels = dbscan.fit_predict(X_pca)

for cluster in range(max(labels)+1):
    mask = labels == cluster
    n_images = np.sum(mask)
    fig, axes = plt.subplots(1, n_images, figsize=(n_images + 1.5, 4),
                            subplot_kw={'xticks':(), 'yticks':()})
    for image, label, ax in zip(X_people[mask], y_people[mask], axes):
        ax.imshow(image.reshape(image_shape), vmin=0, vmax=1)
        ax.set_title(people.target_names[label].split()[-1])

Out[5]:

Screenshot from 2017-11-15 07-44-20.png

出力が大きいので一部のみ表示しています。小泉は日本人なので特徴があり、正しく分類されています。

Kmeans

つぎに、kmeansを用いてみます。kmeansはクラスタセンタを生成します。

In[6]:

km = KMeans(n_clusters=10, random_state=0)
labels_km = km.fit_predict(X_pca)
print("Cluster size k-means:{}".format(np.bincount(labels_km)))

Out[6]:

Cluster size k-means:[113 256 188 147 216 180 258 211 139 355]

In[7]:

import mglearn
mglearn.plots.plot_kmeans_faces(km, pca, X_pca, X_people, y_people, people.target_names)

Out[8]: download (2).png

一番左の出力がクラスタセンタ、その次の5つが最もクラスタセンタに近い画像、右の5つが最も遠い画像です。視線の方向や顔の角度が影響していることがわかります。

AgglomerativeClustering

最後に、AgglomerativeClusteringを試します。

In[8]:

agglomerative = AgglomerativeClustering(n_clusters=10)
labels_agg = agglomerative.fit_predict(X_pca)
print("Cluster size:{}".format(np.bincount(labels_agg)))

Out[8]:

Cluster size:[478 254 317 119  96 191 424  17  55 112]

ランドスコアをチェックし、Kmeansの結果と比較します。

In[9]:

from sklearn.metrics import adjusted_rand_score
adjusted_rand_score(labels_agg, labels_km)

Out[9]:

0.06975311521947071

0.069なので、Kmeansとは全く異なる結果を出力したことがわかります。分類された画像を見てみましょう。

In[10]:

n_clusters = 10
for cluster in range(n_clusters):
    mask = labels_agg == cluster
    fig, axes = plt.subplots(1, 10, subplot_kw={'xticks':(), 'yticks':()}, figsize=(15, 8))
    axes[0].set_ylabel(np.sum(mask))
    for image, label, asdf, ax in zip(X_people[mask], y_people[mask], labels_agg[mask], axes):
        ax.imshow(image.reshape(image_shape), vmin=0, vmax=1)
        ax.set_title(people.target_names[label].split()[-1], fontdict={'fontsize':9})

Out[10]: Screenshot from 2017-11-15 07-53-26.png

おわりに

ここでは、3つの教師なし学習を用いて画像分類しました。EDAで教師なし学習を使うと、データについてなんらかの理解を得られる可能性があります。しかし、実運用で教師なし学習を用いる場合、スコアリングができないという点に注意が必要です。また、複数のクラスタリングアルゴリズムがまったく異なる出力をすることにも注意したほうがよいでしょう。

参考

https://github.com/amueller/introduction_to_ml_with_python/blob/master/03-unsupervised-learning.ipynb