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

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

著者:川嶋 宏彰

本連載では、機械学習の基礎となるさまざまな手法の仕組みや、それらの手法のPythonでの利用方法を解説していきます。第1回となる今回は、機械学習の概要についても解説します。また、「k近傍法」という手法を使いながら機械学習の重要な概念をいくつか紹介します。

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

図7 データセットを読み込んで散布図行列をプロットするPythonコード

import seaborn as sns
import matplotlib.pyplot as plt

plt.rcParams['font.size'] = 14

# seabornより読み込む場合
penguins = sns.load_dataset('penguins')

# seabornではなくpalmerpenguinsから読み込む場合
# from palmerpenguins import load_penguins
# penguins = load_penguins()

# 散布図行列をプロット
sns.pairplot(penguins, hue='species')
plt.show()

# 相関係数を計算
print(penguins.corr())

図9 二つの特徴量を抽出して散布図をプロットするPythonコード

features = ['bill_depth_mm', 'body_mass_g']
target_species = ['Adelie', 'Gentoo']

# 特徴量をクラスラベルと共に取り出す
df = penguins[['species'] + features].copy()
df.dropna(inplace=True)  # NaN が含まれる行は削除

# 読み込むデータセットを対象種のものだけにする
df2 = df[df['species'].isin(target_species)].copy()
print(df2.shape)  # (274, 3) と表示される

# カラーパレットの取得と設定
palette = sns.color_palette()
palette2 = {'Adelie':palette[0], 'Gentoo':palette[2]}

fig = plt.figure(figsize=(5, 5))
sns.scatterplot(data=df2, x=features[0], y=features[1],
                hue='species', palette=palette2)
# plt.axis('equal')  # ★この行は後で利用
plt.show()

図11 k-NNによる判定をするシンプルなPythonコード

import numpy as np
import scipy

X = df2[features].values  # 2次元特徴量
y = df2['species'].values  # クラスラベル

x_test = np.array([16, 4000])  # 判定したいデータ
k = 5  # 近い順に何個のデータまで見るか

# x_testとXの各点(各行)との距離の二乗
dist2 = ((X - x_test) ** 2).sum(axis=1)
# 距離の小さいk個の点のラベル
k_labels = y[np.argsort(dist2)][:k]
result = scipy.stats.mode(k_labels)[0][0]  # 最頻ラベル

図12 k-NNによる判定をする改良版のPythonコード

from sklearn.neighbors import KNeighborsClassifier

clf = KNeighborsClassifier(n_neighbors=k)
clf.fit(X, y)  # 学習
y_pred = clf.predict([[16, 4000], [16, 5000]])  # 一度に複数判定
print(y_pred)

図13 分類器の決定境界を描画するPythonコード

import matplotlib as mpl

def plot_decision_boundary(X, y, clf, xylabels=features, palette=None):
    # 分類器clfの決定境界を描画
    fig = plt.figure(figsize=(5, 5))
    # 2次元空間にグリッド点を準備
    xmin = X.min(axis=0)  # 各列の最小値
    xmax = X.max(axis=0)  # 各列の最大値
    xstep = [(xmax[i]-xmin[i]) / 50 for i in range(2)]  # グリッドのステップ幅
    xmin = [xmin[i] - 8*xstep[i] for i in range(2)]  # 少し広めに
    xmax = [xmax[i] + 8*xstep[i] for i in range(2)]
    aranges = [np.arange(xmin[i], xmax[i] + xstep[i], xstep[i]) for i 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)  # 値をindexへ

    clist = palette.values() if type(palette) is dict else palette
    cmap = mpl.colors.ListedColormap(clist)
    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]])
    plt.xlabel(xylabels[0])
    plt.ylabel(xylabels[1])

clf_orig = KNeighborsClassifier(n_neighbors=k)
clf_orig.fit(X, y)
plot_decision_boundary(X, y, clf_orig, palette=palette2)
plt.show()

図16 スケーリング後に決定境界を描画するPythonコード

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
Xs = scaler.fit_transform(X)
# StandardScaler を用いず以下のようにしてもよい
# Xs = (X - X.mean(axis=0))/X.std(axis=0)

clf_scaled = KNeighborsClassifier(n_neighbors=k)
clf_scaled.fit(Xs, y)

# 軸ラベル変更
xylabels = [s.replace('_mm', '_s').replace('_g', '_s')
           for s in features]
# スケーリング後の決定境界を描く
plot_decision_boundary(Xs, y, clf_scaled, 
                       xylabels=xylabels, palette=palette2)
plt.show()

図21 kの値を変えた場合の3種のペンギンの決定境界を描画するPythonコード

# 取り出す特徴量を変える
features2 = ['bill_depth_mm', 'bill_length_mm']
df3 = penguins[['species'] + features2].copy()
df3.dropna(inplace=True)  # NaN が含まれる行は削除
Xs = scaler.fit_transform(df3[features2])
y = df3['species']

palette3 = dict(zip(y.unique(), palette))  # 3種用カラーパレット
xylabels = [s.replace('_mm', '_s') for s in features2]

for k in [1, 5, 11]:
    clf_scaled = KNeighborsClassifier(n_neighbors=k)
    clf_scaled.fit(Xs, y)
    plot_decision_boundary(Xs, y, clf_scaled, 
                           xylabels=xylabels, palette=palette3)
    plt.title(f'k = {k}')
    plt.legend(loc='upper left', borderaxespad=0.2, fontsize=12)
    plt.show()