סדנה בלמידה עמוקה — סיכום אינטראקטיבי

ד"ר ענת גולדשטיין · אוניברסיטת אריאל · M.Sc הנדסת ידע ונתונים · תשפ"ו

סקירה כוללת של הקורס

3 נקודות זכות 10% מטלות 30% פרויקט 60% מבחן

מטרת הקורס: ללמוד את יסודות הלמידה העמוקה (Deep Learning) המהווה נדבך מרכזי בתחום הבינה המלאכותית. הקורס משלב לימוד תאורטי ויישום ב-Python באמצעות TensorFlow, Keras ו-PyTorch.

מה תדע לעשות בסוף הקורס

  • להסביר מושגי יסוד בלמידה עמוקה
  • לבנות ולאמן רשתות נוירונים (NN)
  • להשתמש בשיטות לאופטימיזציה של רשתות עמוקות
  • לתאר ארכיטקטורות מרכזיות ולהתאים את השימוש בהן לבעיות AI שונות
  • לפתור בעיות AI: סיווג תמונה, עיבוד שפה טבעית, עיבוד קול

מפת הלמידה — היכן אנחנו

#נושאסטטוסמטלה / אבן דרך
1מבוא לקורס וללמידה עמוקהנלמדמטלה 1 (סימון מושגים) ✓
2רשת נוירונים: רדודה ועמוקה (חלק א')נלמדמטלה 2 (רשימת מושגים) ✓
3רשת נוירונים: שכבות נסתרות (חלק ב')נלמד
4אופטימיזציה של NNנלמד
5סביבות פיתוח · Keras / TensorFlowנלמדמטלה 3 (פתוחה)
6NN with Keras: סיווג ורגרסיה מלאנלמדהצעה לפרויקט
7CNN — Computer Visionנלמד
8Sequence Models · RNN · Time Seriesבקרוב
9DL for Text · Word Embeddingsבקרוב
10Transformers · Attentionבקרוב
11Hugging Face · הצגת פרויקטיםבקרובהגשת פרויקט

איך להשתמש בקובץ הזה

טאבים בראש הדף מאפשרים מעבר בין הסקירה, ההרצאות ומילון המושגים.
תקצירים מקופלים: לחיצה על כל פסקה הנפתחת ב-▸ חושפת הסבר מורחב.
מילון מושגים: חיפוש חופשי וסינון לפי קטגוריה. כל כרטיס מתרחב בלחיצה.
תוכן עניינים: הכפתור הכחול בפינה שמאלית למטה פותח ניווט מהיר.

הרצאה 1 — מבוא ללמידה עמוקה

מצגת 1 37 שקפים תאריך: 19.03.2026

מטרת ההרצאה: לתת תמונת רקע — מהי בינה מלאכותית (AI), היכן יושבת בתוכה למידת מכונה (ML) ולמידה עמוקה (DL), אילו בעיות DL פותרת, ומדוע השדה התפוצץ בעשור האחרון. בנוסף, חזרה תמציתית על סוגי בעיות ב-ML לפני הצלילה ל-NN בשיעור הבא.

על מה נדבר בפרק הזה

  1. הקשר היסטורי: AI → ML → DL — איך הגענו לכאן.
  2. שני היתרונות הגדולים של DL ומדוע הן מהותיות.
  3. למה דווקא ב-2010 — משולש החומרה, הנתונים והאלגוריתמים.
  4. רענון מהיר של סוגי בעיות ב-ML (Supervised / Unsupervised, Classification / Regression).
  5. שיטות פתרון מסורתיות אל מול NN — מתי כל אחת מתאימה.
  6. הסביבה הטכנית: Python, Colab, GitHub, Frameworks.

1. הקשר היסטורי — AI, ML, DL

כשמדברים על "בינה מלאכותית" יש שלוש שכבות מקוננות שחשוב לא לבלבל ביניהן:

תקופהפרדיגמההרעיון המרכזי
שנות ה-50AI קלאסי (Symbolic AI)אדם מנסח חוקים מפורשים, המחשב מבצע אותם. עובד היטב על משחקי לוח, נכשל על העולם האמיתי.
שנות ה-90Machine Learningפרדיגמה הפוכה: במקום לכתוב חוקים, נותנים למחשב דוגמאות (X, y) והוא לומד את החוקים. הופיעו עצי החלטה, SVM, Random Forest.
2010 ואילךDeep Learningשימוש ברשתות נוירונים עמוקות (רעיון משנות ה-80) — שעכשיו מסוגלות לעבוד מצוין הודות ל-GPU, אינטרנט (נתונים) ואלגוריתמים.
הרחבה: מבחן טיורינג והחזון המקורי של AI

אלן טיורינג ב-1950 הציע את "משחק החיקוי" — חוקר מנהל דיאלוג טקסטואלי עם שני גורמים סמויים, אדם ומכונה. אם החוקר אינו מסוגל לקבוע מי האדם — המכונה עברה את המבחן. החזון של טיורינג היה לא רק לבנות מערכות חוקים אלא שמחשבים יוכלו ללמוד בעצמם — חזון שלא היה אפשרי לממש בכליו של זמנו, ומומש בשלבים: ML בשנות ה-90, DL בשנות ה-2010.

למה זה רלוונטי לקורס? כי שלוש הפרדיגמות עדיין חיות זו לצד זו: כשבונים מערכת אמיתית בארגון, חלקה תהיה חוקים מפורשים (Rule-based), חלקה ML קלאסי (כש"רץ קליל" מספיק), וחלקה DL (כשנדרשת הבנה של תמונה / טקסט / קול).

הרחבה: הדוגמה של "חתול או כלב" — למה הגישה הקלאסית קרסה

גישה קלאסית: if shape == triangle and dist == X: return "cat". הבעיה — חתול יכול להיות עומד, יושב, מגב הפוך, חצי מוסתר, בתאורה גרועה, גזע פרסי, חתלתול וכו'. אי אפשר לכתוב חוקים שיכסו את כל המקרים.

גישת ML: נותנים למודל אלפי תמונות מתויגות (label), והמודל לומד מהן את ה"חוקים" הסטטיסטיים. ב-DL — הרשת לומדת בעצמה גם מה הם המאפיינים שכדאי להסתכל עליהם (Feature Learning), בלי שאנחנו נדרשים להגדיר אותם — וזה היתרון העצום של DL על פני ML הקלאסי.

2. שני היתרונות הגדולים של DL

כשהקורס מדבר על "מהפכת ה-DL" החל מ-2010, היא נשענת על שני יתרונות מובחנים:

(א) ביצועים פורצי דרך בבעיות שהיו "בלתי פתירות"

הדוגמה הקלאסית — ImageNet challenge. ב-2011 הביצועים הטובים ביותר בסיווג תמונות עמדו על 74% דיוק. ב-2012 הופיעה AlexNet (רשת CNN עמוקה) ושברה את השיא. עד 2015 הביצועים הגיעו ל-96.4%, ועברו את הביצועים האנושיים. אותה קפיצה התרחשה ב:

  • תמונה: סיווג, זיהוי אובייקטים, סגמנטציה, תיאור תמונה במילים.
  • טקסט: תרגום מכונה, מענה על שאלות (Q&A), דמיון בין מסמכים, סיכום.
  • קול: Speech-to-Text, סינתזת קול.
  • גנרציה: Deep Fakes, יצירת תמונות וסרטונים, מודלי שפה.
  • קבלת החלטות: רכבים אוטונומיים (Reinforcement Learning + DL).

(ב) ביטול הצורך ב-Feature Engineering

ב-ML קלאסי, רוב העבודה היא להגדיר ידנית מאפיינים (Features) טובים: "כמה זוויות חדות יש בתמונה", "כמה אדום יש", "כמה פעמים מופיעה המילה X". זה דורש מומחיות בתחום (domain expertise) וזמן רב. ב-DL — שכבות הרשת לומדות את המאפיינים הרלוונטיים בעצמן: השכבות הראשונות לומדות מאפיינים פשוטים (קצוות, צבעים), השכבות הבאות לומדות הרכבות (פינות, צורות), והעמוקות לומדות מושגים סמנטיים (אוזן, עין, פנים).

החיסרון: למה DL היא "קופסה שחורה"

ביכולת ללמוד מאפיינים בעצמה יש מחיר: קושי להסביר את ההחלטה של המודל. כשעץ החלטה מחליט "המחיר גבוה כי כן בית מעל 4 חדרים בשכונה X" — קל להבין. כשרשת עם 50 שכבות ו-100 מיליון פרמטרים מחליטה "זו תמונת חתול" — קשה מאוד לדעת בדיוק מה בתמונה הוביל לכך. תחום שלם בשם Explainable AI (XAI) מתעסק בכך, ובארגונים מסוימים (רפואה, ביטוח, פיננסים) חוסר השקיפות חוסם את אימוץ DL לטובת מודלים פשוטים יותר.

3. למה DL פרצה דווקא ב-2010?

הרעיונות התאורטיים של רשתות נוירונים קיימים כבר מ-1958 (Rosenblatt) ושל Backpropagation מ-1986 (Rumelhart, Hinton, Williams). אז למה רק עכשיו? משולש סיבות:

גורםמה השתנה
חומרהGPUs במקור פותחו לעיבוד גרפיקה במשחקים, אבל מתאימים בדיוק לפעולות מטריצה — בדיוק מה שרשת נוירונים זקוקה לו. NVIDIA CUDA (2007) הפכה את ה-GPU לזמין למחקר. CPU לוקח שעות, GPU מבצע אותו אימון בדקות.
נתוניםהאינטרנט פתח ברז של נתונים מתויגים. ImageNet (Fei-Fei Li, 2009) הביא 14 מיליון תמונות מתויגות ב-1000 קטגוריות — מה שאיפשר לאמן רשתות עמוקות באמת.
אלגוריתמיםשיפורים שייתרו את הבעיה של "Vanishing Gradients" — ReLU במקום sigmoid, אתחולים חכמים, נורמליזציה של batch, Dropout. כל אלה הפכו רשתות עמוקות מאומנות־בקושי לרשתות שמתאמנות בקלות.
הרחבה: "אבות ה-AI" וזוכי פרס טיורינג 2018

פרס טיורינג 2018 הוענק לשלושת המובילים שהביאו את DL לעולם:

  • Geoffrey Hinton — "סבא של ה-DL", פיתח Backpropagation, Boltzmann Machines, AlexNet.
  • Yann LeCun — אבי ה-CNN (LeNet), כיום ראש מחקר ב-Meta AI.
  • Yoshua Bengio — מודלי שפה, RNN, Attention.

שמות נוספים שנזכיר בהמשך: François Chollet (יוצר Keras, מחבר ספר הקורס), Andrew Ng (Coursera, Google Brain), Ian Goodfellow (יוצר GANs).

4. רענון: סוגי בעיות ב-ML

לפני שצוללים ל-NN, חשוב למקם אותן במפת ה-ML. רוב הבעיות שנפתור בקורס הן Supervised Learning, ובתוכן בעיקר Classification ו-Regression.

קטגוריית-עלסוג בעיהדוגמההפלט
Supervised
(יש תיוג ידוע)
Classification"האם הסטודנט יתקבל לאוניברסיטה?" / "חתול / כלב / דג"קטגוריה דיסקרטית
Regression"מה מחיר הדירה?"מספר רציף
Unsupervised
(אין תיוג)
Clustering"חלק את הלקוחות ל-5 קבוצות התנהגותיות"קבוצות
Association Rules"לקוחות שקנו A קונים גם B"חוקים

הבעיה הקלאסית של סיווג שלובה ברחבי הקורס: הקבלה לאוניברסיטה

נתונים: לכל סטודנט שני ערכי קלט — ציון בגרות, ציון מבחן קבלה. תיוג: התקבל / נדחה. מטרה: למצוא קו מפריד במישור הדו-ממדי שיפריד בצורה הטובה ביותר בין נקודות "התקבל" לנקודות "נדחה".

Prediction: ŷ = 1 if Wx + b ≥ 0, else 0

זוהי בדיוק הצורה של פרספטרון — אבן הבניין של רשתות נוירונים שנלמד בהרצאה הבאה.

הבעיה הקלאסית של רגרסיה: מחיר נכס

נתונים: מספר חדרים, אזור, שטח, גיל הבניין. תיוג: מחיר. מטרה: למצוא פונקציה ƒ(x) → price שתחזה הכי טוב את המחיר. ב-DL הפלט הוא מספר רציף, ולעיתים נכפה אותו להיות אי-שלילי על ידי ReLU בפלט.

למה צריך קו ישר למחיר ולא קו עקום?

קו ישר הוא הנחה (assumption) בעיית רגרסיה לינארית. בפועל היחסים בעולם לא תמיד לינאריים — ולכן ב-DL נשתמש ברצף שכבות עם פונקציות אקטיבציה לא-לינאריות (כפי שנראה בהרצאות 2-3) שמסוגלות לקרב כל פונקציה רציפה (Universal Approximation Theorem).

5. שיטות פתרון מסורתיות לעומת NN

ל-Classification ו-Regression יש משפחות של פותרים — ולמעשה כל קורס ML עובר עליהם. לכל אחת חוזקות וחולשות שונות:

  • עץ החלטה — מפצל את הנתונים על סף ערך בכל שלב. שקוף וקריא; אבל overfit אם לא מגוזם.
  • Random Forest — אנסמבל של עצים. חזק ויציב על נתונים טבלאיים.
  • SVM — מוצא את הקו עם השוליים הרחבים ביותר בין המחלקות. עובד טוב במימדים גבוהים.
  • KNN — מסווג נקודה לפי k השכנים הקרובים אליה. אינטואיטיבי, אבל איטי בזמן חיזוי.
  • רגרסיה לוגיסטית — סיגמואיד על קומבינציה לינארית של הקלטים. אבן הבניין של פרספטרון יחיד.
  • רשת נוירונים — מסוגלת ללמוד פונקציות מורכבות מאוד; דורשת הרבה נתונים ומשאבי חישוב.
תובנה חשובה: רשת נוירונים אינה תמיד הכלי הנכון. על נתונים טבלאיים מובנים עם פחות מ-10K דגימות — Random Forest / XGBoost לרוב מנצחים DL. DL מנצחת כאשר הקלט הוא תמונה / טקסט / קול / רצף ויש מספיק נתונים.

6. הסביבה הטכנית של הקורס

כליתפקיד
Python 3שפת התכנות. כל ה-frameworks הגדולים תומכים בה.
Anacondaמנהל סביבות. מתקין Python + ספריות מדעיות בקליק.
Google Colabסביבת Jupyter בענן, חינמית, עם גישה ל-GPU. הסביבה המומלצת לכל הקורס.
GitHubגרסאות וניהול קוד.
TensorFlow / Keras / PyTorchה-frameworks. נלמד אותם החל מהרצאה 5.

ספר הקורס: Deep Learning with Python מאת François Chollet — נכתב על ידי יוצר Keras עצמו. הקוד של הספר זמין ב-github.com/fchollet/deep-learning-with-python-notebooks.

7. שאלות חזרה למבחן — הרצאה 1

ש1. הסבר את ההבדל בין AI, ML ו-DL.
הצג תשובה
AI הוא קטגוריית-על (כל מערכת שמחקה אינטליגנציה אנושית, גם מבוססת חוקים). ML היא תת-קבוצה של AI שלומדת חוקים מנתונים במקום שיתכנתו אותם ידנית. DL היא תת-קבוצה של ML שמשתמשת ברשתות נוירונים עמוקות (מספר שכבות נסתרות) ולומדת בעצמה גם את המאפיינים (Features) הרלוונטיים, מה שמייתר Feature Engineering ידני.
ש2. מנה שלושה גורמים שאיפשרו את פריצת DL ב-2010.
הצג תשובה
(1) חומרה — GPUs מאפשרים אימון מהיר פי עשרות מ-CPU. (2) נתונים — האינטרנט ו-ImageNet סיפקו מאות מיליוני דגימות מתויגות. (3) אלגוריתמים — ReLU, Dropout, אתחולים חכמים ושיטות נורמליזציה פתרו את בעיית ה-Vanishing Gradient.
ש3. נתון: סטודנט עם בגרות 70 ומבחן 60. הקו המפריד הוא 2x₁ + x₂ – 18 = 0 כאשר x₁=ציון מבחן, x₂=ציון בגרות. האם יתקבל?
הצג תשובה
2·60 + 70 – 18 = 120 + 70 – 18 = 172 ≥ 0 → ŷ = 1, מתקבל. (זו דוגמת הפרספטרון הקלאסית של הקורס.)
ש4. למה DL נחשבת "קופסה שחורה" וב-אילו תחומים זה מהווה בעיה?
הצג תשובה
קשה להסביר את ההחלטה של רשת עמוקה כי היא מבוססת על מיליוני פרמטרים שאינטראקציות ביניהם אינן ניתנות לפרשנות פשוטה. זה מהווה בעיה ברפואה (חובת הסבר), פיננסים וביטוח (חוק GDPR מחייב הסבר על החלטה אוטומטית), ובמערכות בטיחות קריטיות.

8. מה תצא מן הפרק הזה

  • תדע למקם DL במפת ה-AI ולהסביר את ההיררכיה AI ⊃ ML ⊃ DL.
  • תוכל להסביר את שני היתרונות של DL: ביצועים פורצי דרך + ביטול Feature Engineering.
  • תזהה אילו בעיות מתאימות ל-DL (תמונה, טקסט, קול, רצף) ואילו לא (טבלאי קטן).
  • תכיר את מפת ה-ML: Classification, Regression, Clustering, Association.
  • תהיה מוכן להבין את שני הסיפורים הרצים בקורס: הקבלה לאוניברסיטה (סיווג) ו-מחיר נכס (רגרסיה).
  • תהיה מסוגל להסביר במשפט אחד את משולש החומרה / הנתונים / האלגוריתמים.
גשר להרצאה הבאה: בהרצאה 2 נלמד את אבן הבניין הבסיסית — Perceptron, איך הוא מחשב את הסיווג שלו (Wx+b), אילו פונקציות אקטיבציה אפשר להפעיל עליו, ואיך מאמנים אותו באמצעות Gradient Descent על פונקציית Loss רציפה.

הרצאה 2 — רשת נוירונים, חלק א'

מצגת 2 57 שקפים תאריך: 26.03.2026 ליבת הקורס

מטרת ההרצאה: להוריד את רשת הנוירונים ליסודותיה — הפרספטרון. נראה איך פרספטרון בודד מקבל החלטות, איך מייצגים שערים לוגיים, מה הבעיות שלו, ואיך עוברים מהחלטה דיסקרטית לפלט הסתברותי שאפשר לאמן באמצעות גרדיאנט.

על מה נדבר בפרק הזה

  1. פרספטרון: ההגדרה, הקשר לנוירון ביולוגי, חישוב פלט.
  2. פונקציית Step ושערים לוגיים (AND, OR, NOT, XOR).
  3. אלגוריתם הפרספטרון לעדכון משקלות (Perceptron Learning Algorithm).
  4. הצורך בפונקציית Loss רציפה — Sigmoid במקום Step.
  5. סיווג רב-מחלקתי עם Softmax ו-One-Hot Encoding.
  6. Maximum Likelihood, Log-Likelihood, Cross Entropy.
  7. Gradient Descent: האלגוריתם, החישוב, ועדכון המשקלות.
  8. השוואה בין Perceptron Algorithm ל-Gradient Descent.

1. הפרספטרון — אבן הבניין

פרספטרון הוא הצורה הפשוטה ביותר של רשת נוירונים: נוירון יחיד. הוא מקבל וקטור קלט x = (x₁, x₂, ..., xₙ), מכפיל כל קלט במשקל w, מסכם, מוסיף הטיה b, ומחזיר 0 או 1 לפי הסימן.

z = w₁x₁ + w₂x₂ + ... + wₙxₙ + b = Wx + b
ŷ = 1 if z ≥ 0, else 0 (Step function)

הקשר לנוירון ביולוגי

הקלטים x — דומים לדנדריטים שקולטים אותות חשמליים. המשקלות W — חוזק החיבור הסינפטי. הסכום והפלט — הפעולה של גוף התא והאקסון: אם ההצטברות החשמלית עברה סף, התא "יורה" אות.

חזרה לדוגמת הקבלה לאוניברסיטה כפרספטרון

קלטים: x₁ = ציון מבחן, x₂ = ציון בגרות. משקלות: w₁ = 2, w₂ = 1, b = –18. החלטה:

ציון קבלה = 2·(ציון מבחן) + 1·(ציון בגרות) – 18

אם הציון חיובי → התקבל. הקו 2x₁ + x₂ – 18 = 0 הוא הקו המפריד בין מתקבלים ללא מתקבלים. אימון פירושו לשנות את w₁, w₂, b כך שהקו יפריד טוב יותר.

2. שערים לוגיים כפרספטרונים

אפשר לחשוב על שערים לוגיים (AND, OR, NOT) כעל פרספטרונים פשוטים. למשל:

שערw₁w₂bהסבר
AND11–1.5שני הקלטים חייבים להיות 1 כדי שהסכום יעבור 1.5
OR11–0.5מספיק שאחד מהם 1
NOT–10.5היפוך של קלט יחיד
הבעיה הגדולה: שער XOR אינו ניתן לפתרון בפרספטרון יחיד! הנקודות (0,0), (1,1) צריכות לתת 0, ו-(0,1), (1,0) צריכות לתת 1 — אין שום קו ישר שמפריד ביניהן. זו הסיבה שצריך שכבות נסתרות ורשתות עמוקות יותר. הסיפור הזה הוא בדיוק מה שעצר את מחקר ה-NN בשנות ה-70 (Minsky & Papert, 1969) ופתח את הדרך לרשתות רב-שכבתיות.
למה צריך שני נוירונים כדי לפתור XOR?

אפשר להרכיב XOR משני קווים: קו של (OR) AND קו של (NOT AND). כלומר: "לפחות אחד מהם 1 וגם לא שניהם". זו בדיוק הדוגמה שמראה את הצורך בשכבה נסתרת — שכבה אחת מחשבת "ביניים" (OR ו-NAND), והשכבה השנייה מחברת אותם (AND).

3. אלגוריתם הפרספטרון

איך לומדים את המשקלות W? האלגוריתם המקורי של Rosenblatt (1958) הוא איטרטיבי:

  1. אתחל את W ו-b באקראי.
  2. לכל נקודה (xᵢ, yᵢ): חשב ŷ = step(Wxᵢ + b).
  3. אם הסיווג נכון → אל תשנה דבר.
  4. אם הנקודה סווגה כ"חיובית" אך באמת "שלילית": הפחת α·xᵢ מ-W ו-α מ-b.
  5. אם הנקודה סווגה כ"שלילית" אך באמת "חיובית": הוסף α·xᵢ ל-W ו-α ל-b.
  6. חזור עד שאין שגיאות (או עד שעברת מספר epochs קבוע).

α (אלפא) הוא קצב הלמידה (learning rate) — קובע כמה גדול כל צעד תיקון.

הבעיה של האלגוריתם הזה

הוא עובד רק כשהנתונים ניתנים להפרדה לינארית. אם הנתונים לא לינאריים — האלגוריתם לא מתכנס. בנוסף, פונקציית Step אינה גזירה (יש "קפיצה" מ-0 ל-1) ולכן לא ניתן להחיל עליה כלים גנריים יותר של אופטימיזציה. לכן עוברים לפונקציה רציפה — Sigmoid.

4. מ-Step ל-Sigmoid: מהחלטה דיסקרטית להסתברות רציפה

במקום שהפרספטרון יחזיר 0/1, נרצה שיחזיר הסתברות בין 0 ל-1. הפונקציה שהופכת מספר אמיתי כלשהו להסתברות היא Sigmoid:

σ(z) = 1 / (1 + e^(–z))
  • z גדול וחיובי → σ ≈ 1
  • z = 0 → σ = 0.5
  • z גדול ושלילי → σ ≈ 0

למה זה כל כך חשוב?

  1. גזירות — סיגמואיד גזירה בכל מקום, ולכן ניתן לחשב גרדיאנטים ולעדכן משקלות בצורה הדרגתית.
  2. פרשנות — הפלט הופך להסתברות. במקום "התקבל / נדחה" אפשר להגיד "סיכוי 73% להתקבל".
  3. נגזרת נחמדה — σ′(z) = σ(z)·(1 – σ(z)), חישוב יעיל מאוד כשמבצעים backpropagation.

5. סיווג רב-מחלקתי: Softmax ו-One-Hot

Sigmoid מתאימה לסיווג בינארי (כן/לא). מה אם יש 3 מחלקות (חתול / כלב / דג)? משתמשים ב-Softmax:

P(class i) = e^(zᵢ) / Σⱼ e^(zⱼ)

למשל: אם הפלטים הלינאריים הם 2, 1, 0 לחתול / כלב / דג בהתאמה:

  • P(חתול) = e²/(e²+e¹+e⁰) ≈ 7.39/11.11 ≈ 0.67
  • P(כלב) = e¹/11.11 ≈ 0.24
  • P(דג) = e⁰/11.11 ≈ 0.09

המכנה של softmax מבטיח שהסכום הוא 1, והאקספוננט מבטיח ערכים חיוביים גם כש-z שלילי (פתרון לבעיה של מנה שלילית/אפס).

One-Hot Encoding לתיוגים

איך מייצגים את התשובה הנכונה? במקום מספר (חתול=0, כלב=1, דג=2) — וקטור של 0 ו-1 בלבד:

חתול → [1, 0, 0], כלב → [0, 1, 0], דג → [0, 0, 1]

ככה אפשר להשוות וקטור-לוקטור (החזוי מול האמיתי) באמצעות פונקציית Loss כמו Cross Entropy.

למה לא לקודד "כלב=1, חתול=2, דג=3" כמספרים רגילים?

כי המודל "ילמד" שדג (3) רחוק מחתול (2) קצת יותר מכלב (1) — וזו שגיאה: אין סדר אמיתי בין המחלקות. One-Hot מבטיח ש"מרחק" בין כל זוג מחלקות זהה.

6. מ-Maximum Likelihood ל-Cross Entropy

איך מודדים "כמה טוב" המודל שלנו? הרעיון: אם המודל אומר ש-Pᵢ הוא הסיכוי שדגימה i תגיע מהמחלקה הנכונה, אז הסיכוי הכולל שכל הדגימות יקבלו את התיוגים הנכונים הוא:

Likelihood = P₁ · P₂ · ... · Pₘ

"מודל טוב" = מודל שממקסם את ה-Likelihood (Maximum Likelihood Estimation).

הבעיה: מכפלות של מספרים קטנים

במציאות יש אלפי דגימות, ומכפלת אלפי הסתברויות (כל אחת בין 0 ל-1) מתכנסת מהר ל-0.000...001 — מספר שאפילו המחשב לא מצליח לייצג. הפתרון: לוקח לוגריתם. תכונה של log:

log(α · β) = log(α) + log(β)

וכך מכפלות הופכות לסכומים, וסכומים מנוהלים בקלות.

Cross Entropy

אם נכפיל את ה-log-likelihood ב-(–1) (כדי שיהיה מספר חיובי שאפשר להמזער), נקבל את Cross Entropy:

CE = –Σᵢ [yᵢ · ln(ŷᵢ) + (1–yᵢ) · ln(1–ŷᵢ)]

איך לקרוא את הנוסחה (סיווג בינארי): אם הדגימה האמיתית היא חיובית (yᵢ=1), הביטוי השני מתאפס ונשאר רק –ln(ŷᵢ) — שמודד עד כמה ŷ קרוב ל-1. אם הדגימה היא שלילית (yᵢ=0), נשאר רק –ln(1–ŷᵢ) — שמודד עד כמה ŷ קרוב ל-0.

אינטואיציה: CE קטן ⇔ מודל טוב. CE גדול ⇔ המודל אומר הסתברויות "בטוחות" שגויות.

דוגמה מספרית של CE

אם התיוג האמיתי הוא [1,1,0,1] והמודל חזה [0.9, 0.7, 0.1, 0.8]:

CE = –[ln(0.9) + ln(0.7) + ln(1–0.1) + ln(0.8)] ≈ 0.79

אם המודל חזה הסתברויות גרועות יותר [0.1, 0.7, 0.9, 0.2] (הוא בטוח בהפך):

CE = –[ln(0.1) + ln(0.7) + ln(1–0.9) + ln(0.2)] ≈ 6.65

שגיאה גבוהה משקפת חיזוי גרוע יותר.

Cross Entropy למשתנה קטגוריאלי

CE = –Σᵢ Σⱼ yᵢⱼ · ln(pᵢⱼ)

כאשר i רץ על המחלקות, j על הדגימות. בגלל ה-One-Hot, רק איבר אחד מ-yᵢⱼ הוא 1 לכל דגימה — ולכן זה מתפשט ל"ln של ההסתברות שהמודל נתן למחלקה הנכונה".

7. Gradient Descent — האימון בפועל

יש לנו מודל (Wx+b → סיגמואיד → ŷ) ופונקציית שגיאה E(W, b). המטרה: למצוא את ה-W וה-b שממזערים את E. השיטה: Gradient Descent.

האינטואיציה

דמיין את E כמשטח תלת-ממדי: צירי X = w₁ ו-w₂, וציר Z = ערך השגיאה. אנחנו עומדים על המשטח באקראי, ורוצים לרדת לעמק (המינימום). בכל צעד מסתכלים על השיפוע (גרדיאנט) ויורדים בכיוון ההפוך לו.

הנוסחה לעדכון

wᵢ ← wᵢ – α · ∂E/∂wᵢ
b ← b – α · ∂E/∂b

כאשר ∂E/∂wᵢ הוא הנגזרת החלקית של השגיאה ביחס ל-wᵢ (מחושבת באמצעות כלל השרשרת, נראה בהרחבה בהרצאה 3), ו-α הוא קצב הלמידה.

חישוב הגרדיאנט (פרספטרון יחיד עם sigmoid + cross entropy)

אחרי שימוש בכלל השרשרת, מתקבל ביטוי יפה ופשוט במיוחד:

∂E/∂wᵢ = –(y – ŷ) · xᵢ

ומכאן עדכון המשקל:

wᵢ ← wᵢ + α · (y – ŷ) · xᵢ

הסבר: הפרש (y – ŷ) הוא "כמה טעינו" בכיוון. אם המודל אמר 0.3 ובאמת y=1 → ההפרש 0.7 → תיקון בגדל xᵢ. אם המודל אמר 0.95 ובאמת y=1 → ההפרש 0.05 → תיקון זעיר. ככל שהמודל קרוב לאמת — התיקון קטן יותר.

האלגוריתם המלא

  1. אתחל את W, b באקראי.
  2. לכל epoch: לכל נקודה (xᵢ, yᵢ) — חשב ŷᵢ = σ(Wxᵢ + b).
  3. חשב את השגיאה ואת הגרדיאנטים.
  4. עדכן: wᵢ ← wᵢ + α(y – ŷ)xᵢ, b ← b + α(y – ŷ).
  5. חזור עד שהשגיאה קטנה מספיק או שהגעת למספר epochs מקסימלי.

8. השוואה: Perceptron Algorithm vs. Gradient Descent

Perceptron AlgorithmGradient Descent על Sigmoid
אקטיבציהStep (דיסקרטית)Sigmoid (רציפה)
על אילו נקודות מעדכןרק על נקודות שגויותעל כל הנקודות
גודל העדכוןα·xᵢ קבוע (אם שגויה)α·(y–ŷ)·xᵢ — פרופורציוני לטעות
מתכנס תמיד?רק על נתונים ניתנים-להפרדה לינאריתמתכנס למינימום (לוקלי או גלובלי)
הכללה לרשתות עמוקותלא ניתנת להכללהבסיס ל-Backpropagation

תובנה: Gradient Descent עם sigmoid הוא בעצם רגרסיה לוגיסטית. כך שפרספטרון אחד עם sigmoid + cross entropy + GD = רגרסיה לוגיסטית קלאסית. רשת נוירונים = הרבה רגרסיות לוגיסטיות מקושרות זו לזו.

9. שאלות חזרה למבחן — הרצאה 2

ש1. למה לא ניתן ללמוד XOR בעזרת פרספטרון יחיד?
הצג תשובה
פרספטרון יחיד יכול לייצג רק קו ישר כקו מפריד. הנקודות של XOR אינן ניתנות להפרדה על ידי שום קו ישר במישור. הפתרון הוא להשתמש בלפחות שכבה נסתרת אחת — שיוצרת מרחב חדש שבו הנתונים כן ניתנים להפרדה.
ש2. למה משתמשים ב-Cross Entropy ולא בסכום ריבועי השגיאות (MSE) בסיווג?
הצג תשובה
MSE על פלט סיגמואיד יוצרת משטח שגיאה לא קמור עם מינימה מקומיות והגרדיאנטים שלה הופכים קטנים מאוד כשהמודל בטוח־ושוגה (saturation). Cross Entropy "מענישה" בחומרה רבה יותר חיזוי שגוי בעוצמה גבוהה (המספר ln(0) שואף לאינסוף), והיא נותנת גרדיאנטים גדולים יותר במצבים האלה — מה שמאפשר לאלגוריתם להתאושש מהר יותר.
ש3. מודל חזה ŷ=0.2 עבור דגימה שתיוגה האמיתי y=1. אם x₁=3, α=0.1 — מהו עדכון המשקל w₁?
הצג תשובה
Δw₁ = α·(y–ŷ)·x₁ = 0.1 · (1–0.2) · 3 = 0.1·0.8·3 = 0.24. משמעות: w₁ עולה ב-0.24, מה שיגדיל את z בפעם הבאה ויקרב את ŷ ל-1.
ש4. מתי נשתמש ב-Sigmoid בשכבת הפלט ומתי ב-Softmax?
הצג תשובה
Sigmoid — בסיווג בינארי (פלט יחיד שהוא הסתברות בין 0 ל-1). Softmax — בסיווג רב-מחלקתי כשרוצים חלוקת הסתברות על פני n מחלקות (סכום = 1).
ש5. למה מתחילים אימון עם משקלות אקראיים ולא עם אפסים?
הצג תשובה
אם כל המשקלות שווים (במיוחד אפסים), כל הנוירונים בשכבה יחשבו אותו הדבר ויתעדכנו באותה צורה — ולכן הרשת תאבד את היכולת ללמוד תכונות שונות (Symmetry Breaking). אתחול אקראי מבטיח שכל נוירון יתחיל ממקום שונה ויפתח התמחות שונה.

10. מה תצא מן הפרק הזה

  • תוכל לצייר פרספטרון, לסמן עליו x, w, b, ולחשב את הפלט שלו על דוגמה מספרית.
  • תזהה ארבע פונקציות אקטיבציה ראשיות: Step, Sigmoid, Softmax, ReLU.
  • תסביר מתי משתמשים ב-Sigmoid (בינארי) ומתי ב-Softmax (רב-מחלקתי).
  • תכתוב את נוסחת Cross Entropy ותסביר למה היא מתאימה לסיווג.
  • תוכל לבצע צעד אחד של Gradient Descent ידנית: מחיזוי, דרך שגיאה, לעדכון משקלות.
  • תבחין בין Perceptron Algorithm ל-Gradient Descent ותסביר את היתרון של GD.
  • תזהה את החיבור בין רגרסיה לוגיסטית קלאסית ל-NN: רגרסיה לוגיסטית = פרספטרון אחד עם sigmoid + CE.
גשר להרצאה הבאה: ראינו שפרספטרון יחיד מוגבל למודלים לינאריים. בהרצאה 3 נוסיף שכבות נסתרות וניצור רשת עמוקה שמסוגלת ללמוד פונקציות מורכבות. נכיר את שני שלבי האימון — Feedforward ו-Backpropagation — והאופן שבו כלל השרשרת מאפשר חישוב גרדיאנטים גם ברשת בעלת עשרות שכבות.

הרצאה 3 — רשת נוירונים, חלק ב' (שכבות נסתרות)

מצגת 3 32 שקפים תאריך: 26.03.2026 ליבת הקורס

מטרת ההרצאה: לעבור מפרספטרון יחיד לרשת נוירונים עמוקה — עם שכבות נסתרות. נראה איך שכבות מאפשרות פתרון בעיות לא-לינאריות, איך עובד מעבר הקלט (Feedforward), ואיך מתעדכנים משקלות בכל שכבה (Backpropagation) באמצעות כלל השרשרת.

על מה נדבר בפרק הזה

  1. למה צריך שכבות נסתרות — שילוב מודלים לינאריים ליצירת מודל לא-לינארי.
  2. ארכיטקטורת רשת: שכבת קלט, שכבות נסתרות, שכבת פלט.
  3. Feedforward — חישוב הפלט שכבה אחרי שכבה.
  4. Backpropagation — האינטואיציה ואז המתמטיקה.
  5. כלל השרשרת ככלי מרכזי לחישוב גרדיאנטים.
  6. לולאת האימון השלמה: FF → Loss → BP → עדכון משקלות.
  7. סיבות אפשריות לכישלון מודל ומבט קדימה לאופטימיזציה.

1. שילוב פרספטרונים — למה זה עובד?

פרספטרון יחיד מציע קו ישר כגבול החלטה. אם נחבר שני פרספטרונים, כל אחד עם קו משלו, ונחבר את הפלטים שלהם — נקבל אזור לא-לינארי. דוגמה:

שם רשמי: רשת בעלת שכבה נסתרת אחת או יותר עם שכבות צפופות (Dense / Fully-Connected) ואקטיבציה לא-לינארית נקראת Multi-Layer Perceptron — MLP. זוהי הארכיטקטורה הסטנדרטית של feed-forward NN — וכל המודלים שנבנה בהרצאות הבאות (כולל המודלים על MNIST, IMDB, Reuters, Boston) הם MLPs. את החלופות (CNN לתמונות, RNN לרצפים) נכיר בהרצאות 7+.

נניח פרספטרון 1 נותן ŷ₁ = σ(7x₁ – 3x₂ – 1), פרספטרון 2 נותן ŷ₂ = σ(4x₁ – 2x₂ + 6). שילוב לינארי שלהם בשכבה הבאה: z = 7·ŷ₁ + 5·ŷ₂ – 6, ואז סיגמואיד.

ŷ = σ( 7·σ(7x₁ – 3x₂ – 1) + 5·σ(4x₁ – 2x₂ + 6) – 6 )

החלק הזה — הרכבה של פונקציות לא-לינאריות — הוא הסוד של עומק. בלי האקטיבציה הלא-לינארית, רצף שכבות היה מתפרק לכפל מטריצות אחד גדול ומתפקד כפרספטרון לינארי יחיד.

תאוריה: Universal Approximation Theorem

תוצאה תיאורטית מפורסמת (Cybenko 1989, Hornik 1991): רשת נוירונים בעלת שכבה נסתרת אחת בלבד ופונקציית אקטיבציה לא-לינארית מתאימה — מסוגלת לקרב כל פונקציה רציפה בכל דיוק רצוי, בהינתן מספיק נוירונים. בפועל, רשתות עמוקות (הרבה שכבות, פחות נוירונים בכל שכבה) יעילות הרבה יותר בלמידה מאשר רשת רחבה ושטוחה.

2. ארכיטקטורת רשת נוירונים

מבנה כללי של NN מורכב משלושה סוגי שכבות:

שכבהתפקידגודל אופייני
Input Layerקולטת את הקלט הגולמי. אין בה חישוב — רק מעבירה את ערכי x הלאה.כמספר התכונות (features) של הקלט
Hidden Layersהשכבות שבהן מתבצע הלימוד. כל נוירון מחבר את הפלטים מהשכבה הקודמת ומפעיל אקטיבציה.היפר-פרמטר — בוחרים בניסוי
Output Layerמפיקה את התחזית הסופית. מספר היחידות תלוי בבעיה.סיווג בינארי: 1 (sigmoid). רב-מחלקתי: n (softmax). רגרסיה: 1 (ללא אקטיבציה).

סימון מוסכם

נסמן ב-Wij(k) את המשקל של חיבור מהנוירון ה-i בשכבה k–1 לנוירון ה-j בשכבה k. למשל ברשת עם שכבה נסתרת אחת ושני קלטים, יש לנו מטריצות W⁽¹⁾ (קלט → נסתרת) ו-W⁽²⁾ (נסתרת → פלט).

סוגי ארכיטקטורות

  • שכבה נסתרת אחת + פלט יחיד: סיווג בינארי לא-לינארי במישור.
  • שכבה נסתרת + פלט מרובה (multi-class): סיווג ל-3 קטגוריות ומעלה.
  • 2+ שכבות נסתרות: Deep Network — לומדת ייצוגים היררכיים.
  • קלטים רבים (3D, 4D): תמונה / רצף — ידרוש שכבות מיוחדות (CNN, RNN) שנלמד בהמשך.

3. Feedforward — מעבר קדימה ברשת

Feedforward הוא תהליך החישוב של הפלט בהינתן קלט. שכבה אחרי שכבה:

  1. קלט x נכנס לשכבה הראשונה.
  2. בכל נוירון בשכבה הנסתרת: מחשבים z = Wx + b, מפעילים אקטיבציה σ(z).
  3. הפלט של כל נוירון הופך לקלט של השכבה הבאה.
  4. בשכבה האחרונה — מקבלים את התחזית ŷ.

בכתיב מטריציוני

עבור רשת עם שכבה נסתרת אחת:

h = σ(W⁽¹⁾x + b⁽¹⁾)
ŷ = σ(W⁽²⁾h + b⁽²⁾)

אפשר לרכז את כל החישוב בביטוי אחד מקונן:

ŷ = σ( W⁽²⁾ · σ( W⁽¹⁾x + b⁽¹⁾ ) + b⁽²⁾ )

חשוב להבין: Feedforward הוא חישוב של פונקציה — אין פה למידה. הלמידה תתרחש ב-Backpropagation, שיעדכן את W ו-b על סמך מידת השגיאה של ŷ ביחס ל-y.

4. Backpropagation — איך לומדים

האינטואיציה

נניח שהרצנו Feedforward ויצא ŷ שגוי. נשאלות שתי שאלות:

  1. מי "אשם" בשגיאה — איזו משקלת תרמה לה הכי הרבה?
  2. לאיזה כיוון לשנות כל משקלת כדי לשפר?

האינטואיציה החזותית: אם השכבה התחתונה תרמה יותר לחיזוי הנכון, ניתן לה משקל גדול יותר; אם השכבה העליונה תרמה לטעות, נחליש אותה. עבור כל משקל פנימי wij(k) — נשאל: "איך השגיאה תשתנה אם נזיז את המשקל הזה במעט?". זו הנגזרת החלקית ∂E/∂wij(k).

למה זה נקרא "לאחור"?

חישוב הגרדיאנט עבור משקל בשכבה האחרונה פשוט. עבור משקלות בשכבה הקודמת — צריך לעבור דרך החישובים שכבר נעשו בשכבה האחרונה. ולכן מתחילים מהפלט והולכים אחורה שכבה אחר שכבה. השגיאה "מתפזרת" אחורה דרך הרשת.

5. כלל השרשרת — הכלי המתמטי

אם h(x) = f(g(x)), אז הנגזרת:

dh/dx = (df/dg) · (dg/dx)

בהקשר שלנו, הקשר בין משקל w11(1) בשכבה הראשונה לבין השגיאה E עובר דרך שרשרת:

w11(1) → h₁ → h → ŷ → E

וכלל השרשרת נותן:

∂E/∂w₁₁⁽¹⁾ = (∂E/∂ŷ) · (∂ŷ/∂h) · (∂h/∂h₁) · (∂h₁/∂w₁₁⁽¹⁾)

כל אחד מהגורמים הוא נגזרת "מקומית" שאנחנו יודעים לחשב:

  • ∂E/∂ŷ — נגזרת של Cross Entropy ביחס ל-ŷ (פשוטה).
  • ∂ŷ/∂h — נגזרת של sigmoid: σ(h)·(1–σ(h)).
  • ∂h/∂h₁ — המשקל בשכבה השנייה W₁₁⁽²⁾ כפול נגזרת sigmoid אם הופעלה.
  • ∂h₁/∂w₁₁⁽¹⁾ — פשוט x₁ (כי h₁ = w₁₁⁽¹⁾·x₁ + ...).
החדשות הטובות: אנחנו לא נחשב את זה ידנית. כל ה-Frameworks (TensorFlow, PyTorch, JAX) עושים גזירה אוטומטית (Automatic Differentiation): נותנים להם רק את ה-Forward pass, והם בונים גרף חישובי וגוזרים אוטומטית. זה אחד הכלים החזקים ביותר ב-DL וזה מה שמאפשר ארכיטקטורות עמוקות ומורכבות.

6. עדכון המשקלות

אחרי שיש לנו את הגרדיאנט ∂E/∂wij(k) — מעדכנים בדיוק כמו ב-Gradient Descent הרגיל:

wij(k) ← wij(k) – α · ∂E/∂wij(k)

זה קורה לכל משקל בכל שכבה. ברשת עם 100 מיליון משקלות — נעשה 100 מיליון עדכונים במקביל בכל epoch. כאן ה-GPU נכנס לתמונה: כפל מטריצות מקבילי הוא בדיוק החוזק שלו.

7. לולאת האימון השלמה

כך נראה איטרציה אחת של אימון ב-NN:

  1. Forward pass: הזנת x → חישוב ŷ דרך כל השכבות.
  2. Loss: השוואת ŷ ל-y באמצעות Cross Entropy או MSE.
  3. Backward pass: חישוב הגרדיאנטים של Loss ביחס לכל w.
  4. Update: w ← w – α·gradient.
  5. חזרה: כל הצעדים על דגימה הבאה (או batch הבא).

תרשים זרימה כללי

קלט X → [Layer 1: Wx+b → σ] → [Layer 2: Wh+b → σ] → ŷ

Loss(ŷ, Y) ← השוואה לתיוג האמיתי

Backprop: חישוב ∂Loss/∂W לכל w

Optimizer: W ← W – α·∇W
↑___________ חזרה לקלט הבא ___________↑

8. מתי המודל לא עובד?

אחרי כל זה, יכול לקרות שהמודל לא מצליח להגיע לדיוק טוב. הסיבות הנפוצות:

  • ארכיטקטורה לא מתאימה — מעט מדי שכבות (underfit) או יותר מדי (overfit).
  • נתונים רועשים או חסרים — תיוגים שגויים, ערכים חסרים שלא טופלו.
  • מעט נתונים — לא מספיק כדי ללמוד את הקשרים.
  • מעט epochs — לא נתנו למודל מספיק זמן ללמוד.
  • קצב למידה לא נכון — α גדול מדי קופץ מעל המינימום, קטן מדי לוקח לנצח.
  • מינימום לוקלי — האופטימיזציה נתקעה במקום לא אופטימלי.
  • Vanishing Gradients — הגרדיאנטים הופכים לזעירים בשכבות הראשונות.

הרצאה 4 כולה מוקדשת לפתרונות לבעיות אלו — Early Stopping, Dropout, Regularization, Momentum, ReLU, Stochastic GD.

9. שאלות חזרה למבחן — הרצאה 3

ש1. מה ההבדל בין רשת רדודה (1 שכבה נסתרת) לרשת עמוקה (3+ שכבות)?
הצג תשובה
תיאורטית, רשת רדודה ברוחב מספיק יכולה להציג כל פונקציה. בפועל, רשת עמוקה לומדת ייצוגים היררכיים — כל שכבה מתמחה ברמת הפשטה גבוהה יותר. זה יעיל הרבה יותר במספר פרמטרים ובדיוק. למשל ברשת לזיהוי פנים: שכבות ראשונות לומדות קצוות, אמצעיות לומדות עיניים/אף/פה, אחרונות לומדות פנים שלמות.
ש2. למה אם נסיר את פונקציות האקטיבציה הלא-לינאריות מהרשת — היא תהפוך לפרספטרון יחיד?
הצג תשובה
בלי אקטיבציה לא-לינארית, כל שכבה היא Wx+b. הרכבה של שכבות לינאריות = W₂(W₁x+b₁)+b₂ = (W₂W₁)x + (W₂b₁+b₂) = W'x + b' — פונקציה לינארית בודדת. כלומר אין יתרון לעומק כשהאקטיבציה לינארית.
ש3. הסבר את כלל השרשרת בהקשר של רשת בעלת 3 שכבות. למה זה הופך את BP ליעיל?
הצג תשובה
השגיאה E היא פונקציה של ŷ, ŷ של h₂, h₂ של h₁, h₁ של w⁽¹⁾. ∂E/∂w⁽¹⁾ = ∂E/∂ŷ · ∂ŷ/∂h₂ · ∂h₂/∂h₁ · ∂h₁/∂w⁽¹⁾. היעילות: כל גורם בשרשרת הוא נגזרת "מקומית" שכבר חושבה (אם תכננו את ה-BP נכון), ולכן עוברים על הרשת מהסוף להתחלה רק פעם אחת — סיבוכיות O(n) ולא O(n²).
ש4. למה Feedforward לבד לא מספיק? למה צריך Backpropagation?
הצג תשובה
Feedforward הוא רק חישוב — נותן את התחזית עם משקלות נוכחיים. הוא לא משנה דבר. Backpropagation הוא תהליך הלמידה: מחשב באיזה כיוון ובאיזו עוצמה לשנות כל משקל כדי להקטין את השגיאה. בלי BP, המשקלות יישארו לנצח על הערכים הראשוניים האקראיים שלהם.

10. מה תצא מן הפרק הזה

  • תוכל לצייר רשת בעלת מספר שכבות ולסמן בה Input / Hidden / Output Layer.
  • תזכה לסמן את הסימון Wij(k) ולהבין מה הוא מייצג.
  • תכתוב את הנוסחה של Feedforward עבור רשת עם שכבה נסתרת אחת.
  • תסביר את ההבדל בין FF (חישוב) ל-BP (למידה).
  • תיישם כלל שרשרת על דוגמה קטנה (3-4 גורמים).
  • תזכור שגזירה אוטומטית ב-frameworks חוסכת חישוב ידני של גרדיאנטים.
  • תזהה את החמש סיבות אפשריות לכישלון מודל ותדע לקשור אותן לפתרונות בהרצאה 4.
גשר להרצאה הבאה: בנינו רשת ולמדנו לאמן אותה. אבל בפועל היא לא תמיד עובדת — Overfitting, מינימה לוקליות, גרדיאנטים נעלמים, אימון איטי. הרצאה 4 היא ארגז כלים: Early Stopping, Regularization (L1/L2), Dropout, Momentum, ReLU, Stochastic GD ו-Learning Rate Decay.

הרצאה 4 — אופטימיזציה של רשתות נוירונים

מצגת 4 37 שקפים תאריך: 09.04.2026 פרקטי במיוחד

מטרת ההרצאה: לתת ארגז כלים שלם להפיכת מודל "לא עובד" למודל "עובד טוב". אנחנו לא נשנה את הארכיטקטורה הבסיסית, אלא נוסיף שכבות של חכמה: כיצד למנוע overfit, איך לצאת ממינימום לוקלי, איך להאיץ אימון, ואיך להתמודד עם vanishing gradients.

על מה נדבר בפרק הזה

  1. הבעיה: Overfitting vs. Underfitting.
  2. Early Stopping — לעצור לפני שמודל "משנן".
  3. Regularization L1 / L2 — קנס על משקלות גדולים.
  4. Dropout — "כיבוי" אקראי של נוירונים.
  5. בעיית מינימה לוקליות: Random Restarts ו-Momentum.
  6. בעיית Vanishing Gradients והפתרון: tanh ו-ReLU.
  7. Stochastic Gradient Descent ו-Mini-batches.
  8. Learning Rate Decay — קצב למידה דינמי.

1. Overfit vs. Underfit — הקיר שצריך לרקוד עליו

הציר המרכזי שבו אנחנו נעים בכל המודלים:

תופעהאופי המודלביצועים על Trainביצועים על Testסיבה
Underfitפשוט מדינמוכיםנמוכיםחסרה לרשת קיבולת ללמוד את הקשר
Just rightמאוזןגבוהיםגבוהיםהכלל הזהוב — בא מניסוי
Overfitמורכב מדיגבוהים מאודנמוכיםהרשת "שיננה" את האימון, לא הכלילה

אסטרטגיה כללית

הגישה המקובלת היא להתחיל עם מודל מורכב יחסית (סיכון ל-overfit) ולהפעיל עליו טכניקות הגנה. זה עדיף על להתחיל קטן ולהגדיל — כי קל יותר לרסן רשת מורכבת מאשר להגדיל את הקיבולת של רשת חלשה.

איך מזהים overfit בפועל

מציירים גרף של Loss על Train ו-Loss על Validation לאורך epochs. ב-overfit:

  • Loss על Train — ממשיך לרדת.
  • Loss על Validation — מתחיל לעלות אחרי נקודה מסוימת.

הנקודה הזו (שבה Validation מתחיל לעלות) — היא הסימן ששכלת לעצור. זה בדיוק הבסיס ל-Early Stopping שנדבר עליו מיד.

2. Early Stopping

טכניקה פשוטה ועוצמתית: לעצור את האימון ברגע שהביצועים על נתוני הולידציה מפסיקים להשתפר. במקום לאמן 100 epochs, אם אחרי 25 ה-validation loss עולה — עוצרים שם.

איך זה עובד טכנית

  1. אחרי כל epoch, מודדים את ה-Loss על מערך הולידציה.
  2. שומרים את הגרסה הטובה ביותר עד כה של המשקלות.
  3. אם עברו N epochs ברצף בלי שיפור (ה-patience) — עוצרים את האימון ומחזירים את הגרסה השמורה.

ב-Keras: callback = EarlyStopping(patience=5, restore_best_weights=True)

חשוב: Early Stopping דורש סט ולידציה נפרד. אסור להשתמש בסט הטסט להחלטה הזאת — זה ידלוף מידע מהטסט לתוך תהליך האימון ויהפוך את ה-test score ללא אמין.

3. Regularization — קנס על מורכבות

הרעיון: להוסיף לפונקציית השגיאה איבר שמעניש משקלות גדולים. רשת עם משקלות "מתונים" פחות מסוגלת לזכור את האימון בעל פה ולכן מכלילה טוב יותר.

L2 — Ridge Regression

הקנס הוא סכום ריבועי המשקלות:

E = CE(y, ŷ) + λ · Σ wᵢ²

אפקט: כל המשקלות נדחפים לכיוון אפס, אבל אף אחד לא מתאפס לגמרי. מתאים כשרוצים לרסן את כל הרשת.

L1 — Lasso Regression

הקנס הוא סכום הערכים האבסולוטיים:

E = CE(y, ŷ) + λ · Σ |wᵢ|

אפקט: משקלות שאינם חשובים — מתאפסים לחלוטין. זה מכניס "דלילות" (sparsity) ברשת ושימושי כש-אנחנו רוצים לדעת אילו תכונות חשובות (Feature Selection אוטומטי).

L1 vs. L2 — מתי כל אחד

קריטריוןL1 (Lasso)L2 (Ridge)
אפקט על משקלות שולייםמאפס לגמרימקטין אך לא מאפס
שימושFeature Selectionריסון כללי, ההגנה הסטנדרטית
גזירות באפסלא גזיר באפס (תת-גרדיאנט)גזיר בכל מקום
נפוץ ברשתות עמוקותפחותהסטנדרט (weight decay)

λ הוא היפר-פרמטר. λ גדול = ריסון חזק יותר. צריך לכוון אותו על ולידציה.

4. Dropout — אנלוגיית "אימון בחדר כושר"

הרעיון: בכל epoch, "כבה" באקראי חלק מהנוירונים בשכבות הנסתרות (למשל 20%). הרשת לא תוכל להסתמך על נוירון יחיד — תהיה מאולצת לפזר את הידע.

אנלוגיה של ההרצאה: כמו אימון בחדר כושר — בכל פעם מתמקדים בקבוצת שרירים אחרת, וכל הגוף מתחזק יותר מאשר אם רק קבוצה אחת התאמנה.

איך זה עובד טכנית

  • בכל epoch, לכל נוירון בשכבת dropout: בהסתברות p ה-output שלו נכפל ב-0.
  • בתהליך ה-Backprop של אותו epoch, אותם נוירונים לא מתעדכנים.
  • בזמן Inference (חיזוי על נתונים חדשים) — כל הנוירונים פעילים, אבל הפלט נכפל ב-(1–p) כדי לפצות.

ב-Keras: model.add(layers.Dropout(0.2)) אחרי שכבה Dense.

למה זה עובד? כל epoch מאמן תת-רשת שונה. כשבזמן Inference מפעילים את כל הנוירונים, מקבלים אנסמבל של אלפי תת-רשתות שונות שאומנו במקביל. זה אחד המשערכים הזולים ביותר ל-ensemble באמת.

5. בעיית מינימה לוקליות

פונקציית השגיאה אינה תמיד בעלת מינימום גלובלי יחיד. ב-NN עמוקות יש הרבה מינימה לוקליות. Gradient Descent יוביל אותנו ל"בור" הראשון שנפגש בו — אבל לא בהכרח לעמוק ביותר.

פתרון 1: Random Restarts

פשוט להריץ את האימון מספר פעמים ממקומות אתחול שונים, ולקחת את התוצאה הטובה ביותר. רעיון פשוט אבל מוגבל ביעילות (יקר חישובית).

פתרון 2: Momentum

הרעיון: צוברים מהירות בכיוון של גרדיאנטים אחרונים. זה מאפשר "להתגלגל" מעל מינימה רדודות.

במקום עדכון פשוט w ← w – α·∇E, מוסיפים מומנט:

v ← β·v + ∇E (וקטור המהירות)
w ← w – α·v

β מקדם המומנטום (טיפוסית 0.9). אינטואיציה מורחבת:

step_n = grad_n + β·grad_{n–1} + β²·grad_{n–2} + ...

הצעדים האחרונים משפיעים פחות (β² < β < 1). אם הגרדיאנטים בכיוון עקבי — המהירות צוברת. אם הם מתחלפים — המהירות מתבטלת. זה בדיוק "כדור שמתגלגל בחיכוך".

מומנטום במונחים פיזיקליים

חשוב על הכדור שגלגלת על משטח: יש לו אינרציה. כשמגיע למינימה רדודה, הוא לא נעצר מיד — ממשיך מעט בכיוון התנועה ויכול "לטפס" מעט מעלה ולהמשיך. אם המינימה הגלובלית עמוקה יותר — הוא יסתיים בה. זה בדיוק האפקט של מומנטום על Gradient Descent.

6. Vanishing Gradients והפתרון

בעיה מתמטית עמוקה ברשתות בעלות הרבה שכבות עם sigmoid:

  • נגזרת של sigmoid מקסימלית ב-z=0 — שווה ל-0.25.
  • בכלל השרשרת מכפילים נגזרות בשכבות.
  • 0.25 × 0.25 × 0.25 × ... = מהר מאוד מספר זעיר לחלוטין.
  • תוצאה: בשכבות הראשונות של הרשת הגרדיאנטים זעירים והמשקלות כמעט לא מתעדכנים.

פתרון 1: tanh

tanh(z) = (eᶻ – e⁻ᶻ) / (eᶻ + e⁻ᶻ)

הטווח של tanh הוא [–1, 1] (לעומת [0, 1] של sigmoid), והנגזרת המקסימלית שלה היא 1. זה מקטין את "הגרדיאנט הנעלם" אבל לא מבטל אותו.

פתרון 2: ReLU (Rectified Linear Unit)

ReLU(z) = max(0, z)

פונקציה פשוטה במיוחד — אם z חיובי, מחזיר את z; אחרת, אפס. הנגזרת:

ReLU′(z) = 1 if z > 0, else 0

היתרונות:

  • נגזרת קבועה 1 לכל z חיובי — אין הקטנה בשרשרת ⇒ אין vanishing gradient!
  • חישוב מהיר במיוחד (max פשוט).
  • "מאפסת" אוטומטית נוירונים ש-z שלילי (סוג של dropout מובנה — sparsity).

חיסרון אפשרי: "Dying ReLU" — אם אקטיבציה מתקבעת ב-0 (כל הקלטים שלילים), הנגזרת תמיד 0 והנוירון מת. פתרונות: Leaky ReLU, ELU, PReLU.

תרגול חשוב: בכל הרצאות הקורס מכאן ואילך, ברירת המחדל לאקטיבציה בשכבות נסתרות היא ReLU. בשכבת הפלט: sigmoid (בינארי) / softmax (multiclass) / ללא אקטיבציה (רגרסיה).

7. Stochastic Gradient Descent (SGD) ו-Mini-batches

הבעיה עם Batch GD

ב-Gradient Descent הקלאסי (Batch GD), בכל epoch:

  1. מריצים את כל ה-Train (אולי מיליוני דגימות) דרך הרשת.
  2. מחשבים את הגרדיאנטים הממוצעים.
  3. מבצעים צעד אחד של עדכון.

בעיות: צריך זיכרון ענק כדי להחזיק הכל; כל epoch לוקח שעות; הגרדיאנט "המושלם" לא יכול לקפוץ ממינימה לוקליות.

SGD: צעד עבור כל דגימה

במקום לחכות עד סוף ה-batch, מבצעים עדכון אחרי כל דגימה בודדת. הצעדים פחות מדויקים אבל הרבה יותר מהירים, והרעש מועיל ליציאה ממינימה לוקליות.

Mini-batch GD: הפתרון בפועל

הפשרה — בכל פעם לוקחים batch של 32, 64, 128 או 256 דגימות:

  1. מחלקים את ה-train ל-mini-batches.
  2. בכל mini-batch: FF + BP + עדכון משקלות.
  3. epoch אחד = מעבר על כל ה-mini-batches.

זה מנצל את ה-GPU (כפל מטריצות מקבילי), פחות "רועש" מ-SGD טהור, ועדיין מהיר משמעותית מ-Batch GD.

שיטהגודל batchזיכרוןיציבותמהירות
Batch GDכל ה-trainגבוה מאודמאוד יציבאיטי מאוד
Mini-batch GD32–256סביריציבמהיר ⇐ הסטנדרט
SGD (true)1מינימלירועשאיטי במונחי convergence

8. Learning Rate Decay

כלל אצבע לבחירת α:

  • α גדול מדי: צעדים ענקיים, "מקפצים" מעל המינימום, אימון מתפזר.
  • α קטן מדי: צעדים זעירים, אימון איטי, סיכון להיתקע במינימום מקומי.

הגישה האידיאלית: קצב למידה שיורד עם הזמן:

  • בתחילת האימון — α גדול, צעדים גדולים, התקדמות מהירה.
  • בסוף האימון — α קטן, התעדנות לעבר המינימום המדויק.

שיטות נפוצות לדעיכת LR:

  • Step decay — חלוקת α בקבוע (למשל 0.5) כל N epochs.
  • Exponential decay — α = α₀ · γᵗ.
  • Cosine annealing — α נע בקצב גל קוסינוס.
  • Adaptive optimizers — Adam, RMSprop — מתאימים α עבור כל פרמטר בנפרד.

9. סיכום ארגז הכלים

בעיהפתרון
OverfittingEarly Stopping, Regularization (L1/L2), Dropout
Underfittingהוסף שכבות / נוירונים, אמן יותר epochs
מינימום מקומיRandom Restarts, Momentum
Vanishing Gradientהחלף sigmoid ב-ReLU בשכבות נסתרות
אימון איטי / זיכרוןMini-batch GD, GPU
קצב למידה לא יציבLearning Rate Decay, Adam optimizer

10. שאלות חזרה למבחן — הרצאה 4

ש1. הסבר את ההבדל המהותי בין L1 ל-L2 regularization וכיצד זה משפיע על המשקלות.
הצג תשובה
L1 משתמש בערך אבסולוטי |w|. הנגזרת שלו קבועה (±1) בכל מקום, ולכן הוא "דוחף" משקלות לאפס באותה עוצמה — מה שמייצר sparsity (חלק מהמשקלות יתאפסו לגמרי). L2 משתמש בריבוע w². הנגזרת שלו פרופורציונית ל-w עצמו, אז משקלות גדולים נדחפים יותר ממשקלות קטנים — אבל אף אחד לא מתאפס לגמרי. L1 שימושי ל-Feature Selection, L2 הוא הסטנדרט לריסון כללי.
ש2. למה Dropout שקול בקירוב ל-Ensemble?
הצג תשובה
בכל epoch, Dropout בוחר באקראי תת-רשת מתוך הרשת המלאה ומאמן אותה. אחרי N epochs, אומנו N תת-רשתות שונות (כולן חולקות משקלות). בזמן Inference מפעילים את כל הנוירונים (ומכפילים בערך הפלט), מה שאקוויוולנטי בקירוב לממוצע על תת-הרשתות. אז זה ensemble כמעט בחינם.
ש3. למה ReLU פותר את Vanishing Gradient?
הצג תשובה
הנגזרת של ReLU עבור z>0 שווה ל-1 קבוע. בכלל השרשרת בעת BP, מכפילים נגזרות. עם sigmoid (מקסימום 0.25), השרשרת מתכווצת מהר; עם ReLU (1), היא לא מתכווצת בכלל לכיוון החיובי. כך הגרדיאנט מגיע גם לשכבות הראשונות בעוצמה משמעותית.
ש4. למה Mini-batch GD הוא הסטנדרט ולא Batch GD או SGD טהור?
הצג תשובה
Mini-batch מאזן בין שני קצוות: (1) הוא מנצל את ה-GPU (כפל מטריצות מקבילי) — מהיר. (2) הוא מספיק יציב כדי שהאימון יתכנס. (3) הוא מספיק "רועש" כדי לעזור לצאת ממינימה לוקליות. (4) הוא חוסך זיכרון לעומת Batch GD שמחייב את כל הנתונים בזיכרון בו-זמנית.
ש5. הסבר במשפט אחד מה Momentum עושה.
הצג תשובה
Momentum צובר מהירות בכיוון של הגרדיאנטים האחרונים, מה שמאפשר ל-Gradient Descent "להמשיך" מעל מינימה רדודות ולהאיץ בכיוון עקבי — בדיוק כמו כדור שמתגלגל בכיוון מסוים בחיכוך.

11. מה תצא מן הפרק הזה

  • תזהה Overfit / Underfit על פי גרפי Loss ל-Train ול-Validation.
  • תכיר ארבע טכניקות מרכזיות נגד Overfit: Early Stopping, L1, L2, Dropout — ותדע מתי כל אחת.
  • תוכל להסביר את Vanishing Gradient ולמה ReLU פותר אותה.
  • תבחין בין Batch / Mini-batch / Stochastic GD ותדע למה Mini-batch הוא הסטנדרט.
  • תסביר את Momentum כמטאפורה (כדור עם אינרציה) ובמתמטיקה (סכום נשקל של גרדיאנטים).
  • תזכור את כללי האצבע: ReLU בנסתרות, Dropout 0.2–0.5, אצא מ-batch=32, התחל עם α=0.001 + Adam.
  • תוכל לבנות "מתכון אופטימיזציה" עבור מודל שלא עובד טוב.
גשר להרצאה הבאה: ראינו את התאוריה ואת ארגז הכלים — אבל לממש את כל זה ידנית מאפס היה לוקח חודשים. הרצאה 5 מציגה את ה-DL Frameworks (TensorFlow, Keras, PyTorch) שמספקים אבני בניין מוכנות, גזירה אוטומטית, ניצול GPU שקוף, ואת השפה שבה ייכתבו כל המודלים שלנו מכאן.

הרצאה 5 — סביבות פיתוח ו-Keras API

מצגת 5 57 שקפים תאריך: 16.04.2026 מעבר לכלים מעשיים

מטרת ההרצאה: להציג את הסביבה שבה נעבוד מכאן ועד סוף הקורס. נכיר את ה-DL Frameworks המובילים, נצלול לתוך Keras (שכובש גם את ה-pipeline של TensorFlow), נלמד על Tensors כיחידת המידע ב-NN, נצלול לפעולות עליהם, ונבנה את המודל הראשון שלנו על MNIST.

על מה נדבר בפרק הזה

  1. סקירת DL Frameworks — TensorFlow, Keras, PyTorch ואחרים.
  2. סביבת עבודה — Anaconda, Jupyter, Google Colab, GPU.
  3. Hello World של DL — סיווג ספרות בכתב יד (MNIST).
  4. שלבי העבודה הסטנדרטיים: Architecture → Compile → Fit → Evaluate.
  5. Tensors — מבנה הנתונים המרכזי, מימדים, פעולות.
  6. Broadcasting ו-Tensor Product (dot).
  7. הפרשנות הגאומטרית של פעולות על Tensors.
  8. Keras Sequential vs. Functional API.

1. סקירת ה-Frameworks

Frameworkבעליםמיוצב מ-אופישימוש טיפוסי
TensorFlowGoogle2015Low-level + Keras (high-level)פרודקשן, מובייל (TF Lite), Web (TF.js)
KerasChollet (כיום ב-Google)2015High-level APIפיתוח מהיר, חינוך, prototyping. זה מה שנעבוד איתו בקורס.
PyTorchMeta (Facebook)2016Pythonic, dynamic graphsמחקר אקדמי, NLP, מודלי שפה גדולים. כיום הסטנדרט בעולם המחקר.
JAXGoogle2018NumPy-like + autodiff + XLAמחקר high-performance, גוגל פנימית
MXNet, DL4J, Sonnetשוניםפחות נפוציםשימושים ספציפיים

נקודת מבט פרקטית: Keras הוא ה-API שיושב מעל TensorFlow. שני סוגריים נחים: כותב Keras = כותב TF (עם ההפשטות החזקות). PyTorch הוא אופציה אחרת לחלוטין, יותר "פייתוני" ופחות קסם — מתאים למחקר.

למה הקורס בחר ב-Keras?

Keras נכתב במפורש כ-API חינוכי — המבנה שלו תואם בדיוק את הצורה שבה חושבים על רשתות (שכבות, הפעלות, optimizer). תוך 5-10 שורות קוד אפשר לבנות מודל מלא, לאמן ולהעריך אותו. זה מאפשר להתמקד בארכיטקטורות וברעיונות ולא בפלאמברינג. לקריירה: גם PyTorch כדאי להכיר, אבל אם הבנת Keras — המעבר ל-PyTorch יקח שבוע.

2. סביבת עבודה

  • GPU מומלץ. CPU עובד, אבל פי 5–10 איטי יותר. לעבודה עם תמונות / רצפים — חיוני.
  • Google Colab — סביבה בענן בחינם, נותנת GPU בכיף. כל הקוד של הקורס יעבוד שם בלי התקנות.
  • Anaconda — מנהל סביבות מקומי. מתקין Python + ספריות מדעיות בקליק.
  • Jupyter Notebooks — סביבת עבודה אינטראקטיבית. כל ה-notebooks של הספר זמינים ב-GitHub של Chollet.
  • UNIX מומלץ להתקנה מקומית. ב-Windows אפשר להשתמש ב-WSL.

3. Hello World של DL — MNIST

MNIST = 70,000 תמונות שחור-לבן בגודל 28×28 של ספרות בכתב יד. 60K לאימון, 10K לטסט. הבעיה הקלאסית של DL והדוגמה של "Hello World".

מה המחשב רואה? מטריצת 28×28 של ערכים 0 (שחור) עד 255 (לבן), כשגווני אפור באמצע. 784 פיקסלים בסך הכל לכל תמונה.

שלבי בניית המודל ב-Keras

  1. טעינת הנתונים(x_train, y_train), (x_test, y_test) = mnist.load_data()
  2. הכנת הנתונים — Reshape ל-(samples, 784), נורמליזציה ל-[0,1] (חלוקה ב-255).
  3. הגדרת הרשת — Sequential עם 3 שכבות Dense.
  4. Compile — בחירת optimizer, loss, metrics.
  5. Fit — אימון בפועל, X epochs, batch size.
  6. Evaluate — הערכה על ה-test.
  7. Predict — חיזוי על דוגמה חדשה.

קוד מינימלי

model = Sequential([ Dense(512, activation='relu', input_shape=(784,)), Dense(256, activation='relu'), Dense(10, activation='softmax') ]) model.compile(optimizer='rmsprop', loss='sparse_categorical_crossentropy', metrics=['accuracy']) model.fit(x_train, y_train, epochs=5, batch_size=128) model.evaluate(x_test, y_test)

זהו. רשת עם ~668K פרמטרים, מתאמנת תוך דקות, ומשיגה ~98% דיוק על MNIST. השכבות הצפופות (Dense) הן בדיוק כמו הרצאה 3 — Wx+b ואקטיבציה. ההבדל היחיד מהמימוש שעשינו ידנית: Keras דואג ל-Backprop, ל-GPU, ולכל הסטנדרט.

4. Tensors — מבנה הנתונים המרכזי

"Tensor" הוא לא יותר מ-מערך מספרים בכמות מימדים כלשהי. הסיווג:

שםמימדיםדוגמה
Scalar0Dטמפרטורה: 23.5
Vector1Dפרופיל לקוח: [גיל, גובה, משקל] = [25, 175, 70]
Matrix2Dטבלה (samples × features) למשל 1000 × 50
3D Tensor3Dסדרת זמן (samples × time × features) או טקסט (samples × words × embedding)
4D Tensor4Dתמונות צבעוניות (samples × height × width × channels)
5D Tensor5Dוידאו (samples × frames × height × width × channels)

תכונות חשובות

  • arr.ndim — מספר המימדים.
  • arr.shape — האורך של כל מימד. למשל לתמונת RGB 28×28: shape = (28, 28, 3).
  • arr.dtype — טיפוס הנתונים (float32, uint8 וכו').
  • Slicing — בדיוק כמו NumPy: arr[10:100, :, :].
פורמט ייצוג תמונות — TensorFlow vs Theano

TensorFlow (channels-last): (samples, height, width, channels). Theano / PyTorch (channels-first): (samples, channels, height, width). חשוב לדעת באיזה פורמט הקלט שלך בנוי כשעוברים בין כלים.

5. פעולות מרכזיות על Tensors

שכבת Dense בעצם מריצה:

output = relu(dot(input, W) + b)

שלוש פעולות שמרכיבות את החישוב:

(א) Element-wise — פעולה איבר-איבר

פעולות פשוטות שמופעלות על כל ערך בנפרד: חיבור, חיסור, ReLU, sigmoid. ב-NumPy זה כתוב כמו פעולה רגילה (a + b, np.maximum(0, x)).

(ב) Broadcasting

מה קורה כשמחברים מטריצה (32, 10) עם וקטור (10,)? NumPy / TF "מותחים" את הוקטור לאורך המימד הראשון:

  1. מוסיפים מימדים חסרים ל-tensor הקטן (10,) → (1, 10).
  2. משכפלים אותו לאורך המימד החדש: (1,10) → (32, 10).
  3. מבצעים את הפעולה איבר-איבר.

זה לא משכפל בזיכרון בפועל — רק לוגית. שימושי במיוחד להוספת bias לכל דגימה ב-batch.

(ג) Tensor Product (dot operation)

הפעולה השכיחה ביותר. שונה מ-element-wise (שמסומן ב-*).

  • וקטור · וקטור = סקלר. סכום מכפלות איבר-איבר. צריכים להיות באותו אורך.
  • מטריצה · וקטור = וקטור. הממד האחרון של המטריצה צריך להיות שווה לאורך הוקטור.
  • מטריצה · מטריצה = מטריצה. הממד האחרון של הראשונה = הממד הראשון של השנייה.

דוגמה: x.shape = (1, 784), W.shape = (784, 16) → dot.shape = (1, 16).

6. הפרשנות הגאומטרית

ניתן לחשוב על Tensor כעל נקודה (או חץ מראשית הצירים) במרחב n-מימדי. פעולות על Tensors הן טרנספורמציות גאומטריות:

  • חיבור וקטורים — תזוזה (translation).
  • סיבוב — כפל במטריצת סיבוב.
  • שינוי קנה מידה (scaling) — כפל במטריצה אלכסונית.
  • טרנספורמציה אפינית — כפל במטריצה + הוספת וקטור (בדיוק Wx+b!).

למה צריך פונקציית אקטיבציה

בלי אקטיבציה לא-לינארית, רצף שכבות = רצף טרנספורמציות אפיניות = טרנספורמציה אפינית אחת. כלומר רשת עמוקה תהפוך לפרספטרון יחיד! האקטיבציה היא ה"שובר" של הלינאריות.

מטאפורת "דפי הנייר המגולגלים"

דמיין שני דפי נייר — כחול ואדום — שלקחו וגלגלו אותם יחד לכדור מעוך. רשת נוירונים מנסה לצייר קו שמפריד בין הכחול לאדום. בלי אקטיבציה היא תוכל לצייר רק קו ישר, מה שלא יספיק. כל שכבה לא-לינארית בעצם "מפרקת" קצת את הכדור, פותחת אותו, ובסוף הרשת הצליחה לפתוח את שני הדפים — מה שמאפשר הפרדה בקו ישר. זוהי האנלוגיה המרכזית להבין עומק וייצוגים היררכיים.

7. גזירה אוטומטית — היתרון הגדול של Frameworks

בהרצאה 3 ראינו שכלל השרשרת הוא הכלי לחישוב גרדיאנטים. ב-Keras / TF / PyTorch — אנחנו לא צריכים לחשב אותו ידנית.

Symbolic Differentiation: הספריה בונה גרף חישובי תוך כדי ה-Forward pass, וגוזרת אותו אוטומטית. משמעות: כל פעולה שאתה מבצע ב-Forward, הספריה יודעת לחשב את הנגזרת של ה-Loss ביחס לכל פרמטר ברשת. אתה כותב רק את הקדימות — Backprop בא חינם.

זה הסוד שמאפשר ארכיטקטורות מורכבות — Transformers, ResNets, GANs — בלי לכתוב Backprop ידנית לכל אחת.

8. Keras: שני סגנונות לבנות מודל

(א) Sequential API — פשוט וליניארי

מתאים כשהמודל הוא ערימה של שכבות בזו אחר זו: input → layer1 → layer2 → ... → output.

model = Sequential([ Dense(64, activation='relu', input_shape=(784,)), Dense(64, activation='relu'), Dense(10, activation='softmax') ])

או לחילופין באופן אינקרמנטלי:

model = Sequential() model.add(Dense(64, activation='relu')) model.add(Dense(64, activation='relu')) model.add(Dense(10, activation='softmax')) model.build(input_shape=(None, 784))

(ב) Functional API — גמיש

מתאים כשיש מספר קלטים, מספר פלטים, או חיבורים מורכבים (skip connections, branching).

inputs = Input(shape=(784,)) x = Dense(64, activation='relu')(inputs) x = Dense(64, activation='relu')(x) outputs = Dense(10, activation='softmax')(x) model = Model(inputs, outputs)

דוגמה לכוח של Functional API: Multi-input

בעיה: סיווג תקלות שירות לקוחות. קלט: כותרת (טקסט) + תיאור (טקסט) + תיוג קטגוריה. פלט: ציון דחיפות + מחלקה לטיפול.

title_in = Input(shape=(None,), name='title') body_in = Input(shape=(None,), name='body') tags_in = Input(shape=(num_tags,), name='tags') # ... עיבוד נפרד לכל קלט ... merged = concatenate([title_features, body_features, tags_in]) priority = Dense(1, activation='sigmoid', name='priority')(merged) department = Dense(N, activation='softmax', name='department')(merged) model = Model([title_in, body_in, tags_in], [priority, department])

(ג) Model Subclassing

בנייה מאפס בסגנון PyTorch — יורשים מ-tf.keras.Model ומגדירים בעצמכם call(). מתאים למומחים, ולמודלים אקזוטיים. רוב הקורס לא יזדקק לזה.

APIמתי להשתמש
Sequentialרוב המודלים הבסיסיים — חכמה אינטואיטיבית
Functionalקלט/פלט מרובה, חיבורים מורכבים — הברירת מחדל הפרקטית
Subclassingארכיטקטורות מותאמות אישית, כתיבת research papers

9. שלבי העבודה הסטנדרטיים ב-Keras

  1. הגדרת נתוני האימון — Tensors של קלט ופלט.
  2. הגדרת הרשת — Sequential / Functional API.
  3. Compile — בחירת optimizer (Adam / RMSprop / SGD), loss function (CE / MSE), metrics (accuracy / precision).
  4. Fit — model.fit(x_train, y_train, epochs, batch_size, validation_split).
  5. Evaluate — model.evaluate(x_test, y_test) → מקבלים loss + metrics.
  6. Predict — model.predict(x_new) על נתונים חדשים.

10. תוספות מקובץ הקוד שהוצג בהרצאה

הסעיף הזה מתבסס על ה-notebook 5.MNIST_Tensors.ipynb שמלווה את ההרצאה. הוא לוקח את התאוריה שלמדנו ומראה איך היא נראית בקוד אמיתי, עם דוגמאות מספריות ופרטים שמעמיקים את ההבנה.

10.1 חקירת tensor בשלוש שורות

אחרי טעינת MNIST, אפשר לדבר עם הנתונים ישירות:

train_images.ndim # 3 train_images.shape # (60000, 28, 28) train_images.dtype # uint8 len(train_labels) # 60000 train_labels # array([5, 0, 4, ..., 5, 6, 8], dtype=uint8)

תרגול: בכל פעם שאתה נתקל ב-tensor חדש — הריץ .ndim, .shape, .dtype כצעד ראשון. זה עוצר טעויות ב-90% מהמקרים שבהם משהו לא עובד.

10.2 הצגת תמונה אחת מ-MNIST

import matplotlib.pyplot as plt digit = train_images[4] # התמונה החמישית plt.imshow(digit, cmap=plt.cm.binary) plt.show() print(train_labels[4]) # 9

שימושי לדיבוג: לוודא שהתמונה והתיוג באמת תואמים, שהנורמליזציה לא הרסה תמונה.

10.3 חיתוכים (slicing) — קווי הגנה לפיצולים

Slicing הוא דרך לדגום חלקים מ-tensor בלי להעתיק זיכרון. דוגמאות מהקוד:

train_images[10:100] # shape (90, 28, 28) — תמונות 10-99 train_images[10:100, :, :] # זהה — שלושת השורות הבאות פירוש מפורש train_images[:, 14:, 14:] # רק הרבע התחתון-ימני של כל תמונה (14×14) train_images[:, 7:-7, 7:-7] # מרכז כל תמונה — חלון 14×14

חיתוך ל-batches:

batch = train_images[:128] # ה-batch הראשון batch = train_images[128:256] # ה-batch השני n = 3 batch = train_images[128*n : 128*(n+1)] # ה-batch ה-n

זה בדיוק מה ש-Keras עושה מאחורי הקלעים כש-batch_size=128.

10.4 פעולות tensor: גרסה תמימה לעומת גרסה וקטורית

מימוש "תמים" של ReLU על מטריצה — Python loop כפול:

def naive_relu(x): assert len(x.shape) == 2 x = x.copy() for i in range(x.shape[0]): for j in range(x.shape[1]): x[i, j] = max(x[i, j], 0) # מאפס שליליים return x

וגרסה וקטורית של NumPy:

z = np.maximum(z, 0.) # שורה אחת, מקבילי בפנים

בדיקת זמנים על מטריצות 20×100, 1000 חזרות:

מימושזמן ריצה אופייני
NumPy וקטורי~0.02 שניות
Python loop תמים~2-5 שניות (פי 100-200 איטי)
למה ההבדל כל כך גדול: NumPy מריץ את הלולאה הפנימית ב-C קומפילד עם SIMD. כל פעולת tensor שאתה כותב עם + - * / @ np.maximum רצה במהירות C. זו הסיבה שאף פעם לא כותבים לולאות Python על נתונים גדולים ב-DL.

10.5 GradientTape — גזירה אוטומטית חיה ב-TensorFlow

בפרק 7 דיברנו על "Symbolic Differentiation" של ה-Frameworks. ככה זה נראה בפועל ב-TF:

import tensorflow as tf x = tf.Variable(0.) with tf.GradientTape() as tape: y = 2 * x + 3 grad_of_y_wrt_x = tape.gradient(y, x) # 2.0

הקוד שמתבצע בתוך ה-with-block נרשם על "סרט" (tape). אחר כך tape.gradient(y, x) מבקש מהסרט לחשב ∂y/∂x.

דוגמה ריאליסטית יותר עם משקלות W ובאיאס b:

W = tf.Variable(tf.random.uniform((2, 2))) b = tf.Variable(tf.zeros((2,))) x = tf.random.uniform((2, 2)) with tf.GradientTape() as tape: y = tf.matmul(x, W) + b grads = tape.gradient(y, [W, b]) # נגזרת של y לפי W ולפי b

זהו בדיוק מה ש-model.fit() עושה מאחורי הקלעים, אלפי פעמים בכל epoch.

10.6 חשבון מהיר: כמה עדכוני משקלות בכל אימון?

נניח אימון על MNIST: 60,000 דגימות, batch_size=128, epochs=5.

  • Batches per epoch: 60,000 / 128 ≈ 469 (עיגול כלפי מעלה).
  • Updates per epoch: 469 (עדכון אחד לכל batch).
  • Total updates: 469 × 5 ≈ 2,345 עדכוני משקלות.

זה כמות הצעדים של Gradient Descent שהמודל עושה לאורך כל האימון. אם תכתוב את לולאת האימון בעצמך, זה מה שתראה.

10.7 RMSprop — מה הוא עושה למעשה

בקוד הופיעה ההערה: "RMSprop מבוסס על אותו רעיון של ממוצע משוקלל אקספוננציאלי של גרדיאנטים כמו Gradient Descent עם Momentum, אך ההבדל הוא באופן עדכון הפרמטר".

הפרשנות המורחבת:

  • Momentum: שומר ממוצע נע של הגרדיאנטים ומוסיף אותו לעדכון.
  • RMSprop: שומר ממוצע נע של ריבועי הגרדיאנטים, ומחלק את הגרדיאנט הנוכחי בשורש שלו. כך LR מתאים את עצמו לכל פרמטר בנפרד — פרמטרים עם גרדיאנטים גדולים מקבלים LR אפקטיבי קטן, ולהפך.
  • Adam: שילוב של שניהם — Momentum + RMSprop. הברירת מחדל הפופולרית בעולם.

10.8 בנייה מאפס — NaiveDense ו-NaiveSequential

ה-notebook מציג מימוש מאפס של שכבת Dense ושל Sequential, ב-~50 שורות Python. זוהי הוכחה אופרטיבית שכל מה ש-Keras עושה — ניתן לכתוב ידנית.

class NaiveDense: def __init__(self, input_size, output_size, activation): self.activation = activation self.W = tf.Variable(tf.random.uniform((input_size, output_size), minval=0, maxval=1e-1)) self.b = tf.Variable(tf.zeros((output_size,))) def __call__(self, inputs): return self.activation(tf.matmul(inputs, self.W) + self.b) @property def weights(self): return [self.W, self.b] class NaiveSequential: def __init__(self, layers): self.layers = layers def __call__(self, inputs): x = inputs for layer in self.layers: x = layer(x) return x @property def weights(self): return [w for layer in self.layers for w in layer.weights]

וצעד אימון מאפס:

def one_training_step(model, images_batch, labels_batch): with tf.GradientTape() as tape: predictions = model(images_batch) per_sample_losses = tf.keras.losses.sparse_categorical_crossentropy( labels_batch, predictions) average_loss = tf.reduce_mean(per_sample_losses) gradients = tape.gradient(average_loss, model.weights) optimizer.apply_gradients(zip(gradients, model.weights)) return average_loss
תובנה ללקיחה: Keras = הכמיפסול הזה בעטיפה נוחה. ההבנה שכל אבן בניין ב-DL היא בסופו של דבר matmul + activation + gradient + update — זה הסוד להבין כל ארכיטקטורה חדשה שתפגוש.

11. שאלות חזרה למבחן — הרצאה 5

ש1. מה ההבדל בין TensorFlow ל-Keras?
הצג תשובה
TensorFlow היא ספרייה low-level — נותנת שליטה מלאה על גרף החישוב, פעולות tensor, GPU וכו'. Keras היא API high-level שיושב מעל TensorFlow, מספק "אבני בניין" (שכבות, מודלים, optimizers) ומאפשר בניית רשתות בפחות שורות קוד. כיום Keras היא בעצם ה-API הרשמי של TensorFlow (tf.keras).
ש2. נתון Tensor בצורה (1000, 28, 28, 3). מה הוא מייצג?
הצג תשובה
1000 תמונות, כל אחת בגודל 28×28 פיקסלים, 3 ערוצי צבע (RGB). בפורמט TensorFlow (channels-last). אם זה היה (1000, 3, 28, 28) זה היה פורמט PyTorch / Theano (channels-first).
ש3. למה Element-wise multiplication שונה מ-Tensor product (dot)?
הצג תשובה
Element-wise (סימון *): מכפיל איברים באותו מיקום, דורש shape זהה, מחזיר tensor באותו shape. Dot product: סכום של מכפלות לאורך מימד פנימי, דורש שהמימד האחרון של הראשון = המימד הראשון של השני, ומחזיר tensor חדש בעל shape שונה. Dot היא הפעולה המרכזית של שכבות Dense, * היא לפעולות נקודתיות.
ש4. מתי נשתמש ב-Functional API ולא ב-Sequential?
הצג תשובה
Functional נדרש כש: (1) למודל יש מספר קלטים (תמונה + מטה-נתונים), (2) מספר פלטים (multi-task learning), (3) חיבורים מקבילים (branches), (4) skip connections (כמו ב-ResNet). Sequential מספיק רק כשהרשת היא רצף ליניארי של שכבות.
ש5. למה Frameworks מספקים גזירה אוטומטית, ולמה זה חשוב?
הצג תשובה
בלי גזירה אוטומטית, היינו צריכים לחשב ידנית את הנגזרת של ה-Loss ביחס לכל פרמטר ברשת — לכל ארכיטקטורה חדשה. עם autodiff, אנחנו כותבים רק את ה-Forward pass, והספרייה בונה גרף חישובי וגוזרת אותו אוטומטית באמצעות כלל השרשרת. זה מאפשר ארכיטקטורות מורכבות (Transformers, ResNets, GANs) בלי שגיאות חישוב ובמינימום קוד.
ש6. נתון אימון על 60,000 דגימות, batch_size=64, 8 epochs. כמה עדכוני משקלות יבוצעו בכל האימון?
הצג תשובה
Batches per epoch = 60000 / 64 = 937.5 → 938 (עיגול כלפי מעלה). סה"כ עדכונים = 938 × 8 = 7,504. הערה: כל עדכון = צעד GD יחיד שמבוסס על Loss ממוצע של 64 דגימות.
ש7. מה ההבדל המרכזי בין Momentum ל-RMSprop?
הצג תשובה
Momentum צובר ממוצע נע של הגרדיאנטים עצמם — מאפשר "אינרציה" בכיוון עקבי. RMSprop צובר ממוצע נע של ריבועי הגרדיאנטים ומחלק את הגרדיאנט הנוכחי בשורש שלו — כך הוא מתאים LR לכל פרמטר בנפרד (פרמטרים עם גרדיאנטים גדולים מקבלים LR אפקטיבי קטן). Adam מאחד בין שניהם.
ש8. הסבר במילים שלך מה tf.GradientTape() עושה.
הצג תשובה
זה context manager שמקליט את כל הפעולות שקורות בתוכו (Forward pass) על "סרט" (tape). אחרי שיצאנו מה-with-block, אפשר לבקש מהסרט את הנגזרת של כל ערך שחושב, ביחס לכל משתנה tf.Variable שהשתתף — באמצעות tape.gradient(y, x). זה הליבה של Backpropagation ב-TF, ומה ש-Keras משתמש בו בפנים.

12. מה תצא מן הפרק הזה

  • תזהה את שלושת ה-Frameworks המובילים ותדע מתי כל אחד.
  • תוכל לקרוא ולכתוב סקריפט Keras בסיסי שמאמן רשת על MNIST תוך פחות מ-15 שורות.
  • תזהה את 6 שלבי עבודת Keras — Architecture → Compile → Prepare data → Fit → Evaluate → Predict.
  • תבחין בין Tensor 2D (טבלאי), 3D (רצף), 4D (תמונה), 5D (וידאו).
  • תיישם פעולות בסיסיות על Tensors: dot, broadcasting, reshape, slicing.
  • תכיר את הפרשנות הגאומטרית של פעולות tensor כטרנספורמציות במרחב.
  • תוכל להחליט בין Sequential API ל-Functional API לפי דרישות המודל.
  • תזהה את ההבדל בין מימוש תמים (Python loop) למימוש וקטורי, ותדע למה תמיד מעדיפים את השני.
  • תוכל לכתוב צעד אימון בסיסי באמצעות tf.GradientTape() — מבלי להסתמך על Keras.
  • תחשב מהר את מספר עדכוני המשקלות באימון לפי גודל הנתונים, batch_size ו-epochs.
גשר להרצאה הבאה: יש לנו עכשיו את כל הכלים הטכניים. הרצאה 6 לוקחת אותנו ל-3 בעיות אמיתיות ושונות מהותית — סיווג בינארי (IMDB ביקורות), סיווג רב-מחלקתי (Reuters ידיעות) ורגרסיה (מחירי דירות בבוסטון). כל אחת תדגים אסטרטגיה אחרת לבנייה, נורמליזציה, בחירת loss ו-metrics, ומה לעשות כשיש מעט נתונים (K-fold).

הרצאה 6 — NN with Keras: שלושה תרחישים מלאים

מצגת 6 62 שקפים תאריך: 30.04.2026 פרקטי במיוחד

מטרת ההרצאה: לעבור משלוש בעיות מסוגים שונים — בינארי, רב-מחלקתי, רגרסיה — ולראות בכל אחת איך עוברים מנתונים גולמיים למודל מאומן. בנוסף, לסכם את תהליך העבודה הסטנדרטי של פרויקט DL בכל תחום שהוא.

על מה נדבר בפרק הזה

  1. סיווג בינארי — IMDB (ביקורות סרטים: חיובי / שלילי).
  2. הכנת נתוני טקסט: Multi-hot encoding ו-Vectorization.
  3. נורמליזציה של ערכים ועקרונות הכנת נתונים ל-NN.
  4. טיפול בערכים חסרים.
  5. סיווג רב-מחלקתי — Reuters (ידיעות לקטגוריות).
  6. רגרסיה — Boston Housing (חיזוי מחיר דירה).
  7. K-fold cross validation — מתי ולמה.
  8. סיכום: 8 שלבי עבודה של פרויקט DL.

1. בעיה ראשונה: סיווג בינארי — IMDB

הנתונים: 50,000 ביקורות סרטים מ-IMDB. 25K לאימון, 25K לטסט. תיוג: 1 = חיובית, 0 = שלילית. כל ביקורת מיוצגת כרצף של מספרים שלמים — כל מספר מקודד מילה ספציפית במילון.

נטעין את 10,000 המילים השכיחות ביותר (מספיק כדי לתפוס את החשיבות, חוסך זיכרון):

from tensorflow.keras.datasets import imdb (x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=10000)

שלב הכנת הנתונים — Multi-hot Encoding

הבעיה: לכל ביקורת אורך שונה. NN דורש קלט בגודל קבוע. הפתרון: וקטור באורך 10,000 שבכל מקום i נסמן 1 אם המילה ה-i הופיעה בביקורת, ו-0 אחרת:

def vectorize(sequences, dim=10000): results = np.zeros((len(sequences), dim)) for i, seq in enumerate(sequences): results[i, seq] = 1 return results x_train = vectorize(x_train) # shape (25000, 10000) x_test = vectorize(x_test) y_train = np.asarray(y_train).astype('float32') y_test = np.asarray(y_test).astype('float32')

ב-One-hot המצב יותר חמור — מטריצה של (25000, num_words, 10000). Multi-hot היא פתרון גבולי: מתעלמת מסדר המילים ומחזרות, אבל פשוטה ועובדת מצוין למטרת sentiment analysis.

חלופה — Embedding Layer (טעימה לקראת DL for Text)

במקום וקטור 10K-מימדי דליל, אפשר להשתמש בשכבת Embedding שלומדת ייצוג צפוף (למשל 50-מימדי) לכל מילה. זה גם שומר על סדר המילים. נראה את זה לעומק בהרצאות 8-9 (DL for Text).

הגדרת הרשת

model = Sequential([ Dense(16, activation='relu', input_shape=(10000,)), Dense(16, activation='relu'), Dense(1, activation='sigmoid') ])
  • 16 יחידות בשכבה נסתרת — מספיק קטן כדי לא לעבוד מדי, מספיק גדול כדי לתפוס את הקשר.
  • 2 שכבות נסתרות — נותן עומק בסיסי.
  • פלט יחיד עם sigmoid — מחזיר הסתברות בין 0 ל-1.

Compile

model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
  • rmsprop — optimizer מתקדם הכולל מנגנון דומה ל-momentum.
  • binary_crossentropy — Loss המתאימה לסיווג בינארי עם sigmoid.
  • accuracy — מדד ביצועים לקריאה.

פיצול ל-Validation

נשמור 10K דגימות מתוך ה-25K כ-validation:

x_val = x_train[:10000]; partial_x = x_train[10000:] y_val = y_train[:10000]; partial_y = y_train[10000:] history = model.fit(partial_x, partial_y, epochs=20, batch_size=512, validation_data=(x_val, y_val))

תוצאה אופיינית

גרף Loss מראה תופעה קלאסית: ה-training loss ממשיך לרדת, אבל ה-validation loss מתחיל לעלות אחרי ~4 epochs — זה Overfitting. הפתרון: לאמן רק 4 epochs, או להשתמש ב-Early Stopping. אחרי תיקון: ~88% accuracy על הטסט.

2. עקרונות הכנת נתונים ל-NN

(א) Vectorization

כל הקלטים ל-NN חייבים להיות tensors של מספרים עשרוניים (float32) או שלמים (int). תמונות → מטריצות פיקסלים. טקסט → סדרות אינדקסים או vectors. אודיו → ספקטוגרמות.

(ב) Value Normalization

לא בטוח להעביר לרשת מספרים ברמות גודל שונות. אם feature אחד בטווח 0-1 ואחר בטווח 200-500 — ה-feature הגדול ידכא את הקטן בחישובי הגרדיאנט. המלצה:

  • נורמליזציה לטווח [0, 1] (חלוקה בערך מקסימלי) — מתאים לתמונות.
  • סטנדרטיזציה: ממוצע=0, סטיית תקן=1 — לכל פיצ'ר בנפרד. זה הסטנדרט בנתונים טבלאיים.
x = x - x.mean(axis=0) x = x / x.std(axis=0)

(ג) ערכים חסרים

אם יש ערכים חסרים (NaN) ב-data set:

  • נעביר 0 לרשת (כל עוד 0 הוא לא ערך משמעותי).
  • הרשת תלמד ש"0 = ערך חסר" ותתחיל להתעלם ממנו.
  • חשוב: אם בנתוני האימון אין חוסרים אבל ב-test יהיו — צריך באופן מלאכותי להוסיף missing samples ל-train, אחרת הרשת לא תוכל ללמוד להתמודד עם המצב הזה.

(ד) ייצוג טקסט במשפטים באורכים שונים

שתי גישות:

  1. Padding/Truncation — בוחרים אורך קבוע (למשל 200 מילים), ממלאים משפטים קצרים באפסים, חותכים ארוכים. נשתמש בשכבת Embedding בהמשך.
  2. Multi-hot encoding — וקטור באורך גודל המילון (כמו ב-IMDB). מאבדים סדר אבל פשוט.

3. רענון מושגים: Tensor, Embedding, Embedding Layer

שלושה מושגים שעולים בקוד של ההרצאה הזו וחשוב להבין לעומק. Tensor הוא הצורה שבה הנתונים נשמרים בכל רשת. Embedding הוא השיטה להפוך מילים לוקטורים שמשמעות שלהם נשמרת. Embedding Layer הוא הכלי ב-Keras שלומד את ה-Embeddings תוך כדי האימון.

3.1 Tensor — מה זה?

אינטואיציה: Tensor הוא בסך הכל "מערך של מספרים בכל כמות מימדים". הוא ההרחבה הכללית של סקלר → וקטור → מטריצה. רשת נוירונים תמיד עובדת על Tensors. כל קלט (תמונה, טקסט, קול) חייב להפוך ל-Tensor לפני שהוא נכנס לרשת.

היררכיית המימדים:

מימדשםדוגמהצורה (shape)
0DScalarהטמפרטורה בחדר: 23.5()
1DVectorפרופיל לקוח [גיל, גובה, משקל](3,)
2DMatrixIMDB train: 25000 ביקורות × 10000 מילים(25000, 10000)
3DTensorרצף טקסט עם embeddings: batch × words × dim(32, 200, 64)
4DTensorתמונות RGB: batch × גובה × רוחב × צבעים(64, 224, 224, 3)
5DTensorוידאו: batch × פריימים × גובה × רוחב × צבעים(8, 30, 224, 224, 3)

בקוד (חזרה מהרצאה 5):

x_train.shape # (25000, 10000) — 2D tensor x_train.ndim # 2 — מספר המימדים x_train.dtype # float32 — טיפוס הנתונים

הקשר לאריתמטיקה: בפועל Tensor הוא NumPy array בגרסת TF/PyTorch. כל פעולת + - * / @ עובדת עליו. שכבת Dense בעצם עושה activation(dot(input_tensor, W) + b) — הכל פעולות tensor.

מה קורה לקלט שלך לאורך הרשת?

בעיית IMDB לדוגמה:

  1. קלט גולמי: טקסט "this movie was great" — לא tensor.
  2. אחרי tokenization: [42, 217, 5, 89] — וקטור של אינדקסים, 1D tensor.
  3. אחרי vectorization (multi-hot): [0,0,1,...,1,...,0] באורך 10,000 — 1D tensor של 10000 ערכים.
  4. קיבוץ ל-batch: 25,000 ביקורות מקובצות → 2D tensor (25000, 10000).
  5. אחרי שכבת Dense (16): כל ביקורת → 16 ערכים → 2D tensor (25000, 16).
  6. פלט סופי: סיכוי בין 0 ל-1 לכל ביקורת → 2D tensor (25000, 1).

בכל שלב הצורה (shape) משתנה — וזה בדיוק מה שהרשת לומדת לעשות.

3.2 Embedding — מה הבעיה שהוא פותר?

הבעיה: איך מציגים מילה לרשת?

נראה שלוש גישות לאותה דוגמה — מילון של 10,000 מילים, נרצה לייצג את המילה "cat":

שיטההייצוג של "cat"מימדמורכבות
One-hot[0, 0, 0, ..., 1, ..., 0, 0]10,000סר ומבזבז זיכרון
Multi-hot (כמו ב-IMDB)וקטור של 10,000, 1 בכל מקום של מילה שהופיעה במשפט10,000מאבד סדר
Embedding[0.21, -0.15, 0.83, 0.42, ..., 0.04]50-300צפוף + שומר משמעות

הקסם של Embedding: הוקטורים נלמדים כך שמילים בעלות משמעות דומה — מקבלות וקטורים קרובים במרחב. כך הרשת מבינה ש-"good" ו-"great" קרובות יותר זו לזו מאשר ל-"terrible".

הדוגמה המפורסמת מ-Word2Vec:

vec("King") – vec("Man") + vec("Woman") ≈ vec("Queen")

כלומר היחסים הסמנטיים בין מילים מתורגמים ליחסים גאומטריים בין הוקטורים. הוקטור של "Queen" הוא בערך הוקטור של "King" פחות "Man" ועוד "Woman".

למה לא פשוט להשתמש ב-One-Hot תמיד?
  1. זיכרון: 10,000 מימדים × 200 מילים × 32,000 דגימות = 64GB. עם embedding של 64 = 410MB. פי 150 פחות.
  2. חוסר משמעות: One-hot של "cat" אורתוגונלי ל-One-hot של "kitten" — הרשת לא יודעת שהן דומות, חייבת ללמוד מאפס.
  3. הכללה: אם המודל ראה "cat" באימון אבל לא ראה "feline", עם One-hot הוא לא יידע שהן קשורות. עם embedding מאומן מראש (Word2Vec) — הוא יידע.

3.3 Embedding Layer — איך לומדים את ה-Embeddings ב-Keras

שכבת Embedding ב-Keras היא בעצם טבלת חיפוש (lookup table) שמתאמנת. הצורה שלה: (vocabulary_size × embedding_dim).

from tensorflow.keras import layers embedding_layer = layers.Embedding( input_dim=10000, # גודל המילון output_dim=64 # מימד ה-embedding )

מה קורה בפנים:

  • פנימית, השכבה היא מטריצה בצורה (10000, 64). זה 640,000 פרמטרים שנלמדים.
  • קלט: מספר שלם (אינדקס מילה).
  • פלט: השורה ה-i של המטריצה — וקטור של 64 מספרים.
  • בזמן אימון: הגרדיאנט מתעדכן כל המטריצה דרך Backpropagation, בדיוק כמו כל משקל אחר.

3.4 איך זה משתלב ב-IMDB עם רצף שלם

במקום multi-hot, אפשר היה לעבוד עם הרצף עצמו:

model = keras.Sequential([ layers.Embedding(input_dim=10000, output_dim=64), # (batch, seq_len) → (batch, seq_len, 64) layers.GlobalAveragePooling1D(), # (batch, seq_len, 64) → (batch, 64) layers.Dense(16, activation='relu'), layers.Dense(1, activation='sigmoid') ])

זרימת הצורות:

  1. קלט: ביקורת באורך 200 מילים → tensor של (32, 200) — batch של 32, כל אחת 200 אינדקסים.
  2. אחרי Embedding: (32, 200, 64) — לכל מילה יש וקטור של 64.
  3. אחרי GlobalAveragePooling1D: (32, 64) — ממוצע על פני המילים.
  4. אחרי Dense(16): (32, 16).
  5. אחרי Dense(1, sigmoid): (32, 1) — הסיכוי שהביקורת חיובית.
למה לא השתמשנו בזה ב-IMDB מההרצאה? כי בהרצאה הזו עוד לא הוצגו רצפים. ההסבר נשמר להרצאות 7-9 (Sequence Models / RNN / Transformers) שבהן השכבות שלאחר ה-Embedding (RNN, LSTM, Transformer) ינצלו את הסדר. עם Multi-hot, סדר המילים מאבד משמעות; עם Embedding + RNN, הוא נשמר.

3.5 Embeddings מאומנים מראש (Pre-trained)

במקום ללמד את ה-Embeddings מאפס על נתוני הקורס שלך, אפשר להשתמש ב-Embeddings שכבר אומנו על קורפוסים ענקיים:

  • Word2Vec (Google, 2013) — 100B מילים מ-Google News.
  • GloVe (Stanford, 2014) — מבוסס סטטיסטיקה גלובלית.
  • FastText (Facebook, 2016) — embeddings ברמת תת-מילה (טוב לעברית).
  • BERT/GPT embeddings (2018+) — embeddings תלויי הקשר: אותה מילה מקבלת וקטור שונה לפי המשפט.

איך משתמשים: טוענים את המטריצה המאומנת לתוך שכבת Embedding ויכולים להגדיר אותה כ-trainable=False (לא להמשיך ללמוד אותה) או trainable=True (Fine-Tuning קל).

4. בעיה שנייה: סיווג רב-מחלקתי — Reuters

הנתונים: 11,228 ידיעות חדשותיות מ-Reuters משנת 1986, מסווגות ל-46 קטגוריות. 8,982 לאימון, 2,246 לטסט. כל ידיעה כבר מקודדת כרצף אינדקסים — כמו ב-IMDB.

זוהי בעיית Single label, Multi-class — לכל דגימה יש בדיוק תיוג אחד מתוך 46.

הכנת התיוג — One-Hot vs. Sparse

שתי דרכים אקוויוולנטיות:

  • One-hot — וקטור באורך 46, רק במקום של הקטגוריה הנכונה יש 1. מתאים לפונקציית Loss categorical_crossentropy.
  • Sparse (integer) — מספר 0-45 מציין את הקטגוריה. מתאים לפונקציית Loss sparse_categorical_crossentropy.

שתי הדרכים תיתן את אותן תוצאות; פשוט אל תערבבו ביניהן.

הגדרת הרשת

model = Sequential([ Dense(64, activation='relu', input_shape=(10000,)), Dense(64, activation='relu'), Dense(46, activation='softmax') # 46 קטגוריות ]) model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])

למה 64 ולא 16? בגלל שיש 46 מחלקות בפלט, שכבה רחבה מדי בקצה (16) תיצור צוואר בקבוק שלא תוכל לייצג את כל הניואנסים. אם השכבה האמצעית קטנה מהפלט — אנחנו "מפסידים מידע" שאי אפשר לשחזר.

תוצאה אופיינית

אחרי 9 epochs מקבלים ~78% accuracy. למה זה טוב? Baseline של ניחוש אקראי שווה ל-1/46 ≈ 2.2%, וניחוש לפי הקטגוריה השכיחה ביותר ≈ 18%. 78% משמעותית טוב מהשניים.

תובנה: בכל בעיה — תמיד תחליטו על baseline לפני שמסתכלים על תוצאות המודל. baseline טיפש (קטגוריה רוב, ממוצע) עוזר לדעת אם המודל באמת לומד משהו או מנחש.

5. בעיה שלישית: רגרסיה — Boston Housing

הנתונים: 506 דגימות (404 train + 102 test) של דירות בבוסטון משנות ה-70. תכונות: אחוז פשיעה באזור, מספר חדרים ממוצע, שיעור מס נכסים, מרחק ממוקדי תעסוקה וכו'. מטרה: לחזות את מחיר הדירה (מספר רציף, באלפי דולרים).

אתגר 1: מעט נתונים

506 דגימות זה מעט מאוד. ההשלכות:

  • אסור לבנות רשת גדולה (overfit מובטח).
  • פיצול train/test רגיל יוצר test קטן (102) שלא בהכרח מייצג.
  • צריך טכניקת ולידציה חזקה — נשתמש ב-K-fold.

אתגר 2: סקאלות שונות בכל feature

אחוז פשיעה (0-100), שיעור מס (0.5-1.0), מספר חדרים (3-9). חובה לנרמל לפני הזנה לרשת:

mean = x_train.mean(axis=0) std = x_train.std(axis=0) x_train = (x_train - mean) / std x_test = (x_test - mean) / std # שימו לב: משתמשים בממוצע של train!

חשוב: את הנורמליזציה מחשבים על ה-train בלבד, ומחילים את אותם הפרמטרים על test. אסור להשתמש בערכי ממוצע/סטייה של ה-test — זה data leakage.

הגדרת הרשת

model = Sequential([ Dense(64, activation='relu', input_shape=(13,)), Dense(64, activation='relu'), Dense(1) # פלט יחיד, ללא אקטיבציה! ]) model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
  • 2 שכבות בלבד — מודל קטן בגלל מעט נתונים.
  • שכבת פלט ללא אקטיבציה — כי זו רגרסיה: רוצים מספר רציף בכל טווח.
  • Loss = MSE (Mean Squared Error) — הסטנדרט לרגרסיה.
  • Metric = MAE (Mean Absolute Error) — קל לפרשנות (במקרה זה: $באלפים).

6. K-fold Cross Validation

הרעיון: מחלקים את ה-train ל-K חלקים שווים. ב-K איטרציות, בכל איטרציה:

  1. שומרים חלק אחד בצד כ-validation.
  2. מאמנים את המודל על שאר K-1 החלקים.
  3. מעריכים את הביצועים על החלק שנשמר.

בסוף לוקחים את הממוצע של K המדדים. זה נותן הערכה הרבה יותר מדויקת ויציבה כשיש מעט נתונים.

שיטהמתי להשתמש
Holdout (train/val/test רגיל)הרבה נתונים (10K+)
K-fold (K=4 או 5)מעט נתונים, רוצים הערכה יציבה

הקוד הבסיסי

k = 4 num_val = len(x_train) // k mae_scores = [] for i in range(k): val_x = x_train[i*num_val : (i+1)*num_val] val_y = y_train[i*num_val : (i+1)*num_val] train_x = np.concatenate([x_train[:i*num_val], x_train[(i+1)*num_val:]], axis=0) train_y = np.concatenate([y_train[:i*num_val], y_train[(i+1)*num_val:]], axis=0) model = build_model() model.fit(train_x, train_y, epochs=80, batch_size=16, verbose=0) val_mse, val_mae = model.evaluate(val_x, val_y, verbose=0) mae_scores.append(val_mae) print(f"Average MAE: {np.mean(mae_scores)}")

תוצאה אופיינית בבוסטון: MAE ממוצע ~$2,400 — כלומר התחזית של המודל סוטה בממוצע ב-2,400$ ממחיר אמיתי. בהתחשב במחירי הדירות אז, זה דיוק יחסית סביר.

7. תהליך העבודה הסטנדרטי של פרויקט DL — 8 שלבים

זוהי תזכורת קריטית למבחן ולפרויקט המסכם:

  1. הגדרת הבעיה והנתונים — מה הקלט? מה הפלט? סוג הבעיה (סיווג / רגרסיה / clustering)? יש מספיק נתונים ואינפורמציה?
  2. בחירת מדדי הצלחה — Accuracy / Precision / Recall / F1 / ROC-AUC / MAE / MSE. תלוי בבעיה ובאיזון של הנתונים.
  3. בחירת שיטת הערכה — Holdout (יש נתונים) / K-fold (מעט נתונים) / Iterated K-fold (מעט מאוד).
  4. הכנת הנתונים — Vectorization → Normalization → טיפול בערכים חסרים → Feature engineering אם נדרש.
  5. בניית מודל טוב מ-Baseline — מודל בסיסי שמנצח ניחוש אקראי או ניחוש לפי קטגוריית הרוב.
  6. אימון מודל עם Overfit מכוון — להגדיל את המודל בכוונה עד שמגיעים ל-overfit. זה מראה שהקיבולת מספיקה.
  7. רגולריזציה והתאמת hyperparameters — Dropout, L2, מספר שכבות, יחידות, learning rate, batch size. השלב הארוך ביותר.
  8. הערכה סופית על Test Set — אחרי שכל ההיפר-פרמטרים נקבעו על ה-validation, מודדים פעם אחת על ה-test.
חוק זהב: אסור לכוון hyperparameters על בסיס ה-test. כל בחירה שעושים על סמך ביצועי הטסט = data leakage. ה-test הוא "פעם אחת בלבד" בסוף הפרויקט, לא במהלכו.

8. השוואת שלוש הבעיות — תזכיר

IMDB (סיווג בינארי)Reuters (רב-מחלקתי)Boston (רגרסיה)
שכבת פלט1 יחידה, sigmoid46 יחידות, softmax1 יחידה, ללא אקטיבציה
Lossbinary_crossentropycategorical_crossentropymse
Metricaccuracyaccuracymae
גודל שכבת ביניים1664 (לא צוואר בקבוק)64
אסטרטגיית ולידציהHoldout (10K val)HoldoutK-fold (K=4)
הכנת קלטMulti-hotMulti-hotZ-score normalization

9. תוספות מקובץ הקוד שהוצג בהרצאה

הסעיף הזה מתבסס על ה-notebook 6.Getting-started-with-NN.ipynb. זה הקובץ הפרקטי המלא של ההרצאה — הוא מציג טכניקות שמשפיעות ישירות על איכות התוצאה, ניסויים אמפיריים שמוכיחים תאוריה, וקטעי קוד שיהיו שימושיים לכל פרויקט עתידי.

9.1 פענוח ביקורת חזרה לטקסט קריא

הביקורות ב-IMDB מגיעות כרצף של מספרים. כדי לראות את הטקסט המקורי:

word_index = imdb.get_word_index() reverse_word_index = dict([(value, key) for (key, value) in word_index.items()]) decoded = " ".join([reverse_word_index.get(i - 3, "?") for i in train_data[0]])
למה i - 3? 3 האינדקסים הראשונים שמורים ב-Keras למשמעויות מיוחדות:
  • אינדקס 0 → "padding" (ריפוד לרצפים קצרים)
  • אינדקס 1 → "start of sequence" (תחילת רצף)
  • אינדקס 2 → "unknown" (מילה שלא במילון)
לכן המילה הראשונה במילון היא באינדקס 3, ויש להחסיר 3 כדי לקבל את האינדקס במילון. ה-fallback ל-"?" מטפל באינדקסים שלא מצאנו.

9.2 הצגת עקומות אימון ב-matplotlib — הקוד המלא

אחרי history = model.fit(...), האובייקט history.history הוא dict עם המפתחות loss, val_loss, accuracy, val_accuracy. ככה מציירים אותם:

import matplotlib.pyplot as plt hist = history.history loss = hist["loss"] val_loss = hist["val_loss"] epochs = range(1, len(loss) + 1) plt.plot(epochs, loss, "bo", label="Training loss") # bo = נקודות כחולות plt.plot(epochs, val_loss, "b", label="Validation loss") # b = קו כחול plt.title("Training and validation loss") plt.xlabel("Epochs"); plt.ylabel("Loss"); plt.legend(); plt.show()

הקראת הגרף:

  • שתי העקומות יורדות ביחד → המודל לומד טוב, אין overfit עדיין.
  • train יורד, val עולה → התחיל overfit. עוצרים בנקודת המינימום של val.
  • שתיהן לא יורדות → הבעיה במודל / הנתונים, לא במספר ה-epochs.

9.3 L2 Regularization — הסינטקס ב-Keras

from tensorflow.keras import regularizers model = keras.Sequential([ layers.Dense(16, kernel_regularizer=regularizers.l2(0.002), activation="relu"), layers.Dense(16, kernel_regularizer=regularizers.l2(0.002), activation="relu"), layers.Dense(1, activation="sigmoid") ])

אופציות נוספות שזמינות:

regularizers.l1(0.001) # רק L1 regularizers.l2(0.002) # רק L2 (הסטנדרט) regularizers.l1_l2(l1=0.001, l2=0.001) # שילוב Elastic Net

הפרמטר (0.002 בדוגמה) הוא ה-λ — מקדם הקנס. גדול יותר = ריסון חזק יותר. טווח טיפוסי: 1e-4 עד 1e-2.

9.4 Dropout ב-Keras

model = keras.Sequential([ layers.Dense(16, activation="relu"), layers.Dropout(0.5), # 50% מהנוירונים מכובים בכל epoch layers.Dense(16, activation="relu"), layers.Dropout(0.5), layers.Dense(1, activation="sigmoid") ])

סדר השכבות: Dropout באה אחרי שכבת Dense ואקטיבציה, לפני השכבה הבאה. ערך טיפוסי: 0.2-0.5. אסור להוסיף Dropout בשכבת הפלט.

9.5 שני אופנים לקודד תיוגים רב-מחלקתיים

אפשרות א' — One-hot ידני:

def to_one_hot(labels, dimension=46): results = np.zeros((len(labels), dimension)) for i, label in enumerate(labels): results[i, label] = 1. return results y_train = to_one_hot(train_labels)

אפשרות ב' — קיצור של Keras:

from tensorflow.keras.utils import to_categorical y_train = to_categorical(train_labels)

אפשרות ג' — בלי one-hot כלל:

y_train = np.array(train_labels) # מספרים שלמים 0-45 model.compile(optimizer="rmsprop", loss="sparse_categorical_crossentropy", # שימו לב: sparse metrics=["accuracy"])

שלוש האופציות נותנות אותן תוצאות. ההבדל הוא רק אופן הייצוג של y. שלישית חוסכת זיכרון אם יש הרבה מחלקות.

9.6 חישוב baseline אקראי ב-Reuters

איך יודעים אם 78% accuracy על Reuters זה "טוב"? בודקים מה היה קורה אם היינו מתייגים אקראית:

import copy test_labels_copy = copy.copy(test_labels) np.random.shuffle(test_labels_copy) # מערבב את התיוגים hits_array = np.array(test_labels) == np.array(test_labels_copy) print(hits_array.mean()) # ~0.18-0.20

הניחוש האקראי, כשהוא דוגם מהתפלגות התיוגים האמיתית, נותן ~18-20% (כי יש כמה קטגוריות שכיחות יותר). 78% של המודל הוא פי 4-5 טוב יותר מניחוש — אישור אמפירי שהמודל באמת לומד.

9.7 ניסוי ה"צוואר בקבוק" — הוכחה אמפירית

בפרק 3 דיברנו על למה שכבת ביניים קטנה ממימד הפלט מזיקה. ה-notebook מציג את הניסוי הזה ישירות:

model = keras.Sequential([ layers.Dense(64, activation="relu"), layers.Dense(4, activation="relu"), # ← צוואר בקבוק! 4 יחידות לפני 46 קטגוריות layers.Dense(46, activation="softmax") ])

תוצאה: Accuracy צונח מ-~78% ל-~71%. המודל פיזית לא מצליח לדחוס מספיק מידע ב-4 ערכים כדי לאחר מכן לפצל ל-46 מחלקות. זוהי הוכחה אמפירית של עקרון כללי שיפעל בכל פרויקט שלך.

כלל אצבע: אף שכבה בדרך לפלט אסור שתהיה קטנה ממימד הפלט. אם בעיית סיווג עם N קטגוריות, השכבה הנסתרת האחרונה צריכה להיות לפחות N (ובדרך כלל הרבה יותר).

9.8 K-fold מתקדם — בחירת מספר epochs מיטבי

ה-notebook מראה שימוש מתוחכם של K-fold לא רק כדי לקבל ציון ממוצע — אלא כדי לבחור את מספר ה-epochs האופטימלי:

k = 4 num_epochs = 500 # מאמנים הרבה epochs כדי לראות את כל העקומה all_mae_histories = [] for i in range(k): val_data, val_targets = ... # החלוקה הסטנדרטית של K-fold partial_train_data, partial_train_targets = ... model = build_model() history = model.fit(partial_train_data, partial_train_targets, validation_data=(val_data, val_targets), epochs=num_epochs, batch_size=16, verbose=0) all_mae_histories.append(history.history["val_mae"]) # ממוצע MAE על כל ה-folds, לכל epoch average_mae_history = [ np.mean([x[i] for x in all_mae_histories]) for i in range(num_epochs) ]

טריק להצגה ברורה:

truncated_mae_history = average_mae_history[10:] # מתעלם מ-10 ה-epochs הראשונים plt.plot(range(1, len(truncated_mae_history) + 1), truncated_mae_history)

למה לחתוך את 10 הראשונים: בהתחלה ה-MAE צונח דרסטית (בעיקר התאוששות מהאתחול האקראי), וזה "דוחס" את שאר העקומה ומקשה לראות את המינימום האמיתי. חיתוך 10 הראשונים פותח את הציר.

שלב סופי: מסתכלים על העקומה החתוכה, מזהים את ה-epoch שבו ה-val_mae מתחיל לעלות (overfit), ומאמנים מחדש על כל ה-train עד אותו epoch. דוגמה מהקוד: 130 epochs.

9.9 שלמות וההפסד מול accuracy ב-Boston

תוצאה אופיינית: test_mae_score ≈ 2.5. תרגום: התחזית של המודל סוטה בממוצע ב-2,500$ ממחיר אמיתי. כשמחירי הבתים נעים בין 10K-50K — זו סטייה של ~5-10%, סביר לבעיה רגרסיבית עם 506 דגימות בלבד.

10. שאלות חזרה למבחן — הרצאה 6

ש1. מה ההבדל בין binary_crossentropy ל-categorical_crossentropy ול-sparse_categorical_crossentropy?
הצג תשובה
binary_crossentropy: סיווג בינארי, פלט יחיד עם sigmoid, תיוג 0/1. categorical_crossentropy: סיווג רב-מחלקתי, פלט עם softmax, תיוג one-hot. sparse_categorical_crossentropy: כמו הקודם אך התיוג כמספר שלם בלבד (לא one-hot) — חוסך זיכרון כשיש הרבה מחלקות.
ש2. למה בבעיית Reuters בחרנו 64 יחידות בשכבות הנסתרות ולא 16 כמו ב-IMDB?
הצג תשובה
בגלל שב-Reuters יש 46 קטגוריות בפלט. שכבה רחבה מדי בקצה (16 יחידות) תהיה צוואר בקבוק — מידע ממדים גבוהים נדחס ל-16 ערכים, ואחר כך אי אפשר לפצל ל-46 קטגוריות בלי לאבד מידע. ב-IMDB יש רק 1 בקצה ולכן 16 מספיק.
ש3. למה ב-Boston Housing משתמשים ב-K-fold ולא ב-train/val/test רגיל?
הצג תשובה
יש רק 506 דגימות. אם נחלק אותן ל-train/val/test, ה-test יהיה ~100 דגימות — קטן מדי כדי שמדידת הביצועים תהיה יציבה (תלות גבוהה במזל של הפיצול). K-fold מאמן 4 (או 5) פעמים על פיצולים שונים, ומקבלים הערכה ממוצעת אמינה הרבה יותר.
ש4. איך מנרמלים פיצ'רים ב-Boston Housing? למה לא משתמשים בממוצע של ה-test?
הצג תשובה
לכל פיצ'ר: x = (x – mean) / std. את הממוצע והסטיית התקן מחשבים על ה-train ומחילים אותם גם על ה-test. אם נשתמש בממוצע של ה-test — זה data leakage: המודל "יראה" סטטיסטיקה של נתונים שאמורים להיות חבויים, ולא נוכל להעריך נכון את ביצועיו על נתונים אמיתיים בעתיד.
ש5. מנה את 8 השלבים של פרויקט DL סטנדרטי.
הצג תשובה
(1) הגדרת הבעיה והנתונים. (2) בחירת מדדי הצלחה. (3) בחירת שיטת הערכה (Holdout/K-fold). (4) הכנת הנתונים (vectorization, normalization). (5) בניית מודל טוב מ-baseline. (6) אימון מודל גדול שיוצר overfit. (7) רגולריזציה והתאמת hyperparameters. (8) הערכה סופית על test.
ש6. למה כשמפענחים ביקורת IMDB חזרה לטקסט מחסירים 3 מהאינדקסים?
הצג תשובה
3 האינדקסים הראשונים (0, 1, 2) שמורים ב-Keras למשמעויות מיוחדות: padding, start-of-sequence, ו-unknown. המילים האמיתיות במילון מתחילות מאינדקס 3, ולכן כדי למצוא את המילה האמיתית צריך i-3.
ש7. בקוד layers.Dense(16, kernel_regularizer=regularizers.l2(0.002)) — מה הפרמטר 0.002 מסמל ומה קורה אם נגדיל אותו פי 100?
הצג תשובה
0.002 הוא λ — מקדם הקנס בנוסחה Loss + λ·Σwᵢ². אם נכפיל פי 100 (ל-0.2), המודל יקבל קנס אדיר על משקלות גדולים, יתכנס למשקלות קטנים מאוד וייתכן שיהיה לו underfit (לא יוכל ללמוד את הקשר). ערך טיפוסי הוא 1e-4 עד 1e-2.
ש8. ב-Reuters, כתיבת תוצאה אקראית מפיקה accuracy של ~18%. למה זה לא 1/46 ≈ 2.2%?
הצג תשובה
בגלל שהתפלגות הקטגוריות ב-Reuters אינה אחידה — יש כמה קטגוריות שכיחות מאוד. הקוד מערבב את התיוגים האמיתיים (לא דוגם בהסתברות אחידה), ולכן כל "ניחוש" שומר על אותה התפלגות מקורית. ההסתברות לפגוע נכון = Σ p(category)² (סכום ריבועי השכיחויות) ≈ 18%.
ש9. מה average_mae_history[10:] עושה ולמה משתמשים בזה?
הצג תשובה
מדלג על 10 ה-epochs הראשונים ומציג רק מ-epoch 11 ואילך. הסיבה: ב-10 הראשונים ה-MAE צונח דרסטית מהאתחול האקראי לערך סביר, וזה "דוחס" את ציר ה-y בגרף ומסתיר את העקומה האמיתית שמגלה את נקודת ה-overfit. החיתוך פותח את התצוגה ומאפשר לזהות בדיוק מתי ה-validation מתחיל לעלות.
ש10. נתון tensor בצורה (32, 200, 64). מה הוא יכול לייצג ובאיזה שלב ברשת הוא מופיע?
הצג תשובה
3D tensor שמייצג batch של 32 רצפים, כל אחד באורך 200 מילים, וכל מילה היא וקטור Embedding באורך 64. זה הפלט של שכבת Embedding כשמזינים אותה ב-batch של רצפי אינדקסים בצורה (32, 200). המימד הראשון = batch, השני = אורך הרצף, השלישי = embedding dimension.
ש11. למה Embedding עדיף על Multi-hot ל-NLP מתקדם?
הצג תשובה
שלוש סיבות: (1) צפיפות — וקטור של 64 מספרים לעומת 10,000 — חיסכון אדיר בזיכרון. (2) משמעות — מילים דומות מקבלות וקטורים דומים, ולכן הרשת מבינה את הקרבה הסמנטית. (3) סדר — Multi-hot מאבד את סדר המילים במשפט; עם Embedding + RNN/Transformer הסדר נשמר ויש בו משמעות.
ש12. שכבת Embedding(input_dim=10000, output_dim=64) — כמה פרמטרים יש בה?
הצג תשובה
10,000 × 64 = 640,000 פרמטרים. השכבה היא בעצם טבלת חיפוש בצורה (vocabulary × dim), שכל ערך בה הוא משקל שנלמד דרך Backpropagation בדיוק כמו כל משקל אחר ברשת.

11. מה תצא מן הפרק הזה

  • תוכל לבנות מודל Keras מלא לסיווג בינארי, רב-מחלקתי, ורגרסיה — ולדעת מתי כל אחד.
  • תזכור את שלושת השילובים הקריטיים: sigmoid+binary_CE, softmax+categorical_CE, linear+MSE.
  • תיישם נורמליזציה (Z-score) ותדע למה משתמשים בערכי train גם בנורמליזציה של test.
  • תכיר את K-fold cross validation ומתי הוא נדרש (מעט נתונים).
  • תזהה את הסכנה של "צוואר בקבוק" ברשת — למה שכבת ביניים קטנה ממימד הפלט מזיקה.
  • תוכל לפרק כל פרויקט DL חדש ל-8 השלבים הסטנדרטיים.
  • תזהה Overfit על גרפי Loss והדרך הנכונה להחליט על מספר epochs.
  • תיישם את הסינטקס של Keras ל-L1 / L2 / L1+L2 ול-Dropout, ותדע איפה ובאיזה ערך טיפוסי.
  • תוכל לפענח טקסט מקודד ב-Keras (offset של 3 ו-padding/start/unknown).
  • תכיר את שלוש האפשרויות לקודד תיוגים רב-מחלקתיים ותבחר ביניהן לפי גודל הזיכרון.
  • תחשב baseline אקראי בעצמך ותוכל להעריך כמה המודל "באמת לומד".
  • תזהה ניסוי "צוואר בקבוק" ותימנע ממנו בארכיטקטורה.
  • תזהה Tensor בכל מימד שלו ותדע לקרוא את ה-shape שלו.
  • תסביר את ההבדל בין Multi-hot ל-Embedding ותדע מתי כל אחד מתאים.
  • תכיר את שכבת ה-Embedding ב-Keras ותחשב את מספר הפרמטרים שלה (vocabulary × dim).
גשר להרצאה הבאה: כל המודלים שבנינו עד עכשיו הם MLPs (Multi-Layer Perceptrons) — רשתות feed-forward עם שכבות Dense בלבד. הם מצוינים לנתונים טבלאיים, אבל לא מנצלים את המבנה המרחבי של תמונות. בהרצאה 7 — CNN — נכיר ארכיטקטורה מיוחדת לתמונות שמשתמשת ב-Convolution ו-Pooling במקום ב-Dense, וחוסכת מיליוני פרמטרים תוך שיפור הביצועים.

הרצאה 7 — Convolutional Neural Networks (CNN)

מצגת 7 69 שקפים תאריך: 07.05.2026 Computer Vision

מטרת ההרצאה: להציג את ה-CNN — הארכיטקטורה הבסיסית של עיבוד תמונות ב-DL. נראה למה MLP לא מתאים לתמונות, נבין את פעולת ה-Convolution ברמת הפיקסל, נכיר את אבני הבניין (Kernel, Stride, Padding, Pooling), ונסיים עם הארכיטקטורות המפורסמות שעשו את המהפכה (AlexNet, VGG, ResNet).

על מה נדבר בפרק הזה

  1. למה MLP לא מספיק לתמונות — בעיית הפרמטרים והאיבוד המרחבי.
  2. שימושים אמיתיים של CNN: ראייה, NLP (פחות שכיח), קול.
  3. Locally Connected Layers — אבן דרך תאורטית לקראת CNN.
  4. פעולת ה-Convolution — מה קורה ברמת הפיקסל הבודד.
  5. Kernels (פילטרים): סוגים שונים — קצוות, חידוד, טשטוש.
  6. טיפול בקצוות (Padding / Crop / Extension).
  7. Hyper-parameters של CNN: גודל פילטר, מספר פילטרים, Stride.
  8. Pooling Layers (Max / Average) — להפחתת מימדים.
  9. הארכיטקטורה הכללית של CNN — איך הצורות משתנות לאורך הרשת.
  10. Translation / Rotation Invariance ו-Data Augmentation.
  11. הארכיטקטורות המפורסמות: AlexNet, VGG, ResNet.
  12. תוספות מקוד ההרצאה — שימוש ב-OpenCV ובמסנני Sobel.

1. למה MLP לא מספיק לתמונות

איך המחשב רואה תמונה — למה זה משנה

לפני שמדברים על למה MLP לא מתאים, צריך להבין איך תמונה נראית מבפנים — מנקודת המבט של המחשב.

פיקסלים של ספרה ב-MNIST — איך המחשב רואה תמונה
תמונה: עמוד 6 של ה-PDF. כל פיקסל הוא ערך 0-255 (0=שחור, 255=לבן, אמצע=אפור).

תמונת MNIST היא מטריצה של 28×28 ערכים. הקשר המרחבי בין פיקסלים שכנים — שערך 144 ליד 65 הוא קצה אפור — הוא חלק מהמשמעות של התמונה. אם נעלים את המבנה הזה (כמו ב-Flatten), המידע הזה אובד.

עד עכשיו כל המודלים שלנו היו MLP — שכבות Dense רצופות. כשרוצים להזין תמונה ל-MLP, חייבים קודם "ליישר" אותה לוקטור (Flatten):

תמונת MNIST 28×28 → flatten → וקטור באורך 784 → MLP

הבעיות עם הגישה הזו:

MLP על תמונה (אחרי Flatten)CNN
סוג החיבוריםFully Connected — כל נוירון מחובר לכל הפיקסליםSparsely Connected — כל נוירון מחובר רק לחלון מקומי
קלטוקטור 1D (תמונה אחרי Flatten)מטריצה 2D / טנסור 3D ישירות
פרמטריםעל MNIST: ~800,000+ פרמטריםפי 10-100 פחות (לרוב 50-100K)
שמירה על מבנה דו-מימדי❌ אובד אחרי Flatten✅ נשמר לאורך הרשת
שיתוף משקלות❌ כל פיקסל לומד עצמאית✅ אותו פילטר על כל התמונה
Translation invariance (אובייקט במיקום אחר)❌ צריך ללמוד מחדש✅ מובנה בארכיטקטורה
ביצועים על MNIST~97%99%+

חישוב מספרי — למה ההבדל כל-כך גדול

ניקח שכבה ראשונה שמחברת MNIST (28×28 = 784 פיקסלים) ל-512 נוירונים, ונשווה את שלוש האפשרויות:

ארכיטקטורהחישובפרמטרים
Fully Connected (Dense)784 × 512401,408
Sparsely Connected (Local, חלון 3×3)9 × 5124,608 (פי 87 פחות)
CNN (Sparsely + Weight Sharing)9 בלבד (משותפים)9 (פי 44,600 פחות!)
למה זה הגיוני? בתמונה, פיקסל בפינה השמאלית-עליונה לא באמת קשור לפיקסל בפינה הימנית-תחתונה. מה שמעניין הוא דפוסים מקומיים — קצוות, פינות, מרקמים. שכבה Sparsely Connected אומרת לרשת: "אל תבזבזי פרמטרים על קשרים מרוחקים — תתמקדי במקומי". Weight Sharing מוסיף: "ואם למדת לזהות קצה אנכי באזור אחד — תשתמשי באותם משקלות בכל התמונה".

הסיבה המעמיקה: למה Flatten הורס מידע

בתמונה, פיקסלים קרובים זה לזה מספרים סיפור משותף — הם חלקים של אותו אובייקט. כשמיישרים תמונה לוקטור, פיקסלים שכנים מאבדים את הקשר המרחבי שלהם. הרשת רואה רק רצף של 784 מספרים בלי לדעת ש-30 ו-31 שכנים בעוד 30 ו-58 רחוקים.

CNN פותר את זה על ידי שכבות שעובדות מקומית — כל נוירון בשכבה הראשונה רואה רק חלון קטן של פיקסלים שכנים, וכך נשמר המבנה הדו-מימדי.

2. שימושים של CNN

  • Computer Vision (התחום העיקרי): סיווג תמונות, זיהוי אובייקטים, סגמנטציה, איתור פנים, רכבים אוטונומיים.
  • NLP: פחות נפוץ מ-RNN/Transformer, אבל עובד יפה לסיווג טקסטים קצרים.
  • אודיו: Google WaveNet — המרת טקסט לקול אנושי. מסתמך על CNN.
  • גרפיקה משחקים: Quick-Draw של Google, FaceApp, פיענוח כתב יד.

3. Locally Connected Layer — מהמלא ללוקלי

זוהי אבן הדרך התאורטית בין MLP ל-CNN. הרעיון: במקום שכל נוירון יראה את כל התמונה, כל נוירון יראה רק אזור מקומי שלה.

המעבר משלב לשלב:

  1. MLP מלא — כל יחידה מחוברת לכל 784 הפיקסלים. הרבה פרמטרים, אין ניצול של מבנה.
  2. Locally Connected — מחלקים את התמונה ל-4 אזורים, כל יחידה רואה אזור אחד בלבד. פחות פרמטרים, נשמר מבנה.
  3. Convolution = Locally Connected + Weight Sharing. כל האזורים משתמשים באותם משקלות. עוד פחות פרמטרים, ושיפור הכללה.

למה Weight Sharing הגיוני? כי תבנית כמו "קצה אנכי" או "פינה" יכולה להופיע בכל מקום בתמונה — אין סיבה ללמוד אותה בנפרד לכל אזור. Weight sharing אומר: "לימדנו פעם אחת לזהות 'קצה אנכי' — נפעיל את הפילטר הזה על כל התמונה".

תרשים — איך נראית Locally Connected Layer

Locally Connected Layer — כל יחידה רואה רבע מהקלט Input (784) Hidden (4 units) Output 7 בניגוד ל-MLP מלא, כאן כל אזור בקלט מקושר רק ליחידה אחת — חסכון אדיר בפרמטרים המסר: Local Connectivity + Weight Sharing = הבסיס של CNN

תרשים — Weight Sharing

Weight Sharing — אותם 4 משקלות בכל אזור Input Regions Shared Kernel Hidden Units w₁₁ w₁₂ w₂₁ w₂₂ משקלות משותפים בלי שיתוף משקלות — 4 אזורים, 4 משקלות לכל אחד = 16 משקלות עם שיתוף משקלות — 4 משקלות בלבד; ברשת אמיתית החיסכון פי עשרות אלפים

4. פעולת ה-Convolution — איך זה עובד בפועל

Kernel (או פילטר) הוא מטריצה קטנה של מספרים — בדרך כלל 3×3 או 5×5. הפעולה: "מחליקים" את ה-Kernel על כל התמונה ולכל מיקום מחשבים סכום משוקלל.

דוגמה מספרית מהשקפים

נניח שיש לנו פיקסל מרכזי בערך 220, וחלון 3×3 סביבו. נכפיל איבר-איבר ב-Kernel:

חלון בתמונה Kernel תוצאת המכפלה [a, b, c] [k1, k2, k3] [d, 220, e] * [k4, k5, k6] → סכום של (a·k1 + b·k2 + ... + e·k9) [f, g, h] [k7, k8, k9] ↓ פיקסל אחד בתמונה החדשה

אם בתמונה המקורית הפיקסל היה 220, ב-feature map שיוצא יהיה ערך אחר (למשל 60) — שמבטא כמה התבנית של ה-Kernel נמצאת באותו מיקום.

הזרימה השלמה לתמונה

  1. בוחרים פיקסל בתמונה.
  2. לוקחים חלון 3×3 (או 5×5) סביבו.
  3. מכפילים איבר-איבר עם ה-Kernel.
  4. סוכמים את כל המכפלות → ערך אחד.
  5. הערך הזה הופך לפיקסל המרכזי ב-Feature Map (התמונה החדשה).
  6. חוזרים על כל פיקסל בתמונה.
למה זה נקרא "convolution"?

במתמטיקה, convolution היא פעולה רשמית בין שתי פונקציות. במחשב, הפעולה שאנחנו עושים היא טכנית "cross-correlation" — אבל בקהילת ה-DL השם "convolution" השתרש. ההבדל המתמטי קטן (היפוך של ה-kernel) ולא רלוונטי לפרקטיקה.

תרשים — דוגמה מספרית של Convolution

דוגמה מספרית — Convolution על חלון 3×3 Input Window קצה אנכי בתמונה 50 100 200 50 100 200 50 100 200 × Kernel (Sobel-x) קצוות אנכיים -1 0 1 -2 0 2 -1 0 1 = Element-wise Products כל קלט × קרנל -50 0 200 -100 0 400 -50 0 200 → Σ Output Pixel בתמונה החדשה 600 סכום הערכים: (-50 + 0 + 200) + (-100 + 0 + 400) + (-50 + 0 + 200) = 600 תוצאה חיובית גבוהה — הקרנל זיהה בחלון הזה קצה אנכי חוזרים על הפעולה לכל פיקסל בתמונה — וכך נוצר Feature Map

5. סוגי Kernels — מה כל אחד עושה

סוגי הפילטרים הקלאסיים:

פילטרהמטריצהמה הוא עושה
Edge detection — אנכי (Sobel-x) [[-1,0,1],
[-2,0,2],
[-1,0,1]]
מזהה קצוות אנכיים. סכום=0 ⇒ באזור אחיד יוצא 0.
Edge detection — אופקי (Sobel-y) [[-1,-2,-1],
[0,0,0],
[1,2,1]]
מזהה קצוות אופקיים.
Sharpen (חידוד) [[0,-1,0],
[-1,6,-1],
[0,-1,0]]
מחדד קצוות בתמונה.
Blur (טשטוש ממוצע) [[1/9,1/9,1/9],
[1/9,1/9,1/9],
[1/9,1/9,1/9]]
ממוצע 9 פיקסלים שכנים — מטשטש את התמונה.
הקסם של CNN: ב-CNN אנחנו לא בוחרים את הפילטרים האלה ידנית. הרשת לומדת אותם בעצמה דרך Backpropagation. כל פילטר ש"שווה" משהו לסיווג — הרשת תפתח. אם המודל מנסה להבדיל בין דולפינים לחתולים — היא תפתח פילטרים שמזהים סנפירים, פנים, וכו'.

תרשים — 4 הקרנלים הקלאסיים זה ליד זה

סוגי Kernels — 4 פילטרים קלאסיים Edge — אופקי Sobel-y -1 -2 -1 0 0 0 1 2 1 סכום=0; חיוב למטה, שלילי למעלה → מזהה קצוות אופקיים Edge — אנכי Sobel-x -1 0 1 -2 0 2 -1 0 1 סכום=0; חיוב מימין, שלילי משמאל → מזהה קצוות אנכיים Sharpen חידוד קצוות 0 -1 0 -1 5 -1 0 -1 0 סכום=1; מרכז חיובי גבוה, שכנים שליליים → מחזק פרטים מקומיים Blur טשטוש (ממוצע) 1/9 1/9 1/9 1/9 1/9 1/9 1/9 1/9 1/9 סכום=1; כל הקלטים בשווה → ממוצע 9 שכנים → מטשטש רעש מקומי

6. טיפול בקצוות — Padding, Crop, Extension

בעיה: כשמפעילים פילטר 3×3 על פיקסל בקצה התמונה, חסר חלק מהחלון — אין שכנים מימין/למעלה/למטה.

שלוש האפשרויות לפתרון:

שיטהאיךתוצאה
Cropמתעלמים מפיקסלי הקצהתמונת פלט קטנה יותר. מאבדים מידע בקצוות.
Paddingמוסיפים "מסגרת" של אפסים סביב התמונהתמונת פלט באותו גודל. הסטנדרט המודרני.
Extensionמעתיקים את ערכי הקצה החוצהפחות נפוץ; שימושי כשפדינג של אפסים יוצר רעש.

בקוד Keras: layers.Conv2D(32, (3,3), padding='same') — משמר את הגודל. padding='valid' — מקטין (Crop).

תרשים — שלוש שיטות זו ליד זו

טיפול בקצוות — 3 שיטות Crop מתעלמים מהקצוות → תמונה קטנה יותר פלט: 3×3 (קטן מהקלט) ⚠️ מאבדים מידע בקצוות Padding (Same) מוסיפים אפסים מסביב 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 פלט: 3×3 (אותו גודל) ✓ הסטנדרט המודרני Extension משכפלים את ערכי הקצה פלט: 3×3 (אותו גודל) פחות נפוץ; שימושי לעיבוד תמונה תמונה מקורית אפסים העתקה קצוות שהוסרו

7. Hyper-parameters של שכבת Convolution

(א) Filter / Kernel Size

  • נפוץ: 3×3, 5×5.
  • 3×3 הוא הסטנדרט במודלים מודרניים — מעט פרמטרים, ושני שכבות 3×3 שוות שדה ראייה ל-5×5 אחד.

(ב) Number of Filters (Depth)

  • בכל שכבת Conv אנחנו מפעילים הרבה פילטרים שונים. נפוץ: 32, 64, 128, 256.
  • כל פילטר מוצא תבנית שונה — אחד יזהה קצוות אנכיים, אחר אופקיים, שלישי קווי 45°...
  • אם יש 32 פילטרים → 32 feature maps שיוצאים מהשכבה.

(ג) Stride

  • קצב ה"החלקה" של הפילטר על התמונה.
  • Stride=1 (ברירת מחדל) — מזיזים את הפילטר פיקסל אחד בכל פעם.
  • Stride=2 — מדלגים על פיקסל בכל פעם — feature map מוקטן בחצי.
  • Stride גדול = פחות מידע, אבל פחות פרמטרים ופחות חישוב.

(ד) Padding

ראינו לעיל. הבחירה האופיינית: padding='same'.

8. Pooling Layers — הקטנת המימדים

לאחר שכבת קונבולוציה (Conv) עם N פילטרים, נוצרות N מפות תכונות (feature maps) — כל אחת באותו גודל מרחבי כמו הקלט. ככל שמספר הערוצים גדל, גדל גם הסיכון להתאמת יתר. שכבת Pooling מצמצמת את הגודל המרחבי של כל feature map (לרוב פי 4 עם חלון 2×2) — מספר הערוצים נשאר זהה, אבל סך הערכים קטן משמעותית. התוצאה: פחות פרמטרים בשכבות הבאות, פחות overfit, ויותר מהירות.

למה צריך Pooling — המוטיבציה הבסיסית

נסתכל על דוגמה קונקרטית. תמונת RGB סטנדרטית בגודל 224×224×3 = 150,528 ערכים. אחרי שכבת Conv אחת עם 64 פילטרים (padding=same): 224×224×64 = ~3.2 מיליון ערכים.

אם מהשכבה הזו ניכנס ישר לשכבת Dense עם 1024 יחידות, נדרשו לנו 3.2 מיליון × 1024 = 3.3 מיליארד פרמטרים רק לחיבור הזה. זה לא ריאלי — לא בזיכרון GPU, לא בזמן אימון, לא בנתונים. צריך מנגנון שדוחס את המידע בצורה חכמה.

זה תפקיד ה-Pooling. כל שכבת pool מקטינה פי 4 את ה-spatial size. אחרי 5 שכבות pool, התמונה הופכת מ-224×224 ל-7×7 — פי 1024 פחות ערכים. הרשת מסוגלת ללמוד תכונות מורכבות בלי להתפוצץ בגודל.

4 הסיבות העיקריות:

סיבהמה זה אומר בפועל
חישוביתפחות ערכים → פחות פעולות multiplication-add → אימון מהיר פי עשרות
זיכרוןקריטי ב-GPU עם 6-24GB VRAM. בלי pool, מודלים גדולים פשוט לא נכנסים
פרמטריםפחות חיבורים בשכבות הבאות → מודל קטן יותר → פחות overfit
הכללהמאלץ את הרשת ללמוד תכונות עמידות לתזוזות קטנות

מה זה "הקטנת מימדים" באמת — והאם זה לא איבוד מידע?

תחושה אינטואיטיבית ראשונה: "אם אני מוריד מ-28×28 ל-14×14, לא איבדתי 75% מהמידע?"

התשובה היא כן ולא בו זמנית — וכאן הקסם:

מה כן מאבדיםמה לא מאבדים
מידע על המיקום המדויק של תכונות (פיקסל-לפיקסל) מידע על נוכחות תכונות בכל אזור
פרטים מקומיים זעירים היכולת לזהות תבניות גדולות יותר
רעש מקומי שלא היה אינפורמטיבי הסיגנל החזק ביותר באזור

אנלוגיה: דמיין שאתה רואה תמונה דרך ערפל קל. אתה לא רואה כל פיסת דשא בודדת, אבל אתה עדיין מזהה שזה כלב, גינה, ואדם. הפרטים שאיבדת אינם קריטיים לזיהוי — בעצם הסרת הרעש הזה עוזרת לרשת להתמקד במהותי.

למה זה גם מונע overfit (לא רק חיסכון בחישוב)

בלי Pooling, הרשת יכולה ללמוד "פיקסל 27,42 הוא קצה" ולהסתמך על המיקום המדויק. עם Pooling, היא נאלצת ללמוד "באזור הזה יש קצה" — הכללה ברמה אחת גבוהה יותר. כשהמודל פוגש תמונה חדשה שבה הקצה זז קצת, הוא עדיין יזהה — כי הוא לא שינן את המיקום המדויק.

Receptive Field — הסוד שמסביר הכל

זה המושג שהופך את כל הסיפור הזה למבריק. אם תבין את זה — תבין למה CNN עובד.

הגדרה: ה-Receptive Field של פיקסל בשכבה X הוא האזור בתמונת הקלט המקורית שהפיקסל הזה "מסכם". כלומר: כמה גדול האזור בתמונה שהשפיע על ערך הפיקסל הזה.

נראה איך זה גדל לאורך הרשת:

שכבהפעולהרזולוציהReceptive Field
Input28×281×1 (כל פיקסל הוא עצמו)
Conv 3×3סינון מקומי28×283×3
MaxPool 2×2דחיסה14×146×6 (כל פיקסל מייצג 6×6 בקלט)
Conv 3×3סינון14×1410×10
MaxPool 2×2דחיסה7×720×20
Conv 3×3סינון7×724×24 ← מכסה כמעט את כל התמונה!

שים לב למה שקרה: Pooling הכפיל את ה-Receptive Field. אחרי כמה שכבות, פיקסל בודד בעומק רואה אזור גדול בקלט. זה מאפשר לרשת לתפוס תבניות גדולות (פנים, אובייקטים שלמים) בלי להגדיל את גודל הפילטר.

זה הסוד שמסביר את "תבניות מורכבות יותר ככל שיורדים בעומק":
  • שכבות ראשונות → Receptive Field קטן → רואות קצוות, צבעים, מרקמים בסיסיים.
  • שכבות אמצעיות → Receptive Field בינוני → רואות פינות, צורות, מרכיבי אובייקטים (עיניים, אוזניים).
  • שכבות עמוקות → Receptive Field גדול → רואות פנים שלמות, אובייקטים, סצנות.
בלי Pooling, היינו צריכים פילטרים ענקיים (24×24!) כדי שפיקסל יראה אזור גדול. זה לא ריאלי. עם Pooling, אנחנו מקבלים את אותה תוצאה עם פילטרים קטנים של 3×3 בלבד.

למה Max ולא Sum / Average?

Max Pooling שואל: "באזור הזה — האם התבנית הופיעה?"

שיטההשאלה שהיא עונה עליהמתאים ל
Max"האם התבנית הופיעה כאן בכלל?"זיהוי (כן/לא)
Sum"כמה תבניות הופיעו כאן ובאיזו עוצמה כוללת?"אגרגציה
Average"מה הסיגנל הממוצע?"החלקה / רגרסיה

במשימת זיהוי תמונה, אנחנו רוצים תשובה ברורה: "באזור הזה — האם זה קצה אנכי? כן/לא". Max נותן את התשובה הכי מובהקת — אם הפילטר זיהה את התבנית במקום אחד באזור, התשובה גבוהה. אם לא — התשובה נמוכה.

יתרון נוסף של Max — Translation Invariance מובנה: אם הקצה האנכי "זז" פיקסל ימינה בתמונה הבאה — עדיין יישאר אותו ערך מקסימלי בחלון. הסיגנל לא משתנה. עם Average, כל תזוזה משנה את הממוצע מעט — פחות יציב.

מטאפורה לסיכום: זכוכית מגדלת מתרחקת

חשוב על ההבדל בין שני אופני הסתכלות על תמונה:

  • בלי Pooling — אתה רואה את התמונה דרך זכוכית מגדלת קבועה. רואה כל פיקסל בבירור, אבל לעולם לא יכול לזהות מה הצורה הכוללת.
  • עם Pooling — אתה מתרחק הדרגתית. ככל שאתה מתרחק, רואה תכונות גדולות יותר: עין → אף → פנים → אדם → קבוצה.

הרשת בונה את ההבנה שלה היררכית דרך הריחוק הזה. בלעדיו, היא לעולם לא תוכל "להתרחק" ולראות את התמונה הגדולה. וזו בדיוק הסיבה שהיררכיית התכונות (קצוות → צורות → אובייקטים) קיימת ב-CNN — היא ישירה הצורך של ה-Pooling.

Max Pooling

חלון (לרוב 2×2), stride 2, ולוקחים את הערך המקסימלי בכל חלון:

[1, 4, 8, 9] [5, 9] [3, 5, 3, 4] → [8, 7] [8, 1, 1, 1] [7, 1, 4, 7] תוצאה: 4×4 → 2×2

Average Pooling

אותה פעולה, אבל לוקחים את הממוצע. פחות נפוץ במודרני (Max עובד טוב יותר ברוב המקרים).

Global Average Pooling

חלון בגודל כל ה-feature map → מספר אחד לכל מפה. נפוץ בקצה רשתות מודרניות (במקום Flatten + Dense).

למה Max Pooling חזק

  • הקטנת מימדים — פי 4 פחות פיקסלים אחרי 2×2 pool.
  • Translation invariance — אם התבנית "זזה" קצת, היא עדיין תופיע ב-pool.
  • פחות פרמטרים → פחות overfit.

תרשים — Max Pooling 4×4 → 2×2

Max Pooling — מטריצה 4×4 הופכת ל-2×2 Input (4×4) 1 4 8 9 3 5 3 4 8 1 1 1 7 1 4 7 MaxPool 2×2 stride=2 לוקחים את המקסימום בכל חלון של 2×2 Output (2×2) 5 9 8 7 4 חלונות צבעוניים — כל אחד 2×2 כל ערך הוא המקסימום של החלון התואם בקלט

9. ארכיטקטורה כללית של CNN — איך הצורות משתנות

הדפוס הסטנדרטי של CNN:

Input (32×32×3) → Conv2D + ReLU → MaxPool → Conv2D + ReLU → MaxPool → ... → Flatten → Dense → Softmax

שינוי הצורה לאורך הרשת:

שכבהגובה×רוחבעומקסה"כ ערכים
Input32×323 (RGB)3,072
Conv2D(32 filters, 3x3, padding=same)32×323232,768
MaxPool(2x2)16×16328,192
Conv2D(64 filters)16×166416,384
MaxPool(2x2)8×8644,096
Conv2D(128 filters)8×81288,192
Flatten8,192
Dense(10, softmax)10

הדפוס: ככל שמתקדמים, האורך והרוחב קטנים אבל העומק גדל. ההיגיון: השכבות הראשונות לומדות תבניות פשוטות (קצוות), השכבות העמוקות לומדות תבניות מורכבות (חלקי גוף, אובייקטים) — ולכל שכבה צריך יותר "ערוצים" לבטא את העושר הזה.

למה ReLU אחרי Conv?

בדיוק כמו ב-MLP — ReLU מוסיפה אי-לינאריות. בלעדיה, רצף שכבות convolution היה שווה לשכבת convolution אחת גדולה.

תרשים — האבולוציה של הצורה לאורך CNN

CNN Architecture — H,W מצטמצמים, depth גדל INPUT 32×32×3 RGB Conv2D CONV 32×32×32 MaxPool POOL 16×16×32 Conv2D CONV 16×16×64 Pool POOL 8×8×64 Flatten FLAT 4096 Dense DENSE 10 H × W: 32×32 32×32 16×16 16×16 8×8 Depth: 3 32 32 64 64 4096 10 המסר — רוחב וגובה מצטמצמים, עומק גדל; הרשת לומדת תבניות מופשטות יותר ויותר

10. Translation / Rotation Invariance ו-Data Augmentation

השאלה: אם הרשת ראתה חתול בצד ימין של התמונה, האם תזהה חתול שמופיע בצד שמאל? בזווית? בגודל אחר?

Translation Invariance — מובנה ב-CNN

בזכות weight sharing ו-Max Pooling, התשובה היא כן. הפילטר שלמדנו לזהות אוזניים יזהה אותן בכל מקום בתמונה.

Rotation/Scale Invariance — לא מובנה

הרשת לא יודעת אוטומטית שזה אותו אובייקט גם כשהוא מסובב או בגודל אחר. הפתרון: Data Augmentation — מייצרים גרסאות מסובבות / מוגדלות / מוטות של התמונות בזמן האימון.

טכניקות סטנדרטיות של Data Augmentation:

  • Rotation — סיבוב אקראי ב-±15°.
  • Translation — הזזה לימין/שמאל.
  • Scaling / Zoom — הגדלה/הקטנה.
  • Horizontal Flip — היפוך אופקי (טוב לחיות, אסור לאותיות).
  • Color jittering — שינוי בהירות, ניגודיות, רוויה.

בונוס: Augmentation מטפל גם ב-overfit — המודל רואה תמונות "חדשות" בכל epoch ולכן לומד להכליל במקום לזכור.

ב-Keras:

data_augmentation = keras.Sequential([ layers.RandomFlip("horizontal"), layers.RandomRotation(0.1), layers.RandomZoom(0.1), ])

11. ארכיטקטורות CNN שעשו מהפכה

AlexNet (2012) — המהפך הגדול

  • צוות: Alex Krizhevsky, Ilya Sutskever, Geoffrey Hinton (אוניברסיטת טורונטו).
  • הישג: זכייה בתחרות ImageNet עם 16% שגיאה — לעומת 26% של המקום השני.
  • חידושים: שימוש ראשון ב-ReLU, ב-Dropout, וב-GPU לאימון.
  • גודל: 8 שכבות (5 Conv + 3 FC), 60M פרמטרים, אומן על 2 GPUs במשך שבוע.
  • השפעה: נקודת המהפך של DL — מאז כל זוכי ImageNet מבוססי DL.

VGG (2014) — אלגנטיות במקום עומק

  • צוות: Visual Geometry Group, אוניברסיטת אוקספורד.
  • גרסאות: VGG-16 (16 שכבות), VGG-19 (19 שכבות).
  • חידוש: משתמש רק ב-Conv 3×3 ו-MaxPool 2×2. במקום פילטרים גדולים (11×11 של AlexNet) — שתי שכבות 3×3 רצופות נותנות אותו שדה ראייה עם פחות פרמטרים.
  • חיסרון: 138M פרמטרים — מודל כבד מאוד.

ResNet (2015) — Skip Connections

  • צוות: Microsoft Research.
  • הבעיה שפתרו: במודלים עמוקים מאוד (50+ שכבות), הביצועים יורדים — בגלל vanishing gradient (הגרדיאנט "נחלש" כשהוא חוזר אחורה).
  • הפתרון: Skip Connections — הוספת קיצור-דרך מכל שכבה לשכבה אחרי הבאה, כך שהגרדיאנט "מדלג" מעל שכבות.
  • תוצאה: רשת של 152 שכבות שעובדת מצוין. ResNet-152 הגיעה לביצועים על-אנושיים ב-ImageNet.
  • השפעה: Skip connections היום הם סטנדרט בכל הארכיטקטורות המודרניות (Transformers, Diffusion וכו').
מודלשנהשכבותפרמטריםשגיאה ב-ImageNet
AlexNet2012860M16%
VGG-16201416138M7.3%
ResNet-152201515260M3.6%
אדם ממוצע~5%

12. תוספות מקובץ הקוד 7.FindingEdgesWithKernels.ipynb

ה-notebook של ההרצאה מדגים בפועל את פעולת ה-Kernel — בלי NN בכלל, רק NumPy + OpenCV. זה הצעד הקריטי להבנה אינטואיטיבית של מה ש-CNN לומדת.

12.1 OpenCV — הספרייה לעיבוד תמונה

cv2 (OpenCV) היא ספריית קוד פתוח ל-computer vision. ההתקנה:

!pip install opencv-python import cv2

12.2 טעינת תמונה והמרה לאפור

import matplotlib.image as mpimg image = mpimg.imread('Dolphin_Louie.jpeg') # (H, W, 3) — RGB gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) # (H, W) — grayscale

למה אפור? כי הדגמת קצוות פשוטה יותר על ערוץ אחד. ב-CNN אמיתי נעבוד ב-RGB.

12.3 פילטר Sobel — קוד מלא

import numpy as np # Sobel x — מזהה קצוות אנכיים (אופקיים? לא, אנכיים!) sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]) # Sobel y — מזהה קצוות אופקיים sobel_y = np.array([[-1, -2, -1], [ 0, 0, 0], [ 1, 2, 1]]) # הפעלת הפילטר על התמונה האפורה filtered = cv2.filter2D(gray, -1, sobel_x) plt.imshow(filtered, cmap='gray')

נסה בעצמך: אחרי שתריץ — תראה את התמונה המקורית של הדולפין הופכת לתמונה ש"מאירה" רק את הקצוות האנכיים.

12.4 פילטרים מותאמים אישית — חלוקה לרבעים

ה-notebook גם מדגים פילטרים גדולים יותר (4×4) שמחלקים את התמונה לחצי שמאל/ימין או עליון/תחתון:

f_left_right = np.array([[-1,-1, 1, 1], [-1,-1, 1, 1], [-1,-1, 1, 1], [-1,-1, 1, 1]]) # מדגיש מעבר משמאל לימין

12.5 הקשר ל-CNN המאומן

תובנה מרכזית: הפילטרים שראינו כאן (Sobel, blur, sharpen) הם בדיוק מה שהשכבות הראשונות של CNN לומדות בעצמן. אם תקח רשת מאומנת ותציג את ה-32 הפילטרים הראשונים שלה — תזהה: קצוות אנכיים, אופקיים, אלכסוניים, נקודות צבע. הרשת "המציאה" את Sobel בלי שלימדנו אותה!

13. שאלות חזרה למבחן — הרצאה 7

ש1. מה ההבדל המרכזי בין MLP ל-CNN בקלט שלהם?
הצג תשובה
MLP מקבל וקטור 1D (תמונה צריכה Flatten לפני הזנה). CNN מקבל מטריצה דו-מימדית (או טנסור 3D עבור RGB) — שומר על המבנה המרחבי. כשמיישרים תמונה לוקטור, פיקסלים שכנים מאבדים את הקשר ביניהם.
ש2. למה ב-CNN יש פחות פרמטרים מ-MLP על אותה תמונה?
הצג תשובה
שתי סיבות: (1) Local connectivity — כל נוירון מחובר רק לחלון קטן (3×3 או 5×5) ולא לכל הפיקסלים. (2) Weight sharing — אותו פילטר מופעל על כל התמונה, אז לומדים את ה-9 משקלות פעם אחת בלבד. ב-MLP על MNIST: ~800K פרמטרים. ב-CNN דומה: ~50K.
ש3. נתונה תמונה 32×32×3, פילטר 3×3, padding='same', stride=1, 64 פילטרים. מה הצורה של ה-feature map שיוצא מהשכבה? כמה פרמטרים יש בשכבה?
הצג תשובה
צורת הפלט: 32×32×64 (padding=same שומר על H,W; 64 פילטרים = 64 ערוצי עומק). פרמטרים: כל פילטר הוא 3×3×3 (3 ערוצי קלט) + bias = 28. ל-64 פילטרים: 64×28 = 1,792 פרמטרים בלבד.
ש4. מה ההבדל בין Max Pooling ל-Average Pooling? למה Max פופולרי יותר?
הצג תשובה
Max מחזיר את הערך הגבוה ביותר בחלון, Average מחזיר את הממוצע. Max פופולרי יותר כי הוא מדגיש את הסיגנל החזק — אם פילטר זיהה תבנית באזור, הערך הגבוה מבטא את עוצמת הזיהוי. Average "מרכך" את הסיגנל הזה. Max גם נותן יותר translation invariance.
ש5. למה Translation invariance מובנה ב-CNN אבל Rotation invariance לא?
הצג תשובה
Translation: הפילטר זהה בכל מקום (weight sharing) + Max Pooling בחלון = אותה תבנית מתגלה גם אם זזה. Rotation: הפילטר עצמו לא מסתובב — פילטר שלמד "אוזניים בצורה X" לא יזהה "אוזניים מסובבות". הפתרון הוא Data Augmentation — מציגים בזמן אימון גרסאות מסובבות של התמונה כדי שהרשת תפתח פילטרים מרובים שכל אחד מתאים לזווית מסוימת.
ש6. מה החידוש המרכזי של ResNet ולמה הוא הכרחי לרשתות עמוקות?
הצג תשובה
Skip connections — קיצורי דרך שמעבירים את הקלט של שכבה ישירות אל הפלט של שכבה אחרי הבאה (פלוס מה שעובר דרך השכבות). למה הכרחי: ברשתות של 50+ שכבות, הגרדיאנט "נחלש" עד שהוא חוזר לשכבות הראשונות (vanishing gradient) — והן לא מתעדכנות. Skip connection מאפשר לגרדיאנט לעבור ישירות ולהגיע לשכבה הראשונה בעוצמה. בזכות זה אפשר לאמן רשתות של מאות שכבות.

14. מה תצא מן הפרק הזה

  • תסביר במשפט אחד את ההבדל בין MLP ל-CNN ולמה CNN עדיף לתמונות.
  • תוכל לחשב את צורת ה-feature map שיוצא משכבת Conv2D עם פרמטרים נתונים.
  • תזהה Kernel של Sobel ותדע מה הוא עושה.
  • תזכור את 4 ה-hyperparameters של Conv2D: filter size, number, stride, padding.
  • תסביר למה Max Pooling חזק (3 סיבות: dim reduction, translation invariance, less overfit).
  • תזהה את הדפוס "אורך-רוחב יורדים, עומק עולה" לאורך CNN.
  • תכיר את 3 הארכיטקטורות הקלאסיות (AlexNet, VGG, ResNet) ותדע את החידוש של כל אחת.
  • תוכל להסביר למה Data Augmentation עוזר גם ל-rotation invariance וגם ל-overfit.
  • תזכור: הפילטרים ב-CNN לא נבחרים ידנית — הם נלמדים בעצמם דרך Backpropagation.
גשר להרצאה הבאה: בהרצאה 8 (CNN with Keras) נעבור מהתאוריה לפועל — נבנה CNN שלם על MNIST, על Cats vs Dogs, ונכיר Data Augmentation ו-Transfer Learning ב-Keras. זה ייקח את כל מה שלמדנו כאן והופך אותו לקוד שאפשר להריץ על ה-RTX PRO 4000 שלך תוך דקות.

הרצאה 8 — CNN with Keras: Cats vs Dogs

מצגת 8 49 שקפים תאריך: 11.05.2026 Transfer Learning

מטרת ההרצאה: לבנות CNN לסיווג בינארי (חתול/כלב) על מערך נתונים קטן (4,000 תמונות מתוך 25,000), ולהראות איך 4 גישות מתקדמות מטפסות מ-66.4% ל-97.9% accuracy על אותו test set. זו ההרצאה הראשונה שבה אנחנו מיישמים Transfer Learning בפועל.

על מה נדבר בפרק הזה

  1. הצגת המשימה — Kaggle Dogs vs Cats — וטיפול ב-dataset קטן.
  2. טעינת תמונות מתיקיות עם image_dataset_from_directory ו-tf.data.Dataset.
  3. גישה 1: CNN מ-Scratch — 5 בלוקי Conv2D + Pool. תוצאה: 66.4% (overfitting חזק).
  4. גישה 2: Data Augmentation + Dropout — כתרופה ל-overfitting. תוצאה: 81.1%.
  5. גישה 3: Feature Extraction עם VGG16 קפוא. שתי שיטות (מהירה vs עם augmentation). תוצאה: 97.9%.
  6. גישה 4: Fine-Tuning — הפשרת השכבות העליונות ב-lr נמוך מאוד. תוצאה: 97.7% (מעט יורד!).
  7. תוצאות בפועל מהריצה ב-Colab — מספרים אמיתיים, לא של הספר.

0. תמונת מפתח — 4 הגישות והקפיצה של Transfer Learning

לפני שניכנס לפרטים, הנה המסר העיקרי של ההרצאה במבט אחד: הקפיצה הגדולה אינה מ-augmentation או מ-fine-tuning, אלא במעבר מאימון From scratch לשימוש ברשת Pretrained — 31.5 נקודות אחוז בקפיצה אחת. ובאופן מפתיע — fine-tuning הפעם פגע במקום לשפר.

השוואת 4 גישות — Test Accuracy
תוצאות הריצה הסופיות מהמחברת (2026-05-12), לא מהספר. הקפיצה הגדולה: 31.5 נקודות בין From-scratch ל-Feature Extraction עם VGG16.
שלוש תובנות שיוצאות מהגרף:
  • Augmentation לבד (גישה 2) שווה ‎+14.7 נקודות. שווה — אבל לא מספיק לרשת קטנה.
  • Transfer learning (גישה 3) שווה ‎+16.8 נקודות נוספות. הזינוק הגדול — כי conv_base כבר ראה 14 מיליון תמונות.
  • Fine-tuning (גישה 4) הפעם הוריד ‎−0.2 נקודות (97.9% → 97.7%). זה לא שגיאה: כש-feature extraction כבר מצוין, fine-tuning יכול להזיק כי הוא מסכן את המשקלים הטובים שכבר נלמדו. אז למה מלמדים את זה? כי בדרך כלל זה עוזר — והוא תלוי בריצה.

1. הצגת המשימה — Kaggle Dogs vs Cats

בשנת 2013 Kaggle ערכה תחרות מפורסמת — לסווג 25,000 תמונות של חתולים וכלבים. ב-2013 קל היה לקבל 80% עם MLP פשוט; היום, עם CNN ו-transfer learning, מגיעים ל-99%+.

ההחלטה במחברת: במקום להשתמש בכל 25,000 התמונות, ניקח subset קטן של 4,000. למה? כי המטרה של ההרצאה היא "איך לעבוד עם dataset קטן" — וזה היה החלק החשוב בספר של Chollet.

חלוקת dataset של Kaggle Dogs vs Cats
החלוקה ל-train (2000) / validation (1000) / test (2000), עם 50/50 חתולים-כלבים בכל סט.

הקוד שיוצר את החלוקה (cell 14)

import os, shutil, pathlib

original_dir = pathlib.Path("train")
new_base_dir = pathlib.Path("cats_vs_dogs_small")

def make_subset(subset_name, start_index, end_index):
    for category in ("cat", "dog"):
        dir = new_base_dir / subset_name / category
        os.makedirs(dir)
        fnames = [f"{category}.{i}.jpg" for i in range(start_index, end_index)]
        for fname in fnames:
            shutil.copyfile(src=original_dir / fname,
                            dst=dir / fname)

make_subset("train",      start_index=0,    end_index=1000)
make_subset("validation", start_index=1000, end_index=1500)
make_subset("test",       start_index=1500, end_index=2500)
למה החלוקה דווקא כך? 2,000-1,000-2,000 ולא 70-15-15? כי כשעובדים עם dataset קטן רוצים test set גדול ככל האפשר כדי שהמספרים יהיו אמינים. validation של 1000 מספיק לבחירת hyperparameters, אבל מעל זה זה "ביזבוז".

2. טעינת תמונות עם image_dataset_from_directory

במקום לכתוב data-loading ידני, Keras מציע פונקציה אלגנטית — שטוענת ישירות תמונות מתיקייה, כאשר שם תת-התיקייה משמש כ-label.

from tensorflow.keras.utils import image_dataset_from_directory

train_dataset = image_dataset_from_directory(
    new_base_dir / "train",
    image_size=(180, 180),    # resize אוטומטי
    batch_size=32)            # 63 batches × 32 = 2016 (קצת יותר מ-2000)

# פלט בפועל:
# Found 2000 files belonging to 2 classes.
# Found 1000 files belonging to 2 classes.
# Found 2000 files belonging to 2 classes.

מה שמתקבל הוא tf.data.Dataset — אובייקט lazy שיודע לטעון, לעבד ולחלק batches בלי לטעון את כל ה-2,000 תמונות ל-RAM בבת-אחת.

בדיקת shape של batch

for data_batch, labels_batch in train_dataset:
    print("data batch shape:", data_batch.shape)
    print("labels batch shape:", labels_batch.shape)
    break

# פלט:
# data batch shape: (32, 180, 180, 3)
# labels batch shape: (32,)

A. למה צריך Dataset object בכלל?

ביחידות 3-6 עבדנו עם MNIST/Boston/Reuters/IMDB — כל אחד מהם dataset קטן יחסית (~50MB) שהסבנו ל-NumPy והעברנו ישירות ל-model.fit(). למה לא נמשיך כך?

חישוב הזיכרון — Cats vs Dogs המלא
פריטחישובתוצאה
גודל תמונה אחת ב-180×180×3180 × 180 × 3 × 4 bytes (float32)~389 KB
‎4,000 תמונות (ה-subset שלנו)4,000 × 389 KB~1.5 GB
‎25,000 תמונות (ה-dataset המלא)25,000 × 389 KB‎~9.7 GB
‎עם תמונות בגודל מלא (500×500)25,000 × 3 MB~75 GB ❌

ל-Colab Free יש 12 GB RAM. גם ה-subset הקטן (1.5 GB כ-NumPy) ניתן לטעון, אבל עם תמונות מלאות זה בלתי-אפשרי.

‎3 הבעיות שה-Dataset object פותר
  1. זיכרון: טוען רק את ה-batch הנוכחי לזיכרון, לא את כל ה-dataset.
  2. GPU starvation: GPU מהיר פי 100 מ-CPU. אם ה-CPU טוען נתונים באיטיות, ה-GPU "מחכה". prefetch מבצע טעינה מקבילית — בזמן שה-GPU מעבד batch N, ה-CPU כבר טוען batch N+1.
  3. גודל ה-dataset לא מוגבל: אפשר לאמן על TB של נתונים בלי לדאוג ל-RAM.
הקשר חשוב: בכל ה-frameworks המודרניים של DL יש Dataset object דומה — PyTorch מציע DataLoader, JAX מציע Grain. הרעיון זהה: הפרדה בין ייצור הנתונים (CPU) לבין הצריכה (GPU).

B. הניסוי במחברת — צעד-צעד עם המספרים

כדי להבין מה tf.data.Dataset עושה, המחברת מתחילה בדוגמה מינימלית עם NumPy אקראי (1000 וקטורים, כל אחד באורך 16). זה לא Cats vs Dogs — זו "סנדבוקס" להבנת ה-API.

שלבהקודהפלט בפועלמה זה אומר
1. יצירת NumPy np.random.normal(
  size=(1000, 16))
(1000, 16) מטריצה: ‎1000 שורות × 16 עמודות. כל שורה היא "דוגמה".
2. הפיכה ל-Dataset tf.data.Dataset
  .from_tensor_slices(
    random_numbers)
(16,)
(16,)
(16,) ...
הציר הראשון "נחתך" — עכשיו יש 1000 elements, כל אחד וקטור באורך 16. זה כמו list(zip(*matrix)) רעיונית.
3. קיבוץ ל-batches dataset.batch(32) (32, 16)
(32, 16)
(32, 16) ...
32 elements מקובצים חזרה למטריצה. שים לב — זה לא קוד שמוסיף ציר חדש, זה מצמצם את מספר ה-elements.
4. טרנספורמציה dataset.map(
  lambda x:
  tf.reshape(x, (4, 4)))
(4, 4)
(4, 4)
(4, 4) ...
הפעלת פונקציה על כל element. הוקטור באורך 16 הופך למטריצה ‎4×4 (סך הערכים זהה: 16).

ה-shape ב-Cats vs Dogs המלא הוא (32, 180, 180, 3) — אותו רעיון, רק ש"דוגמה אחת" היא תמונה 4-מימדית במקום וקטור.

C. הזרימה הוויזואלית של הנתונים

זרימת נתונים ב-tf.data.Dataset
‎4 שלבים: NumPy → Dataset → Batched → Model. הצבעים מציגים את מצב הנתונים בכל שלב.
שני העולמות של Dataset

חשוב להבחין בין שתי דרכים ליצור Dataset:

from_tensor_slices (המחברת)image_dataset_from_directory (אימון אמיתי)
מקור הנתוניםNumPy שכבר ב-RAMקבצי JPG בדיסק
זיכרוןגדל לפי גודל ה-NumPyקבוע — רק batch אחד
תאים מתאימיםנתונים קטנים, ניסוייםתמונות, אודיו, קבצים גדולים
מהירות גישהמיידית (כבר ב-RAM)תלוי בדיסק (SSD מומלץ)

D. למה batch_size = 32? המסחר הכבד של DL

זו אחת הבחירות הכי חשובות בכל אימון. אבל למה ‎32 ולא ‎1 או ‎1000?

batch_sizeגרדיאנטזיכרוןמהירות epochשם הגישה
1 רועש מאוד (דוגמה אחת) מינימלי איטי (לא מנצל GPU) ‎Pure SGD
32-128 איזון טוב (rolling average) סביר (1-4 GB GPU) מהיר (מנצל GPU) Mini-batch SGD
2000 (כל הסט) מדויק (ממוצע מלא) ענקי (לא תמיד אפשרי) איטי לכל צעד Full-batch GD
למה לא ‎1?

גרדיאנט מדוגמה אחת רועש מאוד — הוא יכול להצביע לכל כיוון. הרבה צעדים רועשים = "ריקוד אקראי" שלוקח הרבה זמן עד שמתכנס. בנוסף, GPU מודרני מבצע פעולות מטריצות במקביל — batch קטן מאוד משאיר אותו "רעב".

למה לא 1000?

גרדיאנט מדויק מאוד "תקוע במינימום מקומי" — אם הגרדיאנט אפס בנקודה כלשהי, אין שום רעש שיוציא אותך משם. שינויים קטנים בכיוון (בזכות הרעש של mini-batch) הם טובים לבריחה ממינימה מקומיים.

למה חזקות של 2 (16, 32, 64, 128)?

זיכרון GPU מיועל למיוונים שהם חזקות של 2 (אלו ה-warps של CUDA). batch של 64 רץ יעיל יותר מ-batch של 50, גם אם תאורטית 50 קטן יותר.

כלל אצבע:
  • תמונות ב-CNN בינוני: 32 (default אצל Chollet)
  • תמונות גדולות (512+): 16 או 8 — מוגבל בזיכרון GPU
  • NLP/Transformers: 32-128
  • אם רואים OOM error → להוריד batch

E. למה shuffle חיוני — ומתי לא לעשות אותו

תרחיש: ה-train set שלנו מסודר לפי קטגוריה — קודם 1000 חתולים, אחר כך 1000 כלבים. אם נאמן בלי shuffle:

# בלי shuffle:
# Batch 1-31: כולם cat → הרשת לומדת "תמיד תגיד cat" → val_accuracy = 50%
# Batch 32-63: כולם dog → הרשת לומדת "תמיד תגיד dog" → val_accuracy = 50%
# בסוף epoch: הרשת זוכרת רק את הקטגוריה האחרונה!

# עם shuffle:
# Batch 1: 15 cat + 17 dog → גרדיאנט מאוזן → לימוד אמיתי
איך עובד buffer ב-shuffle

dataset.shuffle(buffer_size=1000) ממלא buffer של ‎1000 elements, ובוחר אקראית מתוכם בכל פעם. כלל אצבע: buffer_size = גודל ה-dataset — כך שכל element יכול להיות בכל מיקום. אם זיכרון לא מספיק, ‎10,000 מספיק לרוב.

‎חוקי-זהב של shuffle:
  • Train set: תמיד shuffle, בכל epoch מחדש (Keras עושה את זה אוטומטית).
  • Validation set: אל תערבב — אתה רוצה לקבל את אותו loss כל פעם להשוואה.
  • Test set: אל תערבב — מדידה סופית פעם אחת.
  • Time series: אסור לערבב — הסדר הוא חלק מהמשמעות!
טריק חשוב — סדר הפעולות
# ❌ שגוי — קודם batch אז shuffle:
dataset.batch(32).shuffle(1000)   # ערבב batches אבל בתוך batch הסדר נשאר

# ✅ נכון — קודם shuffle אז batch:
dataset.shuffle(1000).batch(32)   # ערבב elements ואז קבץ

F. ‎5 טיפים פרקטיים לעבודה עם Dataset

  1. .prefetch(tf.data.AUTOTUNE) — תמיד.
    train_dataset = train_dataset.prefetch(tf.data.AUTOTUNE)
    מבצע טעינה של ה-batch הבא במקביל לעיבוד ה-batch הנוכחי ע"י המודל. שיפור מהירות של 20-50% בחינם. AUTOTUNE נותן ל-TF לבחור את ה-buffer האידיאלי לבד.
  2. .cache() — אם ה-dataset נכנס ל-RAM.
    train_dataset = train_dataset.cache().shuffle(1000).batch(32)
    אחרי ה-epoch הראשון, כל ה-dataset נשמר ב-RAM (או על דיסק). מהאפוק השני והלאה, אין צורך לטעון תמונות מהדיסק. אזהרה: אם ה-dataset לא נכנס ל-RAM, השתמש ב-.cache("filename") לקבצי cache בדיסק.
  3. num_parallel_calls=tf.data.AUTOTUNE ב-map.
    dataset.map(preprocess_fn, num_parallel_calls=tf.data.AUTOTUNE)
    מפעיל את ה-map על מספר ליבות CPU במקביל. קריטי כש-preprocess_fn כבד (augmentation, resize).
  4. הסדר הנכון של pipeline:
    dataset = (raw_dataset
        .shuffle(buffer_size=10000)        # 1. ערבב elements
        .map(preprocess, num_parallel_calls=tf.data.AUTOTUNE)  # 2. עבד
        .batch(32)                          # 3. קבץ ל-batches
        .prefetch(tf.data.AUTOTUNE))        # 4. prefetch אחרון
    ‎ה-prefetch תמיד אחרון. ה-shuffle לפני ה-batch (ראינו ב-§E). ה-map אחרי shuffle כדי שכל epoch תקבל augmentation שונה לאותה תמונה.
  5. בדיקת תקינות לפני אימון:
    # הצץ ב-batch אחד לפני fit() — חוסך שעות של דיבוג
    for images, labels in dataset.take(1):
        print(f"Images: {images.shape}, dtype: {images.dtype}")
        print(f"Labels: {labels.shape}, range: [{labels.min()}, {labels.max()}]")
        print(f"Image values range: [{images.numpy().min()}, {images.numpy().max()}]")
    אם רואים dtype=uint8 במקום float32, או טווח ערכים ‎[0, 255] במקום ‎[0, 1] — יש בעיית pre-processing.
מסר ל-DL בכלל: רוב הזמן באימון לא הולך על "חישוב המודל" אלא על טעינת הנתונים. pipeline איטי = GPU שמחכה ל-CPU = בזבוז של GPU יקר. ‎4 טיפים אלה (prefetch, cache, parallel map, סדר נכון) הם ההבדל בין אימון של 8 שעות לבין 30 דקות על אותו GPU.

3. גישה 1 — CNN מ-Scratch

הגישה הראשונה הכי טבעית: בונים CNN מאפס ומאמנים אותו על 2,000 התמונות. אבל מה הארכיטקטורה הנכונה?

הארכיטקטורה — 5 בלוקי Conv + Dense (cell 16)

inputs = keras.Input(shape=(180, 180, 3))
x = layers.Rescaling(1./255)(inputs)
x = layers.Conv2D(filters=32,  kernel_size=3, activation="relu")(x)
x = layers.MaxPooling2D(pool_size=2)(x)
x = layers.Conv2D(filters=64,  kernel_size=3, activation="relu")(x)
x = layers.MaxPooling2D(pool_size=2)(x)
x = layers.Conv2D(filters=128, kernel_size=3, activation="relu")(x)
x = layers.MaxPooling2D(pool_size=2)(x)
x = layers.Conv2D(filters=256, kernel_size=3, activation="relu")(x)
x = layers.MaxPooling2D(pool_size=2)(x)
x = layers.Conv2D(filters=256, kernel_size=3, activation="relu")(x)
x = layers.Flatten()(x)
outputs = layers.Dense(1, activation="sigmoid")(x)
model = keras.Model(inputs=inputs, outputs=outputs)

model.compile(loss="binary_crossentropy",
              optimizer="rmsprop",
              metrics=["accuracy"])

זרימת ה-shape דרך הרשת

זרימת ה-shape דרך 11 השכבות של ה-CNN
11 שכבות. spatial מתכווץ (180²→7²), depth גדל (3→256). סך פרמטרים: 991,041.

📖 מילון מונחים — מה כל מילה במשוואה אומרת

לפני שמסתכלים על הנוסחה output = (input − kernel) / stride + 1 — נוודא שכל מונח ברור:

מונחהסברערך אצלנו
spatial — "ממדי הרוחב והגובה" הגודל הגיאומטרי של ה-tensor — גובה × רוחב (לא כולל ערוצים). לדוגמה ‎180×180. 180×180 בקלט → 7×7 בסוף
depth — "עומק / מספר ערוצים" המימד השלישי של ה-tensor — ערוצי הצבע בקלט, או מספר ה-filters אחרי Conv2D. 3 בקלט (RGB) → 256 בסוף
kernel / filter "חלון" קטן (לרוב 3×3 או 5×5) שמסתובב על התמונה ומחשב פעולה (convolution). זה הדבר שהרשת לומדת. 3×3 בכל ‎Conv2D שלנו
kernel_h הגובה של ה-kernel (kernel_height). בדרך כלל שווה ל-kernel_w (kernel ריבועי). 3
kernel_w הרוחב של ה-kernel (kernel_width). 3
stride בכמה פיקסלים ה-kernel "קופץ" בכל צעד. stride=1 = קופץ ב-1 (חופף הרבה). stride=2 = קופץ ב-2 (פחות חפיפה, יותר downsample). 1 (ברירת המחדל)
padding="valid" אין מילוי בקצוות → ה-output קטן יותר מה-input (ב-kernel − 1). זה מה שבחרנו
padding="same" הוספת אפסים בקצוות → ה-output שומר על אותו גודל spatial של ה-input. (לא בשימוש פה)
input_channels ‎depth של ה-input שנכנס לשכבה. 3 → 32 → 64 → 128 → 256
filters ‎depth של ה-output. הפרמטר העיקרי של Conv2D — כמה kernels שונים השכבה לומדת. 32 → 64 → 128 → 256 → 256
bias קבוע נוסף שנוסף לכל filter (נוסחה: output + bias). יש bias אחד לכל filter, ולכן +filters בנוסחה. 32, 64, 128, 256, 256
קוד-לחישוב — Conv2D הראשון: filters=32, kernel=3×3, input=180×180×3.
  • פרמטרים: 32 × (3 × 3 × 3) + 32 = 896
  • Output spatial (padding=valid, stride=1): 180 − 3 + 1 = 178. אחרי MaxPool 2×2: 178/2 = 89
💡 איך לזכור את הנוסחה output = (input − kernel) / stride + 1:
  • למה − kernel? ה-kernel "מבזבז" שולי תמונה כי הוא חייב להתאים כולו. עם kernel=3, השוליים של פיקסל מימין ושמאל אובדים → ‎"−2".
  • למה / stride? אם קופצים בקפיצות של 2, יש חצי כמות מיקומים שונים → מחלקים ב-stride.
  • למה + 1? כי גם בנקודת ההתחלה (פיקסל 0) ה-kernel מסיים את הספירה.

קריאת model.summary() — מה מסתתר בכל שורה

הטבלה שמדפיסה Keras מתארת את הרשת שכבה-אחר-שכבה. ‎3 העמודות שלה:

עמודהמה היא אומרת
Layer (type)שם השכבה (אוטומטי או מותאם) ועוצב הסוג שלה: InputLayer, Rescaling, Conv2D, MaxPooling2D, Flatten, Dense.
Output Shapeה-shape של ה-tensor שיוצא מהשכבה. הראשון תמיד None = batch size דינמי (כי הרשת לא תלויה במספר הדוגמאות ב-batch).
Param #כמה משקלים שניתנים לאימון יש בשכבה. אצל Rescaling, MaxPooling2D, Flatten זה תמיד 0 — הן רק "מעצבות" tensor בלי משקלים.

חישוב מפורט — איך מגיעים ל-991,041 פרמטרים

הנוסחה הכללית לחישוב פרמטרים בשכבת Conv2D:

params = filters × (kernel_h × kernel_w × input_channels) + filters
                              ↑                                ↑
                       מטריצת המשקלים                    bias (אחד לכל filter)

ל-Dense זה פשוט יותר:

params = output_units × input_units + output_units
                ↑                            ↑
            המשקלים                       biases
#שכבהOutput ShapeחישובParams
1InputLayer(None, 180, 180, 3)— (placeholder)0
2Rescaling(None, 180, 180, 3)— (חלוקה ב-255, אין משקלים)0
3Conv2D (32)(None, 178, 178, 32)32 × (3 × 3 × 3) + 32896
4MaxPooling2D(None, 89, 89, 32)— (downsample)0
5Conv2D (64)(None, 87, 87, 64)64 × (3 × 3 × 32) + 6418,496
6MaxPooling2D(None, 43, 43, 64)0
7Conv2D (128)(None, 41, 41, 128)128 × (3 × 3 × 64) + 12873,856
8MaxPooling2D(None, 20, 20, 128)0
9Conv2D (256)(None, 18, 18, 256)256 × (3 × 3 × 128) + 256295,168
10MaxPooling2D(None, 9, 9, 256)0
11Conv2D (256)(None, 7, 7, 256)256 × (3 × 3 × 256) + 256590,080
12Flatten(None, 12544)— (7 × 7 × 256 = 12,544)0
13Dense (1)(None, 1)1 × 12,544 + 112,545
Total trainable parameters991,041
בדיקה — סכימה ידנית
     896    (Conv2D 32)
+ 18,496    (Conv2D 64)
+ 73,856    (Conv2D 128)
+ 295,168   (Conv2D 256)
+ 590,080   (Conv2D 256)
+ 12,545    (Dense 1)
─────────
= 991,041   ✓
תובנה חשובה — איפה "הולכים" הפרמטרים?
  • ‎60% מהפרמטרים נמצאים בשכבת Conv2D האחרונה לבדה (‎590K מתוך ‎991K).
  • למה? כי ‎input_channels × filters גדל פי 2 בכל בלוק. בסוף יש 256 × (3 × 3 × 256) ≈ 590K.
  • Flatten ו-MaxPooling הם "חינמיים" — הם רק משנים shape בלי משקלים.
  • Dense(1) הסופי "רק" 12,545 פרמטרים — קטן יחסית למה שאפשר היה לצפות מ-12,544 קלטים.
השוואה כמותית — למה CNN חסכוני יותר מ-MLP על אותה תמונה? בואו נדמיין רשת MLP פשוטה לאותה משימה:
  • MLP: Flatten ל-180 × 180 × 3 = 97,200 נוירונים, ‎Dense של 512 → 97,200 × 512 ≈ 49.8M פרמטרים בשכבה הראשונה לבד!
  • CNN שלנו: ‎991K פרמטרים בכל הרשת. פי 50 פחות.
  • הסיבה — Weight sharing: אותו פילטר ‎3×3 מופעל על כל התמונה. לומדים פעם אחת, משתמשים בכל מקום.

תוצאות אימון — 30 epochs · ~4-6s/epoch (GPU מהיר)

הריצה בפועל מהמחברת:

Epoch  1/30 - loss: 0.6972 - accuracy: 0.5210 - val_loss: 0.6926 - val_accuracy: 0.5150
Epoch  5/30 - loss: 0.6193 - accuracy: 0.6750 - val_loss: 0.6881 - val_accuracy: 0.6040
Epoch 10/30 - loss: 0.4838 - accuracy: 0.7750 - val_loss: 0.6011 - val_accuracy: 0.7330
Epoch 15/30 - loss: 0.2782 - accuracy: 0.8875 - val_loss: 0.7735 - val_accuracy: 0.7420
Epoch 20/30 - loss: 0.1069 - accuracy: 0.9580 - val_loss: 1.7745 - val_accuracy: 0.6880
Epoch 25/30 - loss: 0.0585 - accuracy: 0.9815 - val_loss: 1.4504 - val_accuracy: 0.7460
Epoch 30/30 - loss: 0.0469 - accuracy: 0.9815 - val_loss: 1.5142 - val_accuracy: 0.7420
                          ↑                                          ↑
                ‎98% על training                          ‎74% על validation
                       → overfitting קלאסי
                          (val_loss קופץ מ-0.69 ל-1.5+!)

Test accuracy: 0.664  (= 66.4%)

גרפי learning curves

Accuracy curves From Scratch
Training vs Validation Accuracy
Loss curves From Scratch
Training vs Validation Loss
מה אומרים הגרפים? ה-training accuracy ממשיך לטפס ל-95%+ בעוד ה-validation accuracy תקוע ב-‎70-75%. ה-training loss יורד לכמעט אפס, ה-validation loss עולה. זו ההגדרה הקלאסית של overfitting — הרשת שיננה את 2,000 התמונות במקום ללמוד פיצ'רים שמכלילים.

4. גישה 2 — Data Augmentation + Dropout

אם הבעיה היא overfitting על dataset קטן, יש שתי תרופות מובהקות:

  1. Data Augmentation — להגדיל אפקטיבית את ה-dataset על ידי יצירת variants סינתטיים של כל תמונה.
  2. Dropout — להסיר אחוז מהנוירונים אקראית באימון (כיסינו ב-יחידה 4).

שכבת ה-augmentation (cell 37)

data_augmentation = keras.Sequential([
    layers.RandomFlip("horizontal"),   # היפוך אופקי (לא אנכי!)
    layers.RandomRotation(0.1),        # ±10% × 2π ≈ ±36° rotation
    layers.RandomZoom(0.2),            # ±20% zoom in/out
])
למה רק flip אופקי? חתול במצב שכיבה הפוכה — לא חתול טבעי. ולכן RandomFlip("horizontal_and_vertical") היה פוגע ב-validation accuracy. הבחירה של רוטציות סבירות (±36° ולא ±180°) ו-zoom סביר היא חלק מהאמנות.

איך נראית augmentation בפועל

9 augmentations של אותה תמונה
‎9 augmentations של אותה תמונה — flip, rotation, zoom. כל אחת היא דוגמת אימון "חדשה" שהרשת לא ראתה.

הרשת המעודכנת (cell 41) — שתי תוספות

inputs = keras.Input(shape=(180, 180, 3))
x = data_augmentation(inputs)         # ← חדש: augmentation בכניסה
x = layers.Rescaling(1./255)(x)
# ... אותם 5 בלוקי Conv2D + MaxPool כמו ב-§3 ...
x = layers.Flatten()(x)
x = layers.Dropout(0.5)(x)            # ← חדש: dropout לפני Dense
outputs = layers.Dense(1, activation="sigmoid")(x)
טריק קריטי: שכבות Augmentation ב-Keras פעילות רק ב-training mode. ב-evaluate()/predict() הן שקופות (transparent). לכן מוסיפים אותן כשכבה בתוך הגרף, לא כ-preprocessing.

התוצאה

הריצה התקצרה — נרשמו רק ‎30 מתוך 100 epochs (כנראה Colab התנתק או הופסק):

Epoch  1/100 - loss: 0.6979 - accuracy: 0.5040 - val_loss: 1.2371 - val_accuracy: 0.5000
Epoch 10/100 - loss: 0.5787 - accuracy: 0.6945 - val_loss: 0.5561 - val_accuracy: 0.7170
Epoch 20/100 - loss: 0.4850 - accuracy: 0.7785 - val_loss: 0.4685 - val_accuracy: 0.7760
Epoch 28/100 - loss: 0.4178 - accuracy: 0.8135 - val_loss: 0.4430 - val_accuracy: 0.8090  ← השיא
Epoch 30/100 - loss: 0.4061 - accuracy: 0.8130 - val_loss: 0.4653 - val_accuracy: 0.7990

Test accuracy: 0.811  (= 81.1%)
  • Validation accuracy בשיא: 80.9% (epoch 28)
  • Test accuracy: 81.1% — קפיצה מ-66.4% ל-81.1% = +14.7 נקודות
  • שימו לב — אין overfitting. val_loss ירד יחד עם training_loss (לפחות עד epoch 30). זה הסימן ש-augmentation+dropout עובדים.
הערה על "100 epochs": הספר ממליץ על 100 epochs אבל בריצה זו זה נקטע ב-30. הרבה פעמים Colab Free מתנתק כשיש דממה במחשב או הסשן עובר 12 שעות. פתרונות: (1) Colab Pro / Pro+. (2) להריץ עם ModelCheckpoint(save_best_only=True) כך שכל הזמן יש משקלות שמורים. (3) להריץ מקומית עם GPU.

5. גישה 3 — Transfer Learning עם VGG16

הרעיון: במקום ללמד CNN מ-0, ניקח רשת שכבר אומנה על ‎14 מיליון תמונות של ImageNet, ונשתמש בה כמחלץ פיצ'רים.

למה זה עובד?

  • ה-conv layers הראשונים של VGG16 לומדים פיצ'רים גנריים (קצוות, מרקמים, פינות) — שטובים לכל משימת ראייה.
  • השכבות העמוקות יותר לומדות פיצ'רים יותר ספציפיים, אבל גם הם רלוונטיים — ImageNet מכיל הרבה סוגי חתולים וכלבים בעצמו!

VGG16 — ארכיטקטורה

ארכיטקטורת VGG16 — 5 בלוקים + conv_base
‎5 בלוקי Conv (13 שכבות Conv2D + 5 MaxPool) = conv_base. ה-FC המקורי (1000 קטגוריות) מוחלף ב-classifier בינארי.

טעינת VGG16 ב-Keras

conv_base = keras.applications.vgg16.VGG16(
    weights="imagenet",       # טוען משקלות מאומנים
    include_top=False,        # ❌ ללא ה-FC head המקורי (1000 קטגוריות)
    input_shape=(180, 180, 3))
Keras מציע ב-keras.applications רשתות נוספות: Xception, ResNet50/101/152, MobileNet, EfficientNet, DenseNet — כולן אומנו על ImageNet וזמינות בקריאה אחת. VGG16 נבחר כאן בגלל הפשטות.

בדיקה — מבנה ה-conv_base

Model: "vgg16"
_________________________________________________________________
 Layer (type)                Output Shape              Param #
=================================================================
 input_3 (InputLayer)        [(None, 180, 180, 3)]     0
 block1_conv1 (Conv2D)       (None, 180, 180, 64)      1,792
 block1_conv2 (Conv2D)       (None, 180, 180, 64)      36,928
 block1_pool (MaxPooling2D)  (None, 90, 90, 64)        0
 block2_conv1 (Conv2D)       (None, 90, 90, 128)       73,856
 block2_conv2 (Conv2D)       (None, 90, 90, 128)       147,584
 block2_pool (MaxPooling2D)  (None, 45, 45, 128)       0
 ...
 block5_conv3 (Conv2D)       (None, 11, 11, 512)       2,359,808
 block5_pool (MaxPooling2D)  (None, 5, 5, 512)         0
=================================================================
Total params: 14,714,688
Trainable params: 14,714,688
Non-trainable params: 0

ה-output של ה-conv_base הוא (5, 5, 512) — זה ה"feature vector" שהוא הקלט ל-classifier שלנו.

6. שתי שיטות ל-Feature Extraction

אחרי שיש לנו conv_base מאומן, יש שתי שיטות שונות איך להשתמש בו:

Feature Extraction — 2 שיטות (מהירה / עם augmentation)
למעלה: השיטה המהירה (2s/epoch). למטה: השיטה עם augmentation (360s/epoch).

שיטה 1 — Fast Feature Extraction (cells 51, 54)

מעבירים את כל ה-dataset דרך conv_base פעם אחת, שומרים את ה-features ב-RAM, ומאמנים classifier קטן עליהם.

def get_features_and_labels(dataset):
    all_features = []
    all_labels = []
    for images, labels in dataset:
        preprocessed = keras.applications.vgg16.preprocess_input(images)
        features = conv_base.predict(preprocessed)
        all_features.append(features)
        all_labels.append(labels)
    return np.concatenate(all_features), np.concatenate(all_labels)

train_features, train_labels = get_features_and_labels(train_dataset)
# train_features.shape == (2000, 5, 5, 512)  ← 25.6M ערכים, ~100MB RAM

# מאמנים classifier קטן רק על features
inputs = keras.Input(shape=(5, 5, 512))
x = layers.Flatten()(inputs)
x = layers.Dense(256)(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(1, activation="sigmoid")(x)
  • זמן/epoch: ~0.5 שנייה ⚡⚡ (פלא בלי conv_base בכל epoch)
  • Val accuracy: 97.7-98.4% (השיא ב-epoch 14)
  • חיסרון: אי-אפשר לשלב augmentation (כי features חושבו פעם אחת ושמורים)

שיטה 2 — Feature Extraction עם Augmentation (cells 58, 63)

הופכים את conv_base לשכבה ברשת, מקפיאים אותה, ומשלבים augmentation בכניסה. conv_base רץ מחדש בכל epoch.

conv_base = keras.applications.vgg16.VGG16(
    weights="imagenet", include_top=False)
conv_base.trainable = False    # ❄ קפיאה — קריטי לפני compile!

inputs = keras.Input(shape=(180, 180, 3))
x = data_augmentation(inputs)
x = keras.applications.vgg16.preprocess_input(x)   # BGR + mean subtraction
x = conv_base(x)                                    # ← VGG16 כשכבה
x = layers.Flatten()(x)
x = layers.Dense(256)(x); x = layers.Dropout(0.5)(x)
outputs = layers.Dense(1, activation="sigmoid")(x)

בדיקת freeze — חשוב!

conv_base.trainable = True
print(len(conv_base.trainable_weights))   # → 26  (13 conv × 2 = kernel + bias)

conv_base.trainable = False
print(len(conv_base.trainable_weights))   # → 0   ✓ מקופא
הטעות הנפוצה ביותר ב-transfer learning: לשנות conv_base.trainable = False אחרי model.compile(). ה-compile מקבע את רשימת המשתנים הניתנים לאופטימיזציה — אם הפכת ל-trainable=False אחרי, ה-optimizer "עדיין רואה" את ה-weights כניתנים לעדכון. תמיד: trainable → compile.
  • זמן/epoch: ~10-20 שניות (פי 20-30 איטי!) 🐢
  • Test accuracy: 97.9% — קצת יותר טוב מהשיטה המהירה
  • עלות: ‎50 epochs × ~12s ≈ 10 דקות ב-Colab GPU

גרפי learning curves של Feature Extraction

קל לראות שהרשת מאוד מהר מגיעה לאזור ה-98% — בכמה epochs בלבד:

Accuracy curves Feature Extraction
Training vs Validation Accuracy
Loss curves Feature Extraction
Training vs Validation Loss
שימו לב לציר ה-loss: training loss קורס כמעט מיד ל-0, אבל validation loss נשאר ב-3-5. למרות זאת ה-validation accuracy גבוהה (97-98%)! זה מוכיח שהמודל overfit על הסתברויות — אבל ההחלטות הסופיות (cat vs dog) עדיין טובות. זו תופעה נפוצה במשימות סיווג עם sigmoid.

7. גישה 4 — Fine-Tuning

עד עכשיו ה-conv_base היה מקופא לחלוטין. בגישה הזו אנחנו "מפשירים" (unfreeze) את השכבות העליונות שלו ומאמנים אותן ביחד עם ה-classifier, ב-learning rate נמוך מאוד.

‎5 השלבים הקפדניים (PDF עמ' 47)

5 השלבים של Fine-Tuning
סדר השלבים חשוב — אסור לדלג! כל שלב מתבסס על הקודם.

הקוד של שלב 4-5 (cells 70, 72)

# שלב 4: unfreeze רק 4 שכבות עליונות
conv_base.trainable = True
for layer in conv_base.layers[:-4]:
    layer.trainable = False

# שלב 5: אימון משותף ב-lr נמוך
model.compile(loss="binary_crossentropy",
              optimizer=keras.optimizers.RMSprop(learning_rate=1e-5),  # ← פי 100 קטן!
              metrics=["accuracy"])

history = model.fit(train_dataset, epochs=30,
                    validation_data=validation_dataset, callbacks=callbacks)
למה learning_rate=1e-5 ולא 1e-3?
  • המשקלים כבר טובים: conv_base אומן על 1.4M תמונות ImageNet. שינוי גדול יהרוס את הייצוגים שלמד — catastrophic forgetting.
  • הדרך הנותרת קצרה: conv_base כבר יודע "חתול" ו"כלב"; צריך רק התאמה עדינה לדומיין.
  • למה רק 4 שכבות? השכבות התחתונות (Block 1-3) לומדות פיצ'רים גנריים (קצוות, מרקמים) שטובים לכל משימה. השכבות העליונות (Block 5) הן הספציפיות יותר — שם כדאי להתאים.

התוצאה

Epoch  1/30 - loss: 0.3296 - accuracy: 0.9910 - val_loss: 2.1599 - val_accuracy: 0.9760
Epoch  7/30 - loss: 0.0879 - accuracy: 0.9945 - val_loss: 1.2127 - val_accuracy: 0.9860  ← שיא
Epoch 14/30 - loss: 0.1573 - accuracy: 0.9945 - val_loss: 1.3727 - val_accuracy: 0.9840
Epoch 29/30 - loss: 0.0621 - accuracy: 0.9970 - val_loss: 1.0429 - val_accuracy: 0.9830
Epoch 30/30 - loss: 0.0774 - accuracy: 0.9975 - val_loss: 1.5774 - val_accuracy: 0.9780

Test accuracy: 0.977  (= 97.7%)
⚠ ממצא חשוב: הפעם fine-tuning פגע ב-0.2 נקודות (97.9% → 97.7%).
  • למה זה קרה? כש-feature extraction כבר במצב כמעט-אופטימלי (97.9%), יש פחות מקום לשיפור ויותר סיכון להרס. ה-‎4 שכבות העליונות שהתאמנו זזו במידה מסוימת מהמיקום האופטימלי שלהן.
  • זה לא שגיאה. זה הטבע הסטוכסטי של DL — לפעמים השיפור הוא +1%, לפעמים −0.3%. בריצה קודמת של אותו קוד זה היה +0.3%.
  • איך מנהלים את זה בפועל? משווים val_accuracy בין שני המודלים (feature extraction vs fine-tuned). אם fine-tuned לא טוב יותר — שומרים את ה-best של feature extraction.
  • הספר מצפה לשיפור. במקרים אחרים הוא אכן יחזור — תלוי במשימה, ב-lr, ובמספר השכבות שמשחררים.

8. סיכום תוצאות הריצה

גישהepochsזמן/epochVal accuracy שיאTest accuracyמה נדרש
From scratch 30/30 ~4-6s 74.6% (overfit חזק) 66.4% נתונים + ארכיטקטורה
+ Aug + Dropout 30/100 ⚠ ~3-5s 80.9% (epoch 28) 81.1% ‎augmentation pipeline
VGG16 fast feature 20/20 ~0.5s ⚡ 98.4% (epoch 14) (לא נמדד) VGG16 + classifier קטן
VGG16 + aug (frozen) 30/50 ~10-20s ~98.4% 97.9% ‎+ GPU טוב
Fine-tuning (4 top) 30/30 ~12s 98.6% (epoch 2,7) 97.7% ‎+ lr נמוך + סבלנות
תובנות מהמספרים בפועל:
  • הקפיצה האמיתית היא ‎+31.5 נקודות (66.4 → 97.9) ב-step מ-augmentation ל-feature extraction.
  • Augmentation לבד הוסיף ‎+14.7 נקודות (66.4 → 81.1) — חצי מהקפיצה הגדולה.
  • Fine-tuning הוריד ‎−0.2 נקודות הפעם (97.9 → 97.7) — מקרה שבו לא היה מה לשפר.
  • הזמן/epoch מאוד שונה מההרצה הקודמת (פי 30 מהיר יותר) — סימן שזה GPU חזק יותר.
פערים בין הריצות: אותו קוד הורץ פעמיים. בריצה הראשונה: 65/(~83)/97.5/97.8. בריצה השנייה: 66.4/81.1/97.9/97.7. סטוכסטיות של DL — אתחול אקראי, augmentation אקראי, ב-dataset קטן זה משפיע. המגמה היא העיקר, לא המספר המדויק.

9. שאלות חזרה למבחן

שאלה 1: למה לא משתמשים ב-include_top=True כשעושים transfer learning ל-Cats vs Dogs?

ה-classifier המקורי של VGG16 פולט 1000 קטגוריות של ImageNet (סוגי כלבים שונים, חתולים, כלי רכב, וכו'). למשימה שלנו צריך פלט בינארי. חוץ מזה — ה-Dense העליון מאומן ל-features מאוד ספציפיים של ImageNet, ולא ייצור הכללה טובה למשימה החדשה. בנוסף, ה-FC המקורי תופס 123M פרמטרים בנפרד — בזבזני.

שאלה 2: מתי דרושה preprocess_input במקום Rescaling(1./255)?

תמיד כשמשתמשים ברשת מאומנת שלא נורמלה ל-[0,1]. VGG16 אומן על תמונות BGR (לא RGB) עם חיסור ממוצע ImageNet לכל ערוץ. preprocess_input מבצע את הטיפול הנכון לכל מודל. שימוש לא נכון פוגע משמעותית באיכות ה-features.

שאלה 3: ב-CNN שראינו, Conv2D(32, 3) ראשון לוקח (180, 180, 3) ומחזיר (178, 178, 32). למה זה ולא (180, 180, 32)?

ברירת המחדל של Conv2D ב-Keras היא padding="valid" (ללא padding). עם kernel=3: output = 180 − 3 + 1 = 178. הערוצים 32 הם filters. סך פרמטרים: 32 × (3×3×3) + 32 = 896 — תואם את model.summary().

שאלה 4: מי שמשנה conv_base.trainable = False אחרי model.compile() מגלה שהמשקלים עדיין מתעדכנים. למה?

compile() מקבע את רשימת ה-trainable_variables לאופטימייזר. שינוי של trainable אחרי compile לא מתפשט אחורה. החוק: לקפא קודם, להעביר ל-compile אחר כך. בודקים עם len(model.trainable_weights) — אמור להיות 0 ל-conv_base מקופא.

שאלה 5: למה ב-fine-tuning משתמשים ב-learning_rate=1e-5 במקום ‎1e-3 הרגיל?

המשקלים של conv_base כבר במצב כמעט-אופטימלי לאחר אימון על ImageNet. עדכון חזק יהרוס את הייצוגים שלמדו (catastrophic forgetting). lr נמוך מאפשר התאמה עדינה לדומיין החדש. בנוסף, התקדמות התת-משימה (cats vs dogs) קטנה — הרשת כבר מזהה חתולים וכלבים בצורה מצוינת.

שאלה 6: בריצה הזו fine-tuning הוריד את ה-accuracy מ-97.9% ל-97.7%. האם זה אומר ש-fine-tuning גרוע?

לא! זה טבע סטוכסטי של DL:

  • בריצה הקודמת של אותו קוד: fine-tuning שיפר ‎+0.3 (97.5 → 97.8). הפעם: ‎−0.2 (97.9 → 97.7).
  • הסיבה: כש-feature extraction כבר 97.9%, יש מעט מקום לשיפור. הסיכון של הרס פיצ'רים שכבר טובים גדל.
  • אסטרטגיה נכונה: תמיד שומרים את שני המודלים (FE ו-FT) עם ModelCheckpoint, ומשתמשים בזה שמראה val_accuracy גבוה יותר.
  • מתי fine-tuning תמיד עוזר: כש-feature extraction באזור ה-85-95%. כש-FE כבר 97-98%, ה-margin להוסיף קטן ו-fine-tuning יכול ללכת לכל כיוון.

10. תובנות ושגיאות נפוצות

  1. ‎`train_features.shape = (2000, 5, 5, 512)` — זה הפלט של block5_pool של VGG16 על קלט 180×180. רק 2000 × 12,800 = 25.6M ערכים → ‎~100MB ב-RAM, אז אפשר לאחסן הכל ולקבל אימון של חצי שנייה לאפוק.
  2. למה Conv2D(256, 3) מופיע פעמיים בסוף הרשת מ-scratch? זו בחירה ארכיטקטונית נפוצה — להוסיף "עוד עומק" ברזולוציה הסופית הנמוכה (9×9) לפני ה-Flatten. זה לא חוק — אפשר גם רק פעם אחת.
  3. VGG16 הוא איטי. ‎138M פרמטרים, מתוכם ‎14.7M ב-conv_base. אם מתפשרים על דיוק קטן, MobileNet/EfficientNet ייתנו transfer learning הרבה יותר זול לאימון ופי 10 מהיר ב-inference.
  4. למה loss עולה אבל accuracy מצוין? תופעה נפוצה ב-binary classification עם sigmoid: המודל אומר "100% חתול" על תמונה שהיא באמת חתול — אבל כשהוא טועה, הוא טועה בביטחון ("100% כלב" בעוד זה חתול), וזה דוחף את ה-loss הגבוה. ה-classification eraset (cat/dog) עדיין נכון. בריצה הזו: val_accuracy = 98.3% אבל val_loss = 1.04 — תאם בדיוק את התופעה.
  5. ‎augmentation שולחת רק ב-training mode. שאלה נפוצה — האם זה רץ גם ב-evaluate()? לא. השכבות RandomFlip וכו' פשוט מעבירות את הקלט כפי שהוא במצב inference (training=False).
  6. Fine-tuning יכול גם להזיק. בריצה הזו זה הוריד את ה-test accuracy ב-0.2 נקודות. כשה-baseline (feature extraction) כבר 97.9%, אין הרבה מקום לשפר ויש סיכון להרס. תמיד שומרים גם את ה-FE וגם את ה-FT, ומשתמשים בטוב ביניהם.
  7. סטוכסטיות בריצות: אותו קוד הורץ פעמיים וקיבל מספרים שונים — 65→97.8 בפעם הראשונה, 66.4→97.7 בפעם השנייה. אותו קוד, אותם נתונים, אותם hyperparameters — אבל אתחול אקראי + augmentation אקראי יוצרים שונות של ‎±1-2 נקודות אחוז. כדי לקבל מספר אמין צריך 5+ ריצות ולקחת ממוצע.
  8. אסטרטגיה למבחן/פרויקט: תמיד תתחילו עם feature extraction מהיר (גישה 3א) — זה ייתן baseline טוב תוך דקה. רק אם צריך עוד דיוק — עוברים ל-feature extraction עם augmentation, ורק אז ל-fine-tuning (זוכרים שזה לא תמיד עוזר).
גשר להרצאות הבאות: בהרצאה 9 נעבור ל-NLP — שם נראה שוב transfer learning, אבל עם Word2Vec / GloVe כ-"ImageNet של מילים". בהרצאה 10 (Transformers) נראה את הגרסה המתקדמת — BERT, GPT — שהם conv_base ל-text. אותם עקרונות: freeze → train head → optionally fine-tune.

מילון מושגים — חי ומתעדכן

המילון מבוסס על רשימת 45 המושגים שאוחדה במטלה 2 מתוך 11 הגשות סטודנטים, ב-8 קטגוריות. זוהי "מפת העוגן" של המבחן.

לכל מושג יש שם באנגלית, תרגום מקובל לעברית, הסבר קצר ולחיצה לפתיחת הסבר מורחב עם דוגמה / המחשה. כל קטגוריה ניתן לסנן באמצעות הכפתורים, ואפשר לחפש חופשי בכל המושגים.

מתעדכן: נוסיף מושגים בכל הרצאה חדשה — מתוך החומר ולא מבחוץ.

הכל רשת — מבנה ופעולה פונקציות הפעלה למידה ואופטימיזציה ארכיטקטורות DL NLP Computer Vision אלגוריתמי ML כלים וספריות
Perceptron — יחידת בסיס רשת — מבנה ופעולה
אבן הבניין הבסיסית של רשת נוירונים — נוירון מלאכותי בודד שמחשב סכום משוקלל ומחזיר 0 או 1.
הגדרה מלאה: פרספטרון מקבל וקטור קלט x, מכפיל ב-weights W, מוסיף bias b, ומפעיל פונקציית סף:
ŷ = 1 if Wx + b ≥ 0 else 0
היסטוריה: הוצג ע"י Frank Rosenblatt ב-1958 כמודל למוח ביולוגי. דוגמה: Wx+b מייצג את הסיכוי שסטודנט יתקבל לאוניברסיטה לפי ציון בגרות ומבחן. מגבלה: פרספטרון יחיד מסוגל ללמוד רק קווים ישרים — לא יכול לפתור XOR. הפתרון: שכבות נסתרות.
לחצו לפתיחה/סגירה של הסבר מורחב
Hidden Layers — שכבות נסתרות רשת — מבנה ופעולה
השכבות שבין הקלט לפלט, שבהן הרשת לומדת לזהות דפוסים מורכבים בנתונים.
תפקיד: כל שכבה לוקחת את פלט השכבה הקודמת ומפעילה עליו טרנספורמציה לא-לינארית (Wx+b ואחריו אקטיבציה). היא לא רואה ישירות את הקלט הגולמי ולא ישירות את הפלט הסופי — ולכן "נסתרת". למה צריך אותן: ללא שכבות נסתרות, הרשת היא רגרסיה לוגיסטית פשוטה. עם שכבה אחת — ניתן לקרב כל פונקציה רציפה (Universal Approximation). עם הרבה שכבות — לומדים ייצוגים היררכיים (קצוות → צורות → אובייקטים). פרקטיקה: מספר השכבות (depth) ומספר היחידות בכל שכבה (width) הם hyperparameters שמכוונים בניסוי.
לחצו לפתיחה/סגירה של הסבר מורחב
Multi-layer Network — רשת רב-שכבתית רשת — מבנה ופעולה
רשת בעלת מספר שכבות המאפשרת למידה של קשרים מורכבים ולא-ליניאריים.
הקשר: "Multi-layer" = יותר משכבה אחת; "Deep" = הרבה שכבות (>2-3 בדרך כלל). DL התחיל כאשר הצליחו להתגבר על vanishing gradients ולאמן רשתות עם 5+, 50+, 100+ שכבות. אינטואיציה: כל שכבה עוסקת ברמת הפשטה גבוהה יותר. ברשת לזיהוי תמונות: שכבות ראשונות מזהות קצוות, אמצעיות מזהות חלקי גוף (אוזניים, עיניים), אחרונות מזהות אובייקטים שלמים.
לחצו לפתיחה/סגירה
MLP — Multi-Layer Perceptron — רשת נוירונים מסורתית עם שכבות צפופות רשת — מבנה ופעולה
המונח הסטנדרטי בתעשייה ל-feed-forward NN: רצף של שכבות Dense (Fully-Connected) עם פונקציות אקטיבציה לא-לינאריות. כל המודלים שראינו בהרצאות 3-6 הם MLPs.
המבנה הקלאסי:
Input → Dense + ReLU → Dense + ReLU → ... → Dense + (Sigmoid/Softmax/None)
תכונות מגדירות:
  • Fully-Connected: כל נוירון בשכבה k מחובר לכל נוירון בשכבה k+1.
  • Feed-Forward: אין מעגלים — המידע זורם רק בכיוון אחד מהקלט לפלט.
  • אקטיבציה לא-לינארית: ReLU/tanh/sigmoid בכל שכבה נסתרת — בלעדיהן הרצף הופך לפרספטרון בודד.
מתי הוא הבחירה הנכונה: נתונים טבלאיים (rows × features), קלט בגודל קבוע, אין מבנה מרחבי או זמני. מתי הוא לא מתאים:
  • תמונות — מאבד את המבנה הדו-מימדי, יש יותר מדי פרמטרים. הפתרון: CNN.
  • רצפים (טקסט, סדרות זמן) — לא מנצל את הסדר. הפתרון: RNN, Transformer.
השמות הנרדפים שתפגוש: Multi-Layer Perceptron · Multi-layer Network · Fully-Connected Network (FCN) · Dense Network · Feed-Forward Neural Network (FFNN). כולם מתארים את אותה ארכיטקטורה.
לחצו לפתיחה/סגירה
Fully Connected Layer (FC) — שכבה צפופה / מקושרת מלאה רשת — מבנה ופעולה
שכבה שבה כל נוירון מחובר לכל הנוירונים בשכבה הקודמת. זה השם הרשמי של שכבת `Dense` ב-Keras והבסיס של MLP.
הפעולה המתמטית:
y = Wx + b, כאשר W הוא מטריצה מלאה (n_out × n_in)
חישוב פרמטרים: n_in × n_out + n_out (bias). למשל קלט 784 → 512: 401,408 פרמטרים. מתי משתמשים:
  • נתונים טבלאיים (אין מבנה מרחבי).
  • שכבת פלט סופית — אחרי Flatten בקצה CNN, או על vectors קטנים.
  • בעיות סיווג / רגרסיה פשוטות.
מתי לא: תמונות, רצפים — שם sparsely connected (CNN/RNN) חוסך פי עשרות בפרמטרים. שמות נרדפים: Dense Layer · FC Layer · Linear Layer (ב-PyTorch).
לחצו לפתיחה/סגירה
Sparsely Connected — מקושר באופן דליל / מקומי רשת — מבנה ופעולה
המנוגד של Fully Connected — כל נוירון בפלט מחובר רק לתת-קבוצה קטנה של הקלט (חלון מקומי). הבסיס של CNN ושל מודלים יעילים מודרניים.
למה "Sparsely": אם נכתוב את מטריצת החיבורים W, רובה תהיה אפסים — ערכים בלתי-אפסיים רק במקומות של החלון המקומי. זוהי מטריצה דלילה (sparse). דוגמאות בפועל:
  • CNN — Conv2D: כל נוירון בפלט רואה רק חלון 3×3 בקלט. + Weight Sharing → מינימום פרמטרים.
  • RNN: בכל צעד זמן, הנוירון רואה רק את הקלט הנוכחי + ה-state הקודם.
  • Sparse Transformer / Linformer: מגבילים את ה-attention לחלקים מסוימים מהרצף.
למה זה עובד טוב לתמונות:
  • פיקסלים קרובים קשורים סמנטית (חלקי אותו אובייקט).
  • פיקסלים מרוחקים לרוב לא קשורים ישירות — אז לחבר אותם מבזבז פרמטרים.
  • שכבה Sparsely connected חוסכת פי 100-50,000 בפרמטרים (תלוי ב-stride וב-weight sharing).
הקבלה: Sparsely connected = "תתמקדי במה שקרוב, התעלמי ממה שרחוק". Fully connected = "כל פיקסל יכול להשפיע על כל פלט".
לחצו לפתיחה/סגירה
Network Architecture — ארכיטקטורת הרשת רשת — מבנה ופעולה
המבנה הכולל של הרשת: מספר השכבות, סוג השכבות (Dense / Conv / Recurrent), כמות הנוירונים בכל שכבה, וצורת החיבורים ביניהן.
החלטות מרכזיות:
  1. מספר שכבות נסתרות.
  2. גודל כל שכבה (16, 64, 256...).
  3. אקטיבציות (ReLU בנסתרות, sigmoid/softmax/linear בפלט).
  4. חיבורים מיוחדים: skip connections (ResNet), branches (Multi-task), recurrent (RNN).
גישת בחירה: אין פתרון אנליטי. מתחילים בארכיטקטורה פשוטה, מגדילים עד overfit, ואז מרסנים. מסתמכים על ארכיטקטורות ידועות לבעיות הנפוצות (CNN לתמונות, Transformer לטקסט).
לחצו לפתיחה/סגירה
Dendrites / Axon Terminals — מבנה נוירון ביולוגי רשת — מבנה ופעולה
ההשראה הביולוגית לרשת: דנדריטים קולטים אותות (קלטים), קצות האקסון מעבירים את הפלט לתאים הבאים.
אנלוגיה לפרספטרון:
  • דנדריטים = קלטי x.
  • חוזק חיבור סינפטי = משקל w.
  • גוף התא = סכום משוקלל + הטיה.
  • אקסון = פלט (אם עובר סף, "יורה").
  • אקטיבציה = החלטה אם להעביר אות הלאה.
חשוב: זוהי השראה ולא מודל מדויק של המוח. הרשת המלאכותית מפושטת מאוד ביחס לרשת ביולוגית אמיתית.
לחצו לפתיחה/סגירה
Initialize Weights — אתחול משקולות רשת — מבנה ופעולה
קביעת ערכי המשקלות בתחילת האימון; אתחול נכון עוזר לרשת ללמוד מהר ויציב יותר.
למה לא אפס? אם כל המשקלות שווים, כל הנוירונים ילמדו את אותו הדבר ויאבדו את היכולת ללמוד תכונות שונות (Symmetry Breaking). שיטות נפוצות:
  • Random uniform/normal — מספרים אקראיים קטנים (לדוגמה N(0, 0.01)).
  • Xavier / Glorot — סטיית תקן תלויה בגודל השכבה: √(2/(fan_in+fan_out)). מתאים ל-tanh / sigmoid.
  • He initialization — סטיית תקן √(2/fan_in). מתאים ל-ReLU. הסטנדרט כיום.
פרקטיקה: Keras בוחר אתחול ברירת מחדל (Glorot) — לרוב לא צריך לחשוב על זה.
לחצו לפתיחה/סגירה
Dropout — שכבת השמטה רשת — מבנה ופעולה
שיטה למניעת התאמת יתר (Overfitting) על ידי "כיבוי" אקראי של נוירונים בזמן האימון.
איך זה עובד: בכל epoch, לכל נוירון בשכבת dropout, בהסתברות p (טיפוסית 0.2-0.5) — הפלט שלו נכפל באפס. בזמן Inference כל הנוירונים פעילים, אבל הפלט נכפל ב-(1-p) לפיצוי. למה זה עובד: אקוויוולנטי לאימון אנסמבל של תת-רשתות שונות. הרשת לא יכולה להסתמך על נוירון יחיד — הידע מתפזר. אנלוגיה: אימון בחדר כושר — בכל פעם מתמקדים בקבוצת שרירים אחרת, הגוף כולו מתחזק. פרקטיקה ב-Keras: model.add(Dropout(0.3)) אחרי שכבת Dense.
לחצו לפתיחה/סגירה
Feature Vector — וקטור מאפיינים רשת — מבנה ופעולה
ייצוג מספרי של קלט (תמונה, מילה, מסמך) המאפשר למודל להבין דמיון בין נתונים.
דוגמאות:
  • תמונה 28x28 → וקטור 784 מספרים (אחרי flatten).
  • מילה → embedding בגודל 50-300 (Word2Vec, GloVe).
  • מסמך → ממוצע של embeddings, או Doc2Vec, או embedding של [CLS] ב-BERT.
  • תמונה אחרי CNN → "feature vector" ב-end-of-CNN באורך 512 או 2048.
שימוש: מאפשר השוואת דמיון (Cosine Similarity), חיפוש קרובים, clustering, או הזנה לסיווג.
לחצו לפתיחה/סגירה
Tensor — מערך מספרים בכל כמות מימדים רשת — מבנה ופעולה
מבנה הנתונים המרכזי של DL — כל קלט, ביניים ופלט של רשת הוא Tensor. הכללה של סקלר → וקטור → מטריצה לכל מימד.
היררכיה:
  • 0D — Scalar: מספר בודד.
  • 1D — Vector: וקטור (כמו רשימת תכונות).
  • 2D — Matrix: טבלה (samples × features) — הנפוץ ביותר ב-NN טבלאי.
  • 3D: רצפים — (batch × time × features).
  • 4D: תמונות — (batch × height × width × channels).
  • 5D: וידאו — (batch × frames × height × width × channels).
תכונות: .ndim (מספר מימדים), .shape (אורך כל מימד), .dtype (טיפוס — בדרך כלל float32). הקשר: בפועל Tensor הוא NumPy array בגרסה של TensorFlow / PyTorch. כל פעולה שעושים על מטריצה ב-NumPy מומשת גם על Tensors, ובנוסף יודעת לרוץ על GPU.
לחצו לפתיחה/סגירה
Embedding — ייצוג צפוף וסמנטי NLP
המרת ישות בדידה (מילה, מוצר, משתמש) לוקטור צפוף של מספרים אמיתיים, כך שישויות דומות מקבלות וקטורים קרובים במרחב.
הבעיה: איך מציגים מילה ל-NN? One-hot של 10,000 מימדים בזבזני וחסר משמעות. הפתרון: embedding של 50-300 מימדים, צפוף ועם משמעות. וקטורים נלמדים כך שמילים דומות (cat / kitten) קרובות גאומטרית. הדוגמה הקלאסית: vec("King") – vec("Man") + vec("Woman") ≈ vec("Queen") — היחסים הסמנטיים מתורגמים ליחסים גאומטריים. שימוש: NLP, מערכות המלצה (Movie/User embeddings), אפילו בעיבוד תמונה (face embeddings).
לחצו לפתיחה/סגירה
Embedding Layer — שכבת ה-Embedding ב-Keras NLP
שכבה ב-Keras שלומדת embeddings תוך כדי האימון — בעצם טבלת lookup בגודל (vocabulary × dim) שמתעדכנת דרך Backpropagation.
סינטקס:
layers.Embedding(input_dim=10000, output_dim=64)
  • input_dim: גודל המילון.
  • output_dim: מימד הוקטור לכל מילה (טיפוסי 50-300).
  • פנימית: מטריצה בצורה (10000, 64) = 640,000 פרמטרים שנלמדים.
קלט/פלט:
  • קלט: tensor של אינדקסים בצורה (batch, seq_len).
  • פלט: tensor של embeddings בצורה (batch, seq_len, dim).
חוזק: אפשר לאתחל ב-embeddings מאומנים מראש (Word2Vec / GloVe / FastText) ולהמשיך לאמן או להקפיא.
לחצו לפתיחה/סגירה
Activation Function — פונקציית הפעלה פונקציות הפעלה
פונקציה המוסיפה אי-לינאריות לרשת, ומאפשרת לה ללמוד דפוסים מורכבים.
למה צריך אותה: בלי אקטיבציה לא-לינארית, רצף שכבות שווה לרגרסיה לינארית בודדת. האקטיבציה היא ה"שובר" של הלינאריות. אופציות נפוצות: Step (היסטורית, לא בשימוש), Sigmoid, tanh, ReLU, Leaky ReLU, ELU, GELU, Softmax (בעיקר בפלט). איך לבחור:
  • שכבות נסתרות → ברירת מחדל ReLU.
  • פלט סיווג בינארי → sigmoid.
  • פלט סיווג רב-מחלקתי → softmax.
  • פלט רגרסיה → ללא אקטיבציה (linear).
לחצו לפתיחה/סגירה
Step Function — פונקציית סף פונקציות הפעלה
פונקציית אקטיבציה בסיסית: 0 או 1 לפי סימן הקלט. פשוטה אך לא גזירה — לא מתאימה ל-Gradient Descent.
step(z) = 1 if z ≥ 0 else 0
שימוש היסטורי: הפרספטרון המקורי של Rosenblatt (1958). חיסרון מכריע: הנגזרת היא 0 בכל מקום (פרט לנקודה בודדת שבה היא לא מוגדרת). זה הופך את Backpropagation לבלתי אפשרי. לכן בעידן ה-DL מחליפים אותה ב-Sigmoid (גזירה) או ReLU.
לחצו לפתיחה/סגירה
Sigmoid Function — פונקציה סיגמואידית פונקציות הפעלה
פונקציה הממירה ערכים לטווח שבין 0 ל-1; נפוצה בסיווג בינארי בשכבת הפלט.
σ(z) = 1 / (1 + e^(–z))
תכונות:
  • חלקה (גזירה בכל מקום).
  • טווח (0,1) — ניתן לפרשנות כהסתברות.
  • נגזרת אלגנטית: σ′(z) = σ(z)·(1 – σ(z)).
חיסרון: "Vanishing Gradient" — בקצוות הנגזרת מתקרבת ל-0, ובשכבות עמוקות הגרדיאנטים נעלמים. לכן בשכבות נסתרות מעדיפים ReLU. נשארת כברירת מחדל בשכבת פלט בסיווג בינארי.
לחצו לפתיחה/סגירה
Hyperbolic Tangent (tanh) — פונקציה היפרבולית פונקציות הפעלה
פונקציית אקטיבציה המנרמלת ערכים לטווח שבין -1 ל-1. בעלת נגזרות גדולות יותר מ-sigmoid.
tanh(z) = (eᶻ – e⁻ᶻ) / (eᶻ + e⁻ᶻ)
יחס ל-sigmoid: tanh(z) = 2·σ(2z) – 1. כלומר זו סיגמואיד מותאמת לטווח [-1, 1] עם מרכז ב-0. יתרון על-פני sigmoid: מרכז ב-0 ⇒ הגרדיאנטים מאוזנים יותר (לא רק חיוביים), מתכנס מהר יותר. חיסרון: עדיין סובלת מ-vanishing gradient בקצוות. שימוש כיום: בעיקר ב-RNNים מסוימים (LSTM, GRU). ברשתות feed-forward — ReLU מועדף.
לחצו לפתיחה/סגירה
ReLU (Rectified Linear Unit) — פונקציית רליו פונקציות הפעלה
פונקציית האקטיבציה הנפוצה ביותר ברשתות עמוקות; מחזירה את הקלט אם הוא חיובי ו-0 אחרת.
ReLU(z) = max(0, z)
יתרונות:
  • חישוב מאוד מהיר (max פשוט).
  • הנגזרת = 1 לחיוביים — אין vanishing gradient.
  • יוצרת sparsity (חצי מהפלטים אפס) — יעיל זיכרון וחישוב.
חיסרון: "Dying ReLU" — אם הקלטים שליליים בעקביות, הנגזרת תמיד 0 והנוירון לא לומד. פתרונות:
  • Leaky ReLU: max(0.01z, z) — שיפוע קטן לשליליים.
  • PReLU: השיפוע השלילי הוא פרמטר שנלמד.
  • ELU: e^z – 1 לשליליים — חלקה.
פרקטיקה: ReLU = ברירת המחדל בשכבות נסתרות בכל מודל DL מודרני.
לחצו לפתיחה/סגירה
Softmax — פונקציית סופטמקס פונקציות הפעלה
הופכת וקטור של ציונים לחלוקת הסתברות (סכום = 1). מתאימה לסיווג רב-מחלקתי בשכבת הפלט.
P(class i) = e^(zᵢ) / Σⱼ e^(zⱼ)
למה אקספוננט: מבטיח ערכים חיוביים גם ל-z שלילי, ומגביר הבדלים — ציון מעט גבוה יותר → הסתברות הרבה יותר גבוהה. קשר ל-sigmoid: Softmax עם 2 מחלקות = sigmoid. שימוש: תמיד בשכבת הפלט של סיווג רב-מחלקתי, יחד עם categorical_crossentropy.
לחצו לפתיחה/סגירה
FeedForward — הזנה קדמית למידה ואופטימיזציה
תהליך זרימת הנתונים מהקלט דרך השכבות ועד לקבלת התחזית בפלט.
הצעדים:
  1. קלט x נכנס לשכבה הראשונה.
  2. בכל שכבה: z = Wx + b → a = activation(z).
  3. הפלט של כל שכבה הוא הקלט לשכבה הבאה.
  4. בשכבה האחרונה — מקבלים את התחזית ŷ.
חשוב: Feedforward הוא חישוב בלבד — אין למידה. הלמידה מתבצעת ב-Backpropagation שאחריו.
לחצו לפתיחה/סגירה
Back-propagation — הפצה לאחור למידה ואופטימיזציה
האלגוריתם המחשב את הטעות של כל משקולת מהסוף להתחלה כדי לעדכן אותן ולשפר את המודל.
שלבים:
  1. חישוב Loss בין ŷ ל-y.
  2. חישוב ∂Loss/∂ŷ.
  3. שכבה אחר שכבה (אחורה!): שימוש בכלל השרשרת לחישוב ∂Loss/∂Wₖ עבור כל W בשכבה.
  4. עדכון: W ← W – α · ∂Loss/∂W.
למה "לאחור": הגרדיאנט בשכבה האחרונה פשוט; לחישוב הגרדיאנט בשכבה הקודמת צריך את הגרדיאנט שכבר חישבנו אחריה. לכן עוברים מהפלט להתחלה. קסם ה-Frameworks: אנחנו לא מחשבים את זה ידנית — TF / PyTorch עושים זאת אוטומטית (Automatic Differentiation).
לחצו לפתיחה/סגירה
Gradient — גרדיאנט למידה ואופטימיזציה
הנגזרת של פונקציית ההפסד; מראה את הכיוון והעוצמה לשינוי המשקולות לצמצום הטעות.
פורמלית: וקטור של נגזרות חלקיות:
∇E = (∂E/∂w₁, ∂E/∂w₂, ..., ∂E/∂wₙ)
אינטואיציה: הגרדיאנט מצביע לכיוון העלייה התלולה ביותר. לכן ב-Gradient Descent נעים בכיוון ההפוך — מינוס הגרדיאנט. תכונה חשובה: אורך וקטור הגרדיאנט = שיפוע השטח. בקצוות (חיוביים/שליליים מאוד) של sigmoid — האורך זעיר → vanishing gradient.
לחצו לפתיחה/סגירה
Loss Function — פונקציית הפסד למידה ואופטימיזציה
מדד המחשב כמה התחזית רחוקה מהמציאות; המטרה באימון היא למזער ערך זה.
מקובלות:
  • Binary Cross-Entropy — סיווג בינארי (sigmoid).
  • Categorical Cross-Entropy — סיווג רב-מחלקתי (softmax + one-hot).
  • Sparse Categorical Cross-Entropy — כמו הקודם אך תיוג כ-int.
  • MSE (Mean Squared Error) — רגרסיה.
  • MAE (Mean Absolute Error) — רגרסיה עמידה לחריגים.
הבדל מ-Metrics: Loss משמש לאופטימיזציה (חייב להיות גזיר); Metrics (כמו accuracy) הם רק לדיווח.
לחצו לפתיחה/סגירה
Chain Rule — כלל השרשרת למידה ואופטימיזציה
כלל מתמטי המאפשר לחשב את השפעת המשקולות המוקדמות על הטעות דרך שרשרת נגזרות.
הכלל: אם h(x) = f(g(x)), אז:
dh/dx = (df/dg) · (dg/dx)
בהקשר של רשת: השגיאה E היא פונקציה של ŷ, ŷ של h, h של h₁, h₁ של w. אז:
∂E/∂w = ∂E/∂ŷ · ∂ŷ/∂h · ∂h/∂h₁ · ∂h₁/∂w
חשיבות: זהו הכלי המתמטי שעליו נשען Backpropagation, וכל DL בכלל.
לחצו לפתיחה/סגירה
Optimizer — אופטימייזר למידה ואופטימיזציה
האלגוריתם המעדכן את המשקולות כדי להגיע למינימום של פונקציית ההפסד.
אופציות נפוצות:
  • SGD — Stochastic Gradient Descent בסיסי. דורש כיוון ידני של LR.
  • SGD + Momentum — צובר מהירות בכיוונים עקביים.
  • RMSprop — מתאים LR לכל פרמטר. הסטנדרט בקורס.
  • Adam — שילוב Momentum + RMSprop. הברירת מחדל הפופולרית בעולם.
  • AdamW — Adam עם weight decay נכון. שכיח ב-Transformers.
פרקטיקה: אם בספק — Adam עם LR=0.001. אם המודל לא מתכנס — לנסות RMSprop או SGD+Momentum.
לחצו לפתיחה/סגירה
Stochastic Gradient Descent (SGD) — ירידת גרדיאנט סטוכסטית למידה ואופטימיזציה
עדכון משקולות המשתמש בדגימה אקראית (או mini-batch) בכל שלב, מה שמאיץ את הלמידה ומאפשר שימוש בנתונים גדולים.
הבעיה עם Batch GD: לחשב גרדיאנט על מיליוני דגימות בכל epoch — איטי וזולל זיכרון. SGD הקיצוני: עדכון אחרי כל דגימה בודדת. רועש אך מהיר. Mini-batch (הסטנדרט): עדכון על batch של 32-256 דגימות. מאזן בין יציבות למהירות, ומנצל GPU. יתרון נסתר: הרעש של SGD עוזר לצאת ממינימה לוקליות.
לחצו לפתיחה/סגירה
Epoch — אפוק / סבב אימון מלא למידה ואופטימיזציה
מעבר אחד מלא על כל דגימות סט האימון. אחרי epoch אחד המודל "ראה" את כל הנתונים פעם אחת ועדכן את המשקלות לפי כולם.
למה צריך כמה epochs? מעבר אחד לרוב לא מספיק כדי שהמשקלות יתכנסו. בכל epoch הרשת רואה שוב את אותם נתונים, אבל עם משקלות מעודכנים — וכך משפרת את עצמה הדרגתית. איך מחליטים על מספר ה-epochs?
  • מעט מדי (underfit): המודל לא הספיק ללמוד את הקשר.
  • הרבה מדי (overfit): המודל "שינן" את האימון אך לא מכליל.
  • הנכון: עוקבים אחר val_loss; עוצרים כשהוא מתחיל לעלות (Early Stopping).
חישוב: בכל epoch מתבצעים N / batch_size עדכוני משקלות, כאשר N = מספר דגימות האימון. למשל 60,000 דגימות / batch_size=128 = 469 עדכונים לכל epoch. ערך טיפוסי: 10-100 epochs, תלוי בגודל המודל ובכמות הנתונים.
לחצו לפתיחה/סגירה
Batch — באצ' / קבוצת דגימות למידה ואופטימיזציה
קבוצת דגימות שעוברות יחד ב-Forward + Backward pass של הרשת בצעד אימון אחד. זוהי היחידה האטומית של עדכון משקלות.
הזרימה בכל batch:
  1. בוחרים batch של N דגימות.
  2. Forward pass — חישוב חיזויים לכל הדגימות בו זמנית (כפל מטריצות מקבילי).
  3. חישוב הפסד ממוצע על ה-batch.
  4. Backward pass — חישוב גרדיאנטים.
  5. עדכון משקלות אחד על בסיס הגרדיאנט הממוצע של ה-batch כולו.
למה לעבוד ב-batches ולא דגימה-דגימה?
  • יעילות חישוב: GPU מקבל את כל ה-batch במקביל — מהיר פי 100 מ-loop רציף.
  • יציבות: ממוצע על batch הוא הערכה פחות רועשת של הגרדיאנט מאשר דגימה בודדת.
  • זיכרון: לא צריך לטעון את כל הנתונים בבת אחת — רק batch אחד בכל פעם.
שלוש האסטרטגיות: Batch GD (כל הנתונים יחד) · Mini-batch GD (32-256 דגימות, הסטנדרט) · SGD (דגימה אחת בלבד).
לחצו לפתיחה/סגירה
Batch Size — גודל ה-batch למידה ואופטימיזציה
היפרפרמטר שקובע כמה דגימות יש בכל batch. משפיע על מהירות, יציבות, צריכת זיכרון, ואיכות הגרדיאנט.
ערכים טיפוסיים: חזקות של 2 — 16, 32, 64, 128, 256, 512. הגודל הסטנדרטי בקורס: 128 או 512. טבלת ההשפעות:
קריטריוןbatch קטן (16-32)batch גדול (256-512)
זיכרון GPUנמוךגבוה
גרדיאנטרועשחלק ומדויק
יציאה ממינימה לוקליותטוב יותר (הרעש עוזר)פחות טוב
עדכונים לכל epochהרבהמעט
זמן רץ של epochאיטי יותרמהיר יותר (ניצול GPU)
איכות ההכללהלעיתים טובה יותרלעיתים נוטה ל-overfit
כלל אצבע:
  • אם יש GPU חזק — גדל את ה-batch_size למקסימום שהזיכרון מאפשר (512 או 1024).
  • אם המודל לא מתכנס — נסה batch_size קטן יותר (32-64).
  • אם משנים את batch_size פי X — רצוי לכוון את ה-Learning Rate באותו יחס (Linear Scaling Rule).
איפה זה מופיע בקוד:
model.fit(x_train, y_train, epochs=20, batch_size=128)
לחצו לפתיחה/סגירה
Mini-batches — מיני-באצ'ים למידה ואופטימיזציה
חלוקת הנתונים לקבוצות קטנות; מאזנת בין מהירות חישוב ליציבות בעדכון המשקולות.
גודל טיפוסי: 32, 64, 128, 256. שיקולים:
  • גודל קטן = רעש גבוה יותר, יציאה ממינימה לוקליות, יותר עדכונים ב-epoch.
  • גודל גדול = יציבות, ניצול GPU טוב יותר, פחות עדכונים אך כל אחד מדויק.
כלל אצבע: 32 לרשתות קטנות, 128-256 לרשתות גדולות עם GPU טוב.
לחצו לפתיחה/סגירה
Learning Rate (α) — קצב למידה למידה ואופטימיזציה
קצב הלמידה הקובע את גודל הצעד בכל עדכון משקולות; קריטי להצלחת האימון ולמהירותו.
בנוסחה: w ← w – α · ∂E/∂w. א גדול = צעד גדול. סימפטומים:
  • α גדול מדי: Loss מתפזר או "מנתר" סביב המינימום.
  • α קטן מדי: אימון איטי, מסכן בהיתקעות במינימום מקומי.
  • α נכון: Loss יורד בעקביות.
ערכים סטנדרטיים: Adam=0.001, SGD=0.01-0.1. שיפור: Learning Rate Decay — להקטין את α עם הזמן.
לחצו לפתיחה/סגירה
Learning Rate Decay — קצב למידה דועך למידה ואופטימיזציה
הפחתה הדרגתית של קצב הלמידה בזמן האימון כדי לאפשר למודל להתייצב על הפתרון המדויק.
אסטרטגיות:
  • Step decay — חלוקה ב-2 כל 10 epochs.
  • Exponential decay — α(t) = α₀ · γᵗ.
  • Cosine annealing — נע בקצב גל קוסינוס.
  • Warmup + decay — מתחיל קטן, עולה, ואז יורד (סטנדרט ב-Transformers).
  • ReduceLROnPlateau — Keras callback שמקטין α כש-validation לא משתפר.
לחצו לפתיחה/סגירה
Momentum — מומנטום למידה ואופטימיזציה
טכניקה המאיצה את האימון על ידי הוספת "תנופה" מצעדים קודמים, מה שעוזר לעבור מינימה רדודות.
נוסחה:
v ← β·v + ∇E, w ← w – α·v
מקדם: β = 0.9 בדרך כלל. אינטואיציה: כדור עם אינרציה — צובר מהירות בכיוון עקבי, מתעלם מתנודות מקריות. עוזר במיוחד ב"גאיות צרים" (canyons) של פונקציית השגיאה. וריאציה: Nesterov Accelerated Gradient — חישוב הגרדיאנט במיקום העתידי הצפוי.
לחצו לפתיחה/סגירה
Early Stopping — עצירה מוקדמת למידה ואופטימיזציה
עצירת האימון כשביצועי המודל על נתוני הבדיקה מפסיקים להשתפר, למניעת שינון (Overfitting).
איך זה עובד:
  1. אחרי כל epoch — מודדים val_loss.
  2. שומרים גרסה הטובה ביותר.
  3. אם N epochs ברצף ללא שיפור (patience) — עוצרים ומחזירים את הגרסה הטובה.
ב-Keras:
callback = EarlyStopping(patience=5, restore_best_weights=True) model.fit(..., callbacks=[callback])
חשוב: דורש סט ולידציה נפרד מהטסט.
לחצו לפתיחה/סגירה
Regularization — רגולריזציה למידה ואופטימיזציה
שיטות למניעת התאמת יתר על ידי הוספת "קנס" על משקלות גדולים או מורכבות מודל.
L2 (Ridge): קנס על סכום ריבועי המשקלות:
E = Loss + λ · Σ wᵢ²
מכווץ את כל המשקלות לכיוון 0, אך לא מאפס. ברירת מחדל פרקטית. L1 (Lasso): קנס על סכום הערכים האבסולוטיים:
E = Loss + λ · Σ |wᵢ|
מאפס לחלוטין משקלות לא חשובים → Feature Selection אוטומטי. שיטות נוספות: Dropout, Early Stopping, Data Augmentation, Batch Normalization — כולן צורות של רגולריזציה. היפר-פרמטר λ: טיפוסית 0.001-0.01. גדול מדי → underfit, קטן מדי → overfit.
לחצו לפתיחה/סגירה
Supervised Learning — למידה מונחית אלגוריתמי ML
למידה מנתונים מתויגים — לכל קלט x ידוע y הנכון. דוגמאות: Classification, Regression.
היפותזה: יש פונקציה f כך ש-y = f(x) + רעש. המטרה: ללמוד f על סמך דוגמאות (x, y) רבות. חלוקה: Classification (y קטגוריאלי), Regression (y רציף). רוב הקורס: כל הבעיות שלמדנו (IMDB, Reuters, MNIST, Boston) — Supervised.
לחצו לפתיחה/סגירה
Unsupervised Learning — למידה לא מונחית אלגוריתמי ML
למידה מנתונים ללא תיוג — מנסה למצוא דפוסים, קבוצות וייצוגים בעצמה.
סוגים מרכזיים:
  • Clustering — חלוקה לקבוצות (K-Means, DBSCAN).
  • Dimensionality Reduction — צמצום מימדים (PCA, t-SNE, UMAP, autoencoders).
  • Association Rules — מציאת קשרים (Apriori).
ב-DL: Self-supervised learning הפך לפופולרי — מודלי שפה (BERT, GPT) לומדים בלי תיוגים מפורשים.
לחצו לפתיחה/סגירה
Classification — סיווג אלגוריתמי ML
בעיית supervised שבה הפלט הוא קטגוריה דיסקרטית (לדוגמה: חתול / כלב / דג).
סוגים:
  • Binary — שתי מחלקות (sigmoid + binary_CE).
  • Multi-class single-label — N מחלקות, תיוג יחיד (softmax + categorical_CE).
  • Multi-label — N מחלקות, מספר תיוגים (sigmoid לכל אחת + binary_CE על כל מימד).
מדדים: Accuracy, Precision, Recall, F1, ROC-AUC.
לחצו לפתיחה/סגירה
Regression — רגרסיה אלגוריתמי ML
בעיית supervised שבה הפלט הוא מספר רציף (למשל: מחיר דירה, טמפרטורה, גיל).
שכבת פלט: 1 יחידה ללא אקטיבציה (או linear). Loss: MSE (סטנדרטי), MAE (עמיד יותר לחריגים), Huber (בין שניהם). Metrics: MAE, RMSE, R². חשוב: נורמליזציה של הקלטים — חיונית כשערכי features ברמות גודל שונות.
לחצו לפתיחה/סגירה
Clustering — אשכול אלגוריתמי ML
חלוקה אוטומטית של נתונים לקבוצות (clusters) על סמך דמיון, ללא תיוגים מראש.
אלגוריתמים נפוצים:
  • K-Means — מחלק ל-K קבוצות שכל אחת סביב מרכז (centroid).
  • DBSCAN — מבוסס צפיפות, לא דורש לקבוע K.
  • Hierarchical — בונה עץ של קבוצות.
ב-DL: אפשר לחלץ feature vectors מרשת ואז להפעיל K-Means במרחב הייצוג שלמדה הרשת.
לחצו לפתיחה/סגירה
Cross Entropy — אנטרופיה צולבת למידה ואופטימיזציה
פונקציית הפסד המודדת מרחק בין חלוקת הסתברויות חזויה (ŷ) לאמיתית (y). הסטנדרט בסיווג.
בינארית:
CE = –Σ [yᵢ ln(ŷᵢ) + (1–yᵢ) ln(1–ŷᵢ)]
קטגוריאלית:
CE = –Σ yᵢ · ln(ŷᵢ) (sum across classes)
אינטואיציה: CE קטן ↔ המודל "בטוח ונכון". CE גדול ↔ המודל "בטוח ושוגה" — ענישה כבדה. למה לא MSE: CE נותנת גרדיאנטים גדולים יותר במצבים גרועים, מה שמאיץ למידה.
לחצו לפתיחה/סגירה
Maximum Likelihood — מקסימום נראות למידה ואופטימיזציה
עיקרון: לבחור פרמטרים שמעניקים את ההסתברות הגבוהה ביותר לראות את הנתונים שראינו.
הקשר ל-Cross Entropy: מזעור CE שווה למקסימום log-likelihood. כך שאופטימיזציה של CE היא ביטוי מתמטי של עיקרון Maximum Likelihood. למה log: מכפלות הסתברויות (אלפי פעמים) מתכנסות ל-0. log הופך מכפלות לסכומים, יציב מספרית.
לחצו לפתיחה/סגירה
One-Hot Encoding — קידוד חם-יחיד אלגוריתמי ML
ייצוג קטגוריה כווקטור של 0 ו-1 בדיוק במקום אחד. למשל: מתוך 3 מחלקות, "כלב" → [0,1,0].
למה: אם נקודד "חתול=1, כלב=2, דג=3", המודל "ילמד" שדג קרוב יותר לכלב מאשר לחתול — מה שלא נכון. One-hot מבטיח מרחק שווה בין מחלקות. חיסרון: בזבוז זיכרון כשיש הרבה מחלקות (10K מילים → וקטור באורך 10K). פתרונות: sparse storage, embedding layers. אלטרנטיבה ב-Keras: sparse_categorical_crossentropy עובדת ישירות עם תיוגים כ-int.
לחצו לפתיחה/סגירה
Overfitting / Underfitting — התאמת יתר / חסר למידה ואופטימיזציה
Overfit: המודל "שיננן" את האימון אך נכשל על נתונים חדשים. Underfit: המודל פשוט מדי לתפוס את הקשר.
איך מזהים:
  • Underfit: train_loss + val_loss שניהם גבוהים.
  • Just right: שניהם נמוכים וקרובים.
  • Overfit: train_loss נמוך, val_loss גבוה ונפתח פער.
פתרונות ל-Overfit: יותר נתונים, רגולריזציה (L1/L2/Dropout), Early Stopping, מודל קטן יותר, Data Augmentation. פתרונות ל-Underfit: מודל גדול יותר, יותר epochs, פיצ'רים נוספים, פחות רגולריזציה.
לחצו לפתיחה/סגירה
K-fold Cross Validation — ולידציה צולבת K-קבוצות למידה ואופטימיזציה
חלוקת הנתונים ל-K קבוצות; מאמנים K פעמים, בכל פעם קבוצה אחרת משמשת ולידציה. הממוצע נותן הערכה יציבה.
K טיפוסי: 4, 5, 10. מתי משתמשים:
  • מעט נתונים (פחות מ-1K-10K) — Holdout רגיל לא יציב.
  • חוסר וודאות בפיצול — רוצים הערכה ממוצעת.
חיסרון: יקר חישובית — מאמנים את המודל K פעמים. ב-DL לרוב יקר מדי, ולכן Holdout עם train/val/test הוא הסטנדרט.
לחצו לפתיחה/סגירה
CNN — Convolutional Neural Network — רשת קונבולוציונית ארכיטקטורות DL
ארכיטקטורה לתמונות שמשתמשת בשכבות convolution לחיפוש דפוסים מקומיים (קצוות, צורות) ו-pooling לסיכום מידע.
מקדים את החומר — נלמד בהרצאה 7. הרעיון: במקום לחבר כל פיקסל לכל נוירון (Dense), חיבור מקומי בלבד. שיתוף משקלות בין מיקומים שונים בתמונה — הקרנל "סורק" את התמונה. שכבות מרכזיות: Conv2D (סינון), MaxPooling2D (סיכום), Flatten + Dense (לסיווג סופי). דוגמאות: LeNet, AlexNet, VGG, ResNet, EfficientNet.
לחצו לפתיחה/סגירה
RNN — Recurrent Neural Network — רשת רקורסיבית ארכיטקטורות DL
רשת לעיבוד רצפים (טקסט, סדרות זמן) — שומרת state פנימי שמתעדכן בכל צעד.
מקדים את החומר — נלמד בהרצאות 7-8. הרעיון: בכל צעד t, הרשת מקבלת קלט x_t וזיכרון h_{t-1}, ומחזירה h_t. כך היא מתחשבת בהקשר ההיסטורי. בעיות: Vanishing gradients ברצפים ארוכים → הומצאו LSTM ו-GRU שפותרים את זה. היום: רוב יישומי הטקסט עברו ל-Transformers, אבל RNNים עדיין שימושיים בסדרות זמן.
לחצו לפתיחה/סגירה
GRU — Gated Recurrent Unit — יחידה חוזרת עם שערים ארכיטקטורות DL
סוג של RNN עם שערים שמחליטים אילו חלקים של הזיכרון לעדכן ולשמור — פותר את בעיית ה-vanishing gradient.
מקדים את החומר — נראה בהרצאות 7-8. שערים: Update gate (כמה מהזיכרון הקודם לשמור) ו-Reset gate (כמה לאפס). פשוט יותר מ-LSTM, אבל לרוב נותן ביצועים דומים. פרמטרים: פחות מ-LSTM (אין שער "forget" נפרד) → אימון מהיר יותר.
לחצו לפתיחה/סגירה
BiGRU / BiLSTM — רשת רקורסיבית דו-כיוונית ארכיטקטורות DL
RNN שעובר על הרצף בשני הכיוונים (מהתחלה לסוף ומהסוף להתחלה) ומחבר את הייצוגים.
מתי שימושי: בטקסט — להבנת מילה כדאי לדעת גם מה לפני וגם מה אחרי. דוגמה: "תפוח" יכול להיות פרי או חברה — תלוי בהקשר משני הצדדים. מגבלה: דורש לראות את כל הרצף לפני החיזוי — לא מתאים לזמן אמת (streaming).
לחצו לפתיחה/סגירה
Stacked BiGRU / BiLSTM — שכבות RNN דו-כיווניות מוערמות ארכיטקטורות DL
מספר שכבות BiGRU/BiLSTM זו על גבי זו, לקבלת ייצוגים היררכיים של רצפים.
אינטואיציה: כל שכבה לומדת ייצוג ברמת הפשטה גבוהה יותר — כמו רשת עמוקה רגילה. שימושי במשימות מורכבות של NLP לפני עידן ה-Transformers.
לחצו לפתיחה/סגירה
TCN — Temporal Convolutional Network — רשת קונבולוציה זמנית ארכיטקטורות DL
חלופה ל-RNN שמשתמשת ב-CNN על סדרות זמן עם dilated convolutions לכיסוי תלות ארוכה.
יתרון: אימון מקבילי (CNN) — מהיר יותר מ-RNN. גם יציב יותר מבחינת גרדיאנטים. שימוש: פופולרי בסדרות זמן, אודיו, חיזוי שווקים פיננסיים.
לחצו לפתיחה/סגירה
SNN — Shallow Neural Network — רשת רדודה ארכיטקטורות DL
רשת בעלת שכבה נסתרת אחת. ההפך מ-Deep — מספר שכבות מוגבל.
מתי שימושי: בעיות פשוטות, כשמעט נתונים, או כ-baseline. תיאורטית מסוגלת לקרב כל פונקציה רציפה (Universal Approximation), אך פחות יעילה מבחינת פרמטרים מאשר רשת עמוקה.
לחצו לפתיחה/סגירה
edRVFL — Ensemble Deep Random Vector Functional Link — רשת אנסמבל עם משקלות אקראיות ארכיטקטורות DL
משפחת מודלים שבה רוב המשקלות אקראיים-קבועים, רק שכבת הפלט מאומנת. אנסמבל של מספר רשתות כאלה.
יתרון: אימון מהיר במיוחד (אין backprop ברוב הרשת). מתאים לסדרות זמן. חיסרון: בדרך כלל פחות מדויק מ-DL מאומן במלואו.
לחצו לפתיחה/סגירה
ResNet — Residual Network ארכיטקטורות DL
רשת CNN עמוקה במיוחד שמשתמשת ב"קישורי דילוג" (skip connections) — מאפשרת אימון של 50, 100, 152 שכבות.
הרעיון: במקום שכל שכבה תלמד y = f(x), היא לומדת y = f(x) + x — "שארית" (residual) מעל הקלט. למה זה עובד: השכבה תמיד יכולה ללמוד "אפס" ולא להזיק. גם הגרדיאנט עובר ישירות דרך החיבור — פתרון ל-vanishing gradient ברשתות עמוקות מאוד. גרסאות: ResNet-18, 34, 50, 101, 152. השפעה: פתח את הדרך לכל הרשתות הענקיות שבאו אחרי (DenseNet, EfficientNet).
לחצו לפתיחה/סגירה
NLP — Natural Language Processing — עיבוד שפה טבעית NLP
תחום ב-AI העוסק בעיבוד והבנה של שפה אנושית — תרגום, סיווג, סיכום, מענה לשאלות.
אתגרים: רב-משמעות, הקשר, סדר חשוב, אורך משתנה. תחומים: Sentiment Analysis, Machine Translation, Q&A, Named Entity Recognition, Summarization, Generation. אבולוציה: BoW → Word2Vec/GloVe → RNN/LSTM → Attention → Transformers → LLMs (GPT, BERT, Claude).
לחצו לפתיחה/סגירה
Word2Vec — ייצוג מילים כווקטורים NLP
שיטה ללימוד embedding צפוף לכל מילה (50-300 מימדים) כך שמילים בעלות הקשר דומה יקבלו וקטורים דומים.
שתי אופציות אימון:
  • Skip-gram — מנסה לחזות מילים קרובות בהינתן מילה.
  • CBOW — מנסה לחזות מילה בהינתן מילים קרובות.
תכונות מרשימות: "King – Man + Woman ≈ Queen" — קשרים סמנטיים נשמרים גאומטרית. היום: הוחלף ברובו ב-contextual embeddings (BERT, GPT) — אבל עדיין רלוונטי כ-baseline ושימושים פשוטים.
לחצו לפתיחה/סגירה
GloVe — Global Vectors for Word Representation — וקטורים גלובליים NLP
שיטה אלטרנטיבית ללמידת word embeddings, מבוססת על מטריצת co-occurrence גלובלית (כמה פעמים מילה i מופיעה ליד מילה j).
הבדל מ-Word2Vec: Word2Vec מבוסס על חלון מקומי, GloVe על סטטיסטיקה גלובלית. בפועל: שניהם נותנים ביצועים דומים. בחירה ביניהם היא לרוב עניין של נוחות ולא של דיוק.
לחצו לפתיחה/סגירה
FastText — ייצוג ברמת תת-מילה NLP
הרחבה של Word2Vec של Facebook — מייצגת כל מילה כסכום של n-grams של אותיות. מאפשר התמודדות עם מילים שלא נראו באימון.
דוגמה: "running" → ייצוג של "run", "unn", "nni", "nin", "ing" + המילה השלמה. אפילו מילה חדשה כמו "runned" תקבל ייצוג סביר. שימושי: שפות עם מורפולוגיה עשירה (עברית, ערבית, פינית), מילים נדירות, טעויות הקלדה.
לחצו לפתיחה/סגירה
Doc2Vec — ייצוג מסמכים כווקטורים NLP
הרחבה של Word2Vec לרמת מסמך שלם — לומדת ייצוג צפוף עבור כל מסמך, לא רק עבור כל מילה.
שימוש: חיפוש דמיון בין מסמכים, סיווג מסמכים, המלצות. היום: הוחלף ברובו ב-embeddings של Transformers (BERT [CLS], Sentence-BERT).
לחצו לפתיחה/סגירה
Attention Mechanism — מנגנון קשב NLP
טכניקה המאפשרת למודל "להתמקד" בחלקים רלוונטיים של הקלט בעת חיזוי. הבסיס של Transformers.
מקדים את החומר — נלמד בהרצאה 9. אינטואיציה: כשמתרגמים "הכלב חצה את הכביש", המילה "חצה" בעברית קרובה ל-"חצה" באנגלית — המודל "מתמקד" בה ולא במילים אחרות. נוסחה (פשוטה): Attention(Q, K, V) = softmax(QKᵀ/√d) · V. השפעה: Self-Attention הוא הליבה של Transformers — שינו את כל NLP מ-2017.
לחצו לפתיחה/סגירה
Image Processing — עיבוד תמונה Computer Vision
תחום העוסק בניתוח, עיבוד והבנה של תמונות באמצעות אלגוריתמים. ב-DL הוא הובל ע"י CNNs.
משימות עיקריות:
  • Classification — מהי התמונה? (חתול / כלב / מכונית).
  • Object Detection — איפה ומה בתמונה? (YOLO, Faster R-CNN).
  • Segmentation — תיוג כל פיקסל.
  • Generation — יצירת תמונות חדשות (GANs, Diffusion).
לחצו לפתיחה/סגירה
ImageNet — מאגר נתוני תמונות Computer Vision
מאגר של 14M+ תמונות ב-1000 קטגוריות, נוצר ע"י Fei-Fei Li ב-2009. נחשב ל"benchmark" של DL לתמונות.
השפעה היסטורית: תחרות ImageNet (ILSVRC) הביאה לפריצת AlexNet ב-2012 — נקודת ההפיכה של DL. שימוש: אימון Pre-trained models (ResNet, VGG, EfficientNet) שמשמשים כ-feature extractors בכל פרויקט CV — Transfer Learning.
לחצו לפתיחה/סגירה
Kernel Size — גודל פילטר קונבולוציה Computer Vision
הגודל של הפילטר המתחלק בשכבת Convolution. נפוץ: 3x3, 5x5. קובע את "שדה הראייה" של כל פיקסל בפלט.
שיקולים:
  • קטן (3x3) — מעט פרמטרים, לוכד דפוסים מקומיים מאוד. רוב CNNים מודרניים משתמשים ב-3x3 בלבד.
  • גדול (5x5, 7x7) — לוכד דפוסים גדולים יותר, אבל הרבה יותר פרמטרים.
  • שתי שכבות 3x3 = שדה ראייה אקוויוולנטי ל-5x5, אבל פחות פרמטרים ויותר עומק.
לחצו לפתיחה/סגירה
Global Pooling — איחוד גלובלי Computer Vision
פעולת pooling שמסכמת את כל המפה הספטרלית למספר אחד (ממוצע או מקסימום) לכל ערוץ.
שימוש: בקצה רשת CNN — במקום Flatten + Dense ענקי, משתמשים ב-Global Average Pooling להפחתה דרסטית של פרמטרים. יתרונות: פחות overfit, מודל קל יותר, אינווריאנט לגודל הקלט (בגבולות).
לחצו לפתיחה/סגירה
Convolution — פעולת קונבולוציה Computer Vision
פעולה מתמטית שמחליקה Kernel (פילטר) קטן על תמונה, ולכל מיקום מחשבת סכום משוקלל של הערכים — מייצרת תמונה חדשה (feature map) שמדגישה תבניות מסוימות.
הצעדים בפעולה:
  1. בוחרים פיקסל בתמונה.
  2. לוקחים חלון בגודל ה-Kernel (3x3 או 5x5) סביבו.
  3. מכפילים איבר-איבר עם ה-Kernel.
  4. סוכמים את כל המכפלות → ערך אחד.
  5. זה הופך לפיקסל המרכזי ב-feature map.
  6. חוזרים לכל פיקסל בתמונה.
חשיבות: זוהי הפעולה הבסיסית של CNN. במקום שכבת Dense שמחברת כל פיקסל לכל נוירון, Convolution מחברת רק חלון קטן ושומרת על מבנה מרחבי. הקשר: טכנית, ב-DL הפעולה היא cross-correlation, אבל השם "convolution" השתרש.
לחצו לפתיחה/סגירה
Kernel / Filter — פילטר / קרנל Computer Vision
מטריצה קטנה של מספרים (משקלות) שמיושמת על התמונה כפעולת Convolution. כל Kernel מזהה תבנית מסוימת — קצה אנכי, קצה אופקי, פינה, צבע מסוים.
פילטרים קלאסיים:
  • Sobel-x: [[-1,0,1],[-2,0,2],[-1,0,1]] — קצוות אנכיים.
  • Sobel-y: [[-1,-2,-1],[0,0,0],[1,2,1]] — קצוות אופקיים.
  • Sharpen: [[0,-1,0],[-1,5,-1],[0,-1,0]] — חידוד.
  • Box Blur: מטריצה של 1/9 — טשטוש.
תובנה מרכזית: ב-CNN הפילטרים נלמדים אוטומטית דרך Backpropagation. הרשת "ממציאה" את Sobel ופילטרים אחרים בעצמה — בלי שלימדנו אותה! בקוד: שכבת Conv2D(32, (3,3)) מגדירה 32 פילטרים בגודל 3×3.
לחצו לפתיחה/סגירה
Stride — צעד הפילטר Computer Vision
בכמה פיקסלים מזיזים את ה-Kernel בכל צעד החלקה על התמונה. ברירת מחדל: 1 (פיקסל אחד בכל פעם). Stride גדול = feature map קטן.
השפעות של Stride:
  • Stride=1: מזיזים פיקסל אחד בכל פעם — feature map בגודל מלא.
  • Stride=2: מדלגים על פיקסל בכל פעם — feature map בחצי הגודל.
  • Stride גדול: פחות חישוב, פחות פרמטרים, אבל פחות מידע.
בקוד: Conv2D(32, (3,3), strides=2) חלופה: במקום stride גדול, נפוץ להשתמש ב-stride=1 + Pooling layer (יותר גמיש).
לחצו לפתיחה/סגירה
Padding — ריפוד תמונה Computer Vision
הוספת "מסגרת" של פיקסלים סביב התמונה לפני Convolution, כדי שהפילטר יוכל לעבוד גם על פיקסלי הקצה.
שלוש גישות לטיפול בקצוות:
  • Crop / Valid Padding (padding='valid') — אין ריפוד, פיקסלי הקצה לא נכנסים. תמונת פלט קטנה יותר.
  • Same Padding (padding='same') — מוסיפים אפסים מסביב כך שתמונת הפלט באותו גודל. הסטנדרט המודרני.
  • Extension — מעתיקים את ערכי הקצה החוצה במקום אפסים. פחות נפוץ.
חישוב צורת הפלט: עם padding=same ו-stride=1 — הצורה לא משתנה. עם padding=valid ו-Kernel 3×3 — יורדים 2 פיקסלים בכל מימד.
לחצו לפתיחה/סגירה
Feature Map / Activation Map — מפת תכונות Computer Vision
תמונת הפלט שיוצאת אחרי הפעלת Kernel יחיד על תמונת קלט. כל Conv2D עם N פילטרים מייצר N feature maps — כל אחד מדגיש תבנית שונה.
אינטואיציה: אם פילטר זוהה תבנית מסוימת באזור מסוים בתמונה — הערך באותו מיקום ב-feature map יהיה גבוה. אם לא — נמוך. צורה: תמונת קלט (H, W, 3) → אחרי Conv2D(32 filters) → (H, W, 32). כל מהשכבות הבאות עובדות על הצורה הזו. שדה ראייה (Receptive Field): פיקסל ב-feature map "רואה" אזור 3×3 בקלט. אחרי 2 שכבות Conv 3×3 → שדה ראייה 5×5. אחרי 3 שכבות → 7×7. ככל שיורדים בעומק — שדה הראייה גדל.
לחצו לפתיחה/סגירה
Conv2D Layer — שכבת קונבולוציה Computer Vision
השכבה הבסיסית של CNN ב-Keras. מפעילה N פילטרים על הקלט ומחזירה N feature maps.
סינטקס מלא:
layers.Conv2D(filters=32, kernel_size=(3,3), strides=1, padding='same', activation='relu')
פרמטרים:
  • filters: כמה פילטרים → כמה ערוצים בפלט.
  • kernel_size: 3 או (3,3) — גודל הפילטר.
  • strides: 1 (סטנדרט) או 2 להפחתת מימדים.
  • padding: 'same' או 'valid'.
  • activation: בדרך כלל 'relu'.
חישוב פרמטרים: לכל פילטר: kernel_h × kernel_w × input_channels + 1 (+1 ל-bias). למשל פילטר 3×3 על RGB = 28 פרמטרים. ל-32 פילטרים = 896 פרמטרים בלבד.
לחצו לפתיחה/סגירה
Pooling Layer — שכבת איחוד / דגימה Computer Vision
שכבה שמקטינה את גודל ה-feature maps. לוקחת חלון (לרוב 2×2), מסכמת אותו לערך אחד, וממשיכה. מטרה: הפחתת מימדים, מניעת overfit, יציבות לתזוזה.
שני סוגים מרכזיים:
  • Max Pooling: לוקח את הערך המקסימלי בחלון. הסטנדרט במודרני — שומר על הסיגנל החזק ביותר.
  • Average Pooling: לוקח את הממוצע. פחות נפוץ — "מרכך" מידע.
צורה לאחר 2×2 Max Pooling עם stride=2: (H,W,C) → (H/2, W/2, C). העומק לא משתנה, כי pooling פועל על כל ערוץ בנפרד. הוא לא לומד פרמטרים — אין לו משקלות. רק פעולה דטרמיניסטית. בקוד: layers.MaxPooling2D(pool_size=(2,2))
לחצו לפתיחה/סגירה
Translation Invariance — אינווריאנטיות לתזוזה Computer Vision
תכונה שאומרת: הרשת תזהה את אותו אובייקט גם אם הוא מופיע במיקום שונה בתמונה. ב-CNN זה מובנה — הודות ל-weight sharing ול-Max Pooling.
למה ב-CNN זה מובנה:
  1. Weight sharing: אותו פילטר מופעל על כל אזור בתמונה. אם הוא לומד לזהות "אוזן" בפינה אחת — הוא יזהה גם בפינה השנייה.
  2. Max Pooling: בוחר את הערך המקסימלי בחלון. אם התבנית "זזה" קצת בתוך החלון — עדיין נבחרת.
מה לא מובנה: Rotation Invariance ו-Scale Invariance. הפתרון: Data Augmentation. הקבלה: ב-MLP אין Translation Invariance בכלל — אם רואים אותו אובייקט במיקום אחר, הרשת צריכה ללמוד מחדש את כל הפיקסלים.
לחצו לפתיחה/סגירה
Data Augmentation — הגדלת נתונים Computer Vision
יצירת ואריאציות אוטומטיות של תמונות האימון (סיבוב, היפוך, חיתוך, שינוי בהירות) כדי להגדיל את גודל הסט האפקטיבי ולשפר הכללה.
טכניקות סטנדרטיות:
  • Rotation: סיבוב אקראי ±15-30°.
  • Translation: הזזה לימין/שמאל.
  • Horizontal Flip: היפוך מראה (טוב לחיות; לא טוב לאותיות).
  • Zoom / Crop: חיתוך אקראי + הגדלה.
  • Color Jitter: שינוי בהירות, ניגודיות, רוויה.
  • CutOut, MixUp, CutMix: טכניקות מתקדמות להחלפת חלקי תמונה.
שתי תועלות:
  1. Rotation/Scale Invariance: מה ש-CNN לא יודע אוטומטית — ללמד ע"י דוגמאות.
  2. מניעת Overfit: המודל רואה תמונות "חדשות" בכל epoch.
בקוד Keras:
aug = keras.Sequential([ layers.RandomFlip("horizontal"), layers.RandomRotation(0.1), layers.RandomZoom(0.1), ])
לחצו לפתיחה/סגירה
AlexNet (2012) — הארכיטקטורה שפתחה את עידן ה-DL ארכיטקטורות DL
CNN ראשונה שניצחה בתחרות ImageNet ב-2012 בפער עצום (16% שגיאה לעומת 26% של המקום השני). נחשבת לנקודת המהפך של DL.
פיתחו: Alex Krizhevsky, Ilya Sutskever, Geoffrey Hinton (Toronto). חידושים מרכזיים:
  • שימוש ראשון ב-ReLU במקום sigmoid — הרבה יותר מהיר ופחות vanishing gradients.
  • שימוש ראשון ב-Dropout — הפחתת overfit.
  • שימוש ראשון ב-2 GPUs במקביל לאימון.
  • פילטרים גדולים יחסית: 11×11 ו-5×5.
גודל: 8 שכבות (5 Conv + 3 FC), 60M פרמטרים, אומן שבוע. השפעה: מאז 2012 — כל מודלי הזיהוי הפכו ל-DL.
לחצו לפתיחה/סגירה
VGG (2014) — ארכיטקטורה אלגנטית עם פילטרים קטנים ארכיטקטורות DL
משפחת CNNs מאוקספורד שהוכיחה: עומק > רוחב. משתמשת רק ב-Conv 3×3 ו-MaxPool 2×2 — פשוט אבל יעיל.
פיתחו: Visual Geometry Group, Oxford. גרסאות: VGG-16 (16 שכבות), VGG-19 (19 שכבות). חידוש מרכזי: שתי שכבות Conv 3×3 רצופות נותנות שדה ראייה כמו 5×5, אבל עם פחות פרמטרים ויותר אקטיבציות לא-לינאריות. זה ביסס את העיקרון "העדף עומק על רוחב". חיסרון: 138M פרמטרים — מודל כבד שדורש זיכרון רב. שימושים פרקטיים היום: בעיקר כ-feature extractor ל-Transfer Learning.
לחצו לפתיחה/סגירה
Skip Connection (Residual Connection) — קישור-דילוג ארכיטקטורות DL
מנגנון שמוסיף את הקלט של שכבה ישירות לפלט של שכבה אחרי-הבאה (קיצור-דרך). פותר את בעיית vanishing gradients ברשתות עמוקות מאוד.
הנוסחה:
y = F(x) + x
איפה x הוא הקלט ו-F(x) הוא מה ששתי שכבות עושות עליו. למה זה עובד:
  1. מסלול ישיר לגרדיאנט: בזמן Backprop הגרדיאנט יכול לעבור דרך ה-+ ישירות לאחור — לא נחלש דרך עשרות שכבות.
  2. ברירת מחדל היא Identity: אם השכבה לא צריכה לעשות כלום, היא יכולה ללמוד F(x)≈0 ופשוט להעביר את x. הפיצוי קל יותר ללמידה.
איפה משתמשים: ResNet (2015), Transformers, Diffusion Models — כמעט בכל ארכיטקטורה מודרנית. השפעה: איפשר לעלות מ-30 שכבות (לפני) ל-152 שכבות (ResNet-152) ואף 1000+ שכבות בניסויים.
לחצו לפתיחה/סגירה
Cosine Similarity — דמיון קוסינוס Computer Vision
מדד דמיון בין שני וקטורים על סמך הזווית ביניהם. שווה ל-1 לוקטורים זהים בכיוון, 0 לאורתוגונליים, –1 להפוכים.
cos(A, B) = (A · B) / (‖A‖ · ‖B‖)
שימוש:
  • השוואת feature vectors של תמונות (face recognition).
  • השוואת embeddings של מילים / מסמכים (NLP).
  • חיפוש סמנטי, המלצות.
למה לא Euclidean distance: מתעלם מאורך הוקטורים — נותן רק את כיוון הוקטור. לרוב מתאים יותר ב-DL כי מה שמעניין הוא הכיוון, לא העוצמה.
לחצו לפתיחה/סגירה
Forward Pass — מעבר קדמי Computer Vision
תהליך חישוב הפלט של רשת בהינתן קלט — בדיוק זהה ל-Feedforward.
הקשר: במקור הופיע במאמרים על NN לתמונות. כיום משתמשים ב-Forward Pass ו-Feedforward בערבוביה. שימוש פרקטי: בזמן Inference (חיזוי) — מבצעים רק Forward Pass, ללא BP. לכן הוא הרבה יותר מהיר מ-Training.
לחצו לפתיחה/סגירה
Transfer Learning — למידת העברה Computer Vision
להעביר ידע ממשימה אחת (אומנה על dataset ענק כמו ImageNet) למשימה אחרת קטנה (Cats vs Dogs). שתי הגישות העיקריות: Feature Extraction ו-Fine-Tuning.
למה זה עובד: השכבות התחתונות של CNN לומדות פיצ'רים גנריים (קצוות, מרקמים, פינות) שטובים לכל משימת ראייה — לא רק זו שעליה אומנו. השכבות העליונות לומדות פיצ'רים סמנטיים שעדיין נוטים להיות שימושיים בדומיינים קרובים. שתי גישות:
  1. Feature Extraction: מקפיאים את ה-conv_base ומאמנים רק classifier חדש מעליו.
  2. Fine-Tuning: מפשירים את השכבות העליונות של conv_base ומאמנים אותן יחד עם ה-classifier ב-lr נמוך מאוד.
השוואה כמותית (Cats vs Dogs): From scratch ⇒ 65% · Augmentation ⇒ 83% · Feature Extraction ⇒ 97.5% · Fine-tuning ⇒ 97.8%. מתי לא להשתמש: אם הדומיין שונה מהותית (נגיד תמונות רפואיות vs ImageNet) — Transfer Learning עדיין עוזר, אבל פחות.
לחצו לפתיחה/סגירה
Feature Extraction — חילוץ פיצ'רים Computer Vision
להעביר תמונות חדשות דרך conv_base מאומן (קפוא!) ולקבל features שעליהם מאמנים classifier חדש קטן. הגישה הסטנדרטית של transfer learning.
שתי שיטות ב-Keras:
  1. Fast — בלי augmentation: מריצים את conv_base פעם אחת על כל ה-dataset, שומרים features ב-RAM, מאמנים classifier קטן עליהם. ~2 שניות לאפוק, אבל אי-אפשר לשלב augmentation.
  2. עם augmentation: conv_base הוא חלק מהרשת — רץ בכל epoch על תמונות שעוברות augmentation. ~360 שניות לאפוק, אבל יותר יציב.
הקריטריון לבחירה: אם אין GPU מהיר — שיטה 1. אם יש — שיטה 2 תיתן test accuracy גבוה יותר ב-1-2 נקודות.
לחצו לפתיחה/סגירה
Fine-Tuning — כוונון עדין Computer Vision
השלב המתקדם של Transfer Learning: לאחר Feature Extraction, מפשירים (unfreeze) את השכבות העליונות של conv_base ומאמנים אותן יחד עם ה-classifier ב-lr נמוך מאוד (1e-5).
‎5 השלבים הקפדניים:
  1. בניית custom head על conv_base מאומן.
  2. קפיאת ה-conv_base (trainable=False).
  3. אימון ה-head בלבד (Feature Extraction).
  4. הפשרה (unfreeze) של ‎N השכבות העליונות (לדוגמה Block 5 ב-VGG16).
  5. אימון משותף של השכבות שהופשרו + ה-head ב-lr=1e-5.
למה lr נמוך: המשקלים של conv_base כבר טובים מאוד. עדכון חזק יהרוס את הייצוגים שלמדו על ImageNet (catastrophic forgetting). למה רק חלק עליון: השכבות התחתונות לומדות פיצ'רים גנריים (קצוות, מרקמים) שטובים לכל משימה. השכבות העליונות הן הספציפיות — שם כדאי להתאים. ערך מוסף בפועל: ‎+0.3 נקודות אחוז (97.5% → 97.8%). מינורי אבל "בחינם" אם כבר עשיתם Feature Extraction.
לחצו לפתיחה/סגירה
Pretrained Model / conv_base — מודל מאומן מראש Computer Vision
רשת שכבר אומנה על dataset ענק (ImageNet — ‎1.4M תמונות, 1000 קטגוריות) ושוחררה למשתמשים. ב-Keras: keras.applications.vgg16.VGG16(...).
הקונבנציה: conv_base = החלק הקונבולוציוני של המודל המאומן, ללא ה-classifier הסופי. בקוד:
conv_base = keras.applications.vgg16.VGG16( weights="imagenet", # טוען משקלות מאומנים include_top=False, # ❌ ללא FC head המקורי input_shape=(180, 180, 3))
פלט: tensor של פיצ'רים (לדוגמה 5×5×512 ל-VGG16 על קלט 180×180). מודלים פופולריים ב-keras.applications: Xception, ResNet50/101/152, MobileNet, EfficientNet, DenseNet, VGG16/19, Inception. בחירה: VGG = פשוט אבל איטי. ResNet = איזון טוב. MobileNet/EfficientNet = מהיר ומדויק לזיהוי במכשירים ניידים.
לחצו לפתיחה/סגירה
trainable = False (Freeze Layer) — הקפאת שכבה Computer Vision
דרך לסמן ל-Keras שמשקלות של שכבה/מודל לא יעודכנו באימון. נדרש להקפיא conv_base ב-Transfer Learning כדי לא להרוס את המשקלות שלמד על ImageNet.
תחביר:
conv_base.trainable = False # קפיאה for layer in conv_base.layers[:-4]: layer.trainable = False # קפיאה חלקית — רק החלק התחתון
בדיקת תקינות: len(conv_base.trainable_weights) — צריך להיות ‎0 אחרי קפיאה. ⚠ הטעות הנפוצה ביותר: לשנות trainable אחרי model.compile(). ה-compile מקבע את רשימת המשתנים שניתנים לאופטימיזציה. החוק: trainable → compile.
לחצו לפתיחה/סגירה
preprocess_input — Pre-processing ייעודי למודל מאומן Computer Vision
פונקציית נרמול ספציפית לכל רשת מאומנת ב-keras.applications. VGG16 דורש BGR ולא RGB, וחיסור ממוצע ImageNet — לא רק חלוקה ב-255.
למה צריך: כל רשת ב-keras.applications אומנה עם pre-processing ספציפי. שימוש לא נכון פוגע משמעותית באיכות ה-features. דוגמה:
x = keras.applications.vgg16.preprocess_input(images) # (1) RGB → BGR # (2) חיסור ממוצע ImageNet לכל ערוץ # (3) ערכים ב-range גס סביב 0, לא [0,1]
חשוב: או משתמשים ב-preprocess_input או ב-Rescaling(1./255), אבל לא בשניהם. עבור VGG/ResNet/Inception תמיד תשתמשו ב-preprocess_input. היכן ברשת: ראשית — מיד אחרי הקלט, לפני conv_base.
לחצו לפתיחה/סגירה
Rescaling Layer — שכבת נרמול פיקסלים Computer Vision
שכבה של Keras (layers.Rescaling(1./255)) שמנרמלת ערכי פיקסל מ-[0,255] ל-[0,1]. שימושית במיוחד כש-CNN מאומן מאפס (לא transfer learning).
למה לנרמל ל-[0,1]: כשערכי הקלט גדולים (255) ההפעלה הראשונה של הרשת מקבלת מספרים גדולים → gradient נפיץ → אימון לא יציב. נרמול מסדר את העניין. שתי דרכים אקוויולנטיות:
  • כשכבה ברשת: layers.Rescaling(1./255)(x) — פעיל גם ב-training וגם ב-inference. זו הדרך המומלצת.
  • במהלך טעינת dataset: dataset.map(lambda x, y: (x/255, y)) — עובד, אבל פחות נקי.
מתי לא להשתמש: כשמשתמשים ב-preprocess_input של מודל מאומן (שדורש BGR/mean subtraction, לא [0,1]).
לחצו לפתיחה/סגירה
image_dataset_from_directory — טעינת תמונות מתיקייה ל-Dataset כלים וספריות
פונקציה של Keras שטוענת תמונות מתיקיות באופן אוטומטי. שם תת-תיקייה = label. מחזירה tf.data.Dataset מוכן לאימון.
תחביר:
from tensorflow.keras.utils import image_dataset_from_directory train_dataset = image_dataset_from_directory( "cats_vs_dogs_small/train", image_size=(180, 180), # resize אוטומטי batch_size=32)
איך הוא מסיק labels: חייבים תיקייה ראשית עם תת-תיקיות לפי קטגוריות. לדוגמה train/cat/ ו-train/dog/ → cat=0, dog=1 (סדר אלפביתי). פלט: tf.data.Dataset שמחזיר (images_batch, labels_batch) עם shapes (32, 180, 180, 3) ו-(32,). היתרון על pandas/numpy: טעינה lazy — לא מחזיק את כל ה-dataset ב-RAM. עובד עם dataset של מיליוני תמונות.
לחצו לפתיחה/סגירה
tf.data.Dataset — API לזרימת נתונים ב-TF כלים וספריות
אובייקט lazy של TensorFlow לעבודה עם זרמי נתונים — טעינה לפי דרישה, batching, shuffling, mapping. הקלט הסטנדרטי ל-model.fit() במשימות עם dataset גדול.
הפעולות העיקריות:
  • .batch(n) — קבוצת n אובייקטים → batch אחד.
  • .shuffle(buffer) — ערבוב סדר. חשוב לפני אימון!
  • .map(fn) — הפעלת פונקציה על כל element (לדוגמה preprocessing).
  • .prefetch(n) — טעינה מקבילית של batches הבאים (האצה).
  • .cache() — שמירת ה-dataset בזיכרון אחרי טעינה ראשונה.
iteration: ניתן לעבור עליו ב-for loop רגיל (זה iterator) או להעביר ל-model.fit(). בדיקה:
for data_batch, labels_batch in train_dataset: print(data_batch.shape) # (32, 180, 180, 3) print(labels_batch.shape) # (32,) break
למה lazy: חוסך זיכרון — לא טוען הכל בבת-אחת. קריטי ל-datasets גדולים מ-RAM.
לחצו לפתיחה/סגירה
keras.applications — מודולי מודלים מאומנים מראש כלים וספריות
חבילה ב-Keras עם רשתות פופולריות מאומנות על ImageNet, זמינות בקריאה אחת. הבסיס לכל Transfer Learning ב-Keras.
מודלים זמינים: VGG16/19, ResNet50/101/152, Xception, Inception V3, MobileNet (V1/V2/V3), EfficientNet (B0-B7), DenseNet, NASNet. פרמטרים נפוצים:
  • weights="imagenet" — טעינת משקלות מאומנים. אופציות נוספות: None (אתחול אקראי), פתח קובץ.
  • include_top=False — בלי ה-FC הסופי (1000 קטגוריות). חובה ל-transfer learning.
  • input_shape=(180, 180, 3) — נדרש כש-include_top=False.
בנוסף, כל מודל מספק:
  • keras.applications.<model>.preprocess_input — pre-processing ייעודי לאותה רשת.
  • keras.applications.<model>.decode_predictions — לפענוח פלט של מודל עם include_top=True.
החלפת מודל קלה: כל הרשתות חולקות אותו interface, אז להחליף VGG16 ב-ResNet50 זה שינוי שורה אחת.
לחצו לפתיחה/סגירה
TensorFlow / Keras — Frameworks ל-DL כלים וספריות
TensorFlow היא ספרייה low-level של Google לחישובי tensor ו-DL. Keras היא API high-level שיושב מעליה ומספק שכבות מוכנות.
היחס: כיום Keras היא tf.keras — ה-API הרשמי של TensorFlow. למה Keras בקורס: מאפשר לבנות מודלים ב-5-10 שורות קוד, מתמקד בארכיטקטורה ולא בפלאמברינג. גרסאות: TF 1.x השתמש ב-static graph (קשה לדיבוג). TF 2.x עבר ל-eager execution (כמו PyTorch).
לחצו לפתיחה/סגירה
PyTorch — Framework של Meta כלים וספריות
Framework ל-DL של Meta (Facebook). Pythonic, eager-by-default, פופולרי במחקר אקדמי וב-LLMs.
הבדלים מ-TF/Keras:
  • "Pythonic" יותר — נכתב כאילו זה Python רגיל.
  • Dynamic computation graphs — קל לדיבוג.
  • פופולרי במאמרים אקדמיים והרבה LLMs (GPT, LLaMA) נכתבו ב-PyTorch.
בקורס: נתמקד ב-Keras, אבל אם תמשיכו ל-research או ל-LLMs — PyTorch כדאי להכיר.
לחצו לפתיחה/סגירה
Google Colab — Jupyter בענן כלים וספריות
סביבת Jupyter Notebook חינמית בענן של Google, הכוללת גישה ל-GPU/TPU. הסטנדרט של הקורס.
יתרונות:
  • חינמי (גרסה Pro תמורת תשלום).
  • GPU בלי התקנה.
  • שיתוף קל (Google Drive integration).
  • מותקן מראש כל מה שצריך — TF, Keras, PyTorch, NumPy, Pandas.
מגבלות: זמן ריצה מוגבל, מנותק אחרי תקופה לא פעילה, GPU "חינמי" יכול להיות זמין באופן לא צפוי.
לחצו לפתיחה/סגירה

איך להשתמש במילון בלמידה

  • בכל שאלת חזרה — בדקו אם יש מושג שלא זכור והקפידו להבין אותו לעומק.
  • במהלך קריאת הרצאה — חפשו במילון את כל המושגים שעולים בה.
  • שבוע לפני המבחן — עברו על המילון לפי קטגוריות (חברו אותם לקבוצות הגיוניות).
  • נסו "להקריא" כל מושג בקול: שם → הגדרה במשפט → מתי משתמשים → דוגמה.
  • אם אתם לא יכולים להסביר אותו לעצמכם — לא מבינים אותו עדיין.

תוכן עניינים