Se o app usa modelos personalizados do TensorFlow Lite, é possível usar Firebase ML para implantar os modelos. Ao fazer isso com o Firebase, você pode reduzir o tamanho inicial do download do app e atualizar os modelos de ML sem lançar uma nova versão do app. Além disso, com o Remote Config e o A/B Testing, é possível exibir dinamicamente diferentes modelos para diferentes conjuntos de usuários.
Pré-requisitos
- A biblioteca
MLModelDownloader
está disponível apenas para Swift. - O TensorFlow Lite é executado apenas em dispositivos que usam o iOS 9 e versões mais recentes.
Modelos do TensorFlow Lite
Os modelos do TensorFlow Lite são modelos de ML otimizados para execução em dispositivos móveis. Para receber um modelo do TensorFlow Lite:
- Use um modelo pré-criado, como um dos modelos oficiais do TensorFlow Lite.
- Converta um modelo do TensorFlow, um modelo Keras ou uma função concreta para o TensorFlow Lite.
Antes de começar
Para usar o TensorFlowLite com o Firebase, use o CocoaPods. O TensorFlowLite não é compatível com a instalação usando o Gerenciador de pacotes do Swift. Consulte o
Guia de instalação do CocoaPods para instruções
de instalação do MLModelDownloader
.
Depois de instalado, importe o Firebase e o TensorFlowLite para usá-los.
Swift
import FirebaseMLModelDownloader
import TensorFlowLite
1. Implantar seu modelo
Implante seus modelos personalizados do TensorFlow usando o console do Firebase ou os SDKs Admin para Python e Node.js do Firebase. Consulte Implantar e gerenciar modelos personalizados.
Depois de adicionar um modelo personalizado ao seu projeto do Firebase, você pode referenciá-lo
nos seus apps usando o nome especificado. A qualquer momento é possível implantar
um novo modelo do TensorFlow Lite e fazer o download do novo modelo nos dispositivos dos usuários
chamando getModel()
(veja abaixo).
2. Fazer o download do modelo no dispositivo e inicializar um intérprete do TensorFlow Lite
Para usar o modelo do TensorFlow Lite no app, primeiro use o SDK do Firebase ML para fazer o download da versão mais recente do modelo no dispositivo.Para iniciar o download do modelo, chame o método getModel()
da ferramenta de download de modelos,
especificando o nome atribuído ao modelo durante o upload,
se você quer sempre fazer o download do modelo mais recente e as condições em que
quer permitir o download.
Você pode escolher entre três comportamentos de download:
Tipo de download | Descrição |
---|---|
localModel
|
Consiga o modelo local do dispositivo.
Se não houver um modelo local disponível, o
comportamento será como latestModel . Use esse
tipo de download se você não tiver interesse em
verificar as atualizações do modelo. Por exemplo,
você está usando o Configuração remota para recuperar
nomes de modelos e sempre faz upload de modelos
usando novos nomes (recomendado). |
localModelUpdateInBackground
|
Consiga o modelo local do dispositivo e
comece a atualizá-lo em segundo plano.
Se não houver um modelo local disponível, o
comportamento será como latestModel . |
latestModel
|
Receba o modelo mais recente. Se o modelo local for a versão mais recente, retornará o modelo local. Caso contrário, faça o download do modelo mais recente. Esse comportamento será bloqueado até o download da versão mais recente (não recomendado). Use esse comportamento somente quando precisar da versão mais recente. |
Desative o recurso relacionado ao modelo, por exemplo, usar o recurso esmaecido ou ocultar parte da IU, até confirmar que o download do modelo foi concluído.
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)
}
}
Muitos apps iniciam a tarefa de download no código de inicialização, mas você pode fazer isso a qualquer momento antes de precisar usar o modelo.
3. Realizar inferência em dados de entrada
Gerar formas de entrada e saída do modelo
O intérprete de modelos do TensorFlow Lite utiliza como entrada e produz como saída
uma ou mais matrizes multidimensionais. Essas matrizes contêm valores
byte
, int
, long
ou
float
. Antes de transmitir dados para um modelo ou usar o resultado dele, você precisa saber
o número e as dimensões ("forma") das matrizes usadas pelo modelo.
Se você mesmo criou o modelo ou se o formato de entrada e saída do modelo está documentado, talvez já tenha essas informações. Se você não sabe qual é a forma e o tipo de dados da entrada e da saída do seu modelo, pode usar o intérprete do TensorFlow Lite para inspecionar seu modelo. Exemplo:
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']))
Exemplo de saída:
1 input(s): [ 1 224 224 3] <class 'numpy.float32'> 1 output(s): [1 1000] <class 'numpy.float32'>
Executar o intérprete
Depois de determinar o formato da entrada e da saída do modelo, colete os dados de entrada e execute quaisquer transformações nos dados necessários para ter uma entrada da forma certa para o modelo.Por exemplo, se o modelo processar imagens e seu modelo tiver dimensões de entrada
de valores de ponto flutuante [1, 224, 224, 3]
, talvez seja necessário dimensionar
os valores de cor da imagem para um intervalo de ponto flutuante, como no exemplo a seguir:
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)
}
}
Em seguida, copie a entrada NSData
para o interpretador e a execute:
Swift
try interpreter.allocateTensors()
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()
Chame o método output(at:)
do interpretador para gerar a saída do modelo.
Como você usa a saída depende do modelo que está usando.
Por exemplo, se você estiver realizando uma classificação, o próximo passo poderá ser atribuir os índices do resultado aos rótulos representados:
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])")
}
Apêndice: segurança do modelo
Não importa como você disponibiliza seus modelos do TensorFlow Lite para Firebase ML, Firebase ML as armazena no formato protobuf serializado padrão no armazenamento local.
Teoricamente, isso significa que qualquer pessoa pode copiar seu modelo. No entanto, na prática, a maioria dos modelos é tão específica de cada aplicativo e ofuscada por otimizações que o risco é comparável ao de concorrentes desmontando e reutilizando seu código. Apesar disso, você deve estar ciente desse risco antes de usar um modelo personalizado no seu app.