Check out what’s new from Firebase at Google I/O 2022. Learn more

Используйте пользовательскую модель TensorFlow Lite на платформах Apple.

Если ваше приложение использует пользовательские модели TensorFlow Lite , вы можете использовать Firebase ML для развертывания своих моделей. Развертывая модели с помощью Firebase, вы можете уменьшить первоначальный размер загружаемого приложения и обновить модели машинного обучения своего приложения, не выпуская новую версию своего приложения. А с помощью удаленной настройки и A/B-тестирования вы можете динамически обслуживать разные модели для разных групп пользователей.

Предпосылки

  • Библиотека MLModelDownloader доступна только для Swift.
  • TensorFlow Lite работает только на устройствах с iOS 9 и новее.

Модели TensorFlow Lite

Модели TensorFlow Lite — это модели машинного обучения, оптимизированные для работы на мобильных устройствах. Чтобы получить модель TensorFlow Lite:

Прежде чем вы начнете

Чтобы использовать TensorFlowLite с Firebase, вы должны использовать CocoaPods, поскольку TensorFlowLite в настоящее время не поддерживает установку с диспетчером пакетов Swift. Инструкции по установке MLModelDownloader см. в руководстве по установке MLModelDownloader .

После установки импортируйте Firebase и TensorFlowLite, чтобы использовать их.

Быстрый

import FirebaseMLModelDownloader
import TensorFlowLite

1. Разверните свою модель

Разверните свои пользовательские модели TensorFlow с помощью консоли Firebase или пакетов Firebase Admin Python и Node.js SDK. См. Развертывание пользовательских моделей и управление ими .

После добавления пользовательской модели в проект Firebase вы можете ссылаться на модель в своих приложениях, используя указанное вами имя. В любое время вы можете развернуть новую модель TensorFlow Lite и загрузить новую модель на устройства пользователей, вызвав getModel() (см. ниже).

2. Загрузите модель на устройство и инициализируйте интерпретатор TensorFlow Lite.

Чтобы использовать модель TensorFlow Lite в своем приложении, сначала используйте SDK Firebase ML, чтобы загрузить последнюю версию модели на устройство.

Чтобы начать загрузку модели, вызовите метод getModel() загрузчика модели, указав имя, которое вы присвоили модели при ее загрузке, хотите ли вы всегда загружать последнюю модель и условия, при которых вы хотите разрешить загрузку.

Вы можете выбрать один из трех способов загрузки:

Тип загрузки Описание
localModel Получите локальную модель с устройства. Если нет доступной локальной модели, это ведет себя как latestModel . Используйте этот тип загрузки, если вы не заинтересованы в проверке обновлений модели. Например, вы используете Remote Config для получения имен моделей и всегда загружаете модели под новыми именами (рекомендуется).
localModelUpdateInBackground Получите локальную модель с устройства и запустите обновление модели в фоновом режиме. Если нет доступной локальной модели, это ведет себя как latestModel .
latestModel Получите последнюю модель. Если локальная модель является последней версией, возвращает локальную модель. В противном случае загрузите последнюю модель. Это поведение будет заблокировано до тех пор, пока не будет загружена последняя версия (не рекомендуется). Используйте это поведение только в тех случаях, когда вам явно нужна последняя версия.

Вы должны отключить функции, связанные с моделью, например сделать серым или скрыть часть вашего пользовательского интерфейса, пока вы не подтвердите, что модель была загружена.

Быстрый

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 для проверки вашей модели. Например:

Питон

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] значений с плавающей запятой, вам может потребоваться масштабировать значения цвета изображения до диапазона с плавающей запятой, как в следующем примере. :

Быстрый

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 ..&lt; 224 {
  for col in 0 ..&lt; 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(&amp;bytes, &amp;normalizedRed, elementSize)
    inputData.append(&amp;bytes, count: elementSize)
    memcpy(&amp;bytes, &amp;normalizedGreen, elementSize)
    inputData.append(&amp;bytes, count: elementSize)
    memcpy(&ammp;bytes, &amp;normalizedBlue, elementSize)
    inputData.append(&amp;bytes, count: elementSize)
  }
}

Затем скопируйте входные данные NSData в интерпретатор и запустите его:

Быстрый

try interpreter.allocateTensors()
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()

Вы можете получить выходные данные модели, вызвав метод интерпретатора output(at:) . То, как вы используете выходные данные, зависит от используемой модели.

Например, если вы выполняете классификацию, в качестве следующего шага вы можете сопоставить индексы результата с метками, которые они представляют:

Быстрый

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 в локальном хранилище.

Теоретически это означает, что любой может скопировать вашу модель. Однако на практике большинство моделей настолько зависят от приложения и запутаны оптимизацией, что риск аналогичен риску дизассемблирования и повторного использования вашего кода конкурентами. Тем не менее, вы должны знать об этом риске, прежде чем использовать пользовательскую модель в своем приложении.