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

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

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

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

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

מה [תבנו או תלמדו]

  • בשלבים הבאים נסביר איך לשלב את המודל המותאם אישית באפליקציה שיצרתם בשלבים הקודמים.

הדרישות

2. פותחים את אפליקציית Android הקיימת.

אפשר לקבל את הקוד הזה על ידי ביצוע ההוראות ב-Codelab 1, או על ידי שיבוט המאגר הזה וטעינת האפליקציה מ-TextClassificationStep1.

git clone https://github.com/googlecodelabs/odml-pathways

אפשר למצוא את המידע הזה בנתיב TextClassificationOnMobile->Android.

קוד הסיום זמין גם בשבילכם בתור TextClassificationStep2.

אחרי שהאפליקציה נפתחת, אפשר לעבור לשלב 2.

3. ייבוא של קובץ המודל והמטא-נתונים

במעבדת התכנות Build a comment spam machine learning model (יצירת מודל של למידת מכונה לזיהוי ספאם בתגובות), יצרתם מודל ‎ .TFLITE.

הורדתם את קובץ המודל. אם אין לכם את הקובץ, תוכלו להוריד אותו ממאגר ה-codelab הזה. המודל זמין כאן.

כדי להוסיף אותו לפרויקט, יוצרים תיקיית נכסים.

  1. בחלונית הניווט בפרויקט, מוודאים שהאפשרות Android (אנדרואיד) מסומנת בחלק העליון.
  2. לוחצים לחיצה ימנית על התיקייה app. בוחרים באפשרות חדש > ספרייה.

d7c3e9f21035fc15.png

  1. בתיבת הדו-שיח New Directory (ספרייה חדשה), בוחרים באפשרות src/main/assets.

2137f956a1ba4ef0.png

תיקייה חדשה בשם assets זמינה עכשיו באפליקציה.

ae858835e1a90445.png

  1. לוחצים לחיצה ימנית על נכסים דיגיטליים.
  2. בתפריט שנפתח, לוחצים על הצגה ב-Finder (ב-Mac). בוחרים אותה. (ב-Windows יופיע הכיתוב הצגה בסייר, וב-Ubuntu יופיע הכיתוב הצגה בקבצים).

e61aaa3b73c5ab68.png

ייפתח Finder כדי להציג את מיקום הקבצים (סייר הקבצים ב-Windows, קבצים ב-Linux).

  1. מעתיקים את הקבצים labels.txt, model.tflite ו-vocab לספרייה הזו.

14f382cc19552a56.png

  1. חוזרים אל Android Studio, והקבצים יהיו זמינים בתיקייה assets.

150ed2a1d2f7a10d.png

4. עדכון של build.gradle כדי להשתמש ב-TensorFlow Lite

כדי להשתמש ב-TensorFlow Lite ובספריות המשימות של TensorFlow Lite שתומכות בו, צריך לעדכן את הקובץ build.gradle.

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

6426051e614bc42f.png

תצטרכו לבצע שני שינויים בקובץ הזה. הראשון נמצא בקטע dependencies בתחתית. מוסיפים טקסט implementation לספריית המשימות של TensorFlow Lite, באופן הבא:

implementation 'org.tensorflow:tensorflow-lite-task-text:0.1.0'

יכול להיות שמספר הגרסה השתנה מאז כתיבת המאמר הזה, לכן חשוב לבדוק את הכתובת https://www.tensorflow.org/lite/inference_with_metadata/task_library/nl_classifier כדי לראות את הגרסה העדכנית.

בנוסף, כדי להשתמש בספריות המשימות נדרשת גרסת SDK מינימלית של 21. כדי לשנות את ההגדרה הזו, עוברים אל android > default config ומשנים אותה ל-21:

c100b68450b8812f.png

עכשיו יש לכם את כל התלויות, אז הגיע הזמן להתחיל לכתוב קוד!

5. הוספת מחלקה מסייעת

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

  1. לוחצים לחיצה ימנית על שם החבילה שבה נמצא קוד MainActivity.
  2. בוחרים באפשרות חדש > חבילה.

d5911ded56b5df35.png

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

3b9f1f822f99b371.png

  1. אחרי שמסיימים, לוחצים לחיצה ימנית על התיקייה helpers בסייר הפרויקטים.
  2. בוחרים באפשרות New > Java Class (חדש > מחלקה ב-Java) וקוראים לה TextClassificationClient. בשלב הבא תערכו את הקובץ.

מחלקת העזר שלך ב-TextClassificationClient תיראה כך (אבל שם החבילה שלך עשוי להיות שונה).

package com.google.devrel.textclassificationstep1.helpers;

public class TextClassificationClient {
}
  1. מעדכנים את הקובץ עם הקוד הזה:
package com.google.devrel.textclassificationstep2.helpers;

import android.content.Context;
import android.util.Log;
import java.io.IOException;
import java.util.List;

import org.tensorflow.lite.support.label.Category;
import org.tensorflow.lite.task.text.nlclassifier.NLClassifier;

public class TextClassificationClient {
    private static final String MODEL_PATH = "model.tflite";
    private static final String TAG = "CommentSpam";
    private final Context context;

    NLClassifier classifier;

    public TextClassificationClient(Context context) {
        this.context = context;
    }

    public void load() {
        try {
            classifier = NLClassifier.createFromFile(context, MODEL_PATH);
        } catch (IOException e) {
            Log.e(TAG, e.getMessage());
        }
    }

    public void unload() {
        classifier.close();
        classifier = null;
    }

    public List<Category> classify(String text) {
        List<Category> apiResults = classifier.classify(text);
        return apiResults;
    }

}

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

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

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

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

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

6. סיווג הטקסט

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

  1. בחלק העליון של MainActivity.kt, מוסיפים את השורה הבאה לצד שאר הייבוא:
import com.google.devrel.textclassificationstep2.helpers.TextClassificationClient
import org.tensorflow.lite.support.label.Category
  1. בשלב הבא, צריך לטעון את העוזרים. ב-onCreate, מיד אחרי השורה setContentView, מוסיפים את השורות הבאות כדי ליצור מופע של מחלקת העזר ולטעון אותה:
val client = TextClassificationClient(applicationContext)
client.load()

בשלב הזה, הלחצן onClickListener אמור להיראות כך:

btnSendText.setOnClickListener {
     var toSend:String = txtInput.text.toString()
     txtOutput.text = toSend
 }
  1. מעדכנים אותו כך שייראה כמו הדוגמה הבאה:
btnSendText.setOnClickListener {
    var toSend:String = txtInput.text.toString()
    var results:List<Category> = client.classify(toSend)
    val score = results[1].score
    if(score>0.8){
        txtOutput.text = "Your message was detected as spam with a score of " + score.toString() + " and not sent!"
    } else {
        txtOutput.text = "Message sent! \nSpam score was:" + score.toString()
    }
    txtInput.text.clear()
}

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

  1. בעזרת השורה הזו, לוקחים את המחרוזת שהמשתמש הזין ומעבירים אותה למודל, ומקבלים בחזרה תוצאות:
var results:List<Category> = client.classify(toSend)

יש רק 2 קטגוריות, False ו-True

. (TensorFlow ממיין אותם לפי סדר אלפביתי, כך ש-False יהיה פריט 0 ו-True יהיה פריט 1).

  1. כדי לקבל את הציון של ההסתברות שהערך הוא True, אפשר לעיין בתוצאות[1].score כך:
    val score = results[1].score
  1. הגדרתם ערך סף (במקרה הזה 0.8), שאם הציון של הקטגוריה True גבוה ממנו (0.8), ההודעה היא ספאם. אחרת, ההודעה לא נחשבת לספאם ואפשר לשלוח אותה בבטחה:
    if(score>0.8){
        txtOutput.text = "Your message was detected as spam with a score of " + score.toString() + " and not sent!"
    } else {
        txtOutput.text = "Message sent! \nSpam score was:" + score.toString()
    }
  1. כאן אפשר לראות את המודל בפעולה. ההודעה "כדאי לבקר בבלוג שלי כדי לקנות דברים!" סומנה כספאם בדרגת סבירות גבוהה:

1fb0b5de9e566e.png

לעומת זאת, ההודעה 'היי, מדריך כיפי, תודה!' נחשבה כספאם בסבירות נמוכה מאוד:

73f38bdb488b29b3.png

7. עדכון אפליקציית iOS לשימוש במודל TensorFlow Lite

אפשר לקבל את הקוד הזה על ידי ביצוע ההוראות ב-Codelab 1, או על ידי שיבוט המאגר הזה וטעינת האפליקציה מ-TextClassificationStep1. אפשר למצוא את המידע הזה בנתיב TextClassificationOnMobile->iOS.

קוד הסיום זמין גם בשבילכם בתור TextClassificationStep2.

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

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

כדי להתחיל, צריך את האפליקציה משלב 1, שאפשר לשכפל מהמאגר.

כדי לשלב את TensorFlow Lite, תצטרכו להשתמש ב-CocoaPods. אם עדיין לא התקנתם אותם, תוכלו לעשות זאת באמצעות ההוראות בכתובת https://cocoapods.org/.

  1. אחרי שמתקינים את CocoaPods, יוצרים קובץ בשם Podfile באותה תיקייה שבה נמצא קובץ .xcproject של אפליקציית TextClassification. התוכן של הקובץ הזה צריך להיראות כך:
target 'TextClassificationStep2' do
  use_frameworks!

  # Pods for NLPClassifier
    pod 'TensorFlowLiteSwift'

end

שם האפליקציה צריך להיות בשורה הראשונה, במקום TextClassificationStep2.

באמצעות הטרמינל, עוברים לספרייה הזו ומריצים את הפקודה pod install. אם הפעולה תצליח, תיווצר ספרייה חדשה בשם Pods וקובץ .xcworkspace חדש. תשתמשו בו בעתיד במקום ב-.xcproject.

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

8. הוספת קובצי המודל והאוצר מילים

כשיוצרים את המודל באמצעות TensorFlow Lite Model maker, אפשר לייצא את המודל (כ-model.tflite) ואת אוצר המילים (כ-vocab.txt).

  1. מוסיפים אותם לפרויקט על ידי גרירה שלהם מ-Finder לחלון הפרויקט. מוודאים שהתיבה add to targets (הוספה ליעדים) מסומנת:

1ee9eaa00ee79859.png

בסיום התהליך, הם אמורים להופיע בפרויקט:

b63502b23911fd42.png

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

20b7cb603d49b457.png

9. טעינת אוצר המילים

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

אפשר לפתוח את הקובץ ב-Xcode כדי לראות את הקידודים. מילים כמו song מקודדות ל-6 ו-love ל-12. הסדר הוא למעשה סדר התדירות, כך שהמילה 'I' הייתה הנפוצה ביותר במערך הנתונים, ואחריה המילה 'check'.

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

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

  1. מגדירים משתנה ברמת הכיתה כדי לאחסן את המילון:
var words_dictionary = [String : Int]()
  1. לאחר מכן יוצרים func בכיתה כדי לטעון את אוצר המילים למילון הזה:
func loadVocab(){
    // This func will take the file at vocab.txt and load it into a has table
    // called words_dictionary. This will be used to tokenize the words before passing them
    // to the model trained by TensorFlow Lite Model Maker
    if let filePath = Bundle.main.path(forResource: "vocab", ofType: "txt") {
        do {
            let dictionary_contents = try String(contentsOfFile: filePath)
            let lines = dictionary_contents.split(whereSeparator: \.isNewline)
            for line in lines{
                let tokens = line.components(separatedBy: " ")
                let key = String(tokens[0])
                let value = Int(tokens[1])
                words_dictionary[key] = value
            }
        } catch {
            print("Error vocab could not be loaded")
        }
    } else {
        print("Error -- vocab file not found")

    }
}
  1. אפשר להריץ את הפקודה הזו על ידי הפעלתה מתוך viewDidLoad:
override func viewDidLoad() {
    super.viewDidLoad()
    txtInput.delegate = self
    loadVocab()
}

10. הפיכת מחרוזת לרצף של טוקנים

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

מודל NLP מקבל בדרך כלל רצף באורך קבוע. יש יוצאים מן הכלל במודלים שנבנו באמצעות ragged tensors, אבל ברוב המקרים תראו שהבעיה נפתרה. האורך הזה נקבע כשיוצרים את המודל. חשוב להשתמש באותו אורך באפליקציית iOS.

ברירת המחדל ב-Colab עבור TensorFlow Lite Model Maker שבו השתמשתם קודם הייתה 20, אז צריך להגדיר אותה גם כאן:

let SEQUENCE_LENGTH = 20

מוסיפים את המחרוזת func שתקבל את המחרוזת, תמיר אותה לאותיות קטנות ותסיר ממנה את סימני הפיסוק:

func convert_sentence(sentence: String) -> [Int32]{
// This func will split a sentence into individual words, while stripping punctuation
// If the word is present in the dictionary it's value from the dictionary will be added to
// the sequence. Otherwise we'll continue

// Initialize the sequence to be all 0s, and the length to be determined
// by the const SEQUENCE_LENGTH. This should be the same length as the
// sequences that the model was trained for
  var sequence = [Int32](repeating: 0, count: SEQUENCE_LENGTH)
  var words : [String] = []
  sentence.enumerateSubstrings(
    in: sentence.startIndex..<sentence.endIndex,options: .byWords) {
            (substring, _, _, _) -> () in words.append(substring!) }
  var thisWord = 0
  for word in words{
    if (thisWord>=SEQUENCE_LENGTH){
      break
    }
    let seekword = word.lowercased()
    if let val = words_dictionary[seekword]{
      sequence[thisWord]=Int32(val)
      thisWord = thisWord + 1
    }
  }
  return sequence
}

הערה: הרצף יהיה Int32. הבחירה הזו נעשית בכוונה, כי כשמעבירים ערכים ל-TensorFlow Lite, עובדים עם זיכרון ברמה נמוכה, ו-TensorFlow Lite מתייחס למספרים השלמים ברצף מחרוזות כמספרים שלמים של 32 ביט. כך יהיה לכם קל יותר להעביר מחרוזות למודל.

11. ביצוע הסיווג

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

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

הפעולה הזו תשתמש במפרש הפקודות של TensorFlow Lite, שצריך לייבא:

import TensorFlowLite

מתחילים עם func שמקבל את הרצף, שהוא מערך של סוגי Int32:

func classify(sequence: [Int32]){
  // Model Path is the location of the model in the bundle
  let modelPath = Bundle.main.path(forResource: "model", ofType: "tflite")
  var interpreter: Interpreter
  do{
    interpreter = try Interpreter(modelPath: modelPath!)
  } catch _{
    print("Error loading model!")
    return
  }

הפעולה הזו תטען את קובץ המודל מהחבילה ותפעיל איתו מתורגמן.

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

מתחילים את הקוד כך (עדיין ב-classify func.):

let tSequence = Array(sequence)
let myData = Data(copyingBufferOf: tSequence.map { Int32($0) })
let outputTensor: Tensor

אל דאגה אם מופיעה שגיאה ב-copyingBufferOf. האפשרות הזו תיושם כתוסף בהמשך.

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

do {
  // Allocate memory for the model's input `Tensor`s.
  try interpreter.allocateTensors()

  // Copy the data to the input `Tensor`.
  try interpreter.copy(myData, toInputAt: 0)

  // Run inference by invoking the `Interpreter`.
  try interpreter.invoke()

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

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

// Get the output `Tensor` to process the inference results.
outputTensor = try interpreter.output(at: 0)
// Turn the output tensor into an array. This will have 2 values
// Value at index 0 is the probability of negative sentiment
// Value at index 1 is the probability of positive sentiment
let resultsArray = outputTensor.data
let results: [Float32] = [Float32](unsafeData: resultsArray) ?? []

עכשיו קל יחסית לנתח את הנתונים כדי לקבוע את איכות הספאם. למודל יש 2 פלטים, הראשון עם ההסתברות שההודעה היא לא ספאם, והשני עם ההסתברות שהיא כן ספאם. לכן אפשר להסתכל על results[1] כדי למצוא את ערך הספאם:

let positiveSpamValue = results[1]
var outputString = ""
if(positiveSpamValue>0.8){
    outputString = "Message not sent. Spam detected with probability: " + String(positiveSpamValue)
} else {
    outputString = "Message sent!"
}
txtOutput.text = outputString

לנוחיותכם, הנה השיטה המלאה:

func classify(sequence: [Int32]){
  // Model Path is the location of the model in the bundle
  let modelPath = Bundle.main.path(forResource: "model", ofType: "tflite")
  var interpreter: Interpreter
  do{
    interpreter = try Interpreter(modelPath: modelPath!)
    } catch _{
      print("Error loading model!")
      Return
  }
  
  let tSequence = Array(sequence)
  let myData = Data(copyingBufferOf: tSequence.map { Int32($0) })
  let outputTensor: Tensor
  do {
    // Allocate memory for the model's input `Tensor`s.
    try interpreter.allocateTensors()

    // Copy the data to the input `Tensor`.
    try interpreter.copy(myData, toInputAt: 0)

    // Run inference by invoking the `Interpreter`.
    try interpreter.invoke()

    // Get the output `Tensor` to process the inference results.
    outputTensor = try interpreter.output(at: 0)
    // Turn the output tensor into an array. This will have 2 values
    // Value at index 0 is the probability of negative sentiment
    // Value at index 1 is the probability of positive sentiment
    let resultsArray = outputTensor.data
    let results: [Float32] = [Float32](unsafeData: resultsArray) ?? []

    let positiveSpamValue = results[1]
    var outputString = ""
    if(positiveSpamValue>0.8){
      outputString = "Message not sent. Spam detected with probability: " + 
                      String(positiveSpamValue)
    } else {
      outputString = "Message sent!"
    }
    txtOutput.text = outputString

  } catch let error {
    print("Failed to invoke the interpreter with error: \(error.localizedDescription)")
  }
}

12. הוספת תוספי Swift

הקוד שלמעלה השתמש בהרחבה של סוג הנתונים כדי לאפשר לכם להעתיק את הביטים הגולמיים של מערך Int32 אל Data. הנה הקוד של התוסף:

extension Data {
  /// Creates a new buffer by copying the buffer pointer of the given array.
  ///
  /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit
  ///     for bit with no indirection or reference-counting operations; otherwise, reinterpreting
  ///     data from the resulting buffer has undefined behavior.
  /// - Parameter array: An array with elements of type `T`.
  init<T>(copyingBufferOf array: [T]) {
    self = array.withUnsafeBufferPointer(Data.init)
  }
}

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

extension Array {
  /// Creates a new array from the bytes of the given unsafe data.
  ///
  /// - Warning: The array's `Element` type must be trivial in that it can be copied bit for bit
  ///     with no indirection or reference-counting operations; otherwise, copying the raw bytes in
  ///     the `unsafeData`'s buffer to a new array returns an unsafe copy.
  /// - Note: Returns `nil` if `unsafeData.count` is not a multiple of
  ///     `MemoryLayout<Element>.stride`.
  /// - Parameter unsafeData: The data containing the bytes to turn into an array.
  init?(unsafeData: Data) {
    guard unsafeData.count % MemoryLayout<Element>.stride == 0 else { return nil }
    #if swift(>=5.0)
    self = unsafeData.withUnsafeBytes { .init($0.bindMemory(to: Element.self)) }
    #else
    self = unsafeData.withUnsafeBytes {
      .init(UnsafeBufferPointer<Element>(
        start: $0,
        count: unsafeData.count / MemoryLayout<Element>.stride
      ))
    }
    #endif  // swift(>=5.0)
  }
}

13. הפעלת האפליקציה ל-iOS

מריצים את האפליקציה ובודקים אותה.

אם הכול עבר בצורה חלקה, האפליקציה אמורה להופיע במכשיר כך:

74cbd28d9b1592ed.png

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

14. מעולה!

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

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