TensorFlow.js: בניית מערכת לזיהוי ספאם בתגובות

1. לפני שמתחילים

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

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

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

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

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

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

דרישות מוקדמות

שיעור Lab זה נכתב למפתחי אתרים שאינם חדשים ללמידה חישובית ורוצים להתחיל להשתמש במודלים הנלמדים מראש עם TensorFlow.js.

ההנחה היא שהיכרות עם HTML5, CSS ו-JavaScript נחשבת עבור שיעור ה-Lab הזה.

מה תלמדו

מה עליך לעשות?

  • למידע נוסף על מהי TensorFlow.js ועל מודלים קיימים לעיבוד שפה טבעית.
  • צרו דף אינטרנט פשוט של HTML / CSS / JS עבור בלוג וידאו פיקטיבי עם קטע תגובה בזמן אמת.
  • שימוש ב-TensorFlow.js כדי לטעון מודל של למידה חישובית מאומן מראש שיכול לחזות אם משפט שהוזן עלול להיות ספאם או לא, ואם כן, יש להזהיר את המשתמש על כך שהתגובה שלו עוכבה לניהול.
  • מקודדים משפטים של תגובות באופן שמנצל את מודל הלמידה החישובית כדי לסווג אותם.
  • פרשו את הפלט של מודל הלמידה החישובית כדי להחליט אם לסמן את התגובה באופן אוטומטי או לא. חוויית המשתמש ההיפוטטית הזו יכולה להיות בשימוש בכל אתר שאתם עובדים עליו, ומתאימה אותו לכל תרחיש לדוגמה של לקוח – למשל בלוג רגיל, פורום או מערכת ניהול תוכן כלשהי, כמו Drupal.

יפה יפה. קשה לעשות את זה? תשובה לא נכונה. בואו ננסה...

מה צריך?

  • מומלץ להשתמש בחשבון Glitch.com כדי לעקוב אחריו, או להשתמש בסביבת פרסום באינטרנט שמרגישים בנוח לערוך ולהפעיל אותה בעצמכם.

2. מה זה TensorFlow.js?

1aee0ede85885520.png

TensorFlow.js היא ספרייה של למידה חישובית בקוד פתוח שניתן להפעיל בכל מקום שבו JavaScript יכול לפעול. הוא מבוסס על ספריית TensorFlow המקורית שכתובה ב-Python ומיועדת ליצור מחדש את חוויית המפתח הזו ואת קבוצת ממשקי ה-API עבור הסביבה העסקית של JavaScript.

איפה אפשר להשתמש?

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

  • בצד הלקוח בדפדפן האינטרנט באמצעות JavaScript
  • בצד של השרת ואפילו במכשירי IoT כמו Rasperie Pi באמצעות Node.js
  • אפליקציות למחשב באמצעות Electron
  • אפליקציות מקוריות לנייד עם React Native

TensorFlow.js תומך גם בנקודות קצה עורפיות בכל אחת מהסביבות האלה (למשל, הסביבות המבוססות על החומרה שניתן להפעיל בתוך ה-CPU או ה-WebGL. "backend" בהקשר הזה, אין פירושו סביבה בצד השרת – הקצה העורפי להפעלה יכול להיות בצד הלקוח ב-WebGL) כדי להבטיח תאימות וכדי למנוע שיבושים בפעילות. בשלב זה, TensorFlow.js תומך בשיטות הבאות:

  • הפעלת WebGL בכרטיס הגרפי של המכשיר (GPU) - זו הדרך המהירה ביותר להריץ דגמים גדולים יותר (יותר מ-3MB) בהאצה של המעבד הגרפי.
  • הפעלה של הרכבה באינטרנט (WASM) – במעבד - לשיפור ביצועי המעבד (CPU) במכשירים שונים, כולל טלפונים ניידים מדור קודם. אפשרות זו מתאימה יותר לדגמים קטנים (בגודל של פחות מ-3MB), שבפועל יכולים לפעול מהר יותר עם מעבד WASM בהשוואה ל-WebGL עקב תקורת ההעלאה של תוכן למעבד גרפי.
  • ביצוע CPU – החלופה למקרה שבו אף אחת מהסביבות האחרות לא זמינה. זו הפעולה האיטית ביותר מבין השלוש, אבל היא תמיד שם בשבילך.

הערה: תוכל לאלץ שימוש באחת מאפשרויות הקצה האלהאם אתה יודע באיזה מכשיר תבצע את הפעולה הזו, או פשוט לאפשר ל-TensorFlow.js להחליט עבורך.

כוחות על בצד הלקוח

הפעלת TensorFlow.js בדפדפן האינטרנט במחשב של הלקוח יכולה להניב מספר יתרונות שכדאי לשקול.

פרטיות

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

מהירות

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

היקף החשיפה והיקף החשיפה

בלחיצה אחת, כל אדם בעולם יכול ללחוץ על קישור ששלחת אליו, לפתוח את דף האינטרנט בדפדפן ולהשתמש בכל מה שיצרת. אין צורך בהגדרה מורכבת של Linux בצד השרת עם מנהלי התקנים של CUDA ועוד פעולות רבות יותר כדי להשתמש במערכת הלמידה החישובית.

עלות

ללא שרתים, הדבר היחיד שעליכם לשלם הוא CDN לאירוח קובצי ה-HTML, ה-CSS, ה-JS והמודלים שלכם. העלות של CDN זולה יותר מתחזוקת שרת (עם אופציה עם כרטיס גרפי) סביב השעון.

תכונות בצד השרת

שימוש בהטמעת Node.js של TensorFlow.js מאפשר את התכונות הבאות.

תמיכה מלאה ב-CUDA

בצד השרת, להאצת כרטיס גרפי, עליכם להתקין את מנהלי ההתקנים של NVIDIA CUDA כדי לאפשר ל-TenororFlow לפעול עם כרטיס הגרפיקה (בניגוד לדפדפן שמשתמש ב-WebGL – אין צורך בהתקנה). עם זאת, תמיכה מלאה ב-CUDA מאפשרת לכם למנף באופן מלא את היכולות של כרטיס הגרפיקה ברמה נמוכה יותר, וכך להאיץ את האימון ואת ההסקות. הביצועים תואמים להטמעה של Python TensorFlow מכיוון ששניהם חולקים את הקצה העורפי של C++.

גודל דגם

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

IOT

תמיכה ב-Node.js במחשבים פופולריים בלוח יחיד, כמו Rפטber Pi, זמינה גם למודלים של TensorFlow.js במכשירים כאלה.

מהירות

Node.js כתוב ב-JavaScript, ופירוש הדבר שהוא מפיק תועלת מהדור בלבד. המשמעות היא שבהרבה מקרים אתם עשויים לראות שיפורי ביצועים כשמשתמשים ב-Node.js, כי האופטימיזציה מתבצעת בזמן הריצה, במיוחד בכל עיבוד מראש שאתם מבצעים. אפשר לראות דוגמה מצוינת לכך במקרה לדוגמה הזה, שמראה איך השימוש ב-Hocking Face השתמש ב-Node.js כדי לשפר פי 2 את מודל העיבוד של השפה הטבעית.

עכשיו אתם מבינים את העקרונות הבסיסיים של TensorFlow.js, שבהם הם יכולים לפעול וכמה מהיתרונות שלהם, בואו נתחיל לעבוד איתם.

3. דגמים עם הכנה מראש

למה כדאי להשתמש במודל של אימון מראש?

יש כמה יתרונות שמומלץ להתחיל עם מודל מאומן מראש אם הוא מתאים לתרחיש לדוגמה הרצוי, כגון:

  1. אין צורך לאסוף נתוני הדרכה בעצמכם. הכנת נתונים בפורמט הנכון ותיוג שלהם כך שמערכת למידה חישובית יכולה להשתמש בו ללמידה, יכולה להיות מצריכה זמן ויקר מאוד.
  2. היכולת ליצור אב-טיפוס במהירות של רעיון עם חיסכון בזמן ובעלויות.
    אין נקודה &להמציא מחדש את הגלגל&ה
  3. השימוש במצב של מחקר האומנות. לעתים קרובות, מודלים שהוכשרו מראש מבוססים על מחקר פופולרי, שמעניקים לכם חשיפה למודלים כאלה ומבינים את הביצועים שלהם בעולם האמיתי.
  4. קלות שימוש ותיעוד נרחב. בשל הפופולריות של דגמים כאלה.
  5. העברת הלמידה היכולות. במודלים מסוימים עם הכשרה מראש אפשר להשתמש ביכולות העברה, שהן בעיקרון העברות נתונים שנלמדו ממשימה אחת של למידה חישובית. דוגמה דומה. לדוגמה, אפשר ללמד מחדש מודל שאומן במקור לזהות חתולים, כדי לאמן מחדש לזהות כלבים, אם נתתם להם נתוני אילוף חדשים. הפעולה הזו תהיה מהירה יותר כי אי אפשר להתחיל מלוח קנבס ריק. המודל יכול להשתמש במה שהוא כבר למד כדי לזהות חתולים כדי לזהות את הדבר החדש – לכלבים יש גם עיניים ואוזניים, כך שאם הוא כבר יודע איך למצוא את התכונות האלה, אנחנו כבר באמצע. מאמנים מחדש את המודל בנתונים.

מודל זיהוי ספאם לאימון מראש

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

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

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

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

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

לוחצים על לחצן ההורדה של מודל ה-JS, כפי שמוצג למטה:

ab65deff89b3d939.png

4. תהליך ההגדרה

מה צריך?

  • דפדפן אינטרנט מודרני.
  • ידע בסיסי ב-HTML, CSS, JavaScript וכלי פיתוח של Chrome (בהצגת הפלט של המסוף).

קדימה, מתחילים לקודד.

יצרנו תבנית Glitch.com Node.js Express boilerplate כדי להתחיל ממנה את האפשרות לשכפל אותה כמצב הבסיס של שיעור ה-Lab הזה בקליק אחד.

ב-Glick פשוט לוחצים על הלחצן "remix this" כדי לפצל אותו וליצור קבוצה חדשה של קבצים שאפשר לערוך.

שלד פשוט מאוד מספק לנו את הקבצים הבאים בתיקייה www:

  1. דף HTML (index.html)
  2. גיליון סגנונות (style.css)
  3. קובץ לכתיבת קוד JavaScript (script.js)

לנוחיותך, הוספנו גם בקובץ ה-HTML ייבוא לספריית TensorFlow.js, שנראה כך:

index.html

<!-- Import TensorFlow.js library -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@2.0.0/dist/tf.min.js" type="text/javascript"></script>

לאחר מכן, אנחנו מציגים את התיקייה www באמצעות שרת Node Express פשוט דרך package.json ו-server.js

5. תבנית רגילה של HTML לאפליקציה

מהי נקודת ההתחלה?

לכל אבות הטיפוס יש צורך בפיגומים HTML בסיסיים, שבעזרתם אפשר להעביר את הממצאים. להגדרה עכשיו. בחרת להוסיף:

  • כותרת לדף
  • טקסט תיאורי
  • סרטון placeholder שמייצג את הרשומה של הבלוג
  • אזור להצגה ולהקלדה של תגובות

כדי להגדיר את התכונות שלמעלה, צריך לפתוח את index.html ולהדביק את הקוד הבא:

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>My Pretend Video Blog</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Import the webpage's stylesheet -->
    <link rel="stylesheet" href="/style.css">
  </head>  
  <body>
    <header>
      <h1>MooTube</h1>
      <a id="login" href="#">Login</a>
    </header>
    
    <h2>Check out the TensorFlow.js rap for the show and tell!</h2>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ipsum quam, tincidunt et tempor in, pulvinar vel urna. Nunc eget erat pulvinar, lacinia nisl in, rhoncus est. Morbi molestie vestibulum nunc. Integer non ipsum dolor. Curabitur condimentum sem eget odio dapibus, nec bibendum augue ultricies. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Sed iaculis ut ligula sed tempor. Phasellus ac dictum felis. Integer arcu dui, facilisis sit amet placerat sagittis, blandit sit amet risus.</p>
    
    <iframe width="100%" height="500" src="https://www.youtube.com/embed/RhVs7ijB17c" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

    <section id="comments" class="comments">
      <div id="comment" class="comment" contenteditable></div>
      <button id="post" type="button">Comment</button>

      <ul id="commentsList">
        <li>
          <span class="username">NotASpammer</span>
          <span class="timestamp">3/18/2021, 6:52:16 PM</span> 
          <p>I am not a spammer, I am a good boy.</p>
        </li>
        <li>
          <span class="username">SomeUser</span>
          <span class="timestamp">2/11/2021, 3:10:00 PM</span>
          <p>Wow, I love this video, so many amazing demos!</p>
        </li>
      </ul>
    </section>
    

    <!-- Import TensorFlow.js library -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@2.0.0/dist/tf.min.js" type="text/javascript"></script>

    <!-- Import the page's JavaScript to do some stuff -->
    <script type="module" src="/script.js"></script>
  </body>
</html>

פירוט

נקטע חלק מקוד ה-HTML שלמעלה כדי להדגיש פריטים חשובים שהוספת.

  • הוספת תג <h1> לכותרת הדף יחד עם תג <a> ללחצן ההתחברות שכלול ב<header>. לאחר מכן הוספת <h2> לכותרת המאמר ותג <p> לתיאור הסרטון. אין כאן שום דבר מיוחד.
  • הוספת תג iframe שמטמיע סרטון שרירותי ב-YouTube. נכון לעכשיו, אתם משתמשים ברכיב למעשה, באתר ההפקה, כל הערכים האלה יעובדו באופן דינמי על ידי הקצה העורפי, בהתאם לדף שמוצג.
  • לבסוף, הוספתם section עם מזהה ומחלקה של "comments" המכילות div כדי לכתוב תגובות חדשות, ביחד עם button, כדי לשלוח את התגובה החדשה ביחד עם רשימה לא ממוינת של תגובות. יש לך את שם המשתמש ושעת הפרסום בתוך תג span בתוך כל פריט ברשימה, ולבסוף בתגובה עצמה בתג p. שתי תגובות לדוגמה קודדו בשלב זה כ-placeholder.

אם רואים תצוגה מקדימה של הפלט עכשיו, הוא אמור להיראות בערך כך:

73c8338334d5b251.png

זה נראה די נורא, אז הגיע הזמן להוסיף סגנון...

6. הוספת סגנון

ברירות המחדל של הרכיב

ראשית, מוסיפים סגנונות לרכיבי ה-HTML שהוספתם כדי להבטיח שהם יוצגו כראוי.

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

style.css

/* http://meyerweb.com/eric/tools/css/reset/ 
   v2.0 | 20110126
   License: none (public domain)
*/
a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:'';content:none}table{border-collapse:collapse;border-spacing:0}

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

צריך להוסיף את הערכים הבאים לסוף style.css מתחת לקוד ה-CSS אופס שנוסף למעלה:

style.css

/* CSS files add styling rules to your content */
body {
  background: #212121;
  color: #fff;
  font-family: helvetica, arial, sans-serif;
}

header {
  background: linear-gradient(0deg, rgba(7,7,7,1) 0%, rgba(7,7,7,1) 85%, rgba(55,52,54,1) 100%);
  min-height: 30px;
  overflow: hidden;
}

h1 {
  color: #f0821b;
  font-size: 24pt;
  padding: 15px 25px;
  display: inline-block;
  float: left;
}

h2, p, section, iframe {
  background: #212121;
  padding: 10px 25px;
}

h2 {
  font-size: 16pt;
  padding-top: 25px;
}

p {
  color: #cdcdcd;
}

iframe {
  display: block;
  padding: 15px 0;
}

header a, button {
  color: #222;
  padding: 7px;
  min-width: 100px;
  background: rgb(240, 130, 30);
  border-radius: 3px;
  border: 1px solid #3d3d3d;
  text-transform: uppercase;
  font-weight: bold;
  cursor: pointer;
  transition: background 300ms ease-in-out;
}

header a {
  background: #efefef;
  float: right;
  margin: 15px 25px;
  text-decoration: none;
  text-align: center;
}

button:focus, button:hover, header a:hover {
  background: rgb(260, 150, 50);
}

.comment {
  background: #212121;
  border: none;
  border-bottom: 1px solid #888;
  color: #fff;
  min-height: 25px;
  display: block;
  padding: 5px;
}

.comments button {
  float: right;
  margin: 5px 0;
}

.comments button, .comment {
  transition: opacity 500ms ease-in-out;
}

.comments ul {
  clear: both;
  margin-top: 60px;
}

.comments ul li {
  margin-top: 5px;
  padding: 10px;
  transition: background 500ms ease-in-out;
}

.comments ul li * {
  background: transparent;
}

.comments ul li:nth-child(1) {
  background: #313131;
}

.comments ul li:hover {
  background: rgb(70, 60, 10);
}

.username, .timestamp {
  font-size: 80%;
  margin-right: 5px;
}

.username {
  font-weight: bold;
}

.processing {
  opacity: 0.3;
  filter: grayscale(1);
}

.comments ul li.spam {
  background-color: #d32f2f;
}

.comments ul li.spam::after {
  content: "⚠";
  margin: -17px 2px;
  zoom: 3;
  float: right;
}

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

b86be8e2f6e7456.png

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

7. JavaScript: מניפולציה של DOM, handler של אירועים

הפניה לרכיבי DOM עיקריים

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

מתחילים בהחלפה של התוכן ב-script.js בקבועים הבאים:

Script.js

const POST_COMMENT_BTN = document.getElementById('post');
const COMMENT_TEXT = document.getElementById('comment');
const COMMENTS_LIST = document.getElementById('commentsList');
// CSS styling class to indicate comment is being processed when
// posting to provide visual feedback to users.
const PROCESSING_CLASS = 'processing';

// Store username of logged in user. Right now you have no auth
// so default to Anonymous until known.
var currentUserName = 'Anonymous';

פרסום פוסטים באמצעות תגובות

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

script.js

/** 
 * Function to handle the processing of submitted comments.
 **/
function handleCommentPost() {
  // Only continue if you are not already processing the comment.
  if (! POST_COMMENT_BTN.classList.contains(PROCESSING_CLASS)) {
    POST_COMMENT_BTN.classList.add(PROCESSING_CLASS);
    COMMENT_TEXT.classList.add(PROCESSING_CLASS);
    let currentComment = COMMENT_TEXT.innerText;
    console.log(currentComment);
    
    // TODO: Fill out the rest of this function later.
  }
}

POST_COMMENT_BTN.addEventListener('click', handleCommentPost);

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

827b5f3d09afbb21.png

עכשיו יש לכם שלד HTML / CSS / JS בסיסי, והגיע הזמן להפנות את תשומת הלב שלכם למודל הלמידה החישובית כדי שתוכלו לשלב זאת בדף האינטרנט היפהפה.

8. הצגת מודל הלמידה החישובית

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

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

5634d536ef8be9ca.png

מה יש לכם כאן?

  • model.json - זהו אחד מהקבצים שמהם מורכב מודל TensorFlow.js. יש להפנות לקובץ הספציפי הזה מאוחר יותר בקוד TensorFlow.js.
  • group1-shard1of1.bin - זהו קובץ בינארי שמכיל את המשקלים המיומנים (למעשה, מקבץ מספרים שלמד טוב לבצע את משימת הסיווג שלו) של מודל TensorFlow.js. יש לאחסן אותו בשרת כלשהו כדי להוריד אותו.
  • vocab - הקובץ המוזר הזה ללא תוסף הוא משהו מ-Model Maker שמראה לנו איך לקודד מילים במשפטים כדי שהמודל יבין איך להשתמש בהם. נפרט את הנושא הזה בקטע הבא.
  • labels.txt - השם הזה מכיל רק את שמות הכיתות ש המודל יחזה. עבור המודל הזה אם פותחים את הקובץ הזה בעורך הטקסט הוא פשוט מופיע "false" &"true" מציין "לא ספאם" או "spam" בתור פלט החיזוי.

אירוח קובצי הדגם של TensorFlow.js

תחילה יש להיכנס אל model.json ול-*.bin הקבצים שנוצרו בשרת אינטרנט, כדי שתהיה לך גישה אליהם דרך דף האינטרנט.

העלאת קבצים ל-Glitch

  1. לוחצים על התיקייה נכסים בחלונית הימנית של פרויקט Glitch.
  2. לוחצים על העלאת נכס ובוחרים באפשרות group1-shard1of1.bin להעלאה לתיקייה הזו. לאחר ההעלאה, הוא אמור להיראות כך:

25a2251c7f165184.png

  1. נהדר! עכשיו צריך לחזור על אותה פעולה בקובץ model.json. שני קבצים צריכים להיות בתיקיית הנכסים שלכם:

51a6dbd5d3097ffc.png

  1. לוחצים על הקובץ group1-shard1of1.bin שהעליתם. תוכלו להעתיק את כתובת ה-URL אל המיקום שלה. העתקת הנתיב הזה כפי שהוא מוצג כאן:

92ded8d46442c404.png

  1. עכשיו בפינה הימנית התחתונה של המסך, לוחצים על כלים > מסוף. יש להמתין לטעינה של חלון המסוף. לאחר הטעינה, יש להקליד את הטקסט הבא ולאחר מכן להקיש על Enter כדי לשנות את הספרייה לתיקייה www:

טרמינל:

cd www
  1. לאחר מכן, יש להשתמש ב-wget כדי להוריד את שני הקבצים שהועלו עכשיו על ידי החלפת כתובות ה-URL שלמטה עבור הקבצים שיצרת לקבצים בתיקיית הנכסים ב-Glitch (יש לבדוק בתיקיית הנכסים של כל כתובת URL מותאמת אישית של כל קובץ). שימו לב שהרווח בין שתי כתובות ה-URL יהיה שונה מזה של כתובות ה-URL שבהן תרצו להשתמש, אבל הוא ייראה דומה:

טרמינל

wget https://cdn.glitch.com/1cb82939-a5dd-42a2-9db9-0c42cab7e407%2Fmodel.json?v=1616111344958 https://cdn.glitch.com/1cb82939-a5dd-42a2-9db9-0c42cab7e407%2Fgroup1-shard1of1.bin?v=1616017964562

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

  1. מקלידים ls במסוף ומקישים על Enter. תראו משהו כזה:

9cc90f1d053f517f.png

  1. באמצעות הפקודה mv אפשר לשנות את שמות הקבצים. הקלד את הטקסט הבא במסוף והקש על <kbd>Enter</kbd> או <kbd>return</kbd> אחרי כל שורה:

טרמינל:

mv *group1-shard1of1.bin* group1-shard1of1.bin
mv *model.json* model.json
  1. לבסוף, יש לרענן את פרויקט Glitch על ידי הקלדת refresh במסוף, ולחיצה על <kbd>Enter</kbd>:

טרמינל:

refresh
  1. לאחר הרענון, אמורים להופיע עכשיו model.json ו-group1-shard1of1.bin בתיקייה www של ממשק המשתמש:

50dd98c0a8f3e629.png

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

9. טעינה & שימוש במודל TensorFlow.js

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

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

מוסיפים את הקוד הבא לסוף קובץ ה-script.js, ומקפידים להחליף את ערך המחרוזת MODEL_JSON_URL בנתיב של הקובץ model.json שיצרתם כשמעלים את הקובץ לתיקיית הנכסים ב-Glick בשלב הקודם. (חשוב לזכור שניתן פשוט ללחוץ על הקובץ בתיקייה נכסים ב-Glitch כדי למצוא את כתובת האתר שלו).

כדי להבין מה קורה בכל שורה, יש לעיין בתגובות של הקוד החדש:

script.js

// Set the URL below to the path of the model.json file you uploaded.
const MODEL_JSON_URL = 'model.json';
// Set the minimum confidence for spam comments to be flagged.
// Remember this is a number from 0 to 1, representing a percentage
// So here 0.75 == 75% sure it is spam.
const SPAM_THRESHOLD = 0.75;

// Create a variable to store the loaded model once it is ready so 
// you can use it elsewhere in the program later.
var model = undefined;


/** 
 * Asynchronous function to load the TFJS model and then use it to
 * predict if an input is spam or not spam.
 */
async function loadAndPredict(inputTensor) {
  // Load the model.json and binary files you hosted. Note this is 
  // an asynchronous operation so you use the await keyword
  if (model === undefined) {
    model = await tf.loadLayersModel(MODEL_JSON_URL);
  }
  
  // Once model has loaded you can call model.predict and pass to it
  // an input in the form of a Tensor. You can then store the result.
  var results = await model.predict(inputTensor);
  
  // Print the result to the console for us to inspect.
  results.print();
  
  // TODO: Add extra logic here later to do something useful
}

loadAndPredict(tf.tensor([[1,3,12,18,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]));

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

e72acc17383bec33.png

במסוף יוצגו שני מספרים:

  1. 0.9996011
  2. 0.0003989

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

אם פותחים את קובץ ה-labels.txt שלך מקובצי הדגם שהורדת למערכת במחשב המקומי, יופיעו גם בו שני שדות:

  1. False
  2. True

לכן, לפי המודל הזה 99.96011% בטוחים (מוצג באובייקט התוצאה בתור 0.9996011) שהקלט שמסרת (לא [1,3,12,18,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] היה לא ספאם (כלומר False).

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

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

10. אסימון ו &חיישן

יצירת אסימונים

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

בנוסף, התהליך המדויק משתנה ממודל למודל, אבל בשביל זה יש קובץ אחד נוסף בקובצי המודלים שהורדת בשם vocab,, וזהו המפתח לאופן שבו מקודדים הנתונים.

כדי לפתוח את vocab בעורך טקסט מקומי, יש לפתוח את הקובץ במחשב ולראות משהו כזה:

81e8bca3fbf62429.png

למעשה, זוהי טבלת חיפוש של האופן שבו ניתן להמיר מילים משמעותיות שהמודל למד למספרים שהם יכולים להבין. יש גם כמה מקרים מיוחדים בחלק העליון של הקובץ <PAD>, <START> ו-<UNKNOWN>:

  • <PAD> - זהו קיצור של "מרווח". מסתבר שמודלים של למידה חישובית כמו מספר קלט קבוע, לא משנה כמה זמן המשפט שלך מסתיים. המודל המשמש מצפה שתמיד יהיו 20 מספרים לקלט (כפי שמוגדר על ידי יוצר המודל, וניתן לשנות אותו אם מאמנים מחדש את המודל). לכן, אם יש ביטוי כמו "סימון 'אהבתי'&וידאו; צריך למלא את כל הרווחים במערך מ-0&39; שמייצגים את האסימון מסוג <PAD>. אם המשפט גדול מ-20 מילים, צריך לפצל אותו כך שיתאים לדרישה הזו, ובמקום זאת לבצע כמה סיווגים במשפטים קטנים רבים.
  • <START> - זהו תמיד האסימון הראשון שמציין את תחילת המשפט. בקלט לדוגמה, בשלבים הקודמים, יופיע מערך המספרים שהתחיל עם "1" - זה מייצג את האסימון <START>.
  • <UNKNOWN> – כפי שוודאי ניחשת, אם המילה לא קיימת בחיפוש הזה של מילים, עליך להשתמש באסימון <UNKNOWN> (שמיוצג על ידי "2" ).

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

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       Take another look at the input used in the prior code you ran:

[1,3,12,18,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]

עכשיו אפשר לראות שמשפט זה כולל 4 מילים, מאחר שהשאר הוא אסימונים <START> או <PAD> ויש 20 מספרים במערך. בסדר, קצת יותר הגיוני.

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

חיישנים

יש מגבלה אחת לפני שמודל ה-ML יקבל את הקלט המספרי שלך. עליכם להמיר את מערך המספרים ל-Tenoror, וניחתם נכון, השם TensorFlow נקרא על שם המילה - Flower of Tensors ('מהותם של בני הזוג') דרך דגם שבבסיסו.

מה זה טנור?

ההגדרה הרשמית של TensorFlow.org:

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

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

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

מחברים הכול יחד: קודרים וטנסיזציה

אז איך משתמשים בקובץ vocab הזה בקוד? שאלה מעולה!

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

// Special cases. Export as constants.
export const PAD =  0;
export const START = 1;
export const UNKNOWN = 2;

// Export a lookup object.
export const LOOKUP = {
  "i": 3,
  "check": 4,
  "video": 5,
  "song": 6,
  "com": 7,
  "please": 8,
  "like": 9
  // and all the other words...
}

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

ביצוע פעולה זו מראש ושמירת הקובץ vocab בפורמט הנכון מונעים ממך לבצע את ההמרה והניתוח בכל טעינה של הדף – תהליך מיותר של משאבי מעבד (CPU). ואם כל זה לא מספיק, אובייקטים של JavaScript כוללים את המאפיינים הבאים:

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

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

שימוש בפורמט מועיל יותר

תוכלו להמיר את קובץ ה-vocab בפורמט שלמעלה באופן ידני באמצעות עורך הטקסט או באמצעות הכלי הזה. שמור את הפלט המתקבל בתור dictionary.js בתוך התיקייה www.

ב-Glick אפשר פשוט ליצור קובץ חדש במיקום הזה ולהדביק את התוצאה של ההמרה כדי לשמור אותה כפי שמוצג:

c80f68535c92baf.gif

לאחר ששמרת קובץ dictionary.js בפורמט המתואר למעלה, עכשיו אפשר להוסיף את הקוד הבא בחלק העליון של דף script.js כדי לייבא את המודול dictionary.js שכתבת. כאן צריך להגדיר גם ENCODING_LENGTH קבוע נוסף, כדי שתדעו עד כמה כדאי להזין את הקוד מאוחר יותר בקוד הפונקציה, יחד עם פונקציה tokenize שבה אתם משתמשים כדי להמיר מערך מילים לטנור מתאים שישמש כקלט למודל.

בתגובות הבאות אפשר לראות פרטים נוספים לגבי כל שורה:

script.js

import * as DICTIONARY from '/dictionary.js';

// The number of input elements the ML Model is expecting.
const ENCODING_LENGTH = 20;


/** 
 * Function that takes an array of words, converts words to tokens,
 * and then returns a Tensor representation of the tokenization that
 * can be used as input to the machine learning model.
 */
function tokenize(wordArray) {
  // Always start with the START token.
  let returnArray = [DICTIONARY.START];
  
  // Loop through the words in the sentence you want to encode.
  // If word is found in dictionary, add that number else
  // you add the UNKNOWN token.
  for (var i = 0; i < wordArray.length; i++) {
    let encoding = DICTIONARY.LOOKUP[wordArray[i]];
    returnArray.push(encoding === undefined ? DICTIONARY.UNKNOWN : encoding);
  }
  
  // Finally if the number of words was < the minimum encoding length
  // minus 1 (due to the start token), fill the rest with PAD tokens.
  while (i < ENCODING_LENGTH - 1) {
    returnArray.push(DICTIONARY.PAD);
    i++;
  }
  
  // Log the result to see what you made.
  console.log([returnArray]);
  
  // Convert to a TensorFlow Tensor and return that.
  return tf.tensor([returnArray]);
}

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

צפייה בקוד של התגובות לתוכן שהוספתם:

script.js

/** 
 * Function to handle the processing of submitted comments.
 **/
function handleCommentPost() {
  // Only continue if you are not already processing the comment.
  if (! POST_COMMENT_BTN.classList.contains(PROCESSING_CLASS)) {
    // Set styles to show processing in case it takes a long time.
    POST_COMMENT_BTN.classList.add(PROCESSING_CLASS);
    COMMENT_TEXT.classList.add(PROCESSING_CLASS);
    
    // Grab the comment text from DOM.
    let currentComment = COMMENT_TEXT.innerText;
    // Convert sentence to lower case which ML Model expects
    // Strip all characters that are not alphanumeric or spaces
    // Then split on spaces to create a word array.
    let lowercaseSentenceArray = currentComment.toLowerCase().replace(/[^\w\s]/g, ' ').split(' ');
    
    // Create a list item DOM element in memory.
    let li = document.createElement('li');
    
    // Remember loadAndPredict is asynchronous so you use the then 
    // keyword to await a result before continuing.
    loadAndPredict(tokenize(lowercaseSentenceArray), li).then(function() {
      // Reset class styles ready for the next comment.
      POST_COMMENT_BTN.classList.remove(PROCESSING_CLASS);
      COMMENT_TEXT.classList.remove(PROCESSING_CLASS);
      
      let p = document.createElement('p');
      p.innerText = COMMENT_TEXT.innerText;
      
      let spanName = document.createElement('span');
      spanName.setAttribute('class', 'username');
      spanName.innerText = currentUserName;
      
      let spanDate = document.createElement('span');
      spanDate.setAttribute('class', 'timestamp');
      let curDate = new Date();
      spanDate.innerText = curDate.toLocaleString();
      
      li.appendChild(spanName);
      li.appendChild(spanDate);
      li.appendChild(p);
      COMMENTS_LIST.prepend(li);

      // Reset comment text.
      COMMENT_TEXT.innerText = '';
    });
  }
}

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

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

script.js

/** 
 * Asynchronous function to load the TFJS model and then use it to
 * predict if an input is spam or not spam.
 */
async function loadAndPredict(inputTensor, domComment) {
  // Load the model.json and binary files you hosted. Note this is 
  // an asynchronous operation so you use the await keyword
  if (model === undefined) {
    model = await tf.loadLayersModel(MODEL_JSON_URL);
  }
  
  // Once model has loaded you can call model.predict and pass to it
  // an input in the form of a Tensor. You can then store the result.
  var results = await model.predict(inputTensor);
  
  // Print the result to the console for us to inspect.
  results.print();

  results.data().then((dataArray)=>{
    if (dataArray[1] > SPAM_THRESHOLD) {
      domComment.classList.add('spam');
    }
  })
}

11. עדכונים בזמן אמת: Node.js + Websockets

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

Socket.io

Socket.io היא אחת הדרכים הפופולריות ביותר (בזמן כתיבה) להשתמש ב-websocket עם Node.js. יש לומר ל-Glitch שרוצים לכלול את הספרייה של Socket.io ב-build על ידי עריכת package.json בספרייה ברמה העליונה (בתיקיית ההורה של התיקייה www) כך לכלול את socket.io כאחד מהתלויים:

package. json

{
  "name": "tfjs-with-backend",
  "version": "0.0.1",
  "description": "A TFJS front end with thin Node.js backend",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.17.1",
    "socket.io": "^4.0.1"
  },
  "engines": {
    "node": "12.x"
  }
}

נהדר! לאחר העדכון, עדכון הבא index.html בתוך התיקייה www יכלול את ספריית socket.io.

פשוט צריך להוסיף את שורת הקוד הזו מעל קובץ הייבוא של תג HTML ל-script.js ליד סוף הקובץ index.html:

index.html

<script src="/socket.io/socket.io.js"></script>

עכשיו אמורים להיות 3 תגי סקריפט בקובץ index.html:

  • הייבוא הראשון של ספריית TensorFlow.js
  • הייבוא השני של socket.io שהוספת עכשיו
  • והאחרון צריך לייבא את קוד Script.js.

בשלב הבא, יש לערוך את server.js כדי להגדיר את socket.io בתוך הצומת וליצור קצה עורפי פשוט כדי להעביר הודעות שמתקבלות לכל הלקוחות המחוברים.

הערות לגבי הקוד מפורטות בהמשך כדי לקבל הסבר על הפעולה של קוד Node.js:

server.js

const http = require('http');
const express = require("express");
const app = express();
const server = http.createServer(app);

// Require socket.io and then make it use the http server above.
// This allows us to expose correct socket.io library JS for use
// in the client side JS.
var io = require('socket.io')(server);

// Serve all the files in 'www'.
app.use(express.static("www"));

// If no file specified in a request, default to index.html
app.get("/", (request, response) => {
  response.sendFile(__dirname + "/www/index.html");
});


// Handle socket.io client connect event.
io.on('connect', socket => {
  console.log('Client connected');

  // If you wanted you could emit existing comments from some DB
  // to client to render upon connect.
  // socket.emit('storedComments', commentObjectArray);  
 
  // Listen for "comment" event from a connected client.
  socket.on('comment', (data) => {
    // Relay this comment data to all other connected clients
    // upon receiving.
    socket.broadcast.emit('remoteComment', data);
  });
});


// Start the web server.
const listener = server.listen(process.env.PORT, () => {
  console.log("Your app is listening on port " + listener.address().port);
});

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

תחילה צריך להוסיף את הקוד הבא לסוף של script.js כדי להתחבר לשרת socket.io ולהאזין או לטפל באירועים שהתקבלו מרחוק:

script.js

// Connect to Socket.io on the Node.js backend.
var socket = io.connect();


function handleRemoteComments(data) {
  // Render a new comment to DOM from a remote client.
  let li = document.createElement('li');
  let p = document.createElement('p');
  p.innerText = data.comment;

  let spanName = document.createElement('span');
  spanName.setAttribute('class', 'username');
  spanName.innerText = data.username;

  let spanDate = document.createElement('span');
  spanDate.setAttribute('class', 'timestamp');
  spanDate.innerText = data.timestamp;

  li.appendChild(spanName);
  li.appendChild(spanDate);
  li.appendChild(p);
  
  COMMENTS_LIST.prepend(li);
}


// Add event listener to receive remote comments that passed
// spam check.
socket.on('remoteComment', handleRemoteComments);

לסיום, מוסיפים קוד לפונקציה loadAndPredict כדי לדמות אירוע socket.io אם התגובה לא ספאם. כך אפשר לעדכן את הלקוחות המחוברים האחרים בתגובה הזו, כי התוכן של ההודעה הזו יועבר אליהם דרך הקוד של server.js שצוין למעלה.

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

script.js

/** 
 * Asynchronous function to load the TFJS model and then use it to
 * predict if an input is spam or not spam. The 2nd parameter
 * allows us to specify the DOM element list item you are currently
 * classifying so you can change it+s style if it is spam!
 */
async function loadAndPredict(inputTensor, domComment) {
  // Load the model.json and binary files you hosted. Note this is 
  // an asynchronous operation so you use the await keyword
  if (model === undefined) {
    model = await tf.loadLayersModel(MODEL_JSON_URL);
  }
  
  // Once model has loaded you can call model.predict and pass to it
  // an input in the form of a Tensor. You can then store the result.
  var results = await model.predict(inputTensor);
  
  // Print the result to the console for us to inspect.
  results.print();

  results.data().then((dataArray)=>{
    if (dataArray[1] > SPAM_THRESHOLD) {
      domComment.classList.add('spam');
    } else {
      // Emit socket.io comment event for server to handle containing
      // all the comment data you would need to render the comment on
      // a remote client's front end.
      socket.emit('comment', {
        username: currentUserName,
        timestamp: domComment.querySelectorAll('span')[1].innerText,
        comment: domComment.querySelectorAll('p')[0].innerText
      });
    }
  })
}

מצוין! אם ביצעת מעקב תקין, עכשיו תהיה לך אפשרות לפתוח שני מופעים של הדף של index.html.

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

ee0f13398ea4e91e.gif

12. מזל טוב

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

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

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

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

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

סיכום

ב-codelab הזה:

  1. למדה מהי TensorFlow.js ומהם המודלים הקיימים לעיבוד שפה טבעית
  2. יצרתם אתר פיקטיבי שמאפשר להוסיף תגובות בזמן אמת לאתר לדוגמה.
  3. בוצעה טעינה של מודל למידה חישובית מוכן מראש המתאים לזיהוי תגובות ספאם באמצעות TensorFlow.js בדף האינטרנט.
  4. תלמדו איך לקודד משפטים לשימוש במודל הלמידה החישובית שנטען ולתוחם את הקידוד הזה ב-Tensor.
  5. לפרש את הפלט של מודל הלמידה החישובית כדי להחליט אם להשהות את התגובה לבדיקה, ואם לא, לשלוח אותה לשרת על מנת להעביר אותה ללקוחות קשורים אחרים בזמן אמת.

מה עכשיו?

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

משתפים איתנו את המתכונים

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

אל תשכחו לתייג אותנו ברשתות החברתיות באמצעות ה-hashtag #MadeWithTFJS כדי שהפרויקט שלכם יוצג בבלוג TensorFlow או אפילו באירועים עתידיים. נשמח לראות מה מכינים.

ניתוח מעמיק יותר של TensorFlow.js

אתרים שכדאי לבדוק