יצירת חבילות דפדפן מותאמות לגודל עם TensorFlow.js

סקירה כללית

TensorFlow.js 3.0 מביא תמיכה בבניית חבילות דפדפנים מותאמות לגודל, מוכוונות ייצור . במילים אחרות, אנחנו רוצים להקל עליך לשלוח פחות JavaScript לדפדפן.

תכונה זו מיועדת למשתמשים עם מקרי שימוש בייצור שירוויחו במיוחד מגילוח בתים מהמטען שלהם (ולכן מוכנים להשקיע מאמץ כדי להשיג זאת). כדי להשתמש בתכונה זו עליך להכיר את מודולי ES , כלי צרור של JavaScript כגון webpack או rollup , ומושגים כגון ניעור עצים/ביטול קוד מת .

מדריך זה מדגים כיצד ליצור מודול tensorflow.js מותאם אישית שניתן להשתמש בו עם מצרף כדי ליצור מבנה מותאם לגודל עבור תוכנית באמצעות tensorflow.js.

טרמינולוגיה

בהקשר של מסמך זה ישנם כמה מונחי מפתח בהם נשתמש:

ES Modules - מערכת מודולי JavaScript הסטנדרטית . הוצג ב-ES6/ES2015. ניתן לזיהוי באמצעות הצהרות יבוא ויצוא .

צרור - לקיחת קבוצה של נכסי JavaScript וקיבוץ/איגד אותם לנכס JavaScript אחד או יותר הניתנים לשימוש בדפדפן. זהו השלב שבדרך כלל מייצר את הנכסים הסופיים המוגשים לדפדפן. יישומים בדרך כלל יבצעו צרור משלהם ישירות ממקורות ספרייה שהועברו . חבילות נפוצות כוללות רולאפ ו- webpack . התוצאה הסופית של צרור ידועה בתור חבילה (או לפעמים כחתיכה אם היא מפוצלת למספר חלקים)

Tree-Shaking / Dead Code Elimination - הסרת קוד שלא נעשה בו שימוש באפליקציה הסופית בכתב. זה נעשה במהלך צרור, בדרך כלל בשלב הקטנה.

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

Kernel - יישום ספציפי של אופציה הקשורה ליכולות חומרה ספציפיות. הגרעינים הם 'רמה נמוכה' וספציפיים לקצה האחורי. לאופציות מסוימות יש מיפוי אחד לאחד מאופ לקרנל בעוד שאופציות אחרות משתמשות במספר גרעינים.

מקרי היקף ושימוש

מסקנות רק מודלים גרפים

מקרה השימוש העיקרי ששמענו עליו ממשתמשים הקשורים לזה, ותומכים במהדורה זו הוא זה של הסקת מסקנות עם דגמי גרפים של TensorFlow.js . אם אתה משתמש במודל שכבות של TensorFlow.js , אתה יכול להמיר את זה לפורמט מודל גרף באמצעות tfjs-converter . פורמט מודל הגרף יעיל יותר עבור מקרה השימוש במסקנות.

מניפולציה של Tensor ברמה נמוכה עם tfjs-core

מקרה השימוש הנוסף שאנו תומכים בו הוא תוכניות המשתמשות ישירות בחבילת @tensorflow/tjfs-core עבור מניפולציה של טנסור ברמה נמוכה יותר.

הגישה שלנו לבנות בהתאמה אישית

עקרונות הליבה שלנו בעת תכנון פונקציונליות זו כוללים את הדברים הבאים:

  • עשה שימוש מרבי במערכת מודול JavaScript (ESM) ואפשר למשתמשים של TensorFlow.js לעשות את אותו הדבר.
  • הפוך את TensorFlow.js לנעילת עץ ככל האפשר על ידי חבילות קיימות (למשל webpack, rollup וכו'). זה מאפשר למשתמשים לנצל את כל היכולות של אותם חבילות כולל תכונות כמו פיצול קוד.
  • עד כמה שניתן לשמור על קלות השימוש עבור משתמשים שאינם רגישים לגודל החבילה . זה כן אומר שבניית ייצור ידרוש יותר מאמץ מכיוון שרבות מברירות המחדל בספריות שלנו תומכות בנוחות שימוש על פני בנייה מותאמות לגודל.

המטרה העיקרית של זרימת העבודה שלנו היא לייצר מודול JavaScript מותאם אישית עבור TensorFlow.js המכיל רק את הפונקציונליות הנדרשת עבור התוכנית שאנו מנסים לייעל. אנו מסתמכים על מאגדים קיימים כדי לבצע את האופטימיזציה בפועל.

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

  • מפרטי הדגם המאוחסנים בקבצי model.json
  • מערכת שיגור ה-op to backend-specific-kernel dispatching שאנו משתמשים בה.

זה הופך את יצירת מבנה tfjs מותאם אישית למעורבת קצת יותר מאשר רק הפניית צרור לחבילת @tensorflow/tfjs הרגילה.

כיצד ליצור חבילות מותאמות אישית מותאמות לגודל

שלב 1: קבע באילו ליבות התוכנית שלך משתמשת

שלב זה מאפשר לנו לקבוע את כל הגרעינים המשמשים את כל המודלים שאתה מריץ או קוד טרום/אחרי עיבוד בהתחשב ב-backend שבחרת.

השתמש ב-tf.profile כדי להפעיל את החלקים של היישום שלך שמשתמשים ב-tensorflow.js ולקבל את הגרעינים. זה ייראה משהו כזה

const profileInfo = await tf.profile(() => {
  // You must profile all uses of tf symbols.
  runAllMyTfjsCode();
});

const kernelNames = profileInfo.kernelNames
console.log(kernelNames);

העתק את רשימת הגרעינים ללוח שלך לשלב הבא.

אתה צריך ליצור פרופיל של הקוד באמצעות אותם קצה/ים שבהם אתה רוצה להשתמש בחבילה המותאמת אישית שלך.

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

שלב 2. כתוב קובץ תצורה עבור מודול tfjs המותאם אישית

להלן קובץ תצורה לדוגמה.

זה נראה כמו זה:

{
  "kernels": ["Reshape", "_FusedMatMul", "Identity"],
  "backends": [
      "cpu"
  ],
  "models": [
      "./model/model.json"
  ],
  "outputPath": "./custom_tfjs",
  "forwardModeOnly": true
}
  • גרעינים: רשימת הגרעינים שיש לכלול בחבילה. העתק את זה מהפלט של שלב 1.
  • backends: רשימת backend(s) שברצונך לכלול. אפשרויות חוקיות כוללות "מעבד", "webgl" ו-"wasm".
  • models: רשימה של קבצי model.json עבור דגמים שאתה טוען ביישום שלך. יכול להיות ריק אם התוכנית שלך לא משתמשת ב-tfjs_converter כדי לטעון מודל גרף.
  • outputPath: נתיב לתיקיה להכניס את המודולים שנוצרו.
  • forwardModeOnly: הגדר את זה ל-false אם אתה רוצה לכלול מעברי צבע עבור הגרעינים הרשומים קודם לכן.

שלב 3. צור את מודול tfjs המותאם אישית

הפעל את כלי הבנייה המותאם אישית עם קובץ התצורה כארגומנט. עליך להתקין את החבילה @tensorflow/tfjs כדי לקבל גישה לכלי זה.

npx tfjs-custom-module  --config custom_tfjs_config.json

פעולה זו תיצור תיקיה ב- outputPath עם כמה קבצים חדשים.

שלב 4. הגדר את ה-bunler שלך לכינוי tfjs למודול המותאם אישית החדש.

ב-bunlers כמו webpack ו-rollup אנו יכולים לכנות את ההפניות הקיימות למודולי tfjs כדי להצביע על מודולי ה-tfjs המותאמים אישית החדשים שלנו. ישנם שלושה מודולים שצריכים להיות כינויים כדי לחסוך מקסימלי בגודל החבילה.

הנה קטע של איך זה נראה ב-webpack ( דוגמה מלאה כאן ):

...

config.resolve = {
  alias: {
    '@tensorflow/tfjs$':
        path.resolve(__dirname, './custom_tfjs/custom_tfjs.js'),
    '@tensorflow/tfjs-core$': path.resolve(
        __dirname, './custom_tfjs/custom_tfjs_core.js'),
    '@tensorflow/tfjs-core/dist/ops/ops_for_converter': path.resolve(
        __dirname, './custom_tfjs/custom_ops_for_converter.js'),
  }
}

...

והנה קטע הקוד המקביל לאוסף ( דוגמה מלאה כאן ):

import alias from '@rollup/plugin-alias';

...

alias({
  entries: [
    {
      find: /@tensorflow\/tfjs$/,
      replacement: path.resolve(__dirname, './custom_tfjs/custom_tfjs.js'),
    },
    {
      find: /@tensorflow\/tfjs-core$/,
      replacement: path.resolve(__dirname, './custom_tfjs/custom_tfjs_core.js'),
    },
    {
      find: '@tensorflow/tfjs-core/dist/ops/ops_for_converter',
      replacement: path.resolve(__dirname, './custom_tfjs/custom_ops_for_converter.js'),
    },
  ],
}));

...

אם ה-bunler שלך אינו תומך בכינוי מודול, תצטרך לשנות את הצהרות import ​​שלך כדי לייבא tensorflow.js מ- custom_tfjs.js שנוצר בשלב 3. הגדרות ה-Op לא יתנערו מהעץ, אבל הגרעינים עדיין יהיו עץ -מְזוּעזָע. בדרך כלל גרעינים מטלטלים עצים הם מה שמספק את החיסכון הגדול ביותר בגודל החבילה הסופי.

אם אתה משתמש רק בחבילת @tensoflow/tfjs-core, אז אתה רק צריך לכנות את החבילה האחת הזו.

שלב 5. צור את החבילה שלך

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

שלב 6. בדוק את האפליקציה שלך

הקפד לבדוק שהאפליקציה שלך פועלת כמצופה!