In [2]:
from scipy.spatial import Voronoi, voronoi_plot_2d
from sklearn.neighbors import KNeighborsClassifier
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
%matplotlib inline
plt.style.use('fivethirtyeight')
np.random.seed(1337)
Sebagai awal, kita akan melihat bagaimana batas keputusan dapat dibentuk dengan algoritma Nearest Neighbour. Dengan algoritma ini, Anda dapat menggambarkan Voronoi tessellation.
In [4]:
x1 = 3.5 + np.random.randn(10)
x2 = 6.5 + np.random.randn(10)
y1 = 5 + np.random.randn(10)
y2 = 5 + np.random.randn(10)
x = np.append(x1, x2)
y = np.append(y1, y2)
vor = Voronoi(np.array([x, y]).T)
voronoi_plot_2d(vor, show_points=False, show_vertices=False)
plt.scatter(x1, y1)
plt.scatter(x2, y2)
plt.grid(False)
plt.show()
Kita mencoba mengenalkan data baru di titik (6, 5), tetangga terdekatnya adalah titik dalam region yang sama. Dengan kata lain, Voronoi tessellation ini membentuk batas keputusan (decision boundary) dari algoritma Nearest Neighbour.
In [5]:
voronoi_plot_2d(vor, show_points=False, show_vertices=False)
plt.scatter(x1, y1)
plt.scatter(x2, y2)
plt.scatter([6], [5], marker='X')
plt.grid(False)
plt.show()
Untuk dataset yang lebih "riil", kita akan melihat bagaimana atribut dari Pokemon dapat mempengaruhi status legendarisnya. Dataset ini diambil dari https://www.kaggle.com/abcsds/pokemon.
In [3]:
df = pd.read_csv('../datasets/pokemon.csv')
df.head()
Out[3]:
Dari dataset ini, kita akan mencoba untuk menjalankan algoritma k-NN untuk nilai $k=[1,7]$ pada data latih. Berapa menurut Anda nilai $k$ yang terbaik yang akan didapatkan?
In [6]:
from sklearn.metrics import accuracy_score
X = df[['HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed']]
y = df['Legendary']
acc = []
for k in range(1,8):
knn = KNeighborsClassifier(k)
knn.fit(X, y)
y_pred = knn.predict(X)
acc.append(accuracy_score(y, y_pred))
plt.plot(range(1,8), acc)
plt.xlabel('k')
plt.ylabel('accuracy');
Ternyata, hasil dengan menggunakan 1-NN adalah yang terbaik. Mengapa? Apakah ini cara yang tepat untuk melakukan klasifikasi?
In [34]:
plt.scatter(df['Attack'], df['Defense'], marker='.')
plt.xlabel('Attack')
plt.ylabel('Defense');
Nilai Defense
untuk dari nilai Attack
suatu Pokemon dapat diestimasi dengan
$$
\hat{y} = \frac{1}{k} \sum_{j=1}^{k} y_{ij}
$$
Misalnya, diberikan satu data baru yang akan kita estimasi seperti di bawah ini.
In [8]:
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
X_train, X_test, y_train, y_test = train_test_split(df[['Attack']], df['Defense'], test_size=0.3, random_state=42)
In [39]:
plt.scatter(X_train, y_train, marker='.')
plt.scatter(X_test[:1], y_test[:1], marker='D', color='g')
plt.xlabel('Attack')
plt.ylabel('Defense');
Dari beberapa nilai $k$, ternyata 1-NN menghasilkan regresi yang paling dekat dengan nilai sebenarnya yang digambarkan dengan tanda wajik hijau.
In [38]:
from sklearn.neighbors import KNeighborsRegressor
fig, ax = plt.subplots(figsize=(7,7))
mse = []
ax.scatter(X_train, y_train, marker='.', alpha='1.', label='train')
for k in range(1,4):
knr = KNeighborsRegressor(k)
knr.fit(X_train, y_train)
y_pred = knr.predict(X_test[:1])
ax.scatter(X_test[:1], y_pred, label='%d-NN' % k, marker='X')
ax.scatter(X_test[:1], y_test[:1], label='true', marker='D', color='g')
ax.set_ylim([65,95])
plt.legend()
plt.xlabel('Attack')
plt.ylabel('Defense');
Akan tetapi, saat dievaluasi dengan menggunakan semua data uji, ternyata mean squared error (MSE) terendah justru didapatkan dengan 10-NN. Mengapa hasilnya bisa berbeda?
In [42]:
from sklearn.neighbors import KNeighborsRegressor
mse = []
for k in range(1,11):
knr = KNeighborsRegressor(k)
knr.fit(X_train, y_train)
y_pred = knr.predict(X_test)
mse.append(mean_squared_error(y_test, y_pred))
plt.plot(range(1,11), mse);
Lalu, jika diberikan Pokemon baru dengan nilai Attack
250, kira-kira berapa nilai Defense
yang akan dihasilkan dari algoritma 1-NN?
Berbeda dengan k-Nearest Neighbours, k-Means merupakan algoritma untuk unsupervised learning, dalam hal ini adalah untuk tugas clustering. Dalam clustering, kita berusaha melihat subpopulasi dalam data dengan melihat kesamaan dari elemen di tiap subpopulasi tersebut.
Idenya adalah dengan menggunakan centroids atau titik-titik pusat yang mendefinisikan klaster-klaster yang ada. Tiap centroid merupakan representasi dari nilai rata-rata dari objek yang masuk ke dalam klaster tersebut.
In [79]:
# Kode dari http://nbviewer.jupyter.org/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.11-K-Means.ipynb
from sklearn.datasets.samples_generator import make_blobs
from sklearn.cluster import KMeans
X, y_true = make_blobs(n_samples=300, centers=4,
cluster_std=0.60, random_state=0)
fig, ax = plt.subplots(figsize=(7,7))
ax.scatter(X[:,0], X[:,1])
plt.show()
Dalam gambar di bawah, setiap warna mewakili satu klaster. Tanda silang di gambar tersebut menunjukkan letak centroids-nya.
In [10]:
fig, ax = plt.subplots(2, 2, figsize=(14,14))
for n in range(4):
kmeans = KMeans(n_clusters=n+1)
kmeans.fit(X)
for label in np.unique(kmeans.labels_):
ax[n / 2][n % 2].scatter(X[np.where(kmeans.labels_ == label),0], X[np.where(kmeans.labels_ == label),1], alpha=.5)
centers = kmeans.cluster_centers_
ax[n / 2][n % 2].scatter(centers[:,0], centers[:,1], marker='X')
plt.show()
Pertanyaannya adalah bagaimana cara menentukan nilai $k$?
Ada beberapa cara:
In [11]:
X, y_true = make_blobs(n_samples=300, centers=4,
cluster_std=0.60, random_state=0)
v = []
for i in range(1,11):
kmeans = KMeans(n_clusters=i)
kmeans.fit(X)
v.append(kmeans.inertia_)
plt.plot(range(1,11), v, 'o-')
plt.xticks(range(1,11), range(1,11))
plt.xlabel('# clusters')
plt.ylabel('aggregate distances')
plt.show()
Jika dilihat menggunakan scree plot, perubahan kecuraman yang paling drastis ada pada jumlah klaster 4, yang sebetulnya sama seperti nilai parameter centers
dari fungsi make_blobs()
.
Dalam kasus dataset Pokemon yang telah diberikan di atas, apakah kita bisa membedakan klaster antara Pokemon legendaris dan Pokemon biasa?
In [ ]:
# Kode Anda di sini