Machine Learning: KNN والتجميع

آخر تحديث: 2026-08-26

الطيور على أشكالها تقع — KNN تجد "الجيران الأكثر شبهًا بك" للتصنيف، بينما K-Means تجد "أكثر المجموعات ترابطًا" للتجزئة.

1. ما ستتعلمه


2. قصة حقيقية من مدير عمليات المستخدمين

(1) المشكلة: تسويق موحد لـ 500 ألف مستخدم غير فعال

منصة Bob تضم 500 ألف مستخدم، وكل مستخدم يتلقى نفس رسائل البريد الترويجية. النتيجة: المستخدمون عاليو القيمة يجدون العروض منخفضة الجودة، والمستخدمون الخاملون لا يفتحون البريد مطلقًا، ومعدل التحويل الإجمالي 2% فقط. المستخدمون متنوعون — الاستراتيجية الموحدة تهدر 80% من ميزانية التسويق.

(2) حل التجميع

يمكن للتجميع تقسيم المستخدمين تلقائيًا إلى مجموعات عالية القيمة/نشطة/خاملة/متخلفة، مما يتيح التسويق الدقيق.

PYTHON
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 المختلفة

PYTHON
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:

TEXT 📖 للعرض فقط
# Executed successfully
مقياس المسافة خصائص الصيغة الأفضل لـ
Euclidean مسافة خط مستقيم ميزات مستمرة، جميع الأبعاد مهمة بنفس القدر
Manhattan مسافة كتلة المدينة بيانات عالية الأبعاد، قيم متطرفة كثيرة
Cosine مسافة زاوية متجهات النص، الاتجاه أهم من المقدار
Minkowski مسافة معممة (p=2→Euclidean، p=1→Manhattan) ضبط مرن

(2) تأثير K

قيمة K حدود القرار المخاطرة
K=1 غير منتظمة للغاية فرط التجهيز (حساسة للضوضاء)
K=صغير (3-7) منحنية بشكل معتدل عادةً الأمثل
K=كبير (20+) خطية تقريبًا فرط التجهيز المنخفض

▶ مثال: تنبؤ شراء المستخدم بـ KNN

PYTHON
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:

TEXT 📖 للعرض فقط
# Executed successfully

4. تجميع K-Means

(1) سير عمل الخوارزمية

100%
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 + طريقة الكوع

PYTHON
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:

TEXT 📖 للعرض فقط
# Executed successfully

(2) درجة السيلويت

▶ مثال: اختيار K بدرجة السيلويت

PYTHON
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:

TEXT 📖 للعرض فقط
# Executed successfully
المقياس المعنى كيفية تحديد K الأمثل
القصور الذاتي (طريقة الكوع) مجموع المربعات داخل المجموعات عند نقطة الكوع
درجة السيلويت التماسك مقابل الفصل عند القيمة القصوى
إحصائية الفجوة المقارنة مع التوزيع العشوائي أول K تحت الحد الأعلى

5. تجميع كثافة DBSCAN

▶ مثال: DBSCAN تكتشف مجموعات بأشكال عشوائية

PYTHON
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:

TEXT 📖 للعرض فقط
# Executed successfully
البُعد K-Means DBSCAN
شكل المجموعات كروية أشكال عشوائية
قيمة K يجب ضبطها مسبقًا تُكتشف تلقائيًا
نقاط الضوضاء لا تُعالج تُسمى -1
الكثافة غير المتساوية أداؤها ضعيف لا تزال تُعالج بشكل جيد
سرعة الحساب سريعة معتدلة

6. تجزئة ملفات تعريف المستخدمين RFM لـ Bob

▶ مثال: مشروع تجميع RFM كامل

PYTHON
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:

TEXT 📖 للعرض فقط
# Executed successfully
الشريحة الحداثة التكرار القيمة النقدية استراتيجية التسويق
Champions منخفضة (نشط مؤخرًا) عالية عالية خدمة VIP، توصيات مميزة
Loyal متوسطة متوسطة-عالية متوسطة برامج الولاء، البيع المتقاطع
At Risk عالية (وقت طويل منذ الشراء) متوسطة متوسطة حزم إعادة التفاعل، عروض محدودة الوقت
Lost عالية جدًا منخفضة منخفضة تواصل منخفض التكلفة، بحث استقصائي

❓ أسئلة شائعة

س ما الفرق بين K في KNN و K في K-Means؟
ج K في KNN تعني "كم جارًا يصوت" — إنها خوارزمية تصنيف. K في K-Means تعني "كم مجموعة تتشكل" — إنها خوارزمية تجميع. مفهومان مختلفان تمامًا.
س هل يتطلب KNN التوحيد القياسي؟
ج بالتأكيد. KNN قائم على المسافة، لذا الميزات غير الموحدة (مثل الدخل 0-100,000 مقابل العمر 0-100) ستسيطر تمامًا على حساب المسافة. اقترنه دائمًا مع StandardScaler.
س كيف تختار K لـ K-Means؟
ج ادمج طريقة الكوع ودرجة السيلويت. تجد طريقة الكوع نقطة الانعطاف؛ تجد درجة السيلويت القيمة القصوى. قد يختلفان — اعتمد على المعقولية التجارية.
س كيف تختار eps لـ DBSCAN؟
ج ارسم رسم K-distance (مسافات k-أقرب جار مرتبة) واعثر على الكوع. تتراوح قيم eps النموذجية بين 0.1-1.0 (بعد التوحيد). عادةً ما يُضبط min_samples على ضعف عدد الأبعاد.
س كيف تقيّم نتائج التجميع؟
ج التجميع غير الخاضع للإشراف ليس لديه "حقيقة أرضية". استخدم درجة السيلويت (التقييم الرياضي) + القابلية للتفسير التجاري (التقييم المجال) للتحقق المزدوج. فقط المجموعات التي تترجم إلى مفاهيم تجارية ذات معنى تكون قيمة.
س لماذا تطبق تحويل log قبل تجميع RFM؟
ج التكرار والقيمة النقدية عادة ما يكون لهما توزيعات منحرفة لليمين — عدد قليل من المستخدمين عالي القيمة يمدون المسافات بين المجموعات. يجعل التحويل اللوغاريتمي التوزيع أكثر تماثلًا، مما يحسن أداء K-Means.

📖 ملخص

📝 تمارين

  1. أساسي (الصعوبة ⭐): استخدم KNN لتصنيف Iris. قارن دقة التحقق المتقاطع لـ K=1, 5, 10, 20 واعثر على K الأمثل. تلميح: Pipeline(StandardScaler + KNN) + cross_val_score.
  2. متوسط (الصعوبة ⭐⭐): ولّد بيانات من 4 مجموعات باستخدام make_blobs. اجمع باستخدام كل من K-Means و DBSCAN، ثم قارن درجات السيلويت والتعامل مع نقاط الضوضاء. تلميح: DBSCAN يُسمى الضوضاء بـ -1.
  3. تحدي (الصعوبة ⭐⭐⭐): نفّذ تجميع RFM لـ Bob — ولّد بيانات مستخدمين محاكاة، وطبق التحويل اللوغاريتمي + التوحيد، واختر K بطريقة الكوع، وشغّل K-Means، وسمّ كل شريحة بشكل مفيد (Champions/Loyal/At Risk/Lost)، وأخرج متوسط قيم RFM لكل شريحة. تلميح: راجع المثال الكامل في القسم 6.

← الدرس السابق: آلات المتجهات الداعمة SVM | الدرس التالي: هندسة الميزات →

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%