Agrega metadatos a los modelos de TensorFlow Lite

Los metadatos de TensorFlow Lite proporcionan un estándar para las descripciones de modelos. Los metadatos son una fuente importante de conocimiento sobre lo que hace el modelo y su información de entrada y salida. Los metadatos constan de

Todos los modelos de imágenes publicados en Modelos de Kaggle se propagaron con metadatos.

Modelo con formato de metadatos

model_with_metadata
Figura 1: Modelo de TFLite con metadatos y archivos asociados.

Los metadatos del modelo se definen en metadata_schema.fbs, un archivo FlatBuffer. Como se muestra en la Figura 1, se almacena en el campo metadata del esquema del modelo de TFLite, con el nombre "TFLITE_METADATA". Algunos modelos pueden incluir archivos asociados, como archivos de etiquetas de clasificación. Estos archivos se concatenan al final del archivo de modelo original como un ZIP mediante el modo"append" de ZipFile (modo 'a'). El intérprete de TFLite puede consumir el formato de archivo nuevo de la misma manera que antes. Consulta Empaqueta los archivos asociados para obtener más información.

Consulta las siguientes instrucciones para propagar, visualizar y leer metadatos.

Configura las herramientas de metadatos

Antes de agregar metadatos a tu modelo, deberás configurar un entorno de programación de Python para ejecutar TensorFlow. Aquí encontrarás una guía detallada para configurarlo.

Después de configurar el entorno de programación de Python, deberás instalar herramientas adicionales:

pip install tflite-support

Las herramientas de metadatos de TensorFlow Lite son compatibles con Python 3.

Agrega metadatos con la API de Flatbuffers para Python

Los metadatos del modelo tienen tres partes en el esquema:

  1. Información del modelo: Es la descripción general del modelo y los elementos, como los términos de la licencia. Consulta ModelMetadata.
    1. Información de entrada: Es la descripción de las entradas y el procesamiento previo necesarios, como la normalización. Consulta SubGraphMetadata.input_tensor_metadata.
      1. Información del resultado: descripción del resultado y el procesamiento posterior requeridos, como la asignación a etiquetas. Consulta SubGraphMetadata.output_tensor_metadata.

En este punto, TensorFlow Lite solo admite un subgrafo. Por lo tanto, el generador de código de TensorFlow Lite y la función de vinculación de AA de Android Studio usarán ModelMetadata.name y ModelMetadata.description, en lugar de SubGraphMetadata.name y SubGraphMetadata.description, cuando muestren metadatos y generen código.

Tipos de entrada y salida admitidos

Los metadatos de TensorFlow Lite para la entrada y salida no están diseñados para tipos de modelos específicos, sino para tipos de entrada y salida. El modelo es compatible con los metadatos de TensorFlow Lite, siempre y cuando los tipos de entrada y salida constan de lo siguiente o una combinación de lo siguiente, no importa lo que haga el modelo en términos funcionales:

  • Atributo: números que son números enteros sin firma o float32.
  • Imagen: Actualmente, los metadatos admiten imágenes RGB y en escala de grises.
  • Cuadro de límite: son cuadros de límite de forma rectangular. El esquema admite diversos esquemas de numeración.

Empaqueta los archivos asociados

Los modelos de TensorFlow Lite pueden incluir diferentes archivos asociados. Por ejemplo, los modelos de lenguaje natural suelen tener archivos de vocabulario que asignan fragmentos de palabras a los ID de palabras. Los modelos de clasificación pueden tener archivos de etiquetas que indican categorías de objetos. Sin los archivos asociados (si los hay), un modelo no funcionará bien.

Los archivos asociados ahora se pueden agrupar con el modelo a través de la biblioteca de metadatos de Python. El nuevo modelo de TensorFlow Lite se convierte en un archivo ZIP que contiene el modelo y los archivos asociados. Se puede desempaquetar con herramientas ZIP comunes. Este nuevo formato de modelo sigue usando la misma extensión de archivo: .tflite. Es compatible con el framework y el intérprete de TFLite existentes. Consulta Empaquetar metadatos y archivos asociados en el modelo para obtener más detalles.

La información del archivo asociado se puede registrar en los metadatos. Según el tipo de archivo y a dónde se adjunte (es decir, ModelMetadata, SubGraphMetadata y TensorMetadata), el generador de código de Android para TensorFlow Lite puede aplicar automáticamente el procesamiento previo y posterior correspondiente al objeto. Para obtener más detalles, consulta la sección <Codegen usage> de cada tipo de archivo asociado en el esquema.

Parámetros de normalización y cuantización

La normalización es una técnica común de procesamiento previo de datos en el aprendizaje automático. El objetivo de la normalización es cambiar los valores a una escala común, sin distorsionar las diferencias en los rangos de valores.

La cuantización del modelo es una técnica que permite reducir la precisión de las representaciones de los pesos y, de manera opcional, las activaciones para el almacenamiento y el procesamiento.

En términos de procesamiento previo y posterior, la normalización y la cuantización son dos pasos independientes. A continuación, le indicamos los detalles.

Normalización Cuantización

Ejemplo de los valores de los parámetros de la imagen de entrada en MobileNet para los modelos flotante y cuantitativo, respectivamente.
Modelo de número de punto flotante:
- media: 127.5
- std: 127.5
Modelo cuantitativo:
- media: 127.5
- std: 127.5
Modelo de número de punto flotante:
- zeroPoint: 0
- escala: 1.0
Modelo cuantitativo:
- zeroPoint: 128.0
- scale:0.0078125f




¿Cuándo realizar la invocación?


Entradas: Si los datos de entrada se normalizan durante el entrenamiento, los datos de entrada de inferencia deben normalizarse según corresponda.
Salidas: los datos de salida no se normalizarán en general.
Los modelos flotantes no necesitan cuantización.
Es posible que el modelo cuantizado necesite cuantización en el procesamiento previo y posterior. Depende del tipo de datos de los tensores de entrada y salida.
- Tensores de número de punto flotante: No se necesita cuantización en el procesamiento previo y posterior. Las operaciones cuantitativas y de descuantita se integran en el gráfico del modelo.
- Tensores int8/uint8: Necesitan cuantización en el procesamiento previo y posterior.


Fórmula


normalized_input = (input - media) / std
Cuantización de las entradas:
q = f / scale + zeroPoint
Descuantizar para resultados:
f = (q - zeroPoint) * scale

¿Dónde están los parámetros?
Rellenado por el creador del modelo y almacenado en metadatos del modelo, como NormalizationOptions El conversor de TFLite se completa automáticamente y se almacena en el archivo de modelo de tflite.
¿Cómo obtener los parámetros? A través de la API de MetadataExtractor[2] A través de la API Tensor de TFLite [1] o a través de la API de MetadataExtractor [2]
¿Los modelos de punto flotante y cuantitativos comparten el mismo valor? Sí, los modelos de número de punto flotante y cuantitativos tienen los mismos parámetros de normalización. No, el modelo de número de punto flotante no necesita cuantización.
¿El generador de código de TFLite o la vinculación de AA de Android Studio lo generan automáticamente en el procesamiento de datos?

[1] La API de TensorFlow Lite para Java y la API de C++ de TensorFlow Lite.
[2] La biblioteca de extractor de metadatos

Cuando se procesan datos de imágenes para modelos de uint8, a veces se omiten la normalización y la cuantización. Es correcto hacerlo cuando los valores de píxeles están en el rango de [0, 255]. Pero, en general, siempre debes procesar los datos de acuerdo con los parámetros de normalización y cuantización cuando corresponda.

Ejemplos

Puedes encontrar ejemplos sobre cómo se deben propagar los metadatos para diferentes tipos de modelos aquí:

Clasificación de imágenes

Descarga la secuencia de comandos aquí, que propaga los metadatos en mobilenet_v1_0.75_160_quantized.tflite. Ejecuta la secuencia de comandos de la siguiente manera:

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

Si quieres propagar metadatos para otros modelos de clasificación de imágenes, agrega las especificaciones del modelo, como esta, a la secuencia de comandos. En el resto de esta guía, se destacarán algunas de las secciones clave del ejemplo de clasificación de imágenes para ilustrar los elementos clave.

Análisis detallado del ejemplo de clasificación de imágenes

Información del modelo

Los metadatos comienzan con la creación de una nueva información del 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.")

Información de entrada y salida

En esta sección, se muestra cómo describir la firma de entrada y salida de tu modelo. Los generadores automáticos de código pueden usar estos metadatos para crear código de procesamiento previo y posterior. Para crear información de entrada o salida sobre un tensor, haz lo siguiente:

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

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

Entrada de imágenes

La imagen es un tipo de entrada común para el aprendizaje automático. Los metadatos de TensorFlow Lite admiten información como el espacio de color y la información de procesamiento previo, como la normalización. La dimensión de la imagen no requiere especificación manual, dado que ya está proporcionada por la forma del tensor de entrada y se puede inferir de forma automática.

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

Salida de la etiqueta

La etiqueta se puede asignar a un tensor de salida a través de un archivo asociado con 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]

Crea los búferes planos de metadatos

El siguiente código combina la información del modelo con la información de entrada y salida:

# 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()

Empaquetar metadatos y archivos asociados en el modelo

Una vez que se crean los FlatBuffers de metadatos, los metadatos y el archivo de etiquetas se escriben en el archivo de TFLite a través del 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()

Puedes empaquetar tantos archivos asociados como desees en el modelo a través de load_associated_files. Sin embargo, se deben empaquetar al menos esos archivos documentados en los metadatos. En este ejemplo, es obligatorio empaquetar el archivo de etiquetas.

Visualiza los metadatos

Puedes usar Netron para visualizar tus metadatos o leerlos de un modelo de TensorFlow Lite en un formato JSON con 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)

Android Studio también admite la visualización de metadatos a través de la función de vinculación de AA de Android Studio.

Control de versiones de metadatos

La versión del esquema de metadatos se basa en el número de control de versiones semántico, que realiza un seguimiento de los cambios del archivo de esquema, y por la identificación del archivo Flatbuffers, que indica la verdadera compatibilidad de la versión.

El número de control de versiones semántico

Las versiones del esquema de metadatos se basan en el número de control de versiones semántico, como MAJOR.MINOR.PATCH. Hace un seguimiento de los cambios de esquema según las reglas que se detallan aquí. Consulta el historial de campos agregados después de la versión 1.0.0.

La identificación del archivo FlatBuffers

El control de versiones semántico garantiza la compatibilidad si se siguen las reglas, pero no implica la verdadera incompatibilidad. Cuando se aumenta el número MAJOR, no necesariamente significa que se dañó la retrocompatibilidad. Por lo tanto, usamos la identificación del archivo de búferes planos, file_identifier, para denotar la verdadera compatibilidad del esquema de metadatos. El identificador de archivo tiene exactamente 4 caracteres. Se fija a un esquema de metadatos determinado y no está sujeta a cambios por parte de los usuarios. Si se debe romper la retrocompatibilidad del esquema de metadatos por algún motivo, file_identifier aumentará, por ejemplo, de “M001” a “M002”. Se espera que File_identifier cambie con mucha menos frecuencia que metadata_version.

La versión mínima necesaria del analizador de metadatos

La versión mínima necesaria del analizador de metadatos es la versión mínima del analizador de metadatos (el código generado por los búferes planos) que puede leer los búferes planos de metadatos por completo. La versión es, en efecto, el número de versión más grande entre las versiones de todos los campos completados y la versión compatible más pequeña que indica el identificador del archivo. MetadataPopulator propaga de forma automática la versión mínima necesaria del analizador de metadatos cuando los metadatos se propagan en un modelo de TFLite. Consulta el extractor de metadatos para obtener más información sobre cómo se usa la versión mínima necesaria del analizador de metadatos.

Lee los metadatos de los modelos

La biblioteca del extractor de metadatos es una herramienta conveniente para leer los metadatos y los archivos asociados de un modelo en diferentes plataformas (consulta la versión de Java y la versión de C++). Puedes compilar tu propia herramienta de extracción de metadatos en otros lenguajes mediante la biblioteca de Flatbuffers.

Lee los metadatos en Java

Si quieres usar la biblioteca del extractor de metadatos en tu app para Android, te recomendamos que utilices el AAR de metadatos de TensorFlow Lite alojado en MavenCentral. Contiene la clase MetadataExtractor, así como las vinculaciones de Java FlatBuffers para el esquema de metadatos y el esquema de modelo.

Puedes especificarlo en tus dependencias de build.gradle de la siguiente manera:

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

Para usar instantáneas nocturnas, asegúrate de haber agregado el repositorio de instantáneas de Sonatype.

Puedes inicializar un objeto MetadataExtractor con un ByteBuffer que apunte al modelo:

public MetadataExtractor(ByteBuffer buffer);

El ByteBuffer debe permanecer igual durante toda la vida útil del objeto MetadataExtractor. La inicialización puede fallar si el identificador del archivo Flatbuffers de los metadatos del modelo no coincide con el del analizador de metadatos. Consulta Control de versiones de metadatos para obtener más información.

Con identificadores de archivo coincidentes, el extractor de metadatos leerá de forma correcta los metadatos generados a partir de todos los esquemas pasados y futuros debido a los reenvíos y mecanismo de retrocompatibilidad de Flatbuffers. Sin embargo, los extractores de metadatos más antiguos no pueden extraer los campos de los esquemas futuros. La versión mínima necesaria del analizador de los metadatos indica la versión mínima del analizador de metadatos que puede leer los búferes planos de metadatos por completo. Puedes usar el siguiente método para verificar si se cumple la condición mínima necesaria de la versión del analizador:

public final boolean isMinimumParserVersionSatisfied();

Se permite pasar un modelo sin metadatos. Sin embargo, invocar métodos que leen de los metadatos provocarán errores de entorno de ejecución. Puedes invocar el método hasMetadata para verificar si un modelo tiene metadatos:

public boolean hasMetadata();

MetadataExtractor proporciona funciones convenientes para que obtengas los metadatos de los tensores de entrada y salida. Por ejemplo,

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);

Aunque el esquema del modelo de TensorFlow Lite admite varios subgrafos, actualmente el intérprete de TFLite solo admite un solo subgrafo. Por lo tanto, MetadataExtractor omite el índice de subgrafo como argumento de entrada en sus métodos.

Leer los archivos asociados de modelos

El modelo de TensorFlow Lite con metadatos y archivos asociados es, en esencia, un archivo ZIP que se puede descomprimir con herramientas ZIP comunes para obtener los archivos asociados. Por ejemplo, puedes descomprimir mobilenet_v1_0.75_160_quantized y extraer el archivo de etiquetas en el modelo de la siguiente manera:

$ 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

También puedes leer los archivos asociados a través de la biblioteca del extractor de metadatos.

En Java, pasa el nombre del archivo al método MetadataExtractor.getAssociatedFile:

public InputStream getAssociatedFile(String fileName);

De manera similar, en C++, puedes hacer esto con el método ModelMetadataExtractor::GetAssociatedFile:

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