تخطَّ إلى المحتوى

أول نموذج حقيقي بـ scikit-learn: تصنيف الزهور

الدرس 4 من 8 · تعلّم الآلة

حان وقت جمع كل ما تعلمته في مشروع حقيقي متكامل! سنبني نموذجاً يتعرف على نوع زهرة السوسن (Iris) من قياسات أوراقها. هذه البيانات من أشهر مجموعات البيانات في تاريخ تعلّم الآلة، ويبدأ بها كثير من المتعلمين حول العالم. وستمر بنفس الخطوات التي يتبعها علماء البيانات في المشاريع الكبيرة.

خطوات أي مشروع تعلّم آلة

Section titled “خطوات أي مشروع تعلّم آلة”
  1. تحميل البيانات وفهمها.
  2. تقسيمها إلى تدريب واختبار.
  3. اختيار النموذج وتدريبه.
  4. تقييمه على بيانات الاختبار.
  5. استخدامه للتوقع على بيانات جديدة.

الخطوة 1: تحميل البيانات

Section titled “الخطوة 1: تحميل البيانات”

scikit-learn تأتي مع بيانات الزهور جاهزة:

from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data
y = iris.target
print(X.shape)
print(iris.feature_names)
print(iris.target_names)
print(X[0], y[0])
الناتج
(150, 4)
['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
['setosa' 'versicolor' 'virginica']
[5.1 3.5 1.4 0.2] 0

لنفهم البيانات:

  • 150 زهرة، لكل منها 4 ميزات: طول وعرض الكأس (sepal)، وطول وعرض البتلة (petal) بالسنتيمتر.
  • 3 أنواع (التسميات): setosa و versicolor و virginica، وهي مخزنة كأرقام 0 و 1 و 2.
  • الزهرة الأولى قياساتها [5.1 3.5 1.4 0.2]، ونوعها 0 أي setosa.

إذن هي مشكلة تعلّم موجّه من نوع التصنيف، كما تعلمت في الدرس السابق.

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
print(len(X_train), len(X_test))
الناتج
120 30

120 زهرة للتدريب، و 30 زهرة مخبأة عن النموذج لاختباره لاحقاً.

سنستخدم نموذج شجرة القرار (Decision Tree):

from sklearn.tree import DecisionTreeClassifier
model = DecisionTreeClassifier(random_state=42)
model.fit(X_train, y_train)

شجرة القرار تتعلم سلسلة أسئلة بسيطة، مثل لعبة «20 سؤالاً»:

هل طول البتلة ≤ 2.45 سم؟
├── نعم ← setosa
└── لا ← هل طول البتلة ≤ 4.75 سم؟
├── نعم ← سؤال عن عرض البتلة ← غالباً versicolor
└── لا ← أسئلة أخرى عن عرض البتلة ← غالباً virginica

هذه هي الأسئلة الأولى التي تعلّمها نموذجنا فعلاً (يمكنك عرضها بالدالة export_text من sklearn.tree). النموذج يختار الأسئلة والأرقام بنفسه من بيانات التدريب، بحيث يفصل الأنواع بأفضل شكل. وميزة شجرة القرار أنها مفهومة: تستطيع أن ترى لماذا اتخذ النموذج قراره، على عكس نماذج كثيرة أخرى.

from sklearn.metrics import accuracy_score
predictions = model.predict(X_test)
print(predictions[:5])
print(y_test[:5])
acc = accuracy_score(y_test, predictions)
print(f'الدقة: {acc:.2f}')
الناتج
[1 0 2 1 1]
[1 0 2 1 1]
الدقة: 1.00
  • السطر الأول: توقعات النموذج لأول 5 زهور من الاختبار.
  • السطر الثاني: الإجابات الحقيقية لنفس الزهور. متطابقة!
  • الدقة (Accuracy): نسبة التوقعات الصحيحة من الكل. 1.00 تعني 100%: النموذج أصاب في كل الزهور الثلاثين التي لم يرها أثناء التدريب.

الخطوة 5: التوقع لزهرة جديدة

Section titled “الخطوة 5: التوقع لزهرة جديدة”

وجدت زهرة في الحديقة، وقست أوراقها:

new_flower = [[5.0, 3.4, 1.5, 0.2]]
result = model.predict(new_flower)
print(iris.target_names[result[0]])
الناتج
setosa

النموذج يعرف أنها setosa، وفي الغالب بسبب بتلتها القصيرة جداً (1.5 سم).

for name, imp in zip(iris.feature_names, model.feature_importances_):
print(f'{name}: {imp:.2f}')
الناتج
sepal length (cm): 0.00
sepal width (cm): 0.02
petal length (cm): 0.91
petal width (cm): 0.08

أهمية الميزات (Feature Importance): النموذج اعتمد بنسبة 91% على طول البتلة، ولم يستخدم طول الكأس إطلاقاً. هذه معلومة قيمة: تكشف أي القياسات تهم فعلاً، وقد توفر عليك جمع بيانات لا فائدة منها.

الإفراط في التعلّم والنموذج البسيط جداً

Section titled “الإفراط في التعلّم والنموذج البسيط جداً”

ماذا لو قيّدنا الشجرة بسؤال واحد فقط؟

small = DecisionTreeClassifier(max_depth=1, random_state=42).fit(X_train, y_train)
print(f"{accuracy_score(y_test, small.predict(X_test)):.2f}")
الناتج
0.63

انخفضت الدقة إلى 63%! سؤال واحد يكفي لفصل setosa، لكنه لا يكفي للتمييز بين النوعين الآخرين. هذا يسمى نقص التعلّم (Underfitting): النموذج أبسط من أن يفهم المشكلة.

والعكس أيضاً مشكلة: نموذج معقد جداً قد يحفظ بيانات التدريب بتفاصيلها وأخطائها، فيكون ممتازاً عليها، وضعيفاً على البيانات الجديدة. هذا يسمى الإفراط في التعلّم (Overfitting)، مثل الطالب الذي حفظ أسئلة الكتاب دون فهم. ولهذا بالذات نختبر دائماً على بيانات منفصلة.

iris_model.py
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
iris.data, iris.target, test_size=0.2, random_state=42
)
model = DecisionTreeClassifier(random_state=42)
model.fit(X_train, y_train)
acc = accuracy_score(y_test, model.predict(X_test))
print(f'الدقة: {acc:.2f}')
new_flower = [[5.0, 3.4, 1.5, 0.2]]
print(iris.target_names[model.predict(new_flower)[0]])

أقل من 20 سطراً لنموذج ذكاء اصطناعي كامل! هذا هو جمال scikit-learn.

  1. ثبّت المكتبة بـ pip install scikit-learn وشغّل الكود الكامل.
  2. غيّر test_size إلى 0.5، ولاحظ هل تتغير الدقة.
  3. جرّب max_depth=2 و max_depth=3، وسجّل الدقة لكل قيمة. ما أصغر عمق يعطي نتيجة ممتازة؟
  4. استبدل شجرة القرار بـ KNeighborsClassifier(n_neighbors=5) من الدرس السابق، وقارن الدقة.
  5. تحدٍّ: جرّب بيانات أخرى جاهزة في المكتبة، مثل load_wine لتصنيف أنواع النبيذ من تحليلاته الكيميائية، بنفس الخطوات.
  • التقييم على بيانات التدريب: دقة التدريب هنا كانت 100% أيضاً، لكنها لا تثبت شيئاً. المقياس الحقيقي هو بيانات الاختبار.
  • نسيان القوسين المزدوجين في predict: predict([5.0, 3.4, 1.5, 0.2]) خطأ، لأن النموذج ينتظر قائمة عينات: [[...]].
  • نسيان random_state: تتغير النتائج في كل تشغيل، فيصعب المقارنة بين التجارب.

سؤال

ما معنى أن دقة النموذج على بيانات الاختبار 0.90؟

سؤال

نموذج دقته 99% على بيانات التدريب و 60% على بيانات الاختبار. ما المشكلة غالباً؟

  • خطوات المشروع: تحميل البيانات، ثم التقسيم، ثم التدريب بـ fit، ثم التقييم، ثم التوقع بـ predict.
  • بيانات Iris: 150 زهرة، و 4 ميزات، و 3 أنواع، ومشكلة تصنيف.
  • شجرة القرار نموذج مفهوم يتعلم سلسلة أسئلة، و feature_importances_ تكشف أهم الميزات.
  • accuracy_score يقيس الدقة على بيانات الاختبار، و 100% نادرة في المشاكل الحقيقية.
  • احذر نقص التعلّم والإفراط فيه.