Cập nhật ứng dụng của bạn để sử dụng mô hình Học máy lọc thư rác

1. Trước khi bắt đầu

Trong lớp học lập trình này, bạn sẽ cập nhật ứng dụng mà mình đã tạo trong lớp học lập trình Bắt đầu phân loại văn bản trên thiết bị di động trước đó.

Điều kiện tiên quyết

  • Lớp học lập trình này dành cho những nhà phát triển giàu kinh nghiệm nhưng mới làm quen với học máy.
  • Lớp học lập trình này nằm trong một lộ trình học tập có trình tự. Nếu bạn chưa hoàn thành phần Tạo một ứng dụng nhắn tin có kiểu cơ bản hoặc Tạo một mô hình học máy để phát hiện bình luận không liên quan, vui lòng dừng lại và hoàn thành ngay.

Những nội dung bạn sẽ [tạo hoặc học]

  • Bạn sẽ tìm hiểu cách tích hợp mô hình tuỳ chỉnh vào ứng dụng mà bạn đã tạo trong các bước trước.

Bạn cần có

2. Mở ứng dụng Android hiện có

Bạn có thể lấy mã cho việc này bằng cách làm theo Lớp học lập trình 1 hoặc bằng cách sao chép kho lưu trữ này rồi tải ứng dụng từ TextClassificationStep1.

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

Bạn có thể tìm thấy tệp này trong đường dẫn TextClassificationOnMobile->Android.

Bạn cũng có thể xem mã hoàn tất dưới dạng TextClassificationStep2.

Sau khi mở, bạn có thể chuyển sang bước 2.

3. Nhập tệp mô hình và siêu dữ liệu

Trong lớp học lập trình Xây dựng mô hình học máy để phát hiện bình luận không liên quan, bạn đã tạo một mô hình .TFLITE.

Bạn nên tải tệp mô hình xuống. Nếu chưa có, bạn có thể tải xuống từ kho lưu trữ cho lớp học lập trình này và mô hình có sẵn tại đây.

Thêm thành phần đó vào dự án bằng cách tạo một thư mục thành phần.

  1. Khi dùng trình điều hướng dự án, hãy nhớ chọn Android ở trên cùng.
  2. Nhấp chuột phải vào thư mục app. Chọn New > Directory (Mới > Thư mục).

d7c3e9f21035fc15.png

  1. Trong hộp thoại New Directory (Thư mục mới), hãy chọn src/main/assets.

2137f956a1ba4ef0.png

Bạn sẽ thấy một thư mục assets (tài sản) mới hiện có trong ứng dụng.

ae858835e1a90445.png

  1. Nhấp chuột phải vào assets.
  2. Trên trình đơn mở ra, bạn sẽ thấy (trên máy Mac) Hiện trong Finder. Chọn ứng dụng đó. (Trên Windows, lựa chọn này sẽ là Show in Explorer (Hiện trong Explorer), còn trên Ubuntu, lựa chọn này sẽ là Show in Files (Hiện trong Files)).

e61aaa3b73c5ab68.png

Finder sẽ khởi chạy để cho biết vị trí của tệp (File Explorer trên Windows, Files trên Linux).

  1. Sao chép các tệp labels.txt, model.tflitevocab vào thư mục này.

14f382cc19552a56.png

  1. Quay lại Android Studio, bạn sẽ thấy các tệp này trong thư mục assets (tài sản).

150ed2a1d2f7a10d.png

4. Cập nhật build.gradle để sử dụng TensorFlow Lite

Để sử dụng TensorFlow Lite và các thư viện tác vụ TensorFlow Lite hỗ trợ TensorFlow Lite, bạn cần cập nhật tệp build.gradle.

Các dự án Android thường có nhiều hơn một, vì vậy, hãy nhớ tìm cấp độ ứng dụng. Trong trình khám phá dự án ở chế độ xem Android, hãy tìm tệp này trong phần Gradle Scripts (Tập lệnh Gradle). Tệp chính xác sẽ được gắn nhãn .app như minh hoạ ở đây:

6426051e614bc42f.png

Bạn cần thực hiện hai thay đổi đối với tệp này. Phần đầu tiên nằm ở dưới cùng trong mục dependencies (phần phụ thuộc). Thêm một implementation văn bản cho thư viện tác vụ TensorFlow Lite, như sau:

implementation 'org.tensorflow:tensorflow-lite-task-text:0.1.0'

Số phiên bản có thể đã thay đổi kể từ khi bài viết này được viết, vì vậy, hãy nhớ kiểm tra https://www.tensorflow.org/lite/inference_with_metadata/task_library/nl_classifier để biết thông tin mới nhất.

Các thư viện tác vụ cũng yêu cầu phiên bản SDK tối thiểu là 21. Tìm chế độ cài đặt này trong phần android > default config rồi thay đổi thành 21:

c100b68450b8812f.png

Giờ đây, bạn đã có tất cả các phần phụ thuộc, vì vậy, đã đến lúc bắt đầu viết mã!

5. Thêm một lớp trợ giúp

Để tách logic suy luận (nơi ứng dụng của bạn sử dụng mô hình) khỏi giao diện người dùng, hãy tạo một lớp khác để xử lý suy luận mô hình. Gọi đây là lớp "trợ giúp".

  1. Nhấp chuột phải vào tên gói có chứa mã MainActivity của bạn.
  2. Chọn New > Package (Mới > Gói).

d5911ded56b5df35.png

  1. Bạn sẽ thấy một hộp thoại ở giữa màn hình yêu cầu bạn nhập tên gói. Thêm phần này vào cuối tên gói hiện tại. (Ở đây, chúng tôi gọi là người trợ giúp.)

3b9f1f822f99b371.png

  1. Sau khi hoàn tất, hãy nhấp chuột phải vào thư mục helpers (trợ giúp) trong trình khám phá dự án.
  2. Chọn New > Java Class (Mới > Lớp Java) rồi gọi lớp này là TextClassificationClient. Bạn sẽ chỉnh sửa tệp này trong bước tiếp theo.

Lớp trợ giúp TextClassificationClient của bạn sẽ có dạng như sau (mặc dù tên gói của bạn có thể khác).

package com.google.devrel.textclassificationstep1.helpers;

public class TextClassificationClient {
}
  1. Cập nhật tệp bằng đoạn mã sau:
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;
    }

}

Lớp này sẽ cung cấp một trình bao bọc cho trình thông dịch TensorFlow Lite, tải mô hình và trừu tượng hoá sự phức tạp của việc quản lý trao đổi dữ liệu giữa ứng dụng và mô hình.

Trong phương thức load(), phương thức này sẽ tạo thực thể một kiểu NLClassifier mới từ đường dẫn mô hình. Đường dẫn mô hình chỉ là tên của mô hình, model.tflite. Loại NLClassifier là một phần của thư viện tác vụ văn bản, giúp bạn chuyển đổi chuỗi thành mã thông báo, sử dụng độ dài chuỗi chính xác, truyền chuỗi đó đến mô hình và phân tích kết quả.

(Để biết thêm thông tin chi tiết về những nội dung này, hãy xem lại phần Xây dựng mô hình học máy để phát hiện bình luận không liên quan.)

Quá trình phân loại được thực hiện trong phương thức phân loại, trong đó bạn truyền cho phương thức này một chuỗi và phương thức này sẽ trả về một List. Khi sử dụng các mô hình Học máy để phân loại nội dung mà bạn muốn xác định xem một chuỗi có phải là nội dung rác hay không, thì thông thường, tất cả các câu trả lời sẽ được trả về cùng với xác suất được chỉ định. Ví dụ: nếu bạn chuyển cho mô hình này một thông báo có vẻ là nội dung rác, bạn sẽ nhận được danh sách gồm 2 câu trả lời; một câu trả lời cho biết xác suất là nội dung rác và một câu trả lời cho biết xác suất không phải là nội dung rác. Spam/Not Spam là các danh mục, vì vậy, List được trả về sẽ chứa các xác suất này. Bạn sẽ phân tích cú pháp đó sau.

Giờ đây, khi đã có lớp trợ giúp, hãy quay lại MainActivity và cập nhật lớp này để sử dụng lớp trợ giúp này nhằm phân loại văn bản. Bạn sẽ thấy điều đó trong bước tiếp theo!

6. Phân loại văn bản

Trong MainActivity, trước tiên, bạn nên nhập các trình trợ giúp mà bạn vừa tạo!

  1. Ở đầu MainActivity.kt, cùng với các lệnh nhập khác, hãy thêm:
import com.google.devrel.textclassificationstep2.helpers.TextClassificationClient
import org.tensorflow.lite.support.label.Category
  1. Tiếp theo, bạn sẽ muốn tải các trình trợ giúp. Trong onCreate, ngay sau dòng setContentView, hãy thêm các dòng sau để tạo thực thể và tải lớp trợ giúp:
val client = TextClassificationClient(applicationContext)
client.load()

Hiện tại, onClickListener của nút sẽ có dạng như sau:

btnSendText.setOnClickListener {
     var toSend:String = txtInput.text.toString()
     txtOutput.text = toSend
 }
  1. Cập nhật để có dạng như sau:
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()
}

Điều này thay đổi chức năng từ chỉ xuất dữ liệu đầu vào của người dùng sang phân loại dữ liệu đầu vào đó trước.

  1. Với dòng này, bạn sẽ lấy chuỗi mà người dùng đã nhập và truyền chuỗi đó vào mô hình, sau đó nhận lại kết quả:
var results:List<Category> = client.classify(toSend)

Chỉ có 2 danh mục là FalseTrue

. (TensorFlow sắp xếp các mục theo bảng chữ cái, nên False sẽ là mục 0 và True sẽ là mục 1.)

  1. Để nhận điểm cho xác suất giá trị là True, bạn có thể xem results[1].score như sau:
    val score = results[1].score
  1. Chọn một giá trị ngưỡng (trong trường hợp này là 0,8), trong đó bạn cho rằng nếu điểm số cho danh mục True cao hơn giá trị ngưỡng (0,8), thì thông báo đó là thư rác. Nếu không, đó không phải là thư rác và bạn có thể gửi thư một cách an toàn:
    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. Xem mô hình hoạt động tại đây. Tin nhắn "Hãy truy cập vào blog của tôi để mua hàng!" bị gắn cờ là có khả năng cao là nội dung rác:

1fb0b5de9e566e.png

Ngược lại, "Chào bạn, hướng dẫn rất hay, cảm ơn bạn!" có khả năng rất thấp là thư rác:

73f38bdb488b29b3.png

7. Cập nhật ứng dụng iOS để sử dụng Mô hình TensorFlow Lite

Bạn có thể lấy mã cho việc này bằng cách làm theo Lớp học lập trình 1 hoặc bằng cách sao chép kho lưu trữ này rồi tải ứng dụng từ TextClassificationStep1. Bạn có thể tìm thấy tệp này trong đường dẫn TextClassificationOnMobile->iOS.

Bạn cũng có thể xem mã hoàn tất dưới dạng TextClassificationStep2.

Trong lớp học lập trình Xây dựng mô hình học máy phát hiện bình luận không liên quan, bạn đã tạo một ứng dụng rất đơn giản cho phép người dùng nhập một thông báo vào UITextView và chuyển thông báo đó đến một đầu ra mà không cần lọc.

Giờ đây, bạn sẽ cập nhật ứng dụng đó để sử dụng một mô hình TensorFlow Lite nhằm phát hiện bình luận không liên quan trong văn bản trước khi gửi. Chỉ cần mô phỏng việc gửi trong ứng dụng này bằng cách hiển thị văn bản trong nhãn đầu ra (nhưng một ứng dụng thực có thể có bảng thông báo, cuộc trò chuyện hoặc một thứ tương tự).

Để bắt đầu, bạn cần có ứng dụng ở bước 1. Bạn có thể sao chép ứng dụng này từ kho lưu trữ.

Để kết hợp TensorFlow Lite, bạn sẽ sử dụng CocoaPods. Nếu chưa cài đặt các thành phần này, bạn có thể cài đặt theo hướng dẫn tại https://cocoapods.org/.

  1. Sau khi cài đặt CocoaPods, hãy tạo một tệp có tên là Podfile trong cùng thư mục với .xcproject cho ứng dụng TextClassification. Nội dung của tệp này sẽ như sau:
target 'TextClassificationStep2' do
  use_frameworks!

  # Pods for NLPClassifier
    pod 'TensorFlowLiteSwift'

end

Tên ứng dụng của bạn phải nằm ở dòng đầu tiên, thay vì "TextClassificationStep2".

Sử dụng Terminal, chuyển đến thư mục đó rồi chạy pod install. Nếu thành công, bạn sẽ có một thư mục mới có tên là Pods và một tệp .xcworkspace mới được tạo cho bạn. Bạn sẽ sử dụng thẻ đó trong tương lai thay vì thẻ .xcproject.

Nếu không thành công, vui lòng đảm bảo bạn có Podfile trong cùng thư mục với .xcproject. Podfile nằm trong thư mục không chính xác hoặc tên mục tiêu không chính xác thường là nguyên nhân chính!

8. Thêm tệp mô hình và từ vựng

Khi tạo mô hình bằng Trình tạo mô hình TensorFlow Lite, bạn có thể xuất mô hình (dưới dạng model.tflite) và từ vựng (dưới dạng vocab.txt).

  1. Thêm các tệp đó vào dự án bằng cách kéo và thả các tệp đó từ Finder vào cửa sổ dự án. Đảm bảo bạn đã chọn thêm vào mục tiêu:

1ee9eaa00ee79859.png

Sau khi hoàn tất, bạn sẽ thấy các tệp này trong dự án của mình:

b63502b23911fd42.png

  1. Kiểm tra kỹ để đảm bảo các tệp này được thêm vào gói (để chúng được triển khai cho một thiết bị) bằng cách chọn dự án của bạn (trong ảnh chụp màn hình ở trên, đó là biểu tượng màu xanh dương TextClassificationStep2) và xem thẻ Build Phases (Giai đoạn xây dựng):

20b7cb603d49b457.png

9. Tải từ vựng

Khi thực hiện phân loại NLP, mô hình được huấn luyện bằng các từ được mã hoá thành vectơ. Mô hình mã hoá các từ bằng một tập hợp tên và giá trị cụ thể được học khi mô hình huấn luyện. Xin lưu ý rằng hầu hết các mô hình sẽ có từ vựng khác nhau và bạn phải sử dụng từ vựng cho mô hình của mình được tạo tại thời điểm huấn luyện. Đây là tệp vocab.txt mà bạn vừa thêm vào ứng dụng.

Bạn có thể mở tệp này trong Xcode để xem các phương thức mã hoá. Những từ như "song" được mã hoá thành 6 và "love" thành 12. Thứ tự thực tế là thứ tự tần suất, vì vậy "I" là từ phổ biến nhất trong tập dữ liệu, tiếp theo là "check".

Khi người dùng nhập từ, bạn nên mã hoá các từ đó bằng từ vựng này trước khi gửi đến mô hình để phân loại.

Hãy cùng tìm hiểu về mã đó. Bắt đầu bằng cách tải từ vựng.

  1. Xác định một biến cấp lớp để lưu trữ từ điển:
var words_dictionary = [String : Int]()
  1. Sau đó, hãy tạo một func trong lớp để tải từ vựng vào từ điển này:
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. Bạn có thể chạy thao tác này bằng cách gọi thao tác đó từ bên trong viewDidLoad:
override func viewDidLoad() {
    super.viewDidLoad()
    txtInput.delegate = self
    loadVocab()
}

10. Biến một chuỗi thành một chuỗi mã thông báo

Người dùng sẽ nhập các từ dưới dạng một câu và câu đó sẽ trở thành một chuỗi. Mỗi từ trong câu (nếu có trong từ điển) sẽ được mã hoá thành giá trị khoá cho từ đó như được xác định trong từ vựng.

Một mô hình NLP thường chấp nhận độ dài chuỗi cố định. Có những trường hợp ngoại lệ đối với các mô hình được tạo bằng ragged tensors, nhưng phần lớn bạn sẽ thấy rằng vấn đề này đã được khắc phục. Bạn đã chỉ định độ dài này khi tạo mô hình. Đảm bảo bạn sử dụng cùng một độ dài trong ứng dụng iOS.

Giá trị mặc định trong Colab cho TensorFlow Lite Model Maker mà bạn đã sử dụng trước đó là 20, vì vậy, hãy thiết lập giá trị đó ở đây:

let SEQUENCE_LENGTH = 20

Thêm func này để lấy chuỗi, chuyển đổi chuỗi đó thành chữ thường và loại bỏ mọi dấu chấm câu:

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
}

Xin lưu ý rằng chuỗi này sẽ là Int32. Đây là lựa chọn có chủ ý vì khi truyền các giá trị đến TensorFlow Lite, bạn sẽ phải xử lý bộ nhớ cấp thấp và TensorFlow Lite coi các số nguyên trong một chuỗi là số nguyên 32 bit. Điều này sẽ giúp bạn (một chút) dễ dàng hơn khi truyền các chuỗi đến mô hình.

11. Phân loại

Để phân loại một câu, trước tiên, câu đó phải được chuyển đổi thành một chuỗi mã thông báo dựa trên các từ trong câu. Việc này sẽ được thực hiện ở bước 9.

Giờ đây, bạn sẽ lấy câu đó và truyền cho mô hình, để mô hình suy luận về câu đó và phân tích cú pháp kết quả.

Thao tác này sẽ sử dụng trình thông dịch TensorFlow Lite mà bạn cần nhập:

import TensorFlowLite

Bắt đầu bằng một func nhận vào chuỗi của bạn, đó là một mảng các loại 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
  }

Thao tác này sẽ tải tệp mô hình từ gói và gọi một trình thông dịch bằng tệp đó.

Bước tiếp theo là sao chép bộ nhớ cơ bản được lưu trữ trong chuỗi vào một vùng đệm có tên là myData, để có thể truyền đến một tensor. Khi triển khai pod TensorFlow Lite cũng như trình thông dịch, bạn có quyền truy cập vào một Loại tensor.

Bắt đầu mã như sau (vẫn trong classify func.):

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

Đừng lo lắng nếu bạn gặp lỗi trên copyingBufferOf. Thao tác này sẽ được triển khai dưới dạng một tiện ích sau này.

Bây giờ là lúc phân bổ các tensor trên trình thông dịch, sao chép vùng đệm dữ liệu mà bạn vừa tạo vào tensor đầu vào, rồi gọi trình thông dịch để thực hiện suy luận:

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()

Sau khi lệnh gọi hoàn tất, bạn có thể xem đầu ra của trình thông dịch để xem kết quả.

Đây sẽ là các giá trị thô (4 byte cho mỗi nơ-ron) mà sau đó bạn sẽ phải đọc và chuyển đổi. Vì mô hình cụ thể này có 2 nơ-ron đầu ra, nên bạn sẽ cần đọc 8 byte sẽ được chuyển đổi thành Float32 để phân tích cú pháp. Bạn đang xử lý bộ nhớ cấp thấp, do đó có 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) ?? []

Giờ đây, việc phân tích cú pháp dữ liệu để xác định chất lượng của nội dung rác tương đối dễ dàng. Mô hình này có 2 đầu ra, đầu tiên là xác suất thư không phải là thư rác, đầu ra thứ hai là xác suất thư là thư rác. Vì vậy, bạn có thể xem results[1] để tìm giá trị thư rác:

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

Để thuận tiện cho bạn, sau đây là phương thức đầy đủ:

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. Thêm Swift Extensions

Đoạn mã trên đã dùng một tiện ích cho kiểu dữ liệu để cho phép bạn sao chép các bit thô của một mảng Int32 vào một Data. Dưới đây là mã cho tiện ích đó:

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)
  }
}

Khi xử lý bộ nhớ cấp thấp, bạn sẽ sử dụng dữ liệu "không an toàn" và đoạn mã trên yêu cầu bạn khởi tạo một mảng dữ liệu không an toàn. Tiện ích này giúp bạn làm được điều đó:

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. Chạy ứng dụng iOS

Chạy và kiểm thử ứng dụng.

Nếu mọi thứ diễn ra suôn sẻ, bạn sẽ thấy ứng dụng trên thiết bị của mình như sau:

74cbd28d9b1592ed.png

Tại nơi gửi thông báo "Mua sách của tôi để học giao dịch trực tuyến!", ứng dụng sẽ gửi lại một cảnh báo phát hiện thư rác với xác suất là 0,99!

14. Xin chúc mừng!

Giờ đây, bạn đã tạo một ứng dụng rất đơn giản có thể lọc văn bản để tìm bình luận không liên quan bằng cách sử dụng một mô hình được huấn luyện dựa trên dữ liệu dùng để gửi nội dung rác đến các blog.

Bước tiếp theo trong vòng đời điển hình của nhà phát triển là khám phá những gì cần thiết để tuỳ chỉnh mô hình dựa trên dữ liệu trong cộng đồng của riêng bạn. Bạn sẽ tìm hiểu cách thực hiện việc đó trong hoạt động tiếp theo của lộ trình.