تحديث تطبيقك لاستخدام نموذج تعلُّم الآلة لفلترة الرسائل غير المرغوب فيها

1. قبل البدء

في هذا الدرس التطبيقي حول الترميز، ستُحدِّث التطبيق الذي أنشأته في الإصدار السابق من "بدء استخدام الدروس التطبيقية حول ترميز نصوص الأجهزة الجوّالة".

المتطلبات الأساسية

  • تم تصميم هذا الدرس التطبيقي حول الترميز للمطوِّرين ذوي الخبرة الجدد في مجال تعلُّم الآلة.
  • الدرس التطبيقي حول الترميز هو جزء من مسار متسلسل. إذا لم تكن قد أكملت بالفعل إنشاء تطبيق نمط أساسي للمراسلة أو إنشاء نموذج تعلم آلي للتعليقات غير المرغوب فيها، يُرجى التوقف وتنفيذ ذلك الآن.

ما الذي [ستنشئه أو تتعلمه]

  • وستتعرّف على كيفية دمج النموذج المخصّص في تطبيقك، والذي تم إنشاؤه في الخطوات السابقة.

المتطلبات

  • Android Studio أو CocoaPods لنظام التشغيل iOS

2. فتح تطبيق Android الحالي

يمكنك الحصول على هذا الرمز من خلال اتّباع "الدرس التطبيقي حول الترميز" 1 أو من خلال استنساخ هذا المستودع وتحميل التطبيق من "TextClassificationStep1".

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

يمكنك العثور على ذلك في مسار TextClassificationOnMobile->Android.

يتوفر الرمز تم الانتهاء لك أيضًا بصفتك TextClassificationStep2.

بعد فتحه، تكون جاهزًا للانتقال إلى الخطوة 2.

3- استيراد ملف النموذج والبيانات الوصفية

أنشأت نموذج .TFLITE في الدرس التطبيقي حول الترميز الخاص بنموذج تعلُّم الآلة المتعلّق بالتعليقات غير المرغوب فيها.

من المفترض أنك قد نزّلت ملف النموذج. إذا لم يكن متوفّرًا لديك، يمكنك الحصول عليه من المستودع الخاص بهذا الدرس التطبيقي حول الترميز، ويمكنك الحصول عليه من هنا.

أضِفه إلى مشروعك من خلال إنشاء دليل أصول.

  1. باستخدام أداة التنقّل في المشروع، تأكّد من اختيار Android في الأعلى.
  2. انقر بزر الماوس الأيمن على مجلد التطبيقات. حدد جديد > الدليل.

d7c3e9f21035fc15.png

  1. في مربّع الحوار الدليل الجديد، اختَر src/main/assets.

2137f956a1ba4ef0.png

سيظهر الآن مجلد جديد باسم مواد العرض في التطبيق.

ae858835e1a90445.png

  1. انقر بزر الماوس الأيمن على مواد العرض.
  2. في القائمة التي تفتح، سترى (على نظام التشغيل Mac) الإظهار في Finder (الباحث). اختَرها. (في نظام التشغيل Windows، سيظهر الخيار عرض في Explorer، في نظام التشغيل Ubuntu عرض في الملفات).

e61aaa3b73c5ab68.png

سيتم تشغيل الباحث لعرض موقع الملفات (مستكشف الملفات على Windows وFiles على Linux).

  1. انسخ الملفات labels.txt وmodel.tflite وvocab إلى هذا الدليل.

14f382cc19552a56.png

  1. ارجع إلى "استوديو Android" وستظهر متاحة في مجلد مواد العرض.

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 لمعرفة أحدث المعلومات.

تتطلب مكتبات المهام أيضًا الإصدار 21 من حزمة تطوير البرامج (SDK) كحد أدنى. يمكنك العثور على هذا الإعداد في android >. default config، وتغييره إلى 21:

c100b68450b8812f.png

لديك الآن جميع تبعياتك، لذا حان الوقت لبدء البرمجة!

5- إضافة صف مساعد

لفصل منطق الاستنتاج الذي يستخدم فيه تطبيقك النموذج عن واجهة المستخدم، أنشئ فئة أخرى للتعامل مع استنتاج النموذج. استخدام اسم "مساعد" الصف.

  1. انقر بزر الماوس الأيمن على اسم الحزمة التي يحتوي عليها رمز MainActivity.
  2. حدد جديد > الحزمة.

d5911ded56b5df35.png

  1. سيظهر لك مربع حوار في وسط الشاشة يطلب منك إدخال اسم الحزمة. يمكنك إضافته في نهاية اسم الحزمة الحالية. (يُطلق عليها هنا اسم المساعدة).

3b9f1f822f99b371.png

  1. بعد الانتهاء من ذلك، انقر بزر الماوس الأيمن على مجلد المساعدة في مستكشف المشاريع.
  2. حدد جديد > فئة 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. عند استخدام نماذج تعلُّم الآلة لتصنيف المحتوى الذي تريد تحديد ما إذا كانت السلسلة فيه محتوى غير مرغوب فيه أم لا، من الشائع عرض جميع الإجابات، مع الاحتمالات المعيّنة. على سبيل المثال، إذا نقلتها رسالة تبدو كأنّها غير مرغوب فيها، ستظهر لك قائمة بإجابتَين: أحدهما باحتمالية أنه محتوى غير مرغوب فيه والآخر يُحتمل أنه ليس غير مرغوب فيه الرسائل غير المرغوب فيها/ليست غير مرغوب فيها هي فئات، لذا ستحتوي 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)

ليس هناك سوى فئتَين، هما False وTrue.

. (يرتبها TensorFlow أبجديًا، لذلك سيكون False للعنصر 0 وTrue سيكون العنصر 1).

  1. للحصول على نتيجة لاحتمالية أنّ القيمة هي True، يمكنك الاطّلاع على النتائج[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.

يمكنك الحصول على هذا الرمز من خلال اتّباع "الدرس التطبيقي حول الترميز" 1 أو من خلال استنساخ هذا المستودع وتحميل التطبيق من "TextClassificationStep1". يمكنك العثور على ذلك في مسار TextClassificationOnMobile->iOS.

يتوفر الرمز تم الانتهاء لك أيضًا بصفتك TextClassificationStep2.

في الدرس التطبيقي حول الترميز الخاص بنموذج "تعلُّم الآلة" و"إنشاء تعليق غير مرغوب فيه"، أنشأت تطبيقًا بسيطًا للغاية سمح للمستخدم بكتابة رسالة في 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. إضافة ملفَّي النموذج والمصطلحات

عندما أنشأت النموذج باستخدام صانع النماذج TensorFlow Lite، كان بإمكانك إخراج النموذج (كـ model.tflite) والمصطلح (مثل vocab.txt).

  1. يمكنك إضافتها إلى مشروعك من خلال سحبها وإفلاتها من Finder (الباحث) في نافذة مشروعك. تأكّد من وضع علامة في المربّع إضافة إلى الاستهدافات:

1ee9eaa00ee79859.png

عند الانتهاء من ذلك، من المفترض أن تراها في مشروعك:

b63502b23911fd42.png

  1. تحقق جيدًا من أنه تمت إضافة هذه التطبيقات إلى الحزمة (حتى يتم نشرها على الجهاز) من خلال تحديد مشروعك (يظهر في لقطة الشاشة أعلاه الرمز الأزرق TextClassificationStep2)، والاطّلاع على علامة التبويب مراحل التصميم:

20b7cb603d49b457.png

9. تحميل المفردة

عند تصنيف التعرف على اللغات الطبيعية، يتم تدريب النموذج بكلمات مشفرة في متجهات. يشفّر النموذج الكلمات باستخدام مجموعة محددة من الأسماء والقيم التي يتم تعلمها أثناء تدريب النموذج. يرجى ملاحظة أن معظم النماذج لها مفردات مختلفة، ومن المهم بالنسبة لك استخدام المصطلحات الخاصة بنموذجك الذي تم إنشاؤه أثناء التدريب. هذا هو ملف vocab.txt الذي أضفته للتو إلى تطبيقك.

يمكنك فتح الملف في Xcode للاطّلاع على الترميزات. كلمات مثل "أغنية" يتم ترميزها إلى 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 for 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 Type.

ابدأ الرمز على هذا النحو (لا يزال في تصنيف 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 بايت لكل خلية عصبية) والتي سيتعين عليك قراءتها وتحويلها بعد ذلك. بما أن هذا النموذج تحديدًا يحتوي على خليتين عصبيتين ناتجتين، يجب القراءة بحجم 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) ?? []

أصبح من السهل نسبيًا الآن تحليل البيانات لتحديد جودة الرسائل غير المرغوب فيها. يحتوي النموذج على مخرجين، أولهما احتمال بأن تكون الرسالة غير مرغوب فيها، والثاني يضم احتمالية ذلك. يتيح لك هذا الخيار الاطّلاع على 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. تهانينا!

لقد أنشأت الآن تطبيقًا بسيطًا جدًا يعمل على تصفية النص بحثًا عن التعليقات غير المرغوب فيها باستخدام نموذج تم تدريبه على البيانات المستخدمة في مدونات المحتوى غير المرغوب فيه.

تتمثل الخطوة التالية في دورة حياة المطوّرين النموذجية في استكشاف المتطلبات اللازمة لتخصيص النموذج استنادًا إلى البيانات المتوفّرة في مجتمعك. ستظهر لك طريقة إجراء ذلك في نشاط المسار التالي.