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

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

【Python】 KNNによるデータの分類

書籍「PYTHON機械学習プログラミング」のKNNによるデータの分類例をメモします。

# -*- coding: utf-8 -*-

import numpy as np
from matplotlib.colors import ListedColormap
from matplotlib import pyplot as plt
from sklearn.neighbors import KNeighborsClassifier

def plot_decision_regions(X, y, classifier, resolution=0.02):
    # マーカーとカラーマップの準備
    markers = ('s', 'x', 'o', '^', 'v')
    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
    cmap = ListedColormap(colors[:len(np.unique(y))])

    # 領域の最大最小値
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1

    # グリッドポイントの生成
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution), np.arange(x2_min, x2_max, resolution))

    # 予測を実行する
    Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)

    # 予測結果をプロットする
    Z = Z.reshape(xx1.shape)
    plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)

    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())

    for i, cls in enumerate(np.unique(y)):
        plt.scatter(X[y==cls, 0], X[y==cls, 1], alpha=0.8, c=cmap(i), marker=markers[i], label=cls)

    plt.show()

if __name__ == '__main__':
    # データの生成
    np.random.seed(0)
    X1 = np.random.randn(50, 2)
    y1 = np.ones(50)
    X2 = np.random.randn(50, 2) + 10
    y2 = np.ones(50)*2
    X3 = np.random.randn(50, 2)
    X3[:, 1] += 10
    y3 = np.ones(50)*3
    X4 = np.random.randn(50, 2)
    X4[:, 0] += 10
    y4 = np.ones(50)*4 
    X = np.concatenate((X1, X2, X3, X4))
    y = np.concatenate((y1, y2, y3, y4))

    # KNNによる分類
    knn = KNeighborsClassifier(n_neighbors=4)
    knn.fit(X, y)

    # 分類結果を表示する
    plot_decision_regions(X, y, knn)

f:id:ni4muraano:20170219221457j:plain

【Python】 SVMによるデータの分類

書籍「PYTHON機械学習プログラミング」を読んでいて、SVMによるXORデータの分類例があったのですが、分類結果をグラフ化する部分が勉強になったのでメモします。

# -*- coding: utf-8 -*-

import numpy as np
from matplotlib.colors import ListedColormap
from matplotlib import pyplot as plt
from sklearn.svm import SVC

def plot_decision_regions(X, y, classifier, resolution=0.02):
    # マーカーとカラーマップの準備
    markers = ('s', 'x', 'o', '^', 'v')
    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
    cmap = ListedColormap(colors[:len(np.unique(y))])

    # 領域の最大最小値
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1

    # グリッドポイントの生成
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution), np.arange(x2_min, x2_max, resolution))

    # 予測を実行する
    Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)

    # 予測結果をプロットする
    Z = Z.reshape(xx1.shape)
    plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)

    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())

    for i, cls in enumerate(np.unique(y)):
        plt.scatter(X[y==cls, 0], X[y==cls, 1], alpha=0.8, c=cmap(i), marker=markers[i], label=cls)

    plt.show()

if __name__ == '__main__':
    # データの生成
    np.random.seed(0)
    X = np.random.randn(200, 2)
    y = np.logical_xor(X[:, 0] > 0, X[:, 1] > 0)
    y = np.where(y, 1, -1)

    # SVMによるフィッティング
    svm = SVC(gamma='auto') # γを大きくし過ぎると過適合してしまう
    svm.fit(X, y)

    # 分類結果を表示する
    plot_decision_regions(X, y, svm)

f:id:ni4muraano:20170219102826j:plain

【Python】 Kerasで中間層を可視化する

Kerasを利用してネットワーク中間層を可視化する方法をメモします。プログラムでは学習済みのモデルと重みがある事を想定し、それらを読み取って一層目の中間層であるConvolution2Dの重みを可視化しています。

import numpy as np
from keras.models import model_from_json
from matplotlib import pyplot as plt

with open('lenet.json', 'r') as file:
    model_json = file.read()
model = model_from_json(model_json)
model.load_weights('lenet_weights.hdf5')

# Get 1st layer Convolution2D weights.
# In this example, weights.shape is (6, 1, 5, 5)
weights = model.layers[0].get_weights()[0].transpose(3,2,0,1)
fig = plt.figure()
for i, weight_3d in enumerate(weights):
    for j, weight_2d in enumerate(weight_3d):
        sub = fig.add_subplot(weights.shape[0], weight_3d.shape[0], i*weight_3d.shape[0]+j+1)
        sub.axis('off')
        sub.imshow(weight_2d, 'Greys')
plt.show()

f:id:ni4muraano:20170216202753j:plain

【Python】CIFAR-10画像データを扱う

AlexNet等の実験でCIFAR-10(CIFAR-10 and CIFAR-100 datasets)を利用することがあります。幸いKerasにはデフォルトでCIFAR-10の画像データを取り込む関数があるのですが、諸事情によりこれが利用できなかったのでCIFAR-10を扱うための関数を作成しました。

def load_image_and_label(pickled_files):
    import numpy as np

    # Each file contains 10000 images
    IMAGE_COUNT_PER_FILE = 10000
    # Image shape is 32x32x3
    ROW = 32
    COL = 32
    DIM = 3
    whole_images = np.empty((IMAGE_COUNT_PER_FILE*len(pickled_files), ROW, COL, DIM))
    whole_labels = np.empty(IMAGE_COUNT_PER_FILE*len(pickled_files))
    for i, pickled_file in enumerate(pickled_files):
        dict = _unpickle(pickled_file)
        images = dict['data'].reshape(IMAGE_COUNT_PER_FILE, DIM, ROW, COL).transpose(0, 2, 3, 1)
        whole_images[i*IMAGE_COUNT_PER_FILE:(i + 1)*IMAGE_COUNT_PER_FILE, :, :, :] = images
        labels = dict['labels']
        whole_labels[i*IMAGE_COUNT_PER_FILE:(i + 1)*IMAGE_COUNT_PER_FILE] = labels
    return (whole_images, whole_labels)

def _unpickle(pickled_file):
    import pickle

    with open(pickled_file, 'rb') as file:
        # You'll have an error without "encoding='latin1'"
        dict = pickle.load(file, encoding='latin1')
    return dict

上記をcifar10_handling.pyとして保存します。次にこの関数を利用する方法ですが、CIFAR-10のサイトからダウンロードできる"cifar-10-batches-py.tar.gz"を解凍しな中に入っている"data_batch_1"等のファイルになります。

import cifar10_handling

(X_train, y_train) = cifar10_handling.load_image_and_label(['data_batch_1',
                                                            'data_batch_2',
                                                            'data_batch_3',
                                                            'data_batch_4',
                                                            'data_batch_5'])
(X_test, y_test) = cifar10_handling.load_image_and_label(['test_batch'])

あとは正規化等の前処理を加えれば実験に使えるデータとなります。

【Python】 Kerasでモデルを図に保存する

Kerasでは作成したモデルはここ(可視化 - Keras Documentation)にあるように簡単に図として保存できるはず、と思ったのですが予想外のトラブルに見舞われたので解決方法をメモします。環境は以下の通りです。

トラブルの内容ですが、"from keras.utils.visualize_util import plot"の部分で"pydotやgraphvizが必要だ"といったような内容のエラーが出ました。そのためまずは

  • conda install graphviz
  • conda install pydot-ng

を実施したところ、"conda install graphviz"は問題無く終わったのですが、"conda install pydot-ng"はPython3.5では利用できないと言われます。仕方無く代わりに

  • pip install pydot

を実行し、モデルの保存を試みたのですが、今度は"from keras.utils.visualize_util import plot"の部分で"module ‘pydot’ has no attribute ‘find_graphviz’“というエラーが出ました。調べてみると、最新版のpydotからはfind_graphvizという関数は無くなっているらしく、最新版ではなくpydot1.1.0をインストールすると良いというアドバイスを見つけました。ところがpydot1.1.0をインストールしようとするとPython3.5では利用できないというエラーが。。。
打つ手無しのため、強硬策としてソースコードを変更することにしました。Anaconda3フォルダ配下の"Lib\site-packages\keras\utils"にvisualize_util.pyがあります。このファイルのfind_graphvizを使っている13-15行目を以下のようにコメントしました。

#if not pydot.find_graphviz():
#    raise ImportError('Failed to import pydot. You must install pydot'
#                      ' and graphviz for `pydotprint` to work.')

これでエラーは出なくなると思ったのですが、今度は"dot.exeが見つからない"といった内容のエラーが。。。ただ、このエラーについてはAnaconda3フォルダ配下の"Library\bin\graphviz"を環境変数のPATHに追加し、PCを再起動することで無くなり、無事モデルを図示できるようになりました。まとめると、Kerasでモデルを図として保存するには

  • conda install graphviz
  • pip install pydot
  • visualize_util.pyのfind_graphvizの部分をコメント
  • Anaconda3フォルダ配下の"Library\bin\graphviz"を環境変数のPATHに追加し、PCを再起動する

という手順を踏めば良いと思います。

【Python】 KerasでLeNet5っぽいネットワークを作成する

ディープラーニングの原点と言われるLeNet5(http://yann.lecun.com/exdb/publis/pdf/lecun-01a.pdf)をKerasで実装し、MNISTの手書き文字を学習させました。LeNet5は以下の構造になっています。
f:id:ni4muraano:20170205221322j:plain
“LeNet5っぽい"とタイトルに書いたのは、完全に論文通りには実装できていないためです(活性化関数のカスタマイズ等が必要ですが、現時点でKerasをそこまで使いこなせていません)。この例ではここ(【Python】 MNIST手書き文字データを扱う - 旅行好きなソフトエンジニアの備忘録)で作成したmnist_handlingというモジュールを利用しています。

import mnist_handling

import numpy as np

from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Convolution2D, MaxPooling2D
from keras.optimizers import SGD
from keras.utils import np_utils

if __name__ == '__main__':
    # Load training data
    training_image_file = 'train-images-idx3-ubyte.gz'
    training_label_file = 'train-labels-idx1-ubyte.gz'
    training_images, training_labels = mnist_handling.load_image_and_label(training_image_file, training_label_file)

    # Load test data    
    test_image_file = 't10k-images-idx3-ubyte.gz'
    test_label_file = 't10k-labels-idx1-ubyte.gz'
    test_images, test_labels = mnist_handling.load_image_and_label(test_image_file, test_label_file)

    # Normalize data
    training_images = training_images.astype('float32')
    test_images = test_images.astype('float32')
    training_images /= 255
    test_images /= 255

    # Convert class vectors to binary class matrices
    class_count = 10
    training_labels = np_utils.to_categorical(training_labels, class_count)
    test_labels = np_utils.to_categorical(test_labels, class_count)

    # Build model
    model = Sequential()

    # Layer1
    kernel_count_layer1 = 6
    kernel_row_layer1 = 5
    kernel_col_layer1 = 5
    input_shape = (training_images.shape[1], training_images.shape[2], training_images.shape[3])
    model.add(Convolution2D(kernel_count_layer1, kernel_row_layer1, kernel_col_layer1, border_mode='same',
                            input_shape=input_shape, activation='tanh'))

    # Layer2
    pool_size_layer2 = (2, 2)
    model.add(MaxPooling2D(pool_size=pool_size_layer2))
    model.add(Dropout(6/16))

    # Layer3
    kernel_count_layer3 = 16
    kernel_row_layer3 = 5
    kernel_col_layer3 = 5
    model.add(Convolution2D(kernel_count_layer3, kernel_row_layer3, kernel_col_layer3, border_mode='valid',
                            activation='tanh'))

    # Layer4
    pool_size_layer4 = (2, 2)
    model.add(MaxPooling2D(pool_size=pool_size_layer4))

    # Layer5
    kernel_count_layer5 = 120
    kernel_row_layer5 = 5
    kernel_col_layer5 = 5
    model.add(Convolution2D(kernel_count_layer5, kernel_row_layer5, kernel_col_layer5, border_mode='valid',
                            activation='tanh'))

    # Layer6
    output_count_layer6 = 84
    model.add(Flatten())
    model.add(Dense(output_count_layer6, activation='tanh'))

    # Output layer    
    model.add(Dense(class_count, activation='softmax'))

    model.compile(loss='categorical_crossentropy',
                  optimizer=SGD(),
                  metrics=['accuracy'])

    # Start training
    epoch_count = 20
    batch_size = 128
    model.fit(training_images, training_labels, batch_size=batch_size, nb_epoch=epoch_count,
              verbose=1, validation_data=(test_images, test_labels))
    
    # Save model
    model_file_name = 'lenet.json'
    model_json = model.to_json()
    with open(model_file_name, 'w') as file:
        file.write(model_json)
    # Save weights
    weight_file_name = 'lenet_weights.hdf5'
    model.save_weights(weight_file_name)

論文と比較してトレーニングセットに対する正解率は2.5%、テストセットに対する正解率は1%程度低くなりました。論文では二層目、三層目が全結合になっていないため、代わりにドロップアウトを使用したのですが、これによりトレーニングセットに対する正解率がかなり低くなっているのかもしれません。(2017/02/09追記:最適化関数をAdamに変更することで論文と同程度の正解率となりました)

【Python】 MNIST手書き文字データを扱う

手書き文字のサンプルがダウンロードできる"THE MNIST DATABASE of handwritten digits"(MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges)ですが、gzファイルをダウンロードした後のデータ処理ロジックをメモします。mnist_handling.pyというファイルを作成し、以下を記述します。

import gzip
import numpy as np

def load_image_and_label(gz_image_file_path, gz_label_file_path):
    images = _load_image(gz_image_file_path)
    labels = _load_label(gz_label_file_path)
    return (images, labels)    

def _load_image(gz_file_path):
    with gzip.open(gz_file_path, 'rb') as file:
        content = file.read()
        # 手書き画像枚数
        image_count = int.from_bytes(content[4:8], 'big')
        # 手書き画像行数
        row = int.from_bytes(content[8:12], 'big')
        # 手書き画像列数
        col = int.from_bytes(content[12:16], 'big')
        # 手書き画像データの読み込み
        images = np.frombuffer(content, np.uint8, -1, 16)
    # imagesのshapeは(60000, 28, 28, 1)
    images = images.reshape(image_count, row, col, 1)
    return images

def _load_label(gz_file_path):
    with gzip.open(gz_file_path, 'rb') as file:
        content = file.read()
        label_count = int.from_bytes(content[4:8], 'big')
        labels = np.frombuffer(content, np.uint8, -1, 8)
    return labels

以下はmnist_handlingモジュールの利用例です。

import mnist_handling

training_image_file = 'train-images-idx3-ubyte.gz'
training_label_file = 'train-labels-idx1-ubyte.gz'
training_images, training_labels = mnist_handling.load_image_and_label(training_image_file, training_label_file)
    
test_image_file = 't10k-images-idx3-ubyte.gz'
test_label_file = 't10k-labels-idx1-ubyte.gz'
test_images, test_labels = mnist_handling.load_image_and_label(test_image_file, test_label_file)