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

1. قبل البدء

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

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

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

ما ستتمكّن من [إنشاءه أو تعلُّمه]

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

المتطلبات

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

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

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

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

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

يتوفّر لك أيضًا الرمز finished الذي يحمل القيمة TextClassificationStep2.

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

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

في الدرس التطبيقي حول الترميز "إنشاء نموذج تعلُّم آلة للتعليقات غير المرغوب فيها"، أنشأت نموذجًا بتنسيق ‎ .TFLITE.

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

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

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

d7c3e9f21035fc15.png

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

2137f956a1ba4ef0.png

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

ae858835e1a90445.png

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

e61aaa3b73c5ab68.png

سيتم تشغيل Finder لعرض موقع الملفات (File Explorer على نظام التشغيل Windows وFiles على نظام التشغيل 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 البرمجية. سيتم تصنيف القيمة الصحيحة باستخدام ‎.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، يمكنك الاطّلاع على 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

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

يتوفّر لك أيضًا الرمز finished الذي يحمل القيمة 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 و "حب" بقيمة 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 الذي استخدمته سابقًا هو 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. وسيتم تنفيذ ذلك لاحقًا كإضافة.

حان الوقت الآن لتخصيص أداة الترجمة للمترجم، انسخ المخزن المؤقت للبيانات الذي أنشأته للتوّ إلى متسابق الإدخال، ثم استدعِ المُترجم لإجراء الاستنتاج:

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

في حال إرسال الرسالة "اشترِ كتابي للتعرّف على التداول على الإنترنت"، يرسل التطبيق تنبيهًا بشأن رصد رسالة غير مرغوب فيها بنسبة احتمالية %99.

14. تهانينا!

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

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