Nếu ứng dụng của bạn sử dụng các mô hình TensorFlow Lite tuỳ chỉnh, bạn có thể dùng Firebase ML để triển khai các mô hình đó. Bằng cách triển khai các mô hình bằng Firebase, bạn có thể giảm kích thước tải xuống ban đầu của ứng dụng và cập nhật các mô hình học máy của ứng dụng mà không cần phát hành phiên bản mới của ứng dụng. Ngoài ra, với Remote Config và A/B Testing, bạn có thể linh hoạt phân phát các mô hình khác nhau cho các nhóm người dùng khác nhau.
Điều kiện tiên quyết
- Thư viện
MLModelDownloaderchỉ dành cho Swift. - TensorFlow Lite chỉ chạy trên các thiết bị sử dụng iOS 9 trở lên.
Mô hình TensorFlow Lite
Mô hình TensorFlow Lite là các mô hình học máy được tối ưu hoá để chạy trên thiết bị di động. Cách lấy mô hình TensorFlow Lite:
- Sử dụng một mô hình dựng sẵn, chẳng hạn như một trong các mô hình TensorFlow Lite chính thức.
- Chuyển đổi mô hình TensorFlow, mô hình Keras hoặc hàm cụ thể sang TensorFlow Lite.
Trước khi bắt đầu
Để sử dụng TensorFlow Lite với Firebase, bạn phải sử dụng CocoaPods vì TensorFlow Lite hiện không hỗ trợ việc cài đặt bằng Trình quản lý gói Swift. Hãy xem hướng dẫn cài đặt CocoaPods để biết hướng dẫn về cách cài đặt MLModelDownloader.
Sau khi cài đặt, hãy nhập Firebase và TensorFlowLite để sử dụng.
Swift
import FirebaseMLModelDownloader
import TensorFlowLite
1. Triển khai mô hình
Triển khai các mô hình TensorFlow tuỳ chỉnh bằng bảng điều khiển Firebase hoặc Firebase Admin SDK cho Python và Node.js. Xem phần Triển khai và quản lý mô hình tuỳ chỉnh.
Sau khi thêm một mô hình tuỳ chỉnh vào dự án Firebase, bạn có thể tham chiếu mô hình đó trong các ứng dụng bằng tên mà bạn đã chỉ định. Bất cứ lúc nào, bạn cũng có thể triển khai một mô hình TensorFlow Lite mới và tải mô hình mới xuống thiết bị của người dùng bằng cách gọi getModel() (xem bên dưới).
2. Tải mô hình xuống thiết bị và khởi động một trình thông dịch TensorFlow Lite
Để sử dụng mô hình TensorFlow Lite trong ứng dụng, trước tiên, hãy dùng SDK Firebase ML để tải phiên bản mới nhất của mô hình xuống thiết bị.Để bắt đầu tải mô hình xuống, hãy gọi phương thức getModel() của trình tải mô hình xuống, chỉ định tên mà bạn đã gán cho mô hình khi tải mô hình lên, cho dù bạn có muốn luôn tải mô hình mới nhất xuống hay không và các điều kiện mà bạn muốn cho phép tải xuống.
Bạn có thể chọn trong số 3 chế độ tải xuống:
| Loại tệp tải xuống | Mô tả |
|---|---|
localModel
|
Lấy mô hình cục bộ từ thiết bị.
Nếu không có mô hình cục bộ nào, thì thao tác này sẽ hoạt động như latestModel. Hãy sử dụng loại tải xuống này nếu bạn không muốn kiểm tra các bản cập nhật mô hình. Ví dụ: bạn đang sử dụng Cấu hình từ xa để truy xuất tên mô hình và bạn luôn tải các mô hình lên bằng tên mới (nên dùng). |
localModelUpdateInBackground
|
Lấy mô hình cục bộ từ thiết bị và bắt đầu cập nhật mô hình ở chế độ nền.
Nếu không có mô hình cục bộ nào, thì thao tác này sẽ hoạt động như latestModel. |
latestModel
|
Sử dụng mô hình mới nhất. Nếu mô hình cục bộ là phiên bản mới nhất, hãy trả về mô hình cục bộ. Nếu không, hãy tải mô hình mới nhất xuống. Hành vi này sẽ chặn cho đến khi phiên bản mới nhất được tải xuống (không nên). Chỉ sử dụng hành vi này trong trường hợp bạn cần phiên bản mới nhất một cách rõ ràng. |
Bạn nên tắt chức năng liên quan đến mô hình (ví dụ: làm mờ hoặc ẩn một phần giao diện người dùng) cho đến khi xác nhận rằng mô hình đã được tải xuống.
Swift
let conditions = ModelDownloadConditions(allowsCellularAccess: false)
ModelDownloader.modelDownloader()
.getModel(name: "your_model",
downloadType: .localModelUpdateInBackground,
conditions: conditions) { result in
switch (result) {
case .success(let customModel):
do {
// Download complete. Depending on your app, you could enable the ML
// feature, or switch from the local model to the remote model, etc.
// The CustomModel object contains the local path of the model file,
// which you can use to instantiate a TensorFlow Lite interpreter.
let interpreter = try Interpreter(modelPath: customModel.path)
} catch {
// Error. Bad model file?
}
case .failure(let error):
// Download was unsuccessful. Don't enable ML features.
print(error)
}
}
Nhiều ứng dụng bắt đầu tác vụ tải xuống trong mã khởi tạo, nhưng bạn có thể thực hiện việc này bất cứ lúc nào trước khi cần sử dụng mô hình.
3. Thực hiện suy luận trên dữ liệu đầu vào
Lấy hình dạng đầu vào và đầu ra của mô hình
Trình thông dịch mô hình TensorFlow Lite nhận dữ liệu đầu vào và tạo ra dữ liệu đầu ra là một hoặc nhiều mảng đa chiều. Các mảng này chứa các giá trị byte, int, long hoặc float. Trước khi có thể truyền dữ liệu đến một mô hình hoặc sử dụng kết quả của mô hình đó, bạn phải biết số lượng và kích thước ("hình dạng") của các mảng mà mô hình của bạn sử dụng.
Nếu tự xây dựng mô hình hoặc nếu định dạng đầu vào và đầu ra của mô hình được ghi lại, thì có thể bạn đã có thông tin này. Nếu không biết hình dạng và kiểu dữ liệu của đầu vào và đầu ra của mô hình, bạn có thể dùng trình thông dịch TensorFlow Lite để kiểm tra mô hình. Ví dụ:
Python
import tensorflow as tf interpreter = tf.lite.Interpreter(model_path="your_model.tflite") interpreter.allocate_tensors() # Print input shape and type inputs = interpreter.get_input_details() print('{} input(s):'.format(len(inputs))) for i in range(0, len(inputs)): print('{} {}'.format(inputs[i]['shape'], inputs[i]['dtype'])) # Print output shape and type outputs = interpreter.get_output_details() print('\n{} output(s):'.format(len(outputs))) for i in range(0, len(outputs)): print('{} {}'.format(outputs[i]['shape'], outputs[i]['dtype']))
Ví dụ về kết quả đầu ra:
1 input(s): [ 1 224 224 3] <class 'numpy.float32'> 1 output(s): [1 1000] <class 'numpy.float32'>
Chạy trình thông dịch
Sau khi xác định định dạng đầu vào và đầu ra của mô hình, hãy lấy dữ liệu đầu vào và thực hiện mọi phép biến đổi cần thiết trên dữ liệu để có được đầu vào có hình dạng phù hợp cho mô hình của bạn.Ví dụ: nếu mô hình của bạn xử lý hình ảnh và mô hình của bạn có kích thước đầu vào là [1, 224, 224, 3] giá trị dấu phẩy động, thì bạn có thể phải điều chỉnh các giá trị màu của hình ảnh theo một dải dấu phẩy động như trong ví dụ sau:
Swift
let image: CGImage = // Your input image
guard let context = CGContext(
data: nil,
width: image.width, height: image.height,
bitsPerComponent: 8, bytesPerRow: image.width * 4,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
) else {
return false
}
context.draw(image, in: CGRect(x: 0, y: 0, width: image.width, height: image.height))
guard let imageData = context.data else { return false }
var inputData = Data()
for row in 0 ..< 224 {
for col in 0 ..< 224 {
let offset = 4 * (row * context.width + col)
// (Ignore offset 0, the unused alpha channel)
let red = imageData.load(fromByteOffset: offset+1, as: UInt8.self)
let green = imageData.load(fromByteOffset: offset+2, as: UInt8.self)
let blue = imageData.load(fromByteOffset: offset+3, as: UInt8.self)
// Normalize channel values to [0.0, 1.0]. This requirement varies
// by model. For example, some models might require values to be
// normalized to the range [-1.0, 1.0] instead, and others might
// require fixed-point values or the original bytes.
var normalizedRed = Float32(red) / 255.0
var normalizedGreen = Float32(green) / 255.0
var normalizedBlue = Float32(blue) / 255.0
// Append normalized values to Data object in RGB order.
let elementSize = MemoryLayout.size(ofValue: normalizedRed)
var bytes = [UInt8](repeating: 0, count: elementSize)
memcpy(&bytes, &normalizedRed, elementSize)
inputData.append(&bytes, count: elementSize)
memcpy(&bytes, &normalizedGreen, elementSize)
inputData.append(&bytes, count: elementSize)
memcpy(&ammp;bytes, &normalizedBlue, elementSize)
inputData.append(&bytes, count: elementSize)
}
}
Sau đó, hãy sao chép NSData đầu vào vào trình thông dịch rồi chạy:
Swift
try interpreter.allocateTensors()
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()
Bạn có thể nhận được đầu ra của mô hình bằng cách gọi phương thức output(at:) của trình thông dịch.
Cách bạn sử dụng đầu ra phụ thuộc vào mô hình bạn đang dùng.
Ví dụ: nếu đang thực hiện phân loại, thì bước tiếp theo có thể là bạn sẽ ánh xạ các chỉ mục của kết quả với nhãn mà chúng đại diện:
Swift
let output = try interpreter.output(at: 0)
let probabilities =
UnsafeMutableBufferPointer<Float32>.allocate(capacity: 1000)
output.data.copyBytes(to: probabilities)
guard let labelPath = Bundle.main.path(forResource: "retrained_labels", ofType: "txt") else { return }
let fileContents = try? String(contentsOfFile: labelPath)
guard let labels = fileContents?.components(separatedBy: "\n") else { return }
for i in labels.indices {
print("\(labels[i]): \(probabilities[i])")
}
Phụ lục: Bảo mật mô hình
Bất kể bạn cung cấp mô hình TensorFlow Lite cho Firebase ML như thế nào, Firebase ML đều lưu trữ các mô hình đó ở định dạng protobuf được chuyển đổi tuần tự tiêu chuẩn trong bộ nhớ cục bộ.
Về lý thuyết, điều này có nghĩa là bất kỳ ai cũng có thể sao chép mô hình của bạn. Tuy nhiên, trên thực tế, hầu hết các mô hình đều quá dành riêng cho ứng dụng và bị làm rối mã nguồn bằng các quy trình tối ưu hoá đến mức rủi ro tương tự như việc đối thủ cạnh tranh tháo rời và sử dụng lại mã của bạn. Tuy nhiên, bạn nên biết về rủi ro này trước khi sử dụng một mô hình tuỳ chỉnh trong ứng dụng của mình.