التجميع: اكتشاف المجموعات بخوارزمية K-Means
في كل ما سبق كانت معك الإجابات الصحيحة: تسميات تتدرب عليها وتقيس بها. لكن بيانات كثيرة تأتي بلا أي تسميات: قائمة عملاء، أو مقالات، أو قراءات أجهزة. هنا يأتي التجميع (Clustering) الذي لمحته في درس أنواع التعلّم. في هذا الدرس الأخير ستفهم كيف تعمل خوارزمية K-Means، وتقع عمداً في أشهر أخطائها ثم تصلحه، وتتعلم كيف تختار عدد المجموعات.
كيف تعمل K-Means؟
Section titled “كيف تعمل K-Means؟”الحرف K هو عدد المجموعات الذي تختاره أنت. ثم تكرر الخوارزمية خطوتين:
- تضع K نقطة عشوائية تسمى المراكز (Centroids).
- تُلحق كل عينة بأقرب مركز إليها.
- تنقل كل مركز إلى متوسط العينات التي التحقت به.
- تعيد الخطوتين 2 و 3 حتى تتوقف المراكز عن الحركة.
تخيّل أنك تريد وضع ثلاث عربات مثلجات في حديقة مزدحمة. تضعها في أي مكان، فيذهب كل شخص إلى أقرب عربة. ثم تحرّك كل عربة إلى وسط زبائنها، فيغيّر بعض الناس عربتهم. وتكرر حتى يستقر الجميع.
بيانات المتجر
Section titled “بيانات المتجر”متجر سجّل لخمسة عشر عميلاً عدد الزيارات في الشهر ومتوسط الفاتورة:
import numpy as npfrom sklearn.cluster import KMeans
# [عدد الزيارات في الشهر، متوسط الفاتورة بالجنيه]customers = np.array([ [2, 60], [3, 80], [1, 50], [2, 90], [4, 70], [12, 120], [14, 100], [11, 140], [13, 110], [15, 130], [3, 600], [2, 750], [4, 680], [1, 820], [3, 700],])انظر إلى الأرقام بعينك: خمسة عملاء يزورون قليلاً ويدفعون قليلاً، وخمسة يزورون كثيراً بفواتير متوسطة، وخمسة يزورون قليلاً بفواتير كبيرة. ثلاث مجموعات واضحة. لنر هل يجدها النموذج.
المحاولة الأولى وفخ المقاييس
Section titled “المحاولة الأولى وفخ المقاييس”km = KMeans(n_clusters=3, n_init=10, random_state=42)print(km.fit_predict(customers))[1 1 1 1 1 1 1 1 1 1 0 2 0 2 0]نتيجة مخيّبة! العملاء العشرة الأوائل في مجموعة واحدة، مع أن نصفهم يزور 12 مرة أو أكثر، وأصحاب الفواتير الكبيرة انقسموا إلى مجموعتين بلا سبب مفهوم.
السبب أن K-Means تحسب المسافة، والفاتورة بالمئات والزيارات بالآحاد. فرق 100 جنيه يطغى تماماً على فرق 10 زيارات، فكأن عمود الزيارات غير موجود. هذه هي المشكلة نفسها التي رأيتها مع خوارزمية الجيران الأقرب، والعلاج نفسه: توحيد المقاييس.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()scaled = scaler.fit_transform(customers)
km = KMeans(n_clusters=3, n_init=10, random_state=42)labels = km.fit_predict(scaled)print(labels)[2 2 2 2 2 0 0 0 0 0 1 1 1 1 1]الآن وجد النموذج المجموعات الثلاث التي رأيناها بأعيننا. وتذكّر أن الأرقام 0 و 1 و 2 مجرد أسماء بلا ترتيب ولا معنى.
n_init=10: شغّل الخوارزمية عشر مرات ببدايات عشوائية مختلفة واحتفظ بأفضلها، فالبداية السيئة قد تعطي تقسيماً سيئاً.random_state=42: يثبّت العشوائية لتحصل على النتيجة نفسها في كل تشغيل.
تفسير المجموعات
Section titled “تفسير المجموعات”النموذج أعطاك أرقاماً، والمعنى مهمتك أنت. المراكز محسوبة بالمقاييس الموحّدة، فنعيدها إلى الوحدات الأصلية لنقرأها:
centers = scaler.inverse_transform(km.cluster_centers_)print(centers.round(1))[[ 13. 120. ] [ 2.6 710. ] [ 2.4 70. ]]كل سطر مركز مجموعة: متوسط الزيارات ثم متوسط الفاتورة.
| المجموعة | الزيارات | الفاتورة | اسم نقترحه | فكرة تسويقية |
|---|---|---|---|---|
| 0 | 13 | 120 | العملاء الأوفياء | برنامج نقاط |
| 1 | 2.6 | 710 | أصحاب المشتريات الكبيرة | عروض على المنتجات الغالية |
| 2 | 2.4 | 70 | العملاء العابرون | خصم يشجعهم على العودة |
ولتصنيف عميل جديد، وحّد مقاييسه بالمحوِّل نفسه ثم اسأل النموذج:
new_customers = scaler.transform([[14, 110], [2, 650]])print(km.predict(new_customers))[0 1]الأول عميل وفيّ، والثاني من أصحاب المشتريات الكبيرة.
كم مجموعة؟ طريقة الكوع
Section titled “كم مجموعة؟ طريقة الكوع”هنا عرفنا العدد بالنظر، لكن مع آلاف العملاء وعشر ميزات لن ترى شيئاً. المقياس inertia_ هو مجموع مربعات المسافات بين كل عينة ومركز مجموعتها، وكلما صغر كانت المجموعات أكثر تماسكاً:
for k in range(1, 7): m = KMeans(n_clusters=k, n_init=10, random_state=42).fit(scaled) print(k, round(m.inertia_, 2))1 30.02 12.053 1.124 0.755 0.466 0.3الرقم ينخفض دائماً مع زيادة K، ولو جعلت كل عميل مجموعة وحده لوصل إلى الصفر، فلا تبحث عن أصغر قيمة. ابحث عن الكوع (Elbow): النقطة التي يتوقف بعدها الانخفاض الكبير. من 2 إلى 3 هبوط حاد (من 12.05 إلى 1.12)، وبعد 3 تحسّن ضئيل. الكوع عند 3.
معامل Silhouette
Section titled “معامل Silhouette”الكوع ليس واضحاً دائماً. معامل الظل (Silhouette) يعطيك رقماً واحداً بين -1 و 1: كلما اقترب من 1 كانت كل عينة قريبة من مجموعتها وبعيدة عن غيرها.
from sklearn.metrics import silhouette_score
for k in range(2, 6): m = KMeans(n_clusters=k, n_init=10, random_state=42).fit(scaled) print(k, round(silhouette_score(scaled, m.labels_), 3))2 0.5873 0.8164 0.7015 0.579أعلى قيمة عند K يساوي 3، وهذا يوافق طريقة الكوع وما رأيناه بأعيننا.
التجميع ليس تصنيفاً
Section titled “التجميع ليس تصنيفاً”لنجرّب على بيانات نعرف إجابتها: أزهار Iris، ونخفي أنواعها عن النموذج ثم نقارن:
import pandas as pdfrom sklearn.datasets import load_irisfrom sklearn.pipeline import make_pipeline
iris = load_iris()pipe = make_pipeline(StandardScaler(), KMeans(n_clusters=3, n_init=10, random_state=42))clusters = pipe.fit_predict(iris.data)
species = iris.target_names[iris.target]print(pd.crosstab(species, clusters, rownames=['species'], colnames=['cluster']))cluster 0 1 2speciessetosa 0 50 0versicolor 39 0 11virginica 14 0 36كل أزهار setosa الخمسين في مجموعة وحدها: نوع مميز فعلاً. لكن النوعين الآخرين اختلطا: 11 زهرة versicolor ذهبت مع virginica، و 14 زهرة virginica ذهبت مع versicolor. النموذج لم يخطئ، فهو لا يعرف الأنواع أصلاً. هو يجمع ما تتشابه قياساته، وهذان النوعان متقاربان في القياسات.
جرّب بنفسك
Section titled “جرّب بنفسك”- أضف عميلاً يزور 14 مرة وفاتورته 700 جنيه، وأعد التجميع بثلاث مجموعات ثم بأربع. أين ذهب في كل مرة؟
- اطبع
inertia_للأعداد من 1 إلى 6 على البيانات غير الموحّدة، ولاحظ أين يظهر الكوع ولماذا يخدعك. - احسب متوسط كل مجموعة بـ pandas: ضع البيانات في
DataFrame، وأضف عموداً للمجموعة، ثم استخدمgroupby('cluster').mean(). - تحدٍّ: جمّع أزهار Iris بدون توحيد المقاييس وقارن جدول
crosstabبالجدول الذي في الدرس.
أخطاء شائعة
Section titled “أخطاء شائعة”- نسيان توحيد المقاييس: الميزة ذات الأرقام الكبيرة ستتحكم في النتيجة وحدها. وحّد المقاييس قبل أي خوارزمية تعتمد على المسافة.
- اختيار K بأصغر inertia: القيمة تنخفض دائماً مع زيادة K. ابحث عن الكوع أو عن أعلى Silhouette.
- اعتبار أرقام المجموعات تسميات ثابتة: المجموعة 0 اليوم قد تصبح المجموعة 2 عند إعادة التدريب. اعتمد على خصائص المجموعة لا على رقمها.
- توحيد العميل الجديد بمحوِّل جديد: استخدم
scaler.transformبالمحوِّل الذي تعلّم من بيانات التدريب، ولا تستدعِfitمرة أخرى.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ميزتان: العمر (من 18 إلى 70) والدخل السنوي (من 20000 إلى 900000). ماذا يحدث لو طبّقت K-Means بلا توحيد للمقاييس؟
المسافة تتأثر بالأرقام الكبيرة، ففروق الدخل بالآلاف تطغى على فروق العمر بالعشرات.
سؤال
لماذا لا نختار عدد المجموعات الذي يعطي أصغر قيمة لـ inertia؟
لو جعلت كل عينة مجموعة وحدها لوصلت القيمة إلى الصفر بلا أي فائدة، لذلك نبحث عن الكوع.
الخلاصة
Section titled “الخلاصة”- K-Means تكرر خطوتين: إلحاق كل عينة بأقرب مركز، ثم نقل المركز إلى متوسط عيناته.
- وحّد المقاييس أولاً، وإلا تحكمت الميزة ذات الأرقام الكبيرة في النتيجة.
- اختر عدد المجموعات بطريقة الكوع أو بأعلى معامل Silhouette.
- النموذج يعطيك المجموعات، وتفسيرها وتسميتها مهمتك أنت.
- مبروك! أنهيت كورس تعلّم الآلة: من البيانات إلى نماذج تصنّف وتتوقع الأرقام وتكتشف المجموعات، مقيَّمة ومحسَّنة وجاهزة للاستخدام.