旅行好きなソフトエンジニアの備忘録

プログラミングや技術関連のメモを始めました

【Python】PCANetを試してみる

教師なし学習で画像の特徴量抽出を行う方法を調べていて以下を見つけたのでMNISTで試してみました。以下記事にgithubへのリンクがあるので、そこのpcanet.pyを写します。 qiita.com

写したpcanet.pyを使い、以下のmain.pyを書いて動作させれば特徴抽出⇒分類の結果を確認することが出来ます。ただし、pcanetのtransformメソッドが非常に時間がかかる(train, validation, test合計で1時間位かかります)ため、MNISTデータの一部だけ使用しており、かつハイパーパラメータチューニングもやっていません。この状態だとHOG⇒分類で94.7%、PCANet⇒分類で96.4%の正解率となりました。今後はPCANetのようなネットワークが他にもないか調べておきたいです。

import numpy as np
from datetime import datetime
from keras.datasets import mnist
from pcanet import PCANet
from lightgbm import LGBMClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from skimage.feature import hog


def get_part_of_mnist(ratio_to_use=0.1):
    test_size = 1.0 - ratio_to_use
    (X_train, y_train), (X_test, y_test) = mnist.load_data()
    X_train, X_validation, y_train, y_validation = train_test_split(X_train, y_train, test_size=test_size, stratify=y_train)
    X_validation, X_dummy, y_validation, y_dummy = train_test_split(X_validation, y_validation, test_size=test_size, stratify=y_validation)
    X_test, X_dummy, y_test, y_dummy = train_test_split(X_test, y_test, test_size=test_size, stratify=y_test)
    return X_train, y_train, X_validation, y_validation, X_test, y_test


def extract_feature_using_pcanet(X_train, X_validation, X_test):
    # Arguments are basically passed as tuple in the form (height, width) but int is also allowed.
    # If int is given, the parameter will be converted into (size, size) implicitly.
    pcanet = PCANet(
        image_shape=X_train.shape[1],  # the size of an input image
        # kernel size, kernel step size, and the number of filters in the first layer, respectively
        filter_shape_l1=2, step_shape_l1=1, n_l1_output=4,
        # kernel size, kernel step size, and the number of filters in the second layer, respectively
        filter_shape_l2=2, step_shape_l2=1, n_l2_output=4,
        block_shape=2  # the size of area to calculate histogram
    )

    # Check whether all pixels can be considered. Raise ValueError if the structure is not valid.
    # Calling this function is optional. PCANet works without this line.
    pcanet.validate_structure()

    pcanet.fit(X_train)  # Train PCANet

    # Trained PCANet behaves as a transformer from images into features.
    # `images` is a 3d array in the form (n_images, height, width), who are transformed into feature vectors.
    X_train = pcanet.transform(X_train)
    X_validation = pcanet.transform(X_validation)
    X_test = pcanet.transform(X_test)

    return X_train, X_validation, X_test


def extract_feature_using_hog(X_train, X_validation, X_test):
    features_train = []
    for image in X_train:
        feature = hog(image, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(3, 3))
        features_train.append(feature)
    features_validation = []
    for image in X_validation:
        feature = hog(image, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(3, 3))
        features_validation.append(feature)
    features_test = []
    for image in X_test:
        feature = hog(image, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(3, 3))
        features_test.append(feature)
    return np.array(features_train), np.array(features_validation), np.array(features_test)


if __name__ == '__main__':
    # MNISTデータの一部を取得する
    X_train, y_train, X_validation, y_validation, X_test, y_test = get_part_of_mnist()

    # PCANetで特徴量を生成する
    X_train, X_validation, X_test = extract_feature_using_pcanet(X_train, X_validation, X_test)
    # HOGで特徴量を生成する
    #X_train, X_validation, X_test = extract_feature_using_hog(X_train, X_validation, X_test)

    # LightGBM分類器を生成する
    model = LGBMClassifier(objective='multiclass',
                           num_leaves=31,
                           learning_rate=0.1,
                           n_estimators=300)
    model.fit(X_train, y_train,
              eval_set=[(X_validation, y_validation)],
              eval_metric='multi_logloss',
              early_stopping_rounds=5)
    y_pred = model.predict(X_test)
    print(accuracy_score(y_test, y_pred)*100)

【PyTorch】モデルがevalモードの時にout of memoryが発生する事への対処法

PyTorchでモデルがtrainモードの時には発生しないのですが、evalモードの時にGPUのメモリが解放されないまま消費されていきout of memoryが発生していました。調べたところ、Variableにvolatileという引数があって、これをTrueにすれば良いよというアドバイスがあり、確かにout of memoryが発生しなくなりました。

# evalモードへ
model.eval()
# evalモードの時はvolatile=TrueでGPUメモリ解放される
X = Variable(X, volatile=True)

stackoverflow.com

【Python】不均衡な2クラスセグメンテーション問題に適用するロス関数のメモ

この論文で不均衡な2クラスセグメンテーション問題に適用するロス関数が提案されていたのでメモします。ディープラーニングを使ったセグメンテーションでデータが極端に不均衡(例えば画像のほとんどが0で、1はちょっとだけ)の場合、工夫をしないと学習が上手くいかないのですが、論文ではロス関数の工夫によりこの問題を回避しようとしています。

下記の記事ではセグメンテーションのロス関数に以下のダイス係数を利用しました。

def dice_coef(y_true, y_pred):
    y_true = K.flatten(y_true)
    y_pred = K.flatten(y_pred)
    intersection = K.sum(y_true * y_pred)
    return 2.0 * intersection / (K.sum(y_true) + K.sum(y_pred) + 1)

def dice_coef_loss(y_true, y_pred):
    return 1.0 - dice_coef(y_true, y_pred)

ni4muraano.hatenablog.com

論文ではTversky loss functionという関数を提案しており、以下のようになります。ただこれどこかで見たと思ったらIOUの修正バージョンですね。

ALPHA = 0.3 # 0~1.0の値、Precision重視ならALPHAを大きくする
BETA = 1.0 - ALPHA # 0~1.0の値、Recall重視ならALPHAを小さくする

def tversky_index(y_true, y_pred):
    y_true = K.flatten(y_true)
    y_pred = K.flatten(y_pred)
    intersection = K.sum(y_true * y_pred)
    false_positive = K.sum((1.0 - y_true) * y_pred)
    false_negative = K.sum(y_true * (1.0 - y_pred))
    return intersection / (intersection + ALPHA*false_positive + BETA*false_negative)

def tversky_loss(y_true, y_pred):
    return 1.0 - tversky_index(y_true, y_pred)

【Python】How to generate one-hot encodings for an array in numpy? - 101 Numpy Exercises

Q:

One-hot encodingを計算しなさい
(Kerasのnp_utils.to_categoricalを使えば良いのですが、Keras使わない時のためのメモ)
Input:

arr = np.random.randint(1,4, size=6)
arr
#> array([2, 3, 2, 2, 2, 1])


Output:

#> array([[ 0.,  1.,  0.],
#>        [ 0.,  0.,  1.],
#>        [ 0.,  1.,  0.],
#>        [ 0.,  1.,  0.],
#>        [ 0.,  1.,  0.],
#>        [ 1.,  0.,  0.]])


A:

# Input:
arr = np.random.randint(1,4, size=6)
arr
#> array([2, 3, 2, 2, 2, 1])

# Solution:
def one_hot_encodings(arr):
    uniqs = np.unique(arr)
    out = np.zeros((arr.shape[0], uniqs.shape[0]))
    for i, k in enumerate(arr):
        out[i, k-1] = 1
    return out

one_hot_encodings(arr)
#> array([[ 0.,  1.,  0.],
#>        [ 0.,  0.,  1.],
#>        [ 0.,  1.,  0.],
#>        [ 0.,  1.,  0.],
#>        [ 0.,  1.,  0.],
#>        [ 1.,  0.,  0.]])

# Method 2:
(arr[:, None] == np.unique(arr)).view(np.int8)


www.machinelearningplus.com

【Python】How to find the most frequent value in a numpy array? - 101 Numpy Exercises

Q:

irisのpetal lengthで最も出現頻度が高い値を見つけなさい

A:

# Input:
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
iris = np.genfromtxt(url, delimiter=',', dtype='object')

# Solution:
vals, counts = np.unique(iris[:, 3], return_counts=True)
print(vals[np.argmax(counts)])
#> b'0.2'


www.machinelearningplus.com

【Python】How to sort a 2D array by a column? - 101 Numpy Exercises

Q:

irisデータセットをsepallengthカラムの値でソートしなさい

A:

url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
iris = np.genfromtxt(url, delimiter=',', dtype='object')
names = ('sepallength', 'sepalwidth', 'petallength', 'petalwidth', 'species')

# Sort by column position 0: SepalLength
print(iris[iris[:,0].argsort()][:20])
#> [[b'4.3' b'3.0' b'1.1' b'0.1' b'Iris-setosa']
#>  [b'4.4' b'3.2' b'1.3' b'0.2' b'Iris-setosa']
#>  [b'4.4' b'3.0' b'1.3' b'0.2' b'Iris-setosa']
#>  [b'4.4' b'2.9' b'1.4' b'0.2' b'Iris-setosa']


www.machinelearningplus.com