อัปเดตแอปเพื่อใช้โมเดลแมชชีนเลิร์นนิงสำหรับการกรองสแปม

1. ก่อนเริ่มต้น

ใน Codelab นี้ คุณจะอัปเดตแอปที่สร้างขึ้นใน Codelab "เริ่มต้นใช้งานการจัดประเภทข้อความบนอุปกรณ์เคลื่อนที่" ก่อนหน้านี้

ข้อกำหนดเบื้องต้น

  • Codelab นี้ออกแบบมาสำหรับนักพัฒนาซอฟต์แวร์ที่มีประสบการณ์ซึ่งเพิ่งเริ่มต้นใช้งานแมชชีนเลิร์นนิง
  • Codelab นี้เป็นส่วนหนึ่งของเส้นทางที่จัดลำดับไว้ หากคุณยังไม่ได้สร้างแอปสไตล์การรับส่งข้อความพื้นฐานหรือสร้างโมเดลแมชชีนเลิร์นนิงสำหรับสแปมความคิดเห็น โปรดหยุดและดำเนินการดังกล่าวในตอนนี้

สิ่งที่คุณจะ [สร้างหรือเรียนรู้]

  • คุณจะได้เรียนรู้วิธีผสานรวมโมเดลที่กำหนดเองเข้ากับแอปที่สร้างขึ้นในขั้นตอนก่อนหน้า

สิ่งที่คุณต้องมี

  • Android Studio หรือ CocoaPods สำหรับ iOS

2. เปิดแอป Android ที่มีอยู่

คุณสามารถรับโค้ดสำหรับสิ่งนี้ได้โดยทำตาม Codelab 1 หรือโดยการโคลนที่เก็บ นี้และโหลดแอปจาก TextClassificationStep1

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

คุณดูได้ในเส้นทาง TextClassificationOnMobile->Android

คุณยังสามารถดูโค้ดที่เสร็จสมบูรณ์ได้ที่ TextClassificationStep2

เมื่อเปิดแล้ว คุณก็พร้อมที่จะไปยังขั้นตอนที่ 2

3. นำเข้าไฟล์โมเดลและข้อมูลเมตา

ใน Codelab สร้างโมเดลแมชชีนเลิร์นนิงสำหรับสแปมในความคิดเห็น คุณได้สร้างโมเดล .TFLITE

คุณควรดาวน์โหลดไฟล์โมเดล หากไม่มี คุณสามารถดาวน์โหลดได้จากที่เก็บสำหรับโค้ดแล็บนี้ และดาวน์โหลดโมเดลได้ที่นี่

เพิ่มลงในโปรเจ็กต์โดยสร้างไดเรกทอรีชิ้นงาน

  1. ใช้แถบนำทางของโปรเจ็กต์เพื่อให้แน่ใจว่าได้เลือก Android ที่ด้านบนแล้ว
  2. คลิกขวาที่โฟลเดอร์ app เลือกใหม่ > ไดเรกทอรี

d7c3e9f21035fc15.png

  1. ในกล่องโต้ตอบไดเรกทอรีใหม่ ให้เลือก src/main/assets

2137f956a1ba4ef0.png

คุณจะเห็นว่าตอนนี้มีโฟลเดอร์ชิ้นงานใหม่ในแอปแล้ว

ae858835e1a90445.png

  1. คลิกขวาที่ชิ้นงาน
  2. ในเมนูที่เปิดขึ้น คุณจะเห็นแสดงใน Finder (ใน Mac) เลือก (ใน 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 มักจะมีมากกว่า 1 รายการ ดังนั้นอย่าลืมหารายการระดับแอป ในโปรเจ็กต์ Explorer ในมุมมอง Android ให้ค้นหาในส่วนสคริปต์ Gradle โดยจะมีการติดป้ายกำกับ .app ที่ถูกต้องดังที่แสดงที่นี่

6426051e614bc42f.png

คุณจะต้องทำการเปลี่ยนแปลง 2 อย่างในไฟล์นี้ รายการแรกอยู่ในส่วนการขึ้นต่อกันที่ด้านล่าง เพิ่มข้อความ 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

ตอนนี้คุณมีทรัพยากร Dependency ทั้งหมดแล้ว ก็ถึงเวลาเริ่มเขียนโค้ด

5. เพิ่มคลาส Helper

หากต้องการแยกตรรกะการอนุมานที่แอปใช้โมเดลออกจากอินเทอร์เฟซผู้ใช้ ให้สร้างคลาสอื่นเพื่อจัดการการอนุมานโมเดล เรียกคลาสนี้ว่าคลาส "Helper"

  1. คลิกขวาที่ชื่อแพ็กเกจที่มีโค้ด MainActivity
  2. เลือกใหม่ > แพ็กเกจ

d5911ded56b5df35.png

  1. คุณจะเห็นกล่องโต้ตอบที่กึ่งกลางหน้าจอซึ่งขอให้คุณป้อนชื่อแพ็กเกจ โดยเพิ่มไว้ที่ส่วนท้ายของชื่อแพ็กเกจปัจจุบัน (ในที่นี้เรียกว่าผู้ช่วยเหลือ)

3b9f1f822f99b371.png

  1. เมื่อเสร็จแล้ว ให้คลิกขวาที่โฟลเดอร์ helpers ใน Project Explorer
  2. เลือกใหม่ > คลาส Java แล้วตั้งชื่อว่า TextClassificationClient คุณจะแก้ไขไฟล์ในขั้นตอนถัดไป

TextClassificationClientคลาส Helper จะมีลักษณะดังนี้ (แม้ว่าชื่อแพ็กเกจอาจแตกต่างกัน)

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 โดยจะโหลดโมเดลและลดความซับซ้อนของการจัดการการแลกเปลี่ยนข้อมูลระหว่างแอปกับโมเดล

ในload() ระบบจะสร้างอินสแตนซ์ของประเภท NLClassifier ใหม่จากเส้นทางโมเดล เส้นทางโมเดลคือชื่อของโมเดล model.tflite NLClassifierประเภทนี้เป็นส่วนหนึ่งของไลบรารีงานข้อความ และช่วยคุณโดยการแปลงสตริงเป็นโทเค็น ใช้ความยาวลำดับที่ถูกต้อง ส่งไปยังโมเดล และแยกวิเคราะห์ผลลัพธ์

(ดูรายละเอียดเพิ่มเติมเกี่ยวกับสิ่งเหล่านี้ได้ที่สร้างโมเดลแมชชีนเลิร์นนิงสำหรับสแปมความคิดเห็น)

การจัดประเภทจะดำเนินการในเมธอด classify โดยคุณจะส่งสตริงไปยังเมธอดดังกล่าว และเมธอดจะแสดงผล List เมื่อใช้โมเดลแมชชีนเลิร์นนิงเพื่อจัดประเภทเนื้อหาที่คุณต้องการพิจารณาว่าสตริงเป็นสแปมหรือไม่ โดยทั่วไปแล้วระบบจะแสดงคำตอบทั้งหมดพร้อมกับความน่าจะเป็นที่กำหนด เช่น หากส่งข้อความที่ดูเหมือนสแปมให้โมเดล คุณจะได้รับคำตอบ 2 รายการกลับมา โดยรายการหนึ่งจะแสดงความน่าจะเป็นที่ข้อความนั้นเป็นสแปม และอีกรายการจะแสดงความน่าจะเป็นที่ข้อความนั้นไม่ใช่สแปม สแปม/ไม่ใช่สแปมเป็นหมวดหมู่ ดังนั้น List ที่แสดงจะมีความน่าจะเป็นเหล่านี้ คุณจะแยกวิเคราะห์ในภายหลัง

ตอนนี้คุณมีคลาส Helper แล้ว ให้กลับไปที่ MainActivity และอัปเดตเพื่อใช้คลาสนี้ในการจัดประเภทข้อความ คุณจะเห็นตัวอย่างในขั้นตอนถัดไป

6. จัดประเภทข้อความ

ใน MainActivity คุณจะต้องนําเข้าฟังก์ชันช่วยที่เพิ่งสร้างก่อน

  1. ที่ด้านบนของ MainActivity.kt ให้เพิ่มรายการต่อไปนี้พร้อมกับการนำเข้าอื่นๆ
import com.google.devrel.textclassificationstep2.helpers.TextClassificationClient
import org.tensorflow.lite.support.label.Category
  1. จากนั้นคุณจะต้องโหลดตัวช่วย ใน onCreate ให้เพิ่มบรรทัดต่อไปนี้เพื่อสร้างอินสแตนซ์และโหลดคลาส Helper ทันทีหลังจากบรรทัด 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 คุณสามารถดู 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"

ใช้เทอร์มินัลเพื่อไปยังไดเรกทอรีนั้นและเรียกใช้ pod install หากสำเร็จ คุณจะมีไดเรกทอรีใหม่ชื่อ Pods และไฟล์ .xcworkspace ใหม่ที่สร้างขึ้นสำหรับคุณ คุณจะใช้รหัสนั้นแทน .xcproject ในอนาคต

หากไม่สำเร็จ โปรดตรวจสอบว่าคุณมี Podfile ในไดเรกทอรีเดียวกับที่ .xcproject อยู่ โดยปกติแล้วสาเหตุหลักมักเกิดจาก Podfile อยู่ในไดเรกทอรีที่ไม่ถูกต้องหรือชื่อเป้าหมายไม่ถูกต้อง

8. เพิ่มไฟล์โมเดลและไฟล์คำศัพท์

เมื่อสร้างโมเดลด้วย TensorFlow Lite Model Maker คุณจะสามารถส่งออกโมเดล (เป็น model.tflite) และคำศัพท์ (เป็น vocab.txt) ได้

  1. โดยเพิ่มลงในโปรเจ็กต์ด้วยการลากและวางจาก Finder ลงในหน้าต่างโปรเจ็กต์ ตรวจสอบว่าได้เลือกเพิ่มไปยังเป้าหมายแล้ว

1ee9eaa00ee79859.png

เมื่อเสร็จแล้ว คุณควรเห็นรายการต่อไปนี้ในโปรเจ็กต์

b63502b23911fd42.png

  1. ตรวจสอบอีกครั้งว่าได้เพิ่มลงในแพ็กเกจแล้ว (เพื่อให้ระบบนำไปใช้กับอุปกรณ์) โดยเลือกโปรเจ็กต์ของคุณ (ในภาพหน้าจอด้านบนคือไอคอนสีน้ำเงิน TextClassificationStep2) และดูแท็บขั้นตอนการสร้าง

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, เพื่อให้ส่งไปยังเทนเซอร์ได้ เมื่อใช้พ็อด TensorFlow Lite รวมถึงอินเทอร์พรีเตอร์ คุณจะได้รับสิทธิ์เข้าถึงประเภทเทนเซอร์

เริ่มเขียนโค้ดดังนี้ (ยังอยู่ในฟังก์ชัน 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 รายการ รายการแรกคือความน่าจะเป็นที่ข้อความไม่ใช่จดหมายขยะ ส่วนรายการที่ 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. ยินดีด้วย

ตอนนี้คุณได้สร้างแอปที่เรียบง่ายมากซึ่งกรองข้อความสำหรับสแปมในความคิดเห็นโดยใช้โมเดลที่ได้รับการฝึกจากข้อมูลที่ใช้ในการสแปมบล็อก

ขั้นตอนถัดไปในวงจรนักพัฒนาแอปทั่วไปคือการสำรวจสิ่งที่ต้องทำเพื่อปรับแต่งโมเดลตามข้อมูลที่พบในชุมชนของคุณเอง คุณจะเห็นวิธีทำในกิจกรรมเส้นทางถัดไป