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

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

Предварительные условия

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

Модели TensorFlow Lite

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

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

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

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

Быстрый

import FirebaseMLModelDownloader
import TensorFlowLite

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

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

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

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

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

Чтобы начать загрузку модели, вызовите метод 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 в локальном хранилище.

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