المُقدّمة
يعني K- التكتل هي واحدة من أكثر خوارزميات التعلم الآلي غير الخاضعة للرقابة استخدامًا والتي تشكل مجموعات من البيانات بناءً على التشابه بين مثيلات البيانات.
في هذا الدليل ، سنلقي نظرة أولاً على مثال بسيط لفهم كيفية عمل خوارزمية K-Means قبل تنفيذها باستخدام Scikit-Learn. بعد ذلك ، سنناقش كيفية تحديد عدد المجموعات (Ks) في K-Means ، وأيضًا تغطية مقاييس المسافة والتباين وإيجابيات وسلبيات K-Means.
التحفيز
تخيل الموقف التالي. ذات يوم ، عندما تتجول في الحي ، لاحظت وجود 10 متاجر صغيرة وبدأت تتساءل عن المتاجر المتشابهة - أقرب إلى بعضها البعض على مقربة. أثناء البحث عن طرق للإجابة على هذا السؤال ، صادفت أسلوبًا مثيرًا للاهتمام يقسم المتاجر إلى مجموعات بناءً على إحداثياتها على الخريطة.
على سبيل المثال ، إذا كان أحد المتاجر يقع على بعد 5 كيلومترات غربًا و 3 كيلومترات شمالًا - فستقوم بتعيينه (5, 3)
تنسق إليه ، وتمثله في رسم بياني. دعنا نرسم هذه النقطة الأولى لتصور ما يحدث:
import matplotlib.pyplot as plt
plt.title("Store With Coordinates (5, 3)")
plt.scatter(x=5, y=3)
هذه هي النقطة الأولى فقط ، حتى نتمكن من الحصول على فكرة عن كيفية تمثيل المتجر. لنفترض أن لدينا بالفعل 10 إحداثيات للمحلات العشرة التي تم جمعها. بعد تنظيمهم في أ numpy
المصفوفة ، يمكننا أيضًا رسم مواقعها:
import numpy as np
points = np.array([[5, 3], [10, 15], [15, 12], [24, 10], [30, 45], [85, 70], [71, 80], [60, 78], [55, 52],[80, 91]])
xs = points[:,0]
ys = points[:,1]
plt.title("10 Stores Coordinates")
plt.scatter(x=xs, y=ys)
كيفية تنفيذ خوارزمية K-Means يدويًا
يمكننا الآن إلقاء نظرة على المتاجر العشرة على الرسم البياني ، والمشكلة الرئيسية هي معرفة هل هناك طريقة يمكن تقسيمها إلى مجموعات مختلفة بناءً على القرب؟ بمجرد إلقاء نظرة سريعة على الرسم البياني ، سنلاحظ على الأرجح مجموعتين من المتاجر - واحدة هي النقاط السفلية إلى أسفل اليسار ، والأخرى هي النقاط العلوية اليمنى. ربما يمكننا حتى التمييز بين هاتين النقطتين في المنتصف كمجموعة منفصلة - وبالتالي إنشاء ثلاث مجموعات مختلفة.
في هذا القسم ، سوف ننتقل إلى عملية تجميع النقاط يدويًا - تقسيمها إلى عدد معين من المجموعات. بهذه الطريقة ، سننتقل بعناية إلى جميع خطوات ملف K- يعني خوارزمية التجميع. بنهاية هذا القسم ، ستكتسب فهمًا بديهيًا وعمليًا لجميع الخطوات التي يتم إجراؤها أثناء تجميع K-Means. بعد ذلك ، سنقوم بتفويضه إلى Scikit-Learn.
ما هي أفضل طريقة لتحديد ما إذا كانت هناك مجموعتان أو ثلاث مجموعات من النقاط؟ تتمثل إحدى الطرق البسيطة في اختيار عدد واحد من المجموعات - على سبيل المثال ، اثنان - ثم محاولة تجميع النقاط بناءً على هذا الاختيار.
لنفترض أننا قررنا وجودها مجموعتين من متاجرنا (نقاط). الآن ، نحن بحاجة إلى إيجاد طريقة لفهم النقاط التي تنتمي إلى أي مجموعة. يمكن القيام بذلك عن طريق اختيار نقطة واحدة لتمثيلها مجموعة 1 وواحد لتمثيله مجموعة 2. سيتم استخدام هذه النقاط كمرجع عند قياس المسافة من جميع النقاط الأخرى إلى كل مجموعة.
بهذه الطريقة ، قل النقطة (5, 3)
ينتهي الأمر بالانتماء إلى المجموعة 1 ، والنقطة (79, 60)
للمجموعة 2. عند محاولة تعيين نقطة جديدة (6, 3)
للمجموعات ، نحتاج إلى قياس المسافة بين هاتين النقطتين. في حالة النقطة (6, 3)
is أقرب إلى (5, 3)
، لذلك فهي تنتمي إلى المجموعة التي تمثلها تلك النقطة - مجموعة 1. بهذه الطريقة ، يمكننا بسهولة تجميع جميع النقاط في مجموعات متطابقة.
في هذا المثال ، بجانب تحديد عدد المجموعات (المجموعات) - نختار أيضًا بعض النقاط لتكون a مرجع المسافة لنقاط جديدة لكل مجموعة.
هذه هي الفكرة العامة لفهم أوجه التشابه بين متاجرنا. دعنا نضعها موضع التنفيذ - يمكننا أولاً اختيار النقطتين المرجعيتين في عشوائية. النقطة المرجعية لـ مجموعة 1 سوف يكون (5, 3)
ونقطة مرجعية مجموعة 2 سوف يكون (10, 15)
. يمكننا تحديد كلا النقطتين numpy
مجموعة من قبل [0]
و [1]
الفهارس وتخزينها في g1
(المجموعة 1) و g2
(المجموعة 2) المتغيرات:
g1 = points[0]
g2 = points[1]
بعد القيام بذلك ، نحتاج إلى حساب المسافة من جميع النقاط الأخرى إلى تلك النقاط المرجعية. هذا يثير سؤالًا مهمًا - كيفية قياس تلك المسافة. يمكننا استخدام أي مقياس للمسافة بشكل أساسي ، ولكن لغرض هذا الدليل ، فلنستخدم Euclidean Distance_.
قد يكون من المفيد معرفة أن قياس المسافة الإقليدية يعتمد على نظرية فيثاغورس:
$$
ج ^ 2 = أ ^ 2 + ب ^ 2
$$
عندما تتكيف مع نقاط في الطائرة - (a1, b1)
و (a2, b2)
، تصبح الصيغة السابقة:
$$
ج ^ 2 = (a2-a1) ^ 2 + (b2-b1) ^ 2
$$
ستكون المسافة هي الجذر التربيعي لـ c
، حتى نتمكن أيضًا من كتابة الصيغة على النحو التالي:
$$
euclidean_ {dist} = sqrt [2] [(a2 - a1) ^ 2 + (b2 - b1) ^ 2)]
$$
ملحوظة: يمكنك أيضًا تعميم صيغة المسافة الإقليدية للنقاط متعددة الأبعاد. على سبيل المثال ، في الفضاء ثلاثي الأبعاد ، تحتوي النقاط على ثلاثة إحداثيات - وتعكس صيغتنا ذلك بالطريقة التالية:
$$
euclidean_ {dist} = sqrt [2] [(a2 - a1) ^ 2 + (b2 - b1) ^ 2 + (c2 - c1) ^ 2)]
$$
يتم اتباع نفس المبدأ بغض النظر عن عدد أبعاد المساحة التي نعمل فيها.
حتى الآن ، اخترنا النقاط لتمثيل المجموعات ، ونعرف كيفية حساب المسافات. الآن ، دعنا نضع المسافات والمجموعات معًا من خلال تخصيص كل نقطة من نقاط المتجر المجمعة لمجموعة.
لتصور ذلك بشكل أفضل ، سنعلن عن ثلاث قوائم. أول من يخزن نقاط المجموعة الأولى - points_in_g1
. الثاني لتخزين النقاط من المجموعة 2 - points_in_g2
، وآخر واحد - group
، إلى ملصق النقاط إما 1
(ينتمي إلى المجموعة 1) أو 2
(ينتمي إلى المجموعة 2):
points_in_g1 = []
points_in_g2 = []
group = []
يمكننا الآن تكرار نقاطنا وحساب المسافة الإقليدية بينها وبين كل مراجع مجموعتنا. كل نقطة ستكون أقرب لإحدى مجموعتين - بناءً على المجموعة الأقرب ، سنقوم بتعيين كل نقطة إلى القائمة المقابلة ، مع الإضافة أيضًا 1
or 2
إلى group
قائمة:
for p in points:
x1, y1 = p[0], p[1]
euclidean_distance_g1 = np.sqrt((g1[0] - x1)**2 + (g1[1] - y1)**2)
euclidean_distance_g2 = np.sqrt((g2[0] - x1)**2 + (g2[1] - y1)**2)
if euclidean_distance_g1 < euclidean_distance_g2:
points_in_g1.append(p)
group.append('1')
else:
points_in_g2.append(p)
group.append('2')
لنلقِ نظرة على نتائج هذا التكرار لنرى ما حدث:
print(f'points_in_g1:{points_in_g1}n
npoints_in_g2:{points_in_g2}n
ngroup:{group}')
مما يؤدي إلى:
points_in_g1:[array([5, 3])]
points_in_g2:[array([10, 15]), array([15, 12]),
array([24, 10]), array([30, 45]),
array([85, 70]), array([71, 80]),
array([60, 78]), array([55, 52]),
array([80, 91])]
group:[1, 2, 2, 2, 2, 2, 2, 2, 2, 2]
يمكننا أيضًا رسم نتيجة التجميع ، بألوان مختلفة بناءً على المجموعات المعينة ، باستخدام ألوان Seaborn scatterplot()
مع الالجائزة group
ك hue
حجة:
import seaborn as sns
sns.scatterplot(x=points[:, 0], y=points[:, 1], hue=group)
من الواضح أنه تم تخصيص النقطة الأولى فقط للمجموعة 1 ، وتم تخصيص جميع النقاط الأخرى للمجموعة 2. هذه النتيجة تختلف عما تصورناه في البداية. بالنظر إلى الاختلاف بين نتائجنا وتوقعاتنا الأولية - هل هناك طريقة يمكننا من خلالها تغيير ذلك؟ يبدو أن هناك!
يتمثل أحد الأساليب في تكرار العملية واختيار نقاط مختلفة لتكون بمثابة مراجع للمجموعات. سيغير هذا نتائجنا ، كما نأمل ، بما يتماشى مع ما تصورناه في البداية. هذه المرة الثانية ، يمكننا اختيارهم ليس عشوائيًا كما فعلنا سابقًا ، ولكن بالحصول على تعني من كل نقاطنا المجمعة بالفعل. بهذه الطريقة ، يمكن وضع تلك النقاط الجديدة في منتصف المجموعات المقابلة.
على سبيل المثال ، إذا كانت المجموعة الثانية لديها نقاط فقط (10, 15)
, (30, 45)
. الجديد مركزي ستكون النقطة (10 + 30)/2
و (15+45)/2
- وهو ما يساوي (20, 30)
.
نظرًا لأننا وضعنا نتائجنا في قوائم ، يمكننا تحويلها أولاً إلى numpy
المصفوفات ، حدد xs و ys الخاصة بهم ثم احصل على تعني:
g1_center = [np.array(points_in_g1)[:, 0].mean(), np.array(points_in_g1)[:, 1].mean()]
g2_center = [np.array(points_in_g2)[:, 0].mean(), np.array(points_in_g2)[:, 1].mean()]
g1_center, g2_center
المشورة: حاول استخدام numpy
ومصفوفات NumPy قدر الإمكان. تم تحسينها لأداء أفضل وتبسيط العديد من عمليات الجبر الخطي. عندما تحاول حل بعض مسائل الجبر الخطي ، يجب عليك بالتأكيد إلقاء نظرة على numpy
وثائق للتحقق مما إذا كان هناك أي numpy
طريقة مصممة لحل مشكلتك. الفرصة هي أن هناك!
للمساعدة في تكرار العملية باستخدام نقاط المركز الجديدة الخاصة بنا ، دعنا نحول الكود السابق إلى وظيفة ، وننفذها ونرى ما إذا كانت هناك أي تغييرات في كيفية تجميع النقاط:
def assigns_points_to_two_groups(g1_center, g2_center):
points_in_g1 = []
points_in_g2 = []
group = []
for p in points:
x1, y1 = p[0], p[1]
euclidean_distance_g1 = np.sqrt((g1_center[0] - x1)**2 + (g1_center[1] - y1)**2)
euclidean_distance_g2 = np.sqrt((g2_center[0] - x1)**2 + (g2_center[1] - y1)**2)
if euclidean_distance_g1 < euclidean_distance_g2:
points_in_g1.append(p)
group.append(1)
else:
points_in_g2.append(p)
group.append(2)
return points_in_g1, points_in_g2, group
ملحوظة: إذا لاحظت استمرار تكرار نفس الشفرة مرارًا وتكرارًا ، فيجب عليك التفاف هذا الرمز في وظيفة منفصلة. يعتبر تنظيم التعليمات البرمجية في وظائف أفضل ممارسة ، خاصة لأنها تسهل الاختبار. من الأسهل اختبار جزء من التعليمات البرمجية وعزله عن كود كامل بدون أي وظائف.
دعنا نستدعي الوظيفة ونخزن نتائجها في points_in_g1
, points_in_g2
و group
المتغيرات:
points_in_g1, points_in_g2, group = assigns_points_to_two_groups(g1_center, g2_center)
points_in_g1, points_in_g2, group
وأيضًا ارسم مخطط التشتت بالنقاط الملونة لتصور تقسيم المجموعات:
sns.scatterplot(x=points[:, 0], y=points[:, 1], hue=group)
يبدو أن تجميع نقاطنا هو يتحسن. ولكن لا تزال هناك نقطتان في منتصف الرسم البياني يمكن تخصيصهما لأي من المجموعتين عند النظر في قربهما من كلا المجموعتين. تقوم الخوارزمية التي طورناها حتى الآن بتعيين هاتين النقطتين إلى المجموعة الثانية.
هذا يعني أنه يمكننا على الأرجح تكرار العملية مرة أخرى بأخذ وسائل X و Y ، وإنشاء نقطتين مركزيتين جديدتين (النقطه الوسطى) لمجموعاتنا وإعادة تخصيصها بناءً على المسافة.
لنقم أيضًا بإنشاء وظيفة لتحديث النقط الوسطى. يمكن الآن اختزال العملية برمتها إلى استدعاءات متعددة لهذه الوظيفة:
def updates_centroids(points_in_g1, points_in_g2):
g1_center = np.array(points_in_g1)[:, 0].mean(), np.array(points_in_g1)[:, 1].mean()
g2_center = np.array(points_in_g2)[:, 0].mean(), np.array(points_in_g2)[:, 1].mean()
return g1_center, g2_center
g1_center, g2_center = updates_centroids(points_in_g1, points_in_g2)
points_in_g1, points_in_g2, group = assigns_points_to_two_groups(g1_center, g2_center)
sns.scatterplot(x=points[:, 0], y=points[:, 1], hue=group)
لاحظ أنه بعد هذا التكرار الثالث ، تنتمي كل نقطة الآن إلى مجموعات مختلفة. يبدو أن النتائج تتحسن - فلنقم بذلك مرة أخرى. يذهب الآن إلى التكرار الرابع من طريقتنا:
g1_center, g2_center = updates_centroids(points_in_g1, points_in_g2)
points_in_g1, points_in_g2, group = assigns_points_to_two_groups(g1_center, g2_center)
sns.scatterplot(x=points[:, 0], y=points[:, 1], hue=group)
هذه المرة الرابعة وصلنا نفس النتيجة كالسابق. لذلك يبدو أن نقاطنا لن تغير المجموعات بعد الآن ، فقد وصلت نتيجتنا إلى نوع من الاستقرار - لقد وصلت إلى حالة غير قابلة للتغيير ، أو تلاقت. إلى جانب ذلك ، لدينا نفس النتيجة تمامًا كما تصورنا للمجموعتين. يمكننا أيضًا معرفة ما إذا كان هذا التقسيم الذي تم التوصل إليه منطقيًا.
دعنا نلخص بسرعة ما فعلناه حتى الآن. قسمنا متاجرنا العشرة جغرافيًا إلى قسمين - قسم في المناطق الجنوبية الغربية السفلية والآخر في الشمال الشرقي. قد يكون من المثير للاهتمام جمع المزيد من البيانات إلى جانب ما لدينا بالفعل - الإيرادات والعدد اليومي للعملاء وغير ذلك الكثير. بهذه الطريقة يمكننا إجراء تحليل أكثر ثراءً وربما الحصول على نتائج أكثر إثارة للاهتمام.
يمكن إجراء دراسات مجمعة مثل هذه عندما تريد علامة تجارية قائمة بالفعل اختيار منطقة لفتح متجر جديد. في هذه الحالة ، هناك العديد من المتغيرات التي تؤخذ في الاعتبار إلى جانب الموقع.
ما علاقة كل هذا بخوارزمية K-Means؟
أثناء اتباع هذه الخطوات ، ربما تساءلت عما يجب عليهم فعله مع خوارزمية K-Means. العملية التي أجريناها حتى الآن هي خوارزمية K-Means. باختصار ، لقد حددنا عدد المجموعات / المجموعات ، واخترنا النقاط الأولية بشكل عشوائي ، وقمنا بتحديث النقط الوسطى في كل تكرار حتى تتقارب المجموعات. لقد أجرينا الخوارزمية بالكامل يدويًا - ونفذنا كل خطوة بعناية.
• K في K-Means يأتي من عدد المجموعات التي يجب تعيينها قبل بدء عملية التكرار. في حالتنا هذه K = 2. يُنظر إلى هذه الخاصية أحيانًا على أنها سلبي مع الأخذ في الاعتبار أن هناك طرق أخرى للتجميع ، مثل Hierarchical Clustering ، والتي لا تحتاج إلى عدد ثابت من المجموعات مسبقًا.
نظرًا لاستخدامها للوسائل ، تصبح K-mean أيضًا حساسة للقيم المتطرفة والقيم المتطرفة - إنها تعزز التباين وتجعل من الصعب على النقطتين الوسطى لدينا أن تلعب دورها. لذا ، كن مدركًا للحاجة إلى الأداء القيم المتطرفة والتحليل الناشز قبل إجراء التجميع باستخدام خوارزمية K-Means.
لاحظ أيضًا أن نقاطنا مقسمة إلى أجزاء مستقيمة ، ولا توجد منحنيات عند إنشاء المجموعات. يمكن أن يكون ذلك أيضًا عيبًا في خوارزمية K-Means.
ملحوظة: عندما تحتاج إلى أن يكون أكثر مرونة وقابلية للتكيف مع الأشكال الناقصة والأشكال الأخرى ، جرب استخدام ملف معمم K- يعني نموذج خليط غاوسي. يمكن أن يتكيف هذا النموذج مع مجموعات التجزئة الإهليلجية.
K-Means لديها أيضًا الكثير مزايا! يعمل بشكل جيد على مجموعات كبيرة من البيانات التي يمكن أن يصبح من الصعب التعامل معها إذا كنت تستخدم بعض أنواع خوارزميات المجموعات الهرمية. كذلك يضمن التقاربويمكن بسهولة عمم و تكيف. إلى جانب ذلك ، من المحتمل أن تكون خوارزمية التجميع الأكثر استخدامًا.
الآن بعد أن انتهينا من جميع الخطوات التي تم إجراؤها في خوارزمية K-Means ، وفهمنا جميع مزاياها وعيوبها ، يمكننا أخيرًا تنفيذ K-Means باستخدام مكتبة Scikit-Learn.
كيفية تنفيذ خوارزمية K-Means باستخدام سكيكيت ليرن
لمضاعفة التحقق من نتيجتنا ، لنقم بهذه العملية مرة أخرى ، ولكن الآن نستخدم 3 أسطر من التعليمات البرمجية مع sklearn
:
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=2, random_state=42)
kmeans.fit(points)
kmeans.labels_
هنا ، التسميات هي نفسها مجموعاتنا السابقة. دعنا فقط نرسم النتيجة بسرعة:
sns.scatterplot(x = points[:,0], y = points[:,1], hue=kmeans.labels_)
المؤامرة الناتجة هي نفسها التي من القسم السابق.
تحقق من دليلنا العملي العملي لتعلم Git ، مع أفضل الممارسات ، والمعايير المقبولة في الصناعة ، وورقة الغش المضمنة. توقف عن أوامر Googling Git وفي الواقع تعلم ذلك!
ملحوظة: مجرد النظر إلى كيفية قيامنا بخوارزمية K-Means باستخدام Scikit-Learn قد يمنحك انطباعًا بأن الشخص لا يحتاج إلى تفكير وأنه لا داعي للقلق كثيرًا بشأنه. تقوم ثلاثة أسطر فقط من التعليمات البرمجية بتنفيذ جميع الخطوات التي ناقشناها في القسم السابق عندما تجاوزنا خوارزمية K-Means خطوة بخطوة. ولكن، الشر في التفاصيل في هذه الحالة! إذا كنت لا تفهم كل خطوات وقيود الخوارزمية ، فمن المرجح أنك ستواجه الموقف الذي تمنحك فيه خوارزمية K-Means النتائج التي لم تكن تتوقعها.
باستخدام Scikit-Learn ، يمكنك أيضًا تهيئة K-Means لتقارب أسرع عن طريق تعيين init='k-means++'
جدال. بعبارات أوسع ، K- يعني ++ لا يزال يختار k مراكز الكتلة الأولية بشكل عشوائي بعد توزيع موحد. بعد ذلك ، يتم اختيار كل مركز كتلة لاحق من نقاط البيانات المتبقية ليس عن طريق حساب قياس المسافة فقط - ولكن باستخدام الاحتمال. يؤدي استخدام الاحتمال إلى تسريع الخوارزمية وهو مفيد عند التعامل مع مجموعات البيانات الكبيرة جدًا.
طريقة الكوع - اختيار أفضل عدد من المجموعات
حتى الان جيدة جدا! قمنا بتجميع 10 متاجر على أساس المسافة الإقليدية بين النقاط والنقاط الوسطى. ولكن ماذا عن هاتين النقطتين في منتصف الرسم البياني والتي يصعب تجميعها قليلاً؟ ألا يمكنهم تكوين مجموعة منفصلة أيضًا؟ هل ارتكبنا خطأ بالفعل باختيارنا K = 2 مجموعات؟ ربما كان لدينا بالفعل K = 3 مجموعات؟ يمكن أن يكون لدينا أكثر من ثلاث مجموعات ولا نكون على علم بذلك.
السؤال المطروح هنا هو كيفية تحديد عدد المجموعات (K) في K-Means. للإجابة على هذا السؤال ، نحتاج إلى فهم ما إذا كان سيكون هناك مجموعة "أفضل" لقيمة مختلفة لـ K.
الطريقة الساذجة لاكتشاف ذلك هي عن طريق تجميع النقاط بقيم مختلفة لـ K، وذلك ل K = 2 ، K = 3 ، K = 4 ، وهكذا:
for number_of_clusters in range(1, 11):
kmeans = KMeans(n_clusters = number_of_clusters, random_state = 42)
kmeans.fit(points)
لكن ، تجميع نقاط مختلفة Ks وحده لن يكون كافيًا لفهم ما إذا كنا قد اخترنا القيمة المثالية لـ K. نحن بحاجة إلى طريقة لتقييم جودة التجميع لكل منها K اخترنا.
يدويا حساب ضمن مجموع المربعات العنقودية (WCSS)
هذا هو المكان المثالي لتقديم مقياس لمدى قرب نقاطنا العنقودية من بعضها البعض. يصف أساسا كم فرق لدينا داخل مجموعة واحدة. هذا المقياس يسمى ضمن مجموع المربعات العنقوديةالطرق أو WCSS لفترة قصيرة. كلما كان WCSS أصغر ، كلما كانت نقاطنا أقرب ، وبالتالي لدينا مجموعة جيدة التكوين. يمكن استخدام صيغة WCSS لأي عدد من المجموعات:
$$
WCSS = sum (Pi_1 - Centroid_1) ^ 2 + cdots + sum (Pi_n - Centroid_n) ^ 2
$$
ملحوظة: في هذا الدليل ، نستخدم ملف المسافة الإقليدية للحصول على النقطتين الوسطى ، ولكن يمكن أيضًا استخدام مقاييس المسافة الأخرى ، مثل مانهاتن.
الآن يمكننا أن نفترض أننا اخترنا وجود مجموعتين ومحاولة تنفيذ WCSS لفهم ماهية WCSS بشكل أفضل وكيفية استخدامه. كما تنص الصيغة ، نحتاج إلى تلخيص الفروق التربيعية بين جميع نقاط الكتلة والنقاط الوسطى. إذن ، إذا كانت النقطة الأولى من المجموعة الأولى هي (5, 3)
وآخر النقطه الوسطى (بعد التقارب) من المجموعة الأولى هي (16.8, 17.0)
، سيكون WCSS:
$$
WCSS = مجموع ((5,3،16.8) - (17.0، 2)) ^ XNUMX
$$
$$
WCSS = مجموع ((5-16.8) + (3-17.0)) ^ 2
$$
$$
WCSS = مجموع ((- 11.8) + (-14.0)) ^ 2
$$
$$
WCSS = مجموع ((- 25.8)) ^ 2
$$
$$
دبليو سي إس إس = 335.24
$$
يوضح هذا المثال كيف نحسب WCSS لنقطة واحدة من الكتلة. لكن الكتلة تحتوي عادةً على أكثر من نقطة واحدة ، ونحن بحاجة إلى أخذها جميعًا في الاعتبار عند حساب WCSS. سنفعل ذلك عن طريق تحديد دالة تتلقى مجموعة من النقاط والنقاط الوسطى ، وتعيد مجموع المربعات:
def sum_of_squares(cluster, centroid):
squares = []
for p in cluster:
squares.append((p - centroid)**2)
ss = np.array(squares).sum()
return ss
الآن يمكننا الحصول على مجموع المربعات لكل عنقود:
g1 = sum_of_squares(points_in_g1, g1_center)
g2 = sum_of_squares(points_in_g2, g2_center)
ولخص النتائج للحصول على المجموع WCSS:
g1 + g2
وينتج عنه:
2964.3999999999996
لذلك ، في حالتنا ، متى K يساوي 2 ، إجمالي WCSS هو 2964.39. الآن ، يمكننا تبديل Ks وحساب WCSS لكل منهم. بهذه الطريقة ، يمكننا الحصول على نظرة ثاقبة لما K يجب أن نختار جعل نظام المجموعات لدينا يعمل بشكل أفضل.
حساب WCSS باستخدام سكيكيت ليرن
لحسن الحظ ، لا نحتاج إلى حساب WCSS يدويًا لكل منهما K. بعد إجراء تجميع K-Means لعدد معين من المجموعات ، يمكننا الحصول على WCSS الخاص به باستخدام inertia_
ينسب. الآن ، يمكننا العودة إلى K-Means for
loop ، استخدمها لتبديل عدد المجموعات ، وقم بإدراج قيم WCSS المقابلة:
wcss = []
for number_of_clusters in range(1, 11):
kmeans = KMeans(n_clusters = number_of_clusters, random_state = 42)
kmeans.fit(points)
wcss.append(kmeans.inertia_)
wcss
لاحظ أن القيمة الثانية في القائمة هي بالضبط نفس القيمة التي حسبناها من قبل K = 2:
[18272.9, # For k=1
2964.3999999999996, # For k=2
1198.75, # For k=3
861.75,
570.5,
337.5,
175.83333333333334,
79.5,
17.0,
0.0]
لتصور هذه النتائج ، دعنا نرسم Ks جنبًا إلى جنب مع قيم WCSS:
ks = [1, 2, 3, 4, 5 , 6 , 7 , 8, 9, 10]
plt.plot(ks, wcss)
هناك انقطاع في المؤامرة عندما x = 2
، ونقطة منخفضة في الخط ، ونقطة أقل عندما x = 3
. لاحظ أنه يذكرنا بـ شكل الكوع. من خلال رسم Ks جنبًا إلى جنب مع WCSS ، فإننا نستخدم امتداد طريقة الكوع لاختيار عدد Ks. و ال اختيار K هو بالضبط أدنى نقطة كوع، لذلك ، سيكون 3
بدلا من 2
، في حالتنا هذه:
ks = [1, 2, 3, 4, 5 , 6 , 7 , 8, 9, 10]
plt.plot(ks, wcss);
plt.axvline(3, linestyle='--', color='r')
يمكننا تشغيل خوارزمية مجموعة K-Means مرة أخرى ، لنرى كيف ستبدو بياناتنا ثلاث مجموعات:
kmeans = KMeans(n_clusters=3, random_state=42)
kmeans.fit(points)
sns.scatterplot(x = points[:,0], y = points[:,1], hue=kmeans.labels_)
كنا سعداء بالفعل بمجموعتين ، ولكن وفقًا لطريقة الكوع ، ستكون ثلاث مجموعات أكثر ملاءمة لبياناتنا. في هذه الحالة ، سيكون لدينا ثلاثة أنواع من المتاجر بدلاً من اثنين. قبل استخدام طريقة الكوع ، فكرنا في مجموعات المتاجر الجنوبية الغربية والشمالية الشرقية ، والآن لدينا أيضًا متاجر في المركز. ربما يكون هذا موقعًا جيدًا لفتح متجر آخر لأنه سيكون لديه منافسة أقل في مكان قريب.
مقاييس جودة الكتلة البديلة
هناك أيضًا مقاييس أخرى يمكن استخدامها عند تقييم جودة الكتلة:
- نتيجة صورة ظلية - يحلل ليس فقط المسافة بين نقاط داخل المجموعة ولكن أيضًا بين المجموعات نفسها
- بين العناقيد مجموع المربعات (BCSS) - متري مكمل لـ WCSS
- مجموع خطأ المربعات (جنوب شرق أوروبا)
- أقصى نصف قطر - يقيس أكبر مسافة من نقطة إلى النقطه الوسطى
- متوسط نصف القطر - مجموع أكبر مسافة من نقطة إلى النقطه الوسطى مقسومًا على عدد العناقيد.
يوصى بإجراء التجربة والتعرف على كل منها نظرًا لأنه اعتمادًا على المشكلة ، يمكن أن تكون بعض البدائل أكثر قابلية للتطبيق من المقاييس الأكثر استخدامًا (WCSS و Silhouette Score).
في النهاية ، كما هو الحال مع العديد من خوارزميات علوم البيانات ، نريد تقليل التباين داخل كل مجموعة وزيادة التباين بين المجموعات المختلفة. لذلك لدينا مجموعات أكثر تحديدًا وقابلة للفصل.
تطبيق K-Means على مجموعة بيانات أخرى
دعنا نستخدم ما تعلمناه في مجموعة بيانات أخرى. هذه المرة ، سنحاول العثور على مجموعات من النبيذ المتشابه.
ملحوظة: يمكنك تنزيل مجموعة البيانات هنا.
نبدأ بالاستيراد pandas
لقراءة wine-clustering
CSV (قيم مفصولة بفواصل) ملف في ملف Dataframe
بناء:
import pandas as pd
df = pd.read_csv('wine-clustering.csv')
بعد تحميله ، دعنا نلقي نظرة خاطفة على السجلات الخمسة الأولى للبيانات بامتداد head()
الأسلوب:
df.head()
وينتج عنه:
Alcohol Malic_Acid Ash Ash_Alcanity Magnesium Total_Phenols Flavanoids Nonflavanoid_Phenols Proanthocyanins Color_Intensity Hue OD280 Proline
0 14.23 1.71 2.43 15.6 127 2.80 3.06 0.28 2.29 5.64 1.04 3.92 1065
1 13.20 1.78 2.14 11.2 100 2.65 2.76 0.26 1.28 4.38 1.05 3.40 1050
2 13.16 2.36 2.67 18.6 101 2.80 3.24 0.30 2.81 5.68 1.03 3.17 1185
3 14.37 1.95 2.50 16.8 113 3.85 3.49 0.24 2.18 7.80 0.86 3.45 1480
4 13.24 2.59 2.87 21.0 118 2.80 2.69 0.39 1.82 4.32 1.04 2.93 735
لدينا العديد من القياسات للمواد الموجودة في النبيذ. هنا ، لن نحتاج أيضًا إلى تحويل الأعمدة الفئوية لأن جميعها رقمية. الآن ، دعنا نلقي نظرة على الإحصاء الوصفي باستخدام describe()
الأسلوب:
df.describe().T
جدول الوصف:
count mean std min 25% 50% 75% max
Alcohol 178.0 13.000618 0.811827 11.03 12.3625 13.050 13.6775 14.83
Malic_Acid 178.0 2.336348 1.117146 0.74 1.6025 1.865 3.0825 5.80
Ash 178.0 2.366517 0.274344 1.36 2.2100 2.360 2.5575 3.23
Ash_Alcanity 178.0 19.494944 3.339564 10.60 17.2000 19.500 21.5000 30.00
Magnesium 178.0 99.741573 14.282484 70.00 88.0000 98.000 107.0000 162.00
Total_Phenols 178.0 2.295112 0.625851 0.98 1.7425 2.355 2.8000 3.88
Flavanoids 178.0 2.029270 0.998859 0.34 1.2050 2.135 2.8750 5.08
Nonflavanoid_Phenols 178.0 0.361854 0.124453 0.13 0.2700 0.340 0.4375 0.66
Proanthocyanins 178.0 1.590899 0.572359 0.41 1.2500 1.555 1.9500 3.58
Color_Intensity 178.0 5.058090 2.318286 1.28 3.2200 4.690 6.2000 13.00
Hue 178.0 0.957449 0.228572 0.48 0.7825 0.965 1.1200 1.71
OD280 178.0 2.611685 0.709990 1.27 1.9375 2.780 3.1700 4.00
Proline 178.0 746.893258 314.907474 278.00 500.500 673.500 985.0000 1680.00
بالنظر إلى الجدول يتضح أن هناك البعض منها التباين في البيانات - لبعض الأعمدة مثل Alchool
هناك المزيد ، وللآخرين ، مثل Malic_Acid
، أقل. الآن يمكننا التحقق مما إذا كان هناك أي منها null
الطرق أو NaN
القيم في مجموعة البيانات الخاصة بنا:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 178 entries, 0 to 177
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Alcohol 178 non-null float64
1 Malic_Acid 178 non-null float64
2 Ash 178 non-null float64
3 Ash_Alcanity 178 non-null float64
4 Magnesium 178 non-null int64
5 Total_Phenols 178 non-null float64
6 Flavanoids 178 non-null float64
7 Nonflavanoid_Phenols 178 non-null float64
8 Proanthocyanins 178 non-null float64
9 Color_Intensity 178 non-null float64
10 Hue 178 non-null float64
11 OD280 178 non-null float64
12 Proline 178 non-null int64
dtypes: float64(11), int64(2)
memory usage: 18.2 KB
ليست هناك حاجة لإسقاط البيانات أو إدخالها ، مع الأخذ في الاعتبار عدم وجود قيم فارغة في مجموعة البيانات. يمكننا استخدام Seaborn pairplot()
لمشاهدة توزيع البيانات وللتحقق مما إذا كانت مجموعة البيانات تشكل أزواجًا من الأعمدة التي يمكن أن تكون مثيرة للاهتمام للتجميع:
sns.pairplot(df)
من خلال النظر إلى حبكة الزوج ، يبدو أن هناك عمودين واعدتين لأغراض التجميع - Alcohol
و OD280
(وهي طريقة لتحديد تركيز البروتين في النبيذ). يبدو أن هناك 3 مجموعات متميزة على قطع الأرض تجمع بين اثنتين منها.
هناك أعمدة أخرى يبدو أنها مرتبطة أيضًا. الجدير بالذكر Alcohol
و Total_Phenols
و Alcohol
و Flavanoids
. لديهم علاقات خطية كبيرة يمكن ملاحظتها في حبكة الزوج.
نظرًا لأن تركيزنا ينصب على التجميع باستخدام K-Means ، فلنختار زوجًا واحدًا من الأعمدة ، على سبيل المثال Alcohol
و OD280
، واختبر طريقة الكوع لمجموعة البيانات هذه.
ملحوظة: عند استخدام المزيد من أعمدة مجموعة البيانات ، ستكون هناك حاجة للتخطيط في 3 أبعاد أو تقليل البيانات إلى المكونات الرئيسية (استخدام PCA). هذا نهج صالح وأكثر شيوعًا ، فقط تأكد من اختيار المكونات الأساسية بناءً على مقدار شرحها وتذكر أنه عند تقليل أبعاد البيانات ، يكون هناك بعض فقدان المعلومات - لذا فإن المؤامرة هي تقريب من البيانات الحقيقية ، وليس كيف هي بالفعل.
دعنا نرسم مخطط التشتت مع هذين العمودين المعينين ليكونا محوره لإلقاء نظرة فاحصة على النقاط التي نريد تقسيمها إلى مجموعات:
sns.scatterplot(data=df, x='OD280', y='Alcohol')
الآن يمكننا تحديد أعمدةنا واستخدام طريقة الكوع لتحديد عدد المجموعات. سنبدأ أيضًا الخوارزمية باستخدام kmeans++
فقط للتأكد من أنه يتقارب بسرعة أكبر:
values = df[['OD280', 'Alcohol']]
wcss_wine = []
for i in range(1, 11):
kmeans = KMeans(n_clusters = i, init = 'k-means++', random_state = 42)
kmeans.fit(values)
wcss_wine.append(kmeans.inertia_)
لقد قمنا بحساب WCSS ، حتى نتمكن من رسم النتائج:
clusters_wine = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
plt.plot(clusters_wine, wcss_wine)
plt.axvline(3, linestyle='--', color='r')
وفقًا لطريقة الكوع ، يجب أن يكون لدينا 3 مجموعات هنا. للخطوة الأخيرة ، دعنا نجمع نقاطنا في 3 مجموعات ونرسم تلك المجموعات المحددة بواسطة الألوان:
kmeans_wine = KMeans(n_clusters=3, random_state=42)
kmeans_wine.fit(values)
sns.scatterplot(x = values['OD280'], y = values['Alcohol'], hue=kmeans_wine.labels_)
يمكننا أن نرى العناقيد 0
, 1
و 2
في الرسم البياني. بناءً على تحليلنا ، مجموعة 0 يحتوي على نبيذ يحتوي على نسبة عالية من البروتين ونسبة كحول أقل ، مجموعة 1 يحتوي على نبيذ يحتوي على نسبة عالية من الكحول وبروتين منخفض ، و مجموعة 2 يحتوي على نسبة عالية من البروتين والكحول في النبيذ.
هذه مجموعة بيانات مثيرة جدًا للاهتمام وأنا أشجعك على المضي قدمًا في التحليل من خلال تجميع البيانات بعد التطبيع و PCA - أيضًا من خلال تفسير النتائج وإيجاد اتصالات جديدة.
وفي الختام
K- الوسائل التجميع هو خوارزمية تعلم آلي بسيطة لكنها فعالة للغاية غير خاضعة للإشراف لتجميع البيانات. يقوم بتجميع البيانات بناءً على المسافة الإقليدية بين نقاط البيانات. تحتوي خوارزمية التجميع K-Means على العديد من الاستخدامات لتجميع المستندات النصية والصور ومقاطع الفيديو وغير ذلك الكثير.