シェルスクリプトマガジン

機械学習ことはじめ(Vol.76掲載)

著者:川嶋 宏彰

本連載では、機械学習の基礎となるさまざまな手法の仕組みや、それらの手法のPythonでの利用方法を解説していきます。今回は、複数のモデルを束ねて用いるアンサンブル学習について解説します。

シェルスクリプトマガジン Vol.76は以下のリンク先でご購入できます。

図2 データセットの読み込みから、散布図のプロットまでを実施するPythonコード

import seaborn as sns
import matplotlib.pyplot as plt

plt.rcParams['font.size'] = 14
penguins = sns.load_dataset('penguins')
# 取り出す特徴量
features = ['bill_depth_mm', 'body_mass_g']  # (★)
# features = ['bill_depth_mm', 'bill_length_mm']  # 取り出す特徴量を変える
# 対象とするペンギン種
target_species = ['Adelie', 'Gentoo']  # (★)
# target_species = ['Adelie', 'Chinstrap', 'Gentoo']  # 3種のペンギンにする
# 今回用いる特徴量をクラスラベルと共に取り出す
df = penguins[['species'] + features].copy()
df.dropna(inplace=True)  # NaN が含まれる行は削除
# 今回用いるペンギン種のみとする
df2 = df[df['species'].isin(target_species)].copy()
print(df2.shape)  # (274, 3) と表示される
# 現在のパレットから用いる種 (target_species) に合わせたパレットを作成
palette = {c: sns.color_palette()[k] for k, c
           in enumerate(df['species'].unique()) if c in target_species}
plt.figure(figsize=(5, 5))
sns.scatterplot(data=df2, x=features[0], y=features[1],
                hue='species', palette=palette)
plt.show()
X = df2[features].values   # 各個体の2次元特徴量(行数=個体数)
y = df2['species'].values  # 各個体のクラスラベル

図3 決定木による2クラス分類をするPythonコード

import matplotlib as mpl
import numpy as np
from sklearn.tree import DecisionTreeClassifier, export_text, plot_tree

def plot_decision_boundary(X, y, clf, xylabels=None, palette=None, fig=None, ngrid=50):
  """ 分類器 clf の決定境界を描画 """
  if fig is None: fig = plt.figure(figsize=(5, 5))
  else: plt.figure(fig.number)
  # 2次元空間にグリッド点を準備
  xmin = X.min(axis=0)  # 各列の最小値
  xmax = X.max(axis=0)  # 各列の最大値
  xstep = [(xmax[j]-xmin[j]) / ngrid for j in range(2)]  # グリッドのステップ幅
  xmin = [xmin[j] - 8*xstep[j] for j in range(2)]  # 少し広めに
  xmax = [xmax[j] + 8*xstep[j] for j in range(2)]
  aranges = [np.arange(xmin[j], xmax[j] + xstep[j], xstep[j]) for j in range(2)]
  x0grid, x1grid = np.meshgrid(*aranges)
  # 各グリッド点でクラスを判定
  y_pred = clf.predict(np.c_[x0grid.ravel(), x1grid.ravel()])
  y_pred = y_pred.reshape(x0grid.shape)  # 2次元に
  y_pred = np.searchsorted(np.unique(y_pred), y_pred)  # 値をインデックスに
  clist = palette.values() if type(palette) is dict else palette
  cmap = mpl.colors.ListedColormap(clist) if palette is not None else None
  plt.contourf(x0grid, x1grid, y_pred, alpha=0.3, cmap=cmap)
  sns.scatterplot(x=X[:, 0], y=X[:, 1], hue=y, palette=palette)
  plt.legend()
  plt.xlim([xmin[0], xmax[0]])
  plt.ylim([xmin[1], xmax[1]])
  if xylabels is not None:
    plt.xlabel(xylabels[0])
    plt.ylabel(xylabels[1])
  return fig
# 決定木
max_depth = 2  #(★)木の深さ
clf_dt = DecisionTreeClassifier(max_depth=max_depth)
clf_dt.fit(X, y)  # 学習
# 決定境界を可視化
plot_decision_boundary(X, y, clf_dt, features, palette)
plt.show()
# 決定木をテキストで表示
tree_text = export_text(clf_dt)
print(tree_text)
# 木の可視化
plt.figure(figsize=(10, 8))
plot_tree(clf_dt, class_names=target_species, feature_names=features)
plt.show()

図13 アンサンブル学習された決定木の表示用関数を定義するPythonコード

from sklearn.base import is_classifier

def plot_trees_and_boundaries(clf, X, y):
  # 決定境界を可視化
  plot_decision_boundary(X, y, clf, features, palette)
  plt.show()
  # 各ベースモデル(決定木)を表示
  for i, clf_dt in enumerate(clf.estimators_[:3]):
    print('-' * 10, i, '-' * 10)
    plt.figure(figsize=(10, 8))
    plot_tree(clf_dt, class_names=target_species, feature_names=features)
    plt.show()
    if is_classifier(clf_dt):  # 分類木ならば決定境界を可視化            
      plot_decision_boundary(X, y, clf_dt, features, palette)
      plt.show()

図14 ランダムフォレストによる分類器の学習をするPythonコード

from sklearn.ensemble import RandomForestClassifier

clf_rf = RandomForestClassifier(n_estimators=100, random_state=1)
clf_rf.fit(X, y)
plot_trees_and_boundaries(clf_rf, X, y)

図16 構成モデルに決定木を用いたバギングをするPythonコード

from sklearn.ensemble import BaggingClassifier

clf_bag = BaggingClassifier(DecisionTreeClassifier(),
                            n_estimators=100, random_state=1)
clf_bag.fit(X, y)

図18 構成モデルに決定木を用いたアダブーストをするPythonコード

from sklearn.ensemble import AdaBoostClassifier

clf_ada = AdaBoostClassifier(DecisionTreeClassifier(max_depth=2),
                             n_estimators=100)
clf_ada.fit(X, y)
plot_trees_and_boundaries(clf_ada, X, y)

図20 構成モデル(回帰木)の数を変えながら勾配ブースティングをするPythonコード

from sklearn.ensemble import GradientBoostingClassifier

for m in [2, 4, 10, 20, 50]:
  clf_gbdt = GradientBoostingClassifier(n_estimators=m)
  clf_gbdt.fit(X, y)  # 学習
  plot_decision_boundary(X, y, clf_gbdt, features, palette)
  plt.title(f'GradientBoosting m = {m}')
  plt.show()

図22 XGBoost による学習をするPythonコード

from xgboost import XGBClassifier

# クラスラベルを整数0、1、2に変換
y_int = df2['species'].map({'Adelie': 0, 'Chinstrap': 1, 'Gentoo': 2}).values
clf_xgb = XGBClassifier(use_label_encoder=False, eval_metric='mlogloss')
clf_xgb.fit(X, y_int)
plot_decision_boundary(X, y, clf_xgb, features, palette)
plt.show()