المعمل الأول: التجميع باستخدام K-Means على بيانات اصطناعية
أهداف المعمل
عند إتمام هذا المعمل، سيكون الطلاب قادرين على:
- فهم توليد البيانات الاصطناعية (Synthetic Data Generation): استيعاب كيفية توليد بيانات اصطناعية متعددة الأنماط (multi-modal) مناسبة للتجميع باستخدام أدوات مثل
sklearn.datasets.make_blobs. - تطبيق تجميع K-Means: تنفيذ وتشغيل خوارزمية تجميع K-Means بنجاح على مجموعة بيانات معطاة باستخدام وحدة
sklearn.cluster.KMeans. - تفسير معاملات K-Means: شرح الغرض من أهم معاملات K-Means وتأثيرها، بما في ذلك
n_clustersوinitوmax_iterوn_init. - تصور نتائج التجميع: توليد وتفسير الرسوم البيانية التي تعرض نقاط البيانات ملوّنة حسب المجموعة (cluster) المخصصة لها، وإبراز مواقع مراكز المجموعات (centroids) المحسوبة.
- تقييم أداء التجميع (درجة السلويت - Silhouette Score): حساب درجة السلويت (Silhouette Score) وتفسيرها كمقياس لتقييم جودة ووضوح المجموعات التي شكّلتها خوارزمية K-Means بشكل كمّي.
- تحديد العدد الأمثل للمجموعات (طريقة الكوع - Elbow Method): تطبيق طريقة الكوع (Elbow Method)، باستخدام مجموع مربعات الأخطاء داخل المجموعة (Within-Cluster Sum of Squares - WCSS)، لتقدير العدد المناسب من المجموعات (k) لمجموعة بيانات يكون فيها العدد الحقيقي للمجموعات غير معروف.
- التمييز بين المجموعات الحقيقية والمجموعات المتنبأ بها: المقارنة بين البنية الحقيقية الأساسية للمجموعات (من توليد البيانات) والمجموعات التي حددتها خوارزمية K-Means.
الخطوة 1: استيراد المكتبات المطلوبة
أولًا، لنستورد جميع المكتبات اللازمة لتحليل التجميع الخاص بنا.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
# Set style for better-looking plots
plt.style.use('default')
plt.rcParams['figure.figsize'] = (10, 6)
print("Libraries imported successfully!")Libraries imported successfully!الخطوة 2: توليد بيانات اصطناعية
سنستخدم دالة make_blobs من scikit-learn لإنشاء مجموعة بيانات مناسبة للتجميع. تُنشئ هذه الدالة تكتلات غاوسية متجانسة الخواص (isotropic Gaussian blobs) مناسبة للتجميع.
المعاملات الأساسية:
n_samples: عدد نقاط البياناتn_features: عدد الخصائص (الأبعاد)centers: عدد المجموعات المميزة المراد توليدهاcluster_std: الانحراف المعياري للمجموعاتrandom_state: من أجل إمكانية إعادة إنتاج النتائج
# Parameters for synthetic data generation
n_samples = 300 # Number of data points
n_features = 2 # Number of features (dimensions)
n_centers = 4 # Number of distinct clusters we want to generate
cluster_std = 0.8 # Standard deviation of the clusters
random_state = 42 # For reproducibility of the synthetic data
# Generate synthetic data
# X will be our features, y_true will be the true cluster labels
X, y_true = make_blobs(n_samples=n_samples,
n_features=n_features,
centers=n_centers,
cluster_std=cluster_std,
random_state=random_state)
print(f"Shape of generated data (X): {X.shape}")
print(f"Shape of true labels (y_true): {y_true.shape}")
print(f"True cluster labels: {np.unique(y_true)}")Shape of generated data (X): (300, 2)
Shape of true labels (y_true): (300,)
True cluster labels: [0 1 2 3]الخطوة 3: تصور البيانات الاصطناعية الأصلية
لنقم بتصور البيانات الاصطناعية المولّدة مع تسميات المجموعات الحقيقية (true cluster labels).
plt.figure(figsize=(10, 8))
plt.scatter(X[:, 0], X[:, 1], c=y_true, cmap='viridis', s=50, alpha=0.7)
plt.title("Synthetic Data Generated for Clustering (True Labels)", fontsize=14)
plt.xlabel("Feature 1", fontsize=12)
plt.ylabel("Feature 2", fontsize=12)
plt.colorbar(label="True Cluster ID")
plt.grid(True, alpha=0.3)
plt.show()
print("This plot shows the 'ground truth' clustering that we generated.")
print("Each color represents a different true cluster.")This plot shows the 'ground truth' clustering that we generated.
Each color represents a different true cluster.الخطوة 4: تطبيق تجميع K-Means
الآن سنطبّق خوارزمية تجميع K-Means على بياناتنا الاصطناعية.
في KMeans الخاصة بـ scikit-learn، عندما تكون قيمة n_init أكبر من 1، تقوم الخوارزمية بالتشغيل عدة مرات باستخدام قيم ابتدائية عشوائية مختلفة. وبعد انتهاء جميع عمليات التشغيل، تختار أفضل نتيجة بناءً على أقل قيمة لـ"العطالة" (inertia).
ما هي العطالة (inertia)؟
-
العطالة هي مجموع مربعات المسافات بين كل نقطة بيانات ومركز المجموعة (centroid) المخصصة لها. رياضيًا:
inertia = Σ (distance(point, assigned_centroid))²لجميع النقاط. كيف يتم اختيار أفضل نتيجة؟ -
بعد انتهاء جميع عمليات التشغيل الـ
n_init، يتم اختيار الحل الذي يملك أقل قيمة عطالة (أي المجموعات الأكثر تماسكًا وإحكامًا) كنتيجة نهائية. الخلاصة:
أفضل نتيجة هي التي تملك أصغر مجموع لمربعات المسافات بين النقاط ومراكز مجموعاتها.
معاملات K-Means الأساسية:
n_clusters: عدد المجموعات المراد إيجادهاinit: طريقة التهيئة الابتدائية (يُنصح باستخدام 'k-means++')max_iter: الحد الأقصى لعدد التكرارات (iterations) للوصول إلى التقارب (convergence)n_init: عدد مرات التهيئة العشوائيةrandom_state: من أجل إمكانية إعادة إنتاج النتائج
# Set the number of clusters for K-Means
k = 4 # We'll try to find 4 clusters, matching the number we generated
# Initialize K-Means clustering
kmeans = KMeans(n_clusters=k,
init='k-means++', # Smart initialization to speed up convergence
#The first centroid is chosen randomly from the data points.
# Each subsequent centroid is chosen from the remaining points, with a probability proportional to its distance squared from the nearest existing centroid.
# This ensures new centroids are far from existing ones.
max_iter=300, # Maximum number of iterations for a single run
n_init=10, # Number of times the algorithm will run with different seeds
random_state=random_state
)
# Fit the K-Means model to our data
kmeans.fit(X)
# Get the cluster assignments for each data point
labels = kmeans.labels_
# Get the coordinates of the cluster centroids
centroids = kmeans.cluster_centers_
print(f"K-Means found {k} clusters.")
print(f"First 10 cluster assignments: {labels[:10]}")
print(f"\nCluster centroids:")
for i, centroid in enumerate(centroids):
print(f" Cluster {i}: ({centroid[0]:.3f}, {centroid[1]:.3f})")
print(f"\nWithin-cluster sum of squares (WCSS): {kmeans.inertia_:.3f}")K-Means found 4 clusters.
First 10 cluster assignments: [3 3 0 1 3 1 2 1 0 2]
Cluster centroids:
Cluster 0: (-2.637, 8.986)
Cluster 1: (-6.842, -6.840)
Cluster 2: (4.703, 2.028)
Cluster 3: (-8.833, 7.218)
Within-cluster sum of squares (WCSS): 362.472الخطوة 5: تصور نتائج تجميع K-Means
لنقم بتصور نتائج تجميع K-Means، بحيث نعرض كلًا من نقاط البيانات ملوّنة حسب المجموعة المخصصة لها، ومراكز المجموعات (centroids) المحسوبة.
plt.figure(figsize=(10, 8))
# Plot the data points, colored by their assigned cluster
plt.scatter(X[:, 0], X[:, 1],
c=labels, # Color based on assigned cluster
cmap='plasma', # Colormap for the clusters
s=50, # Size of the point
alpha=0.7,
label='Data points'
)
# Plot the cluster centroids
plt.scatter(centroids[:, 0], # X-coordinates of centroids
centroids[:, 1], # Y-coordinates of centroids
s=200,
marker='X',
c='red',
edgecolor='black',
linewidth=2,
label='Centroids'
)
plt.title(f"K-Means Clustering Results (k={k})", fontsize=14)
plt.xlabel("Feature 1", fontsize=12)
plt.ylabel("Feature 2", fontsize=12)
plt.colorbar(label="Assigned Cluster ID")
plt.legend(fontsize=12)
plt.grid(True, alpha=0.3)
plt.show()
print("The red 'X' markers show the computed cluster centroids.")
print("Compare this with the true clusters shown in the previous plot.")The red 'X' markers show the computed cluster centroids.
Compare this with the true clusters shown in the previous plot.الخطوة 6: تقييم أداء التجميع باستخدام درجة السلويت (Silhouette Score)
تقيس درجة السلويت (Silhouette Score) مدى تشابه عنصر ما مع المجموعة التي ينتمي إليها مقارنةً بالمجموعات الأخرى.
كيف يتم حسابها؟ لكل نقطة:
- يُحسب متوسط المسافة إلى جميع النقاط الأخرى في نفس المجموعة (a).
- يُحسب متوسط المسافة إلى جميع النقاط في أقرب مجموعة مختلفة (b).
- درجة السلويت للنقطة هي:
(b - a) / max(a, b) - قيمة
silhouette_scoreالإجمالية هي متوسط درجات جميع النقاط.
التفسير:
- درجة قريبة من +1: تجميع جيد (بعيد عن المجموعات المجاورة)
- درجة قريبة من 0: على حدود الفصل بين المجموعات أو قريبة جدًا منها
- درجة قريبة من -1: على الأرجح تم تخصيص النقطة للمجموعة الخاطئة
# Calculate silhouette score
if len(np.unique(labels)) > 1 and len(X) > 1:
silhouette_avg = silhouette_score(X, labels)
print(f"Silhouette Score: {silhouette_avg:.3f}")
# Interpret the score
if silhouette_avg > 0.7:
interpretation = "Excellent clustering quality"
elif silhouette_avg > 0.5:
interpretation = "Good clustering quality"
elif silhouette_avg > 0.3:
interpretation = "Fair clustering quality"
else:
interpretation = "Poor clustering quality"
print(f"Interpretation: {interpretation}")
else:
print("Cannot compute silhouette score (less than 2 clusters or samples).")
# Additional cluster statistics
unique_labels, counts = np.unique(labels, return_counts=True)
print(f"\nCluster sizes:")
# --------------------------------------------------------------
# zip() returns a list of tuples, so we need to unpack each tuple to get the values
# unique_labels = [0, 1, 2]
# counts = [50, 60, 40]
# list(zip(unique_labels, counts)) => [(0, 50), (1, 60), (2, 40)]
# --------------------------------------------------------------
for label, count in zip(unique_labels, counts):
print(f" Cluster {label}: {count} points ({count / len(X) * 100:.1f}%)")Silhouette Score: 0.834
Interpretation: Excellent clustering quality
Cluster sizes:
Cluster 0: 75 points (25.0%)
Cluster 1: 75 points (25.0%)
Cluster 2: 75 points (25.0%)
Cluster 3: 75 points (25.0%)الخطوة 7: تحديد العدد الأمثل للمجموعات (طريقة الكوع - Elbow Method)
تساعد طريقة الكوع (Elbow Method) في إيجاد العدد الأمثل للمجموعات عندما يكون غير معروف. نقوم برسم مجموع مربعات الأخطاء داخل المجموعة (WCSS) مقابل عدد المجموعات، ونبحث عن نقطة "الكوع" حيث يتباطأ معدل الانخفاض بشكل ملحوظ.
الصيغة الرياضية:
WCSS = Σ (for each cluster) Σ (for each point in cluster) (distance(point, centroid))²

# Test different numbers of clusters
max_k = 10
wcss = []
silhouette_scores = []
print("Computing WCSS and Silhouette Scores for different k values...")
for i in range(1, max_k + 1):
kmeans_elbow = KMeans(n_clusters=i,
init='k-means++',
max_iter=300,
n_init=10,
random_state=random_state)
kmeans_elbow.fit(X)
wcss.append(kmeans_elbow.inertia_) # inertia_ is the WCSS
# Calculate silhouette score for k > 1
if i > 1:
sil_score = silhouette_score(X, kmeans_elbow.labels_)
silhouette_scores.append(sil_score)
else:
silhouette_scores.append(0) # No silhouette score for k=1
print("Done!")Computing WCSS and Silhouette Scores for different k values...
Done!# Plot the Elbow Method results
fig, (ax1, ax2) = plt.subplots(1, # Rows of plots
2, # Columns of plots
figsize=(15, 6))
# WCSS Plot (Elbow Method)
ax1.plot(range(1, max_k + 1), wcss, marker='o', linestyle='--', linewidth=2, markersize=8)
ax1.set_title('Elbow Method for Optimal K', fontsize=14)
ax1.set_xlabel('Number of Clusters (K)', fontsize=12)
ax1.set_ylabel('Within-Cluster Sum of Squares (WCSS)', fontsize=12)
ax1.grid(True, alpha=0.3)
ax1.axvline(x=4, color='red', linestyle=':', alpha=0.7, label='True k=4')
ax1.legend()
# Silhouette Score Plot
ax2.plot(range(1, max_k + 1), silhouette_scores, marker='s', linestyle='--',
linewidth=2, markersize=8, color='green')
ax2.set_title('Silhouette Score vs Number of Clusters', fontsize=14)
ax2.set_xlabel('Number of Clusters (K)', fontsize=12)
ax2.set_ylabel('Silhouette Score', fontsize=12)
ax2.grid(True, alpha=0.3)
ax2.axvline(x=4, color='red', linestyle=':', alpha=0.7, label='True k=4')
ax2.legend()
plt.tight_layout()
plt.show()
print("\nAnalysis:")
print("- Look for the 'elbow' point in the WCSS plot (left) to estimate optimal K.")
print("- Higher silhouette scores (right) indicate better clustering quality.")
# Find the k with the highest silhouette score
best_k_silhouette = np.argmax(silhouette_scores[1:]) + 2 # +2 because we start from k=2
print(f"\nK with highest silhouette score: {best_k_silhouette} (score: {max(silhouette_scores[1:]):.3f})")
Analysis:
- Look for the 'elbow' point in the WCSS plot (left) to estimate optimal K.
- Higher silhouette scores (right) indicate better clustering quality.
K with highest silhouette score: 4 (score: 0.834)الخطوة 8: مقارنة المجموعات الحقيقية بالمجموعات المتنبأ بها
لننشئ مقارنة جنبًا إلى جنب بين المجموعات الحقيقية (من توليد البيانات) والمجموعات التي وجدتها خوارزمية K-Means.
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))
# True clusters
scatter1 = ax1.scatter(X[:, 0], X[:, 1], c=y_true, cmap='viridis', s=50, alpha=0.7)
ax1.set_title('True Clusters (Ground Truth)', fontsize=14)
ax1.set_xlabel('Feature 1', fontsize=12)
ax1.set_ylabel('Feature 2', fontsize=12)
ax1.grid(True, alpha=0.3)
plt.colorbar(scatter1, ax=ax1, label='True Cluster ID')
# K-Means predicted clusters
scatter2 = ax2.scatter(X[:, 0], X[:, 1], c=labels, cmap='plasma', s=50, alpha=0.7)
ax2.scatter(centroids[:, 0], centroids[:, 1],
s=200, marker='X', c='red', edgecolor='black',
linewidth=2, label='Centroids')
ax2.set_title('K-Means Predicted Clusters', fontsize=14)
ax2.set_xlabel('Feature 1', fontsize=12)
ax2.set_ylabel('Feature 2', fontsize=12)
ax2.grid(True, alpha=0.3)
ax2.legend()
plt.colorbar(scatter2, ax=ax2, label='Predicted Cluster ID')
plt.tight_layout()
plt.show()
print("Comparison Analysis:")
print("- Left plot: True cluster structure from data generation")
print("- Right plot: Clusters identified by K-Means algorithm")
print("- Note: Cluster labels may be different (e.g., true cluster 0 might be predicted as cluster 2)")
print("- What matters is whether the algorithm correctly groups similar points together")Comparison Analysis:
- Left plot: True cluster structure from data generation
- Right plot: Clusters identified by K-Means algorithm
- Note: Cluster labels may be different (e.g., true cluster 0 might be predicted as cluster 2)
- What matters is whether the algorithm correctly groups similar points togetherالخطوة 9: التلخيص والتحليل
لنلخّص النتائج التي توصلنا إليها ونقدم تحليلًا شاملاً لنتائج التجميع.
print("=" * 60)
print("LAB SUMMARY AND ANALYSIS")
print("=" * 60)
print(f"\n1. DATA GENERATION:")
print(f" - Generated {n_samples} data points with {n_features} features")
print(f" - True number of clusters: {n_centers}")
print(f" - Cluster standard deviation: {cluster_std}")
print(f"\n2. K-MEANS CLUSTERING:")
print(f" - Number of clusters used: {k}")
print(f" - Initialization method: k-means++")
print(f" - WCSS (inertia): {kmeans.inertia_:.3f}")
print(f"\n3. PERFORMANCE EVALUATION:")
if 'silhouette_avg' in locals():
print(f" - Silhouette Score: {silhouette_avg:.3f} ({interpretation})")
print(f" - Best k by silhouette score: {best_k_silhouette}")
print(f"\n4. CLUSTER ANALYSIS:")
for i, (label, count) in enumerate(zip(unique_labels, counts)):
centroid = centroids[label]
print(
f" - Cluster {label}: {count} points ({count / len(X) * 100:.1f}%) at ({centroid[0]:.2f}, {centroid[1]:.2f})")
print(f"\n5. KEY OBSERVATIONS:")
print(f" - K-Means successfully identified the cluster structure")
print(f" - The elbow method suggests optimal k around the true value")
print(f" - Silhouette score indicates {'good' if silhouette_avg > 0.5 else 'fair'} clustering quality")
print(f" - Cluster assignments may differ from true labels but groupings are similar")
print("\n" + "=" * 60)============================================================
LAB SUMMARY AND ANALYSIS
============================================================
1. DATA GENERATION:
- Generated 300 data points with 2 features
- True number of clusters: 4
- Cluster standard deviation: 0.8
2. K-MEANS CLUSTERING:
- Number of clusters used: 4
- Initialization method: k-means++
- WCSS (inertia): 362.472
3. PERFORMANCE EVALUATION:
- Silhouette Score: 0.834 (Excellent clustering quality)
- Best k by silhouette score: 4
4. CLUSTER ANALYSIS:
- Cluster 0: 75 points (25.0%) at (-2.64, 8.99)
- Cluster 1: 75 points (25.0%) at (-6.84, -6.84)
- Cluster 2: 75 points (25.0%) at (4.70, 2.03)
- Cluster 3: 75 points (25.0%) at (-8.83, 7.22)
5. KEY OBSERVATIONS:
- K-Means successfully identified the cluster structure
- The elbow method suggests optimal k around the true value
- Silhouette score indicates good clustering quality
- Cluster assignments may differ from true labels but groupings are similar
============================================================أسئلة تدريبية
أجب عن الأسئلة التالية بناءً على نتائجك:
-
فهم توليد البيانات:
- ما الذي يتحكم فيه المعامل
cluster_stdفيmake_blobs؟ - كيف سيؤثر زيادة هذا المعامل على صعوبة عملية التجميع؟
- ما الذي يتحكم فيه المعامل
-
معاملات K-Means:
- لماذا يُفضَّل استخدام
init='k-means++'على التهيئة العشوائية؟ - ما الغرض من المعامل
n_init؟
- لماذا يُفضَّل استخدام
-
تقييم الأداء:
- ماذا تشير درجة السلويت (silhouette score) التي حصلت عليها إلى جودة التجميع؟
- كيف تفسّر درجة سلويت قدرها 0.2 مقارنة بدرجة قدرها 0.8؟
-
طريقة الكوع (Elbow Method):
- بالنظر إلى رسم WCSS الخاص بك، أين تلاحظ نقطة "الكوع"؟
- هل تتطابق هذه النقطة مع العدد الحقيقي للمجموعات؟
-
تحليل المقارنة:
- إلى أي مدى نجحت خوارزمية K-Means في استرجاع البنية الحقيقية للمجموعات؟
- هل هناك أي نقاط مصنَّفة تصنيفًا خاطئًا؟ ولماذا قد يحدث ذلك؟
تمارين إضافية:
جرّب تعديل المعاملات ولاحظ تأثيرها:
- غيّر
cluster_stdإلى 1.5 وأعد تشغيل التحليل - جرّب قيمًا مختلفة لـ
k(مثل 3 أو 5) وقارن النتائج - جرّب قيمًا مختلفة لـ
n_centersعند توليد البيانات
الخلاصة
استعرض هذا المعمل سير العمل الكامل لتجميع K-Means:
- توليد البيانات: إنشاء بيانات اصطناعية متعددة الأنماط مناسبة للتجميع
- تطبيق الخوارزمية: تطبيق K-Means باستخدام المعاملات المناسبة
- التصور: إنشاء رسوم بيانية ذات دلالة لفهم البيانات والنتائج
- التقييم: استخدام درجة السلويت لتقييم جودة التجميع
- التحسين: تطبيق طريقة الكوع لإيجاد العدد الأمثل للمجموعات
- التحقق: مقارنة المجموعات المتنبأ بها بالبنية الحقيقية (ground truth)
أهم النقاط المستفادة:
- تعمل خوارزمية K-Means بشكل جيد عندما تكون المجموعات كروية الشكل ومنفصلة بوضوح عن بعضها
- اختيار قيمة
kأمر بالغ الأهمية، ويمكن الاسترشاد فيه بالمعرفة المجالية (domain knowledge) أو بطرق مثل طريقة الكوع - تساعد مقاييس التقييم مثل درجة السلويت في تقييم جودة التجميع بشكل موضوعي
- التصور البصري أساسي لفهم كل من البيانات والنتائج
الخطوات التالية: في التطبيقات الواقعية، ستعمل عادةً مع بيانات ذات أبعاد أعلى يصبح فيها التصور البصري أمرًا صعبًا، مما يجعل مقاييس التقييم أكثر أهمية.