Se o app usa modelos personalizados do TensorFlow Lite, é possível usar o Firebase ML para implantar os modelos. Ao implantar modelos com o Firebase, é possível reduzir o tamanho inicial do download do app e atualizar os modelos de ML sem lançar uma nova versão do aplicativo. Além disso, com a Configuração remota e o Teste A/B, você pode 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
Independentemente de como você disponibiliza seus modelos do TensorFlow Lite para o Firebase ML, o Firebase ML os armazena localmente no formato padrão protobuf serializado.
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.