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

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

新規登録して質問してみよう
ただいま回答率
85.35%
機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Python

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

Q&A

解決済

1回答

1475閲覧

pythonで、kmeansを実装したいです

isonodayodayo

総合スコア6

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Python

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

0グッド

0クリップ

投稿2020/06/24 05:33

pythonで、kmeansを実装したいのですが、予想外の結果が出てきて驚いています。
コードを以下に示します。

Python

1import numpy as np 2from sklearn.datasets import load_iris 3import matplotlib.pyplot as plt 4%matplotlib inline 5 6iris=load_iris() 7data=iris.data 8label=iris.target 9plt.scatter(data[:,0],data[:,1],c=label) 10 11def culc_dist(target_data,centroid):#距離の計算 12 dist_holder=(target_data-centroid)**2 13 distance=np.sqrt(np.sum(dist_holder,axis=1)) 14 n_cluster=np.argmin(distance) 15 dist=distance[n_cluster] 16 return n_cluster,dist 17 18class kmeans: 19 def __init__(self,K,max_iter=100):#クラスターの数と、最大学習回数の初期化関数 20 if K < 2 or not(isinstance(K,int)):#Kは、学習回数2以上でかつ整数 21 raise ValueError("クラスター数に不適切な値が入力されています") 22 else: 23 self.K=K 24 25 if not(isinstance(max_iter,int)):#学習回数は、整数 26 raise ValueError("学習回数に不適切な値が入力されています。") 27 else: 28 self.max_iter=max_iter 29 30 def fit(self, X, y=None):#学習データのセット関数 31 self.X=X 32 self.y=np.zeros(self.X.shape[0]) 33 self.n_feature=X.shape[1] 34 self.centroids=np.random.uniform(0 , 1 , (self.K , self.n_feature)) 35 self.new_centroids=np.random.uniform(0 , 1 , (self.K , self.n_feature)) 36 self.SSE=0 37 38 def cluster_learn(self):#学習開始関数 39 for iter in range(self.max_iter):#学習回数分ループ 40 for data_index in range(self.X.shape[0]):#データの個数分ループ 41 n_cluster,dist=culc_dist(self.X[data_index],self.centroids) 42 self.y[data_index]=n_cluster 43 self.SSE+=dist 44 for label_num in range(self.K):#クラスター数分ループ 45 k_mask=(self.y==label_num) 46 masking_data=self.X[k_mask] 47 for feature in range(masking_data.shape[1]):#データの数分ループ 48 self.new_centroids[label_num][feature]=np.average(masking_data[:,feature]) 49 if (self.centroids==self.new_centroids).any(): 50 return self.centroids,self.y,self.SSE 51 else: 52 self.centroids=self.new_centroids 53 return self.centroids,self.y,self.SSE 54 55a=kmeans(3) 56a.fit(iris.data) 57a.cluster_learn() 58

実行結果がこちらです。

(array([[5.84333333, 3.05733333, 3.758 , 1.19933333], [ nan, nan, nan, nan], [ nan, nan, nan, nan]]), array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]), nan)

なぜか、クラスターがちゃんと保存されません。
おそらくcentroidから各データまでの距離の計算がおかしいんだと思いますが、どこが間違っているのかわかりません。
また、何回か実行するときちんとクラスタリングされるタイミングがあるのですが、それもなぜかわかりません。
こちらがそのうまくった例です。

(array([[4.65714286, 2.62857143, 2.1 , 0.55714286], [6.30103093, 2.88659794, 4.95876289, 1.69587629], [5.05869565, 3.4826087 , 1.47826087, 0.25 ]]), array([2., 2., 2., 2., 2., 2., 2., 2., 0., 2., 2., 2., 2., 0., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 0., 2., 2., 0., 2., 2., 2., 2., 2., 2., 2., 2., 1., 1., 1., 1., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 0., 1., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]), 1167.5006234945204)

どうすればいいでしょうか。
よろしくお願いいたします。

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

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

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

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

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

aokikenichi

2020/06/24 09:07

すみません、なぜこうなってるかは詳しく見てみないと分からないのですが scikit-learnにKMeansのライブラリがありますが、実装の勉強をなさっているということでしょうか c.f. https://qiita.com/maskot1977/items/34158d044711231c4292 >なぜか、クラスターがちゃんと保存されません。 これは乱数を使っているからです。 >np.random.uniform 等 乱数は毎回結果が異なります。再現させたいの出れば np.random.seed(123) とseedを指定するとよろしいです。
isonodayodayo

2020/06/24 14:06

回答が遅れました。申し訳ありません。 質問ありがとうございます。 おっしゃる通りです。実装の勉強をしています。 ご丁寧にURLまで、ありがとうございます!!! 参考にさせていただきます! random.seed()に関してですが、randomを実装している場面に書いてみましたが、うまくいきませんでした。 学習中に何か問題があるのでしょうか?
aokikenichi

2020/06/24 23:22

あなたのコードの中にて、例えば下記のように書きますと、 出力が固定されています デバッグのときは出力が変わると追いにくいのでまずはここから固めるとよろしいかと思います。 kmeansについては実装したことがないのでもう少しコードを詳しく見て何かわかればお伝えいたします。 (略) iris=load_iris() data=iris.data label=iris.target plt.scatter(data[:,0],data[:,1],c=label) np.random.seed(123) (略)
isonodayodayo

2020/06/25 05:21

ありがとうございます! ランダムで値を生成するコードの直前に、seed値の設定を追加しました。 値を追ってみたいと思います。
guest

回答1

0

自己解決

こんにちは。
解決に至りましたので、解決方法を示します。
まず、centroidsの初期化の方法ですが、random.uniform(01)で生成していましたが、そもそもiris datasets のデータの範囲が、(がくの長さの範囲は)約4.37くらいの幅だったので、centroidsが近すぎたため、いちばん近い点が、一つになってしまったのだと思われます。うまくいったやつは、おそらく奇跡的に三つの点がばらけて配置されたことによるものだったのでしょう。

そこで、初期化の方法を無理やりばらけさせるようにしました。
該当のコードがこちらです。

kmeans.ipynb

1for feature_index in range(self.n_feature): 2 data_range=np.max(data[:,feature_index])-np.min(data[:,feature_index]) 3 data_width=data_range/self.K 4 data_min=np.min(data[:,feature_index]) 5 data_max=data_width+data_min 6 for cent_index in range(len(self.centroids)): 7 np.random.seed(123) 8 self.centroids[cent_index,feature_index]=np.random.uniform(data_min,data_max) 9 data_min+=data_width 10 data_max+=data_width

各特徴の最大最小から、データの範囲を求め、その範囲をクラスターの数で割り、最小からデータの範囲分の間からランダムで生成、その後、データの範囲を最大と最小に足し合わせ、無理やり近すぎないように配置しました。

調べてみたところ、sklearnのライブラリのKMeansでは、kmeans++と呼ばれる手法を使っていたそうです(データ点をランダムに選択、それ自身の座標をcentroidsにするそうです?)

また、質問の際に記述したコードですが、SSE(sum of squere errors)の求めかたが一部違っていました。
おそらくこれが正しいと思います。

culc_distance

1def culc_dist(target_data,centroid):#距離の計算 2 dist_holder=(target_data-centroid)**2 3 distance=np.sqrt(np.sum(dist_holder,axis=1)) 4 n_cluster=np.argmin(distance) 5 SSE=np.sum((target_data-centroid)[n_cluster]**2) 6 return n_cluster,SSE

この関数は、データから、centroidの距離を求め、最小の距離を選択、のちにSSEを返す関数です。

どなたか参考にされる場合は、こちらを理解した上で実装することをお勧めします。
また、より良い方法がありましたら、教えてくださると幸いです。

ありがとうございました。

投稿2020/06/29 02:49

isonodayodayo

総合スコア6

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問