LoginSignup
4
9

More than 5 years have passed since last update.

【機械学習入門】「SVMでクラス分類」というのをやってみる♬

Last updated at Posted at 2019-02-11

機械学習入門第三弾は、SVM(SuportVectorMachine)です。
K-means(K-近傍法)、 RFC(RandomForestClassification)、そしてSVMがクラス分類における機械学習御三家と言える手法だと思います。そこにDL(DeepLearning)が殴り込みをしたような構図です。
※【追記】K-meansとK-近傍法は、以下の参考の通り異なる手法でした。そのため本文も一部加筆修正しました
【参考】
k近傍法とk平均法の違いと詳細

当面の目標

機械学習のクラス分類という面についてまとめます。
K-meansクラスタリング
ランダムフォレストでクラス分類
・SVMでクラス分類(今回の記事)
・DeepLearningでクラス分類
※DeepLearningは昨年集中してやりましたが、対比するためにまとめる予定です

今回やったこと

・SVM
・SVMでMNISTライクな手書き数字分類
・SVMでランダムフォレストに殴り込み;K-meansもランダムフォレストに殴り込みしました(追記)
SVMでK-meansに殴り込み;参考だけ紹介しました(逆センスですが…)

・SVM

SVMの紹介的なアプリを動かしてみます。解説は省略します。
【参考】
SVM(多クラス分類)
参考のアプリがまんま動きましたが、あんまりおもしろくないので、データ出力等少しだけ変更しました。

import numpy as np
import matplotlib.pyplot as plt
#%matplotlib inline
from sklearn import datasets
from sklearn.cross_validation import train_test_split # クロスバリデーション用
from sklearn.svm import SVC # SVM用
from sklearn import metrics       # 精度検証用
import pandas as pd

# データ用意
iris = datasets.load_iris()    # データロード
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['target'] = iris.target_names[iris.target] #これないと名前が出ない
#df.head() #これだけじゃ出力しない
print(df.head())

X = iris.data                  # 説明変数セット
Y = iris.target                # 目的変数セット
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, random_state=0) # random_stateはseed値。
print(len(X_train),len(Y_train),len(X_test),len(Y_test))

# SVM実行
model = SVC()               # インスタンス生成
model.fit(X_train, Y_train) # SVM実行

# 予測実行
predicted = model.predict(X_test) # テストデータでの予測実行
acc=metrics.accuracy_score(Y_test, predicted)
print("acc=",acc)

これ実行すると、以下の警告がでますが、とりあえず以下の結果が得られます。

cross_validation.py:41: DeprecationWarning:...
   sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)  \
0                5.1               3.5                1.4               0.2
1                4.9               3.0                1.4               0.2
2                4.7               3.2                1.3               0.2
3                4.6               3.1                1.5               0.2
4                5.0               3.6                1.4               0.2

   target
0  setosa
1  setosa
2  setosa
3  setosa
4  setosa
112 112 38 38
acc= 0.973684210526

精度はまあまあですが、データ150個(学習112個、テスト38個)です。
※データ出力はpandas使っています。iris.head()だけだと出力できません
【参考】
irisのデータセットをpandasで使う

・SVMでMNISTライクな手書き数字分類

次は以下の参考を動かします。これもまんま動きました。
【参考】
Recognizing hand-written digits
コードは、肝心なところだけ抜き出しますので、コメントは原文見てください。
また、少しだけ改変しています。

"""
================================
Recognizing hand-written digits
================================
"""
print(__doc__)
# Author: Gael Varoquaux <gael dot varoquaux at normalesup dot org>
# License: BSD 3 clause
import matplotlib.pyplot as plt
from sklearn import datasets, svm, metrics
digits = datasets.load_digits()

plt.figure(figsize=(12, 6)) #ここ追加しました
images_and_labels = list(zip(digits.images, digits.target))
for index, (image, label) in enumerate(images_and_labels[:8]): #4から8に変更
    plt.subplot(2, 8, index + 1)  #4から8に変更
    plt.axis('off')  #これコメントアウトすると枠と座標が出る
    plt.imshow(image, cmap=plt.cm.gray_r, interpolation='nearest')
    plt.title('Training: %i' % label)

# To apply a classifier on this data, we need to flatten the image, to
# turn the data in a (samples, feature) matrix:
n_samples = len(digits.images)
print('n_samples=',n_samples)
data = digits.images.reshape((n_samples, -1))

# Create a classifier: a Support Vector Classifier
classifier = svm.SVC(gamma=0.001)
# We learn the digits on the first half of the digits;学習データは前半半分
classifier.fit(data[:n_samples // 2], digits.target[:n_samples // 2])

# Now predict the value of the digit on the second half:Testデータは後半半分
expected = digits.target[n_samples // 2:]
predicted = classifier.predict(data[n_samples // 2:])

print("Classification report for classifier %s:\n%s\n"
      % (classifier, metrics.classification_report(expected, predicted)))
print("Confusion matrix:\n%s" % metrics.confusion_matrix(expected, predicted))

images_and_predictions = list(zip(digits.images[n_samples // 2:], predicted))
for index, (image, prediction) in enumerate(images_and_predictions[:8]):
    plt.subplot(2, 8, index + 9)
    plt.axis('off')
    plt.imshow(image, cmap=plt.cm.gray_r, interpolation='nearest')
    plt.title('Prediction: %i' % prediction)
plt.savefig('svm_mnistlike.jpg')
plt.pause(1)

実行例は以下のとおり、実行は瞬時に終わります。
サンプル数はn_samples= 1797です。
svm_mnistlike.jpg

>python svm_mnistlike.py

================================
Recognizing hand-written digits
================================

n_samples= 1797
Classification report for classifier SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape='ovr', degree=3, gamma=0.001, kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False):
             precision    recall  f1-score   support

          0       1.00      0.99      0.99        88
          1       0.99      0.97      0.98        91
          2       0.99      0.99      0.99        86
          3       0.98      0.87      0.92        91
          4       0.99      0.96      0.97        92
          5       0.95      0.97      0.96        91
          6       0.99      0.99      0.99        91
          7       0.96      0.99      0.97        89
          8       0.94      1.00      0.97        88
          9       0.93      0.98      0.95        92

avg / total       0.97      0.97      0.97       899


Confusion matrix:
[[87  0  0  0  1  0  0  0  0  0]
 [ 0 88  1  0  0  0  0  0  1  1]
 [ 0  0 85  1  0  0  0  0  0  0]
 [ 0  0  0 79  0  3  0  4  5  0]
 [ 0  0  0  0 88  0  0  0  0  4]
 [ 0  0  0  0  0 88  1  0  0  2]
 [ 0  1  0  0  0  0 90  0  0  0]
 [ 0  0  0  0  0  1  0 88  0  0]
 [ 0  0  0  0  0  0  0  0 88  0]
 [ 0  0  0  1  0  1  0  0  0 90]]

・SVMでランダムフォレストに殴り込み

上記は、ほとんどランダムフォレストでやったことと同じなので、ランダムフォレストのコードにSVMを定義して実行してみました。
※K-meansの分類の話も同じように出来ると思いますが、"まんま"だとできないのでパスしました。
また、参考の通りK-近傍法ではクラスタリングできているので紹介します。
参考はK-近傍法でirisの分類をやっています
※上記のirisの分類については"まんまK-近傍法で動いたのでおまけに追記しました
※併せて、同じセンスでK-近傍法でランダムフォレストのコードに殴り込みもできたのでさらに追記しておきます(冒頭の参考の通り、K-meansとK-近傍法は異なる手法です)
【参考】
K近傍法(多クラス分類)

コード全体はおまけに掲載しますが、要は以下のコードだけ入れ替えています。
※つまり、clf=RFC...をclf=svm.SVC...に変更しただけで動きました。

"""
clf = RFC(verbose=True,       # 学習中にログを表示します。この指定はなくてもOK
          n_jobs=-1,          # 複数のCPUコアを使って並列に学習します。-1は最大値。
          random_state=2525)  # 乱数のシードです。
clf.fit(train_images, train_labels)

print(clf.feature_importances_)
"""
# Create a classifier: a support vector classifier
clf = svm.SVC(gamma=0.001)
# We learn the digits on the first half of the digits
clf.fit(train_images, train_labels)

実行結果は以下のとおりで、ランダムフォレストの結果を超えています。

>python RFC.py
acc:98.33 %

dataset_input.jpg
RFC_results.jpg

まとめ

・SVMでクラス分類をやってみた
・ランダムフォレストのコードにclsの入れ替えだけで手書き文字のクラス分類動いた
K-means、ランダムフォレスト、そしてSVMだとSVMが一番精度よさそうである
※理由はおまけの追記を確認してください

・【追記】それぞれの理論(K-means、K-近傍法、ランダムフォレスト、そしてSVM)を簡単に解説しようと思う
・DLと合わせて、自前データを分類して精度問題、データの食わせ方の整理をしたい

おまけ

from sklearn import datasets
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.model_selection import train_test_split, GridSearchCV
# Import datasets, classifiers and performance metrics
from sklearn import datasets, svm, metrics

mnist = datasets.load_digits()

train_images, test_images, train_labels, test_labels = \
train_test_split(mnist.data, mnist.target, test_size=0.2)

plt.figure(figsize=(15,15))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(train_images[i].reshape((8,8)), cmap=plt.cm.binary)
    plt.xlabel(train_labels[i], fontsize=18)
plt.savefig('RFC/dataset_input.jpg')
plt.pause(1)
plt.close()
"""
clf = RFC(verbose=True,       # 学習中にログを表示します。この指定はなくてもOK
          n_jobs=-1,          # 複数のCPUコアを使って並列に学習します。-1は最大値。
          random_state=2525)  # 乱数のシードです。
clf.fit(train_images, train_labels)

print(clf.feature_importances_)
"""
# Create a classifier: a support vector classifier
clf = svm.SVC(gamma=0.001)
# We learn the digits on the first half of the digits
clf.fit(train_images, train_labels)

#print(f"acc: {clf.score(test_images, test_labels)}")
acc=clf.score(test_images, test_labels)*100
print("acc:{:.2f} %".format(acc))

predicted_labels = clf.predict(test_images)

plt.figure(figsize=(15,15))
# 先頭から25枚テストデータを可視化
for i in range(25):

    # 画像を作成
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(test_images[i].reshape((8,8)), cmap=plt.cm.binary)

    # 今プロットを作っている画像データの予測ラベルと正解ラベルをセット
    predicted_label = predicted_labels[i]
    true_label      = test_labels[i]

    # 予測ラベルが正解なら緑、不正解なら赤色を使う
    if predicted_label == true_label:
        color = 'green' # True label color
    else:
        color = 'blue'   # False label color red
    plt.xlabel("{} True({})".format(predicted_label,
                                  true_label), color=color, fontsize=18)
    if i==0:
        plt.title("acc:{:.2f} %".format(acc), fontsize=36)  

plt.savefig('RFC/RFC_results.jpg')
plt.pause(1)
plt.close()

【追記】K-近傍法によるirisの分類は、以下のコードを上記のSVMのコードに追記するだけで動きました。なぜか、得られた精度も同じでした。

※冒頭の参考の通り、K-近傍法はK-meansとは異なる手法です

from sklearn.neighbors import KNeighborsClassifier
#from sklearn.cross_validation import train_test_split # trainとtest分割用
# train用とtest用のデータ用意。test_sizeでテスト用データの割合を指定。random_stateはseed値を適当にセット。
#X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size = 0.4, random_state=3) 

knn = KNeighborsClassifier(n_neighbors=6) # インスタンス生成。n_neighbors:Kの数
knn.fit(X_train, Y_train)                 # モデル作成実行
Y_pred = knn.predict(X_test)              # 予測実行

# 精度確認用のライブラリインポートと実行
#from sklearn import metrics
acc1=metrics.accuracy_score(Y_test, Y_pred)    # 予測精度計測
print("acc1=",acc1)

実行結果は以下のとおり(SVMの結果も併せて表示しています)
※つまり、データ読み込みはSVCのコードのものを利用しており、最後のacc1だけがこのコードの実行結果です

   sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)  \
0                5.1               3.5                1.4               0.2
1                4.9               3.0                1.4               0.2
2                4.7               3.2                1.3               0.2
3                4.6               3.1                1.5               0.2
4                5.0               3.6                1.4               0.2

   target
0  setosa
1  setosa
2  setosa
3  setosa
4  setosa
112 112 38 38
acc= 0.973684210526
acc1= 0.973684210526

【追記】ランダムフォレストのコードにK-近傍法で殴り込みできました

上記のランダムフォレストのコードを以下の部分のみ変更します。
※精度はSVMとほぼ同一の精度になっています
※冒頭の参考の通り、K-近傍法はK-meansとは異なる手法です

"""
clf = RFC(verbose=True,       # 学習中にログを表示します。この指定はなくてもOK
          n_jobs=-1,          # 複数のCPUコアを使って並列に学習します。-1は最大値。
          random_state=2525)  # 乱数のシードです。
clf.fit(train_images, train_labels)

print(clf.feature_importances_)

# Create a classifier: a support vector classifier
clf = svm.SVC(gamma=0.001)
# We learn the digits on the first half of the digits
clf.fit(train_images, train_labels)
"""
from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=6) # インスタンス生成。n_neighbors:Kの数
clf.fit(train_images, train_labels)

K-means_results.jpg

>python RFC.py
acc:98.61 %
4
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
9