10. 分類

機械学習による分類を行う.データを分類する能力を持つ分類器の作成である.

使用するデータセット(データセット名: Iris)の出典

出典:http://archive.ics.uci.edu/ml/datasets/Iris

R. A. Fisher

Department of Statistics

University of Cambridge

Iris データセットは,アヤメの 3 品種(setosa,versicolor,virginica)について,がく片の長さ,がく片の幅,花弁の長さ,花弁の幅の 4 特徴量を各品種 50 サンプル,計 150 サンプル収録したデータセットである.本資料では,このうちがく片の長さと幅の 2 特徴量を使用する.

演習準備

この演習では Python を使用する。Python がインストールされていない場合は,下記の「Python 3.12 のインストール(Windows 上)」を展開し,手順に従いインストールすること。下記の「必要なライブラリのインストール」を実施すること。

Python 3.12 のインストール(Windows 上) [クリックして展開]

以下のいずれかの方法で Python 3.12 をインストールする。Python がインストール済みの場合、この手順は不要である。

方法1:winget によるインストール

管理者権限コマンドプロンプトで以下を実行する。管理者権限のコマンドプロンプトを起動するには、Windows キーまたはスタートメニューから「cmd」と入力し、表示された「コマンドプロンプト」を右クリックして「管理者として実行」を選択する。

winget install -e --id Python.Python.3.12 --scope machine --silent --accept-source-agreements --accept-package-agreements --override "/quiet InstallAllUsers=1 PrependPath=1 AssociateFiles=1 InstallLauncherAllUsers=1"

--scope machine を指定することで、システム全体(全ユーザー向け)にインストールされる。このオプションの実行には管理者権限が必要である。インストール完了後、コマンドプロンプトを再起動すると PATH が自動的に設定される。

方法2:インストーラーによるインストール

  1. Python 公式サイト(https://www.python.org/downloads/)にアクセスし、「Download Python 3.x.x」ボタンから Windows 用インストーラーをダウンロードする。
  2. ダウンロードしたインストーラーを実行する。
  3. 初期画面の下部に表示される「Add python.exe to PATH」に必ずチェックを入れてから「Customize installation」を選択する。このチェックを入れ忘れると、コマンドプロンプトから python コマンドを実行できない。
  4. 「Install Python 3.xx for all users」にチェックを入れ、「Install」をクリックする。

インストールの確認

コマンドプロンプトで以下を実行する。

python --version

バージョン番号(例:Python 3.12.x)が表示されればインストール成功である。「'python' は、内部コマンドまたは外部コマンドとして認識されていません。」と表示される場合は、インストールが正常に完了していない。

(1) Linear SVM による機械学習

SVM(サポートベクターマシン)は,データ点の間に決定境界(分類の境界線)を設定することで分類を行う手法である.特に,決定境界とそれに最も近いデータ点との距離(マージン)を最大化するように境界を決定する.Linear SVM は,この決定境界を直線(高次元では超平面)として求める.

正則化パラメータ C は,マージンの最大化と誤分類の許容度のバランスを制御するパラメータである.C の値が大きいほど誤分類を厳しく罰し,小さいほど誤分類をある程度許容してマージンを広くとる.ここでは C を 1.0 に設定する.

Linear SVM のオブジェクトを作成し,classifier という名前で格納する.カーネルには'linear'を指定する.分類対象データとして Iris データセットを使用し,がく片の長さと幅の 2 特徴量を用いて学習を行う.

学習後,動作確認として 2 次元の特徴ベクトル[[5.0, 3.5]]および[[6.5, 3.0]]に対する分類予測を実行する.予測結果が出力され,エラーメッセージが出なければ正常に動作している.出力される数値は品種に対応しており,0 が setosa,1 が versicolor,2 が virginica を表す.

import numpy as np
import sklearn.svm
import matplotlib.pyplot as plt
from sklearn import datasets
import pandas as pd

# Iris データセットの読み込みとデータフレームの作成
iris = datasets.load_iris()
d = pd.DataFrame(data=iris.data, columns=iris.feature_names)
d['species'] = iris.target

# SVM パラメータの設定とモデルの学習
C = 1.0  # 正則化パラメータ
classifier = sklearn.svm.SVC(kernel='linear', C=C).fit(d.iloc[:, [0, 1]], d['species'])

# 分類器の動作確認
print(classifier.predict([[5.0, 3.5]]))
print(classifier.predict([[6.5, 3.0]]))

(2) 機械学習の結果の可視化

ここではまず Linear SVM を用いて,決定境界の可視化手法を学ぶ.(3) では,この手法を用いて複数のカーネルを比較する.

決定境界とは,分類器が異なるクラスを区分する境界のことである.これを可視化するために,特徴量空間上に細かいメッシュグリッド(格子点の集合)を作成し,各格子点に対して分類予測を行う.予測結果を色分けして描画することで,決定境界の形状を視覚的に確認できる.

コード中の ravel() は配列を1次元に平坦化するメソッドである.np.c_ は配列を列方向に結合するためのインデクサである.reshape() は配列の形状を変更するメソッドであり,ここでは1次元の予測結果を元のグリッド形状に戻すために使用している.

以下のコードでは,メッシュグリッドを作成し,各点に対して SVM による分類予測を行い,結果を色分けして表示する.次に,元の Iris データセットの特徴量(がく片の長さと幅)を散布図として重ねて描画する.なお,コード中の変数 m(マージン)は描画領域の余白を意味し,(1) で説明した SVM のマージン(決定境界とデータ点の距離)とは異なる概念である.

また,本コードでは散布図の描画を 2 回行っている.1 回目は iloc によるインデックス指定,2 回目はカラム名による指定であり,同一データに対する異なるアクセス方法の確認を兼ねている.タイトルは後から設定した値で上書きされる.

import numpy as np
import sklearn.svm
import matplotlib.pyplot as plt
from sklearn import datasets
import pandas as pd

# Iris データセットの読み込みとデータフレームの作成
iris = datasets.load_iris()
d = pd.DataFrame(data=iris.data, columns=iris.feature_names)
d['species'] = iris.target

# SVM パラメータの設定とモデルの学習
C = 1.0  # 正則化パラメータ
classifier = sklearn.svm.SVC(kernel='linear', C=C).fit(d.iloc[:, [0, 1]], d['species'])

# 決定境界の可視化のためのメッシュグリッド作成
m = 0.2  # 描画領域の余白
xmin, xmax = d.iloc[:, 0].min() - m, d.iloc[:, 0].max() + m
ymin, ymax = d.iloc[:, 1].min() - m, d.iloc[:, 1].max() + m
h = 0.01  # グリッド解像度

xx, yy = np.meshgrid(np.arange(xmin, xmax, h), np.arange(ymin, ymax, h))

# 決定境界の計算と描画
Z = classifier.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.pcolor(xx, yy, Z, alpha=0.3)
plt.scatter(d.iloc[:, 0], d.iloc[:, 1], c=d['species'], cmap='Set1')
plt.title('Linear SVM on Iris Data')

plt.scatter(d['sepal length (cm)'], d['sepal width (cm)'], c=d['species'], cmap='Set1')
plt.title('Original Iris Data')
plt.show()

実行すると,背景が分類結果に応じて色分けされ,その上に Iris データの散布図が重なって表示される.Linear SVM では決定境界が直線となるため,色分けされた領域の境界が直線で構成されることを確認できる.ただし,直線的な境界ではクラス間の複雑な分布に対応しきれない場合がある.次の (3) では,非線形カーネルを導入することでこの制約を緩和する.

(3) 3 種類の SVM カーネルを用いた分類結果の比較

SVM はカーネル関数を変更することで,決定境界の形状を変えられる.カーネル関数は,元の特徴量空間を高次元空間に写像する役割を果たす.高次元空間で線形分離を行うことにより,元の空間では非線形の決定境界が得られる.

ここでは以下の 3 種類のカーネルを比較する:

カーネルの選択にあたっては,一般にまず RBF カーネルを試し,データの性質に応じて他のカーネルを検討することが多い.

すべてのモデルにおいて,正則化パラメータ C を 1.0 に設定する.Iris データセットのがく片の長さと幅を特徴量として学習を行う.可視化のために,データの範囲に 0.2 のマージンを加えた領域を 0.01 間隔のメッシュグリッドに分割する.各 SVM モデルでの分類結果を描画する.品種に応じて'Set1'カラーマップで色分けする.

# 3 種類のカーネルを用いた SVM の比較
import numpy as np
import sklearn.svm
import matplotlib.pyplot as plt
from sklearn import datasets
import pandas as pd

# Iris データセットの読み込みとデータフレームの作成
iris = datasets.load_iris()
d = pd.DataFrame(data=iris.data, columns=iris.feature_names)
d['species'] = iris.target

# SVM パラメータの設定とモデルの学習
C = 1.0  # 正則化パラメータ

# 3 種類の SVM モデルの学習と可視化
classifiers = [
    ('Linear SVM', sklearn.svm.SVC(kernel='linear', C=C)),
    ('RBF SVM', sklearn.svm.SVC(kernel='rbf', C=C, gamma=0.7)),
    ('Polynomial SVM', sklearn.svm.SVC(kernel='poly', C=C, degree=3))
]

m = 0.2  # 描画領域の余白
xmin, xmax = d.iloc[:, 0].min() - m, d.iloc[:, 0].max() + m
ymin, ymax = d.iloc[:, 1].min() - m, d.iloc[:, 1].max() + m
h = 0.01  # グリッド解像度

xx, yy = np.meshgrid(np.arange(xmin, xmax, h), np.arange(ymin, ymax, h))

plt.figure(figsize=(15, 5))
for i, (name, clf) in enumerate(classifiers, 1):
    clf.fit(d.iloc[:, [0, 1]], d['species'])
    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    plt.subplot(1, 3, i)
    plt.pcolor(xx, yy, Z, alpha=0.3)
    plt.scatter(d.iloc[:, 0], d.iloc[:, 1], c=d['species'], cmap='Set1')
    plt.title(f'{name} on Iris Data')

plt.show()

実行すると,3 つの図が横に並んで表示される.Linear SVM では直線的な境界,RBF SVM では曲線的な境界,Polynomial SVM では多項式に基づく境界がそれぞれ描画される.カーネルの違いにより,同じデータに対して異なる決定境界が形成されることを確認できる.

(4) k-最近傍法(k-NN)を用いた分類

k-最近傍法(k-Nearest Neighbor, k-NN)は,未知のデータ点に最も近い k 個の学習データを参照し,多数決でクラスを決定する手法である.SVM とは異なり,学習時にモデルを構築するのではなく,予測時に都度近傍点を探索する.

k の値は分類結果に影響を与える.k が小さいほど個々のデータ点の影響が大きくなり,決定境界が複雑になる(過学習しやすい).k が大きいほど決定境界が滑らかになり,ノイズの影響を受けにくくなるが,境界が単純化しすぎる場合がある.

ここでは k の値を 1,3,5 と変えて,その影響を比較する.Iris データセットのがく片の長さと幅を特徴量として使用する.可視化のために,データの範囲に 0.2 のマージンを加えた領域を 0.01 間隔のメッシュグリッドに分割する.各 k-NN モデルでの分類結果を描画する.品種に応じて'Set1'カラーマップで色分けする.

# k-最近傍法による分類と可視化
import numpy as np
import sklearn.neighbors
import matplotlib.pyplot as plt
from sklearn import datasets
import pandas as pd

# Iris データセットの読み込みとデータフレームの作成
iris = datasets.load_iris()
d = pd.DataFrame(data=iris.data, columns=iris.feature_names)
d['species'] = iris.target

# メッシュグリッドの作成
m = 0.2  # 描画領域の余白
xmin, xmax = d.iloc[:, 0].min() - m, d.iloc[:, 0].max() + m
ymin, ymax = d.iloc[:, 1].min() - m, d.iloc[:, 1].max() + m
h = 0.01  # グリッド解像度

xx, yy = np.meshgrid(np.arange(xmin, xmax, h), np.arange(ymin, ymax, h))

# 3 種類の k-NN モデルの学習と可視化
classifiers = [
    ('1-Nearest Neighbor', sklearn.neighbors.KNeighborsClassifier(n_neighbors=1)),
    ('3-Nearest Neighbor', sklearn.neighbors.KNeighborsClassifier(n_neighbors=3)),
    ('5-Nearest Neighbor', sklearn.neighbors.KNeighborsClassifier(n_neighbors=5))
]

plt.figure(figsize=(15, 5))
for i, (name, clf) in enumerate(classifiers, 1):
    clf.fit(d.iloc[:, [0, 1]], d['species'])
    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    plt.subplot(1, 3, i)
    plt.pcolor(xx, yy, Z, alpha=0.3)
    plt.scatter(d.iloc[:, 0], d.iloc[:, 1], c=d['species'], cmap='Set1')
    plt.title(f'{name} on Iris Data')

plt.show()

実行すると,3 つの図が横に並んで表示される.k=1 では決定境界が複雑になり,個々のデータ点の影響が強く表れる.k=3,k=5 と値を大きくするにつれて決定境界が滑らかになることを確認できる.

(5) ランダムフォレストを用いた分類

ランダムフォレストは,複数の決定木を組み合わせて分類を行うアンサンブル学習の手法である.個々の決定木は学習データからランダムにサンプリングしたデータと特徴量を用いて構築され,最終的な予測は全決定木の多数決で決定される.単一の決定木に比べて過学習しにくく,汎化性能が高いという特徴がある.

sklearn.ensemble モジュールの RandomForestClassifier を使用し,デフォルトのパラメータ設定でモデルを学習する.Iris データセットのがく片の長さと幅を特徴量として使用する.可視化のために,データの範囲に 0.2 のマージンを加えた領域を 0.01 間隔のメッシュグリッドに分割する.ランダムフォレストでの分類結果を描画する.品種に応じて'Set1'カラーマップで色分けする.

さらに,学習したモデルの特徴量重要度(feature_importances_)を算出し,がく片の長さと幅それぞれの分類への寄与度を表示する.特徴量重要度は,各特徴量の分類への貢献度を 0 から 1 の範囲で示す値であり,全特徴量の合計は 1 となる.

なお,ランダムフォレストは内部でランダムなサンプリングを行うため,実行するたびに決定境界や特徴量重要度の値がわずかに変化する.結果を固定したい場合は,RandomForestClassifier(random_state=0) のように乱数シードを指定する.

# ランダムフォレストによる分類と可視化
import numpy as np
import sklearn.ensemble
import matplotlib.pyplot as plt
from sklearn import datasets
import pandas as pd

# Iris データセットの読み込みとデータフレームの作成
iris = datasets.load_iris()
d = pd.DataFrame(data=iris.data, columns=iris.feature_names)
d['species'] = iris.target

# メッシュグリッドの作成
m = 0.2  # 描画領域の余白
xmin, xmax = d.iloc[:, 0].min() - m, d.iloc[:, 0].max() + m
ymin, ymax = d.iloc[:, 1].min() - m, d.iloc[:, 1].max() + m
h = 0.01  # グリッド解像度

xx, yy = np.meshgrid(np.arange(xmin, xmax, h), np.arange(ymin, ymax, h))

# ランダムフォレストモデルの学習と可視化
classifier7 = sklearn.ensemble.RandomForestClassifier().fit(d.iloc[:, [0, 1]], d['species'])

Z = classifier7.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

# 決定境界とデータ点の可視化
plt.pcolor(xx, yy, Z, alpha=0.3)
plt.scatter(d.iloc[:, 0], d.iloc[:, 1], c=d['species'], cmap='Set1')
plt.title('Random Forest on Iris Data')
plt.show()

# 特徴量の重要度を表示
print('特徴量の重要度:', classifier7.feature_importances_)

実行すると,ランダムフォレストによる決定境界が表示される.ランダムフォレストは複数の決定木の集合であるため,決定境界は直線や滑らかな曲線ではなく,階段状の複雑な形状となることが多い.また,特徴量重要度の出力により,がく片の長さと幅のどちらが分類により大きく寄与しているかを定量的に確認できる.

まとめ

本資料では,Iris データセットのがく片の長さと幅の 2 特徴量を用いて,3 種類の分類手法を実装し比較した.各手法の特徴を整理する.

SVM は,決定境界とデータ点の間のマージンを最大化する手法である.カーネル関数の変更により,直線的な境界から曲線的な境界まで対応できる.正則化パラメータ C やカーネルのパラメータ(gamma,degree)の設定が分類結果に影響を与える.

k-最近傍法は,近傍点の多数決によって分類を行う手法である.予測時に近傍探索を行い,明示的なモデル構築を行わない点で SVM やランダムフォレストとは異なる.k の値によって決定境界の複雑さが変化する.

ランダムフォレストは,複数の決定木のアンサンブルによって分類を行う手法である.過学習しにくく,特徴量重要度を算出できるという利点がある.

手法の選択にあたっては,データの規模,特徴量の性質,計算コスト,結果の解釈しやすさなどを考慮する必要がある.本資料で扱った各手法の決定境界の違いは,手法選択の判断材料となる.

いずれの手法においても,パラメータの選択がモデルの性能に影響を与える.実際のデータ分析では,交差検証(データを分割して学習と評価を繰り返す手法)などを用いたパラメータの調整が重要である.