Если в вашем приложении используются специальные модели TensorFlow Lite, вы можете развернуть их с помощью Firebase ML. Развертывая модели с помощью Firebase, вы можете уменьшить размер приложения при первой загрузке и обновлять модели машинного обучения без выпуска новой версии приложения. А с помощью Remote Config и A/B Testing вы можете динамически предоставлять разные модели разным группам пользователей.
Требования
- Библиотека
MLModelDownloaderдоступна только для Swift. - TensorFlow Lite работает только на устройствах с iOS 9 и более поздних версий.
Модели TensorFlow Lite
Модели TensorFlow Lite – это модели машинного обучения, оптимизированные для работы на мобильных устройствах. Чтобы получить модель TensorFlow Lite:
- Используйте готовую модель, например одну из официальных моделей TensorFlow Lite.
- Преобразование модели TensorFlow, модели Keras или конкретной функции в TensorFlow Lite.
Подготовка
Чтобы использовать TensorFlow Lite с Firebase, необходимо установить CocoaPods, поскольку TensorFlow Lite пока не поддерживает установку с помощью Swift Package Manager. Инструкции по установке MLModelDownloader приведены в руководстве по установке CocoaPods.
После установки импортируйте Firebase и TensorFlowLite, чтобы использовать их.
Swift
import FirebaseMLModelDownloader
import TensorFlowLite
1. Как развернуть модель
Развертывать собственные модели TensorFlow можно с помощью консоли Firebase или Firebase Admin SDK для Python и Node.js. Подробнее о том, как развертывать специальные модели и управлять ими…
После того как вы добавите специальную модель в проект Firebase, вы сможете ссылаться на нее в своих приложениях, используя заданное вами имя. Вы можете в любое время развернуть новую модель TensorFlow Lite и скачать ее на устройства пользователей, вызвав функцию getModel() (см. ниже).
2. Скачайте модель на устройство и инициализируйте интерпретатор TensorFlow Lite.
Чтобы использовать модель TensorFlow Lite в приложении, сначала скачайте ее последнюю версию на устройство с помощью SDK Firebase ML.Чтобы начать скачивание модели, вызовите метод getModel() загрузчика модели, указав название, которое вы присвоили модели при загрузке, хотите ли вы всегда скачивать последнюю версию модели и условия, при которых вы хотите разрешить скачивание.
Вы можете выбрать один из трех вариантов:
| Тип скачивания | Описание |
|---|---|
localModel
|
Получите локальную модель с устройства.
Если локальная модель недоступна, это правило работает так же, как latestModel. Этот тип скачивания подходит, если вы не хотите проверять наличие обновлений модели. Например, вы используете Remote Config, чтобы получать названия моделей, и всегда загружаете модели под новыми названиями (рекомендуется). |
localModelUpdateInBackground
|
Получите локальную модель с устройства и начните обновлять модель в фоновом режиме.
Если локальная модель недоступна, это правило работает так же, как latestModel. |
latestModel
|
Используйте последнюю версию модели. Если локальная модель является последней версией, возвращает локальную модель. В противном случае скачайте последнюю версию модели. В этом случае загрузка последней версии будет блокировать все другие операции до момента ее завершения (не рекомендуется). Используйте это поведение только в тех случаях, когда вам явно нужна последняя версия. |
Отключите функции, связанные с моделью, например сделайте неактивными или скройте элементы интерфейса, пока не убедитесь, что модель скачана.
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)
}
}
Многие приложения начинают скачивание в коде инициализации, но вы можете сделать это в любой момент до того, как модель понадобится.
3. Выполнять логический вывод на основе входных данных.
Как получить информацию о входных и выходных данных модели
Интерпретатор модели TensorFlow Lite принимает на вход и возвращает на выход один или несколько многомерных массивов. Эти массивы содержат значения byte, int, long или float. Прежде чем передавать данные в модель или использовать ее результаты, необходимо узнать количество и размеры массивов, которые она использует.
Если вы создали модель самостоятельно или если формат входных и выходных данных модели задокументирован, у вас уже может быть эта информация. Если вы не знаете форму и тип данных входных и выходных данных модели, вы можете использовать интерпретатор TensorFlow Lite, чтобы проверить модель. Пример:
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']))
Пример выходных данных:
1 input(s): [ 1 224 224 3] <class 'numpy.float32'> 1 output(s): [1 1000] <class 'numpy.float32'>
Как запустить интерпретатор
Определив формат входных и выходных данных модели, получите входные данные и выполните необходимые преобразования, чтобы привести их к нужному формату.Например, если ваша модель обрабатывает изображения и имеет входные размеры [1, 224, 224, 3] значений с плавающей запятой, вам может потребоваться масштабировать значения цвета изображения до диапазона с плавающей запятой, как в следующем примере:
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)
}
}
Затем скопируйте входные данные NSData в интерпретатор и запустите его:
Swift
try interpreter.allocateTensors()
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()
Выходные данные модели можно получить, вызвав метод output(at:) интерпретатора.
Способ использования выходных данных зависит от модели.
Например, если вы выполняете классификацию, следующим шагом может быть сопоставление индексов результата с ярлыками, которые они представляют:
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])")
}
Приложение. Безопасность моделей
Независимо от того, как вы предоставляете модели TensorFlow Lite для Firebase ML, Firebase ML хранит их в стандартном сериализованном формате protobuf в локальном хранилище.
Теоретически это означает, что любой может скопировать вашу модель. Однако на практике большинство моделей настолько специфичны для приложений и запутаны оптимизациями, что риск аналогичен риску того, что конкуренты разберут и повторно используют ваш код. Тем не менее, прежде чем использовать в приложении специальную модель, вам следует знать об этом риске.