מודלים לאימון

מדריך זה מניח שכבר קראת את מדריך הדגמים והשכבות .

ב-TensorFlow.js יש שתי דרכים לאמן מודל למידת מכונה:

  1. באמצעות Layers API עם LayersModel.fit() או LayersModel.fitDataset() .
  2. שימוש ב-Core API עם Optimizer.minimize() .

ראשית, נבחן את Layers API, שהוא API ברמה גבוהה יותר לבניית מודלים והדרכה. לאחר מכן, נראה כיצד לאמן את אותו מודל באמצעות Core API.

מבוא

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

האימון כולל מספר שלבים:

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

מודל מאומן היטב יספק מיפוי מדויק מהקלט לפלט הרצוי.

פרמטרים של דגם

בואו נגדיר מודל דו-שכבתי פשוט באמצעות Layers API:

const model = tf.sequential({
 layers: [
   tf.layers.dense({inputShape: [784], units: 32, activation: 'relu'}),
   tf.layers.dense({units: 10, activation: 'softmax'}),
 ]
});

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

model.weights.forEach(w => {
 console.log(w.name, w.shape);
});

אנו מקבלים את הפלט הבא:

> dense_Dense1/kernel [784, 32]
> dense_Dense1/bias [32]
> dense_Dense2/kernel [32, 10]
> dense_Dense2/bias [10]

יש 4 משקולות בסך הכל, 2 לכל שכבה צפופה. זה צפוי מכיוון ששכבות צפופות מייצגות פונקציה הממפה את טנסור הקלט x לטנסור הפלט y באמצעות המשוואה y = Ax + b כאשר A (הגרעין) ו- b (ההטיה) הם פרמטרים של השכבה הצפופה.

הערה: כברירת מחדל, שכבות צפופות כוללות הטיה, אך ניתן לא לכלול אותה על ידי ציון {useBias: false} באפשרויות בעת יצירת שכבה צפופה.

model.summary() היא שיטה שימושית אם ברצונך לקבל סקירה כללית של המודל שלך ולראות את המספר הכולל של הפרמטרים:

שכבה (סוג) צורת פלט פראם מס'
צפוף_צפוף1 (צפוף) [null,32] 25120
dense_Dense2 (צפוף) [null,10] 330
סה"כ פרמים: 25450
פרמטרים ניתנים לאימון: 25450
פרמטרים שאינם ניתנים לאימון: 0

כל משקל במודל הוא backend על ידי אובייקט Variable . ב-TensorFlow.js, Variable הוא Tensor בנקודה צפה עם שיטה אחת נוספת assign() המשמשת לעדכון הערכים שלו. ה-Layers API מאתחל את המשקולות באופן אוטומטי באמצעות שיטות עבודה מומלצות. לצורך ההדגמה, נוכל להחליף את המשקולות על ידי קריאה assign() על המשתנים הבסיסיים:

model.weights.forEach(w => {
  const newVals = tf.randomNormal(w.shape);
  // w.val is an instance of tf.Variable
  w.val.assign(newVals);
});

מייעל, הפסד ומדד

לפני שאתה עושה אימון כלשהו, ​​אתה צריך להחליט על שלושה דברים:

  1. מייעל . תפקידו של האופטימיזר הוא להחליט כמה לשנות כל פרמטר במודל, בהתחשב בתחזית המודל הנוכחית. בעת שימוש ב-Layers API, אתה יכול לספק מזהה מחרוזת של מייעל קיים (כגון 'sgd' או 'adam' ), או מופע של מחלקת Optimizer .
  2. פונקציית אובדן . מטרה שהמודל ינסה למזער. המטרה שלו היא לתת מספר בודד ל"כמה שגוי" התחזית של המודל הייתה. ההפסד מחושב על כל אצווה של נתונים כך שהמודל יוכל לעדכן את משקלו. בעת שימוש ב-Layers API, אתה יכול לספק מזהה מחרוזת של פונקציית אובדן קיימת (כגון 'categoricalCrossentropy' ), או כל פונקציה שלוקחת ערך חזוי וערך אמיתי ומחזירה הפסד. ראה רשימה של הפסדים זמינים במסמכי ה-API שלנו.
  3. רשימת מדדים. בדומה להפסדים, מדדים מחשבים מספר בודד, המסכמים עד כמה המודל שלנו מצליח. המדדים מחושבים בדרך כלל על כל הנתונים בסוף כל תקופה. לכל הפחות, אנחנו רוצים לעקוב שההפסד שלנו יורד עם הזמן. עם זאת, לעתים קרובות אנו רוצים מדד ידידותי יותר לאדם כמו דיוק. בעת שימוש ב-Layers API, אתה יכול לספק מזהה מחרוזת של מדד קיים (כגון 'accuracy' ), או כל פונקציה שלוקחת ערך חזוי וערך אמיתי ומחזירה ניקוד. ראה רשימה של מדדים זמינים במסמכי ה-API שלנו.

לאחר שהחלטת, הידור LayersModel על ידי קריאה ל- model.compile() עם האפשרויות שסופקו:

model.compile({
  optimizer: 'sgd',
  loss: 'categoricalCrossentropy',
  metrics: ['accuracy']
});

במהלך הקומפילציה, המודל יבצע אימות מסוים כדי לוודא שהאפשרויות שבחרת תואמות זו לזו.

הַדְרָכָה

ישנן שתי דרכים לאמן LayersModel :

  • שימוש model.fit() ומספק את הנתונים כטנזור אחד גדול.
  • שימוש model.fitDataset() ומתן הנתונים דרך אובייקט Dataset .

model.fit()

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

// Generate dummy data.
const data = tf.randomNormal([100, 784]);
const labels = tf.randomUniform([100, 10]);

function onBatchEnd(batch, logs) {
  console.log('Accuracy', logs.acc);
}

// Train for 5 epochs with batch size of 32.
model.fit(data, labels, {
   epochs: 5,
   batchSize: 32,
   callbacks: {onBatchEnd}
 }).then(info => {
   console.log('Final accuracy', info.history.acc);
 });

מתחת למכסה המנוע, model.fit() יכול לעשות הרבה עבורנו:

  • מפצל את הנתונים לרכבת ולסט אימות, ומשתמש בערכת האימות כדי למדוד התקדמות במהלך האימון.
  • מערבב את הנתונים אבל רק לאחר הפיצול. ליתר ביטחון, עליך לערבב מראש את הנתונים לפני העברתם ל- fit() .
  • מפצל את טנזור הנתונים הגדול לטנזורים קטנים יותר בגודל batchSize.
  • קורא ל- optimizer.minimize() תוך חישוב אובדן המודל ביחס לאצוות הנתונים.
  • זה יכול להודיע ​​לך על ההתחלה והסיום של כל תקופה או אצווה. במקרה שלנו, אנו מקבלים הודעה בסוף כל אצווה באמצעות האפשרות callbacks.onBatchEnd . אפשרויות אחרות כוללות: onTrainBegin , onTrainEnd , onEpochBegin , onEpochEnd ו- onBatchBegin .
  • זה נכנע לחוט הראשי כדי להבטיח שניתן לטפל במשימות בתור בלולאת האירועים של JS בזמן.

למידע נוסף, עיין בתיעוד של fit() . שים לב שאם תבחר להשתמש ב-Core API, תצטרך ליישם את ההיגיון הזה בעצמך.

model.fitDataset()

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

function* data() {
 for (let i = 0; i < 100; i++) {
   // Generate one sample at a time.
   yield tf.randomNormal([784]);
 }
}

function* labels() {
 for (let i = 0; i < 100; i++) {
   // Generate one sample at a time.
   yield tf.randomUniform([10]);
 }
}

const xs = tf.data.generator(data);
const ys = tf.data.generator(labels);
// We zip the data and labels together, shuffle and batch 32 samples at a time.
const ds = tf.data.zip({xs, ys}).shuffle(100 /* bufferSize */).batch(32);

// Train the model for 5 epochs.
model.fitDataset(ds, {epochs: 5}).then(info => {
 console.log('Accuracy', info.history.acc);
});

למידע נוסף על מערכי נתונים, עיין בתיעוד של model.fitDataset() .

חיזוי נתונים חדשים

לאחר הכשרה של המודל, תוכל לקרוא ל- model.predict() כדי לבצע תחזיות על נתונים שלא נראים:

// Predict 3 random samples.
const prediction = model.predict(tf.randomNormal([3, 784]));
prediction.print();

Core API

קודם לכן, הזכרנו שיש שתי דרכים לאמן מודל למידת מכונה ב-TensorFlow.js.

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

ייתכן שתרצה להשתמש ב-Core API בכל פעם:

  • אתה צריך גמישות או שליטה מקסימלית.
  • ואתה לא צריך סדרה, או שאתה יכול ליישם את היגיון ההסדרה שלך.

למידע נוסף על API זה, קרא את הקטע "Core API" במדריך מודלים ושכבות .

אותו דגם כמו לעיל שנכתב באמצעות Core API נראה כך:

// The weights and biases for the two dense layers.
const w1 = tf.variable(tf.randomNormal([784, 32]));
const b1 = tf.variable(tf.randomNormal([32]));
const w2 = tf.variable(tf.randomNormal([32, 10]));
const b2 = tf.variable(tf.randomNormal([10]));

function model(x) {
  return x.matMul(w1).add(b1).relu().matMul(w2).add(b2);
}

בנוסף ל-Layers API, ה-Data API גם עובד בצורה חלקה עם Core API. בואו נעשה שימוש חוזר במערך הנתונים שהגדרנו קודם לכן בסעיף model.fitDataset() , שעושה עבורנו ערבוב ואצווה:

const xs = tf.data.generator(data);
const ys = tf.data.generator(labels);
// Zip the data and labels together, shuffle and batch 32 samples at a time.
const ds = tf.data.zip({xs, ys}).shuffle(100 /* bufferSize */).batch(32);

בואו נאמן את הדגם:

const optimizer = tf.train.sgd(0.1 /* learningRate */);
// Train for 5 epochs.
for (let epoch = 0; epoch < 5; epoch++) {
  await ds.forEachAsync(({xs, ys}) => {
    optimizer.minimize(() => {
      const predYs = model(xs);
      const loss = tf.losses.softmaxCrossEntropy(ys, predYs);
      loss.data().then(l => console.log('Loss', l));
      return loss;
    });
  });
  console.log('Epoch', epoch);
}

הקוד למעלה הוא המתכון הסטנדרטי בעת אימון מודל עם Core API:

  • לולאה על מספר העידנים.
  • בתוך כל תקופה, בצע לולאה על קבוצות הנתונים שלך. בעת שימוש Dataset , dataset.forEachAsync() הוא דרך נוחה לבצע לולאה על האצוות שלך.
  • עבור כל אצווה, קרא optimizer.minimize(f) , אשר מבצע f ומצמצם את הפלט שלו על ידי מחשוב מעברי צבע ביחס לארבעת המשתנים שהגדרנו קודם לכן.
  • f מחשב את ההפסד. הוא קורא לאחת מפונקציות ההפסד המוגדרות מראש תוך שימוש בחיזוי המודל והערך האמיתי.