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
- ใช้เครื่องมือนำทางโปรเจ็กต์ ตรวจสอบว่าได้เลือก Android ที่ด้านบน
- คลิกขวาที่โฟลเดอร์ app เลือกใหม่ > ไดเรกทอรี
- ในกล่องโต้ตอบ New Directory ให้เลือก src/main/assets
คุณจะเห็นโฟลเดอร์เนื้อหาใหม่ในแอป
- คลิกขวาที่ชิ้นงาน
- ในเมนูที่เปิดขึ้น คุณจะเห็น (ใน Mac) Reveal ใน Finder เลือกแอป (ใน Windows จะระบุว่า Show in Explorer ส่วนใน Ubuntu จะระบุว่า Show in Files)
Finder จะเปิดขึ้นเพื่อแสดงตำแหน่งไฟล์ (File Explorer ใน Windows, Files ใน Linux)
- คัดลอกไฟล์
labels.txt
,model.tflite
และvocab
ไปยังไดเรกทอรีนี้
- กลับไปที่ Android Studio แล้วคุณจะเห็นเนื้อหาเหล่านั้นอยู่ในโฟลเดอร์เนื้อหา
4. อัปเดต Build.gradle เพื่อใช้ TensorFlow Lite
หากต้องการใช้ TensorFlow Lite และไลบรารีงาน TensorFlow Lite ที่รองรับ คุณจะต้องอัปเดตไฟล์ build.gradle
โปรเจ็กต์ Android มักจะมีมากกว่า 1 โปรเจ็กต์ ดังนั้นอย่าลืมหาระดับแอปที่ 1 ในโปรแกรมสำรวจโปรเจ็กต์ในมุมมอง Android ให้ค้นหาโปรเจ็กต์ในส่วน Gradle Scripts แอปที่ถูกต้องจะมีป้ายกำกับเป็น .app ดังตัวอย่างต่อไปนี้
คุณจะต้องทำการเปลี่ยนแปลง 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
ตอนนี้คุณมีทรัพยากร Dependency ทั้งหมดแล้ว ก็ถึงเวลาเริ่มเขียนโค้ด
5. เพิ่มชั้นเรียนผู้ช่วยเหลือ
หากต้องการแยกตรรกะการอนุมานซึ่งแอปใช้โมเดลดังกล่าวจากอินเทอร์เฟซผู้ใช้ ให้สร้างคลาสอีกรายการเพื่อจัดการการอนุมานโมเดล เรียกสิ่งนี้ว่า "ผู้ช่วย"
- คลิกขวาที่ชื่อแพ็กเกจที่มีรหัส
MainActivity
ของคุณ - เลือก New > บรรจุหีบห่อ
- คุณจะเห็นกล่องโต้ตอบตรงกลางหน้าจอที่ขอให้คุณป้อนชื่อแพ็กเกจ เพิ่มต่อท้ายชื่อแพ็กเกจปัจจุบัน (ตรงนี้เรียกว่าผู้ช่วย)
- เมื่อดำเนินการเสร็จแล้ว ให้คลิกขวาที่โฟลเดอร์ผู้ช่วยในโปรแกรมสำรวจโปรเจ็กต์
- เลือก New > Java Class และเรียกว่า
TextClassificationClient
คุณจะได้แก้ไขไฟล์ในขั้นตอนถัดไป
คลาสตัวช่วยของ TextClassificationClient
จะมีลักษณะดังนี้ (แม้ว่าชื่อแพ็กเกจอาจต่างออกไป)
package com.google.devrel.textclassificationstep1.helpers;
public class TextClassificationClient {
}
- อัปเดตไฟล์ด้วยโค้ดนี้
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
คุณจะต้องนำเข้าตัวช่วยที่เพิ่งสร้างขึ้นก่อน
- ที่ด้านบนของ
MainActivity.kt
พร้อมด้วยการนำเข้าอื่นๆ ให้เพิ่มรายการต่อไปนี้
import com.google.devrel.textclassificationstep2.helpers.TextClassificationClient
import org.tensorflow.lite.support.label.Category
- ถัดไป คุณจะต้องโหลดตัวช่วย ใน
onCreate
ให้เพิ่มบรรทัดเหล่านี้เพื่อสร้างอินสแตนซ์และโหลดคลาสตัวช่วย โดยอยู่หลังบรรทัดsetContentView
val client = TextClassificationClient(applicationContext)
client.load()
ในขณะนี้ onClickListener
ของปุ่มควรมีลักษณะดังนี้
btnSendText.setOnClickListener {
var toSend:String = txtInput.text.toString()
txtOutput.text = toSend
}
- โปรดอัปเดตการตั้งค่าให้มีลักษณะดังนี้
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()
}
ซึ่งจะเปลี่ยนฟังก์ชันการทำงานจากการป้อนข้อมูลของผู้ใช้เป็นการแยกประเภทก่อน
- ในการใช้บรรทัดนี้ คุณจะนำสตริงที่ผู้ใช้ป้อนและส่งไปยังโมเดล โดยได้ผลลัพธ์กลับมา:
var results:List<Category> = client.classify(toSend)
มีเพียง 2 หมวดหมู่ ได้แก่ False
และ True
. (TensorFlow จะจัดเรียงตามลำดับตัวอักษร ดังนั้น False จะเป็นรายการที่ 0 และ True จะเป็นรายการที่ 1)
- หากต้องการดูคะแนนสำหรับความน่าจะเป็นที่ค่าจะเป็น
True
ให้ดูผลลัพธ์[1].score เช่น
val score = results[1].score
- เลือกค่าเกณฑ์ (ในกรณีนี้คือ 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()
}
- ดูโมเดลในการใช้งานจริงที่นี่ ข้อความ "เยี่ยมชมบล็อกของฉันเพื่อซื้อสินค้า!" ถูกระบุว่ามีโอกาสสูงที่จะเป็นสแปม:
ในทางกลับกัน "สวัสดี บทแนะนำสนุกๆ ขอบคุณนะ" พบว่ามีโอกาสน้อยมากที่จะเป็นสแปม:
7. อัปเดตแอป iOS เพื่อใช้โมเดล TensorFlow Lite
คุณสามารถรับโค้ดสำหรับกรณีนี้ได้โดยทำตาม Codelab 1 หรือโดยการโคลนที่เก็บนี้แล้วโหลดแอปจาก TextClassificationStep1
ซึ่งจะอยู่ในเส้นทาง TextClassificationOnMobile->iOS
คุณยังใช้รหัสที่เสร็จสิ้นแล้วได้ด้วย TextClassificationStep2
ในการสร้าง Codelab ของโมเดลแมชชีนเลิร์นนิงในการสร้างสแปมความคิดเห็น คุณสร้างแอปที่เรียบง่ายมากๆ ที่ให้ผู้ใช้พิมพ์ข้อความลงใน UITextView
แล้วส่งผ่านไปยังเอาต์พุตโดยไม่ต้องมีการกรองใดๆ
คุณจะต้องอัปเดตแอปนั้นให้ใช้โมเดล TensorFlow Lite เพื่อตรวจหาสแปมความคิดเห็นในข้อความก่อนส่ง เพียงจำลองการส่งในแอปนี้โดยการแสดงผลข้อความในป้ายกำกับเอาต์พุต (แต่แอปจริงอาจมีกระดานข่าวสาร แชท หรือข้อความที่คล้ายกัน)
ในการเริ่มต้นใช้งาน คุณต้องมีแอปจากขั้นตอนที่ 1 ซึ่งจะโคลนจากที่เก็บได้
คุณจะต้องใช้ CocoaPods เพื่อรวม TensorFlow Lite หากยังไม่มีข้อมูลเหล่านี้ คุณก็สามารถติดตั้งได้โดยทำตามวิธีการที่ https://cocoapods.org/
- เมื่อติดตั้ง 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
) ได้
- เพิ่มรายการเหล่านี้ลงในโปรเจ็กต์โดยการลากและวางจาก Finder ลงในหน้าต่างโปรเจ็กต์ ตรวจสอบว่าได้เลือกเพิ่มลงในเป้าหมายแล้ว ดังนี้
เมื่อทําเสร็จแล้ว คุณควรเห็นโปรเจ็กต์อยู่ในโปรเจ็กต์
- ตรวจสอบอีกครั้งว่ามีการเพิ่มโปรเจ็กต์ลงในแพ็กเกจแล้ว (เพื่อให้ติดตั้งใช้งานในอุปกรณ์) โดยเลือกโปรเจ็กต์ (ในภาพหน้าจอด้านบนไอคอนสีฟ้า TextClassificationStep2) แล้วดูที่แท็บระยะการสร้าง
9. โหลดคำศัพท์
เมื่อทำการจำแนกประเภท NLP โมเดลจะได้รับการฝึกด้วยคำที่เข้ารหัสเป็นเวกเตอร์ โมเดลจะเข้ารหัสคำด้วยชุดชื่อและค่าที่เจาะจงซึ่งเรียนรู้เมื่อฝึกโมเดล โปรดทราบว่าโมเดลส่วนใหญ่จะมีคำศัพท์แตกต่างกัน และคุณจำเป็นต้องใช้คำศัพท์สำหรับโมเดลของคุณที่สร้างขึ้นในช่วงการฝึก นี่คือไฟล์ vocab.txt
ที่คุณเพิ่งเพิ่มลงในแอป
คุณสามารถเปิดไฟล์ใน Xcode เพื่อดูการเข้ารหัสได้ คำต่างๆ เช่น "เพลง" เข้ารหัสเป็น 6 และ "love" ถึง 12 ลำดับที่แท้จริงคือลำดับความถี่ ดังนั้น "I" เป็นคำที่พบบ่อยที่สุดในชุดข้อมูล ตามด้วย "ตรวจสอบ"
เมื่อผู้ใช้พิมพ์คำ คุณจะต้องเข้ารหัสคำศัพท์นี้ด้วยคำศัพท์นี้ก่อนที่จะส่งไปยังโมเดลเพื่อจัดประเภท
ลองมาดูโค้ดนั้นกัน เริ่มด้วยการโหลดคำศัพท์
- กำหนดตัวแปรระดับชั้นเรียนเพื่อเก็บพจนานุกรม
var words_dictionary = [String : Int]()
- จากนั้นสร้าง
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")
}
}
- คุณดำเนินการนี้ได้โดยเรียกใช้จากภายใน
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
เรียกใช้และทดสอบแอป
หากทุกอย่างเป็นไปด้วยดี คุณควรจะเห็นแอปในอุปกรณ์ดังนี้
เมื่อมีข้อความ "ซื้อหนังสือของฉันเพื่อเรียนรู้การค้าออนไลน์!" ส่งไป แอปจะส่งข้อความแจ้งเตือนที่ตรวจพบสแปมกลับไป โดยมีความน่าจะเป็น 0 .99%!
14. ยินดีด้วย
ตอนนี้คุณได้สร้างแอปที่เรียบง่ายมากๆ ที่กรองข้อความเพื่อหาสแปมความคิดเห็นโดยใช้โมเดลที่ได้รับการฝึกกับข้อมูลที่ใช้สแปมบล็อก
ขั้นตอนถัดไปในวงจรชีวิตของนักพัฒนาซอฟต์แวร์โดยทั่วไปคือการสำรวจสิ่งที่ต้องทำในการปรับแต่งโมเดลจากข้อมูลที่พบในชุมชนของคุณ คุณจะดูวิธีการนั้นได้ในกิจกรรมเส้นทางถัดไป