環境構築からk平均法を用いた画像分類までをやってみた話

こちらは[株式会社BIOTOPE](https://biotope.co.jp)の社内イベント、第二回BIOTOPE HACKATHON のための資料です。
その資料を一般公開します。

もし改善点等ありましたらどなたでもご指摘いただけると幸いです。

#環境構築編

こちらはまだpython環境に全く触れたことない人向きです。
(対象はmacユーザの方です、Windowsはごめんなさい分からないです…)
私は普段pyenv_vertualenvを使っているので、そのinstallもサクッと載せておきます。



###Homebrewをinstall
ターミナルを起動し、以下コマンドをコピペします。

```/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```


###pyenv_virtualenvをinstall

詳しくは参考文献の部分を見てください
[仮想環境バージョン管理 まとめ Python](https://qiita.com/hedgehoCrow/items/0733c63c690450b14dcf#pyenv-virtualenv-1)


###環境を作成

作業ディレクトリに移動して、pyenv_virtualenvを用いて仮想環境を構築します。
私はanaconda3-5.0.1で作りましたが、たぶんpythonでも大丈夫です。
しかし、pythonで作成する場合は、python3を使用してください。
仮想環境名は自分の好きな名前でどうぞ。
```pyenv virtualenv 3.6.4 仮想環境名
```

そして、作業ディレクトリで作成した仮想環境に設定します。
```pyenv local 仮想環境名
```

###scikit_imageをinstall

最後に仮想環境を設定した状態で以下のコマンドを叩き、scikit_learnとscikit_imageを入れます。
```pip install scikit-learn
``````pip install scikit-image
```

これで環境構築は完了です。


#K平均法とは

今回、画像分類をするためにK平均法という手法を用います。
K平均法は[こちらのページ](http://tech.nitoyon.com/ja/blog/2009/04/09/kmeans-visualise/)に非常にわかりやすく書かれています。

K平均法とはK-means法とも呼ばれており、
1. クラスラベルがトレーニングデータに含まれていない**教師なし学習**の一つで
2. 答えが事前にわかっていないデータから隠れた構造を見つけ出すことができる(=**クラスタ分析**) 

…というものです。
クラスタリングの目標は、
*データを自然なグループにまとめる方法を見つけ出すこと*にあります。

というわけで今回はこのアルゴリズムを用いて
コーディングを行い画像分類を行ってみたいと思います。
#コーディング編

以下参考コードとなります。
コードはかなり、参考文献の方のコードを参考にさせていただきました。

変数名とクラスター数の数を変えることで使うことができます。
####ディレクトリ名
- origin_path：画像を入れるディレクトリ名 (/〇〇/の形で記載)
- convert_path：元々の画像を同じサイズに変形させた時の画像を入れるディレクトリ名 (/〇〇/の形で記載)
- group_path：グループ分けした時のディレクトリ名 (/〇〇/の形で記載)

####画像サイズ
- h：リサイズした時の画像サイズの縦(int型)
- w：リサイズした時の画像サイズの横(int型)

(注)
データの画像サイズを統一しないとプログラムエラーが発生します。
今回のサンプルプログラムでは適当にh=100, w=125としましたが、
リサイズすることによって画像の比率が変わってしまうことがあるので注意が必要です。

####クラスタ数
- n_cluster：何分類したいかの数(int型)

####pythonコード

こちらは、origin_path内に入っている画像を任意の数でグループ分けし、
グループごとgroup_path内のディレクトリ内に画像をコピーするというものです。

```python:img_kmeans.py
#coding: utf-8
import os
import shutil
import numpy as np
from PIL import Image
from skimage import data
from sklearn.cluster import KMeans


if __name__ ==  "__main__":

    # 変数名
    origin_path = './img/'
    convert_path = './img_convert/'
    group_path =  './img_group/'

    # 画像サイズ
    h = 100
    w = 125

    # クラスタ数
    n_cluster = 2



    # 画像サイズを揃えて保存
    for f in os.listdir(origin_path):
        print(f)
        img = Image.open(origin_path+f)
        img=img.convert('RGB')
        img_resize = img.resize((w,h))

        # もしディレクトリがなければ、そのディレクトリを作成
        if os.path.exists(convert_path) == False:
            os.mkdir(convert_path)

        img_resize.save(convert_path+f)

    # 3次元の画像データを2次元データに変換する
    feature = np.array([data.imread(convert_path + f)for f in os.listdir(convert_path)])
    feature = feature.reshape(len(feature), -1).astype(np.float64)

    # 学習
    model = KMeans(n_clusters=n_cluster).fit(feature)

    # ラベリングの結果
    labels = model.labels_
    print(labels)

    # 分類結果ごと、画像をディレクトリにコピー
    for label, path in zip(labels, os.listdir(convert_path)):

        if os.path.exists(group_path) == False:
            os.mkdir(group_path)

        copy_path = group_path+str(label)

        if os.path.exists(copy_path) == False:
            os.mkdir(copy_path)

        shutil.copyfile(origin_path+path, copy_path+"/"+path)
        print(label, path)

```
#実行・分析編

試しに作ってみたプログラムを試してみました。

###使用した画像データ

今回使用したデータは[Cartoon Set](https://google.github.io/cartoonset/index.html)の中から選んだデータです。
このデータセットは二次元のアバターのイメージデータセットであり、機械学習のデータセットとして使用可能です。
今回はこの画像の中から30枚を適当にピックアップし、K平均法で分類を行ってみました。

###分類結果
- w=100
- h=100
- n_cluster = 10

という画像サイズと分類数で実際プログラムを動かしてみました。
その結果はこんな感じになりました。

**Group0**
![スクリーンショット 2018-11-21 12.56.14.png](https://qiita-image-store.s3.amazonaws.com/0/228353/2fc87fe7-d90f-d85e-30c1-baf689f4a391.png)
**Group1**
![スクリーンショット 2018-11-21 12.57.57.png](https://qiita-image-store.s3.amazonaws.com/0/228353/1b0745cf-7292-24ae-d38b-f25e666e6e8d.png)
**Group2**
![スクリーンショット 2018-11-21 12.59.01.png](https://qiita-image-store.s3.amazonaws.com/0/228353/7054d983-4206-d41c-3b2f-7f4925658863.png)
**Group3**
![スクリーンショット 2018-11-21 13.04.38.png](https://qiita-image-store.s3.amazonaws.com/0/228353/eec5247c-4175-d500-b220-45cca80bc661.png)
**Group4**
![スクリーンショット 2018-11-21 13.00.46.png](https://qiita-image-store.s3.amazonaws.com/0/228353/0c316bfc-9056-0d1b-1bef-29e95f932556.png)
**Group5**
![スクリーンショット 2018-11-21 13.05.38.png](https://qiita-image-store.s3.amazonaws.com/0/228353/e194e151-b082-d060-a0d0-caaab7bda215.png)
**Group6**
![スクリーンショット 2018-11-21 13.06.36.png](https://qiita-image-store.s3.amazonaws.com/0/228353/34cf951a-3f00-8bd5-b3fb-af15aa0fb2db.png)
**Group7**
![スクリーンショット 2018-11-21 13.07.29.png](https://qiita-image-store.s3.amazonaws.com/0/228353/87123ccb-de14-b23c-46e3-aeca7f0f4291.png)
**Group8**
![スクリーンショット 2018-11-21 13.08.30.png](https://qiita-image-store.s3.amazonaws.com/0/228353/f1c238bc-6826-b6aa-d497-2708556284d5.png)
**Group9**
![スクリーンショット 2018-11-21 13.13.41.png](https://qiita-image-store.s3.amazonaws.com/0/228353/00e50718-2600-0d96-e3cc-d457684ca0cc.png)

意外にこの画像ではそれっぽく分類できました。
髪型だったり顔の輪郭の形、あとは髪の色なんかが分類に大きく影響を与えてる感じがします。

#まとめ

K平均法は考え方はシンプルながらも意外にきちんと分類できる優れものです。
皆さんも是非プログラムを動かして色々と遊んでみてください。


#参考文献

参考資料は以下の通りです。
【環境構築】
[仮想環境バージョン管理 まとめ Python](https://qiita.com/hedgehoCrow/items/0733c63c690450b14dcf)


【コーディング】
[scikit learn で国旗画像をクラスタリングして似ているものを探す](https://webbibouroku.com/Blog/Article/sklearn-cluster-flag)