هندسة السمات
درس 6 من 8
6هندسة السمات
الصورة الكبيرةالنموذج لا يرى إلا الأرقام التي تعطيه إياها. تحويل المدن والتواريخ والنصوص إلى أرقام ذات معنى، وتوحيد مقاييسها، غالبًا يحسّن النتيجة أكثر من تغيير الخوارزمية. وكل سمة جديدة يجب أن تثبت أنها تستحق مكانها.
ماذا تشاهد في الرسم: ثلاث سمات حقيقية و120 صفًّا فقط. كلما أضفنا سمات ضجيج نزلت دقة التحقّق: 89.2% ثم 87.5% مع عشرين، ثم 63.3% مع مئتين. كل سمة بلا معنى فرصة جديدة ليحفظ النموذج نمطًا وهميًا.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
النموذج لا يفهم كلمة «الرياض» ولا تاريخًا مكتوبًا، يفهم أرقامًا فقط. هندسة السمات أن تترجم المعلومة إلى أرقام تحمل المعنى: المدينة إلى أعمدة نعم أو لا، والتاريخ إلى «يوم الأسبوع» و«هل هو عطلة». سمة جيدة واحدة قد تفيد أكثر من نموذج أعقد.
الترميز one-hot للفئات القليلة، وtarget encoding للكثيرة مع الحذر من التسريب، والقياس StandardScaler للنماذج الحساسة للمقياس. تقيس أثر كل سمة جديدة على مجموعة التحقق، وتحذف ما لا يحسّن الرقم لأنه ضوضاء تزيد الإفراط في التعلّم.
قبل أن تبدأ
✂️التقسيم والتسريب
تعرف لماذا تُحسب كل معالجة على التدريب وحده، وتستخدم make_pipeline كما في درس التقسيم.
🌲الأشجار
تعرف من درس الأشجار أن الشجرة تسأل «هل القيمة أكبر من حدّ معيّن؟».
الفكرة الجوهرية
النموذج لا يرى إلا الأرقام التي تعطيه. تبني هنا أرقامًا من المدن، ثم تضع الأعمدة على مقياس واحد، ثم تستخرج سمات من التاريخ والنص، ثم ترى لماذا يجب أن تثبت كل سمة جدارتها.
1عمود لكل فئة
لو كتبت الرياض = 1 وجدة = 2 والدمام = 3، لقرأ النموذج الخطي الدمام «ثلاثة أضعاف» الرياض. انظر إلى الرسم: كل مدينة تصير ثلاثة أرقام، واحد منها فقط يساوي 1.
الترميز الأحادي (One-hot) يصنع عمودًا لكل مدينة، فيه 1 إذا كانت هذه مدينة الصف و0 غير ذلك. لا ترتيب مزيّف، ولا مسافات مخترعة.
import pandas as pd
df = pd.DataFrame({"city": ["Riyadh", "Jeddah", "Dammam", "Jeddah"], "rooms": [3, 2, 4, 3]})
print(pd.get_dummies(df, columns=["city"], dtype=int).to_string(index=False))
أربعة صفوف، وثلاثة أعمدة جديدة. في الإنتاج استخدم OneHotEncoder(handle_unknown="ignore") داخل خط أنابيب، لأن get_dummies لا يتذكّر الأعمدة، ومدينة جديدة لم ترها في التدريب ستكسر الشكل.
الرقم الترتيبي يصلح فقط إذا كان الترتيب حقيقيًا، مثل صغير ووسط وكبير.
2مقياس واحد لكل الأعمدة
الراتب بالآلاف والعمر بالعشرات. النموذج الذي يعتمد على المسافة يرى فرق ألف ريال أهمّ بكثير من فرق عشر سنوات، فقط لأن الرقم أكبر.
التوحيد يطرح المتوسط ويقسم على الانحراف، فيصير لكل عمود متوسط صفر وانتشار واحد.
المخرج يعتمد على العمر فقط (فوق الأربعين أو لا). الراتب ضجيج بأرقام كبيرة.
import numpy as np
from sklearn.model_selection import cross_val_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
rng = np.random.default_rng(0)
n = 600
salary = rng.normal(9000, 3000, n)
age = rng.uniform(20, 60, n)
y = (age > 40).astype(int)
X = np.column_stack([salary, age])
raw = cross_val_score(KNeighborsClassifier(), X, y, cv=5).mean()
scaled = cross_val_score(make_pipeline(StandardScaler(), KNeighborsClassifier()), X, y, cv=5).mean()
print(f"kNN raw: {raw:.3f}")
print(f"kNN scaled: {scaled:.3f}")
بدون توحيد 57.8%، قريب من الصدفة، لأن المسافة كلها تقريبًا راتب. بعد التوحيد 98.7%. نفس البيانات ونفس النموذج، والفرق سطر واحد. ولاحظ أن التوحيد داخل make_pipeline حتى لا يتسرّب من أجزاء التحقّق.
3سمات من التاريخ والنص
التاريخ الخام كرقم لا يفيد النموذج تقريبًا. انظر إلى الرسم: من تاريخ واحد تخرج الساعة ويوم الأسبوع وهل هو عطلة.
import pandas as pd
orders = pd.DataFrame({
"placed_at": pd.to_datetime(["2026-03-05 08:10", "2026-03-06 21:40", "2026-03-07 13:05"]),
"note": ["عاجل جدا", "", "الرجاء الاتصال قبل الوصول"],
})
orders["hour"] = orders.placed_at.dt.hour
orders["weekday"] = orders.placed_at.dt.day_name()
orders["is_weekend"] = orders.placed_at.dt.day_of_week.isin([4, 5]).astype(int)
orders["note_words"] = orders.note.str.split().str.len()
orders["is_urgent"] = orders.note.str.contains("عاجل").astype(int)
print(orders.drop(columns=["placed_at", "note"]).to_string(index=False))
عمود تاريخ واحد صار ثلاث سمات، وعمود نص صار سمتين. day_of_week يبدأ من الاثنين = 0، فالجمعة 4 والسبت 5. هذه معرفة محلية لا يملكها النموذج: في بلد آخر ستكون عطلة نهاية الأسبوع مختلفة.
والنص يعطي سمات بسيطة قوية: عدد الكلمات، ووجود كلمة مثل «عاجل». هذه تأتي قبل التضمينات بكثير.
4كل سمة تثبت جدارتها
السمة التي هي ضجيج يتعلّم منها النموذج أنماطًا وهمية، خاصة مع بيانات قليلة. لذلك تُقاس كل سمة على التحقّق قبل أن تبقى.
ثلاث سمات حقيقية، ثم نضيف سمات عشوائية تمامًا. مع 120 صفًا فقط، راقب ماذا يحدث.
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
rng = np.random.default_rng(1)
n = 120
signal = rng.normal(size=(n, 3))
y = (signal.sum(axis=1) + rng.normal(scale=0.8, size=n) > 0).astype(int)
for extra in [0, 20, 200]:
X = np.hstack([signal, rng.normal(size=(n, extra))])
score = cross_val_score(LogisticRegression(max_iter=2000), X, y, cv=5).mean()
print(f"3 real + {extra:3} noise features -> {score:.3f}")
من 89.2% إلى 87.5% بعشرين سمة ضجيج، ثم 63.3% بمئتين. النموذج وجد «أنماطًا» في الضجيج تنطبق على التدريب ولا تنطبق على غيره. كلما قلّت البيانات، صارت كل سمة إضافية مخاطرة.
كيف يعمل، خطوة بخطوة: كيف تقرّر أن سمة جديدة تستحق البقاء
سجّل رقم التحقّق للنموذج الحالي قبل أي إضافة.
أضف سمة واحدة فقط، محسوبة من بيانات متاحة لحظة التنبّؤ ومن التدريب وحده.
أعِد التحقّق المتقاطع بالطيّات نفسها، وقارن الرقم الجديد بالقديم.
أبقِ السمة إذا تحسّن الرقم بأكثر من التذبذب بين الطيّات، واحذفها إن لم يتحسّن، وسجّل القرار في جدول.
مثال محلول
المدينة نص، والنموذج يحتاج أرقامًا. لو كتبت الرياض = 1 وجدة = 2 والدمام = 3، لفهم النموذج الخطي أن الدمام «أكبر» من الرياض بمرتين، وهذا هراء.
٢. مقاييس مختلفة تخدع المسافة
الراتب بالآلاف والعمر بالعشرات. نموذج يعتمد على المسافة، مثل أقرب الجيران، سيرى أن فرق ألف ريال أهم بمئة مرة من فرق عشر سنوات، لأن الرقم أكبر فقط.
جرّب بلا كود
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| ترقيم الفئات 1 و2 و3 يكفي. | النموذج الخطي سيفهم أن الدمام «ضعف» الرياض. استخدم عمودًا لكل فئة ما لم يكن للترتيب معنى حقيقي. |
| سمات أكثر تعني نموذجًا أفضل. | مع 120 صفًّا نزلت الدقة من 89.2% إلى 63.3% بعد إضافة مئتي سمة ضجيج. |
| رقم العميل سمة مفيدة لأنه فريد. | النموذج يحفظ أن العميل 4471 اشترى، وهذا لا ينفع مع أي عميل جديد، فيبدو ممتازًا في التدريب فقط. |
جرّب بنفسك
أي نموذج لن يتغيّر أداؤه تقريبًا إن وحّدت المدخلات؟
الأشجار تسأل «هل القيمة أكبر من حد؟»، والتوحيد لا يغيّر ترتيب القيم، فتتطابق الأسئلة الممكنة. أقرب الجيران يعتمد على المسافة، والانحدار والشبكات تعتمد على النزول بالميل، وكلها حسّاسة للمقاييس.
أضفت سمة «متوسط المبيعات الشهرية للعميل» محسوبة من كل البيانات، فقفز الأداء من 70% إلى 96%. ما أول ما تشكّ فيه؟
متوسط محسوب على كل الفترة يتضمّن مبيعات حدثت بعد لحظة التنبّؤ، وربما المبيعات التي تحاول التنبّؤ بها نفسها. القفزة الكبيرة المفاجئة من سمة واحدة إشارة تسريب كلاسيكية. احسب السمة من الماضي فقط بالنسبة لكل صف.
بطاقات مراجعة سريعة
تحقق من فهمك
عندك عمود «نوع الجهاز» فيه 3 قيم، وعمود «رمز المنتج» فيه 40 ألف قيمة. هل تعامل الاثنين بالترميز الأحادي؟
نوع الجهاز نعم: ثلاثة أعمدة بلا مشكلة. رمز المنتج لا: أربعون ألف عمود أغلبها أصفار، ومنتجات كثيرة بعيّنة أو اثنتين فقط فيحفظها النموذج. البدائل: تجميع المنتجات النادرة في فئة «أخرى»، أو استخدام سمة أعلى مستوى مثل قسم المنتج، أو ترميز بالهدف محسوب داخل أجزاء التدريب فقط حتى لا يتسرّب.
عندك عمود «الحجم» بقيم صغير ووسط وكبير. أي ترميز تختار؟
ترميزًا ترتيبيًا مثل 0 و1 و2، لأن الترتيب هنا حقيقي: الكبير أكبر فعلًا من الوسط، فلا تخترع الأرقام علاقة غير موجودة.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
توثيق رسمي1
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.