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

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

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

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

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

d7c3e9f21035fc15.png

  1. ในกล่องโต้ตอบ New Directory ให้เลือก src/main/assets

2137f956a1ba4ef0.png

คุณจะเห็นโฟลเดอร์เนื้อหาใหม่ในแอป

ae858835e1a90445.png

  1. คลิกขวาที่ชิ้นงาน
  2. ในเมนูที่เปิดขึ้น คุณจะเห็น (ใน Mac) Reveal ใน Finder เลือกแอป (ใน Windows จะระบุว่า Show in Explorer ส่วนใน Ubuntu จะระบุว่า Show in Files)

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 โปรเจ็กต์ ดังนั้นอย่าลืมหาระดับแอปที่ 1 ในโปรแกรมสำรวจโปรเจ็กต์ในมุมมอง Android ให้ค้นหาโปรเจ็กต์ในส่วน Gradle Scripts แอปที่ถูกต้องจะมีป้ายกำกับเป็น .app ดังตัวอย่างต่อไปนี้

6426051e614bc42f.png

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

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

5. เพิ่มชั้นเรียนผู้ช่วยเหลือ

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

  1. คลิกขวาที่ชื่อแพ็กเกจที่มีรหัส MainActivity ของคุณ
  2. เลือก New > บรรจุหีบห่อ

d5911ded56b5df35.png

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

3b9f1f822f99b371.png

  1. เมื่อดำเนินการเสร็จแล้ว ให้คลิกขวาที่โฟลเดอร์ผู้ช่วยในโปรแกรมสำรวจโปรเจ็กต์
  2. เลือก New > 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 ซึ่งจะโหลดโมเดลและแยกความซับซ้อนของการจัดการการแลกเปลี่ยนข้อมูลระหว่างแอปกับโมเดล

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

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

การแยกประเภทจะเกิดขึ้นในวิธีการแยกประเภทที่คุณส่งต่อสตริง จากนั้นสตริงจะแสดงผล List เมื่อใช้โมเดลแมชชีนเลิร์นนิงในการแยกประเภทเนื้อหาที่คุณต้องการพิจารณาว่าสตริงหนึ่งๆ เป็นสแปมหรือไม่ เป็นเรื่องปกติที่จะมีการส่งคืนคำตอบทั้งหมด พร้อมกับความน่าจะเป็นที่กำหนดไว้ เช่น ถ้าส่งข้อความที่ดูเหมือนจดหมายขยะ คุณจะได้รับรายการคำตอบกลับมา 2 คำตอบ กลุ่มแรกที่มีความเป็นไปได้ที่จะเป็นจดหมายขยะ และอีกแบบหนึ่งที่มีความเป็นไปได้ว่าจะไม่ใช่สแปม จดหมายขยะ/ไม่ใช่จดหมายขยะเป็นหมวดหมู่ ดังนั้น 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)

มีเพียง 2 หมวดหมู่ ได้แก่ 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

คุณสามารถรับโค้ดสำหรับกรณีนี้ได้โดยทำตาม Codelab 1 หรือโดยการโคลนที่เก็บนี้แล้วโหลดแอปจาก TextClassificationStep1 ซึ่งจะอยู่ในเส้นทาง TextClassificationOnMobile->iOS

คุณยังใช้รหัสที่เสร็จสิ้นแล้วได้ด้วย TextClassificationStep2

ในการสร้าง Codelab ของโมเดลแมชชีนเลิร์นนิงในการสร้างสแปมความคิดเห็น คุณสร้างแอปที่เรียบง่ายมากๆ ที่ให้ผู้ใช้พิมพ์ข้อความลงใน UITextView แล้วส่งผ่านไปยังเอาต์พุตโดยไม่ต้องมีการกรองใดๆ

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

ในการเริ่มต้นใช้งาน คุณต้องมีแอปจากขั้นตอนที่ 1 ซึ่งจะโคลนจากที่เก็บได้

คุณจะต้องใช้ CocoaPods เพื่อรวม TensorFlow Lite หากยังไม่มีข้อมูลเหล่านี้ คุณก็สามารถติดตั้งได้โดยทำตามวิธีการที่ 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.tflite) และคำศัพท์ (เป็น vocab.txt) ได้

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

1ee9eaa00ee79859.png

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

b63502b23911fd42.png

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

20b7cb603d49b457.png

9. โหลดคำศัพท์

เมื่อทำการจำแนกประเภท NLP โมเดลจะได้รับการฝึกด้วยคำที่เข้ารหัสเป็นเวกเตอร์ โมเดลจะเข้ารหัสคำด้วยชุดชื่อและค่าที่เจาะจงซึ่งเรียนรู้เมื่อฝึกโมเดล โปรดทราบว่าโมเดลส่วนใหญ่จะมีคำศัพท์แตกต่างกัน และคุณจำเป็นต้องใช้คำศัพท์สำหรับโมเดลของคุณที่สร้างขึ้นในช่วงการฝึก นี่คือไฟล์ vocab.txt ที่คุณเพิ่งเพิ่มลงในแอป

คุณสามารถเปิดไฟล์ใน Xcode เพื่อดูการเข้ารหัสได้ คำต่างๆ เช่น "เพลง" เข้ารหัสเป็น 6 และ "love" ถึง 12 ลำดับที่แท้จริงคือลำดับความถี่ ดังนั้น "I" เป็นคำที่พบบ่อยที่สุดในชุดข้อมูล ตามด้วย "ตรวจสอบ"

เมื่อผู้ใช้พิมพ์คำ คุณจะต้องเข้ารหัสคำศัพท์นี้ด้วยคำศัพท์นี้ก่อนที่จะส่งไปยังโมเดลเพื่อจัดประเภท

ลองมาดูโค้ดนั้นกัน เริ่มด้วยการโหลดคำศัพท์

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

เริ่มต้นโค้ดดังนี้ (ยังอยู่ในประเภท func):

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

ไม่ต้องกังวลหากคุณได้รับข้อผิดพลาดใน copyingBufferOf ซึ่งจะนำมาใช้เป็นส่วนขยายในภายหลัง

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

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

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