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

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

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

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

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

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

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

למה תזדקק?

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

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

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

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

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

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

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

ב-Codelab של מודל למידת המכונה 'יצירת תגובות ספאם' יצרתם מודל .TFLITE.

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

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

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

d7c3e9f21035fc15.png

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

2137f956a1ba4ef0.png

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

ae858835e1a90445.png

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

e61aaa3b73c5ab68.png

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

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

14f382cc19552a56.png

  1. חוזרים אל Android Studio והם יהיו זמינים בתיקיית הנכסים.

150ed2a1d2f7a10d.png

4. צריך לעדכן את build.gradle כדי להשתמש ב-TensorFlow Lite

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

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

6426051e614bc42f.png

צריך לבצע שני שינויים בקובץ הזה. הראשון נמצא בקטע תלות למטה. מוסיפים טקסט 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 ב-project Explorer.
  2. בוחרים באפשרות חדש > Java Class, ולקרוא לו 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;
    }

}

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

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

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

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

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

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

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

  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, אפשר להציג את התוצאות[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".

נכנסים לספרייה ב-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. טעינת Vocab

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

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

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

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

  1. מגדירים משתנה ברמת הכיתה כדי לאחסן את המילון:
var words_dictionary = [String : Int]()
  1. לאחר מכן צריך ליצור func בכיתה כדי לטעון את אוצר המילים (vocab) למילון הזה:
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,, כדי שניתן יהיה להעביר אותו לטנזור. כשמטמיעים את רצף TensorFlow Lite וגם את המתרגם, קיבלתם גישה לסוג Tensor.

מתחילים את הקוד כך (עדיין ב'סיווג func'):

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

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

בשלב הזה צריך להקצות למפענח, מעתיקים את מאגר הנתונים הזמני שיצרתם ל-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()

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

אלה יהיו ערכים גולמיים (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 about Online מהרגע!" נשלחה, האפליקציה שולחת חזרה התראה על זיהוי ספאם בסבירות של 0 .99%!

14. מעולה!

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

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