1. מבוא
במדריך הזה ניצור מודל TensorFlow.js לזיהוי ספרות בכתב יד באמצעות רשת נוירונים קונבולוציונית. קודם כול, נאמן את המסווג על ידי כך שהוא 'יסתכל' על אלפי תמונות של ספרות בכתב יד ועל התוויות שלהן. לאחר מכן, נבדוק את רמת הדיוק של המסווג באמצעות נתוני בדיקה שהמודל מעולם לא ראה.
המשימה הזו נחשבת למשימת סיווג, כי אנחנו מאמנים את המודל להקצות קטגוריה (הספרה שמופיעה בתמונה) לתמונת הקלט. אנחנו נאמן את המודל על ידי הצגת דוגמאות רבות של קלט יחד עם הפלט הנכון. השיטה הזו נקראת למידה מונחית.
מה תפַתחו
תצרו דף אינטרנט שמשתמש ב-TensorFlow.js כדי לאמן מודל בדפדפן. Given a black and white image of a particular size it will classify which digit appears in the image. השלבים שצריך לבצע:
- טוענים את הנתונים.
- הגדרת הארכיטקטורה של המודל.
- מאמנים את המודל ועוקבים אחרי הביצועים שלו במהלך האימון.
- מעריכים את המודל שאומן על ידי יצירת תחזיות.
מה תלמדו
- תחביר של TensorFlow.js ליצירת מודלים של רשתות קונבולוציה באמצעות TensorFlow.js Layers API.
- ניסוח משימות סיווג ב-TensorFlow.js
- איך עוקבים אחרי אימון בדפדפן באמצעות הספרייה tfjs-vis.
מה תצטרכו
- גרסה עדכנית של Chrome או דפדפן מודרני אחר שתומך במודולים של ES6.
- כלי לעריכת טקסט שפועל באופן מקומי במחשב או באינטרנט, למשל Codepen או Glitch.
- ידע ב-HTML, CSS, JavaScript ובכלי הפיתוח ל-Chrome (או בכלי הפיתוח של הדפדפן המועדף).
- הבנה מושגית ברמה גבוהה של רשתות נוירונים. אם אתם צריכים מבוא או רענון, כדאי לצפות בסרטון הזה של 3blue1brown או בסרטון הזה על למידה עמוקה ב-JavaScript של Ashi Krishnan.
מומלץ גם להכיר את החומר במדריך ההדרכה הראשון שלנו.
2. להגדרה
יוצרים דף HTML וכוללים בו את קוד ה-JavaScript
מעתיקים את הקוד הבא לקובץ HTML בשם
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TensorFlow.js Tutorial</title>
<!-- Import TensorFlow.js -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.0/dist/tf.min.js"></script>
<!-- Import tfjs-vis -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-vis@1.0.2/dist/tfjs-vis.umd.min.js"></script>
<!-- Import the data file -->
<script src="data.js" type="module"></script>
<!-- Import the main script file -->
<script src="script.js" type="module"></script>
</head>
<body>
</body>
</html>
יצירת קובצי JavaScript לנתונים ולקוד
- באותה תיקייה שבה נמצא קובץ ה-HTML שלמעלה, יוצרים קובץ בשם data.js ומעתיקים את התוכן מהקישור הזה לקובץ.
- באותה תיקייה שבה יצרתם את הקובץ בשלב הראשון, יוצרים קובץ בשם script.js ומכניסים לתוכו את הקוד הבא.
console.log('Hello TensorFlow');
רוצים לנסות?
אחרי שיצרתם את קובצי ה-HTML וה-JavaScript, אתם יכולים לבדוק אותם. פותחים את הקובץ index.html בדפדפן ופותחים את המסוף של כלי הפיתוח.
אם הכול פועל, אמורים להיווצר שני משתנים גלובליים. tf היא הפניה לספריית TensorFlow.js, tfvis היא הפניה לספריית tfjs-vis.
אם מופיעה ההודעה Hello TensorFlow, אפשר לעבור לשלב הבא.
3. טעינת הנתונים
במדריך הזה תאמנו מודל לזיהוי ספרות בתמונות כמו אלה שלמטה. התמונות האלה הן תמונות בגווני אפור בגודל 28x28 פיקסלים מתוך מערך נתונים שנקרא MNIST.

סיפקנו קוד לטעינת התמונות האלה מקובץ ספרייט מיוחד (בגודל של כ-10MB) שיצרנו בשבילכם, כדי שנוכל להתמקד בחלק של האימון.
מומלץ לעיין בקובץ data.js כדי להבין איך הנתונים נטענים. אפשר גם ליצור גישה משלכם לטעינת הנתונים אחרי שתסיימו את המדריך הזה.
הקוד שסופק מכיל מחלקה MnistData עם שתי שיטות ציבוריות:
-
nextTrainBatch(batchSize): מחזירה קבוצה אקראית של תמונות והתוויות שלהן מתוך קבוצת נתונים לאימון. -
nextTestBatch(batchSize): מחזירה קבוצה של תמונות והתוויות שלהן מקבוצת נתונים לבדיקה
בנוסף, המחלקה MnistData מבצעת את השלבים החשובים של ערבוב ונרמול הנתונים.
יש בסך הכול 65,000 תמונות. נשתמש ב-55,000 תמונות כדי לאמן את המודל, ונשמור 10,000 תמונות שבהן נוכל להשתמש כדי לבדוק את ביצועי המודל אחרי שנסיים. אנחנו הולכים לעשות את כל זה בדפדפן!
עכשיו נטען את הנתונים ונוודא שהם נטענו בצורה נכונה.
מוסיפים את הקוד הבא לקובץ script.js.
import {MnistData} from './data.js';
async function showExamples(data) {
// Create a container in the visor
const surface =
tfvis.visor().surface({ name: 'Input Data Examples', tab: 'Input Data'});
// Get the examples
const examples = data.nextTestBatch(20);
const numExamples = examples.xs.shape[0];
// Create a canvas element to render each example
for (let i = 0; i < numExamples; i++) {
const imageTensor = tf.tidy(() => {
// Reshape the image to 28x28 px
return examples.xs
.slice([i, 0], [1, examples.xs.shape[1]])
.reshape([28, 28, 1]);
});
const canvas = document.createElement('canvas');
canvas.width = 28;
canvas.height = 28;
canvas.style = 'margin: 4px;';
await tf.browser.toPixels(imageTensor, canvas);
surface.drawArea.appendChild(canvas);
imageTensor.dispose();
}
}
async function run() {
const data = new MnistData();
await data.load();
await showExamples(data);
}
document.addEventListener('DOMContentLoaded', run);
מרעננים את הדף, ואחרי כמה שניות אמורה להופיע חלונית בצד ימין עם מספר תמונות.

4. הגדרת המשימה
נתוני הקלט שלנו נראים כך.

המטרה שלנו היא לאמן מודל שיקבל תמונה אחת וילמד לחזות ציון לכל אחת מ-10 המחלקות האפשריות שהתמונה עשויה להשתייך אליהן (הספרות 0-9).
כל תמונה היא ברוחב 28 פיקסלים ובגובה 28 פיקסלים, ויש לה ערוץ צבע אחד כי היא תמונה בגווני אפור. לכן הצורה של כל תמונה היא [28, 28, 1].
חשוב לזכור שאנחנו מבצעים מיפוי של אחד לעשרה, וגם את הצורה של כל דוגמת קלט, כי זה חשוב לקטע הבא.
5. הגדרת ארכיטקטורת המודל
בקטע הזה נכתוב קוד לתיאור ארכיטקטורת המודל. ארכיטקטורת המודל היא דרך מתוחכמת להגיד "אילו פונקציות יופעלו במודל כשהוא יבצע את הפעולות", או לחלופין "באיזה אלגוריתם ישתמש המודל כדי לחשב את התשובות".
בלמידת מכונה, אנחנו מגדירים ארכיטקטורה (או אלגוריתם) ומאפשרים לתהליך האימון ללמוד את הפרמטרים של האלגוריתם הזה.
מוסיפים את הפונקציה הבאה אל
script.js קובץ להגדרת ארכיטקטורת המודל
function getModel() {
const model = tf.sequential();
const IMAGE_WIDTH = 28;
const IMAGE_HEIGHT = 28;
const IMAGE_CHANNELS = 1;
// In the first layer of our convolutional neural network we have
// to specify the input shape. Then we specify some parameters for
// the convolution operation that takes place in this layer.
model.add(tf.layers.conv2d({
inputShape: [IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_CHANNELS],
kernelSize: 5,
filters: 8,
strides: 1,
activation: 'relu',
kernelInitializer: 'varianceScaling'
}));
// The MaxPooling layer acts as a sort of downsampling using max values
// in a region instead of averaging.
model.add(tf.layers.maxPooling2d({poolSize: [2, 2], strides: [2, 2]}));
// Repeat another conv2d + maxPooling stack.
// Note that we have more filters in the convolution.
model.add(tf.layers.conv2d({
kernelSize: 5,
filters: 16,
strides: 1,
activation: 'relu',
kernelInitializer: 'varianceScaling'
}));
model.add(tf.layers.maxPooling2d({poolSize: [2, 2], strides: [2, 2]}));
// Now we flatten the output from the 2D filters into a 1D vector to prepare
// it for input into our last layer. This is common practice when feeding
// higher dimensional data to a final classification output layer.
model.add(tf.layers.flatten());
// Our last layer is a dense layer which has 10 output units, one for each
// output class (i.e. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9).
const NUM_OUTPUT_CLASSES = 10;
model.add(tf.layers.dense({
units: NUM_OUTPUT_CLASSES,
kernelInitializer: 'varianceScaling',
activation: 'softmax'
}));
// Choose an optimizer, loss function and accuracy metric,
// then compile and return the model
const optimizer = tf.train.adam();
model.compile({
optimizer: optimizer,
loss: 'categoricalCrossentropy',
metrics: ['accuracy'],
});
return model;
}
בואו נבחן את זה קצת יותר לעומק.
קונבולוציות
model.add(tf.layers.conv2d({
inputShape: [IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_CHANNELS],
kernelSize: 5,
filters: 8,
strides: 1,
activation: 'relu',
kernelInitializer: 'varianceScaling'
}));
בדוגמה הזו אנחנו משתמשים במודל רציף.
אנחנו משתמשים בשכבת conv2d במקום בשכבה דחוסה. אנחנו לא יכולים להיכנס לכל הפרטים של אופן הפעולה של קונבולוציות, אבל הנה כמה מקורות מידע שמסבירים את הפעולה הבסיסית:
עכשיו נפרק כל ארגומנט באובייקט ההגדרה של conv2d:
-
inputShape. הצורה של הנתונים שיזרמו לשכבה הראשונה של המודל. במקרה הזה, הדוגמאות שלנו מ-MNIST הן תמונות בשחור-לבן בגודל 28x28 פיקסלים. הפורמט הקנוני של נתוני תמונה הוא[row, column, depth], ולכן כאן אנחנו רוצים להגדיר צורה של[28, 28, 1]. 28 שורות ועמודות למספר הפיקסלים בכל מימד, ועומק של 1 כי לתמונות שלנו יש רק ערוץ צבע אחד. שימו לב: אנחנו לא מציינים את גודל האצווה בצורת הקלט. השכבות מתוכננות כך שהן לא תלויות בגודל אצווה, ולכן במהלך ההסקה אפשר להעביר טנזור בכל גודל אצווה. -
kernelSize. גודל החלונות של המסנן הקונבולוציוני הנע שיוחל על נתוני הקלט. בדוגמה הזו, הגדרנוkernelSizeשל5, שמציין חלון קונבולוציה ריבועי בגודל 5x5. -
filters. מספר חלונות הסינון בגודלkernelSizeשיוחלו על נתוני הקלט. במקרה הזה, נחיל 8 מסננים על הנתונים. -
strides. גודל הצעד של החלון הנע – כלומר, כמה פיקסלים המסנן יזיז בכל פעם שהוא נע מעל התמונה. כאן אנחנו מציינים את הצעדים של 1, כלומר המסנן יחליק על התמונה בצעדים של פיקסל אחד. -
activation. פונקציית ההפעלה שרוצים להחיל על הנתונים אחרי שהקונבולוציה מסתיימת. במקרה הזה, אנחנו מיישמים פונקציה של יחידה ליניארית מתוקנת (ReLU), שהיא פונקציית הפעלה נפוצה מאוד במודלים של למידת מכונה. -
kernelInitializer. השיטה שבה משתמשים כדי לאתחל באופן אקראי את משקלי המודל, וזה חשוב מאוד לדינמיקת האימון. לא נפרט כאן על האתחול, אבלVarianceScaling(שמשמש כאן) הוא בדרך כלל אמצעי אתחול טוב.
השטחת ייצוג הנתונים
model.add(tf.layers.flatten());
תמונות הן נתונים רב-ממדיים, ופעולות קונבולוציה נוטות להגדיל את גודל הנתונים שמוזנים אליהן. לפני שמעבירים אותם לשכבת הסיווג הסופית, צריך לשטח את הנתונים למערך ארוך אחד. שכבות צפופות (שבהן אנחנו משתמשים כשכבה הסופית) לוקחות רק tensor1ds, ולכן השלב הזה נפוץ במשימות סיווג רבות.
חישוב התפלגות ההסתברות הסופית
const NUM_OUTPUT_CLASSES = 10;
model.add(tf.layers.dense({
units: NUM_OUTPUT_CLASSES,
kernelInitializer: 'varianceScaling',
activation: 'softmax'
}));
נשתמש בשכבה צפופה עם הפעלת softmax כדי לחשב התפלגויות הסתברות על פני 10 המחלקות האפשריות. הספרה החזויה תהיה המחלקה עם הציון הכי גבוה.
בחירת פונקציית אופטימיזציה והפסד
const optimizer = tf.train.adam();
model.compile({
optimizer: optimizer,
loss: 'categoricalCrossentropy',
metrics: ['accuracy'],
});
אנחנו קומפילציה של המודל, מציינים אופטימיזציה, פונקציית הפסד ומדדים שאנחנו רוצים לעקוב אחריהם.
בניגוד למדריך הראשון, כאן אנחנו משתמשים ב-categoricalCrossentropy כפונקציית הפסד. כפי שהשם מרמז, נעשה שימוש בשיטה הזו כשהפלט של המודל הוא התפלגות הסתברויות. categoricalCrossentropy מודדת את השגיאה בין התפלגות ההסתברות שנוצרה על ידי השכבה האחרונה של המודל שלנו לבין התפלגות ההסתברות שניתנה על ידי התווית האמיתית שלנו.
לדוגמה, אם הספרה שלנו מייצגת באמת את הספרה 7, יכול להיות שנקבל את התוצאות הבאות
אינדקס | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
True Label | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
תחזית | 0.1 | 0.01 | 0.01 | 0.01 | 0.20 | 0.01 | 0.01 | 0.60 | 0.03 | 0.02 |
הפונקציה Categorical cross entropy תפיק מספר יחיד שמציין עד כמה וקטור החיזוי דומה לווקטור התווית האמיתית.
הייצוג של הנתונים שמשמש כאן לתוויות נקרא קידוד one-hot, והוא נפוץ בבעיות סיווג. לכל מחלקה יש הסתברות שמשויכת אליה לכל דוגמה. אם אנחנו יודעים בדיוק מה צריך להיות, אנחנו יכולים להגדיר את ההסתברות הזו כ-1 ואת ההסתברויות האחרות כ-0. מידע נוסף על קידוד one-hot זמין בדף הזה.
מדד נוסף שנעקוב אחריו הוא accuracy, שבבעיית סיווג מייצג את אחוז התחזיות הנכונות מתוך כל התחזיות.
6. אימון המודל
מעתיקים את הפונקציה הבאה לקובץ script.js.
async function train(model, data) {
const metrics = ['loss', 'val_loss', 'acc', 'val_acc'];
const container = {
name: 'Model Training', tab: 'Model', styles: { height: '1000px' }
};
const fitCallbacks = tfvis.show.fitCallbacks(container, metrics);
const BATCH_SIZE = 512;
const TRAIN_DATA_SIZE = 5500;
const TEST_DATA_SIZE = 1000;
const [trainXs, trainYs] = tf.tidy(() => {
const d = data.nextTrainBatch(TRAIN_DATA_SIZE);
return [
d.xs.reshape([TRAIN_DATA_SIZE, 28, 28, 1]),
d.labels
];
});
const [testXs, testYs] = tf.tidy(() => {
const d = data.nextTestBatch(TEST_DATA_SIZE);
return [
d.xs.reshape([TEST_DATA_SIZE, 28, 28, 1]),
d.labels
];
});
return model.fit(trainXs, trainYs, {
batchSize: BATCH_SIZE,
validationData: [testXs, testYs],
epochs: 10,
shuffle: true,
callbacks: fitCallbacks
});
}
אחר כך מוסיפים את הקוד הבא ל-
פונקציית run.
const model = getModel();
tfvis.show.modelSummary({name: 'Model Architecture', tab: 'Model'}, model);
await train(model, data);
מרעננים את הדף, ואחרי כמה שניות אמורים להופיע כמה גרפים שמציגים את התקדמות האימון.

בואו נבחן את זה קצת יותר לעומק.
מעקב אחרי מדדים
const metrics = ['loss', 'val_loss', 'acc', 'val_acc'];
כאן מחליטים אילו מדדים רוצים לעקוב אחריהם. אנחנו נעקוב אחרי ההפסד והדיוק של קבוצת נתונים לאימון, וגם אחרי ההפסד והדיוק של קבוצת נתונים לתיקוף (val_loss ו-val_acc בהתאמה). בהמשך נרחיב על קבוצת נתונים לתיקוף.
הכנת נתונים כטנסורים
const BATCH_SIZE = 512;
const TRAIN_DATA_SIZE = 5500;
const TEST_DATA_SIZE = 1000;
const [trainXs, trainYs] = tf.tidy(() => {
const d = data.nextTrainBatch(TRAIN_DATA_SIZE);
return [
d.xs.reshape([TRAIN_DATA_SIZE, 28, 28, 1]),
d.labels
];
});
const [testXs, testYs] = tf.tidy(() => {
const d = data.nextTestBatch(TEST_DATA_SIZE);
return [
d.xs.reshape([TEST_DATA_SIZE, 28, 28, 1]),
d.labels
];
});
בשלב הזה אנחנו יוצרים שני מערכי נתונים: קבוצת נתונים לאימון שבאמצעותה נאמן את המודל, וקבוצת נתונים לתיקוף שבאמצעותה נבדוק את המודל בסוף כל תקופת אימון. עם זאת, הנתונים בקבוצת הנתונים לתיקוף לא מוצגים למודל במהלך האימון.
סיווג הנתונים שסיפקנו מאפשר לקבל בקלות טנסורים מנתוני התמונה. אבל אנחנו עדיין משנים את הצורה של הטנסורים לצורה שהמודל מצפה לה, [num_examples, image_width, image_height, channels], לפני שאנחנו יכולים להזין אותם למודל. לכל מערך נתונים יש לנו גם נתוני קלט (ה-X) וגם תוויות (ה-Y).
return model.fit(trainXs, trainYs, {
batchSize: BATCH_SIZE,
validationData: [testXs, testYs],
epochs: 10,
shuffle: true,
callbacks: fitCallbacks
});
אנחנו קוראים ל-model.fit כדי להתחיל את לולאת האימון. אנחנו גם מעבירים מאפיין validationData כדי לציין באילו נתונים המודל צריך להשתמש כדי לבדוק את עצמו אחרי כל תקופה (אבל לא כדי לאמן את עצמו).
אם המודל משיג ביצועים טובים בנתוני האימון אבל לא בנתוני האימות, סביר להניח שהוא מותאם יתר על המידה לנתוני האימון ולא יבצע הכללה טובה של קלט שהוא לא ראה בעבר.
7. הערכת המודל שלנו
דיוק האימות מספק הערכה טובה לגבי רמת הביצועים של המודל שלנו בנתונים שהוא לא ראה בעבר (כל עוד הנתונים האלה דומים במידה מסוימת לקבוצת הנתונים לתיקוף). עם זאת, יכול להיות שנרצה פירוט מדויק יותר של הביצועים בכל אחת מהקטגוריות.
יש כמה שיטות ב-tfjs-vis שיכולות לעזור לכם.
מוסיפים את הקוד הבא לתחתית הקובץ script.js
const classNames = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine'];
function doPrediction(model, data, testDataSize = 500) {
const IMAGE_WIDTH = 28;
const IMAGE_HEIGHT = 28;
const testData = data.nextTestBatch(testDataSize);
const testxs = testData.xs.reshape([testDataSize, IMAGE_WIDTH, IMAGE_HEIGHT, 1]);
const labels = testData.labels.argMax(-1);
const preds = model.predict(testxs).argMax(-1);
testxs.dispose();
return [preds, labels];
}
async function showAccuracy(model, data) {
const [preds, labels] = doPrediction(model, data);
const classAccuracy = await tfvis.metrics.perClassAccuracy(labels, preds);
const container = {name: 'Accuracy', tab: 'Evaluation'};
tfvis.show.perClassAccuracy(container, classAccuracy, classNames);
labels.dispose();
}
async function showConfusion(model, data) {
const [preds, labels] = doPrediction(model, data);
const confusionMatrix = await tfvis.metrics.confusionMatrix(labels, preds);
const container = {name: 'Confusion Matrix', tab: 'Evaluation'};
tfvis.render.confusionMatrix(container, {values: confusionMatrix, tickLabels: classNames});
labels.dispose();
}
מה הקוד הזה עושה?
- ביצוע חיזוי.
- חישוב מדדי הדיוק.
- הצגת המדדים
בואו נבחן מקרוב כל אחד מהשלבים.
יצירת תחזיות
function doPrediction(model, data, testDataSize = 500) {
const IMAGE_WIDTH = 28;
const IMAGE_HEIGHT = 28;
const testData = data.nextTestBatch(testDataSize);
const testxs = testData.xs.reshape([testDataSize, IMAGE_WIDTH, IMAGE_HEIGHT, 1]);
const labels = testData.labels.argMax(-1);
const preds = model.predict(testxs).argMax(-1);
testxs.dispose();
return [preds, labels];
}
קודם כל, צריך ליצור תחזיות. בשלב הזה ניקח 500 תמונות וננסה לחזות איזו ספרה מופיעה בהן (אפשר להגדיל את המספר הזה בהמשך כדי לבדוק קבוצה גדולה יותר של תמונות).
חשוב לציין שהפונקציה argmax היא זו שמחזירה את האינדקס של המחלקה עם ההסתברות הכי גבוהה. חשוב לזכור שהמודל מוציא הסתברות לכל מחלקה. בשלב הזה אנחנו מוצאים את ההסתברות הכי גבוהה ומשתמשים בה כחיזוי.
אפשר גם לשים לב שאנחנו יכולים לבצע חיזוי לגבי כל 500 הדוגמאות בבת אחת. זוהי עוצמת הווקטוריזציה ש-TensorFlow.js מספק.
הצגת רמת הדיוק לכל כיתה
async function showAccuracy() {
const [preds, labels] = doPrediction();
const classAccuracy = await tfvis.metrics.perClassAccuracy(labels, preds);
const container = { name: 'Accuracy', tab: 'Evaluation' };
tfvis.show.perClassAccuracy(container, classAccuracy, classNames);
labels.dispose();
}
בעזרת קבוצה של תחזיות ותוויות, אפשר לחשב את רמת הדיוק של כל מחלקה.
הצגת מטריצת בלבול
async function showConfusion() {
const [preds, labels] = doPrediction();
const confusionMatrix = await tfvis.metrics.confusionMatrix(labels, preds);
const container = { name: 'Confusion Matrix', tab: 'Evaluation' };
tfvis.render.confusionMatrix(container, {values: confusionMatrix, tickLabels: classNames});
labels.dispose();
}
מטריצת בלבול דומה לדיוק לפי סיווג, אבל היא מפרטת יותר כדי להציג דפוסים של סיווג שגוי. כך אפשר לראות אם המודל מתבלבל לגבי זוגות מסוימים של מחלקות.
הצגת המחיר ביחס לשוק
מוסיפים את הקוד הבא לחלק התחתון של פונקציית ההפעלה כדי להציג את ההערכה.
await showAccuracy(model, data);
await showConfusion(model, data);
התצוגה אמורה להיראות כך:

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