Machine Learning: KNN والتجميع
آخر تحديث: 2026-08-26
الطيور على أشكالها تقع — KNN تجد "الجيران الأكثر شبهًا بك" للتصنيف، بينما K-Means تجد "أكثر المجموعات ترابطًا" للتجزئة.
1. ما ستتعلمه
- تصنيف KNN: مقاييس المسافة (Euclidean/Manhattan/Cosine)، اختيار K، آلية التصويت
- تجميع K-Means: سير عمل الخوارزمية، اختيار K (طريقة الكوع/درجة السيلويت)، معايير التقارب
- تجميع كثافة DBSCAN: النقاط الأساسية/النقاط الحدودية/نقاط الضوضاء، ضبط eps و min_samples
- التجميع الهرمي: AgglomerativeClustering والـ dendrograms
- تجزئة ملفات تعريف المستخدمين لـ Bob: تجميع RFM لتحديد مجموعات المستخدمين عالية القيمة/الخاملة/المتخلفة
2. قصة حقيقية من مدير عمليات المستخدمين
(1) المشكلة: تسويق موحد لـ 500 ألف مستخدم غير فعال
منصة Bob تضم 500 ألف مستخدم، وكل مستخدم يتلقى نفس رسائل البريد الترويجية. النتيجة: المستخدمون عاليو القيمة يجدون العروض منخفضة الجودة، والمستخدمون الخاملون لا يفتحون البريد مطلقًا، ومعدل التحويل الإجمالي 2% فقط. المستخدمون متنوعون — الاستراتيجية الموحدة تهدر 80% من ميزانية التسويق.
(2) حل التجميع
يمكن للتجميع تقسيم المستخدمين تلقائيًا إلى مجموعات عالية القيمة/نشطة/خاملة/متخلفة، مما يتيح التسويق الدقيق.
from sklearn.cluster import KMeans
# تجميع RFM: تجميع المستخدمين حسب Recency, Frequency, Monetary
rfm = df[["recency", "frequency", "monetary"]]
kmeans = KMeans(n_clusters=4, random_state=42)
df["segment"] = kmeans.fit_predict(rfm)
for i in range(4):
segment = df[df["segment"] == i]
print(f"Segment {i}: {len(segment)} users, "
f"Avg Monetary={segment['monetary'].mean():.0f} USD")
(3) النتيجة: التسويق الدقيق يعزز التحويل 4 مرات
بعد أن استخدم Bob التجميع لتقسيم المستخدمين إلى 4 شرائح، حصلت المجموعة عالية القيمة على عروض مميزة، والمجموعة الخاملة حصلت على حزم إعادة تفاعل، وقفز معدل التحويل الإجمالي من 2% إلى 8% — تحسن 3 أضعاف في عائد التسويق.
3. تصنيف KNN
(1) مقاييس المسافة واختيار K
▶ مثال: تصنيف KNN + مقارنة قيم K المختلفة
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np
X, y = load_iris(return_X_y=True)
# مقارنة قيم K المختلفة
for k in [1, 3, 5, 7, 11, 21]:
pipe = Pipeline([
("scaler", StandardScaler()),
("knn", KNeighborsClassifier(n_neighbors=k)),
])
scores = cross_val_score(pipe, X, y, cv=5, scoring="accuracy")
print(f"K={k:2d}: Accuracy={scores.mean():.3f} +/- {scores.std():.3f}")
Output:
# Executed successfully
| مقياس المسافة | خصائص الصيغة | الأفضل لـ |
|---|---|---|
| Euclidean | مسافة خط مستقيم | ميزات مستمرة، جميع الأبعاد مهمة بنفس القدر |
| Manhattan | مسافة كتلة المدينة | بيانات عالية الأبعاد، قيم متطرفة كثيرة |
| Cosine | مسافة زاوية | متجهات النص، الاتجاه أهم من المقدار |
| Minkowski | مسافة معممة (p=2→Euclidean، p=1→Manhattan) | ضبط مرن |
(2) تأثير K
| قيمة K | حدود القرار | المخاطرة |
|---|---|---|
| K=1 | غير منتظمة للغاية | فرط التجهيز (حساسة للضوضاء) |
| K=صغير (3-7) | منحنية بشكل معتدل | عادةً الأمثل |
| K=كبير (20+) | خطية تقريبًا | فرط التجهيز المنخفض |
▶ مثال: تنبؤ شراء المستخدم بـ KNN
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
import numpy as np
rng = np.random.default_rng(42)
n = 1000
X = np.column_stack([
rng.uniform(0, 100, n), # وقت_التصفح
rng.uniform(0, 50, n), # قيمة_السلة_usd
rng.integers(1, 30, n), # الصفحات_المعروضة
rng.integers(0, 10, n), # المشتريات_السابقة
])
y = (X[:, 1] * 0.05 + X[:, 3] * 0.3 + rng.normal(0, 0.5, n) > 2).astype(int)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
pipe = Pipeline([
("scaler", StandardScaler()),
("knn", KNeighborsClassifier(n_neighbors=7, weights="distance")),
])
pipe.fit(X_train, y_train)
print(classification_report(y_test, pipe.predict(X_test)))
Output:
# Executed successfully
4. تجميع K-Means
(1) سير عمل الخوارزمية
sequenceDiagram
participant Init as تهيئة K مراكز
participant Assign as تعيين النقاط
participant Update as تحديث المراكز
participant Check as تقارب؟
Init->>Assign: مواقع K مراكز عشوائية
loop حتى التقارب
Assign->>Update: كل نقطة → أقرب مركز
Update->>Check: المراكز = متوسط النقاط المعينة
Check->>Assign: لم يتقارب (المراكز تحركت)
end
Check-->>Done: تقارب! أعد المجموعات
▶ مثال: تجميع K-Means + طريقة الكوع
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
# توليد 4 مجموعات طبيعية
X = np.vstack([
rng.normal([20, 500], [3, 50], (200, 2)), # قيمة عالية
rng.normal([50, 200], [5, 30], (300, 2)), # متوسطة
rng.normal([80, 50], [8, 20], (350, 2)), # منخفضة
rng.normal([10, 800], [2, 40], (150, 2)), # VIP
])
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# طريقة الكوع
inertias = []
K_range = range(2, 10)
for k in K_range:
km = KMeans(n_clusters=k, random_state=42, n_init=10)
km.fit(X_scaled)
inertias.append(km.inertia_)
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(K_range, inertias, "bo-", linewidth=2)
ax.set_xlabel("Number of Clusters (K)")
ax.set_ylabel("Inertia (Within-Cluster Sum of Squares)")
ax.set_title("Elbow Method for Optimal K")
ax.axvline(x=4, color="red", linestyle="--", label="Elbow at K=4")
ax.legend()
plt.tight_layout()
plt.savefig("elbow_method.png", dpi=150)
Output:
# Executed successfully
(2) درجة السيلويت
▶ مثال: اختيار K بدرجة السيلويت
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.preprocessing import StandardScaler
import numpy as np
# باستخدام نفس X_scaled من المثال السابق
for k in range(2, 8):
km = KMeans(n_clusters=k, random_state=42, n_init=10)
labels = km.fit_predict(X_scaled)
score = silhouette_score(X_scaled, labels)
print(f"K={k}: Silhouette Score={score:.3f}")
Output:
# Executed successfully
| المقياس | المعنى | كيفية تحديد K الأمثل |
|---|---|---|
| القصور الذاتي (طريقة الكوع) | مجموع المربعات داخل المجموعات | عند نقطة الكوع |
| درجة السيلويت | التماسك مقابل الفصل | عند القيمة القصوى |
| إحصائية الفجوة | المقارنة مع التوزيع العشوائي | أول K تحت الحد الأعلى |
5. تجميع كثافة DBSCAN
▶ مثال: DBSCAN تكتشف مجموعات بأشكال عشوائية
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score
import numpy as np
rng = np.random.default_rng(42)
# إنشاء مجموعات غير كروية (دائرتان)
from sklearn.datasets import make_circles
X, _ = make_circles(n_samples=500, factor=0.5, noise=0.05, random_state=42)
X_scaled = StandardScaler().fit_transform(X)
# K-Means مقابل DBSCAN
km = KMeans(n_clusters=2, random_state=42, n_init=10)
km_labels = km.fit_predict(X_scaled)
db = DBSCAN(eps=0.3, min_samples=10)
db_labels = db.fit_predict(X_scaled)
n_clusters = len(set(db_labels)) - (1 if -1 in db_labels else 0)
n_noise = list(db_labels).count(-1)
print(f"K-Means Silhouette: {silhouette_score(X_scaled, km_labels):.3f}")
print(f"DBSCAN Clusters: {n_clusters}, Noise points: {n_noise}")
if n_clusters > 1:
db_valid = db_labels != -1
print(f"DBSCAN Silhouette: {silhouette_score(X_scaled[db_valid], db_labels[db_valid]):.3f}")
Output:
# Executed successfully
| البُعد | K-Means | DBSCAN |
|---|---|---|
| شكل المجموعات | كروية | أشكال عشوائية |
| قيمة K | يجب ضبطها مسبقًا | تُكتشف تلقائيًا |
| نقاط الضوضاء | لا تُعالج | تُسمى -1 |
| الكثافة غير المتساوية | أداؤها ضعيف | لا تزال تُعالج بشكل جيد |
| سرعة الحساب | سريعة | معتدلة |
6. تجزئة ملفات تعريف المستخدمين RFM لـ Bob
▶ مثال: مشروع تجميع RFM كامل
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
import pandas as pd
import numpy as np
rng = np.random.default_rng(42)
n = 5000
df = pd.DataFrame({
"user_id": range(10001, 10001 + n),
"recency_days": rng.integers(1, 365, n),
"frequency": rng.integers(1, 50, n),
"monetary_usd": rng.exponential(500, n),
})
# ميزات RFM
rfm = df[["recency_days", "frequency", "monetary_usd"]]
# تحويل لوغاريتمي للميزات المنحرفة
rfm_log = rfm.copy()
rfm_log["frequency"] = np.log1p(rfm_log["frequency"])
rfm_log["monetary_usd"] = np.log1p(rfm_log["monetary_usd"])
# توحيد القياس
scaler = StandardScaler()
rfm_scaled = scaler.fit_transform(rfm_log)
# K-Means مع K=4
km = KMeans(n_clusters=4, random_state=42, n_init=10)
df["segment"] = km.fit_predict(rfm_scaled)
# تحليل المجموعات
segment_summary = df.groupby("segment").agg({
"recency_days": "mean",
"frequency": "mean",
"monetary_usd": "mean",
"user_id": "count",
}).round(1)
segment_summary.columns = ["avg_recency", "avg_frequency", "avg_monetary", "count"]
segment_summary = segment_summary.sort_values("avg_monetary", ascending=False)
# تسمية المجموعات
labels = ["Champions", "Loyal", "At Risk", "Lost"]
for idx, (_, row) in enumerate(segment_summary.iterrows()):
print(f"{labels[idx]:10s}: {int(row['count']):5d} users, "
f"Recency={row['avg_recency']:.0f}d, "
f"Freq={row['avg_frequency']:.1f}, "
f"Monetary={row['avg_monetary']:.0f} USD")
Output:
# Executed successfully
| الشريحة | الحداثة | التكرار | القيمة النقدية | استراتيجية التسويق |
|---|---|---|---|---|
| Champions | منخفضة (نشط مؤخرًا) | عالية | عالية | خدمة VIP، توصيات مميزة |
| Loyal | متوسطة | متوسطة-عالية | متوسطة | برامج الولاء، البيع المتقاطع |
| At Risk | عالية (وقت طويل منذ الشراء) | متوسطة | متوسطة | حزم إعادة التفاعل، عروض محدودة الوقت |
| Lost | عالية جدًا | منخفضة | منخفضة | تواصل منخفض التكلفة، بحث استقصائي |
❓ أسئلة شائعة
📖 ملخص
- تصنيف KNN: اختر K من الجيران الأقرب حسب مقياس المسافة وصوّت؛ K صغير يُفرط في التجهيز، K كبير يُقلل في التجهيز؛ التوحيد إلزامي
- تجميع K-Means: تعيين النقاط وتحديث المراكز بشكل تكراري؛ استخدم طريقة الكوع ودرجة السيلويت لاختيار K؛ يعمل فقط للمجموعات الكروية
- تجميع كثافة DBSCAN: يكتشف تلقائيًا عدد المجموعات، يُسمى الضوضاء، يتعامل مع الأشكال العشوائية، لكنه حساس للمعاملات
- تجزئة المستخدمين RFM تطبيق كلاسيكي للتجميع في التجارة الإلكترونية: Recency + Frequency + Monetary → ملفات تعريف المستخدمين
- طبق التحويلات اللوغاريتمية للتعامل مع التوزيعات المنحرفة قبل التجميع؛ وحّد لإزالة فروق المقاييس
- تقييم المجموعات = مقاييس رياضية (درجة السيلويت) + قابلية تفسير تجارية — كلاهما ضروري
📝 تمارين
- أساسي (الصعوبة ⭐): استخدم KNN لتصنيف Iris. قارن دقة التحقق المتقاطع لـ K=1, 5, 10, 20 واعثر على K الأمثل. تلميح: Pipeline(StandardScaler + KNN) + cross_val_score.
- متوسط (الصعوبة ⭐⭐): ولّد بيانات من 4 مجموعات باستخدام make_blobs. اجمع باستخدام كل من K-Means و DBSCAN، ثم قارن درجات السيلويت والتعامل مع نقاط الضوضاء. تلميح: DBSCAN يُسمى الضوضاء بـ -1.
- تحدي (الصعوبة ⭐⭐⭐): نفّذ تجميع RFM لـ Bob — ولّد بيانات مستخدمين محاكاة، وطبق التحويل اللوغاريتمي + التوحيد، واختر K بطريقة الكوع، وشغّل K-Means، وسمّ كل شريحة بشكل مفيد (Champions/Loyal/At Risk/Lost)، وأخرج متوسط قيم RFM لكل شريحة. تلميح: راجع المثال الكامل في القسم 6.
← الدرس السابق: آلات المتجهات الداعمة SVM | الدرس التالي: هندسة الميزات →