توسيع البيانات
درس 4 من 7
4توسيع البيانات
الصورة الكبيرةالتوسيع يصنع أمثلة تدريب جديدة من القديمة بتحويلات لا تغيّر الجواب: إزاحة، قص، تغيير إضاءة. يعلّم النموذج ما يجب أن يتجاهله. لكن تحويلًا يغيّر المعنى، مثل قلب نص عربي أفقيًا، يعلّمه شيئًا خاطئًا تمامًا.
ماذا تشاهد في الرسم: الإزاحات العشوائية رفعت الدقة من 56.2% إلى 80.1% لأنها علّمت النموذج أن الموضع لا يهم. القلب الأفقي خفّضها إلى 54.8%، أسوأ من لا شيء، لأنه علّمه أن الرقم ومرآته فئة واحدة.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
لو لم يرَ النموذج إلا صور قطط تنظر لليمين، قد يرتبك حين يرى قطة تنظر لليسار. توسيع البيانات أن تصنع من الصورة الواحدة نسخًا كثيرة: مقلوبة ومقصوصة وأفتح وأغمق. لكن احذر: قلب صورة فيها كتابة عربية يجعل الكلام مقلوبًا ويفسد معناها.
تحويلات torchvision.transforms مثل RandomHorizontalFlip وRandomResizedCrop وColorJitter تُطبق عشوائيًا على بيانات التدريب فقط. التحويل الصالح يحفظ التسمية، فلا قلب للنصوص ولا للأرقام 6 و9، وتقيس أثره على التحقق لا على الإحساس.
قبل أن تبدأ
🚚Dataset و DataLoader
تعرف أن __getitem__ هو المكان الذي يُطبَّق فيه التحويل العشوائي على كل صورة.
🖼️الصورة كموتر
تعرف خط المعالجة الثابت للتقييم: تغيير الحجم والقص والتطبيع.
الفكرة الجوهرية
التوسيع يصنع أمثلة جديدة من القديمة. تتعلّم هنا السؤال الذي يسبق كل تحويل، ثم التحويلات الآمنة والهدّامة، ثم قياس الأثر، ثم أين يُطبَّق التوسيع.
1السؤال الوحيد: هل يبقى الجواب؟
قبل أي تحويل اسأل: لو طبّقته على الصورة، هل يبقى الجواب الصحيح كما هو؟ قطة مقلوبة أفقيًا ما زالت قطة، لكن 2 مقلوبًا ليس 2.
import numpy as np
two = np.array([
[0, 1, 1, 0],
[1, 0, 0, 1],
[0, 0, 1, 0],
[0, 1, 0, 0],
[1, 1, 1, 1],
])
for name, img in [("original", two), ("flipped", two[:, ::-1]), ("brighter", np.clip(two * 1.0 + 0.3, 0, 1))]:
print(name)
for row in img:
print(" " + "".join("#" if v >= 1 else ("+" if v > 0 else ".") for v in row))
الرقم 2 مقلوبًا صار شكلًا لا يشبه أي رقم، وقطعته المائلة صارت بالاتجاه المعاكس. أما رفع الإضاءة فأبقى الشكل كما هو تمامًا. هذا هو الفرق بين توسيع مفيد وتوسيع مدمّر، والقرار يعتمد على المسألة لا على المكتبة.
RandomHorizontalFlip في تلك الأمثلة دائمًا تقريبًا، وهو هنا يعكس الكلمات ويقلب «يسار» إلى «يمين».2تحويلات آمنة وأخرى هدّامة
القرار يتبع المهمة لا المكتبة. حين يكون الاتجاه أو الشكل هو الجواب، كما في النص والأرقام وإشارات الاتجاه، يصبح القلب خطأً.
انظر إلى الرسم: سؤال واحد يقسم المهام إلى فرعين.
القصّ العشوائي وتغيير الحجم، وتغيير بسيط في الإضاءة والتباين، وضجيج خفيف، ودوران صغير: هذه آمنة في أغلب المهام. والقصّ أنفعها عادةً.
احذر تغيير الألوان حين يكون اللون نفسه هو الجواب، مثل نضج الفاكهة. وفي الصور الطبية قد يكون اليمين واليسار هو الفرق كله.
3قِس كل تحويل
التوسيع يضع معرفتك داخل النموذج. قاعدة صحيحة توفّر عليه آلاف الأمثلة، وقاعدة خاطئة تعلّمه خطأً بنفس القوة.
لذلك نعامل كل تحويل مثل أي خاصية: ندرّب معه وبدونه، ونقارن على تحقّق ثابت.
200 صورة أرقام فقط للتدريب، وبيانات اختبار مُزاحة بكسلًا يمينًا أو يسارًا عشوائيًا، كأن الكاميرا لم تكن في المنتصف تمامًا. ثلاثة تدريبات: بلا توسيع، وبإزاحات عشوائية، وبقلب أفقي.
import numpy as np
from sklearn.datasets import load_digits
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
digits = load_digits()
X_tr, X_te, y_tr, y_te = train_test_split(digits.images, digits.target, train_size=200, random_state=0, stratify=digits.target)
rng = np.random.default_rng(0)
X_te_shifted = np.array([np.roll(img, rng.integers(-1, 2), axis=1) for img in X_te])
def shift(img):
return np.roll(img, rng.integers(-1, 2), axis=1)
def flip(img):
return img[:, ::-1]
def fit_and_score(extra=None):
imgs, labels = list(X_tr), list(y_tr)
if extra:
imgs += [extra(img) for img in X_tr]
labels += list(y_tr)
model = LogisticRegression(max_iter=3000).fit(np.array(imgs).reshape(len(imgs), -1), labels)
return model.score(X_te_shifted.reshape(len(X_te), -1), y_te)
print(f"no augmentation: {fit_and_score():.3f}")
print(f"random 1px shifts: {fit_and_score(shift):.3f}")
print(f"horizontal flips: {fit_and_score(flip):.3f}")
بلا توسيع 56.2%: النموذج حفظ الأرقام في موضعها الدقيق، فبكسل واحد من الإزاحة أفسد نصف تنبّؤاته. الإزاحات العشوائية رفعته إلى 80.1%، لأنها علّمته بالضبط ما يحتاجه: الموضع لا يهم. القلب الأفقي خفّضه إلى 54.8%، أسوأ من لا شيء، لأنه علّمه أن الرقم ومرآته نفس الفئة. نفس البيانات ونفس النموذج، والفرق قرار توسيع واحد.
تحويل لا يحسّن الرقم يُحذف، مهما بدا منطقيًا.
4على التدريب فقط
التوسيع عشوائي ويتغيّر في كل دورة، ويُطبَّق على التدريب وحده. التحقّق والاختبار يمرّان دائمًا بنفس المعالجة الثابتة.
انظر إلى الرسم: الجزء العشوائي هو التدريب فقط. لو طبّقته على التحقّق لقست هدفًا متحرّكًا.
from torchvision import transforms
train_tf = transforms.Compose([
transforms.RandomResizedCrop(224, scale=(0.7, 1.0)),
transforms.ColorJitter(brightness=0.2, contrast=0.2),
transforms.RandomRotation(8),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
])
eval_tf = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
])
خطّان منفصلان دائمًا: عشوائي للتدريب، وثابت للتقييم. لاحظ غياب RandomHorizontalFlip: هذا خط لصور فيها نص عربي أو أرقام. ولاحظ أن الدوران 8 درجات فقط، لأن 90 درجة تحوّل 6 إلى 9.
كيف يعمل، خطوة بخطوة: كيف تختار توسيعًا يساعد ولا يضر
لكل تحويل اسأل: إن طبّقته على صورة، هل يبقى الجواب الصحيح كما هو؟
ابنِ خطّين منفصلين: عشوائي للتدريب، وثابت للتحقّق والاختبار.
ارسم 16 صورة من DataLoader بعد التوسيع وتأكّد أنك ما زلت تعرف جواب كل واحدة.
درّب مع التحويل وبدونه وقارن على تحقّق ثابت، واحذف أي تحويل لا يحسّن الرقم.
مثال محلول
السؤال الوحيد قبل أي تحويل: لو طبّقته على صورة، هل يبقى الجواب الصحيح كما هو؟ قطة مقلوبة أفقيًا ما زالت قطة. الرقم 2 مقلوبًا ليس 2.
جرّب بلا كود
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| RandomHorizontalFlip آمن لأنه في كل مثال على الإنترنت. | تلك الأمثلة لصور الحيوانات. على النصوص والأرقام وإشارات الاتجاه يعكس الكلمات ويحوّل «يسار» إلى «يمين». |
| كلما زاد التوسيع كان أفضل. | توسيع قوي جدًّا يصنع صورًا لا تعرف أنت جوابها، ويحتاج دورات أكثر ليثمر. الحكم هو دقة التحقّق، لا القوة. |
| طبّق نفس التوسيع على التحقّق ليكون القياس عادلًا. | التوسيع العشوائي على التحقّق يعطي رقمًا مختلفًا في كل تقييم، فلا تستطيع مقارنة نموذجين وقد تحفظ «أفضل نموذج» بالحظ. |
جرّب بنفسك
مصنّف لصور الطيور. أي تحويل الأقل أمانًا؟
الطائر مقلوبًا ما زال نفس الطائر، والقص والإضاءة الخفيفة آمنة. لكن إن كان اللون نفسه هو ما يميّز نوعًا عن آخر، فحذفه يحذف الجواب. القاعدة واحدة: هل يبقى الجواب كما هو؟
أضفت توسيعًا قويًا فانخفضت دقة التدريب كثيرًا وبقيت دقة التحقّق كما هي. ماذا يعني هذا؟
دقة التدريب مع توسيع قوي تقيس أداءً على صور أصعب من الواقع، فانخفاضها طبيعي. المعيار هو التحقّق: لم يتحسّن، فهذا التوسيع لا يساعد بهذه القوة. جرّب نسخة أخف، أو تدريبًا أطول، فالتوسيع القوي يحتاج دورات أكثر ليؤتي ثماره.
بطاقات مراجعة سريعة
تحقق من فهمك
تبني نموذجًا يقرأ لوحات السيارات السعودية (فيها حروف عربية ولاتينية وأرقام). أي توسيع تستخدم وأيها تتجنّب؟
استخدم: تغيير الإضاءة والتباين (ليل ونهار)، والضبابية الخفيفة (حركة)، والإزاحة والقص الصغير، والدوران الصغير والمنظور الخفيف (زاوية الكاميرا)، والضجيج. تجنّب: القلب الأفقي والرأسي تمامًا لأنهما يعكسان الحروف والأرقام، والدوران الكبير، والقص الذي قد يقطع حرفًا من الطرف فيتغيّر الجواب.
لماذا حُدّد الدوران في خط الدرس بـ 8 درجات فقط؟
لأن الدوران الكبير قد يغيّر الجواب، فدوران 90 درجة أو أكثر قد يحوّل 6 إلى 9. الدوران الصغير يحاكي ميل الكاميرا دون أن يغيّر المعنى.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.