KNN با پایتون

پیاده سازی KNN با پایتون: پیش‌بینی سرطان

این آموزش یکی از پست‌های زیرمجموعه آموزش‌های مرتبط با پایتون می‌باشد، که به آموزش پیاده سازی الگوریتم KNN با پایتون می‌پردازد. در صورتی که تمایل دارید کل آموزش‌های مرتبط با پایتون را مشاهده کنید، از این لینک استفاده کنید. در ضمن اگه حوصله خوندن متن ندارید، می‌تونید از ویدیو آخر این پست استفاده کنید و از متن صرف نظر کنیدهمچنین اگه با خوندن این مطلب سوالی برای شما پیش اومد، خوشحال میشم که اون را از طریق راه‌های ارتباطی که در همین وبسایت موجوده، باهام مطرح کنید.

پیشنیاز این آموزش تسلط نسبی بر پایتون است.

KNN چیه؟

KNN یا k-nearest Neighbors یکی از الگوریتم‌های ساده و در عین حال خیلی کاربردی یادگیری ماشین (Machine Learning) هست که در ادامه به ابعاد مختلف این الگوریتم مثل فلسفه، فرمول‌های ریاضی و پیاده‌سازی اون در پایتون می‌پردازیم. مثالی که در این آموزش قراره پیاده‌اش کنم مربوط به دیتای حدود 600 نفر بیمار مشکوک به سرطان سینه هست که قصد دارم با استفاده از این دیتا و KNN، برای یک بیمار جدید پیش‌بینی کنم که آیا سرطان سینه دارد یا خیر؟ البته دیتا حاوی اطلاعات واقعی افراد نیست و صرفا جنبه آموزش دارد.

فلسفه KNN اینه که بعضی از چیزها را میشه از همسایه‌های دیوار به دیوار شما (نزدیک‌ترین همسایه‌های شما) فهمید، مثلا اگه نزدیک‌ترین همسایه‌های شما از یک سوپر مارکت خرید می‌کنند، احتمالا شما هم از همون سوپر مارکت خرید می‌کنید یا اگه بچه‌های اونا به یک مدرسه خاص میرن، احتمالا بچه شما هم به همون مدرسه میره. البته KNN اینا هم میگه که بعضی از چیزها را نمیشه از نزدیک‌ترین همسایه‌ها فهمید، مثل رنگ مورد علاقه شما. قطعا واضحه که اینجا منظور از همسایه، همسایه فیزیکی نیست و منظور هر فردی هست که در چندین عامل، مشابه شماست. مثال زیر را در نظر بگیرید:

فرض کنید قراره مهاجرت کنید، در مهاجرت عوامل زیادی مثل درآمد، تحصیلات، سن و… در قبول یا رد ویزا نقش دارند، حالا شما دوست دارید بدونید چقدر شانس گرفتن ویزا از کشور مقصدتون را دارید؟ کافیه مثلا 10 نفر که شرایط مشابه شما را داشتند و اقدام به اخذ ویزا کردند را پیدا کنید و ببینید چند نفر از اون‌ها موفق شده‌اند. مثلا اگه 7 نفر از اون‌ها موفق به اخذ ویزا شده، این یعنی شانس شما برای گرفتن ویزا احتمالا 70 درصده. از نظر KNN این 10 نفر همون نزدیک‌ترین همسایه‌های شما هستند که وضعیت مهاجرت شما را میشه با در نظرگیری وضعیت مهاجرت اون‌ها حدس زد. همچنین عواملی مثل درآمد، تحصیلات، سن و… که در رد یا قبول ویزا تأثیر دارند، متغیرهای مستقل (Independent Variable یا Inputs) ما هستند و شانس اخذ ویزا هم متغیر وابسته (Dependent Variable یا Output) ما هست. اصولا متغیر وابسته تحت تاثیر متغیرهای مستقل هست.

KNN هم برای مسائل گسسته بکار میره و هم برای پیوسته. مثلا فرض کنید طول و عرض 1000 تا لاکپشت را اندازه گرفتیم و بعد هم که اون لاکپشت‌ها مُردن، عمرشون را کنار طول و عرضشون نوشتیم. حالا یک لاکپشت جدید داریم که قصد داریم عمرش را با توجه به طول و عرضش پیش‌بینی کنیم (لطفا گیج نشید، طول و عرض لاکپشت‌ها شاید با عمر اون‌ها ارتباطی نداشته باشه و این کاملا یک مثال برای فهم بهتر انواع مسائل هست)، طبق KNN میریم از داخل دیتا خودمون چندتا لاکپشت که نزدیک‌ترین طول و عرض به این لاکپشت را دارند، پیدا می‌کنیم و از عمر اون‌ها میانگین می‌گیریم و این عدد میشه پیش‌بینی عمر لاکپشت جدید ما. به این مسأله پیوسته یا Regression میگن چون طول عمر لاکپشت هر عددی می‌تونه باشه، مثلا می‌تونه 7 سال باشه یا 8 سال یا 9.5 سال و خلاصه هر عددی. حالا فرض کنید قراره نتیجه بازی منچستر یونایتد و اورتون را پیش‌بینی کنید، جواب پیش‌بینی سه حالت بیشتر نداره: برد منچستر، باخت منچستر و مساوی. در نتیجه در اینجا برخلاف مثال عمر لاکپشت با بینهایت جواب مواجه نیستیم و جواب الگوریتم کلا سه حالت بیشتر نداره. به این نوع مسائل گسسته یا Classification میگن.

KNN برای حل مسائل Regression از مقدار نزدیک‌ترین همسایه‌ها میانگین می‌گیره و این میانگین را به عنوان جواب آخر به شما برمی‌گردونه، ولی برای حل مسائل Classification، نظر اکثریت همسایه‌های دیوار به دیوار را به عنوان جواب مدل در نظر می‌گیره. 

ملاک فاصله در KNN چیه؟

در رابطه با اینکه نزدیک‌ترین همسایه‌ها در این الگوریتم نقش خیلی مهمی دارند، صحبت کردم ولی ملاک این نزدیکی چیه؟ برگردیم به مثال لاکپشت، زوج مرتب طول و عرض هر لاکپشت را می‌توان یک نقطه روی محور مختصات در نظر بگیریم، یعنی محور مختصاتی را تصور کنید که محور x طول لاکپشت را نشون بده و محور y عرض اون را:

ملاک فاصله در KNN

هر لاکپشت جدیدی که قرار طول عمر اون را پیش‌بینی کنیم یک نقطه روی این محور مختصات داره (در حوزه هوش مصنوعی به هر نمونه Observation میگیم، یعنی یک لاکپشت جدید میشه یک Observation جدید):

ملاک فاصله در KNN

اون 1000 تا لاکپشت فوت شده هم 1000 تا نقطه روی این محورها دارند:

ملاک فاصله در KNN

حالا من 6 تا از نزدیک‌ترین همسایه‌ها به لاکپشت جدید را انتخاب می‌کنم و از عمر اونا میانگین می‌گیرم تا تخمین عمر لاکپشت جدید بدست بیاد (در KNN این عدد 6 را با حرف k نمایش میدن):

ملاک فاصله در KNN

رسیدیم به سوال اصلی این قسمت، چطور فهمیدم این 6 نقطه نزدیک‌ترین نقطه‌ها به نقطه جدید هست؟ کاری که کردم این بود که با فرمول زیر فاصله نقطه جدید را با 1000 نقطه قبلی حساب کردم، این فاصله‌ها را از کوچک به بزرگ مرتب کردم و 6 تای اول را انتخاب کردم:

ملاک فاصله در KNN

شاید سوال براتون پیش بیاد که اگه متغیرهای مستقل ما بیشتر از 2 تا بود چی؟ اون موقع فضا مسأله از حالت دو بعدی درمیاد ولی اصلا مهم نیست، چون حتی اگه 1000 بعد هم داشتیم، آخرش فاصله دو تا نقطه یک عدده. مثلا برای سه بعد کافیه توان دو اختلاف xها، yها و zهای دو نقطه را بدست بیارید، با هم جمع کنید و از حاصلشون جذر بگیرید (عین فرمول بالا با این تفاوت که z هم اضافه میشه).

KNN با پایتون

خب بریم سراغ پیاده‌سازی KNN با پایتون. KNN الگوریتم ساده‌ای هست و شما می‌تونید بدون کتابخونه خاصی هم اون را پیاده کنید ولی من اینجا از Sklearn به عنوان کتابخونه اصلی استفاده می‌کنم.

کدهای استفاده شده در این آموزش را هم از اینجا می‌تونید کپی کنید و هم از گیت‌هاب من با این لینک.

مرحله اول: دیتا

دیتا مهم‌ترین بخش از پیاده‌سازی یک مدل هوش مصنوعی هست، دیتایی که قراره باهاش یک مدل ایجاد کنید باید مرتبط با مسئله باشه، کافی باشه و تمیز باشه. هر کدوم از این سه عامل اگه درست رعایت نشده باشه، دقت مدل کمتر میشه. در این آموزش من قصد ندارم وارد بحث نحوه جمع‌آوری دیتا و تمیز کردن اون بشم ولی مقاله تمیز کردن دیتا در اکسل می‌تونه دید خوبی در رابطه با دیتای تمیز به شما بده، همچنین مقاله وب اسکرپینگ هم یکی از راه‌های جمع‌آوری دیتا را به شما آموزش میده.

بجز دیتا، انتخاب نوع الگوریتم هم مهمه، منظورم اینه که شاید برای یک دیتا KNN خوب عمل کنه و برای دیتای دیگه‌ای نه! بعضی وقتا بعد از پیاده کردن مدل متوجه میشید که الگوریتمی که انتخاب کردید برای دیتای خودتون مناسب نیست و باید برید سراغ یک الگوریتم دیگه.

من یک دیتا دانلود کردم که برای شما هم در این لینک قرار میدم تا دانلود کنید. دیتا همونطور که توضیح دادم در رابطه با مشخصات غده موجود در سینه حدود 600 نفر مشکوک به سرطان سینه هست. تصویر زیر چند ردیف از این دیتا را نشون میده:

دیتا مورد استفاده برای پیاده سازی KNN با پایتون

ستون اول کد هر بیمار را نشون میده که ستون مهمی برای من نیست و جلوتر در مدل حذفش می‌کنم. ستون دوم، یعنی ستون diagnosis متغیر وابسته من هست که اگه برابر M باشه، یعنی اون غده بدخیمه و اگه برابر B باشه، یعنی غده خوش‌خیمه، از همین جا میشه فهمید که مسأله من از نوع Classification هست. بقیه ستون‌ها هم هر کدوم یک متغیر مستقل را در بر داره، مثلا ستون سوم متوسط شعاع غده را نشون میده.

نکته خیلی مهمی که وجود داره اینه که اگه واحد اندازه‌گیری ستون‌های دیتا شما متفاوته، در الگوریتم‌های بر مبنای فاصله مثل همین KNN، قبل از اینکه از دیتا استفاده کنید باید حتما دیتا را نرمال کنید. به بیان دیگه فرض کنید یک ستون از دیتای شما قد افراد برحسب سانتی‌متر را نشون میده و یک ستون هم وزن اون‌ها را برحسب کیلوگرم، در نتیجه شما نمی‌تونید روی دو ستون دیتا با واحد اندازه‌گیری متفاوت، عملیات محاسبه فاصله را انجام دهید.

مرحله دوم: وارد کردن دیتا

من از VS Code به عنوان محیط برنامه نویسی استفاده می‌کنم، البته روی VS Code خودم پلاگین Jupyter را نصب کردم تا محیطی شبیه Jupyter Notebook بدست بیارم ولی شما می‌تونید از خود Jupyter Notebook یا هر محیط دیگه‌ای استفاده کنید. دوباره یادآوری می‌کنم که اگه حوصله خوندن متن ندارید، می‌تونید از ویدیو آخر این پست استفاده کنید و متن را صرف نظر کنید.

با استفاده از کد پایین،  از دیتا در کد خودم استفاده می‌کنم:

				
					import pandas as pd
data = pd.read_csv("Cancer_Data.csv")
print(data.shape)
data.head()
				
			

در کد بالا، فایل دیتا را کنار فایل پایتون خودم قرار دادم و با استفاده از تابع read_csv کتابخونه Pandas اون را خوندم. خروجی کد بالا به شکل زیر هست:

مرحله سوم: دستکاری دیتا

متغیر وابسته من اینجا از نوع کاراکتر هست، یعنی مقادیر M و B را می‌گیره ولی من برای پیاده‌سازی KNN با پایتون نیاز دارم که این ستون عددی بشه. در نتیجه با دستورات زیر، مقدار M را با 1 مقدار B را با 0 جایگزین می‌کنم:

				
					data["diagnosis"].replace('M' , '1',inplace=True)
data["diagnosis"].replace('B' , '0',inplace=True)
				
			

با کد زیر می‌تونم متوجه بشم که هر متغیر مستقل چقدر روی متغیر وابسته یا همون diagnosis تاثیر داره:

				
					correlations = data.corr()
correlations['diagnosis']
				
			

خروجی این کد به این شکله:

				
					id                         0.039769
diagnosis                  1.000000
radius_mean                0.730029
texture_mean               0.415185
perimeter_mean             0.742636
area_mean                  0.708984
smoothness_mean            0.358560
compactness_mean           0.596534
concavity_mean             0.696360
concave points_mean        0.776614
symmetry_mean              0.330499
fractal_dimension_mean    -0.012838
radius_se                  0.567134
texture_se                -0.008303
perimeter_se               0.556141
area_se                    0.548236
smoothness_se             -0.067016
compactness_se             0.292999
concavity_se               0.253730
concave points_se          0.408042
symmetry_se               -0.006522
fractal_dimension_se       0.077972
radius_worst               0.776454
texture_worst              0.456903
perimeter_worst            0.782914
area_worst                 0.733825
smoothness_worst           0.421465
compactness_worst          0.590998
concavity_worst            0.659610
concave points_worst       0.793566
symmetry_worst             0.416294
fractal_dimension_worst    0.323872
Unnamed: 32                     NaN
Name: diagnosis, dtype: float64
				
			

تفسیر خروجی اینجوریه که مثلا متغیر radius_mean تأثیر خیلی بالایی روی متغیر وابسته داره (چون عدد مقابلش بیشتره) ولی مثلا متغیرهای texture_se و symmetry_se تاثیر کمی روی مدل دارند و میشه اون‌ها را حذف کرد (هر چند من اینجا اینکار را نکردم). در کد بعدی ستون‌های id و Unnamed: 32 که هیچ تأثیری روی مدل ندارند را حذف کردم:

				
					data.drop(columns=['Unnamed: 32' ,'id'] , inplace=True)
				
			

مرحله چهارم: بخش‌بندی کردن دیتا

کد زیر را به کدهای خودم اضافه می‌کنم:

				
					from sklearn.model_selection import train_test_split

y = data.values[: , 0]
x = data.values[: , 1:28]
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=12345)
				
			

این کد اول دیتا را به دو بخش x و y تقسیم می‌کنه. x شامل ستون‌های 1 تا 28 دیتا است که در حقیقت متغیرهای مستقل را شامل میشه و y شامل ستون 1 هست که متغیر وابسته یعنی ستون diagnosis را شامل میشه.

در خط 5 کد، با استفاده از تابع train_test_split از sklearn متغیرهای x و y را به 4 بخش تقسیم کردم. در ایجاد مدل‌های هوش مصنوعی ما معمولا یک قسمت از دیتا را جدا می‌کنیم و با اون مدل را ایجاد می‌کنیم (اصطلاحا مدل را Train یا Fit می‌کنیم) و با قسمت باقیمانده از دیتا، مدلی که ایجاد کردیم را تست می‌کنیم و دقتش را اندازه می‌گیریم. اینجا هم متغیرهای x_train و y_train برای ایجاد مدل قراره بکار بره و متغیرهای x_test و y_test برای ارزیابی مدل بکار میره.

پارامتر test_size مشخص می‌کنه که چقدر از دیتا را برای تست لازم داریم، من این پارامتر را روی 0.3 قرار دادم، یعنی 30 درصد دیتا را برای تست جدا کن. پارامتر random_state را برابر یک عدد دلخواه قرار میدم که هر بار که کد را اجرا می‌کنم، مقادیر متغیرهای x_test، x_train و بقیه متغیرهای حاصل از این تابع، عوض نشه.

مرحله پنجم: ایجاد مدل

با کد زیر مدل KNN خودم را ایجاد می‌کنم:

				
					from sklearn.neighbors import KNeighborsClassifier

model = KNeighborsClassifier(n_neighbors=5)
model.fit(x_train, y_train)
				
			

در این کد با استفاده از کلاس KNeighborsClassifier یک متغیر به نام model ایجاد کردم. این متغیر در حقیقت شی اولیه KNN من هست. در خط بعدی هم مدل را با استفاده از متد fit و دیتاهایی که برای آموزش مدل کنار گذاشته بودم، train کردم.

چیزی که اینجا باید بگم اینه که اگه خط سوم کد را ببینید، یک پارامتر به نام n_neighbors داریم. در این پارامتر من تعداد نزدیک‌ترین همسایه که باید در نتیجه‌گیری لحاظ بشن را روی 5 تنظیم کردم (k=5).

نکته مهم اینه که اگه مسئله شما از نوع Regression بود، به جای کلاس KNeighborsClassifier از کلاس KNeighborsRegressor استفاده کنید. البته یادتون نره قبلش اون را با دستور زیر به کد اضافه کنید:

				
					from sklearn.neighbors import KNeighborsRegressor
				
			

مرحله پنجم: پیش‌بینی

الان کافیه تکه کدی بنویسم که وقتی بیمار جدیدی به من مراجعه می‌کنه، کد متغیرهای مستقل اون فرد را بگیره و پیش‌بینی کنه که آیا غده این فرد خوش‌خیمه یا بدخیم. این پیش‌بینی با استفاده تابع predict شی model انجام میشه:

				
					import numpy as np
sample = np.array([13.73,22.61,93.6,578.3,0.1131,0.2293,
0.2128,0.08025,0.2069,0.07682,0.2121,1.169,2.061,19.21,0.006429,
0.05936,0.05501,0.01628,0.01961,0.008093,15.03,32.01,108.8,697.7,0.1651,0.7725,0.6943])
y_pred = model.predict(sample.reshape(1, -1))
print(y_pred)
				
			

خروجی کد بالا به صورت زیر هست:

				
					['0']
				
			

تفسیر این عدد اینه که طبق پیش‌بینی هوش مصنوعی غده این بیمار خوشبختانه خوش‌خیمه ولی سوال اساسی اینه که دقت این مدل چقدره؟

مرحله ششم: سنجش دقت مدل

تابع زیر دقت مدل را با استفاده از متد accuracy_score که از متدهای sklearn هست، اندازه می‌گیره:

				
					from sklearn.metrics import accuracy_score

def calAccuracy(y_test , y_pred):
    accuracy = accuracy_score(y_test, y_pred) * 100
    return accuracy
				
			

چیزی که شاید لازم باشه بگم اینه که من باید به عنوان پارامتر y_test و y_pred را به تابع calAccuracy بدم، حالا y_pred چیه؟ این متغیر، حامل مقادیر پیش‌بینی شده حاصل از پیش‌بینی x_test هست. به زبان ساده من مقادیر واقعی و مقادیر پیش‌بینی شده را به این تابع میدم تا دقت KNN را برام حساب کنه. دقیقا برای همین بود که اون اول کار، دیتا خودم را به چند بخش تقسیم کردم.

در کد پایین، x_test که دیتا مربوط به تست مدل بود را به تابع predict دادم تا برام مقادیر پیش‌بینی را بدست بیاره و بریزه داخل یک متغیر به نام y_pred. بعدش مقادیر واقعی خودم یعنی y_test و مقادیر پیش‌بینی را به تابع calAccuracy دادم تا دقت KNN را بدست بیاره:

				
					y_pred = model.predict(x_test)
accuracy = calAccuracy(y_test , y_pred)
print(accuracy)
				
			

خروجی این کد به صورت زیر است:

				
					92.98245614035088
				
			

الان من می‌دونم که دقت پیش‌بینی انجام شده حدود 93 درصد هست، پس با اطمینان خاطر بیشتری برای قدم‌های بعدی درمان این بیمار تصمیم‌گیری می‌کنم.

بهبود دقت مدل

برای بهبود مدل KNN میشه چندتا کار کرد، یکیش اینه که مقدار پارامتر n_neighbors را عوض کنید تا ببینید دقیقا انتخاب چند همسایه دقت بیشتری برای شما فراهم می‌کنه. البته کتابخونه‌هایی هستند که مقدار بهینه k را برای شما مشخص کنند، می‌تونید درباره اونا سرچ کنید.

قدم بعدی می‌تونه این باشه که از پارامتر weights کلاس KNeighborsClassifier استفاده کنید، این پارامتر به شما کمک می‌کنه که به جای میانگین گیری عادی از همسایه‌ها، از میانگین وزن‌دار استفاده کنید، اینطوری میشه به همسایه‌های نزدیک وزن بیشتری نسبت به همسایه‌های دورتر داد.

برای افزایش دقت مدل، می‌تونید از تکنیک‌های یادگیری گروهی هم استفاده کنید که قطعا بعدا براش یک پست می‌نویسم.

البته توجه کنید که همونطور که گفتم، دیتا مهم‌ترین بخش پیاده‌سازی یک مدل هوش مصنوعی هست، در نتیجه بهبود دیتا همیشه گزینه اول بهبود هر مدله.

مقاله‌های مرتبط

در صورتی که به این موضوع و کلا یادگیری الگوریتم‌های هوش مصنوعی علاقه دارید، می‌تونید از آموزش‌های پیاده‌سازی درخت تصمیم با پایتون و جنگل تصادفی من هم استفاده کنید. قطعا این مقالات و دانش مرتبط با هوش مصنوعی خیلی در عصر حاضر واجبه!

ویدیو آموزش پیاده‌سازی KNN با پایتون

قبل اینکه ویدیو را ببینید، بگم که من ویدیو‌ها را در چنل یوتوب خودم هم آپلود می‌کنم، خیلی خوشحال میشم که اونجا هم من را دنبال کنید.

نمایش ویدیو درباره KNN با پایتون