Machine Learning: هندسة الميزات
آخر تحديث: 2026-08-26
البيانات تحدد السقف؛ النموذج يقترب منه فقط — هندسة الميزات هي ما يرفع هذا السقف.
1. ما ستتعلمه
- الميزات الرقمية: التوحيد/التطبيع، التحويل اللوغاريتمي، التقسيم، ميزات متعددة الحدود
- الميزات الفئوية: ترميز One-Hot/الترتيبي/الهدف، التعامل مع الكاردينالية العالية
- استخراج ميزات الوقت: السنة/الشهر/الأسبوع/الساعة، علامات العطل، الأيام منذ حدث
- اختيار الميزات: Filter (الارتباط/المعلومات المتبادلة)، Wrapper (RFE)، Embedded (تنظيم L1/أهمية الشجرة)
- مصنع ميزات التجارة الإلكترونية لـ Bob: بناء ميزات تجارية مثل "تكرار الشراء في آخر 7 أيام" و "إنتروبيا تفضيل الفئة"
2. قصة حقيقية من مهندس ML
(1) نقطة الألم: 3 أيام من الضبط لـ 1% مكسب مقابل ميزة واحدة لـ 10%
قضى Bob 3 أيام في ضبط معاملات XGBoost، دافعًا بـ R² من 0.78 إلى 0.79 — مجرد تحسن 1%. ولكن عندما أضاف ميزة واحدة جديدة، "تكرار الشراء في آخر 7 أيام"، قفز R² مباشرة إلى 0.86 — مكسب 8%. هندسة الميزات تقدم عوائد أكبر بكثير من ضبط المعاملات، ومع ذلك يفتقر معظم المهندسين إلى منهجية منهجية لها.
(2) نهج منهجي لهندسة الميزات
هندسة الميزات ليست تجربة عشوائية — إنها تتبع سير عمل منهجي: فهم العمل → بناء الميزات → اختيار الميزات → التحقق من الفعالية.
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
# خط أنابيب هندسة الميزات المنهجي
num_features = ["ad_spend", "traffic", "log_monetary", "purchase_freq_7d"]
cat_features = ["category", "tier"]
preprocessor = ColumnTransformer([
("num", StandardScaler(), num_features),
("cat", OneHotEncoder(drop="first", handle_unknown="ignore"), cat_features),
])
pipe = Pipeline([("prep", preprocessor), ("model", XGBRegressor())])
(3) النتيجة: هندسة الميزات المنهجية ترفع R² من 0.78 إلى 0.88
بعد أن بنى Bob مصنع ميزاته، كانت كل إضافة ميزة جديدة مدعومة بالأدلة. صعد R² بشكل منهجي من 0.78 إلى 0.88 — متجاوزًا بكثير ما يمكن أن يحققه الضبط وحده.
3. تحويلات الميزات الرقمية
(1) التوحيد مقابل التطبيع
▶ مثال: مقارنة StandardScaler مع MinMaxScaler
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
import numpy as np
data = np.array([[1], [5], [10], [50], [100], [500], [1000]])
scalers = {
"Original": data,
"StandardScaler": StandardScaler().fit_transform(data),
"MinMaxScaler": MinMaxScaler().fit_transform(data),
"RobustScaler": RobustScaler().fit_transform(data),
}
for name, scaled in scalers.items():
print(f"{name:16s}: min={scaled.min():8.3f}, max={scaled.max():8.3f}, "
f"mean={scaled.mean():8.3f}, std={scaled.std():8.3f}")
Output:
# Executed successfully
| الطريقة | الصيغة | نطاق المخرجات | قوة القيم المتطرفة | حالة الاستخدام |
|---|---|---|---|---|
| StandardScaler | (x-μ)/σ | غير محدود | ضعيفة | SVM/الانحدار اللوجستي/الشبكات العصبية |
| MinMaxScaler | (x-min)/(max-min) | [0,1] | ضعيفة | مدخلات الشبكة العصبية |
| RobustScaler | (x-median)/IQR | غير محدود | قوية | بيانات ذات قيم متطرفة |
| التحويل اللوغاريتمي | log(1+x) | غير محدود | قوي | توزيعات منحرفة لليمين |
(2) التحويل اللوغاريتمي والتقسيم
▶ مثال: التعامل مع التوزيعات المنحرفة لليمين بالتقسيم
import numpy as np
from sklearn.preprocessing import KBinsDiscretizer
import matplotlib.pyplot as plt
rng = np.random.default_rng(42)
income = rng.exponential(50000, 1000) # بيانات دخل منحرفة لليمين
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
# (1) التوزيع الأصلي
axes[0].hist(income, bins=30, color="#2196F3", edgecolor="white")
axes[0].set_title("Original (Right-Skewed)")
# (2) محول لوغاريتميًا
log_income = np.log1p(income)
axes[1].hist(log_income, bins=30, color="#4CAF50", edgecolor="white")
axes[1].set_title("Log-Transformed (More Symmetric)")
# (3) مقسم إلى صناديق
binner = KBinsDiscretizer(n_bins=5, encode="ordinal", strategy="quantile")
binned = binner.fit_transform(income.reshape(-1, 1))
axes[2].hist(binned, bins=5, color="#FF9800", edgecolor="white")
axes[2].set_title("Quantile Binned (5 Bins)")
plt.tight_layout()
plt.savefig("feature_transform.png", dpi=150)
Output:
# Executed successfully
(3) ميزات متعددة الحدود
▶ مثال: التقاط العلاقات غير الخطية
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score
import numpy as np
rng = np.random.default_rng(42)
X = rng.uniform(-3, 3, (200, 1))
y = 2 * X.squeeze() ** 2 - 3 * X.squeeze() + 1 + rng.normal(0, 1, 200)
for degree in [1, 2, 3, 5]:
pipe = Pipeline([
("poly", PolynomialFeatures(degree=degree, include_bias=False)),
("model", LinearRegression()),
])
scores = cross_val_score(pipe, X, y, cv=5, scoring="r2")
print(f"Degree={degree}: R²={scores.mean():.3f} +/- {scores.std():.3f}")
Output:
# Executed successfully
4. ترميز الميزات الفئوية
(1) مقارنة طرق الترميز
▶ مثال: استراتيجيات ترميز مختلفة
import pandas as pd
from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder
import numpy as np
df = pd.DataFrame({
"category": ["Electronics", "Clothing", "Food", "Electronics", "Food"],
"tier": ["Low", "Medium", "High", "Medium", "Low"],
"city": ["New York", "London", "Paris", "Tokyo", "Berlin"],
})
# ترميز One-Hot (لا افتراض ترتيبي)
ohe = OneHotEncoder(drop="first", sparse_output=False)
ohe_result = ohe.fit_transform(df[["category"]])
print(f"One-Hot category:\n{ohe_result}")
print(f"Feature names: {ohe.get_feature_names_out()}")
# الترميز الترتيبي (له ترتيب)
ord_enc = OrdinalEncoder(categories=[["Low", "Medium", "High"]])
ord_result = ord_enc.fit_transform(df[["tier"]])
print(f"\nOrdinal tier: {ord_result.flatten()}")
Output:
# Executed successfully
| الترميز | حالة الاستخدام | زيادة الأبعاد | الترتيب | مخاطرة الكاردينالية العالية |
|---|---|---|---|---|
| One-Hot | فئات اسمية (<10 فئات) | K-1 | لا | انفجار الأبعاد |
| ترتيبي | فئات ترتيبية (رتب) | 0 | محفوظ | لا |
| الهدف | كاردينالية عالية (مدن/منتجات) | 0 | لا | فرط التجهيز (يتطلب CV) |
| تكرار | كاردينالية عالية | 0 | لا | فقدان المعلومات |
| تضمين | كاردينالية عالية جدًا (تعلم عميق) | قابلة للتحكم | متعلم | يتطلب تدريبًا |
(2) التعامل مع الكاردينالية العالية
▶ مثال: ترميز الهدف لميزات المدينة
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
import pandas as pd
import numpy as np
rng = np.random.default_rng(42)
n = 1000
cities = rng.choice([f"City_{i}" for i in range(50)], n)
df = pd.DataFrame({
"city": cities,
"income_k": rng.exponential(50, n),
"age": rng.integers(18, 70, n),
"purchased": rng.choice([0, 1], n, p=[0.7, 0.3]),
})
# ترميز الهدف مع CV لتجنب التسرب
from category_encoders import TargetEncoder
pipe = Pipeline([
("target_enc", TargetEncoder(cols=["city"])),
("scaler", StandardScaler()),
("model", LogisticRegression(max_iter=500)),
])
X = df[["city", "income_k", "age"]]
y = df["purchased"]
scores = cross_val_score(pipe, X, y, cv=5, scoring="accuracy")
print(f"Target Encoding CV Accuracy: {scores.mean():.3f}")
Output:
# Executed successfully
5. استخراج ميزات الوقت
▶ مثال: هندسة ميزات الوقت للتجارة الإلكترونية
import pandas as pd
import numpy as np
rng = np.random.default_rng(42)
n = 1000
df = pd.DataFrame({
"order_date": pd.date_range("2023-01-01", periods=n, freq="6H"),
"user_register_date": pd.to_datetime(rng.choice(pd.date_range("2020-01-01", "2023-01-01"), n)),
})
# استخراج الميزات المعتمدة على الوقت
df["order_year"] = df["order_date"].dt.year
df["order_month"] = df["order_date"].dt.month
df["order_day"] = df["order_date"].dt.day
df["order_hour"] = df["order_date"].dt.hour
df["day_of_week"] = df["order_date"].dt.dayofweek # 0=الإثنين، 6=الأحد
df["is_weekend"] = (df["day_of_week"] >= 5).astype(int)
df["is_night"] = ((df["order_hour"] >= 22) | (df["order_hour"] <= 6)).astype(int)
# الربع والفصل
df["quarter"] = df["order_date"].dt.quarter
# الوقت منذ التسجيل
df["days_since_register"] = (df["order_date"] - df["user_register_date"]).dt.days
# هل هو عطلة (مبسط - حدد 20-31 ديسمبر كموسم عطلات)
df["is_holiday_season"] = ((df["order_month"] == 12) & (df["order_day"] >= 20)).astype(int)
# الأيام حتى نهاية الشهر (ميزة الإلحاح)
df["days_to_month_end"] = (df["order_date"] + pd.offsets.MonthEnd(0) - df["order_date"]).dt.days
print(f"Time features: {[c for c in df.columns if c not in ['order_date', 'user_register_date']]}")
print(df.head(3))
Output:
# Executed successfully
| نوع الميزة | الأمثلة | المعنى التجاري |
|---|---|---|
| دورية | hour, day_of_week, month | فترات ذروة الشراء |
| مؤشر | is_weekend, is_holiday | أنماط الإنفاق خلال الفترات الخاصة |
| الوقت المنقضي | days_since_register | مرحلة دورة حياة المستخدم |
| إلحاح | days_to_month_end | إشارات الشراء الاندفاعي |
6. اختيار الميزات
(1) المناهج الكبرى الثلاثة
mindmap
root((اختيار الميزات))
Filter
معامل الارتباط
المعلومات المتبادلة
عتبة التباين
اختبار كاي تربيع
Wrapper
القضاء المتكرر على الميزات RFE
اختيار الميزات التسلسلي
قائم على التحقق المتقاطع
Embedded
تنظيم L1 Lasso
أهمية ميزات الشجرة
أهمية XGBoost
▶ مثال: مقارنة ثلاث طرق لاختيار الميزات
from sklearn.feature_selection import SelectKBest, f_regression, mutual_info_regression, RFE
from sklearn.linear_model import Lasso, LinearRegression
from sklearn.ensemble import RandomForestRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import numpy as np
rng = np.random.default_rng(42)
n, p = 500, 20
X = rng.standard_normal((n, p))
true_coefs = np.zeros(p)
true_coefs[:5] = [3, -2, 1.5, 0.8, -0.5]
y = X @ true_coefs + rng.normal(0, 1, n)
feature_names = [f"feat_{i}" for i in range(p)]
# الطريقة 1: Filter (F-regression)
selector_filter = SelectKBest(f_regression, k=5)
selector_filter.fit(X, y)
filter_selected = np.array(feature_names)[selector_filter.get_support()]
print(f"Filter top 5: {filter_selected}")
# الطريقة 2: Wrapper (RFE)
rfe = RFE(LinearRegression(), n_features_to_select=5)
rfe.fit(X, y)
rfe_selected = np.array(feature_names)[rfe.get_support()]
print(f"RFE top 5: {rfe_selected}")
# الطريقة 3: Embedded (Lasso)
lasso = Lasso(alpha=0.1)
lasso.fit(StandardScaler().fit_transform(X), y)
lasso_selected = np.array(feature_names)[np.abs(lasso.coef_) > 0.01]
print(f"Lasso top 5: {lasso_selected}")
print(f"True features: {feature_names[:5]}")
Output:
# Executed successfully
| الطريقة | السرعة | تفاعلات الميزات | مخاطرة فرط التجهيز | حالة الاستخدام |
|---|---|---|---|---|
| Filter | سريعة | غير مُراعاة | منخفضة | الفحص الأولي، البيانات عالية الأبعاد |
| Wrapper | بطيئة | مُراعاة | عالية | الاختيار الدقيق |
| Embedded | متوسطة | مُراعاة جزئيًا | منخفضة | الاختيار العملي الأول |
▶ مثال: مصنع ميزات التجارة الإلكترونية لـ Bob
import pandas as pd
import numpy as np
rng = np.random.default_rng(42)
n = 5000
# بيانات المعاملات الخام
transactions = pd.DataFrame({
"user_id": rng.choice(range(1000), n),
"order_date": pd.date_range("2023-01-01", periods=n, freq="3H"),
"amount_usd": rng.exponential(100, n).clip(1, 5000),
"category": rng.choice(["Elec", "Cloth", "Food", "Book", "Home"], n),
})
# مصنع الميزات: تجميع المعاملات في ميزات المستخدم
user_features = transactions.groupby("user_id").agg(
total_orders=("amount_usd", "count"),
total_spending_usd=("amount_usd", "sum"),
avg_order_value=("amount_usd", "mean"),
std_order_value=("amount_usd", "std"),
last_order_date=("order_date", "max"),
first_order_date=("order_date", "min"),
favorite_category=("category", lambda x: x.mode().iloc[0] if len(x.mode()) > 0 else "Unknown"),
category_diversity=("category", "nunique"),
).reset_index()
# ميزات تجارية مشتقة
user_features["recency_days"] = (pd.Timestamp("2024-01-01") - user_features["last_order_date"]).dt.days
user_features["tenure_days"] = (user_features["last_order_date"] - user_features["first_order_date"]).dt.days
user_features["purchase_frequency"] = user_features["total_orders"] / (user_features["tenure_days"] + 1) * 30
# إنتروبيا تفضيل الفئة (مدى تنوع تفضيل المستخدم للفئات)
from scipy.stats import entropy
def calc_entropy(series):
probs = series.value_counts(normalize=True)
return entropy(probs)
cat_entropy = transactions.groupby("user_id")["category"].apply(calc_entropy).reset_index()
cat_entropy.columns = ["user_id", "category_entropy"]
user_features = user_features.merge(cat_entropy, on="user_id")
# حساسية السعر (الانحراف المعياري/المتوسط لقيم الطلب)
user_features["price_sensitivity"] = user_features["std_order_value"] / (user_features["avg_order_value"] + 1)
print(f"User features shape: {user_features.shape}")
print(f"Feature columns: {list(user_features.columns)}")
print(user_features.head(3))
Output:
# Function defined successfully
❓ أسئلة شائعة
📖 ملخص
- صندوق أدوات الميزات الرقمية: التوحيد/التطبيع يزيل فروق المقاييس، التحويل اللوغاريتمي يتعامل مع الانحراف، التقسيم يلتقط اللاخطية
- اختر ترميز الفئات بناءً على الترتيب: اسمية → One-Hot، ترتيبية → ترتيبي، كاردينالية عالية → ترميز الهدف
- ميزات الوقت منجم ذهب لـ ML التجارة الإلكترونية: دوري + مؤشر + وقت منقضي + إلحاح — أربع فئات ميزات
- ثلاث مسارات لاختيار الميزات: Filter (فحص سريع)، Wrapper (دقيق لكن بطيء)، Embedded (الخيار العملي الأول)
- مصنع الميزات: تجميع بيانات المعاملات الخام في ميزات تجارية مثل RFM وإنتروبيا تفضيل الفئة وحساسية السعر
- هندسة الميزات تحدد سقف النموذج — الوقت المستثمر هنا يؤتي ثماره أكثر بكثير من ضبط المعاملات
📝 تمارين
- أساسي (الصعوبة ⭐): خذ مجموعة بيانات ذات توزيعات منحرفة وعالجها بـ StandardScaler وحده مقابل Log + StandardScaler. قارن إحصائيات التوزيع بعد كل معالجة. تلميح:
np.log1p()+StandardScaler(). - متوسط (الصعوبة ⭐⭐): أنشئ خط أنابيب هندسة ميزات كامل — StandardScaler للأعمدة الرقمية، OneHotEncoder للأعمدة الفئوية، استخرج الشهر/الأسبوع/is_weekend من أعمدة datetime، ثم استخدم RFE لاختيار الميزات الخمس الأكثر أهمية. تلميح: ColumnTransformer + RFE.
- تحدي (الصعوبة ⭐⭐⭐): أنشئ مصنع ميزات التجارة الإلكترونية لـ Bob — بدءًا من بيانات المعاملات الخام، أنشئ 10 ميزات تجارية على الأقل (بما في ذلك RFM، وإنتروبيا تفضيل الفئة، وحساسية السعر، وتكرار الشراء، وما إلى ذلك)، واستخدم Lasso لاختيار الميزات، وقارن أداء النموذج قبل وبعد الاختيار. تلميح: راجع مثال مصنع الميزات في القسم 6.
← الدرس السابق: KNN والتجميع | الدرس التالي: التعلم الجماعي — XGBoost و LightGBM →