やましなぶろぐ

機械学習(Machine Learning)を色々試してみる

🕒 Last mod: 2020-10-16


機械学習の手法などをいろいろ試してみます。

1. UMAP

分析が難しい教師なし学習。そのなかでも強力だと話題のUMAPを試してみます。

パッケージのインストールに苦戦、お手軽とは言えませんが精度などについても検証してみます。

1.1. Install by pip

Ubuntu18.04にインストールしています。

検索してみるとCondaによるインストールする方法が多く紹介されています。

私としてはCondaは使用したくないのでPipによるインストール方法を試してみます。

PipとCondaを混在させてパッケージ管理していて環境が壊れた経験からCondaを使用しないことにしています。

1.1.1. RuntimeError: llvm-config failed executing

手始めにpipでインストールを試してみるとエラーが発生しました。

Terminal input
pip install umap-learn
Terminal result
RuntimeError: llvm-config failed executing

llvm-configがインストールされていないようです。

1.1.2. llvm-configをインストール

llvm-configのインストールも情報が少なくマニュアルも難しい。

手始めにaptでインストールしてみます。

Terminal input
sudo apt-get install llvm
バージョンが古くてエラー

llvm-configをインストール後に再度pipによるインストールを試します。

環境変数”LLVM_CONFIG”を設定してからpipコマンドを実行します。

Terminal input
export LLVM_CONFIG=/usr/bin/llvm-config
pip install umap-learn
Terminal result
RuntimeError: Building llvmlite requires LLVM 10.0.x or 9.0.x, got '6.0.0'.

1.1.3. llvm-config-9 をインストール

aptコマンドによるインストールではバージョンが古いのでaptコマンドを使用しないで9.0.xをインストールします。

下記ページにしたがってインストールします。

LLVM Debian/Ubuntu nightly packages
Terminal input
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 9
export LLVM_CONFIG=/usr/bin/llvm-config-9

これでllvm-config-9のインストールが完了です。

1.1.4. wheelをインストール

再度pipでumap-learnのインストールを試したところwheelパッケージが存在しないためエラーが発生しました。

pipでインストールします。

Terminal input
pip install wheel

1.1.5. umap-learnをインストール

llvm-configとwheelをインストールした結果umap-learnがインストールできました。

Terminal input
pip install umap-learn

もしかするとscipyやsklearnが必要なのかもしれません。私の環境ではインストール済みなのでこれらのパッケージインストール作業は省略しています。

1.1.6. インストール作業のまとめ

umap-learnをインストールする作業コマンドをまとめます。

Terminal input
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 9
export LLVM_CONFIG=/usr/bin/llvm-config-9
pip install wheel
pip install umap-learn

1.2. umapを試してみる。

クイックに試してみます。

breast_cancerデータを次元削減して2次元の行列を取得しています。

Quickumap.py
from sklearn.datasets import load_breast_cancer
import umap

X, y = load_breast_cancer(return_X_y=True)

umap_ = umap.UMAP(random_state=0)
embedding = umap_.fit_transform(X)
embedding
Terminal result
array([[15.124994 ,  1.6796212],
       [14.847959 ,  1.423338 ],
       [14.595355 ,  3.2353883],
       ...,
       [12.313152 ,  9.627517 ],
       [14.778064 ,  2.3684618],
       [-2.9384632, -2.6674404]], dtype=float32)

1.3. 精度を確認

UMAPの精度を確認します。

精度はデータによりけりなところがありますが簡略ながら比較してみます。

データはbreast_cancerを使用します。

スコアはf1_scoreを使用します。

1.3.1. ベースライン:lightgbmによる精度

まずは教師あり学習の精度から。

model_lightgbm.py
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import cross_val_predict
from sklearn.metrics import f1_score
import lightgbm

X, y = load_breast_cancer(return_X_y=True)

clf = lightgbm.LGBMClassifier()
p = cross_val_predict(
    clf,
    X,
    y,
)
f1_score(y, p)
Result Score
0.9734265734265735

スコアは0.97超え。教師あり学習ならほぼ正解出来てしまうデータです。

1.3.2. 比較対象:主成分分析(comp=2)+KMeans

教師なし学習の比較対象として主成分分析を試してみます。

model_PCA.py
from sklearn.datasets import load_breast_cancer
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from sklearn.metrics import f1_score

X, y = load_breast_cancer(return_X_y=True)

kmeans = KMeans(n_clusters=2, random_state=0).fit(X)
f1_score(y, kmeans.labels_)
Result Score
0.8955974842767296

想像していたよりも高いスコア。

1.3.3. UMAP(comp=2)+KMeans

今回の目的のUMAP。PCAを超えられるでしょうか?

model_UMAP.py
from sklearn.datasets import load_breast_cancer
import umap
from sklearn.cluster import KMeans
from sklearn.metrics import f1_score

X, y = load_breast_cancer(return_X_y=True)

umap_ = umap.UMAP(random_state=0)
comps = umap_.fit_transform(X)
kmeans = KMeans(n_clusters=2, random_state=0).fit(comps)
f1_score(y, kmeans.labels_)
Result Score
0.8346709470304975

…PCAより低いスコアになってしまいました…

PCAよりも高い精度を期待していたのですが想定外です。何かしら検証方法が良くなかったのでしょうか?

このような使用方法にはむいていないのでしょうか?散布図を作成すれば人間にも分かりやすく分離しているのかもしれません。

1.3.4. 散布図確認

UMAPとPCAの結果を散布図にしてみます。

UMAP_scattar
UMAP_scattar
PCA2_scattar
PCA2_scattar

両者を比較してみるとUMAPの方が2群がキレイに分離しています。

精度は振るいませんでしたが人間が状況を理解するにはキレイに分離しているUMAPが適していそうです。

2. opencv

画像の処理でopencvを使用することがあります。何度か使用しているのですがインストールなど環境構築方法は都度調べることになってしまっています。良い機会なので環境構築方法について整理しておきます。

2.1. Install

最新バージョンのインストールは簡単にはできなさそうです。

調べてみてこちらの記事を見つけました。記事を公開してくれていることはありがたいことです。

この記事で紹介されているgithub上のスクリプトを使用してインストールしてみます。

Terminal Input
git clone https://github.com/milq/milq.git
bash milq/scripts/bash/install-opencv.sh

無事にインストールできました。実行時間はかなりかかりました。私のPCが非力なことも原因の1つですが…

あとはpipでpythonのパッケージをインストールします。

Terminal Input
pip install opencv-python

pythonで実行できるか試してみます。

Example.py
import cv2
cv2.__version__

バージョンが表示されて無事実行出来たことが確認できます。

3. 座標と値のリストから行列を作成

実現したいことが言葉でうまく表現できず、解決方法の検索も難しいのだが座標と値のリストから行列を作成したい。

色々と調べた結果、coo_matrixを使用する方法を突き止めた。

座標と値のリストで行列を表現する方法は考えてみればSparseな行列の表現方法だと今にして納得いている。

とにもかくにも実現したいことを上手に表現できないので実例を見てもらいたい。

3.1. サンプルプログラム

coo_matrix.py
import numpy as np
from scipy.sparse import coo_matrix

rows = list(range(4))
cols = list(range(3, -1, -1))
values = [1] * 4
mat = coo_matrix(
    (values, (rows, cols)),
    shape=(4, 4)
).toarray()

print('rows: ', rows)
print('cols: ', cols)
print('values: ', values)
print('mat: \n', mat)

3.2. 実行結果

output
rows:  [0, 1, 2, 3]
cols:  [3, 2, 1, 0]
values:  [1, 1, 1, 1]
mat:
     [[0 0 0 1]
      [0 0 1 0]
      [0 1 0 0]
      [1 0 0 0]]

4. 画像配列を新規作成する

マスク画像や背景画像など画像配列を作成したい。

実際にやってみようとしたときに少しだけ実現方法に迷ったのでメモ代わりに残します。

4.1. ホワイト画像を作成する

真っ白だと背景と区別ができないので少しグレーの画像を作成する。

white200.py
import numpy as np
from PIL import Image

white200 = np.full(
    (100, 100, 3),
    200,
    np.uint8,
)

Image.fromarray(
    white200
).save(
    'work/white200.jpg'
)
white200.jpg
white200.jpg

4.2. 赤画像を作成する

赤画像を作成する。

  1. 0値の画像配列を作成

  2. Rの値に255を代入

red.py
import numpy as np
from PIL import Image

red = np.zeros(
    (100, 100, 3),
    np.uint8,
)
red[:, :, 0] = 255

Image.fromarray(
    red
).save(
    'work/red.jpg'
)
red.jpg
red.jpg

5. KerasClassifierで画像の2値分類:Binary Classification for images

DeepLearningに苦手意識があり避けてしまう傾向なのでこの辺で慣れておきたい。

データ作成から学習、評価までの一通りの流れを実装します。

sklearnの扱いには慣れているのでsklearnラッパーを使用します。

実装していて気になったことがいくつかある。

keras or tf.keras

DeepLearningを避けているうちにいつのまにかtensolflow.kerasが追加されていた。

今後はtf.kerasが主流になるのだろうか?この記事ではtf.kerasを使用する。

2値分類と多値分類

当初は2値分類でsigmoidを使用しようと考えていたが多値分類でsoftmaxのほうが汎用的だと思いなおした。

predict_proba

keras.Sequentialのpredict_probaが将来なくなるというWarningが出る。

でもcross_val_predictの引数method="predict_proba"を指定しないと確率値ではなくクラスが出力されてします。とりあえずいまのところはpredict_probaを指定しておく。将来的に使用できなくなってしまうのだろうか?

5.1. データ作成~学習~評価

正規分布に従う画像データを作成します。

平均が155と145の2種類の画像群を作成してどちらの群に属するかを学習します。

標準偏差は両方とも20とします。

画像サイズは20*20とします。

画像を作成するための関数を用意します。

それぞれの画像を1000個、合計2000個作成します。もろもろの情報を変数として作成します。

そして実際にX, Yを作成します。

モデルを定義します。慣れないうちは構造は使いまわしです。

あとはsklearnの書き方で学習します。クロスバリデーションで精度を評価します。

5.2. コードまとめ

try_KerasClassifier.py
import numpy as np

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten
from tensorflow.keras.layers import Conv2D, MaxPooling2D


from tensorflow.keras.wrappers.scikit_learn import KerasClassifier
from sklearn.model_selection import cross_val_predict
from sklearn.metrics import roc_auc_score

rows=20
cols=20
input_shape = (rows, cols, 1)

m_1 = 155
m_0 = 145

sd = 20

n_images_1 = 1000
n_images_0 = 1000
n_images = n_images_1 + n_images_0


def create_x(
    m,
    s,
    rows=20,
    cols=20,
):
    return np.random.normal(
        m,
        s,
        rows * cols,
    ).astype(
        np.uint8
    ).reshape(
        (rows, cols, 1)
    )

X = np.zeros(
    (n_images, rows, cols, 1),
    dtype=np.uint8
)

for i in range(n_images_1):
    X[i] = create_x(m_1, sd)

for i in range(n_images_0):
    X[i + n_images_1] = create_x(m_0, sd)

Y = np.zeros(n_images, dtype=np.uint8)
Y[:n_images_1] = 1

def create_model():
    model = Sequential()
    model.add(
        Conv2D(
            16,
            kernel_size=(3, 3),
            activation='relu',
            input_shape=input_shape
        )
    )
    model.add(
        Conv2D(
            32,
            (3, 3),
            activation='relu'
        )
    )
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.25))
    model.add(Flatten())
    model.add(Dense(64, activation='relu'))
    model.add(Dropout(0.5))
    model.add(Dense(2, activation='softmax'))
    model.compile(
        loss='categorical_crossentropy',
        optimizer='adam',
        metrics=['accuracy']
    )
    return model




clf = KerasClassifier(
    build_fn=create_model,
    epochs=20,
    batch_size=10
)
p = cross_val_predict(
    clf,
    X / 255,
    Y,
    method='predict_proba'
)
print(roc_auc_score(Y, p[:, 1]))