الدرس 6 من 8
الشبكات الالتفافية (CNN)
الموديلات اللي بتتعرف على الوشوش والأرقام والأمراض في الأشعة.
المشكلة مع الشبكة العادية
لو فردت صورة ٢٢٤×٢٢٤ ملونة لمتجه، هيبقى ١٥٠ ألف رقم، وأول طبقة لوحدها هتبقى ملايين الأوزان. والأهم: الشبكة العادية مش فاهمة إن البكسلات اللي جنب بعض مرتبطة، ولا إن القطة هي هي لو اتحركت يمين شوية.
الـ Convolution: عدسة صغيرة بتمسح الصورة
الفكرة: فلتر صغير (مثلاً ٣×٣) بيتحرك على الصورة كلها، وعند كل مكان بيعمل dot product مع البكسلات اللي تحته. فلتر معين ممكن «يولّع» عند الحواف الرأسية، وتاني عند المنحنيات. والسحر إن الـ CNN بيتعلم الفلاتر دي لوحده.
import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import load_sample_image photo = load_sample_image("china.jpg").mean(axis=2)[::3, ::3] # grayscale, smaller vertical_edges = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]]) # a 3x3 filter def convolve(img, kernel): h, w = img.shape[0] - 2, img.shape[1] - 2 out = np.zeros((h, w)) for i in range(h): for j in range(w): out[i, j] = np.sum(img[i:i + 3, j:j + 3] * kernel) # slide & dot return out fig, axes = plt.subplots(1, 2, figsize=(9, 3.2)) axes[0].imshow(photo, cmap="gray"); axes[0].set_title("original") axes[1].imshow(np.abs(convolve(photo, vertical_edges)), cmap="gray"); axes[1].set_title("vertical edges") for ax in axes: ax.axis("off") plt.tight_layout() plt.show()
الفلتر ده لقط الحواف الرأسية بس: أعمدة المبنى وحوافه بقت منورة، والسما والأسطح الناعمة بقت سودا. الطبقات الأولى في الـ CNN بتتعلم حاجات بسيطة زي كده، واللي بعدها بتجمعهم لأشكال، واللي بعدها لأجزاء (عين، ودن)، لحد ما آخر طبقة تقول «قطة».
Conv2d طبقة فيها فلاتر كتير بتتعلمها.channels عدد الفلاتر، أو الألوان في المدخل (3 للـ RGB).MaxPool بتصغّر الصورة بإنها تاخد أكبر قيمة في كل مربع صغير.Flatten بتفرد الناتج لمتجه عشان يدخل طبقة عادية في الآخر.CNN بيقرا أرقام بخط الإيد
import torch from torch import nn from sklearn.datasets import load_digits from sklearn.model_selection import train_test_split torch.manual_seed(0) digits = load_digits() # 1797 images, 8x8, digits 0-9 X = torch.tensor(digits.images / 16.0, dtype=torch.float32).unsqueeze(1) # (N, 1, 8, 8) y = torch.tensor(digits.target) X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.25, random_state=0) model = nn.Sequential( nn.Conv2d(1, 16, kernel_size=3, padding=1), nn.ReLU(), # 16 learned filters nn.MaxPool2d(2), # 8x8 -> 4x4 nn.Conv2d(16, 32, kernel_size=3, padding=1), nn.ReLU(), nn.MaxPool2d(2), # 4x4 -> 2x2 nn.Flatten(), nn.Linear(32 * 2 * 2, 10), # 10 classes ) loss_fn = nn.CrossEntropyLoss() # multi-class (softmax inside) opt = torch.optim.Adam(model.parameters(), lr=0.01) for epoch in range(30): model.train() for i in range(0, len(X_tr), 64): opt.zero_grad() loss_fn(model(X_tr[i:i + 64]), y_tr[i:i + 64]).backward() opt.step() model.eval() with torch.no_grad(): preds = model(X_te).argmax(dim=1) print(f"test accuracy: {(preds == y_te).float().mean():.1%}") print("first 10 predictions:", preds[:10].tolist()) print("true labels: ", y_te[:10].tolist())
🤖 نفس الكود، صور أكبرالأرقام هنا ٨×٨ بس. لصور حقيقية بتزود طبقات Conv وقنوات، والكود نفسه تقريباً مابيتغيرش. وفي الغالب مش هتبني CNN من الصفر أصلاً، هتستخدم موديل متدرب جاهز، ودي فكرة آخر درس.