Adicionar metadados a modelos do TensorFlow Lite

Os metadados do TensorFlow Lite fornecem um padrão para descrições de modelos. Os metadados são uma importante fonte de conhecimento sobre o que o modelo faz e suas informações de entrada/saída. Os metadados consistem em ambos

Todos os modelos de imagem publicados no TensorFlow Hub foram preenchidos com metadados.

Modelo com formato de metadados

modelo_com_metadados
Figura 1. Modelo TFLite com metadados e arquivos associados.

Os metadados do modelo são definidos em metadata_schema.fbs , um arquivo FlatBuffer . Conforme mostrado na Figura 1, ele é armazenado no campo de metadados do esquema do modelo TFLite , sob o nome "TFLITE_METADATA" . Alguns modelos podem vir com arquivos associados, como arquivos de etiquetas de classificação . Esses arquivos são concatenados ao final do arquivo do modelo original como um ZIP usando o modo "anexar" do ZipFile (modo 'a' ). O TFLite Interpreter pode consumir o novo formato de arquivo da mesma forma que antes. Consulte Compactar os arquivos associados para obter mais informações.

Consulte as instruções abaixo sobre como preencher, visualizar e ler metadados.

Configure as ferramentas de metadados

Antes de adicionar metadados ao seu modelo, você precisará de uma configuração de ambiente de programação Python para executar o TensorFlow. Há um guia detalhado sobre como configurar isso aqui .

Após configurar o ambiente de programação Python, você precisará instalar ferramentas adicionais:

pip install tflite-support

As ferramentas de metadados do TensorFlow Lite são compatíveis com Python 3.

Adicionando metadados usando Flatbuffers Python API

Existem três partes nos metadados do modelo no esquema :

  1. Informações do modelo – Descrição geral do modelo, bem como itens como termos de licença. Consulte ModelMetadata .
  2. Informações de entrada – Descrição das entradas e pré-processamento necessários, como normalização. Consulte SubGraphMetadata.input_tensor_metadata .
  3. Informações de saída – Descrição da saída e do pós-processamento necessário, como mapeamento para rótulos. Consulte SubGraphMetadata.output_tensor_metadata .

Como o TensorFlow Lite oferece suporte apenas a subgráficos únicos neste momento, o gerador de código do TensorFlow Lite e o recurso Android Studio ML Binding usarão ModelMetadata.name e ModelMetadata.description , em vez de SubGraphMetadata.name e SubGraphMetadata.description , ao exibir metadados e gerar código.

Tipos de entrada/saída suportados

Os metadados do TensorFlow Lite para entrada e saída não são projetados com tipos de modelo específicos em mente, mas sim com tipos de entrada e saída. Não importa o que o modelo faz funcionalmente, desde que os tipos de entrada e saída consistam no seguinte ou em uma combinação dos seguintes, ele é compatível com os metadados do TensorFlow Lite:

  • Recurso - Números que são inteiros sem sinal ou float32.
  • Imagem - Atualmente, os metadados suportam imagens RGB e em escala de cinza.
  • Caixa delimitadora - Caixas delimitadoras de formato retangular. O esquema oferece suporte a vários esquemas de numeração .

Empacote os arquivos associados

Os modelos do TensorFlow Lite podem vir com diferentes arquivos associados. Por exemplo, modelos de linguagem natural geralmente possuem arquivos de vocabulário que mapeiam partes de palavras para IDs de palavras; modelos de classificação podem ter arquivos de rótulos que indicam categorias de objetos. Sem os arquivos associados (se houver), um modelo não funcionará bem.

Os arquivos associados agora podem ser agrupados com o modelo por meio da biblioteca Python de metadados. O novo modelo do TensorFlow Lite se torna um arquivo zip que contém o modelo e os arquivos associados. Ele pode ser descompactado com ferramentas zip comuns. Este novo formato de modelo continua usando a mesma extensão de arquivo, .tflite . É compatível com a estrutura e intérprete TFLite existentes. Consulte Compactar metadados e arquivos associados no modelo para obter mais detalhes.

As informações do arquivo associado podem ser registradas nos metadados. Dependendo do tipo de arquivo e de onde o arquivo está anexado (ou seja, ModelMetadata , SubGraphMetadata e TensorMetadata ), o gerador de código Android do TensorFlow Lite pode aplicar o pré/pós-processamento correspondente automaticamente ao objeto. Consulte a seção <uso do Codegen> de cada tipo de arquivo associado no esquema para obter mais detalhes.

Parâmetros de normalização e quantização

A normalização é uma técnica comum de pré-processamento de dados em aprendizado de máquina. O objetivo da normalização é alterar os valores para uma escala comum, sem distorcer as diferenças nas faixas de valores.

A quantização de modelos é uma técnica que permite representações de pesos com precisão reduzida e, opcionalmente, ativações para armazenamento e computação.

Em termos de pré-processamento e pós-processamento, a normalização e a quantização são duas etapas independentes. Aqui estão os detalhes.

Normalização Quantização

Um exemplo dos valores dos parâmetros da imagem de entrada no MobileNet para modelos float e quant, respectivamente.
Modelo flutuante :
- média: 127,5
- padrão: 127,5
Modelo quantitativo :
- média: 127,5
- padrão: 127,5
Modelo flutuante :
- zeroPonto: 0
- escala: 1,0
Modelo quantitativo :
- ponto zero: 128,0
escala: 0,0078125f




Quando invocar?


Entradas : se os dados de entrada forem normalizados no treinamento, os dados de entrada da inferência precisam ser normalizados de acordo.
Saídas : os dados de saída não serão normalizados em geral.
Os modelos flutuantes não precisam de quantização.
O modelo quantizado pode ou não precisar de quantização no pré/pós processamento. Depende do tipo de dados dos tensores de entrada/saída.
- tensores flutuantes: não é necessária quantização no pré/pós processamento. Quant op e dequant op são inseridos no gráfico do modelo.
- tensores int8/uint8: precisam de quantização no pré/pós processamento.


Fórmula


normalized_input = (entrada - média) / std
Quantizar para entradas :
q = f / escala + ponto zero
Desquantize para saídas :
f = (q - ponto zero) * escala

Onde estão os parâmetros
Preenchido pelo criador do modelo e armazenado nos metadados do modelo, como NormalizationOptions Preenchido automaticamente pelo conversor TFLite e armazenado no arquivo de modelo tflite.
Como obter os parâmetros? Através da API MetadataExtractor [2] Através da API TFLite Tensor [1] ou através da API MetadataExtractor [2]
Os modelos float e quant compartilham o mesmo valor? Sim, os modelos float e quant têm os mesmos parâmetros de normalização Não, o modelo float não precisa de quantização.
O gerador de código TFLite ou a ligação do Android Studio ML o geram automaticamente no processamento de dados?
Sim

Sim

[1] API Java do TensorFlow Lite e API C++ do TensorFlow Lite .
[2] A biblioteca extratora de metadados

Ao processar dados de imagem para modelos uint8, a normalização e a quantização às vezes são ignoradas. Não há problema em fazer isso quando os valores dos pixels estão na faixa de [0, 255]. Mas, em geral, você deve sempre processar os dados de acordo com os parâmetros de normalização e quantização, quando aplicável.

A biblioteca de tarefas do TensorFlow Lite pode lidar com a normalização para você se você configurar NormalizationOptions em metadados. O processamento de quantização e desquantização é sempre encapsulado.

Exemplos

Você pode encontrar exemplos de como os metadados devem ser preenchidos para diferentes tipos de modelos aqui:

Classificação de imagens

Baixe o script aqui , que preenche os metadados para mobilenet_v1_0.75_160_quantized.tflite . Execute o script assim:

python ./metadata_writer_for_image_classifier.py \
    --model_file=./model_without_metadata/mobilenet_v1_0.75_160_quantized.tflite \
    --label_file=./model_without_metadata/labels.txt \
    --export_directory=model_with_metadata

Para preencher metadados para outros modelos de classificação de imagens, adicione as especificações do modelo como esta ao script. O restante deste guia destacará algumas das seções principais do exemplo de classificação de imagens para ilustrar os elementos principais.

Aprofunde-se no exemplo de classificação de imagens

Informações do modelo

Os metadados começam criando uma nova informação de modelo:

from tflite_support import flatbuffers
from tflite_support import metadata as _metadata
from tflite_support import metadata_schema_py_generated as _metadata_fb

""" ... """
"""Creates the metadata for an image classifier."""

# Creates model info.
model_meta = _metadata_fb.ModelMetadataT()
model_meta.name = "MobileNetV1 image classifier"
model_meta.description = ("Identify the most prominent object in the "
                          "image from a set of 1,001 categories such as "
                          "trees, animals, food, vehicles, person etc.")
model_meta.version = "v1"
model_meta.author = "TensorFlow"
model_meta.license = ("Apache License. Version 2.0 "
                      "http://www.apache.org/licenses/LICENSE-2.0.")

Informações de entrada/saída

Esta seção mostra como descrever a assinatura de entrada e saída do seu modelo. Esses metadados podem ser usados ​​por geradores automáticos de código para criar código de pré e pós-processamento. Para criar informações de entrada ou saída sobre um tensor:

# Creates input info.
input_meta = _metadata_fb.TensorMetadataT()

# Creates output info.
output_meta = _metadata_fb.TensorMetadataT()

Entrada de imagem

A imagem é um tipo de entrada comum para aprendizado de máquina. Os metadados do TensorFlow Lite oferecem suporte a informações como espaço de cores e informações de pré-processamento, como normalização. A dimensão da imagem não requer especificação manual, pois já é fornecida pelo formato do tensor de entrada e pode ser inferida automaticamente.

input_meta.name = "image"
input_meta.description = (
    "Input image to be classified. The expected image is {0} x {1}, with "
    "three channels (red, blue, and green) per pixel. Each value in the "
    "tensor is a single byte between 0 and 255.".format(160, 160))
input_meta.content = _metadata_fb.ContentT()
input_meta.content.contentProperties = _metadata_fb.ImagePropertiesT()
input_meta.content.contentProperties.colorSpace = (
    _metadata_fb.ColorSpaceType.RGB)
input_meta.content.contentPropertiesType = (
    _metadata_fb.ContentProperties.ImageProperties)
input_normalization = _metadata_fb.ProcessUnitT()
input_normalization.optionsType = (
    _metadata_fb.ProcessUnitOptions.NormalizationOptions)
input_normalization.options = _metadata_fb.NormalizationOptionsT()
input_normalization.options.mean = [127.5]
input_normalization.options.std = [127.5]
input_meta.processUnits = [input_normalization]
input_stats = _metadata_fb.StatsT()
input_stats.max = [255]
input_stats.min = [0]
input_meta.stats = input_stats

Saída de etiqueta

O rótulo pode ser mapeado para um tensor de saída por meio de um arquivo associado usando TENSOR_AXIS_LABELS .

# Creates output info.
output_meta = _metadata_fb.TensorMetadataT()
output_meta.name = "probability"
output_meta.description = "Probabilities of the 1001 labels respectively."
output_meta.content = _metadata_fb.ContentT()
output_meta.content.content_properties = _metadata_fb.FeaturePropertiesT()
output_meta.content.contentPropertiesType = (
    _metadata_fb.ContentProperties.FeatureProperties)
output_stats = _metadata_fb.StatsT()
output_stats.max = [1.0]
output_stats.min = [0.0]
output_meta.stats = output_stats
label_file = _metadata_fb.AssociatedFileT()
label_file.name = os.path.basename("your_path_to_label_file")
label_file.description = "Labels for objects that the model can recognize."
label_file.type = _metadata_fb.AssociatedFileType.TENSOR_AXIS_LABELS
output_meta.associatedFiles = [label_file]

Crie os metadados Flatbuffers

O código a seguir combina as informações do modelo com as informações de entrada e saída:

# Creates subgraph info.
subgraph = _metadata_fb.SubGraphMetadataT()
subgraph.inputTensorMetadata = [input_meta]
subgraph.outputTensorMetadata = [output_meta]
model_meta.subgraphMetadata = [subgraph]

b = flatbuffers.Builder(0)
b.Finish(
    model_meta.Pack(b),
    _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER)
metadata_buf = b.Output()

Empacotar metadados e arquivos associados no modelo

Depois que os metadados Flatbuffers são criados, os metadados e o arquivo de rótulo são gravados no arquivo TFLite por meio do método populate :

populator = _metadata.MetadataPopulator.with_model_file(model_file)
populator.load_metadata_buffer(metadata_buf)
populator.load_associated_files(["your_path_to_label_file"])
populator.populate()

Você pode compactar quantos arquivos associados desejar no modelo por meio de load_associated_files . No entanto, é necessário compactar pelo menos os arquivos documentados nos metadados. Neste exemplo, a embalagem do arquivo de etiqueta é obrigatória.

Visualize os metadados

Você pode usar o Netron para visualizar seus metadados ou pode ler os metadados de um modelo do TensorFlow Lite em um formato json usando o MetadataDisplayer :

displayer = _metadata.MetadataDisplayer.with_model_file(export_model_path)
export_json_file = os.path.join(FLAGS.export_directory,
                                os.path.splitext(model_basename)[0] + ".json")
json_file = displayer.get_metadata_json()
# Optional: write out the metadata as a json file
with open(export_json_file, "w") as f:
  f.write(json_file)

O Android Studio também oferece suporte à exibição de metadados por meio do recurso Android Studio ML Binding .

Controle de versão de metadados

O esquema de metadados é versionado tanto pelo número de versionamento semântico, que rastreia as alterações do arquivo de esquema, quanto pela identificação do arquivo Flatbuffers, que indica a verdadeira compatibilidade da versão.

O número de versão semântico

O esquema de metadados é versionado pelo número de versão semântico , como MAJOR.MINOR.PATCH. Ele rastreia alterações de esquema de acordo com as regras aqui . Veja o histórico de campos adicionados após a versão 1.0.0 .

A identificação do arquivo Flatbuffers

O versionamento semântico garante a compatibilidade se seguir as regras, mas não implica a verdadeira incompatibilidade. Aumentar o número MAJOR não significa necessariamente que a compatibilidade com versões anteriores esteja quebrada. Portanto, usamos a identificação de arquivo Flatbuffers , file_identifier , para denotar a verdadeira compatibilidade do esquema de metadados. O identificador do arquivo tem exatamente 4 caracteres. Ele é fixado em um determinado esquema de metadados e não está sujeito a alterações pelos usuários. Se a compatibilidade retroativa do esquema de metadados tiver que ser quebrada por algum motivo, o file_identifier aumentará, por exemplo, de “M001” para “M002”. Espera-se que o file_identifier seja alterado com muito menos frequência do que o metadata_version.

A versão mínima necessária do analisador de metadados

A versão mínima necessária do analisador de metadados é a versão mínima do analisador de metadados (o código gerado pelos Flatbuffers) que pode ler os metadados Flatbuffers por completo. A versão é efetivamente o maior número de versão entre as versões de todos os campos preenchidos e a menor versão compatível indicada pelo identificador do arquivo. A versão mínima necessária do analisador de metadados é preenchida automaticamente pelo MetadataPopulator quando os metadados são preenchidos em um modelo TFLite. Consulte o extrator de metadados para obter mais informações sobre como a versão mínima necessária do analisador de metadados é usada.

Leia os metadados dos modelos

A biblioteca Metadata Extractor é uma ferramenta conveniente para ler os metadados e arquivos associados de modelos em diferentes plataformas (consulte a versão Java e a versão C++ ). Você pode construir sua própria ferramenta de extração de metadados em outras linguagens usando a biblioteca Flatbuffers.

Leia os metadados em Java

Para usar a biblioteca Metadata Extractor em seu aplicativo Android, recomendamos usar o AAR de metadados do TensorFlow Lite hospedado no MavenCentral . Ele contém a classe MetadataExtractor , bem como as ligações FlatBuffers Java para o esquema de metadados e o esquema do modelo .

Você pode especificar isso em suas dependências build.gradle da seguinte maneira:

dependencies {
    implementation 'org.tensorflow:tensorflow-lite-metadata:0.1.0'
}

Para usar snapshots noturnos, certifique-se de ter adicionado o repositório de snapshots Sonatype .

Você pode inicializar um objeto MetadataExtractor com um ByteBuffer que aponta para o modelo:

public MetadataExtractor(ByteBuffer buffer);

O ByteBuffer deve permanecer inalterado durante todo o tempo de vida do objeto MetadataExtractor . A inicialização poderá falhar se o identificador do arquivo Flatbuffers dos metadados do modelo não corresponder ao do analisador de metadados. Consulte controle de versão de metadados para obter mais informações.

Com identificadores de arquivo correspondentes, o extrator de metadados lerá com sucesso os metadados gerados de todos os esquemas passados ​​e futuros devido ao mecanismo de compatibilidade direta e retroativa dos Flatbuffers. No entanto, os campos de esquemas futuros não podem ser extraídos por extratores de metadados mais antigos. A versão mínima necessária do analisador dos metadados indica a versão mínima do analisador de metadados que pode ler os metadados Flatbuffers por completo. Você pode usar o método a seguir para verificar se a condição mínima necessária da versão do analisador foi atendida:

public final boolean isMinimumParserVersionSatisfied();

É permitido passar um modelo sem metadados. No entanto, invocar métodos que leem os metadados causará erros de tempo de execução. Você pode verificar se um modelo possui metadados invocando o método hasMetadata :

public boolean hasMetadata();

MetadataExtractor fornece funções convenientes para você obter os metadados dos tensores de entrada/saída. Por exemplo,

public int getInputTensorCount();
public TensorMetadata getInputTensorMetadata(int inputIndex);
public QuantizationParams getInputTensorQuantizationParams(int inputIndex);
public int[] getInputTensorShape(int inputIndex);
public int getoutputTensorCount();
public TensorMetadata getoutputTensorMetadata(int inputIndex);
public QuantizationParams getoutputTensorQuantizationParams(int inputIndex);
public int[] getoutputTensorShape(int inputIndex);

Embora o esquema do modelo do TensorFlow Lite ofereça suporte a vários subgráficos, o interpretador TFLite atualmente oferece suporte apenas a um único subgráfico. Portanto, MetadataExtractor omite o índice do subgráfico como argumento de entrada em seus métodos.

Leia os arquivos associados dos modelos

O modelo TensorFlow Lite com metadados e arquivos associados é essencialmente um arquivo zip que pode ser descompactado com ferramentas zip comuns para obter os arquivos associados. Por exemplo, você pode descompactar mobilenet_v1_0.75_160_quantized e extrair o arquivo de rótulo no modelo da seguinte maneira:

$ unzip mobilenet_v1_0.75_160_quantized_1_metadata_1.tflite
Archive:  mobilenet_v1_0.75_160_quantized_1_metadata_1.tflite
 extracting: labels.txt

Você também pode ler arquivos associados por meio da biblioteca do Extrator de Metadados.

Em Java, passe o nome do arquivo para o método MetadataExtractor.getAssociatedFile :

public InputStream getAssociatedFile(String fileName);

Da mesma forma, em C++, isso pode ser feito com o método ModelMetadataExtractor::GetAssociatedFile :

tflite::support::StatusOr<absl::string_view> GetAssociatedFile(
      const std::string& filename) const;