knnとは機械学習アルゴリズムの一つで、ある点からk個の最も近い点を選び、その中で最も多いラベルを用いて分類する方法です。このアルゴリズムはとても単純でわかりやすいので、ここでは、knnを用いて汎化性能の意味を確かめます。
Jupyter notebookを用いる
anacondaをインストールし、以下のコマンドを実行します。
$jupyter notebook
ブラウザから新しいpython 3ノートブックを起動します。
mglearnのインストール
$ pip install --user mglearn
必要なライブラリのインポート
In[1]:
%matplotlib inline import numpy as np import matplotlib.pyplot as plt import pandas as pd import mglearn from IPython.display import display
forgeデータとwaveデータをプロットする
In[2]:
X, y = mglearn.datasets.make_wave(n_samples=40) plt.plot(X, y, 'o') plt.ylim(-3, 3) plt.xlabel("Feature") plt.ylabel("Target")
In[3]:
X, y = mglearn.datasets.make_forge() mglearn.discrete_scatter(X[:, 0], X[:, 1], y) plt.legend(["Class 0", "Class 1"], loc=4) plt.xlabel("First feature") plt.ylabel("Second feature") print("X.shape:{}".format(X.shape))
このデータをknnによって分類することを試みます。
k=1,3,9における分類
In[4]:
fig, axes = plt.subplots(1, 3, figsize=(10, 3)) for n_neighbors, ax in zip([1,3,9], axes): clf = KNeighborsClassifier(n_neighbors=n_neighbors).fit(X,y) mglearn.plots.plot_2d_separator(clf, X, fill=True, eps=0.5, ax=ax, alpha=.4) mglearn.discrete_scatter(X[:, 0], X[:, 1], y, ax=ax) ax.set_title("{} neighbor(s)".format(n_neighbors)) ax.set_xlabel("feature 0") ax.set_ylabel("feature 1") axes[0].legend(loc=3)
In[5]:
from sklearn.neighbors import KNeighborsRegressor X, y = mglearn.datasets.make_wave(n_samples=40) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) fig, axes = plt.subplots(1, 3, figsize=(15, 4)) line = np.linspace(-3, 3, 1000).reshape(-1,1) for n_neighbors, ax in zip([1,3,9], axes): reg = KNeighborsRegressor(n_neighbors=n_neighbors) reg.fit(X_train, y_train) ax.plot(line, reg.predict(line)) ax.plot(X_train, y_train, '^', c=mglearn.cm2(0), markersize=8) ax.plot(X_test, y_test, 'v', c=mglearn.cm2(1), markersize=8) ax.set_title("{} neighbor(s)\n train score: {:.2f} test score: {:.2f}".format( n_neighbors, reg.score(X_train, y_train), reg.score(X_test, y_test) )) ax.set_xlabel("Feature") ax.set_ylabel("Target") axes[0].legend(["Model predictions", "Training data/target", "Test data/target"], loc="best")
kが増えるほど、分類は滑らかな線に近づいていくことがわかります。一般的に、k=9のようなシンプルなモデルほど汎化性能が高いと言われます。
kごとの訓練データとテストデータのscoreをプロットする
前述の例ではforgeデータを用いましたが、このscoreを求める例では、load_breast_cancerデータを使用します。
In[4]:
from sklearn.datasets import load_breast_cancer cancer = load_breast_cancer() X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, stratify=cancer.target, random_state=66) neighbors_settings = range(1, 11) training_accuracy = [] test_accuracy = [] for n_neighbors in neighbors_settings: clf = KNeighborsClassifier(n_neighbors=n_neighbors) clf.fit(X_train, y_train) training_accuracy.append(clf.score(X_train, y_train)) test_accuracy.append(clf.score(X_test, y_test)) plt.plot(neighbors_settings, training_accuracy, label="training accuracy") plt.plot(neighbors_settings, test_accuracy, label="test accuracy") plt.ylabel("Accuracy") plt.xlabel("n_neighbors") plt.legend()
このグラフを見ればわかるように、k=6あたりでテストに対する予測性能が最大となっています。一方で、k=1のときには、訓練データに対して高い精度を持っていることがわかります。
考察
訓練データに対して高い性能を持つような学習は、一般的にオーバーフィッティングの可能性が高いと言えます。言い換えれば、訓練データ以外のデータに対する予測性能が落ちるということです。
knnの場合、k=1はオーバーフィッティングしやすくなり、適切なkを選択することによって汎化性能が高まると言えます。