¡Reserva! Google I / O regresa del 18 al 20 de mayo Regístrese ahora
Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Versiones del operador de TensorFlow Lite

Este documento describe el esquema de control de versiones de operaciones de TensorFlow Lite. El control de versiones de operaciones permite a los desarrolladores agregar nuevas funcionalidades y parámetros a las operaciones existentes. Además, garantiza lo siguiente:

  • Compatibilidad con versiones anteriores: la nueva implementación de TensorFlow Lite debería manejar un archivo de modelo antiguo.
  • Compatibilidad con versiones posteriores: la implementación anterior de TensorFlow Lite debe manejar un nuevo archivo de modelo producido por la nueva versión del convertidor, siempre que no se utilicen nuevas funciones.
  • Detección de compatibilidad hacia adelante: si una implementación antigua de TensorFlow Lite lee un nuevo modelo que contiene una nueva versión de una operación que no es compatible, debería informar el error.

Ejemplo: agregar dilatación en convolución en profundidad

El resto de este documento explica el versionado de operaciones en TFLite mostrando cómo agregar parámetros de dilatación a la operación de convolución en profundidad.

No se requieren conocimientos de dilatación para comprender este documento. Tenga en cuenta que:

  • Se agregarán 2 nuevos parámetros enteros: dilation_width_factor y dilation_height_factor .
  • Los viejos núcleos de convolución en profundidad que no admiten la dilatación equivalen a establecer los factores de dilatación en 1.

Cambiar el esquema FlatBuffer

Para agregar nuevos parámetros a una operación, cambie la tabla de opciones en lite/schema/schema.fbs .

Por ejemplo, la tabla de opciones de convolución en profundidad se ve así:

table DepthwiseConv2DOptions {
  padding:Padding;
  stride_w:int;
  stride_h:int;
  depth_multiplier:int;
  fused_activation_function:ActivationFunctionType;
}

Al agregar nuevos parámetros:

  • Agregue comentarios que indiquen qué parámetros son compatibles con qué versión.
  • Cuando la nueva implementación obtiene los valores predeterminados para los parámetros recién agregados, debería funcionar exactamente igual que la implementación anterior.

La tabla quedará así después de que se agreguen los nuevos parámetros:

table DepthwiseConv2DOptions {
  // Parameters for DepthwiseConv version 1 or above.
  padding:Padding;
  stride_w:int;
  stride_h:int;
  depth_multiplier:int;
  fused_activation_function:ActivationFunctionType;
  // Parameters for DepthwiseConv version 2 or above.
  dilation_w_factor:int = 1;
  dilation_h_factor:int = 1;
}

El archivo lite/schema/schema_generated.h debe volver a generarse para el nuevo esquema.

Cambiar las estructuras de C y la implementación del kernel

En TensorFlow Lite, la implementación del kernel está desacoplada de la definición de FlatBuffer. Los núcleos leen el parámetro de las estructuras C definidas en lite/c/builtin_op_data.h .

El parámetro de convolución en profundidad original es el siguiente:

typedef struct {
  TfLitePadding padding;
  int stride_width;
  int stride_height;
  int depth_multiplier;
  TfLiteFusedActivation activation;
} TfLiteDepthwiseConvParams;

Al igual que con el esquema FlatBuffer, agregue comentarios que indiquen qué parámetros son compatibles a partir de qué versión. El resultado se ve a continuación:

typedef struct {
  // Parameters for DepthwiseConv version 1 or above.
  TfLitePadding padding;
  int stride_width;
  int stride_height;
  int depth_multiplier;
  TfLiteFusedActivation activation;
  // Parameters for DepthwiseConv version 2 or above.
  int dilation_width_factor;
  int dilation_height_factor;
} TfLiteDepthwiseConvParams;

También cambie la implementación del kernel para leer los parámetros recién agregados de las estructuras de C. Los detalles son omitidos aquí.

Cambiar el código de lectura FlatBuffer

La lógica para leer FlatBuffer y producir la estructura C está en lite/core/api/flatbuffer_conversions.cc .

Actualice el archivo para manejar los nuevos parámetros, como se muestra a continuación:

TfLiteStatus ParseDepthwiseConv2D(const Operator* op,
                                  ErrorReporter* error_reporter,
                                  BuiltinDataAllocator* allocator,
                                  void** builtin_data) {
  CheckParsePointerParams(op, error_reporter, allocator, builtin_data);

  SafeBuiltinDataAllocator safe_allocator(allocator);

  std::unique_ptr<TfLiteDepthwiseConvParams,
                  SafeBuiltinDataAllocator::BuiltinDataDeleter>
      params = safe_allocator.Allocate<TfLiteDepthwiseConvParams>();
  TF_LITE_ENSURE(error_reporter, params != nullptr);

  const DepthwiseConv2DOptions* schema_params =
      op->builtin_options_as_DepthwiseConv2DOptions();

  if (schema_params != nullptr) {
    params->padding = ConvertPadding(schema_params->padding());
    params->stride_width = schema_params->stride_w();
    params->stride_height = schema_params->stride_h();
    params->depth_multiplier = schema_params->depth_multiplier();
    params->activation =
        ConvertActivation(schema_params->fused_activation_function());

    params->dilation_width_factor = schema_params->dilation_w_factor();
    params->dilation_height_factor = schema_params->dilation_h_factor();
  }

  *builtin_data = params.release();
  return kTfLiteOk;
}

No es necesario verificar la versión de operación aquí. Cuando la nueva implementación lee un archivo de modelo antiguo donde faltan factores de dilatación, usará 1 como valor predeterminado y el nuevo kernel funcionará de manera consistente con el kernel anterior.

Cambiar el registro del kernel

El MutableOpResolver (definido en lite/mutable_op_resolver.h ) proporciona algunas funciones para registrar núcleos operativos. La versión mínima y máxima son 1 por defecto:

void AddBuiltin(tflite::BuiltinOperator op, TfLiteRegistration* registration,
                int min_version = 1, int max_version = 1);
void AddCustom(const char* name, TfLiteRegistration* registration,
               int min_version = 1, int max_version = 1);

Las operaciones integradas están registradas en lite/kernels/register.cc . En este ejemplo, implementamos un nuevo kernel de operaciones que puede manejar DepthwiseConv2D versiones 1 y 2 de DepthwiseConv2D , por lo que necesitamos cambiar esta línea:

AddBuiltin(BuiltinOperator_DEPTHWISE_CONV_2D, Register_DEPTHWISE_CONV_2D());

a:

AddBuiltin(BuiltinOperator_DEPTHWISE_CONV_2D, Register_DEPTHWISE_CONV_2D(),
             /* min_version = */ 1,
             /* max_version = */ 2);

Cambiar la versión de operación de TFLite

El siguiente paso es hacer que TFLite llene la versión mínima necesaria para ejecutar la operación. En este ejemplo, significa:

  • Completar versión = 1 cuando los factores de dilatación son todos 1.
  • Completar versión = 2 de lo contrario.

Para hacer esto, primero debe agregar los parámetros correspondientes a depthwise_conv_2d dentro de la estructura OpSignature :

struct {
      int32_t dilation_w_factor;
      int32_t dilation_h_factor;
    } depthwise_conv_2d;

Luego, GetOpSignature estos nuevos parámetros en la función GetOpSignature en lite/tools/versioning/op_version.cc :

case BuiltinOperator_DEPTHWISE_CONV_2D: {
      auto conv_option = op->builtin_options_as_DepthwiseConv2DOptions();
      if (conv_option) {
        op_sig.options.depthwise_conv_2d.dilation_w_factor =
            conv_option->dilation_w_factor();
        op_sig.options.depthwise_conv_2d.dilation_h_factor =
            conv_option->dilation_h_factor();
      }
    } break;

Tenga en cuenta que si está agregando soporte para nuevos tipos, los pasos anteriores no son necesarios. Los tipos de entrada y salida se definen y completan para todas las operaciones en OpSignature .

Finalmente, modifique la función GetBuiltinOperatorVersion para el operador en lite/tools/versioning/op_version.cc agregando la nueva versión al caso de DepthwiseConv2D :

case BuiltinOperator_DEPTHWISE_CONV_2D:
  if (op_sig.options.depthwise_conv_2d.dilation_w_factor != 1 ||
      op_sig.options.depthwise_conv_2d.dilation_h_factor != 1) {
    return 2;
  }
  return 1;

Actualizar el mapa de versiones del operador

El último paso es agregar la información de la nueva versión en el mapa de versiones del operador. Este paso es necesario porque necesitamos generar la versión de tiempo de ejecución mínima requerida del modelo en función de este mapa de versiones.

Para hacer esto, necesita agregar una nueva entrada de mapa en lite/tools/versioning/runtime_version.cc .

En este ejemplo, debe agregar la siguiente entrada en op_version_map :

{ {BuiltinOperator_DEPTHWISE_CONV_2D, 2}, %CURRENT_RUNTIME_VERSION%}

donde %CURRENT_RUNTIME_VERSION% corresponde a la versión de tiempo de ejecución actual definida en tensorflow / core / public / version.h .

Implementación de delegación

TensorFlow Lite proporciona una API de delegación que permite delegar operaciones en backends de hardware. En la función Prepare del delegado, verifique si la versión es compatible con cada nodo en el código de delegación.

const int kMaxVersion = 1;
TfLiteNode* node;
TfLiteRegistration* registration = nullptr;
TF_LITE_ENSURE_STATUS(context->GetNodeAndRegistration(context, node_index, &node, &registration));

if (registration->version > kMaxVersion) {
  // Reject the node if the version isn't supported.
}

Esto es necesario incluso si la delegación solo admite operaciones de la versión 1, por lo que la delegación puede detectar incompatibilidades al obtener una operación de versión superior.