عبارت KNN مخفف k Nearest Neighbor و به معنای نزدیکترین همسایگی میباشد. این روش در زیرمجموعه روش Supervised Learning قرار میگیرد و در ادامه بیشتر در مورد آن صحبت خواهیم کرد.
در این مسئله میخواهیم با روش موسوم به KNN عمل Classification یا دستهبندی را انجام دهیم. مثلا در شکل زیر اگر بخواهیم دستهبندی انجام دهیم، ممکن است از روش KNN استفاده کنیم.

در این روش پارامتر k اهمیت زیادی در تعیین دسته دارد. برای مثال در شکل زیر اگر k را 3 در نظر بگیریم، نقطه قرمز در دسته بنفشها قرار میگیرد. اما اگر k را 6 در نظر بگیریم، در دسته زردها قرار خواهد گرفت. در واقع باید K ای را انتخاب کنیم که کمترین خطا را داشته باشد.
- در حالت k=3 به سه مورد از نزدیک ترین ها نگاه میکنیم.
- در حالت k=6 به شش مورد از نزدیک ترین ها نگاه میکنیم.

فرض کنیم که میخواهیم تعیین کنیم که یک نقطه جدید در کدام دسته قرار میگیرد. اولین کار این است که فاصله این نقطه را از تمامی نقاط اطراف آن بدست بیاوریم:
سپس آن فواصل را از کوچک به بزرگ sort میکنیم.
مزایای روش Knn :
1- سادگی
2- در برگرفتن چندین دسته
معایب knn:
1- برای دیتاستهای خیلی بزرگ مناسب نیست.
2- برای دیتا ستهای با پارامتر زیاد ، مناسب نیست.
3- در مواردی که پارامترها غیرعددی هستند، کاربرد ندارد.
حل مسئله:
ابتدا کتابخانههای مورد نظر را ایمپورت میکنیم:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
سپس فایل مورد نظر را فراخوانی میکنیم:
df = pd.read_csv('KNN_Project_Data.txt')
df.head()
مشاهده میکنیم که ستونهای دیتاست ما معنای مشخصی ندارند. و ستون هدف ما ستون Target_class است که مقادیر آن صفر یا یک هستند.
مسئله این است: با داشتن دیتای جدید باید پیشبینی بکنیم که آن دیتا در کدام دسته (صفر یا یک) قرار میگیرند.
مرحله Preprocessing :
اولین مرحله preprocessing است. کتابخانه scikit_learn یک زیرمجموعه پیشپردازش کننده به نام standardScaler دارد.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()# we give it a name
سپس دیتایی که در اختیار داریم را با این کتابخانه fit میکنیم. دیتای ما همهس ستونها به جز ستون target است.
scaler.fit(df.drop('TARGET CLASS',axis=1))
بعد از fit کردن یک مرحله دیگر به نام transform نیز نیاز داریم:
scaled_features = scaler.transform(df.drop('TARGET CLASS',axis=1))
اگر scaled_features را مشاهده کنیم، میبینیم که از نوع array است. اما ما در اینجا داده از نوع array نمیخواهیم. بنابراین با دستورات زیر آن را به دیتافریم تبدیل میکنیم و نام ستونها را مطابق با نام ستونهای df انتخاب میکنیم به جز مورد آخر (target).
df_feat = pd.DataFrame(scaled_features,columns=df.columns[:-1])
حال مجددا پنج سطر اول را میبینیم:
df_feat.head()
مشاهده میکنیم که دیتای ما استاندارد شده است. یعنی همه در یک رنج هم اندازه قرار دارند و قابل مقایسه با یکدیگر هستند. همه آنها متوسط صفر دارند و انحراف از معیارشان برابر با یک است:

حال شروع به ساخت مدل مناسب برای آموزش سیستم خود میکنیم:
ابتدا دیتاهای موجود را با نسبت 0.7 آموزش و 0.3 تست، تقسیم میکنیم:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = \
train_test_split(scaled_features,\
df['TARGET CLASS'],test_size=0.30)
سپس زیرمجموعه KNeighborsClassifier از کتابخانه سایکیتلرن را فراخوانی میکنیم:
from sklearn.neighbors import KNeighborsClassifier
با هدف سادگی و کوتاه شدن نام، عبارت knn را با تعداد همسایگی یک را تعریف میکنیم:
knn = KNeighborsClassifier(n_neighbors=1)
سپس مدل را با دستور زیر train میکنیم:
knn.fit(X_train,y_train)
مرحله بعد از train مربوط به پیشبینی است:
pred = knn.predict(X_test)
حال برای ارزیابی میزان دقت مدل خود همانند مسئله لاجستیک رگرسیون از confusion Matrix استفاده میکنیم:
from sklearn.metrics import classification_report,confusion_matrix
--> print(confusion_matrix(y_test,pred))

--> print(classification_report(y_test,pred))
آنچه تا اینجا محاسبه کردیم مربوط به تعداد همسایگی k=1 بود. حال میخواهیم این تعداد همسایگی را تغییر دهیم و ببینیم مقدار خطا به چه میزان تغییر خواهد کرد. به روشی که در ادامه آن را اجرا خواهیم کرد، روش elbow method گویند. آنچه در این روش اتفاق می فاتد این است که روش knn را به ازای k های مختلف اجرا میکنیم و در به دنبال این هستیم که ببینیم در کدام k مقدار error_rate کمترین مقدار خواهد بود.
error_rate = []
for i in range(1,40):
knn = KNeighborsClassifier(n_neighbors=i)
knn.fit(X_train,y_train)
pred_i = knn.predict(X_test)
error_rate.append(np.mean(pred_i != y_test))
در دستور بالا در بخش (error_rate.append(np.mean(pred_i != y_test))) تنها آن مقادیری به error_rate اضافه میشود که مقدار پیشبینی شده با مقدار واقعی یعنی متناظر آن در y_test برابر نباشد.
سپس با دستورات زیر نمودار erro_rate را بر حسب k ترسیم میکنیم(x ما همان رنج 1 تا 40 و y ما همان error_rate است):
#Now create the following plot using the information from your for loop.
plt.figure(figsize=(10,6))# determine the size of picture
plt.plot(range(1,40),error_rate,color='blue', linestyle='dashed',\
marker='o',markerfacecolor='red', markersize=10)
plt.title('Error Rate vs. K Value')
plt.xlabel('K')
plt.ylabel('Error Rate')

حال به نمودار بالا نگاه میکنیم و تصمیم میگیریم کدام مقدارk را برای مدل خود به کار ببریم. برای مثال k=13 به دلیل پایینتر بودن مقدار خطای آن میتواند یک گزینه خوب باشد.
مقدار k را انتخاب میکنیم و با استفاده از دستورات زیر مقدار خطای آن را با استفاده از confusion matrix ارزیابی میکنیم:
# NOW WITH K=13
knn = KNeighborsClassifier(n_neighbors=13)
knn.fit(X_train,y_train)
pred = knn.predict(X_test)
print('WITH K=13')
print('\n')
print('Confusion_Matrix:')
print(confusion_matrix(y_test,pred))
print('\n')
print('Confusion_report:')
print(classification_report(y_test,pred))
منابع :
1-ویدئو از کانال یوتوب خانم مونا حاتمی(لینک)