مشروع: مصنّف صور
درس 7 من 7
7مشروع: مصنّف صور
الصورة الكبيرةمشروع هذا المسار: مصنّف صور لفئات تجمعها بنفسك من حولك، بنسختين، واحدة من الصفر وأخرى منقولة، وجدول يقارن الدقة ووقت التدريب، وتحليل لأين يفشل كل منهما. صورك أنت، لا مجموعة بيانات جاهزة، لأن جمع البيانات وتنظيفها نصف العمل الحقيقي.
ماذا تشاهد في الرسم: المشروع دورة لا خط مستقيم: تحليل الأخطاء يعيدك إلى جمع الصور. جملة مثل «العجوة والخلاص يختلطان في الصور المعتمة» تقودك إلى إصلاح محدّد: صور أكثر في ضوء خافت.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
تجمع صورًا بنفسك لفئات تختارها، ثم تدرّب نموذجين: واحدًا من الصفر، وواحدًا يبدأ من نموذج جاهز. وتقارن بينهما: أيهما أدق، وأيهما احتاج وقتًا أقل. ستكتشف بنفسك لماذا يبدأ أغلب المهندسين من نموذج جاهز.
مجموعة بيانات مقسّمة بتوازن، ونموذج التفافي صغير من الصفر مقابل تعلّم منقول من نموذج مدرّب مسبقًا، بنفس التوسيع والتقسيم. التقرير يقارن الدقة ومصفوفة الخلط وزمن التدريب وزمن الاستدلال لكل صورة.
قبل أن تبدأ
♻️التعلّم المنقول
تجمّد عمود ResNet18 الفقري وتستبدل رأسه، فهذه هي النسخة الثانية من المشروع.
🌀توسيع البيانات
تختار تحويلات لا تغيّر الجواب، وتطبّقها على التدريب وحده.
📏المقاييس
تقرأ مصفوفة الالتباس وتعرف لماذا لا تكفي الدقة الإجمالية وحدها.
الفكرة الجوهرية
مشروع المسار مصنّف صور لأصناف تجمعها بنفسك. تجمع الصور وتقسّمها، ثم تبني نسختين بنفس الإعدادات، ثم تبحث أين تخطئ كل نسخة، ثم تكتب التقرير.
1اجمع وقسّم بالجلسة
اختر 3 إلى 5 أصناف تستطيع تصويرها بهاتفك في يومين، مثل أنواع التمور. اجمع 60 إلى 100 صورة لكل صنف، بإضاءات وخلفيات وزوايا مختلفة، فالتنوّع أهم من العدد.
انظر إلى الرسم: الخطوة المميّزة هي تصوير الاختبار في ظروف أخرى.
torchvision يقرأ فئات الصور من أسماء المجلدات مباشرة. قسّم مبكرًا ولا تنقل صورة بين المجلدات بعد ذلك.
dates/
train/
sukkari/ *.jpg
ajwa/ *.jpg
khalas/ *.jpg
val/
sukkari/ ajwa/ khalas/
test/
sukkari/ ajwa/ khalas/
صوّر مجموعة الاختبار في يوم مختلف أو مكان مختلف عن التدريب. إن صوّرت كل شيء على نفس الطاولة، قد يتعلّم النموذج الطاولة لا التمر، وتبدو دقته ممتازة إلى أن يراه أحد على طاولة أخرى.
2نسختان بنفس الإعدادات
النسخة الأولى شبكة التفافية صغيرة من الصفر، وهي خط الأساس. الثانية ResNet18 بعمود فقري مجمّد ورأس جديد.
انظر إلى الرسم: الفرق بينهما في النموذج وحده. نفس التقسيم والتوسيع وعدد الدورات والبذرة، وإلا لم تعرف سبب الفرق.
يحتاج torchvision وصورك وتنزيل الأوزان، فهو للقراءة هنا وللتشغيل على جهازك.
import time
import torch
from torch import nn
from torchvision import datasets, transforms
from torchvision.models import ResNet18_Weights, resnet18
norm = transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
train_tf = transforms.Compose([transforms.RandomResizedCrop(224, scale=(0.7, 1.0)),
transforms.ColorJitter(0.2, 0.2), transforms.ToTensor(), norm])
eval_tf = transforms.Compose([transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), norm])
train = datasets.ImageFolder("dates/train", train_tf)
val = datasets.ImageFolder("dates/val", eval_tf)
train_dl = torch.utils.data.DataLoader(train, batch_size=32, shuffle=True, num_workers=2)
val_dl = torch.utils.data.DataLoader(val, batch_size=64)
torch.manual_seed(0)
model = resnet18(weights=ResNet18_Weights.DEFAULT)
model.requires_grad_(False)
model.fc = nn.Linear(model.fc.in_features, len(train.classes))
optimizer = torch.optim.Adam(model.fc.parameters(), lr=1e-3)
start = time.perf_counter()
for epoch in range(8):
model.train()
for xb, yb in train_dl:
loss = nn.functional.cross_entropy(model(xb), yb)
optimizer.zero_grad()
loss.backward()
optimizer.step()
model.eval()
with torch.no_grad():
correct = sum((model(xb).argmax(1) == yb).sum().item() for xb, yb in val_dl)
print(f"epoch {epoch} val acc {correct / len(val):.3f}")
print(f"training time: {time.perf_counter() - start:.0f}s")
كل ما في هذا الملف رأيته في دروس المسار: المعالجة والتطبيع من الدرس الأول، والتوسيع بلا قلب، والتجميد واستبدال الرأس، والأسطر الخمسة للتدريب، وeval مع no_grad للتقييم. انسخه للنسخة من الصفر واستبدل فقط تعريف model والمحسّن.
3أين يخطئ النموذج
مصفوفة الالتباس تقول لك أي صنفين يخلط بينهما النموذج. ثم تنظر بنفسك إلى الصور الخاطئة، فتجد غالبًا سببًا واحدًا يتكرّر.
انظر إلى الرسم: الدقة العامة تخفي أن صنفًا واحدًا أضعف بكثير من البقية.
قبل صورك، جرّب التحليل على صور الأرقام بمصنّف بسيط. نفس الأسطر تعمل على تنبّؤات أي نموذج.
import numpy as np
from sklearn.datasets import load_digits
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
X, y = load_digits(return_X_y=True)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, train_size=300, random_state=0, stratify=y)
pred = SVC().fit(X_tr, y_tr).predict(X_te)
cm = confusion_matrix(y_te, pred)
print(f"accuracy: {(pred == y_te).mean():.3f}")
per_class = cm.diagonal() / cm.sum(axis=1)
print("worst classes:", [(int(c), round(float(per_class[c]), 2)) for c in np.argsort(per_class)[:3]])
off = cm.copy()
np.fill_diagonal(off, 0)
i, j = np.unravel_index(off.argmax(), off.shape)
print(f"most common confusion: true {i} predicted as {j}, {off[i, j]} times")
دقة 95.3% إجمالًا، لكن الرقم 3 يُصنَّف صحيحًا 86% فقط، وأكثر خلط هو 3 يُقرأ 8، ثماني مرات. هذا منطقي بصريًا: 3 هو 8 بنصف أيسر مفتوح. في مشروعك ستجد مثله: «العجوة والخلاص يختلطان في الصور المعتمة»، وهذه جملة تقود لإصلاح محدّد، مثل صور أكثر بإضاءة خافتة.
4التقرير
التقرير يقارن النسختين بأرقام قيست على نفس الجهاز. ثم يحكم: هل الفرق يستحق؟ نسخة أفضل بنقطتين وأبطأ عشر مرات في التوقّع قد لا تناسب الهاتف.
انظر إلى الرسم: الأرقام التي تدخل الجدول، ثم فقرة عن أكثر نوعين من الأخطاء مع صورة وفرضية لكلٍّ.
كيف يعمل، خطوة بخطوة: كيف تبني المصنّف وتقارن نسختيه
صوّر 60 إلى 100 صورة لكل فئة بإضاءات وخلفيات وزوايا مختلفة، وصوّر الاختبار في يوم أو مكان آخر.
رتّبها في مجلدات train و val و test حسب جلسة التصوير، ولا تنقل صورة بينها بعد ذلك.
درّب شبكة صغيرة من الصفر و ResNet18 مجمّدًا بنفس التوسيع وعدد الدورات والبذرة، وسجّل الدقة والوقت.
اقرأ مصفوفة الالتباس لكل نسخة، ثم انظر بنفسك إلى كل صورة اختبار أخطأ فيها النموذج.
مثال محلول
اختر ثلاث إلى خمس فئات تستطيع تصويرها بهاتفك في يومين، مثل أنواع التمر، أو أصناف القهوة، أو أوراق نباتات حديقتك. اجمع 60 إلى 100 صورة لكل فئة، بإضاءات وخلفيات وزوايا مختلفة.
جرّب بلا كود
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| دقة تحقّق عالية تعني أن النموذج جاهز. | إن صُوّر التحقّق مع التدريب على نفس الطاولة، فقد يتعلّم النموذج الطاولة لا الفئة. 97% على التحقّق قد تصير نصف الإجابات خطأ على صور صديق. |
| تقسيم الصور عشوائيًا يكفي. | لقطات متتالية من نفس الجلسة شبه متطابقة، فتظهر في التدريب والاختبار معًا. قسّم حسب جلسة التصوير، لا حسب الصورة. |
| النسخة الأدق هي الأفضل دائمًا. | إن كانت أدق بنقطتين وأبطأ في التنبّؤ عشر مرات، فقد لا تناسب هاتفًا. التقرير يذكر الفرق بالأرقام ويربط القرار بمكان التشغيل. |
جرّب بنفسك
مصفوفة الالتباس تُظهر أن 80% من أخطاء نموذجك بين فئتين فقط من خمس. ما أفضل خطوة تالية؟
المشكلة محدّدة جدًّا، والحل يجب أن يكون محدّدًا. النظر في الصور يكشف السبب (حجم؟ لون في الإضاءة الخافتة؟ زاوية؟)، والأمثلة الإضافية الموجّهة تعالجه مباشرة. النموذج الأكبر والدورات الإضافية حلول عامة لمشكلة خاصة.
النسخة من الصفر 82% وتدريبها 4 دقائق، والمنقولة 94% وتدريبها 6 دقائق، والتنبّؤ بالمنقولة أبطأ مرتين. أي جملة تناسب التقرير؟
التقرير الجيد يذكر الفرق بأرقام، ويربط القرار بظروف الاستخدام. 12 نقطة فرق كبير غالبًا، لكن «أفضل» يعتمد على أين سيعمل النموذج. الجمل المطلقة لا تساعد من سيقرّر.
بطاقات مراجعة سريعة
تحقق من فهمك
النسخة المنقولة حصلت على 97% على التحقّق، لكن صديقًا جرّبها على صوره فأخطأت في نصفها. ما أول ما تشكّ فيه؟
أن التحقّق يشبه التدريب أكثر مما يشبه الواقع: نفس الخلفية، أو نفس الإضاءة، أو نفس الهاتف، أو نفس جلسة التصوير. النموذج تعلّم شيئًا مرتبطًا بالفئة في بياناتك فقط، مثل «السكري دائمًا على صحن أبيض». الحل: مجموعة اختبار مصوّرة في ظروف مختلفة تمامًا، وتوسيع أقوى للخلفيات والإضاءة، وصور من أشخاص آخرين.
في مثال الأرقام كانت الدقة 95.3%، لكن الرقم 3 صحيح بنسبة 86% فقط. ماذا تفعل بهذه المعلومة؟
تبحث عن أكثر خطأ تكرارًا، وهنا 3 يُقرأ 8 ثماني مرات، ثم تنظر إلى تلك الصور لتجد السبب. الدقة الإجمالية تخفي أن فئة واحدة أضعف بكثير.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
توثيق رسمي1
مقالات2
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.