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à bạn đã tạo trong các lớp học lập trình trước đó về cách Bắt đầu với lớp học lập trình về phân loại văn bản trên thiết bị di động.
Điều kiện tiên quyết
- Lớp học lập trình này dành cho các nhà phát triển giàu kinh nghiệm mới làm quen với công nghệ học máy.
- Lớp học lập trình này là một phần của lộ trình được sắp xếp theo trình tự. Nếu bạn chưa hoàn tất phần Tạo ứng dụng nhắn tin kiểu cơ bản hoặc Tạo mô hình học máy để phát hiện bình luận rác, vui lòng dừng lại và làm ngay.
Nội dung bạn sẽ [tạo hoặc tìm hiểu]
- Bạn sẽ tìm hiểu cách tích hợp mô hình tuỳ chỉnh vào ứng dụng đã tạo ở các bước trước.
Bạn cần có
- Android Studio hoặc CocoaPods cho iOS
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 nhân bản kho lưu trữ này và 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ể sử dụng 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 Tạo mô hình học máy để phát hiện bình luận vi phạm, bạn đã tạo một mô hình .TFLITE.
Bạn đã tải tệp mô hình xuống. Nếu chưa có, bạn có thể lấy trong kho lưu trữ dành cho lớp học lập trình này. Mô hình này có tại đây.
Thêm tệp này vào dự án bằng cách tạo thư mục tài sản.
- Khi sử dụng trình điều hướng dự án, hãy đảm bảo bạn đã chọn Android ở trên cùng.
- Nhấp chuột phải vào thư mục app (ứng dụng). Chọn New (Mới) > Directory (Thư mục).
- Trong hộp thoại New Directory (Thư mục mới), hãy chọn src/main/assets.
Bạn sẽ thấy thư mục assets (thành phần) mới trong ứng dụng.
- Nhấp chuột phải vào assets (thành phần).
- Trên trình đơn mở ra, bạn sẽ thấy (trên máy Mac) Hiện trong Finder. Chọn mục đó. (Trên Windows, bạn sẽ thấy dòng chữ Show in Explorer (Hiện trong Explorer), trên Ubuntu, bạn sẽ thấy dòng chữ Show in Files (Hiện trong Tệp).)
Finder sẽ khởi chạy để hiển thị vị trí tệp (File Explorer trên Windows, Files trên Linux).
- Sao chép các tệp
labels.txt
,model.tflite
vàvocab
vào thư mục này.
- Quay lại Android Studio và bạn sẽ thấy các nội dung này trong thư mục Asset (tài sản).
4. Cập nhật build.gradle của bạn để 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 cấp độ, vì vậy, hãy nhớ tìm cấp độ 1 của ứng dụng. Trong trình khám phá dự án ở chế độ xem Android, hãy tìm mục này trong phần Gradle Scripts (Tập lệnh Gradle). URL chính xác sẽ được gắn nhãn .app như được hiển thị ở đây:
Bạn cần thực hiện hai thay đổi đối với tệp này. Danh sách đầu tiên là trong mục phần phụ thuộc ở dưới cùng. Thêm 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 phiên bản mới nhất.
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 android
> default config
và thay đổi thành 21:
Giờ đây, bạn đã có tất cả các phần phụ thuộc, vì vậy, đã đến lúc bắt đầu lập trình!
5. Thêm Lớp trợ giúp
Để tách logic suy luận (trong đó ứ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. Hãy gọi lớp này là "helper" (trợ giúp).
- Nhấp chuột phải vào tên gói chứa mã
MainActivity
. - Chọn New (Mới) > Package (Gói).
- Bạn sẽ thấy một hộp thoại ở giữa màn hình yêu cầu nhập tên gói. Hãy thêm mã này vào cuối tên gói hiện tại. (Ở đây, hàm này được gọi là trợ giúp.)
- 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.
- Chọn New > Java Class (Mới > Lớp Java) rồi đặt tên là
TextClassificationClient
. Bạn sẽ chỉnh sửa tệp này trong bước tiếp theo.
Lớp trình trợ giúp TextClassificationClient
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 {
}
- Cập nhật tệp bằng 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à tóm tắt độ phức tạp của việc quản lý việc 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 bản sao của một loại 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 các thư viện tác vụ văn bản và giúp bạn bằng cách chuyển đổi chuỗi của bạn thành mã thông báo, sử dụng đúng độ dài trình tự, truyền chuỗi đó đến mô hình và phân tích cú pháp kết quả.
(Để biết thêm thông tin chi tiết về các thuộc tính này, hãy xem lại bài viết Tạo mô hình học máy để phát hiện bình luận rác.)
Hoạt động phân loại được thực hiện trong phương thức phân loại, trong đó bạn truyền vào 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, hệ thống thường trả về tất cả các câu trả lời theo xác suất được chỉ định. Ví dụ: nếu bạn chuyển tin nhắn trông giống như spam, bạn sẽ nhận được một danh sách gồm 2 câu trả lời; một có khả năng là spam và một có xác suất không phải là spam. 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 thông tin đó 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 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!
- Ở đầ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
- Tiếp theo, bạn sẽ muốn tải các trình trợ giúp. Trong
onCreate
, ngay sau dòngsetContentView
, hãy thêm các dòng sau để tạo bản sao 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
}
- Cập nhật thành 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ừ việc chỉ xuất dữ liệu đầu vào của người dùng sang việc phân loại dữ liệu đó trước.
- 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, nhận lại kết quả:
var results:List<Category> = client.classify(toSend)
Chỉ có 2 danh mục là False
và True
. (TensorFlow sắp xếp các giá trị này theo thứ tự bảng chữ cái, vì vậy, False sẽ là mục 0 và True sẽ là mục 1.)
- Để biết điểm số cho xác suất giá trị là
True
, bạn có thể xem results[1].score như sau:
val score = results[1].score
- Chọn một giá trị ngưỡng (trong trường hợp này là 0,8), trong đó bạn cho biết nếu điểm số của danh mục True (Đúng) cao hơn giá trị ngưỡng (0,8), thì thư đó 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()
}
- Xem mô hình thực tế tại đây. Thông báo "Hãy truy cập vào blog của tôi để mua hàng!" đã bị gắn cờ là có nhiều khả năng là nội dung rác:
Ngược lại, cụm từ "Cảm ơn bạn! Hướng dẫn rất thú vị" có khả năng rất thấp là nội dung rác:
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 nhân bản kho lưu trữ này và tải ứng dụng từ TextClassificationStep1
. Bạn có thể tìm thấy mã này trong đường dẫn TextClassificationOnMobile->iOS
.
Bạn cũng có thể sử dụng mã đã hoàn tất dưới dạng TextClassificationStep2
.
Trong lớp học lập trình Tạo mô hình học máy để phát hiện bình luận rác, 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 đầu ra mà không cần lọc.
Bây giờ, bạn sẽ cập nhật ứng dụng đó để sử dụng mô hình TensorFlow Lite nhằm phát hiện bình luận rác trong văn bản trước khi gửi. Bạn 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 ứng dụng thực tế có thể có bảng thông báo, cuộc trò chuyện hoặc nội dung tương tự).
Để bắt đầu, bạn cần có ứng dụng từ 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 công cụ này, bạn có thể làm theo hướng dẫn tại https://cocoapods.org/.
- Sau khi cài đặt CocoaPods, hãy tạo một tệp có tên 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ẽ có dạng 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, hãy chuyển đến thư mục đó và 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ỏ) và một tệp .xcworkspace
mới được tạo cho bạn. Bạn sẽ sử dụng lớp này trong tương lai thay vì .xcproject
.
Nếu không thực hiện được, hãy đảm bảo rằng bạn có Podfile trong cùng thư mục với .xcproject
. Tệp pod nằm sai thư mụ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ể tạo ra mô hình (dưới dạng model.tflite
) và từ vựng (dưới dạng vocab.txt
).
- 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:
Sau khi hoàn tất, bạn sẽ thấy chúng trong dự án của mình:
- Kiểm tra kỹ để đảm bảo rằng các lớp này được thêm vào gói (để đượ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):
9. Tải từ vựng
Khi phân loại NLP, mô hình sẽ được huấn luyện bằng các từ được mã hoá thành vectơ. Mô hình này 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ó các từ vựng khác nhau và điều quan trọng là bạn phải sử dụng từ vựng cho mô hì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 trong Xcode để xem các cách mã hoá. Các từ như "song" (bài hát) được mã hoá thành 6 và "love" (tình yêu) thành 12. Thứ tự thực sự là thứ tự tần suất, vì vậy "I" là từ phổ biến nhất trong tập dữ liệu, theo sau 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 khám phá mã đó. Bắt đầu bằng cách tải từ vựng.
- Xác định biến cấp lớp để lưu trữ từ điển:
var words_dictionary = [String : Int]()
- 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")
}
}
- Bạn có thể chạy mã này bằng cách gọi mã đó 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 trình tự mã thông báo
Người dùng sẽ nhập các từ dưới dạng câu và các từ này 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ô hình NLP thường chấp nhận độ dài trình tự cố định. Có một số ngoại lệ với các mô hình được tạo bằng ragged tensors
, nhưng hầu hết bạn sẽ thấy vấn đề này đã được khắc phục. Khi tạo mô hình, bạn đã chỉ định chiều dài này. Hãy nhớ sử dụng cùng một độ dài trong ứng dụng iOS.
Giá trị mặc định trong Colab cho Trình tạo mô hình TensorFlow Lite mà bạn đã sử dụng trước đó là 20, vì vậy, hãy thiết lập giá trị đó tại đây:
let SEQUENCE_LENGTH = 20
Thêm func
này để lấy chuỗi, chuyển thành chữ thường và xoá mọi dấu 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 trình tự sẽ là Int32. Lựa chọn này được cân nhắc kỹ lưỡng vì khi truyền 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 tuần tự là số nguyên 32 bit. Điều này sẽ giúp bạn dễ dàng hơn (một chút) khi truyền chuỗi đến mô hình.
11. Phân loại
Để phân loại một câu, trước tiên, bạn phải chuyển đổi câu đó thành một chuỗi mã thông báo dựa trên các từ trong câu. Bạn đã thực hiện việc này ở bước 9.
Bây giờ, bạn sẽ lấy câu và truyền câu đó vào mô hình, yêu cầu 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 theo trình tự của bạn, vốn là một mảng gồm các kiểu 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 trình thông dịch bằng tệp đó.
Bước tiếp theo sẽ là sao chép bộ nhớ cơ bản được lưu trữ trong trình tự vào một vùng đệm có tên là myData,
để có thể truyền bộ nhớ đó đến một tensor. Khi triển khai nhóm TensorFlow Lite cũng như trình thông dịch, bạn có quyền truy cập vào Loại tensor.
Bắt đầu mã như thế này (vẫn ở phân loại 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
. Sau này, khoá này sẽ được triển khai dưới dạng phần mở rộng.
Bây giờ, đã đến lúc phân bổ tensor trên trình thông dịch, sao chép bộ đệm dữ liệu bạn vừa tạo vào tensor đầu vào, sau đó 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 kết quả của trình thông dịch.
Đây sẽ là các giá trị thô (4 byte cho mỗi tế bào thần kinh) mà sau đó bạn sẽ phải đọc và chuyển đổi. Vì mô hình cụ thể này có 2 tế bào thần kinh đầu ra, bạn sẽ cần đọc trong 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 đó là 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, bạn có thể dễ dàng phân tích cú pháp dữ liệu để xác định chất lượng thư rác. Mô hình này có 2 đầu ra, đầu tiên có xác suất tin nhắn không phải là thư rác, đầu ra thứ hai có xác suất đúng như vậy. 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, 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 tiện ích Swift
Mã ở trên đã sử dụng một tiện ích cho loại Dữ liệu để cho phép bạn sao chép các bit thô của một mảng Int32 vào 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ử dụng dữ liệu "không an toàn" và mã ở trên cần bạn khởi chạy 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ị như sau:
Khi thông báo "Mua sách của tôi để học cách kinh doanh trực tuyến!" được gửi đi, ứng dụng sẽ gửi lại một cảnh báo phát hiện nội dung 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 để lọc văn bản tìm bình luận rác 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 bình luận rác trên blog.
Bước tiếp theo trong vòng đời thông thường 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 tìm thấy 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 này trong hoạt động tiếp theo của lộ trình.