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

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

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

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

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

מה תפַתחו או תלמדו

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

מה נדרש

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

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

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

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

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

אחרי שנפתחו, תוכלו לעבור לשלב 2.

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

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

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

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

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

d7c3e9f21035fc15.png

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

2137f956a1ba4ef0.png

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

ae858835e1a90445.png

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

e61aaa3b73c5ab68.png

Finder יופעל כדי להציג את מיקום הקבצים (File Explorer ב-Windows, ‏ Files ב-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. בוחרים באפשרות New (חדש) > Package (חבילה).

d5911ded56b5df35.png

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

3b9f1f822f99b371.png

  1. בסיום, לוחצים לחיצה ימנית על התיקייה helpers ב-project Explorer.
  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 הוא חלק מספריות המשימות של טקסט, והוא עוזר לכם להמיר את המחרוזת לאסימונים, להשתמש באורך הרצף הנכון, להעביר אותה למודל ולנתח את התוצאות.

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

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

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

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

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

  1. בחלק העליון של MainActivity.kt, יחד עם שאר הייבוא, מוסיפים:
import com.google.devrel.textclassificationstep2.helpers.TextClassificationClient
import org.tensorflow.lite.support.label.Category
  1. בשלב הבא, צריך לטעון את ה-helpers. ב-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)

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

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

  1. כדי לקבל את הציון של ההסתברות שהערך הוא True, אפשר להסתכל על results[1].score באופן הבא:
    val score = results[1].score
  1. בוחרים ערך סף (במקרה הזה 0.8), שבו מציינים שאם הציון של הקטגוריה 'נכון' גבוה מערך הסף (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'.

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

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

8. הוספת קובצי המודל ו-Vocab

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

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

1ee9eaa00ee79859.png

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

b63502b23911fd42.png

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

20b7cb603d49b457.png

9. טעינת הלקסיקון

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

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

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

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

  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

הכתובת שאליה נשלחה ההודעה "Buy my book to learn onlinecommerce!" (קניית הספר שלי כדי ללמוד על מסחר אונליין) נשלחה, והאפליקציה שולחת בחזרה התראה על זיהוי ספאם בסבירות של 0 .99%!

14. מעולה!

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

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