Agrega metadatos a los modelos de TensorFlow Lite

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

Todos los modelos de imágenes publicados en TensorFlow Hub 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 de metadatos del esquema del modelo de TFLite, debajo del nombre "TFLITE_METADATA". Algunos modelos pueden incluir archivos asociados, como los archivos de etiquetas de clasificación. Estos archivos se concatenan al final del archivo del modelo original como un ZIP con el modo “append” de ZipFile (modo 'a'). El intérprete de TFLite puede consumir el nuevo formato de archivo de la misma manera que antes. Consulta Cómo empaquetar 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 Python de FlatBuffers

Hay tres partes de los metadatos del modelo 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.
  2. Información de entrada: Es una descripción de las entradas y el procesamiento previo necesarios, como la normalización. Consulta SubGraphMetadata.input_tensor_metadata.
  3. Información de salida: es una descripción del resultado y el procesamiento posterior necesarios, como la asignación a etiquetas. Consulta SubGraphMetadata.output_tensor_metadata.

Dado que TensorFlow Lite en este punto solo admite un subgrafo, 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, para mostrar metadatos y generar código.

Tipos de entrada y salida compatibles

Los metadatos de TensorFlow Lite para entrada y salida no están diseñados con tipos de modelos específicos en mente, sino tipos de entrada y salida. No importa lo que haga funcionalmente el modelo. Siempre que los tipos de entrada y salida consten de lo siguiente o una combinación de ellos, sea compatible con los metadatos de TensorFlow Lite:

  • 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: Cuadros de límite con forma rectangular. El esquema admite una variedad de 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 ID de palabra; 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.

Ahora los archivos asociados 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 descomprimir con herramientas comunes de ZIP. 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 Empaqueta los metadatos y los 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 el lugar al que se adjunte (es decir, ModelMetadata, SubGraphMetadata y TensorMetadata), el generador de código para Android de TensorFlow Lite puede aplicar automáticamente el procesamiento previo y posterior correspondiente al objeto. Consulta la sección <Codegen usage> de cada tipo de archivo asociado para obtener más información en el esquema.

Parámetros de normalización y cuantización

La normalización es una técnica de procesamiento previo de datos común 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 de modelos es una técnica que permite reducir representaciones de precisión de los pesos y, opcionalmente, 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 cuantitativos y flotantes, 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 Cuant:
- zeroPoint: 128.0
- scale:0.0078125f




¿Cuándo invocar?


Entradas: Si los datos de entrada se normalizan en el entrenamiento, los datos de entrada de inferencia deben normalizarse en consecuencia.
Salidas: Los datos de salida no se normalizarán en general.
Los modelos flotantes no necesitan cuantización.
El modelo cuantizado puede o no necesitar la 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 op cuantitativas y decuantitativas se integran en el gráfico del modelo.
: tensores int8/uint8: necesitan cuantización en el procesamiento previo y posterior.


Fórmula


normalized_input = (entrada - media) / std
Cuantizar entradas:
q = f / scale + zeroPoint
Descuantizar para resultados:
f = (q - zeroPoint) * scale

¿Dónde están los parámetros?
Se completa por el creador del modelo y se almacena en los metadatos del modelo, como NormalizationOptions Se completa automáticamente con el convertidor de TFLite y se almacena en un 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 de Tensor de TFLite [1] o de la API de MetadataExtractor[2]
¿Los modelos flotantes y cuánticos comparten el mismo valor? Sí, los modelos cuantitativos y los números de punto flotante 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 TFLite o la vinculación de AA de Android Studio los generan automáticamente en el procesamiento de datos?

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

Cuando se procesan datos de imagen para modelos uint8, a veces se omiten la normalización y la cuantización. Puedes hacerlo cuando los valores de píxeles se encuentren en el rango de [0, 255]. Sin embargo, 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 los diferentes tipos de modelos aquí:

Clasificación de imágenes

Descarga la secuencia de comandos aquí, que propaga los metadatos a 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

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

Analiza en detalle el ejemplo de clasificación de imágenes

Información del modelo

Los metadatos comienzan con la creación de la información de un modelo nuevo:

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 de código automáticos pueden usar estos metadatos para crear código de procesamiento previo y posterior. Sigue estos pasos para crear información de entrada o salida sobre un tensor:

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

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

Entrada de imagen

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, ya que la forma del tensor de entrada ya la proporciona y se puede inferir automáticamente.

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

Resultado de la etiqueta

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

En el siguiente código, se 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()

Empaqueta los metadatos y los archivos asociados en el modelo

Una vez que se crean los FlatBuffers de los metadatos, los metadatos y el archivo de etiquetas se escriben en el archivo 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, es necesario empaquetar al menos los 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 puedes 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 del AA de Android Studio.

Control de versiones de los 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 en la identificación del archivo de búferes planos, que indica la verdadera compatibilidad de la versión.

El número del control de versiones semántico

El esquema de metadatos está controlado por 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 indican aquí. Consulta el historial de campos agregados después de la versión 1.0.0.

La identificación del archivo de 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 la retrocompatibilidad no funciona. Por lo tanto, usamos la identificación de archivos de búferes planos, file_identifier, para indicar 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á sujeto a cambios por parte de los usuarios. Si la retrocompatibilidad del esquema de metadatos tiene que dañarse por algún motivo, file_identifier subirá, por ejemplo, de "M001" a "M002". Se espera que File_identifier cambie con mucha menos frecuencia que el metadata_version.

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

La versión mínima del analizador de metadatos necesaria es la versión mínima del analizador de metadatos (el código generado por los FlatBuffers) que puede leer los metadatos FlatBuffers en su totalidad. De hecho, la versión es 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 de archivo. MetadataPopulator propaga automáticamente 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 modelos 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 con la biblioteca FlatBuffers.

Lee los metadatos en Java

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

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

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

Para usar instantáneas por la noche, 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 sin cambios durante toda la vida útil del objeto MetadataExtractor. La inicialización puede fallar si el identificador de archivo de los metadatos del modelo no coincide con el del analizador de metadatos. Consulta control de versiones de los metadatos para obtener más información.

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

public final boolean isMinimumParserVersionSatisfied();

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

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, por el momento, el intérprete de TFLite solo admite un subgrafo. Por lo tanto, MetadataExtractor omite el índice del subgrafo como un argumento de entrada en sus métodos.

Lee los archivos asociados desde los 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++, esto se puede hacer con el método ModelMetadataExtractor::GetAssociatedFile:

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