質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

5550閲覧

xmeans法による画像分類において求まったクラスタ数がおかしいです

a_3_

総合スコア1

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/08/03 15:14

前提・実現したいこと

xmeans法によるクラスタリングの精度向上

発生している問題・エラーメッセージ

任意のカラー画像を似通った画像ごとに分類したいのですが、ほとんどの場合クラスタ数が2になってしまいます。
例えば、ただのコピー画像同士は同じクラスタに分類されるのですが、クラスタ数が2であるゆえに他の画像と混ざってしまいます。
これをxmeans法である程度の精度で実現したいと考えております。xmeans法以外の手法は現時点では考えておりません。

念のためソースコードをすべて記載しますが、該当箇所は64行目以降(#クラスタ数の取得およびxmeansの実行 というサブタイトル)だと思われます。

該当のソースコード

python

1import shutil 2from skimage import data 3import os 4import glob 5from PIL import Image 6import numpy as np 7from sklearn.cluster import KMeans 8import matplotlib.pyplot as plt 9from sklearn.decomposition import IncrementalPCA 10from pyclustering.cluster.xmeans import xmeans, kmeans_plusplus_initializer 11from pyclustering.utils import draw_clusters 12 13 14# 入力画像の読み込み[リストの作成] 15img_paths = [] #リストの宣言 16for root, dirs, files in os.walk("C:\Users\user\python_VScode_cwd\画像が入っているフォルダ\"): 17 for file in files : 18 if file.endswith(".png"): 19 img_paths.append(file) 20img_num = len(img_paths) 21print("*************") 22print("入力画像の枚数 :", img_num) 23print("*************") 24 25 26# データセットの製作[画像データ→RGB値の2,3次元配列→平らにする(2次元にする)] 27plt.close("all") 28img_paths = img_paths[:600] 29def img_to_matrix(img): 30 img_array = np.asarray(img) 31 return img_array 32def flatten_img(img_array): 33 s = img_array.shape[0] * img_array.shape[1] * img_array.shape[2] #RGBカラー画像は3次元、白黒は2次元? 34 img_width = img_array.reshape(1, s) 35 return img_width[0] 36dataset = [] 37for i in img_paths: 38 a = "C:\Users\user\python_VScode_cwd\画像が入っているフォルダ\" + i 39 img = Image.open(a) 40 #img = img.resize((int(1232/4), int(1754/4)), Image.BICUBIC) 41 img = img_to_matrix(img) 42 #img_gray = 0.299 * img[:, :, 0] + 0.587 * img[:, :, 1] + 0.114 * img[:, :, 2] 43 img = flatten_img(img) 44 dataset.append(img) 45dataset = np.array(dataset) 46print(dataset.shape) 47print("データセットの作成完了") 48print("*************") 49 50 51# 次元数の圧縮 [主成分分析-PCA] 52# batch_size < len(dataset) && n_components < batch_size である必要がある 53n = dataset.shape[0] 54batch_size = img_num 55ipca = IncrementalPCA(n_components=batch_size-1) 56for i in range(n//batch_size): 57 r_dataset = ipca.partial_fit(dataset[i*batch_size:(i+1)*batch_size]) 58r_dataset = ipca.transform(dataset) 59print(r_dataset.shape) 60print("主成分分析(PCA)の完了") 61print("*************") 62 63#クラスタ数の取得およびxmeansの実行 64class XMeans(): 65 def fit(self,features): 66 #pyclusteringのxmeansで計算 67 initializer = kmeans_plusplus_initializer(data=features,amount_centers=2) 68 initial_centers = initializer.initialize() 69 xm = xmeans(data=features,initial_centers=initial_centers,kmax=50) #第三引数はkの最大値 70 xm.process() 71 # 72 #分類が出力される 73 #しかしこの出力の仕方がscikit-learnとは違うので厄介 74 # 75 clusters = xm.get_clusters() 76 #一次元配列flat_labelを用意 77 flat_label = np.array([]) 78 #sklearnでいうlabel_の大きさを調べる 79 for cluster in clusters: 80 flat_label = np.append(flat_label,cluster) 81 #正規のlabelを代入するための配列を確保 82 labels = np.zeros((1,flat_label.size)) 83 84 #pyclusteringのclustersはクラスター数次元配列があり、標本の名前がクラスターに分類され配列として返す。 85 #一方でsklearnのlabel_は標本と同様の順番に、その標本が属するクラスターの番号を配列で返す。 86 #下記のコードはその変換を行っている 87 # 88 for n,n_th_cluster in np.ndenumerate(clusters): 89 for img_num in n_th_cluster: 90 labels[0][img_num] = n[0] 91 #このままのラベルだと[[a........z]]の二重括弧になる。 92 #それはsklearnの仕様に沿わない。[a........z]の一重括弧にする。 93 # 94 self.labels_ = labels[0] 95 return self 96model = XMeans() 97x_means = model.fit(r_dataset) 98labels = x_means.labels_ 99print("X-means法によるクラスタリングの完了") 100print("*************") 101 102#ラベルの最大値=クラスタ数-1 103k_num_before=int(np.max(labels)) 104#Xmeansによって決まったk=最大値+1 105k_num=k_num_before+1 106 107# 保存 108n_clusters = k_num 109for i in range(n_clusters): 110 label = np.where(labels==i)[0] 111 num = i +1 112 if not os.path.exists("class"+str(num)): 113 os.makedirs("class"+str(num)) 114 for j in label: 115 img = Image.open("C:\Users\user\python_VScode_cwd\画像が入っているフォルダ\" + img_paths[j]) 116 fname = img_paths[j].split('/')[-1] 117 img.save("class"+str(num)+"/" + fname) 118print("新たなファイルとして保存完了") 119print("*************") 120print("すべての処理が終了しました")

試したこと

主成分分析の過程を飛ばす→成果なし
sklearnのk-meansとpyclusteringのx-meansの配列の返し方の違いを見直す→成果なし

補足情報(FW/ツールのバージョンなど)

pythonは最新版のものを使用しています
VScodeを使用しています

よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tiitoi

2020/08/03 15:26

どのような画像を分類しようとしているのでしょうか? MNIST レベルの簡単な画像なのか、犬と猫のように複雑な特徴を持つ画像なのかによっても難易度が変わってくると思います。
a_3_

2020/08/03 15:34

返信ありがとうございます! 例えば飛行機、川、木など風景写真です。 実は言うと大学の研究で引き継いだテーマなのですが、その方は白黒画像でやっていました。画像を2次元フーリエ変換し、模様をx-means法を用いて分類するというものです。7つほどのカテゴリに分類できてたそうです
tiitoi

2020/08/03 16:31

飛行機、川、木など風景写真となると様々なバリエーションが考えられるので、難しい問題といえます。 回答にも書きましたが、kmeans は高次元のデータには適していません。 前処理として画像から kmeans でも扱えるぐらいの次元数の特徴を抽出する必要があるかと思います。
a_3_

2020/08/04 04:43

ただでさえ次元数の多い画像がkmeans法に適していないのに、複雑な風景画像だともはやランダムに近い結果になってしまうということですね。納得しました!とてもわかりやすい回答ありがとうございました!
guest

回答1

0

ベストアンサー

kmeans 系の距離ベースのアルゴリズムは、特徴量の次元数が高い場合、次元の呪い の影響を受けるので、機能しません。そのため、画像のような高次元のデータを扱うには向いていません。

machine learning - Why is Euclidean distance not a good metric in high dimensions? - Cross Validated

How do I know my k-means clustering algorithm is suffering from the curse of dimensionality? - Cross Validated

kmeans 系のアルゴリズムを使うのであれば、画像の次元圧縮は必須です。
MNIST レベルの簡単な画像であれば PCA でも機能しますが、コメントにある「飛行機、川、木など風景写真」となると、特徴量も複雑になるので、PCA だと画像から有効な特徴を抽出するのは難しいのではないでしょうか。


hymenoptera_data という蟻と鉢の2クラスの画像分類用データセットで kmeans によるクラスタリングを試したところ、以下のような精度になりました。

  • 画像を (256, 256) にリサイズして、そのまま kmean に突っ込む: 58%
  • 画像を (256, 256) にリサイズして、PCA で200次元に圧縮して kmean に突っ込む: 57%
  • 画像を (256, 256) にリサイズして、ImageNet の事前学習済みの VGG16 で512次元に圧縮して kmean に突っ込む: 92%

※ クラスタ番号とラベル番号の対応付けは正答率が高いほうに紐付けました。

結果を見ればわかるように、画像をそのまま kmeans に突っ込んだり、PCA で次元圧縮して kmeans に突っ込んだ場合は、ランダムに答えるのとほぼ変わらない正答率しかでません。

一方、特徴抽出に CNN と使うと90%以上と kmeans でもかなり精度よく分類可能であることがわかりました。

kmeans をうまく機能させるには、その前段階の特徴抽出の方法が鍵となりそうです。


過去の関連質問

Python - メルカリのCNN後の次元削減手法について|teratail

投稿2020/08/03 16:29

編集2020/08/03 16:39
tiitoi

総合スコア21956

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問