クラスタリングのk-meansで画像の分類に挑戦
みなさん、画像はどうやって管理していますか?
私は画像ディレクトリに雑多に入れているため、いつも探すのが大変です。
整理したいけど自分でやるのは面倒・・・。
そんな時に「k-meansで分類できるんじゃない?」という話を聞いたので試してみました。
事前情報
自動分類にはPythonを利用します。
Pythonにはバージョンがありますが、今回は3を利用しています。
また、Pythonの「Jupyter notebook」というものを利用し、ブラウザからPythonが実行できる環境となっています。
Python: 3.7.1(Anaconda)
Jupyter notebook: 5.7.4
※Python3およびJupyter notebookのインストールは割愛いたします。
そもそもk-meansって何?
k-means(k平均法)とは、ウィキペディアによるとこんな感じだそうです。
k平均法(kへいきんほう、英: k-means clustering)は、非階層型クラスタリングのアルゴリズム。
クラスタの平均を用い、与えられたクラスタ数k個に分類することから、MacQueen がこのように命名した。
k-平均法(k-means)、c-平均法(c-means)とも呼ばれる。
つまり、「個数kを指定すると、その数に自動的に分類してくれる」という感じみたいです。
分類してみる
では画像を分類していきましょう。
まずは各種ライブラリなどを読み込みます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# ライブラリ読み込み・初期化 # -*- coding: utf-8 -*- import os import pwd import grp import glob from PIL import Image import numpy as np from sklearn.cluster import KMeans import matplotlib.pyplot as plt from sklearn.decomposition import IncrementalPCA %matplotlib inline np.random.seed(5) print("Init done.") |
次に、画像が保存されているディレクトリを指定し、中身のファイル一覧を取ってきます。
※ここではソースコードと同じディレクトリにある「img」ディレクトリ内のjpgファイルのみ取得しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# リソースディレクトリの設定、配列追加 unclassified_label = "img" output_path = "." img_paths = [] for root, dirs, files in os.walk("./" + unclassified_label + "/") : for file in files : if file.endswith(".jpg") : img_paths.append(os.path.join(root, file)) img_num = len(img_paths) print("Image number:", img_num) print("Image list make done.") |
画像の一覧が取得出来たら、画像サイズを取得・チェックしましょう。
1 2 3 4 5 6 7 8 9 |
# イメージサイズの取得 for i in img_paths[::124]: img = Image.open(i) print(img.size) plt.figure() plt.imshow(np.asarray(img)) print("Size Check done.") |
画像サイズを調整しながら、データセットを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# imgサイズの調整・配列に追加 plt.close("all") img_paths = img_paths[:600] print(len(img_paths)) def img_to_matrix(img): img_array = np.asarray(img) return img_array def flatten_img(img_array): s = img_array.shape[0] * img_array.shape[1] * img_array.shape[2] img_width = img_array.reshape(1, s) return img_width[0] dataset = [] for i in img_paths: img = Image.open(i) img = img.resize((int(1232/4), int(1754/4)), Image.BICUBIC) img = img_to_matrix(img) img = flatten_img(img) dataset.append(img) dataset = np.array(dataset) print(dataset.shape) print("Dataset make done.") |
データセットは多次元になっており、次元数が多すぎるため、「次元数の圧縮」を行います。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 次元数の圧縮 # batch_size < len(dataset) && n_components < batch_size である必要がある n = dataset.shape[0] batch_size = img_num ipca = IncrementalPCA(n_components=batch_size-1) for i in range(n//batch_size): r_dataset = ipca.partial_fit(dataset[i*batch_size:(i+1)*batch_size]) r_dataset = ipca.transform(dataset) print(r_dataset.shape) print("PCA done.") |
さて、お待たせしました。
クラスタ数を指定し、分類を行っていきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# K-means によるクラスタリング n_clusters = 5 kmeans = KMeans(n_clusters=n_clusters, random_state=5).fit(r_dataset) labels = kmeans.labels_ print("K-means clustering done.") uid = pwd.getpwnam("apache").pw_uid gid = grp.getgrnam("apache").gr_gid for i in range(n_clusters): label = np.where(labels==i)[0] # Image placing if not os.path.exists(output_path+"/img_"+str(i)): os.makedirs(output_path+"/img_"+str(i)) os.chown(output_path+"/img_"+str(i), uid, gid) for j in label: img = Image.open(img_paths[j]) fname = img_paths[j].split('/')[-1] img.save(output_path+"/img_"+str(i)+"/" + fname) os.chown(output_path+"/img_"+str(i)+"/" + fname, uid, gid) print("Image placing done.") |
ここまでのプログラムを実行すると、img内のjpg画像が5つに分類されるかと思います。
(「img_0」 から 「img_4」 まで5つに分けられ、中に画像がコピーされます)
個数を指定したくない!!
何個に分類するか、それすらも自動化したいめんどくさがりやさんな人は多いと思います。
そんな方に朗報、 「x-means」 というものがあるそうです。
こちらはk-meansの 「k」 を自動推定してくれるので、クラスタ数を指定する必要がなくなります。
x-meansによるクラスタ数の推定
今回は「pyclustering」というライブラリを使用して、x-meansのクラスタ数推定を行ってみます。
まずはライブラリの読み込みを追加しましょう。
Step1の部分を改修します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# ライブラリ読み込み・初期化 # -*- coding: utf-8 -*- import os import pwd import grp import glob from PIL import Image import numpy as np from sklearn.cluster import KMeans import matplotlib.pyplot as plt from sklearn.decomposition import IncrementalPCA %matplotlib inline # x-means用 from pyclustering.cluster.xmeans import xmeans, kmeans_plusplus_initializer from pyclustering.utils import draw_clusters np.random.seed(5) print("Init done.") |
続いて、クラスタ数を指定する前に、x-meansでクラスタ数推定をします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# np配列から通常配列を取得 points = r_dataset.tolist() # クラスタ数2から探索させてみる initial_centers = kmeans_plusplus_initializer(points, 2).initialize() # クラスタリングの実行 instances = xmeans(points, initial_centers, ccore=True) instances.process() # クラスタはget_clustersで取得できる clusters = instances.get_clusters() # 最適クラスタサイズを取得 cluster_size = len(clusters) print("Cluster Size: " + str(cluster_size)) |
あとは最適クラスタサイズを指定して分類すればOKです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# K-means によるクラスタリング # n_clusters = 5 n_clusters = cluster_size kmeans = KMeans(n_clusters=n_clusters, random_state=5).fit(r_dataset) labels = kmeans.labels_ print("K-means clustering done.") uid = pwd.getpwnam("apache").pw_uid gid = grp.getgrnam("apache").gr_gid for i in range(n_clusters): label = np.where(labels==i)[0] # Image placing if not os.path.exists(output_path+"/img_"+str(i)): os.makedirs(output_path+"/img_"+str(i)) os.chown(output_path+"/img_"+str(i), uid, gid) for j in label: img = Image.open(img_paths[j]) fname = img_paths[j].split('/')[-1] img.save(output_path+"/img_"+str(i)+"/" + fname) os.chown(output_path+"/img_"+str(i)+"/" + fname, uid, gid) print("Image placing done.") |
これでめんどくさがりな人も分類ができるはず!!
さいごに
このx-meansによる最適クラスタ数推定ですが、
「確実なものではなく、大枠としての最適数が取得できる」だけで、
確実なクラスタ数が取得できるわけではありません。
また、この「教師なし学習」と呼ばれる方法においては、
「素材となる画像によって、分類方法が大きく異なることになる」ため、
「画像を足したら結果が変わった」というのはよくある話です。
ですので「完全な分類」をするのではなく、「与えられたものの中で大きく分類する」くらいの感覚が非常に近いです。
以上、画像分類の自動化のお話でした。
- おすすめ記事
-
-
もきち2017.12.05
-
POPULAR
のえる
Full-stack Developer