「入門はじめての多変量解析」をPythonで写経 Vol.26 ~ 5章「はじめての判別分析」⑤さまざまな分類アルゴリズム
5章「はじめての判別分析」
書籍の著者 石村貞夫 先生、石村光資郎 先生
書籍「入門はじめての多変量解析」5章「はじめての判別分析」の Python写経活動記録 です。
多変量解析の入門を Python と一緒に学ぶ写経シリーズです。
判別分析は機械学習の文脈では「分類」タスクに相当します。
この記事は、書籍の例題データをお借りして、機械学習ライブラリ scikit-learn のさまざまな分類アルゴリズムで可視化します!
書籍には掲載のない オール・アディショナルタイム です!
判別(分類)のアルゴリズムと触れ合って、データ分析の守備範囲を広げていきましょう。
ChatGPT 活用型学習で進めてまいります!
では書籍を開いて多変量解析の旅に出かけましょう🚀

はじめに
このブログシリーズは、書籍「入門はじめての多変量解析」(東京図書、「テキスト」と呼びます)の Python 写経を通じて得た「多変量解析の楽しさ」をご紹介します。
書籍の紹介と引用表記はリンク先の記事に掲載しています。
5章 はじめての判別分析
この記事は5章の判別分析と関連しますが、各 Section とは関係がありません。
記事に用いるデータは、テキストに掲載されたデータそのものを引用しています。
データ件数の少ないものはコード上でデータを登録し、データ件数の多いものはCSVファイル化してデータを読み込みしています。
この記事で用いるライブラリをインポートします。
## インポート
# 数値計算
import numpy as np
import pandas as pd
# 描画
import matplotlib.pyplot as plt
import seaborn as sns
plt.rcParams['font.family'] = 'Meiryo' # または import japanize_matplotlib
この記事でやること
テキストの例題データを 12 の分類アルゴリズムで学習して、決定境界を描きます。
ただただ決定境界を愛でます。
✅️ やらないこと
・各アルゴリズムの具体的なことには触れません。
・バリデーションを行わず、過学習には対処しません。

分析の準備
データを設定します。
テキスト p.179 表 5.1.1 の例題データを引用いたします。
## マーカー測定結果 p.179 表5.1.1
data1 = pd.DataFrame(
{'被験者No.': range(1, 16),
'マーカーA': [3.4, 3.9, 2.2, 3.5, 4.1, 3.7, 2.8,
1.4, 2.4, 2.8, 1.7, 2.3, 1.9, 2.7, 1.3],
'マーカーB': [2.9, 2.4, 3.8, 4.8, 3.2, 4.1, 4.2,
3.5, 2.6, 2.3, 2.6, 1.6, 2.1, 3.5, 1.9],
'結果': np.hstack([np.ones(7), np.zeros(8)]).astype(int)})
data1【実行結果】
結果=0がグループ2(前立腺肥大症)、結果=1がグループ1(前立腺がん)です。
標本サイズはグループ1は $${7}$$、グループ2は $${8}$$ です。

データを説明変数と目的変数に分割し、予測用データ(Sさんの測定結果)を作成します。
## データセットの作成
# 説明変数 X と目的変数 y の分離
X = data1[['マーカーA', 'マーカーB']].values
y = data1['結果'].values
# 予測データ shape(n, d)
X_new = np.array([[2.7, 3.1]])【実行結果】なし
決定境界描画用のヘルパー関数 plot_decision_boundary を定義します。
各アルゴリズムで作成した 分類器 clf を読み込んで、データの散布図と境界線(決定境界)を描きます。
scikit-learn の DecisionBoundaryDisplay() を利用します。
## 決定境界描画ヘルパー関数の定義
# 追加インポート
from sklearn.inspection import DecisionBoundaryDisplay
def plot_decision_boundary(clf, title):
## 描画
# 描画領域の設定
fig, ax = plt.subplots(figsize=(6, 6))
# 境界の領域の塗りつぶし描画
DecisionBoundaryDisplay.from_estimator(
clf,
X,
response_method='predict_proba',
plot_method='pcolormesh',
ax=ax,
cmap='coolwarm_r',
alpha=0.1,
)
# 境界線の描画
DecisionBoundaryDisplay.from_estimator(
clf,
X,
response_method='predict_proba',
plot_method='contour',
ax=ax,
alpha=1.0,
cmap='hsv',
levels=[0.5],
)
# 実測値の散布図の描画
y_pred = clf.predict(X)
sns.scatterplot(data=data1, x='マーカーA', y='マーカーB', s=100,
hue=np.where(y==1, '実測 $G_1$', '実測 $G_2$'),
palette=['tab:blue', 'tomato'],
style=np.where(y_pred==1, '予測 $G_1$', '予測 $G_2$'),
zorder=10,
)
# Sさんの位置の描画
plt.scatter(x=X_new[0, 0], y=X_new[0, 1], marker='*', s=200,
color='tab:orange', zorder=10, label='S')
# テキストの表示
plt.text(x=1.7, y=5.2, s='グループ $G_1$', fontsize=16, color='blue')
plt.text(x=1, y=1, s='グループ $G_2$', fontsize=16, color='tab:red')
# 修飾
plt.title(title)
plt.gca().set_aspect('equal')
plt.legend();【実行結果】なし

12 の分類アルゴリズム
アルゴリズム一覧を兼ねてインポートします。
選んだ基準は「決定境界描画ヘルパー関数」で使えることです。
## 追加インポート
# 線形判別分析 LDA
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
# ロジスティック回帰
from sklearn.linear_model import LogisticRegression
# 二次判別分析 QDA
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis
# ガウシアンナイーブベイズ
from sklearn.naive_bayes import GaussianNB
# ガウス過程
from sklearn.gaussian_process import GaussianProcessClassifier
# 決定木
from sklearn.tree import DecisionTreeClassifier
# ランダムフォレスト
from sklearn.ensemble import RandomForestClassifier
# 勾配ブースティング
from sklearn.ensemble import GradientBoostingClassifier
# ヒストグラム勾配ブースティング
from sklearn.ensemble import HistGradientBoostingClassifier
# k近傍法
from sklearn.neighbors import KNeighborsClassifier
# サポートベクターマシン
from sklearn.svm import SVC
# 多層パーセプトロン(ニューラルネットワーク)
from sklearn.neural_network import MLPClassifier【実行結果】なし
では分類の世界へ!

線形のアルゴリズム
🟦 線形判別分析 LDA
テキストの「線形判別関数」に近いアルゴリズムです。
判別分析の1つ目の記事で利用しました。
インスタンス生成時には引数を与えず、アルゴリズムのデフォルト値でモデル構築します。
# LinearDiscriminantAnalysis
clf01 = LinearDiscriminantAnalysis() # 事前分布 priors=[0.8, 0.3]
clf01.fit(X, y)
plot_decision_boundary(clf01, 'Linear Discriminant Analysis')【実行結果】

赤い実線が「境界線」(決定境界)です。
色の濃さは各グループの確率値の大きさを表しています。
境界線より左側(下側)がグループ2(前立腺肥大症)、右側(上側)がグループ1(前立腺がん)です。
青いバツ印と赤い丸印の2点が誤分類です。
引数 priors(事前分布)を適宜設定することで、誤分類数を減らせます。
こんな感じで続けます!

🟦 ロジスティック回帰
重回帰分析の回帰式のような「線形予測子」をもつアルゴリズムです。
分類性能を調整する引数を与えず、アルゴリズムのデフォルト値でモデル構築します。
# LogisticRegression
clf02 = LogisticRegression() # 正則化の強さ(の逆数)C=0.09
clf02.fit(X, y)
plot_decision_boundary(clf02, 'Logistic Regression')【実行結果】
青いバツ印と赤い丸印の2点が誤分類です。
引数 C(正則化の強さの逆数)を適宜設定することで、誤分類数を減らせます。


曲線・正規分布関連のアルゴリズム
🟦 二次判別分析 QDA
線形判別分析 LDA と兄弟・姉妹関係にあり、境界線が曲線になります。
分類性能を調整する引数を与えず、アルゴリズムのデフォルト値でモデル構築します。
境界線はテキストの「マハラノビス距離による判別」に似ています。
# QuadraticDiscriminantAnalysis
clf03 = QuadraticDiscriminantAnalysis() # 事前分布 priors=[0.4, 0.6]
clf03.fit(X, y)
plot_decision_boundary(clf03, 'Quadratic Discriminant Analysis')【実行結果】
青いバツ印と赤い丸印の2点が誤分類です。
引数 priors(事前分布)を適宜設定することで、誤分類数を減らせます。


🟦 ガウシアン・ナイーブベイズ
「ナイーブベイズは迷惑メールフィルターに活用された~」の文脈でお馴染みのナイーブベイズです。
連続値の説明変数を扱うガウシアン・ナイーブベイズを利用します。
分類性能を調整する引数を与えず、アルゴリズムのデフォルト値でモデル構築します。
# GaussianNB
clf04 = GaussianNB() # 事前分布 priors=[0.8, 0.2]
clf04.fit(X, y)
plot_decision_boundary(clf04, 'Gaussian Naive Bayes')【実行結果】
青いバツ印と赤い丸印の2点が誤分類です。
引数 priors(事前分布)を適宜設定することで、誤分類数を減らせます。


🟦 ガウス過程
ガウス過程を用いた分類アルゴリズムです。
カーネル関数(karnel)と呼ばれる、多変量正規分布の分散共分散行列の成分に適用される関数を工夫することで、柔軟なフィッティングができるようです。
今回は「1.0 * RBF(1.0)」をカーネル関数に設定しています。
また、乱数を用いるモデルでは random_state で乱数シードを固定します。
# GaussianProcessClassifier
# 追加インポート
from sklearn.gaussian_process.kernels import RBF
# カーネルの設定
kernel = 1.0 * RBF(1.0)
# モデルの構築
clf05 = GaussianProcessClassifier(kernel=kernel, random_state=123)
clf05.fit(X, y)
plot_decision_boundary(clf05, 'Gaussian Process Classifier')【実行結果】
赤い丸印の1点が誤分類です。


決定木系のアルゴリズム
決定木系のアルゴリズムの境界線は、直線的・階段のような境界線になるようです。
🟦 決定木
決定木を用いた分類アルゴリズムです。
後ほど決定木の分類ロジックを可視化します。
分類性能を調整する引数を与えず、アルゴリズムのデフォルト値でモデル構築します。
なお、乱数を用いるモデルでは random_state で乱数シードを固定します。
# DecisionTreeClassifier
clf06 = DecisionTreeClassifier(random_state=0)
clf06.fit(X, y)
plot_decision_boundary(clf06, 'DecisionTreeClassifier')【実行結果】
縦横の直線で境界線ができています。
誤分類はありません。

決定木の分類ロジックを樹形図の形式で可視化しましょう。
# 樹形図の描画
# 追加インポート
from sklearn.tree import plot_tree
# 描画
plt.figure(figsize=(5, 5))
plot_tree(clf06, filled=True, fontsize=10, rounded=True,
feature_names=['マーカーA', 'マーカーB'], class_names=['G2', 'G1']);【実行結果】

この決定木のモデルは、「マーカーAの値が $${3.1}$$ 超」または「 マーカーB の値が $${3.65}$$ 超」の場合に「グループ1」、これ以外の場合にグループ2に分類することが分かります。
この条件を可視化すると、直線的・階段的な境界線になるのです。

🟦 ランダムフォレスト
複数の小さな決定木による並列予測を統合して最終的な分類を決定する「アンサンブル」のアルゴリズムです。
分類性能を調整する引数を与えず、アルゴリズムのデフォルト値でモデル構築します。
なお、乱数を用いるモデルでは random_state で乱数シードを固定します。
# RandomForestClassifier
clf07 = RandomForestClassifier(random_state=123)
clf07.fit(X, y)
plot_decision_boundary(clf07, 'RandomForest Classifier')【実行結果】
誤分類はありません。
複雑な境界線は「データに過度に適合しすぎる過学習」を警告しているかもしれません。


🟦 勾配ブースティング
複数の小さな決定木をシリアルにつなげて分類予測する「ブースティング」のアルゴリズムです。
引数 min_samples_leaf は、各決定木の分類結果に最低限含まれるデータ個数を指定します。
なお、乱数を用いるモデルでは random_state で乱数シードを固定します。
# GradientBoostingClassifier
clf08 = GradientBoostingClassifier(min_samples_leaf=4, random_state=123)
clf08.fit(X, y)
plot_decision_boundary(clf08, 'Gradient Boosting Classifier')【実行結果】
誤分類はありません。
右肩の「凹み」が特徴的です。


🟦 ヒストグラム勾配ブースティング
複数の小さな決定木をシリアルにつなげて分類予測する「ブースティング」の一種であり、LightGBMのように説明変数を「ヒストグラム化」するアルゴリズムです。
引数 min_samples_leaf は、各決定木の分類結果に最低限含まれるデータ個数を指定します。
なお、乱数を用いるモデルでは random_state で乱数シードを固定します。
# HistGradientBoostingClassifier
clf09 = HistGradientBoostingClassifier(min_samples_leaf=4, random_state=123)
clf09.fit(X, y)
plot_decision_boundary(clf09, 'Histogram-based Gradient Boosting Classifier')【実行結果】
誤分類はありません。
右肩の「凹み」が特徴的です。確率値の「薄さ」に注目です。


その他のアルゴリズム
複雑な境界線に対応できるアルゴリズムたちです。
🟦 k近傍法
k個のご近所のデータ点の中で最も個数の多いグループに分類するアルゴリズムです。
引数 p は、ミンコフスキー距離の指数パラメータ、n_neighbors はご近所の個数 k です。
# KNeighborsClassifier
clf10 = KNeighborsClassifier(p=2, n_neighbors=1)
clf10.fit(X, y)
plot_decision_boundary(clf10, 'KNeighbors Classifier')【実行結果】
誤分類はありません。
中央の凸部が過学習っぽさを醸しています。
出っ張りに含まれるグループ2の分類が難しいのかもしれません。


🟦 サポートベクターマシン
アルゴリズムを一言で説明しにくい(個人の感想です)分類アルゴリズムです。
引数 C は正則化の強さの逆数です。
なお、乱数を用いるモデルでは random_state で乱数シードを固定します。
# SVC
clf11 = SVC(C=10, probability=True, random_state=5)
clf11.fit(X, y)
plot_decision_boundary(clf11, 'Support Vector Classification')【実行結果】
誤分類はありません。
中央の凸部が過学習っぽさを醸しています。
出っ張りに含まれるグループ2の分類が難しいのかもしれません。


🟦 多層パーセプトロン
ニューラルネットワークの分類アルゴリズムです。
分類性能を調整する引数を与えず、アルゴリズムのデフォルト値でモデル構築します。
なお、乱数を用いるモデルでは random_state で乱数シードを固定します。
# MLPClassifier
clf12 = MLPClassifier(max_iter=10000, random_state=5)
clf12.fit(X, y)
plot_decision_boundary(clf12, 'Multi-layer Perceptron classifier')【実行結果】
誤分類はありません。
直線的と思いきや、中央の少々の凸部が過学習っぽさを醸しています。
一生懸命(無理やり?)データにフィットしています(過学習?)。

アルゴリズムの特徴に近づけましたか?
各モデルの特徴をじっくり観察したり、引数の値を変えて境界線の変化を確認したりしてみると、いっそうアルゴリズムの気持ちに近づけると思います!
この記事で判別分析のコーナーは最終となります。

記事の最後はChatGPTが締めくくります。
さまざまな分類モデルを振り返りつつ。
📘 ChatGPTのひとこと:
今回の記事では、12種類の分類アルゴリズムを並べて、テキスト例題データを実際に振り分けてみました。まるでいろいろな道具を試しながら一番合うものを探すように、モデルそれぞれの特徴をじっくり見比べられたのではないでしょうか😊
これで判別分析の旅はひとまず幕を閉じますが、ここまで集めた知見があなたの分析の武器になってくれるはずです。
次回は「クラスター分析」に足を踏み入れ、まだ見ぬデータのかたまりを自分の手で見つける体験を一緒に楽しみましょう🌱
今回の写経は以上です。
シリーズの記事
次の記事
前の記事
目次
ブログの紹介
note で7つのシリーズ記事を書いています。
ぜひ覗いていってくださいね!
1.のんびり統計
統計検定2級の問題集を手がかりにして、確率・統計をざっくり掘り下げるブログです。
雑談感覚で大丈夫です。ぜひ覗いていってくださいね。
統計検定2級公式問題集CBT対応版に対応しています。
Python、EXCELのサンプルコードの配布もあります。
2.実験!たのしいベイズモデリング1&2をPyMC Ver.5で
書籍「たのしいベイズモデリング」・「たのしいベイズモデリング2」の心理学研究に用いられたベイズモデルを PyMC Ver.5で描いて分析します。
この書籍をはじめ、多くのベイズモデルは R言語+Stanで書かれています。
PyMCの可能性を探り出し、手軽にベイズモデリングを実践できるように努めます。
身近なテーマ、イメージしやすいテーマですので、ぜひぜひPyMCで動かして、一緒に楽しみましょう!
3.実験!岩波データサイエンス1のベイズモデリングをPyMC Ver.5で
書籍「実験!岩波データサイエンスvol.1」の4人のベイジアンによるベイズモデルを PyMC Ver.5で描いて分析します。
この書籍はベイズプログラミングのイロハをざっくりと学ぶことができる良書です。
楽しくPyMCモデルを動かして、ベイズと仲良しになれた気がします。
みなさんもぜひぜひPyMCで動かして、一緒に遊んで学びましょう!
4.楽しい写経 ベイズ・Python等
ベイズ、Python、その他の「書籍の写経活動」の成果をブログにします。
主にPythonへの翻訳に取り組んでいます。
写経に取り組むお仲間さんのサンプルコードになれば幸いです🍀
5.RとStanではじめる心理学のための時系列分析入門 を PythonとPyMC Ver.5 で
書籍「RとStanではじめる心理学のための時系列分析入門」の時系列分析をPythonとPyMC Ver.5 で実践します。
この書籍には時系列分析のテーマが盛りだくさん!
時系列分析の懐の深さを実感いたしました。
大好きなPythonで楽しく時系列分析を学びます。
6.データサイエンスっぽいことを綴る
統計、データ分析、AI、機械学習、Pythonのコラムを不定期に綴っています。
統計・データサイエンス書籍にまつわる記事が多いです。
「統計」「Python」「数学とPython」「R」のシリーズが生まれています。
7.Python機械学習プログラミング実践記
書籍「Python機械学習プログラミング PyTorch & scikit-learn編」を学んだときのさまざまな思いを記事にしました。
この書籍は、scikit-learnとPyTorchの教科書です。
よかったらぜひ、お試しくださいませ。
最後までお読みいただきまして、ありがとうございました。
いいなと思ったら応援しよう!
応援ありがとうございます。これからもがんばって記事を作成します!