Path: blob/master/site/es-419/guide/migrate/tf1_vs_tf2.ipynb
25118 views
Copyright 2021 The TensorFlow Authors.
TensorFlow 1.x vs TensorFlow 2: Comportamientos y APIs
En el fondo, TensorFlow 2 sigue un paradigma de programación fundamentalmente diferente de TF1.x.
Esta guía describe las diferencias fundamentales entre TF1.x y TF2 en cuanto a los comportamientos y las API, y cómo todo ello se relaciona con su proceso de migración.
Resumen de alto nivel de los principales cambios
En esencia, TF1.x y TF2 usan un conjunto diferente de comportamientos runtime sobre la ejecución (eager en TF2), las variables, el flujo de control, las formas del tensor y las comparaciones de igualdad del tensor. Para ser compatible con TF2, su código debe ser compatible con el conjunto completo de comportamientos de TF2. Durante la migración, puede activar o desactivar individualmente la mayoría de estos comportamientos a través de las APIs tf.compat.v1.enable_*
o tf.compat.v1.disable_*
. La única excepción es la retirada de colecciones, que es un efecto secundario de habilitar/deshabilitar ejecución eager.
Dicho a un alto nivel, TensorFlow 2:
Elimina APIs redundantes.
Hace que las API sean más consistentes: por ejemplo, RNNs unificadas y Optimizadores unificados.
Prefiere funciones en lugar de sesiones y se integra mejor con el runtime de Python con ejecución eager activada por default junto con
tf.function
que provee dependencias automáticas de control para grafos y compilación.Deja obsoleto las collections del grafo global.
Altera la semántica de concurrencia de variables al usar
ResourceVariables
en lugar deReferenceVariables
.Admite flujo de control basado en funciones y flujo de control diferenciable (Flujo de control v2).q
Simplifica la API TensorShape para que contenga objetos
int
en lugar detf.compat.v1.Dimension
.Actualiza la mecánica de igualdad de tensores. En TF1.x el operador
==
en tensores y variables verifica la igualdad de referencia de objetos. En TF2 verifica la igualdad de valores. Además, los tensores/variables ya no son hasheables, pero puede obtener referencias de objetos hasheables a ellos mediantevar.ref()
si necesita usarlos en conjuntos o como clavesdict
.
Las secciones siguientes explican con más detalle las diferencias entre TF1.x y TF2. Para saber más sobre el proceso de diseño que hay detrás de TF2, lea las RFCs y los documentos de diseño.
Limpieza de API
Muchas API han desaparecido o cambiado en TF2. Algunos de los principales cambios incluyen la eliminación de tf.app
, tf.flags
, y tf.logging
en favor de absl-py, que ahora es de código abierto, la reubicación de proyectos que residían en tf. contrib
, y la limpieza del namespace principal tf.*
moviendo las funciones menos usadas a subpaquetes como tf.math
. Algunas API han sido reemplazadas por sus equivalentes en TF2: tf.summary
, tf.keras.metrics
y tf.keras.optimizers
.
tf.compat.v1
: Puntos finales de las API heredadas y de compatibilidad
Los símbolos de los namespaces tf.compat
y tf.compat.v1
no se consideran API de TF2. Estos namespaces exponen una mezcla de símbolos de compatibilidad, así como puntos finales de API heredados de TF 1.x. Están pensados para ayudar a la migración de TF1.x a TF2. Sin embargo, como ninguna de estas compat.v1
son API idiomáticas de TF2, no las use para escribir código TF2 totalmente nuevo.
Algunos símbolos tf.compat.v1
pueden ser compatibles con TF2 porque siguen funcionando incluso con los comportamientos TF2 activados (como tf.compat.v1.losses.mean_squared_error
), mientras que otros son incompatibles con TF2 (como tf.compat.v1.metrics.accuracy
). Muchos símbolos compat.v1
(aunque no todos) contienen información en su documentación dedicada a la migración que explica su grado de compatibilidad con los comportamientos de TF2, así como la forma de migrarlos a las API de TF2.
El script de actualización de TF2 puede mapear muchos símbolos de API compat.v1
a APIs TF2 equivalentes en el caso de que sean alias o tengan los mismos argumentos pero con un orden diferente. También puede usar el script de actualización para renombrar automáticamente las API TF1.x.
APIs falsas amigas
Hay un conjunto de símbolos "falsos amigos" que se encuentran en el namespace de TF2 tf
(no bajo compat.v1
) que en realidad ignoran los comportamientos de TF2 a nivel interno, y/o no son totalmente compatibles con el conjunto completo de comportamientos de TF2. Como tales, es probable que estas API se comporten mal con el código TF2, potencialmente de forma silenciosa.
tf.estimator.*
: Los Estimator crean y usan grafos y sesiones En el fondo. Como tales, no deben considerarse compatibles con TF2. Si su código ejecuta estimators, no está usando los comportamientos de TF2.keras.Model.model_to_estimator(...)
: En el fondo esto crea un Estimator, que como ya se ha mencionado no es compatible con TF2.tf.Graph().as_default()
: Esto introduce comportamientos de grafo TF1.x y no sigue los comportamientostf.function
compatibles con TF2. El código que introduce grafos como éste generalmente los ejecutará a través de Sessions, y no debe considerarse compatible con TF2.tf.feature_column.*
Las API de columnas de características se basan generalmente en la creación de variables al estilo TF1tf.compat.v1.get_variable
y asumen que se accederá a las variables creadas a través de colecciones globales. Como TF2 no admite colecciones, es posible que las API no funcionen correctamente si se ejecutan con los comportamientos de TF2 activados.
Otros cambios en la API
TF2 presenta mejoras significativas en los algoritmos de colocación de dispositivos que hacen innecesario el uso de
tf.colocate_with
. Si su eliminación provoca una degradación del rendimiento, por favor, envíe un reporte de error.Reemplace todos los usos de
tf.v1.ConfigProto
por funciones equivalentes detf.config
.
Ejecución eager
TF1.x requería que usted ensamblara manualmente un árbol de sintaxis abstracta (el grafo) haciendo llamadas de API tf.*
y luego compilara manualmente el árbol de sintaxis abstracta pasando un conjunto de tensores de salida y tensores de entrada a una llamada session.run
. TF2 ejecuta de forma eager (como hace normalmente Python) y hace que los grafos y las sesiones parezcan detalles de implementación.
Un subproducto notable de la ejecución eager es que tf.control_dependencies
ya no es necesario, ya que todas las líneas de código se ejecutan en orden (dentro de una tf.function
, el código con efectos secundarios se ejecuta en el orden escrito).
No más globales
TF1.x dependía en gran medida de namespaces y colecciones globales implícitas. Cuando llamaba a tf.Variable
, se ponía en una recolección en el grafo por default, y permanecía allí, incluso si perdía la pista de la variable Python que apuntaba a ella. Entonces se podría recuperar esa tf.Variable
, pero sólo si conocía el nombre con el que se había creado. Esto era difícil si no controlaba la creación de la variable. Resultó que proliferaron todo tipo de mecanismos para intentar ayudarle a encontrar de nuevo sus variables, y para que los frameworks encontraran las variables creadas por el usuario. Algunos de ellos son: ámbitos de variables, colecciones globales, métodos de ayuda como tf.get_global_step
y tf.global_variables_initializer
, optimizadores que calculan implícitamente gradientes sobre todas las variables entrenables, etc. TF2 elimina todos estos mecanismos (Variables 2.0 RFC) en favor del mecanismo por default: usted da su propio seguimiento de sus variables. Si pierde el rastro de una tf.Variable
, el garbage collector lo recolecta.
El requisito de realizar un seguimiento de las variables crea algo de trabajo extra, pero con herramientas como los shim de modelado y comportamientos como las recolecciones implícitas de variables orientadas a objetos en tf.Module
s y tf.keras.layers.Layer
s, la carga se minimiza.
Functions, no sessions
Una llamada session.run
es casi como una llamada a una función: se especifican las entradas y la función a llamar, y se obtiene de vuelta un conjunto de salidas. En TF2, puede decorar una función Python usando tf.function
para marcarla para la compilación JIT de modo que TensorFlow la ejecute como un único grafo (Functions 2.0 RFC). Este mecanismo permite a TF2 obtener todos los beneficios del modo grafo:
Rendimiento: La función puede optimizarse (poda de nodos, fusión de kernel, etc.)
Portabilidad: La función puede exportarse/reimportarse (SavedModel 2.0 RFC), lo que permite reutilizar y compartir funciones modulares TensorFlow.
Con la posibilidad de intercalar libremente código Python y TensorFlow, puede aprovechar la expresividad de Python. Sin embargo, TensorFlow portátil se ejecuta en contextos sin intérprete de Python, como mobile, C++ y JavaScript. Para evitar reescribir su código al añadir tf.function
, use AutoGraph para convertir un subgrupo de constructos de Python en sus equivalentes de TensorFlow:
for
/while
->tf.while_loop
(se aceptanbreak
ycontinue
)if
->tf.cond
for _ in dataset
->dataset.reduce
AutoGraph admite anidaciones arbitrarias del flujo de control, lo que permite implementar de forma eficaz y concisa muchos programas de ML complejos, como modelos de secuencias, aprendizaje por refuerzo, bucles de entrenamiento personalizados, etc.
Adaptación a los cambios de comportamiento de TF 2.x
Su migración a TF2 sólo estará completa cuando haya migrado al conjunto completo de comportamientos de TF2. El conjunto completo de comportamientos puede activarse o desactivarse mediante tf.compat.v1.enable_v2_behaviors
y tf.compat.v1.disable_v2_behaviors
. Las secciones siguientes analizan en detalle cada uno de los principales cambios de comportamiento.
Usar tf.function
s
Es probable que los mayores cambios en sus programas durante la migración procedan del cambio de paradigma fundamental del modelo de programación, de grafos y sesiones a ejecución eager y tf.function
. Consulte las guías de migración a TF2 para saber más sobre cómo pasar de las API incompatibles con ejecución eager y tf.function
a las API compatibles con ellas.
Nota: Durante la migración puede seleccionar activar y desactivar directamente la ejecución eager con tf.compat.v1.enable_eager_execution
y tf.compat.v1.disable_eager_execution
, pero sólo podrá hacerlo una vez durante la vida de su programa.
A continuación se muestran algunos patrones de programa comunes no ligados a ninguna API que pueden causar problemas al cambiar de tf.Graph
s y tf.compat.v1.Session
s a ejecución eager con tf.function
s.
Patrón 1: La manipulación de objetos y la creación de variables en Python pensadas para hacerse una sola vez se ejecutan varias veces
En los programas TF1.x que se basan en grafos y sesiones, la expectativa suele ser que toda la lógica Python de su programa se ejecute una sola vez. Sin embargo, con ejecución eager y tf.function
es razonable esperar que su lógica Python se ejecute al menos una vez, pero posiblemente más veces (ya sea varias veces en eager, o varias veces a través de diferentes trazados de tf.function
). A veces, tf.function
incluso trazará dos veces sobre la misma entrada, provocando comportamientos inesperados (véanse los ejemplos 1 y 2). Consulte la guía tf.function
para más detalles.
Nota: Este patrón suele provocar que su código no funcione correctamente cuando se corre la ejecución eager sin tf.function
s, pero generalmente lanza un InaccessibleTensorError
o un ValueError
cuando se intenta encapsular el código problemático dentro de un tf.function
. Para descubrir y depurar este problema, se recomienda encapsular el código con tf.function
desde el principio, y usar pdb o depuración interactiva para identificar el origen del InaccessibleTensorError
.
Ejemplo 1: Creación de variables
Considere el siguiente ejemplo, en el que la función crea una variable al ser llamada:
Sin embargo, encapsular ingenuamente la función anterior que contiene la creación de variables con tf.function
no está permitido. tf.function
sólo admite creaciones de variables aisladas en la primera llamada. Para garantizarlo, cuando tf.function detecte la creación de variables en la primera llamada, intentará realizar el trazado de nuevo y emitirá un error si se produce la creación de variables en el segundo trazado.
Una solución es almacenar en caché y reutilizar la variable después de crearla en la primera llamada.
Ejemplo 2: Tensores fuera de ámbito debido a un retrazado de tf.function
Como se demuestra en el ejemplo 1, tf.function
volverá a trazar cuando detecte la creación de variables en la primera llamada. Esto puede causar confusión adicional, porque los dos trazados crearán dos grafos. Cuando el segundo grafo del retrazado intente acceder a un Tensor del grafo generado durante el primer trazado, Tensorflow lanzará un error quejándose de que el Tensor está fuera de ámbito. Para demostrar el escenario, el código siguiente crea un conjunto de datos en la primera llamada tf.function
. Esto se ejecutará como se esperaba.
Sin embargo, si también intentamos crear una variable en la primera llamada tf.function
, el código emitirá un error quejándose de que el conjunto de datos está fuera de su ámbito. Esto se debe a que el conjunto de datos se encuentra en el primer grafo, mientras que el segundo grafo también está intentando acceder a él.
La solución más sencilla es asegurarse de que tanto la creación de la variable como la creación del conjunto de datos están fuera de la llamada a tf.function
. Por ejemplo:
Sin embargo, a veces no se puede evitar crear variables en tf.function
(como las variables de ranura en algunos optimizadores keras TF). Aún así, podemos simplemente mover la creación del conjunto de datos fuera de la llamada tf.function
. La razón por la que podemos confiar en esto es porque tf.function
recibirá el conjunto de datos como una entrada implícita y ambos grafos podrán acceder a él correctamente.
Ejemplo 3: Recreaciones inesperadas de objetos Tensorflow debido al uso de dict
tf.function
tiene un soporte muy pobre para efectos secundarios python como añadir a una lista, o comprobar/añadir a un diccionario. Si desea más detalles, consulte "Mejor rendimiento con tf.function". En el ejemplo siguiente, el código usa diccionarios para almacenar en caché conjuntos de datos e iteradores. Para una misma clave, cada llamada al modelo devolverá el mismo iterador del conjunto de datos.
Sin embargo, el patrón anterior no funcionará como se espera en tf.function
. Durante el Seguimiento, tf.function
ignorará el efecto secundario python de la adición a los diccionarios. En su lugar, sólo recordará la creación de un nuevo conjunto de datos e iterador. Como resultado, cada llamada al modelo devolverá siempre un nuevo iterador. Este problema es difícil de notar a menos que los resultados numéricos o el rendimiento sean lo suficientemente significativos. Por lo tanto, recomendamos a los usuarios que piensen detenidamente en el código antes de encapsular tf.function
ingenuamente en el código python.
Podemos usar tf.init_scope
para elevar el conjunto de datos y la creación de iteradores fuera del grafo, para conseguir el comportamiento esperado:
Podemos usar tf.init_scope
para elevar el conjunto de datos y la creación de iteradores fuera del grafo, para conseguir el comportamiento esperado:
Ejemplo 4: Manipulación de una lista global de Python
El siguiente código TF1.x usa una lista global de pérdidas para mantener únicamente la lista de pérdidas generadas por el paso de entrenamiento actual. Tenga en cuenta que la lógica de Python que añade las pérdidas a la lista sólo se llamará una vez, independientemente del número de pasos de entrenamiento en los que se ejecute la sesión.
Sin embargo, si esta lógica de Python se mapea ingenuamente a TF2 con ejecución eager, a la lista global de pérdidas se le añadirán nuevos valores en cada paso de entrenamiento. Esto significa que el código del paso de entrenamiento que antes esperaba que la lista sólo contuviera las pérdidas del paso de entrenamiento actual, ahora ve en realidad la lista de pérdidas de todos los pasos de entrenamiento ejecutados hasta el momento. Se trata de un cambio de comportamiento no intencionado, y la lista deberá borrarse al inicio de cada paso o hacerse local al paso de entrenamiento.
Patrón 2: Un tensor simbólico destinado a ser recalculado a cada paso en TF1.x se almacena accidentalmente en caché con el valor inicial al pasar a eager.
Este patrón normalmente hace que su código no funcione correctamente cuando se ejecuta en forma eager fuera de tf.functions, pero lanza un InaccessibleTensorError
si el almacenamiento en caché del valor inicial ocurre dentro de una tf.function
. Sin embargo, tenga en cuenta que para evitar el Patrón 1 anterior, a menudo estructurará inadvertidamente su código de tal forma que este almacenamiento en caché del valor inicial se produzca fuera de cualquier tf.function
que pudiera provocar un error. Por lo tanto, tenga especial cuidado si sabe que su programa puede ser susceptible a este patrón.
La solución general a este patrón es reestructurar el código o usar callables de Python si es necesario para asegurarse de que el valor se vuelve a calcular cada vez en lugar de almacenarse accidentalmente en la memoria caché.
Ejemplo 1: Programas de tasa de aprendizaje/hiperparámetros/etc. que dependen del paso global
En el siguiente recorte de código, se espera que cada vez que se ejecute la sesión se lea el valor global_step
más reciente y se calcule una nueva tasa de aprendizaje.
Sin embargo, cuando intente cambiar a eager, cuídese de no acabar calculando la tasa de aprendizaje una sola vez y luego reutilizándola, en lugar de seguir el programa previsto:
Dado que este ejemplo concreto es un patrón común y que los optimizadores sólo deben inicializarse solo una vez en lugar de en cada paso del entrenamiento, los optimizadores TF2 admiten programaciones tf.keras.optimizers.schedules.LearningRateSchedule
o callables de Python como argumentos para la tasa de aprendizaje y otros hiperparámetros.
Ejemplo 2: Las inicializaciones simbólicas de números aleatorios asignadas como atributos de objetos y luego reutilizadas mediante punteros se almacenan accidentalmente en caché al cambiar a eager
Veamos el siguiente módulo NoiseAdder
:
Usándolo de la siguiente manera en TF1.x calculará un nuevo tensor de ruido aleatorio cada vez que se ejecute la sesión:
Sin embargo, en TF2, la inicialización de noise_adder{/código0} al principio hará que
noise_distribution{/código1} sólo se calcule una vez y se congele para todos los pasos del entrenamiento:``
Para solucionarlo, modifique NoiseAdder
para que llame a tf.random.normal
cada vez que se necesite un nuevo tensor aleatorio, en lugar de referirse al mismo objeto tensor cada vez.
Patrón 3: El código TF1.x depende directamente de los tensores y los busca por su nombre
Es habitual que las pruebas de código del TF1.x se basen en comprobar qué tensores u operaciones están presentes en un grafo. En algunos casos poco frecuentes, el código de modelado también se basará en estas búsquedas por nombre.
Los nombres de los tensores no se generan cuando se ejecutan en forma eager fuera de tf.function
en absoluto, por lo que todos los usos de tf.Tensor.name
deben producirse dentro de una tf.function
. Tenga en cuenta que es muy probable que los nombres generados reales difieran entre TF1.x y TF2 incluso dentro del mismo tf.function
, y las garantías de la API no aseguran la estabilidad de los nombres generados entre versiones de TF.
Nota: Los nombres de las variables se siguen generando incluso fuera de tf.function
s, pero tampoco se garantiza que sus nombres coincidan entre TF1.x y TF2 excepto si se siguen las secciones pertinentes de la guía de mapeo de modelos.
Patrón 4: La sesión TF1.x ejecuta selectivamente sólo una parte del grafo generado
En TF1.x, puede construir un grafo y luego seleccionar que sólo se ejecute selectivamente un subgrupo del mismo con una sesión, configurando un conjunto de entradas y salidas que no requieran la ejecución de todas las op del grafo.
Por ejemplo, puede tener un generador y un discriminador dentro de un mismo grafo, y usar llamadas tf.compat.v1.Session.run
separadas para alternar entre sólo el entrenamiento del discriminador o sólo el entrenamiento del generador.
En TF2, debido a las dependencias automáticas de control en tf.function
y a la ejecución eager, no hay poda selectiva de los trazados de tf.function
. Un grafo completo que contenga todas las actualizaciones de variables se ejecutaría incluso si, por ejemplo, sólo la salida del discriminador o del generador es salida de tf.function
.
Por lo tanto, necesitaría o bien usar múltiples tf.function
que contengan diferentes partes del programa, o bien un argumento condicional a la tf.function
que ramifique para ejecutar sólo las cosas que realmente desea que se ejecuten.
Eliminación de colecciones
Cuando la ejecución eager está activada, las compat.v1
relacionadas con la colección de grafos (incluidas las que leen o escriben en colecciones en el fondo, como tf.compat.v1.trainable_variables
) ya no están disponibles. Algunas pueden lanzar ValueError
s, mientras que otras pueden devolver silenciosamente listas vacías.
El uso estándar de las colecciones en TF1.x es mantener los inicializadores, el paso global, las ponderaciones, las pérdidas de regularización, las pérdidas de salida del modelo y las actualizaciones de las variables que deben ejecutarse, como las de las capas BatchNormalization
.
Para manejar cada uno de estos usos estándares:
Inicializadores: Ignorar. La inicialización manual de variables no es necesaria con la ejecución eager activada.
Paso global: Consulte la documentación de
tf.compat.v1.train.get_or_create_global_step
para ver las instrucciones de migración.Ponderaciones: Mapee sus modelos a
tf.Module
s/tf.keras.layers.Layer
s/tf.keras.Model
s siguiendo las indicaciones de la guía de mapeo de modelos y luego use sus respectivos mecanismos de seguimiento de ponderaciones comotf.module.trainable_variables
.Pérdidas de regularización: Mapee sus modelos a
tf.Module
s/tf.keras.layers.Layer
s/tf.keras.Model
s siguiendo las indicaciones de la guía de mapeo de modelos y luego usetf.keras.losses
. Alternativamente, también puede realizar un seguimiento manual de sus pérdidas de regularización.Pérdidas de salida del modelo: Use los mecanismos de gestión de pérdidas de
tf.keras.Model
o haga un seguimiento por separado de sus pérdidas sin usar colecciones.Actualizaciones de ponderación: Ignore esta colección. La ejecución eager y
tf.function
(con autograph y auto-control-dependencias) significa que todas las actualizaciones de variables se ejecutarán automáticamente. Por lo tanto, no tendrá que ejecutar explícitamente todas las actualizaciones de ponderación al final, pero tenga en cuenta que esto significa que las actualizaciones de ponderación pueden producirse en un momento diferente al que lo hacían en su código TF1.x, dependiendo de cómo estuviera usando las dependencias de control.Resúmenes: Consulte la guía de migración de la API de resúmenes.
Un uso más complejo de las colecciones (como el uso de colecciones personalizadas) puede requerir que modifique su código para mantener sus propios almacenes globales o para hacer que no dependa en absoluto de los almacenes globales.
ResourceVariables
en vez de ReferenceVariables
ResourceVariables
tiene garantías de consistencia de lectura-escritura más sólidas que ReferenceVariables
. Esto conduce a una semántica más predecible y fácil de razonar sobre si se observará o no el resultado de una escritura anterior al usar sus variables. Es muy poco probable que este cambio provoque errores en el código existente o que se desestabilice silenciosamente.
Sin embargo, es posible aunque improbable que estas garantías de consistencia más fuertes puedan aumentar el uso de memoria de su programa específico. Levante u reporte si se da el caso. Además, si tiene pruebas de unidad que dependen de comparaciones exactas de cadenas contra los nombres de operadores en un grafo correspondiente a lecturas de variables, tenga en cuenta que habilitar variables de recursos puede cambiar ligeramente el nombre de estos operadores.
Para aislar el impacto de este cambio de comportamiento en su código, si ejecución eager está habilitada puede usar tf.compat.v1.disable_resource_variables()
y tf.compat.v1.enable_resource_variables()
para deshabilitar o habilitar globalmente este cambio de comportamiento. ResourceVariables
se usará siempre si ejecución eager está habilitada.
Flujo de control v2
En TF1.x, las ops de flujo de control como tf.cond
y tf.while_loop
incorporan ops de bajo nivel como Switch
, Merge
, etc. TF2 ofrece ops de flujo de control funcional mejoradas que se implementan con trazados tf.function
separados para cada derivación y admiten la diferenciación de orden mayor.
Para aislar el impacto de este cambio de comportamiento en su código, si ejecución eager está deshabilitada puede usar tf.compat.v1.disable_control_flow_v2()
y tf.compat.v1.enable_control_flow_v2()
para deshabilitar o habilitar globalmente este cambio de comportamiento. Sin embargo, sólo puede desactivar el flujo de control v2 si la ejecución eager también está desactivada. Si está habilitada, siempre se usará el flujo de control v2.
Este cambio de comportamiento puede modificar drásticamente la estructura de los programas TF generados que usan flujo de control, ya que contendrán varios trazados de función anidados en lugar de un solo grafo plano. Por lo tanto, cualquier código que dependa en alto grado de la semántica exacta de los trazados producidos puede requerir alguna modificación. Esto incluye:
Código basado en nombres de operadores y tensores
Código que hace referencia a tensores creados dentro de una rama de flujo de control de TensorFlow desde fuera de la misma. Es probable que esto produzca un
InaccessibleTensorError
Este cambio de comportamiento está destinado a ser el rendimiento neutro a positivo, pero si se encuentra con un problema donde el flujo de control v2 se desempeña peor para usted que el flujo de control TF1.x, entonces levante un reporte incluyendo los pasos para reproducir el problema.
Cambios en el comportamiento de la API TensorShape
La clase TensorShape
se simplificó para albergar int
s, en lugar de objetos tf.compat.v1.Dimension
. Así que no es necesario llamar a .value
para obtener un int
.
Los objetos individuales tf.compat.v1.Dimension
siguen siendo accesibles desde tf.TensorShape.dims
.
Para aislar el impacto de este cambio de comportamiento en su código, puede usar tf.compat.v1.disable_v2_tensorshape()
y tf.compat.v1.enable_v2_tensorshape()
para desactivar o activar globalmente este cambio de comportamiento.
A continuación se muestran las diferencias entre TF1.x y TF2.
Si tenía esto en TF1.x:
Entonces haga esto en TF2:
Si tenía esto en TF1.x:
Entonces haga esto en TF2:
Si tenía esto en TF1.x (o usaba cualquier otro método de dimensión):
Entonces haga esto en TF2:
El valor booleano de un tf.TensorShape
es True
si se conoce el rango, False
en caso contrario.
Posibles errores debidos a cambios en TensorShape
Es poco probable que los cambios de comportamiento de TensorShape estropeen silenciosamente su código. Sin embargo, es posible que el código relacionado con las formas comience a lanzar AttributeError
s ya que int
s y None
s no tienen los mismos atributos que tf.compat.v1.Dimension
s. Aquí tiene algunos ejemplos de estos AttributeError
s:
Igualdad de tensores por valor
Los operadores binarios ==
y !=
sobre variables y tensores se cambiaron para comparar por valor en TF2 en lugar de comparar por referencia de objeto como en TF1.x. Además, los tensores y variables ya no son directamente hasheables o utilizables en conjuntos o claves dict, porque puede que no sea posible hashearlos por valor. En su lugar, exponen un método .ref()
que puede usar para obtener una referencia hasheable al tensor o variable.
Para aislar el impacto de este cambio de comportamiento, puede usar tf.compat.v1.disable_tensor_equality()
y tf.compat.v1.enable_tensor_equality()
para desactivar o activar globalmente este cambio de comportamiento.
Por ejemplo, en TF1.x, dos variables con el mismo valor devolverán false cuando use el operador ==
:
Mientras que en TF2 con las comprobaciones de igualdad de tensor activadas, x == y
devolverá True
.
Así que, en TF2, si necesita comparar por referencia de objeto asegúrese de usar is
e is no
.
Hashear tensores y variables
Con los comportamientos TF1.x solía ser posible añadir directamente variables y tensores a las estructuras de datos que requieren hashing, como las claves set
y dict
.
Sin embargo, en TF2 con la igualdad de tensores activada, los tensores y las variables se hacen inhasheables debido a que la semántica de los operadores ==
y !=
cambia a comprobaciones de igualdad de valores.
Así que, en TF2, si necesita utilizar objetos tensores o variables como claves o contenidos set
, puede utilizar tensor.ref()
para obtener una referencia hasheable que pueda usarse como clave:
Si es necesario, también puede obtener el tensor o la variable de la referencia utilizando reference.deref()
:
Recursos y lecturas complementarias
Visite la sección Migrar a TF2 para saber más sobre la migración a TF2 desde TF1.x.
Lea la guía de mapeado de modelos para saber más sobre cómo mapear sus modelos TF1.x para que funcionen en TF2 directamente.