خوشه بندی K-Means در پایتون — راهنمای کاربردی

۱۲۶۲ بازدید
آخرین به‌روزرسانی: ۲۸ خرداد ۱۴۰۱
زمان مطالعه: ۱۵ دقیقه
خوشه بندی K-Means در پایتون — راهنمای کاربردی

در این مطلب، الگوریتم خوشه‌ بندی K-Means به طور کامل و به زبان ساده آموزش داده شده است. همچنین، نحوه پیاده‌سازی خوشه بندی K-Means در «زبان برنامه‌نویسی پایتون» (Python Programming Language) با بهره‌گیری از کتابخانه Scikit-Learn و مثالی برای درک بهتر موضوع مورد بررسی قرار گرفته است.

در «یادگیری ماشین» (Machine Learning)، انواع یادگیری در سه دسته «یادگیری نظارت شده» (Supervised Learning)، «یادگیری نظارت نشده» (Unsupervised Learning) و «یادگیری تقویتی» (Reinforcement Learning) قرار می‌گیرند. الگوریتم‌هایی که متعلق به دسته الگوریتم‌های نظارت نشده هستند، هیچ متغیر پیش‌بینی وابسته به داده‌هایی را در ورودی‌ها دریافت نمی‌کنند. در واقع، داده‌ها فقط دارای ورودی‌های داده به صورت نمونه‌هایی برای چندین متغیر هستند. این متغیرها، نمونه داده‌ها را توصیف می‌کنند. در چنین نوع داده‌هایی که «داده‌های فاقد برچسب» (unlabeled data) نام دارند، از روش‌های «خوشه‌بندی» (Clustering) برای یادگیری ماشین و داده‌کاوی استفاده می‌شود.

خوشه‌بندی، وظیفه گروه‌بندی یک مجموعه از نمونه‌ها به صورتی است که نمونه‌های داخل یک خوشه دارای بیشترین «مشابهت» (Similarity) با یکدیگر و کمترین مشابهت با داده‌های خارج از خوشه خودشان باشند. مشابهت، سنجه‌ای است که قدرت ارتباط بین دو نمونه داده را منعکس می‌کند.

خوشه‌بندی، اساسا برای «داده‌کاوی اکتشافی» (Exploratory Data Mining) مورد استفاده قرار می‌گیرد و کاربردهای گسترده‌ای در بسیاری از زمینه‌ها شامل یادگیری ماشین، «بازشناسی الگو» (Pattern Recognition)، «تحلیل تصویر» (Image Analysis)، «بازیابی اطلاعات» (Information Retrieval)، «بیوانفورماتیک» (Bio-informatics)، «فشرده‌سازی داده‌ها» (Data Compression) و گرافیک کامپیوتری دارد.

در مطلب پیش رو، چگونگی عملکرد الگوریتم K-Means که یک روش خوشه‌بندی بسیار محبوب است شرح داده می‌شود. این الگوریتم به کاربر کمک می‌کند تا با داده‌های فاقد برچسب کار کند (مجموعه داده‌ای که فاقد برچسب کلاس است). خوشه بندی K-Means در دسته الگوریتم‌های خوشه‌بندی مبتنی بر «مرکزوار» (Centroid) قرار می‌گیرد. مرکزوار، یک نقطه داده (تخیلی یا واقعی) در مرکز خوشه است. در خوشه‌بندی مبتنی بر مرکزوار، خوشه‌ها به وسیله یک بردار یا مرکزوار مرکزی ارائه می‌شوند. این مرکزوار الزاما عضو مجموعه داده نیست. خوشه‌بندی مبتنی بر مرکزوار، یک «الگوریتم تکرار شونده» (Iterative Algorithm) است که مشابهت در آن بر اساس نزدیک بودن نقطه داده به به مرکزوار خوشه تعیین می‌شود. موضوعاتی که در این مطلب مورد بررسی قرار می‌گیرند، عبارتند از:

  • چگونگی عملکرد الگوریتم خوشه‌بندی K-Means
  • یک بررسی موردی ساده در پایتون
  • معایب K-Means

چگونگی عملکرد الگوریتم خوشه بندی K-Means

برای فراگیری هرچه بهتر این موضوع، ابتدا نیاز است که یک مجموعه داده (مجموعه داده آموزش)، تعریف شود.

خوشه‌بندی K-Means

مجموعه داده نمونه، حاوی هشت نمونه داده با مختصات Y ،X و Z مربوط به هر یک از نمونه داده‌ها است. وظیفه قابل انجام برای کاربر آن است که این اشیا را در دو خوشه (در اینجا است که مقدار K در الگوریتم K-Means یا همان K-میانگین تعریف می‌شود) قرار دهد. در واقع، در اینجا K = 2 داریم. بنابراین، الگوریتم به صورت زیر کار می‌کند. الگوریتم، ابتدا دو نقطه را به عنوان مرکزوار دریافت می‌کند (زیرا تعداد خوشه‌ها ۲ تعیین شده و این یعنی K = 2 که در واقع به معنای داشتن دو مرکزوار است). پس از انتخاب مرکزوارها (فرض می‌شود که C1 و C2 دو مرکزوار منتخب هستند) نقاط داده (در اینجا مختصات) بسته به فاصله‌ای که از هر یک از مراکز خوشه‌ها (مرکزوارها) دارند، به هر یک از خوشه‌ها تخصیص پیدا می‌کنند (در اینجا و برای ابتدای کار، مرکزوارها = خوشه‌ها در نظر گرفته می‌شوند). فرض می‌شود که الگوریتم (OB-2 (1,2,2 و (OB-6 (2,4,2 را به عنوان مرکزوارها و خوشه‌های ۱ و ۲ انتخاب می‌کند. برای اندازه‌گیری فاصله‌ها، تابع اندازه‌گیری، فاصله‌های زیر را مورد استفاده قرار می‌دهد که به آن «تابع اندازه‌گیری فاصله» (Distance Measurement Function) گفته می‌شود.

|d=|x2–x1|+|y2–y1|+|z2–z1

این تابع، با عنوان «فاصله منهتن» (به انگلیسی به آن هم Taxicab distance و Manhattan distance گفته می‌شود. البته، دومین عبارت رایج‌تر است) نیز شناخته می‌شود که در آن d اندازه فاصله بین دو شی (x1,y1,z1) و (x2,y2,z2) است و Y ،X و Z مختصات دو شی است که فاصله آن‌ها از یکدیگر سنجیده می‌شود. جدول زیر نشان‌گر محاسبه فواصل (با استفاده از تابع اندازه‌گیری فاصله معرفی شده در بالا) میان نقاط داده و مرکزوارها (OB-2 و OB-6) است.

خوشه بندی K-Means

اشیا بر اساس فاصله آن‌ها با مرکزوارها، خوشه‌بندی می‌شوند. بر این اساس، نمونه داده‌ای که فاصله کمتری با یک مرکزوار (برای مثال C1) نسبت به مرکزوار دیگر (برای مثال C2) دارد، در خوشه C1 قرار می‌گیرد. پس از انجام خوشه‌بندی اولیه، نمونه داده‌ها چیزی مانند آنچه در جدول زیر قرار دارد، خواهند بود.

خوشه بندی K-Means
خوشه بندی K-Means

اکنون، الگوریتم کار به روز رسانی مرکزوارهای خوشه‌ها را تا هنگامی انجام می‌دهد که دیگر امکان به روز رسانی آن‌ها وجود نداشته باشد. به روز رسانی به صورت زیر انجام می‌شود:

خوشه بندی K-Means
که در آن n برابر با تعداد اشیائی است که به خوشه خاصی تعلق دارند)

بنابراین، با دنبال کردن قاعده بالا، خوشه ۱ در حالت به روز رسانی شده به صورت ((1+2+1+1+2)/5, (2+1+1+1+1)/5,(2+2+1+2+1)/5) = (1.4,1.2,1.6) خواهد بود. پس از این، الگوریتم دوباره شروع به پیدا کردن فاصله بین نقاط داده و مرکزوارهای جدید خوشه‌ها می‌کند. بنابراین، فاصله‌های جدید به صورت زیر خواهند بود:

خوشه بندی K-Means

تخصیص نمونه داده‌های جدید با توجه به خوشه‌های به روز رسانی شده، به صورت زیر خواهد بود.

خوشه بندی K-Means

این همان مرحله‌ای است که الگوریتم دیگر مرکزوارها را به روز رسانی نمی‌کند، زیرا هیچ تغییری در شکل‌گیری خوشه‌ها رخ نمی‌دهد و شکل خوشه‌ها مانند قبل باقی می‌ماند. پس از آنکه شکل‌دهی خوشه‌ها با الگوریتم K-Means به پایان رسید، می‌توان آن را روی داده‌هایی که الگوریتم پیش تر ندیده است (و به آن‌ها داده آزمون یا تست گفته می‌شود) اعمال کرد. داده‌های تست به صورت جدول زیر هستند:

خوشه‌بندی K-Means

پس از اعمال خوشه‌بندی K-Means روی مجموعه داده بالا، خوشه‌های نهایی به صورت زیر خواهد بود:

خوشه بندی K-Means

هر الگوریتمی، مادامی که میزان کارایی آن معلوم نباشد عملا بی‌فایده است. اکنون، برای ارزیابی کارایی الگوریتم K-Means می‌توان از سنجه‌هایی که برای این کار تعبیه شده‌اند استفاده کرد. این سنجه‌ها عبارتند از:

  • شاخص رَند اصلاح شده (Adjusted rand index)‌
  • امتیاز بر مبنای اطلاعات متقابل (Mutual information based scoring)
  • اندازه V، همگنی، کامل بودن (Homogeneity ،V-measure و Completeness)

اکنون که مکانیزم داخلی الگوریتم K-Means تشریح شد، روی یک مساله جهان واقعی با استفاده از پایتون اعمال خواهد شد.

یک بررسی موردی ساده برای الگوریتم K-Means در پایتون

برای پیاده‌سازی الگوریتم K-Means، از «مجموعه داده تایتانیک» (Titanic Dataset) [+] استفاده می‌شود. پیش از ادامه این بخش، توضیحاتی در مورد داده‌ها ارائه می‌شود. غرق شدن کشتی تایتانیک، یکی از تلخ‌ترین غرق شدگی‌ها در تاریخ است. کشتی تایتانیک در ۱۵ آپریل سال ۱۹۱۲، طی اولین سفر دریاییش، پس از برخورد با یک صخره یخی غرق شد و ۱۵۰۲ نفر از ۲۲۲۴ مسافر و خدمه آن کشته شدند.

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

درباره مجموعه داده تایتانیک باید گفت که این مجموعه داده شامل چندین رکورد پیرامون مسافران تایتانیک است (همانطور که از نام مجموعه داده بر می‌آید). این مجموعه داده دارای ۱۲ «ویژگی» (Feature) است که دربردارنده اطلاعاتی پیرامون passenger_fare ،port_of_Embarkation ،passenger_class و دیگر موارد هستند. برچسب مجموعه داده survival است که بیان می‌کند وضعیت نجات یافتن (بقا) یک مسافر خاص به چه صورت بوده است. در اینجا، وظیفه نیازمند انجام، خوشه‌بندی رکوردها در دو دسته است؛ افرادی که نجات پیدا کرده‌اند و افرادی که غرق شده‌اند.

ممکن است این پرسش مطرح شود که با توجه به برچسب‌دار بودن مجموعه داده، چگونه می‌توان از آن برای خوشه‌بندی استفاده کرد. در این راستا، تنها کافی است که ستون «survivial» از مجموعه داده حذف شود و به همین راحتی مجموعه داده فاقد برچسب می‌شود. وظیفه K-Means خوشه‌بندی داده‌های مجموعه داده بر این اساس است که آیا نجات پیدا کرده‌اند یا خیر.

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

در این راهنما، کاربر برای استفاده از کدهایی که در ادامه بیان شده است، نیاز به بسته‌های پایتون «پانداس» (pandas)، «نام‌پای» (NumPy)، «سایکیت‌لِرن» (scikit-learn)، «سیبورن» (Seaborn) و «مَت‌پلات‌لیب» دارد.

1# Dependencies
2
3import pandas as pd
4import numpy as np
5from sklearn.cluster import KMeans
6from sklearn.preprocessing import LabelEncoder
7from sklearn.preprocessing import MinMaxScaler
8import seaborn as sns
9import matplotlib.pyplot as plt
10%matplotlib inline

همه وابستگی‌های مورد نیاز برای کدهایی که در ادامه راهنما مورد استفاده قرار می‌گیرند با بهره‌گیری از قطعه کد بالا «وارد» (Import) شدند. اکنون، می‌توان مجموعه داده را بارگذاری کرد.

1# Load the train and test datasets to create two DataFrames
2
3train_url = "http://s3.amazonaws.com/assets.datacamp.com/course/Kaggle/train.csv"
4train = pd.read_csv(train_url)
5test_url = "http://s3.amazonaws.com/assets.datacamp.com/course/Kaggle/test.csv"
6test = pd.read_csv(test_url)

در ادامه، یک پیش‌نمایش از داده‌هایی که قرار است با آن‌ها کار شود دریافت می‌شود. برای این کار، برخی نمونه‌ها از «دیتافریم‌های» (DataFrames) «آموزش» (train) و «آزمون» (test) پرینت می‌شوند.

1print("***** Train_Set *****")
2print(train.head())
3print("\n")
4print("***** Test_Set *****")
5print(test.head())
***** Train_Set *****
   PassengerId  Survived  Pclass  \
0            1         0       3   
1            2         1       1   
2            3         1       3   
3            4         1       1   
4            5         0       3   

                                                Name     Gender   Age  SibSp  \
0                            Braund, Mr. Owen Harris    male  22.0      1   
1  Cumings, Mrs. John Bradley (Florence Briggs Th...  female  38.0      1   
2                             Heikkinen, Miss. Laina  female  26.0      0   
3       Futrelle, Mrs. Jacques Heath (Lily May Peel)  female  35.0      1   
4                           Allen, Mr. William Henry    male  35.0      0   

   Parch            Ticket     Fare Cabin Embarked  
0      0         A/5 21171   7.2500   NaN        S  
1      0          PC 17599  71.2833   C85        C  
2      0  STON/O2. 3101282   7.9250   NaN        S  
3      0            113803  53.1000  C123        S  
4      0            373450   8.0500   NaN        S  


***** Test_Set *****
   PassengerId  Pclass                                          Name     Gender  \
0          892       3                              Kelly, Mr. James    male   
1          893       3              Wilkes, Mrs. James (Ellen Needs)  female   
2          894       2                     Myles, Mr. Thomas Francis    male   
3          895       3                              Wirz, Mr. Albert    male   
4          896       3  Hirvonen, Mrs. Alexander (Helga E Lindqvist)  female   

    Age  SibSp  Parch   Ticket     Fare Cabin Embarked  
0  34.5      0      0   330911   7.8292   NaN        Q  
1  47.0      1      0   363272   7.0000   NaN        S  
2  62.0      0      0   240276   9.6875   NaN        Q  
3  27.0      0      0   315154   8.6625   NaN        S  
4  22.0      1      1  3101298  12.2875   NaN        S

برای دریافت اطلاعات آماری پایه‌ای مربوط به دیتافریم‌های آموزش و آزمون می‌توان از متد ()describe کتابخانه پانداس استفاده کرد.

1print("***** Train_Set *****")
2print(train.describe())
3print("\n")
4print("***** Test_Set *****")
5print(test.describe())
***** Train_Set *****
       PassengerId    Survived      Pclass         Age       SibSp  \
count   891.000000  891.000000  891.000000  714.000000  891.000000   
mean    446.000000    0.383838    2.308642   29.699118    0.523008   
std     257.353842    0.486592    0.836071   14.526497    1.102743   
min       1.000000    0.000000    1.000000    0.420000    0.000000   
25%     223.500000    0.000000    2.000000   20.125000    0.000000   
50%     446.000000    0.000000    3.000000   28.000000    0.000000   
75%     668.500000    1.000000    3.000000   38.000000    1.000000   
max     891.000000    1.000000    3.000000   80.000000    8.000000   

            Parch        Fare  
count  891.000000  891.000000  
mean     0.381594   32.204208  
std      0.806057   49.693429  
min      0.000000    0.000000  
25%      0.000000    7.910400  
50%      0.000000   14.454200  
75%      0.000000   31.000000  
max      6.000000  512.329200  


***** Test_Set *****
       PassengerId      Pclass         Age       SibSp       Parch        Fare
count   418.000000  418.000000  332.000000  418.000000  418.000000  417.000000
mean   1100.500000    2.265550   30.272590    0.447368    0.392344   35.627188
std     120.810458    0.841838   14.181209    0.896760    0.981429   55.907576
min     892.000000    1.000000    0.170000    0.000000    0.000000    0.000000
25%     996.250000    1.000000   21.000000    0.000000    0.000000    7.895800
50%    1100.500000    3.000000   27.000000    0.000000    0.000000   14.454200
75%    1204.750000    3.000000   39.000000    1.000000    0.000000   31.500000
max    1309.000000    3.000000   76.000000    8.000000    9.000000  512.329200

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

1print(train.columns.values)
['PassengerId' 'Survived' 'Pclass' 'Name' 'Gender' 'Age' 'SibSp' 'Parch'
 'Ticket' 'Fare' 'Cabin' 'Embarked']

شایان ذکر است که همه الگوریتم‌های یادگیری ماشین، از این کار که داده‌هایی با «مقادیر ناموجود» (Missing Values) به آن‌ها خورانده شود پشتیبانی نمی‌کنند. K-Means یکی از این الگوریتم‌ها است. بنابراین، نیاز به مدیریت مقادیر ناموجودی که در داده‌ها وجود دارند، پیش از اعمال الگوریتم بر آن‌ها است. ابتدا باید دید که کدام قسمت‌ها دارای مقدار ناموجود هستند.

1# For the train set
2train.isna().head()

خوشه بندی K-Means

1# For the test set
2test.isna().head()

خوشه بندی K-Means

اکنون، مجموع تعداد «مقادیر ناموجود» (Missing Values) در هر دو مجموعه داده محاسبه می‌شود.

1print("*****In the train set*****")
2print(train.isna().sum())
3print("\n")
4print("*****In the test set*****")
5print(test.isna().sum())
*****In the train set*****
PassengerId      0
Survived         0
Pclass           0
Name             0
Gender              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64


*****In the test set*****
PassengerId      0
Pclass           0
Name             0
Gender           0
Age             86
SibSp            0
Parch            0
Ticket           0
Fare             1
Cabin          327
Embarked         0
dtype: int64

می‌توان مشاهده کرد که در مجموعه داده آموزش، ستون‌های Cabin ،Age و Embarked و در مجموعه تست، ستون‌های Age و Cabin حاوی مقادیر ناموجود هستند. راهکارهایی برای مدیریت مقادیر ناموجود وجود دارد که عبارتند از:

  • حذف سطرهایی با مقادیر ناموجود
  • جایگذاری مقادیر ناموجود

در اینجا از راهکار دوم استفاده می شود، زیرا اگر سطری که حاوی مقدار ناموجود است حذف شود، موجب از بین رفتن داده‌ها و این امر به نوبه خود منجر به آموزش نامناسب مدل یادگیری ماشین می‌شود. اکنون، چندین راهکار وجود دارد که می‌توان برای انجام جایگذاری از آن‌ها بهره برد:

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

هر روش جایگذاری که برای مجموعه داده آموزش استفاده شد، باید روی مجموعه داده تست نیز اعمال شود. این کار هنگامی انجام می‌شود که قصد اعمال مدل نهایی روی مجموعه داده تست وجود دارد. نکته مذکور باید هنگام انتخاب چگونگی جایگذاری مقادیر ناموجود در نظر گرفته شود. در کتابخانه Pandas، از تابع ()fillna برای جایگذاری مقادیر ناموجود با یک مقدار خاص استفاده می‌شود. در اینجا، کار جایگذاری مقادیر ناموجود با «مقدار میانگین» انجام می‌شود.

1# Fill missing values with mean column values in the train set
2train.fillna(train.mean(), inplace=True)
1# Fill missing values with mean column values in the test set
2test.fillna(test.mean(), inplace=True)

اکنون که مقادیر ناموجودی که در مجموعه داده وجود داشتند، با مقدارهای دیگری جایگذاری شدند، زمان آن رسیده که بررسی شود آیا مجموعه داده هنوز حاوی مقادیر ناموجود هست یا خیر. این کار برای مجموعه داده آموزش با استفاده از کد زیر انجام می‌شود:

1print(train.isna().sum())
PassengerId      0
Survived         0
Pclass           0
Name             0
Gender           0
Age              0
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64

وجود مقادیر ناموجود در مجموعه داده تست، با استفاده از کد زیر بررسی می‌شود:

1print(test.isna().sum())
PassengerId      0
Pclass           0
Name             0
Gender           0
Age              0
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          327
Embarked         0
dtype: int64

می‌توان مشاهده کرد که همچنان مقادیر ناموجودی در ستون‌های Cabin و Embarked وجود دارند. این امر بدین دلیل است که این مقادیر غیر عددی هستند. به منظور انجام جایگذاری، مقادیر باید به شکل عددی باشند. راه‌های برای تبدیل مقادیر غیر عددی به عددی نیز وجود دارد. در ادامه، تحلیل‌هایی به منظور کسب درک بهتری از داده‌ها انجام می‌شود. درک داده‌ها یک امر لازم و مهم برای انجام هر وظیفه یادگیری ماشینی است. کار با پیدا کردن ویژگی‌هایی که طبقه‌ای و عددی هستند انجام می‌شود.

  • طبقه‌ای: Gender ،Survived و Embarked.
  • ترتیبی: Pclass.
  • پیوسته: Fare ،Age.
  • گسسته: Parch ،SibSp.

دو ویژگی باقی مانده‌اند که در هیچ یک از دسته‌ها قرار نگرفته‌اند. چنانکه مشهود است، این ویژگی‌ها Ticket و Cabin هستند. Ticket ترکیبی از انواع داده‌های عددی و «حرفی‌عددی» (Alphanumeric) است. Cabin نیز از نوع داده حرفی‌عددی محسوب می‌شود. در ادامه، برخی از مقدارهای نمونه مشاهده می‌شوند.

1train['Ticket'].head()
0           A/5 21171
1            PC 17599
2    STON/O2. 3101282
3              113803
4              373450
Name: Ticket, dtype: object
1train['Cabin'].head()
0     NaN
1     C85
2     NaN
3    C123
4     NaN
Name: Cabin, dtype: object

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

  • Pclass
  • Gender
  • SibSp
  • Parch

این کار برای تک به تک ویژگی‌های بیان شده صورت می‌پذیرد.

1train[['Pclass', 'Survived']].groupby(['Pclass'], as_index=False).mean().sort_values(by='Survived', ascending=False)

الگوریتم K-Means

تعداد نجات‌یافتگان با توجه به Gender:

1train[["Gender", "Survived"]].groupby(['Gender'], as_index=False).mean().sort_values(by='Survived', ascending=False)

خوشه‌بندی K-Means

می‌توان مشاهده کرد که جمعیت زنان نجات یافته به طور چشم‌گیری بیشتر از مردها بوده است.

جمعیت نجات‌یافتگان با توجه به SibSp:

1train[["SibSp", "Survived"]].groupby(['SibSp'], as_index=False).mean().sort_values(by='Survived', ascending=False)

خوشه‌بندی K-Means

اکنون، زمان آن رسیده که برخی از نمودارها ترسیم شوند. در ادامه، نمودار «سن نسبت به نجات یافتن» (Age vs. Survived) ترسیم شده است.

1g = sns.FacetGrid(train, col='Survived')
2g.map(plt.hist, 'Age', bins=20)
seaborn.axisgrid.FacetGrid at 0x7fa990f87080

خوشه‌بندی K-Means

زمان آن رسیده که با رسم یک نمودار، بررسی شود که ویژگی‌های Pclass و Survived چگونه به یکدیگر مرتبط هستند.

1grid = sns.FacetGrid(train, col='Survived', row='Pclass', size=2.2, aspect=1.6)
2grid.map(plt.hist, 'Age', alpha=.5, bins=20)
3grid.add_legend();

خوشه بندی K-Means

بسیار خب، به نظر می‌رسد این حجم از بصری‌سازی تا این لحظه کافی باشد. اکنون، مدل K-Means با مجموعه داده آموزش ساخته می‌شود. اما پیش از آن، نیاز به انجام «پیش‌پردازش» (Preprocessing) داده‌ها است. می‌توان مشاهده کرد که مقادیر همه ویژگی‌ها از یک نوع نیست. برخی از آن‌ها عددی هستند. به منظور تسهیل محاسبات، همه داده‌های عددی به مدل خورانده می‌شوند. با استفاده از کد زیر،‌ نوع داده ویژگی‌های موجود در مجموعه داده مشخص می‌شوند:

1train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Name           891 non-null object
Gender          891 non-null object
Age            891 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          204 non-null object
Embarked       889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.6+ KB

اکنون، می‌توان مشاهده کرد که ویژگی‌های زیر غیر عددی هستند.

  • Name
  • Gender
  • Ticket
  • Cabin
  • Embarked

پیش از تبدیل این موارد به مقادیر عددی، ممکن است نیاز به انجام مقداری مهندسی ویژگی‌ها باشد. برای مثال، ویژگی‌های Cabin ،Ticket ،Name و Embarked هیچ تاثیری روی وضعیت مسافران ندارد. اغلب اوقات، بهتر است که مدل صرفا با ویژگی‌های موثر آموزش ببینید، به جای آنکه با همه ویژگی‌ها شامل موارد غیر لازم آموزش داده شود. این کار، نه فقط به آموزش موثر مدل کمک می‌کند، بلکه آموزش مدل با بهره‌گیری از آن در زمان کمتری به وقوع می‌پیوندد. «مهندسی ویژگی‌ها» (Feature Engineering) خود یک زمینه مطالعاتی گسترده است. در اینجا، مشخص است که ویژگی‌های غیر عددی Cabin ،Ticket ،Name و Embarked قابل حذف هستند و هیچ تاثیر قابل توجهی روی آموزش دادن مدل K-Means ندارند.

1train = train.drop(['Name','Ticket', 'Cabin','Embarked'], axis=1)
2test = test.drop(['Name','Ticket', 'Cabin','Embarked'], axis=1)

اکنون که کار حذف ویژگی‌ها انجام شد، ویژگی «Gender» به نوع عددی تبدیل می‌شود (تنها Gender باقیمانده که ویژگی غیر عددی است). این کار با استفاده از روشی که «رمزگذاری برچسب‌ها» (Label Encoding) نام دارد، قابل انجام است.

1labelEncoder = LabelEncoder()
2labelEncoder.fit(train['Gender'])
3labelEncoder.fit(test['Gender'])
4train['Gender'] = labelEncoder.transform(train['Gender'])
5test['Gender'] = labelEncoder.transform(test['Gender'])
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 8 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Gender         891 non-null int64
Age          891 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Fare         891 non-null float64
dtypes: float64(2), int64(6)
memory usage: 55.8 KB

توجه به این نکته لازم است که مجموعه تست، دارای ویژگی Survived (که همان برچسب است) نیست.

1test.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 7 columns):
PassengerId    418 non-null int64
Pclass         418 non-null int64
Gender         418 non-null int64
Age          418 non-null float64
SibSp          418 non-null int64
Parch          418 non-null int64
Fare         418 non-null float64
dtypes: float64(2), int64(5)
memory usage: 22.9 KB

به نظر می‌رسد کار آموزش دادن مدل K-Means تا این لحظه خوب پیش رفته است. ابتدا می‌توان ستون Survival را از داده‌ها با استفاده از تابع ()drop حذف کرد.

1X = np.array(train.drop(['Survived'], 1).astype(float))
1y = np.array(train['Survived'])

می‌توان همه ویژگی‌هایی که قرار است به الگوریتم داده شود را با دستور ()train.info بررسی کرد.

1  train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 7 columns):
PassengerId    891 non-null int64
Pclass         891 non-null int64
Gender         891 non-null int64
Age            891 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Fare           891 non-null float64
dtypes: float64(2), int64(5)
memory usage: 48.8 KB

اکنون، مدل K-Means ساخته می‌شود.

1kmeans = KMeans(n_clusters=2) # You want cluster the passenger records into 2: Survived or Not survived
2kmeans.fit(X)
1KMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=300,
2    n_clusters=2, n_init=10, n_jobs=1, precompute_distances='auto',
3    random_state=None, tol=0.0001, verbose=0)

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

1correct = 0
2for i in range(len(X)):
3    predict_me = np.array(X[i].astype(float))
4    predict_me = predict_me.reshape(-1, len(predict_me))
5    prediction = kmeans.predict(predict_me)
6    if prediction[0] == y[i]:
7        correct += 1
8
9print(correct/len(X))
0.5084175084175084

برای ابتدای کار خوب است. مدل قادر به خوشه‌بندی درست برای ۵۰٪ از داده‌ها است (صحت مدل). اما به منظور بهبود کارایی مدل، می‌توان برخی از پارامترهای مدل را تغییر داد و در واقع تنظیم کرد. برخی از پارامترهایی که در پیاده‌سازی کتابخانه Scikit-Learn الگوریتم K-Means وجود دارند، عبارتند از:

  • algorithm
  • max_iter
  • n_jobs

اکنون، مقدار پارامترها تنظیم می‌شود و سپس تغییرات رخ داده در نتایج خروجی مورد بررسی قرار می‌گیرد. در مستندات کتابخانه «سایکیت‌لرن» (Scikit-Learn) [+] اطلاعاتی پیرامون پارامترهای بیان شده وجود دارد که توصیه می‌شود افراد آن‌ها را مطالعه کنند.

1kmeans = kmeans = KMeans(n_clusters=2, max_iter=600, algorithm = 'auto')
2kmeans.fit(X)
1KMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=600,
2    n_clusters=2, n_init=10, n_jobs=1, precompute_distances='auto',
3    random_state=None, tol=0.0001, verbose=0)
1correct = 0
2for i in range(len(X)):
3    predict_me = np.array(X[i].astype(float))
4    predict_me = predict_me.reshape(-1, len(predict_me))
5    prediction = kmeans.predict(predict_me)
6    if prediction[0] == y[i]:
7        correct += 1
8
9print(correct/len(X))
0.49158249158249157

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

1scaler = MinMaxScaler()
2X_scaled = scaler.fit_transform(X)
1kmeans.fit(X_scaled)
1KMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=600,
2    n_clusters=2, n_init=10, n_jobs=1, precompute_distances='auto',
3    random_state=None, tol=0.0001, verbose=0)
1correct = 0
2for i in range(len(X)):
3    predict_me = np.array(X[i].astype(float))
4    predict_me = predict_me.reshape(-1, len(predict_me))
5    prediction = kmeans.predict(predict_me)
6    if prediction[0] == y[i]:
7        correct += 1
8
9print(correct/len(X))
0.6262626262626263

خیلی خوب است. می‌توان مشاهده کرد که با انجام این کار، ٪۱۲ صحت افزایش یافته است. تا این لحظه، چگونگی بارگذاری داده‌ها، پیش‌پردازش آن‌ها، انجام اندکی مهندسی ویژگی‌ها و پیاده‌سازی و اعمال مدل K-Means بررسی شد. اما الگوریتم K-Means دارای محدودیت‌هایی نیز هست که در ادامه بیان می‌شوند.

معایب الگوریتم K-Means

اکنون که ساز و کار الگوریتم K-Means به خوبی مشخص شده است، برخی از معایب آن تشریح می‌شود. بزرگترین عیب الگوریتم K-Means آن است که نیاز به از پیش تعریف شدن تعداد خوشه‌ها (K) دارد. اگرچه، برای مجموعه داده تایتانیک، با بهره‌گیری از دانش دامنه این امکان وجود داشت که تعداد خوشه‌ها (نجات یافتن یا نیافتن افراد) مشخص شود. این موضوع برای مسائل جهان واقعی همیشه صادق نیست. «خوشه‌بندی سلسله‌مراتبی» (Hierarchical Clustering) رویکردی جایگزین است که نیازی به تعداد خوشه‌ها ندارد. یک عیب دیگر الگوریتم K-Means این است که نسبت به «دورافتادگی‌ها» (Outliers) حساس است و در صورت تغییر ترتیب داده‌ها ممکن است خروجی‌ها تغییر کنند.

K-Means یک یادگیرنده تنبل است که عمومی‌سازی داده‌های آموزش تا هنگامی که یک کوئری برای سیستم ایجاد شود، به تاخیر می‌افتد. این یعنی K-Means تنها زمانی کار می‌کند که برای انجام آن راه اندازی شود، بدین ترتیب روش یادگیری تنبل می‌تواند تخمین متفاوت یا نتایج متفاوتی را برای تابع هدف، جهت هر کوئری که با آن مواجه است فراهم کند. K-Means یک روش خوب برای «یادگیری آنلاین» (Online Learning) است، اما نیاز به میزان زیادی حافظه برای ذخیره‌سازی داده‌ها دارد و هر درخواست شامل آغاز شناسایی مدل محلی از پایه می‌شود.

نتیجه‌گیری

در این راهنما، ساز و کار یکی از محبوب‌ترین روش‌های خوشه‌بندی، یعنی الگوریتم K-Means تشریح شد. سپس، یک بررسی موردی با استفاده از این الگوریتم انجام و در نهایت پیاده‌سازی مدل با بهره‌گیری از کتابخانه Scikit-Learn پایتون انجام پذیرفت.

اگر نوشته بالا برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

^^

بر اساس رای ۱۳ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
datacamp
۵ دیدگاه برای «خوشه بندی K-Means در پایتون — راهنمای کاربردی»

سلام عالی
اما کاش ارزیابی مدل رو هم می گفتید

سلام خوب هستین خانم من به کمک شما نیاز دارم تو روخدا جواب بدین ….پروژه من داده کاوی روی داده های رمزنگاری شده همومورفیک….تو قسمت رمزنگاری ازPailierاستفاده کردم ی فایل از اعداد دارم که میگیره رمزنگاری میکنه بعد عملیات انجام میده و خروجی نشون میده الان تو قسمت داده کاوی موندم …که میخوام داده های که به ابر فرستاده میشن بعد تو \ایگاه داده ذخیره میشن به صورتk-meansخوشه بنذی کنم بنظرتون قابل اجرااست …توروخدا کمکم کنید6ماه درگیرشم

سلام ممنون بابت انتشار مقاله مفیدتون، 15 آپریل سال 1912 این کشتی غرق شد نه 2015!!

با سلام؛

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

پیروز، شاد و تندرست باشید.

سلام.
ممنون از مطالب مفیدتون.
لطاف کد ها رو با کامنت گذاری کمی بیشتری توضیح دهید که قابل فهم‌تر باشد.

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *