あいん、sklearnを使った機械学習①(分類)
概要
sklearnは、機械学習のためのpythonライブラリである。pipを持っているけどsklearnを持っていない場合は
pip install scikit-learn
をターミナルに打ち込むとインストールできる。このモジュールは、ざっくり言うとclassifier(分類器)の集まりである。分類器とは、空間上(3次元空間に限定しない。1次元かもしれないし5次元かもしれない)にある、グループ分けされた点の間に面を作って点を区切るものである。また、このモジュールは機械学習を練習するためのサンプルデータや分類器によって分類した結果をいい感じに表示する機能なども備わっている。
使用するデータ
使用するデータは、ndarrayという型でなくてはならない。と書いてあるサイトがあるっぽいが、普通のリストで構わない。ndarrayは、numpyというモジュールで作られる型である。リストLがある時、L=numpy.narray(L)とすればLはndarrayに変換される。もしnumpyを持っていなければ、
pip install numpy
とかでインストールできる。データは、説明変数の集まりをx、出力結果の集まりをyとして、たとえば
import numpy
import random
x_train, x_test, y_train, y_test = [],[],[],[]
for i in range(100):
a,b = random.randrange(1,10), random.randrange(1,10)
x_train.append(numpy.array([a,b]))
y_train.append(1 if(a+b < 10) else -1)
a,b = random.randrange(1,10), random.randrange(1,10)
x_test.append(numpy.array([a,b]))
y_test.append(1 if(a+b < 10) else -1)
のようにして作れる。x_trainとy_trainは機械学習に用いるデータで、x_testとy_testは機械学習が精度良く行われたかどうかを評価するために用いるデータである。普通はデータの数は限られているのでtrainの方に多めにデータを使い、trainとtestの割合は7:3くらいにするが、上記のコードではいくらでもデータを生み出せるのでtrainとtestの割合を同じにしてみた。
機械学習とは本来、学習させたいデータがあるから行うものであるから、機械学習の練習をするためにデータを頑張って探すというのは本末転倒である。そこで、sklearnには最初からいくつかのサンプルデータが備えられている。多くのサイトではアヤメ(アイリス)の分類が用いられているが、他にもさまざまなものがある。以下のサイトを参考にすると良い。
<scikit-learnのサンプルデータセットの一覧と使い方>
今勉強しているのは「分類」の機械学習なので、上記のサイトにあるデータセットのうち、「分類」と示されているのが今使えるデータセットである。
・アイリス(アヤメ)の種類
・手書き文字(数字)
・ワインの種類
・がんの診断結果
が使えることがわかる。使い方は、
import sklearn.datasets
NOTEdata = sklearn.datasets.load_iris()
#あやめのデータ(説明変数)をNOTEdataXに格納する
NOTEdataX = pd.DataFrame(data=NOTEdata.data,columns=NOTEdata.feature_names)
#あやめのデータ(目的変数)をNOTEdataYに格納する
NOTEdataY = pd.DataFrame(data=NOTEdata.target)
NOTEdataY = NOTEdataY.rename(columns={0: 'Species'})
#データの分割を行う(訓練用データ 0.7 評価用データ 0.3)
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(NOTEdataX, NOTEdataY, test_size=0.3)
とすれば使える。僕は、自分で定義した変数名なのかモジュールの方で意味を持っている名前なのか分からないような変数名が嫌いなので、このNOTEで定義した変数名と分かるように、ところどころに自分で定義した変数名にNOTEと付けた。sklearnで用意されているデータは、pandasというモジュールで作られるデータフレームという形になっているので、それをリストにする必要があり、ちょっとめんどくさいところだが、機械学習を学ぶ上で必須では無いので、適当に読み飛ばしてコピーペーストすれば良い。大切なのは、説明変数のリストx_trainとx_test、出力結果のリストy_trainとy_testが正しくリストの形で得られることである。
分類器
分類器こそ、機械学習(分類)の中で最も大切なものだと僕は思う。これは、x_trainとy_trainを元にして学習を行う箇所である。以下のサイトを参考にすれば色々分かるが、データの性質に応じていくつかの分類器が用意されている。clfとは、分類器(classifier)の略である。サイトによってはclfではなくmodelという表記になっていることもあるが、別に良い。好きな名前をつければ良い。ただし、いろいろなサイトからコピペすると途中で別の変数名が使われ始めたりするので注意しよう。
<機械学習のライブラリ!scikit-learnとは【初心者向け】現役エンジニアが解説>
これらの分類器を、以下のようにして使う
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import Perceptron
from sklearn.neural_network import MLPClassifier
clf = LogisticRegression() ###### Edit Here!!
clf.fit(x_train,y_train)
このコードではLogisticRegressionを使っているが、上記のEditHere!!と書いてある行を、
clf = KNeighborsClassifier()
や
clf = DecisionTreeClassifier()
や
clf = SVC(kernel='linear') #サポートベクターマシン 線形
や
clf = SVC(kernel='rbf') #サポートベクターマシン 非線形
のように直せば別の分類器を使える。分類器の理解は機械学習を学ぶ上で非常に大切なことであるが、一気に理解するのは大変なので、おいおいやっていこう。とにかく、分類機にx_trainとy_trainを渡せば機械学習が行われるのである。
ところで、機械学習というと、多層学習や深層学習、ニューラルネットワークなどという文言を聞くことがあるが、それもできる。詳しくは以下のサイトを参考にしよう。
<Python(scikit-learn)でニューラルネットワーク>
長いのは読みたく無い、と言う人は、以下のコードをコピペすれば良い。
from sklearn.neural_network import MLPClassifier
clf = MLPClassifier(solver="sgd",random_state=0,max_iter=10000)
clf.fit(x_train, y_train)
表示
データを作って分類器で学習させて、これでもう機械学習としては終了であるが、我々がその結果を知るために、機械学習の結果を表示する必要がある。それには、以下のコードをコピペすれば良い。
# 評価
from sklearn.metrics import accuracy_score
pred = clf.predict(x_test)
print(accuracy_score(y_test, pred))
学習した結果を、未知のデータにも適応できないと、せっかく学習した意味がない。それには、x_unknownなるデータを用意してから以下のようにすれば良い。
# 学習済みモデルを使う
print(clf.predict(x_unknown))
まとめ
import random
import random as rd
import pandas
import pandas as pd
import numpy
import numpy as np
import sklearn
#############
##### 1 ##### 使用するデータ x_train, y_train, x_test, y_testを定義する
#############
x_train, y_train, x_test, y_test = [],[],[],[]
##データ1 自作
for i in range(100):
a,b = random.randrange(1,10), random.randrange(1,10)
x_train.append([a,b])
y_train.append(1 if(a+b<10) else -1)
a,b = random.randrange(1,10), random.randrange(1,10)
x_test.append([a,b])
y_test.append(1 if(a+b<10) else -1)
##データ2 アヤメ
import sklearn.datasets
data = sklearn.datasets.load_iris()
x = data.data
y = data.target
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)
##データ3 手書き数字
import sklearn.datasets
data = sklearn.datasets.load_digits()
x = data.data
y = data.target
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)
##データ4 ワイン
import sklearn.datasets
data = sklearn.datasets.load_wine()
x = data.data
y = data.target
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)
#############
##### 2 ##### 分類器を選ぶ
#############
##分類器1 ロジスティック回帰
from sklearn.linear_model import LogisticRegression
clf = LogisticRegression()
##分類器2
from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier()
#分類器3 決定木
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier()
#分類器4 サポートベクターマシン 線形
from sklearn.svm import SVC
clf = SVC(kernel='linear')
#分類器5 サポートベクターマシン 非線形
from sklearn.svm import SVC
clf = SVC(kernel='rbf')
#分類器6 ランダムフォレスト
from sklearn.ensemble import RandomForestClassifier
clf = RandomForestClassifier()
#分類器7
from sklearn.linear_model import Perceptron
clf = Perceptron()
#分類器8
from sklearn.neural_network import MLPClassifier
clf = MLPClassifier()
#分類器9 ニューラルネットワーク
from sklearn.neural_network import MLPClassifier
clf = MLPClassifier(solver="sgd",random_state=0,max_iter=10000)
#############
##### 3 ##### 分類器を使う
#############
#まずはこれ
clf.fit(x_train,y_train)
#エラーが出たときはデータが多すぎるということだから、これを使う
clf.fit(x_train[:60],y_train[:60])
#もしくは、多少時間がかかっても良いという人はこれを使う。
clf = clf(max_iter = M)
clf.fit(x_train,y_train)
#############
##### 4 ##### 評価する
#############
from sklearn.metrics import accuracy_score
pred = clf.predict(x_test)
print(accuracy_score(y_test, pred))
まとめてみた。4ステップある。ステップ1で好きなようにサンプルデータを選ぶ。もちろん、学習させたいデータを持っている場合はそれを使えば良い。ステップ2で、分類器を選ぶ。データの種類と分類器の相性によって、結果が良くなったりならなかったりする。ステップ3で分類器を使い、ステップ4でそれを評価する。ステップ3では、clf.fit(x_train,y_train)とすると計算の繰り返し回数の上限が100回になっており、その回数以内に計算が収束しなければ以下のエラーが出る
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
これを防ぐために、使うデータの量を減らすか繰り返す計算回数の上限を増やせば良いのである。以下のサイトを参考にした。
<質問をすることでしか得られない、回答やアドバイスがある。>
補遺 学習結果の図示
mlxtendといモジュールにある、plot_decision_regionsというのを使うと便利です。mlxtendは以下のコマンドをターミナルに打ち込むことで、インストールできます。
pip install mlxtend
ここからの文章は、以下のサイトを参考にしました。
<【Python】Scikit-learn:SVMで行った分類の境界をmlxtendで可視化>
さて、plot_decision_regionsは入力として2つのリストを渡す必要があります。1つめはX, 2つ目はYです。Xというのは今まで使っていたx_trainやx_testなどの説明変数で、Yというのはy_trainやy_testなどの値です。しかし、plot_decision_regionsは出力できるのが平面のグラフなので、x_trainにある多次元ベクトルのうち、使えるのは2つだけです。たとえば、
x_train = [[1,2,1,1,4], [2,1,5,6,2],[2,3,4,1,2],[1,2,1,4,2],[2,4,5,6,2]]だったとき、
x_train2 = [[1,2], [2,1],[2,3],[1,2],[2,4]]みたいな形に変換しないといけません。そして、変換した後に再度学習させる必要があります。また、ここで渡すXやYはndarrayという型でないといけません。これらを考えた上でコードを組みましょう。
import matplotlib.pyplot as plt
from mlxtend.plotting import plot_decision_regions
x_train2 = [0] * len(x_train)
for i in range(len(x_train)):
x_train2[i] = [x_train[i][0],x_train[i][2]]
x_train2 = np.array(x_train2)
y_train = np.array(y_train)
clf.fit(x_train2, y_train)
plot_decision_regions(x_train2, y_train, clf=clf)
plt.show()
上記のようにコードを組みことで、分類器の決定境界を描くことができます。
補遺 ステップ1, ステップ2で全部選ぶ
以下のコードを実行することで、ステップ1で作った4通りのデータについて、ステップ2で選んだ9通りの分類器について、合計4×9 = 36通りのループを回して計算できる。
import random
import random as rd
import pandas
import pandas as pd
import numpy
import numpy as np
import sklearn
#############
##### 1 ##### 使用するデータ x_train, y_train, x_test, y_testを定義する
#############
x_train, y_train, x_test, y_test = [],[],[],[]
x_trains, y_trains, x_tests, y_tests = [],[],[],[]
##データ1 自作
for i in range(100):
a,b = random.randrange(1,10), random.randrange(1,10)
x_train.append([a,b])
y_train.append(1 if(a+b<10) else -1)
a,b = random.randrange(1,10), random.randrange(1,10)
x_test.append([a,b])
y_test.append(1 if(a+b<10) else -1)
x_trains.append(x_train)
y_trains.append(y_train)
x_tests.append(x_test)
y_tests.append(y_test)
##データ2 アヤメ
import sklearn.datasets
data = sklearn.datasets.load_iris()
x = data.data
y = data.target
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)
x_trains.append(x_train)
y_trains.append(y_train)
x_tests.append(x_test)
y_tests.append(y_test)
##データ3 手書き数字
import sklearn.datasets
data = sklearn.datasets.load_digits()
x = data.data
y = data.target
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)
x_trains.append(x_train)
y_trains.append(y_train)
x_tests.append(x_test)
y_tests.append(y_test)
##データ4 ワイン
import sklearn.datasets
data = sklearn.datasets.load_wine()
x = data.data
y = data.target
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)
x_trains.append(x_train)
y_trains.append(y_train)
x_tests.append(x_test)
y_tests.append(y_test)
#############
##### 2 ##### 分類器を選ぶ
#############
M = 300000
clfs = []
##分類器1 ロジスティック回帰
from sklearn.linear_model import LogisticRegression
clf = LogisticRegression(max_iter = M)
clfs.append(clf)
##分類器2
from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier()
clfs.append(clf)
#分類器3 決定木
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier()
clfs.append(clf)
#分類器4 サポートベクターマシン 線形
from sklearn.svm import SVC
clf = SVC(kernel='linear',max_iter = M)
clfs.append(clf)
#分類器5 サポートベクターマシン 非線形
from sklearn.svm import SVC
clf = SVC(kernel='rbf',max_iter = M)
clfs.append(clf)
#分類器6 ランダムフォレスト
from sklearn.ensemble import RandomForestClassifier
clf = RandomForestClassifier()
clfs.append(clf)
#分類器7
from sklearn.linear_model import Perceptron
clf = Perceptron(max_iter = M)
clfs.append(clf)
#分類器8
from sklearn.neural_network import MLPClassifier
clf = MLPClassifier(max_iter = M)
clfs.append(clf)
#分類器9 ニューラルネットワーク
from sklearn.neural_network import MLPClassifier
clf = MLPClassifier(solver="sgd",random_state=0,max_iter=M)
clfs.append(clf)
for k in range(len(x_trains)):
print("")
print("ステップ1: ",k+1,"start!!!!!!!!!")
print("")
for clf in clfs:
x_train = x_trains[k]
y_train = y_trains[k]
x_test = x_tests[k]
y_test = y_tests[k]
clf.fit(x_train,y_train)
from sklearn.metrics import accuracy_score
pred = clf.predict(x_test)
print(clf, accuracy_score(y_test, pred))
結果は以下のようになる。
ステップ1: 1 start!!!!!!!!!
LogisticRegression(max_iter=300000) 1.0
KNeighborsClassifier() 0.97
DecisionTreeClassifier() 0.93
SVC(kernel='linear', max_iter=300000) 1.0
SVC(max_iter=300000) 0.97
RandomForestClassifier() 0.93
Perceptron(max_iter=300000) 1.0
MLPClassifier(max_iter=300000) 1.0
MLPClassifier(max_iter=300000, random_state=0, solver='sgd') 0.92
ステップ1: 2 start!!!!!!!!!
LogisticRegression(max_iter=300000) 0.9111111111111111
KNeighborsClassifier() 0.9555555555555556
DecisionTreeClassifier() 0.8888888888888888
SVC(kernel='linear', max_iter=300000) 0.9333333333333333
SVC(max_iter=300000) 0.9333333333333333
RandomForestClassifier() 0.8888888888888888
Perceptron(max_iter=300000) 0.6888888888888889
MLPClassifier(max_iter=300000) 0.9555555555555556
MLPClassifier(max_iter=300000, random_state=0, solver='sgd') 0.9777777777777777
ステップ1: 3 start!!!!!!!!!
LogisticRegression(max_iter=300000) 0.9740740740740741
KNeighborsClassifier() 0.987037037037037
DecisionTreeClassifier() 0.8685185185185185
SVC(kernel='linear', max_iter=300000) 0.987037037037037
SVC(max_iter=300000) 0.9907407407407407
RandomForestClassifier() 0.9796296296296296
Perceptron(max_iter=300000) 0.9592592592592593
MLPClassifier(max_iter=300000) 0.9851851851851852
MLPClassifier(max_iter=300000, random_state=0, solver='sgd') 0.9777777777777777
ステップ1: 4 start!!!!!!!!!
LogisticRegression(max_iter=300000) 0.9629629629629629
KNeighborsClassifier() 0.6296296296296297
DecisionTreeClassifier() 0.8888888888888888
SVC(kernel='linear', max_iter=300000) 0.9259259259259259
SVC(max_iter=300000) 0.6296296296296297
RandomForestClassifier() 0.9629629629629629
Perceptron(max_iter=300000) 0.6111111111111112
MLPClassifier(max_iter=300000) 0.9629629629629629
MLPClassifier(max_iter=300000, random_state=0, solver='sgd') 0.222222222222222
LogisticRegressionやSVC(kernel='linear', max_iter=300000)は線形な分類器であり、お手軽というか柔軟性に欠けるというか、説明変数の多次元空間の中に、平面でしか分割面を引くことができない。よって、精度はあまりよくならないと予想されたが、今回用いたデータが単純だったのか割と精度が良かった。ステップ1: 1では、用意したデータが完全に線形分離できるものだったので線形な分類器を用いた時に精度が1.0となった。