Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/es-419/guide/ragged_tensor.ipynb
25115 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.

Documentación de la API: tf.RaggedTensor tf.ragged

Preparación

!pip install --pre -U tensorflow import math import tensorflow as tf

Descripción general

Sus datos vienen en muchas formas; sus tensores también deberían venir en muchas formas. Los tensores irregulares son el equivalente de TensorFlow a las listas anidadas de longitud variable. Facilitan el almacenamiento y procesamiento de datos con formas no uniformes, que incluyen:

  • Funciones de longitudes variables, como el conjunto de actores de una película.

  • Lotes de entradas secuenciales de longitud variable, como oraciones o videoclips.

  • Entradas jerárquicas, como documentos de texto que se subdividen en secciones, párrafos, oraciones y palabras.

  • Campos individuales en entradas estructuradas, como búferes de protocolo.

Qué se puede hacer con un tensor irregular

Los tensores irregulares son compatibles con más de cien operaciones de TensorFlow, incluidas las operaciones matemáticas (como tf.add y tf.reduce_mean), operaciones de arreglos (como tf.concat y tf.tile), operaciones de manipulación de cadenas de texto (como tf.strings.substr), operaciones de flujo de control (como tf.while_loop y tf.map_fn) y muchas otras:

digits = tf.ragged.constant([[3, 1, 4, 1], [], [5, 9, 2], [6], []]) words = tf.ragged.constant([["So", "long"], ["thanks", "for", "all", "the", "fish"]]) print(tf.add(digits, 3)) print(tf.reduce_mean(digits, axis=1)) print(tf.concat([digits, [[5, 3]]], axis=0)) print(tf.tile(digits, [1, 2])) print(tf.strings.substr(words, 0, 2)) print(tf.map_fn(tf.math.square, digits))

También hay una serie de métodos y operaciones que son específicos de los tensores irregulares, incluidos los métodos de fábrica, métodos de conversión y operaciones de mapeo de valores. Para ver una lista de operaciones compatibles, consulte la documentación del paquete de tf.ragged.

Los tensores irregulares son compatibles con muchas API de TensorFlow, incluidas Keras, Datasets, tf.function, SavedModels y tf.Example. Para obtener más información, consulte la sección sobre las API de TensorFlow abajo.

Al igual que con los tensores normales, se puede utilizar la indexación estilo Python para acceder a segmentos específicos de un tensor irregular. Para obtener más información, consulte la sección sobre Indexación abajo.

print(digits[0]) # First row
print(digits[:, :2]) # First two values in each row.
print(digits[:, -2:]) # Last two values in each row.

Y al igual que los tensores normales, se pueden utilizar operadores aritméticos y de comparación de Python para realizar operaciones por elementos. Para obtener más información, consulte la sección sobre Operadores sobrecargados abajo.

print(digits + 3)
print(digits + tf.ragged.constant([[1, 2, 3, 4], [], [5, 6, 7], [8], []]))

Si necesita realizar una transformación por elementos de los valores de un RaggedTensor, puede usar tf.ragged.map_flat_values, que toma una función y uno o más argumentos y aplica la función para transformar los valores de RaggedTensor.

times_two_plus_one = lambda x: x * 2 + 1 print(tf.ragged.map_flat_values(times_two_plus_one, digits))

Los tensores irregulares se pueden convertir en list de Python anidada y en array NumPy:

digits.to_list()
digits.numpy()

Construir un tensor irregular

La forma más sencilla de construir un tensor irregular es usar tf.ragged.constant, que construye el RaggedTensor correspondiente a una list de Python anidada o array NumPy ingresado:

sentences = tf.ragged.constant([ ["Let's", "build", "some", "ragged", "tensors", "!"], ["We", "can", "use", "tf.ragged.constant", "."]]) print(sentences)
paragraphs = tf.ragged.constant([ [['I', 'have', 'a', 'cat'], ['His', 'name', 'is', 'Mat']], [['Do', 'you', 'want', 'to', 'come', 'visit'], ["I'm", 'free', 'tomorrow']], ]) print(paragraphs)

Los tensores irregulares también se pueden construir al emparejar tensores de valores planos con tensores de partición de filas que indican cómo se deben dividir esos valores en filas, utilizando métodos de clase de fábrica como tf.RaggedTensor.from_value_rowids, tf.RaggedTensor.from_row_lengths y tf.RaggedTensor.from_row_splits.

tf.RaggedTensor.from_value_rowids

If you know which row each value belongs to, then you can build a RaggedTensor using a value_rowids row-partitioning tensor:

value_rowids row-partitioning tensor

print(tf.RaggedTensor.from_value_rowids( values=[3, 1, 4, 1, 5, 9, 2], value_rowids=[0, 0, 0, 0, 2, 2, 3]))

tf.RaggedTensor.from_row_lengths

Si sabe cuánto mide cada fila, entonces puede usar un tensor de partición de filas row_lengths:

row_lengths row-partitioning tensor

print(tf.RaggedTensor.from_row_lengths( values=[3, 1, 4, 1, 5, 9, 2], row_lengths=[4, 0, 2, 1]))

tf.RaggedTensor.from_row_splits

Si conoce el índice donde comienza y termina cada fila, entonces puede usar un tensor de partición de filas row_splits:

row_splits row-partitioning tensor

print(tf.RaggedTensor.from_row_splits( values=[3, 1, 4, 1, 5, 9, 2], row_splits=[0, 4, 4, 6, 7]))

Consulte la documentación de la clase tf.RaggedTensor para obtener una lista completa de los métodos de fábrica.

Nota: De forma predeterminada, estos métodos de fábrica agregan afirmaciones de que el tensor de partición de filas está bien formado y es consistente con la cantidad de valores. El parámetro validate=False se puede utilizar para omitir estas comprobaciones si se puede garantizar que las entradas estén bien formadas y sean consistentes.

Qué se puede almacenar en un tensor irregular

Al igual que con los Tensor normales, todos los valores en un RaggedTensor deben tener el mismo tipo; y todos los valores deben estar a la misma profundidad de anidamiento (el rango del tensor):

print(tf.ragged.constant([["Hi"], ["How", "are", "you"]])) # ok: type=string, rank=2
print(tf.ragged.constant([[[1, 2], [3]], [[4, 5]]])) # ok: type=int32, rank=3
try: tf.ragged.constant([["one", "two"], [3, 4]]) # bad: multiple types except ValueError as exception: print(exception)
try: tf.ragged.constant(["A", ["B", "C"]]) # bad: multiple nesting depths except ValueError as exception: print(exception)

Caso de uso de ejemplo

El siguiente ejemplo demuestra cómo se pueden usar los RaggedTensor para construir y combinar incrustaciones de unigramas y bigramas para un lote de consultas de longitud variable. Se usan marcadores especiales para el principio y el final de cada oración. Para obtener más detalles sobre las operaciones usadas en este ejemplo, consulte la documentación del paquete tf.ragged.

queries = tf.ragged.constant([['Who', 'is', 'Dan', 'Smith'], ['Pause'], ['Will', 'it', 'rain', 'later', 'today']]) # Create an embedding table. num_buckets = 1024 embedding_size = 4 embedding_table = tf.Variable( tf.random.truncated_normal([num_buckets, embedding_size], stddev=1.0 / math.sqrt(embedding_size))) # Look up the embedding for each word. word_buckets = tf.strings.to_hash_bucket_fast(queries, num_buckets) word_embeddings = tf.nn.embedding_lookup(embedding_table, word_buckets) # ① # Add markers to the beginning and end of each sentence. marker = tf.fill([queries.nrows(), 1], '#') padded = tf.concat([marker, queries, marker], axis=1) # ② # Build word bigrams and look up embeddings. bigrams = tf.strings.join([padded[:, :-1], padded[:, 1:]], separator='+') # ③ bigram_buckets = tf.strings.to_hash_bucket_fast(bigrams, num_buckets) bigram_embeddings = tf.nn.embedding_lookup(embedding_table, bigram_buckets) # ④ # Find the average embedding for each sentence all_embeddings = tf.concat([word_embeddings, bigram_embeddings], axis=1) # ⑤ avg_embedding = tf.reduce_mean(all_embeddings, axis=1) # ⑥ print(avg_embedding)

Ragged tensor example

Dimensiones irregulares y uniformes

Una dimensión irregular es una dimensión cuyos segmentos pueden tener diferentes longitudes. Por ejemplo, la dimensión (de la columna) interna de rt=[[3, 1, 4, 1], [], [5, 9, 2], [6], []] es irregular ya que los segmentos de la columna (rt[0, :] , ..., rt[4, :]) tienen longitudes diferentes. Las dimensiones cuyos segmentos tienen la misma longitud se denominan dimensiones uniformes.

La dimensión más externa de un tensor irregular es siempre uniforme, ya que consta de un solo segmento (y, por lo tanto, no hay posibilidad de diferentes longitudes de segmento). Las dimensiones restantes pueden ser irregulares o uniformes. Por ejemplo, puede almacenar las incrustaciones de palabras para cada palabra en un lote de oraciones usando un tensor irregular con forma [num_sentences, (num_words), embedding_size], donde los paréntesis alrededor de (num_words) indican que la dimensión es irregular.

Word embeddings using a ragged tensor

Los tensores irregulares pueden tener varias dimensiones irregulares. Por ejemplo, podría almacenar un lote de documentos de texto estructurados con un tensor con forma [num_documents, (num_paragraphs), (num_sentences), (num_words)] (donde nuevamente se usan paréntesis para indicar las dimensiones irregulares).

Al igual que con tf.Tensor, el rango de un tensor irregular es su número total de dimensiones (incluidas las dimensiones irregulares y uniformes). Un tensor potencialmente irregular es un valor que puede ser tf.Tensor o tf.RaggedTensor.

Al describir la forma de un RaggedTensor, las dimensiones irregulares se indican entre paréntesis. Por ejemplo, como vimos anteriormente, la forma de un RaggedTensor 3D que almacena incrustaciones de palabras para cada palabra en un lote de oraciones se puede escribir como [num_sentences, (num_words), embedding_size].

El atributo RaggedTensor.shape devuelve un tf.TensorShape para un tensor irregular donde las dimensiones irregulares tienen un tamaño None:

tf.ragged.constant([["Hi"], ["How", "are", "you"]]).shape

Se puede usar el método tf.RaggedTensor.bounding_shape para encontrar una forma delimitadora ajustada para un RaggedTensor determinado:

print(tf.ragged.constant([["Hi"], ["How", "are", "you"]]).bounding_shape())

Irregular versus disperso

No se puede considerar un tensor irregular como un tipo de tensor disperso. En particular, los tensores dispersos son codificaciones eficientes para tf.Tensor que modelan los mismos datos en un formato compacto; pero el tensor irregular es una extensión de tf.Tensor que modela una clase ampliada de datos. Esta diferencia es crucial a la hora de definir operaciones:

  • Aplicar una operación a un tensor disperso o denso siempre debería dar el mismo resultado.

  • Aplicar una operación a un tensor irregular o disperso puede dar resultados diferentes.

Como ejemplo ilustrativo, considere cómo se definen operaciones de arreglo como concat, stack y tile para tensores irregulares frente a tensores dispersos. La concatenación de tensores irregulares une cada fila para formar una sola fila con la longitud combinada:

Concatenating ragged tensors

ragged_x = tf.ragged.constant([["John"], ["a", "big", "dog"], ["my", "cat"]]) ragged_y = tf.ragged.constant([["fell", "asleep"], ["barked"], ["is", "fuzzy"]]) print(tf.concat([ragged_x, ragged_y], axis=1))

Sin embargo, concatenar tensores dispersos equivale a concatenar los tensores densos correspondientes, como se ilustra en el siguiente ejemplo (donde Ø indica los valores faltantes):

Concatenating sparse tensors

sparse_x = ragged_x.to_sparse() sparse_y = ragged_y.to_sparse() sparse_result = tf.sparse.concat(sp_inputs=[sparse_x, sparse_y], axis=1) print(tf.sparse.to_dense(sparse_result, ''))

Para ver otro ejemplo de por qué esta distinción es importante, considere la definición de "el valor medio de cada fila" para una operación como tf.reduce_mean. Para un tensor irregular, el valor medio de una fila es la suma de los valores de la fila dividida por el ancho de la fila. Pero para un tensor disperso, el valor medio de una fila es la suma de los valores de la fila dividida por el ancho total del tensor disperso (que es mayor o igual que el ancho de la fila más larga).

Las API de TensorFlow

Keras

tf.keras es la API de alto nivel de TensorFlow para crear y entrenar modelos de aprendizaje profundo. Los tensores irregulares se pueden pasar como entradas a un modelo de Keras al establecer ragged=True en tf.keras.Input o tf.keras.layers.InputLayer. También se pueden pasar tensores irregulares entre las capas de Keras y devolverlos mediante los modelos de Keras. El siguiente ejemplo muestra un modelo LSTM de juguete que se entrena con tensores irregulares.

# Task: predict whether each sentence is a question or not. sentences = tf.constant( ['What makes you think she is a witch?', 'She turned me into a newt.', 'A newt?', 'Well, I got better.']) is_question = tf.constant([True, False, True, False]) # Preprocess the input strings. hash_buckets = 1000 words = tf.strings.split(sentences, ' ') hashed_words = tf.strings.to_hash_bucket_fast(words, hash_buckets) # Build the Keras model. keras_model = tf.keras.Sequential([ tf.keras.layers.Input(shape=[None], dtype=tf.int64, ragged=True), tf.keras.layers.Embedding(hash_buckets, 16), tf.keras.layers.LSTM(32, use_bias=False), tf.keras.layers.Dense(32), tf.keras.layers.Activation(tf.nn.relu), tf.keras.layers.Dense(1) ]) keras_model.compile(loss='binary_crossentropy', optimizer='rmsprop') keras_model.fit(hashed_words, is_question, epochs=5) print(keras_model.predict(hashed_words))

tf.Example

tf.Example es una codificación protobuf estándar para datos de TensorFlow. Los datos codificados con las tf.Example suelen incluir funciones de longitud variable. Por ejemplo, el siguiente código define un lote de cuatro mensajes tf.Example con diferentes longitudes de función:

import google.protobuf.text_format as pbtext def build_tf_example(s): return pbtext.Merge(s, tf.train.Example()).SerializeToString() example_batch = [ build_tf_example(r''' features { feature {key: "colors" value {bytes_list {value: ["red", "blue"]} } } feature {key: "lengths" value {int64_list {value: [7]} } } }'''), build_tf_example(r''' features { feature {key: "colors" value {bytes_list {value: ["orange"]} } } feature {key: "lengths" value {int64_list {value: []} } } }'''), build_tf_example(r''' features { feature {key: "colors" value {bytes_list {value: ["black", "yellow"]} } } feature {key: "lengths" value {int64_list {value: [1, 3]} } } }'''), build_tf_example(r''' features { feature {key: "colors" value {bytes_list {value: ["green"]} } } feature {key: "lengths" value {int64_list {value: [3, 5, 2]} } } }''')]

Puede analizar estos datos codificados con tf.io.parse_example, que toma un tensor de cadenas serializadas y un diccionario de especificación de funciones, y devuelve un diccionario que asigna nombres de funciones a tensores. Para leer las funciones de longitudes variables en tensores irregulares, simplemente use tf.io.RaggedFeature en el diccionario de especificación de funciones:

feature_specification = { 'colors': tf.io.RaggedFeature(tf.string), 'lengths': tf.io.RaggedFeature(tf.int64), } feature_tensors = tf.io.parse_example(example_batch, feature_specification) for name, value in feature_tensors.items(): print("{}={}".format(name, value))

tf.io.RaggedFeature también se puede utilizar para leer funciones con vafrias dimensiones irregulares. Para obtener más información, consulte la documentación de la API.

Conjuntos de datos

tf.data es una API que le permite desarrollar canalizaciones de entradas complejas a partir de piezas reutilizables simples. Su estructura de datos principal es tf.data.Dataset, que representa una secuencia de elementos, en la que cada elemento se compone de uno o más componentes.

# Helper function used to print datasets in the examples below. def print_dictionary_dataset(dataset): for i, element in enumerate(dataset): print("Element {}:".format(i)) for (feature_name, feature_value) in element.items(): print('{:>14} = {}'.format(feature_name, feature_value))

Construir conjuntos de datos con tensores irregulares

Los conjuntos de datos se pueden construir a partir de tensores irregulares con los mismos métodos que se usan para construir los tensores desde los tf.Tensor o array NumPy, como Dataset.from_tensor_slices:

dataset = tf.data.Dataset.from_tensor_slices(feature_tensors) print_dictionary_dataset(dataset)

Nota: Dataset.from_generator aún no admite tensores irregulares, pero pronto se podrá.

Cómo agrupar o desagrupar conjuntos de datos con tensores dispersos

Los conjuntos de datos con tensores irregulares se pueden agrupar (lo que combina n elementos consecutivos en un solo elemento) con el método Dataset.batch.

batched_dataset = dataset.batch(2) print_dictionary_dataset(batched_dataset)

Por el contrario, un conjunto de datos agrupado se puede transformar en un conjunto de datos plano con Dataset.unbatch.

unbatched_dataset = batched_dataset.unbatch() print_dictionary_dataset(unbatched_dataset)

Agrupar conjuntos de datos con tensores no irregulares de longitud variable

Si tiene un conjunto de datos que contiene tensores no irregulares y las longitudes de los tensores varían según los elementos, puede agrupar esos tensores no irregulares en tensores irregulares aplicando la transformación dense_to_ragged_batch:

non_ragged_dataset = tf.data.Dataset.from_tensor_slices([1, 5, 3, 2, 8]) non_ragged_dataset = non_ragged_dataset.map(tf.range) batched_non_ragged_dataset = non_ragged_dataset.apply( tf.data.experimental.dense_to_ragged_batch(2)) for element in batched_non_ragged_dataset: print(element)

Transformar conjuntos de datos con tensores irregulares

También se pueden crear o transformar tensores irregulares en los conjuntos de datos con Dataset.map:

def transform_lengths(features): return { 'mean_length': tf.math.reduce_mean(features['lengths']), 'length_ranges': tf.ragged.range(features['lengths'])} transformed_dataset = dataset.map(transform_lengths) print_dictionary_dataset(transformed_dataset)

tf.function

tf.function es un decorador que precalcula gráficos de TensorFlow para funciones de Python, lo que puede mejorar sustancialmente el rendimiento de su código de TensorFlow. Los tensores irregulares se pueden usar de forma clara con funciones decoradas @tf.function. Por ejemplo, la siguiente función funciona con tensores irregulares y no irregulares:

@tf.function def make_palindrome(x, axis): return tf.concat([x, tf.reverse(x, [axis])], axis)
make_palindrome(tf.constant([[1, 2], [3, 4], [5, 6]]), axis=1)
make_palindrome(tf.ragged.constant([[1, 2], [3], [4, 5, 6]]), axis=1)

Si quiere specificar explícitamente input_signature para tf.function, puede hacerlo con tf.RaggedTensorSpec.

@tf.function( input_signature=[tf.RaggedTensorSpec(shape=[None, None], dtype=tf.int32)]) def max_and_min(rt): return (tf.math.reduce_max(rt, axis=-1), tf.math.reduce_min(rt, axis=-1)) max_and_min(tf.ragged.constant([[1, 2], [3], [4, 5, 6]]))

Funciones concretas

Las funciones concretas encapsulan gráficos trazados individuales creados por tf.function. Los tensores irregulares se pueden usar de forma clara con funciones concretas.

@tf.function def increment(x): return x + 1 rt = tf.ragged.constant([[1, 2], [3], [4, 5, 6]]) cf = increment.get_concrete_function(rt) print(cf(rt))

SavedModels

Un SavedModel es un programa serializado de TensorFlow, que incluye pesos y cálculos. Se puede construir a partir de un modelo de Keras o de un modelo personalizado. En cualquier caso, los tensores irregulares se pueden usar de forma clara con las funciones y métodos definidos por un SavedModel.

Ejemplo: guardar un modelo de Keras

import tempfile keras_module_path = tempfile.mkdtemp() tf.saved_model.save(keras_model, keras_module_path) imported_model = tf.saved_model.load(keras_module_path) imported_model(hashed_words)

Ejemplo: guardar un modelo personalizado

class CustomModule(tf.Module): def __init__(self, variable_value): super(CustomModule, self).__init__() self.v = tf.Variable(variable_value) @tf.function def grow(self, x): return x * self.v module = CustomModule(100.0) # Before saving a custom model, you must ensure that concrete functions are # built for each input signature that you will need. module.grow.get_concrete_function(tf.RaggedTensorSpec(shape=[None, None], dtype=tf.float32)) custom_module_path = tempfile.mkdtemp() tf.saved_model.save(module, custom_module_path) imported_model = tf.saved_model.load(custom_module_path) imported_model.grow(tf.ragged.constant([[1.0, 4.0, 3.0], [2.0]]))

Nota: Las signaturas de SavedModel son funciones concretas. Como explicamos en la sección anterior sobre funciones concretas, los tensores irregulares solo se manipulan correctamente mediante funciones concretas que comienzan con TensorFlow 2.3. Si necesita usar signaturas de SavedModel en una versión anterior de TensorFlow, se recomienda descomponer el tensor irregular en los tensores que lo componen.

Operadores sobrecargados

La clase RaggedTensor sobrecarga los operadores aritméticos y de comparación estándar de Python, lo que facilita que se realicen operaciones matemáticas básicas por elementos:

x = tf.ragged.constant([[1, 2], [3], [4, 5, 6]]) y = tf.ragged.constant([[1, 1], [2], [3, 3, 3]]) print(x + y)

Dado que los operadores sobrecargados realizan cálculos por elementos, las entradas a todas las operaciones binarias deben tener la misma forma o deben poder transmitirse a la misma forma. En el caso de transmisión más simple, un único escalar se combina elemento a elemento con cada valor en un tensor irregular:

x = tf.ragged.constant([[1, 2], [3], [4, 5, 6]]) print(x + 3)

Para ver una explicación de casos más avanzados, consulte la sección sobre Transmición.

Los tensores irregulares sobrecargan el mismo conjunto de operadores que los Tensor normales: los operadores unarios -, ~ y abs(); y los operadores binarios +, -, *, /, //, %, **, &, |, ^, ==, <, <=, > y >=.

Indexación

Los tensores irregulares admiten la indexación al estilo Python, incluida la indexación y la segmentación multidimensional. Los siguientes ejemplos demuestran la indexación de tensores irregulares con un tensor irregular 2D y 3D.

Ejemplos de indexación: tensor irregular 2D

queries = tf.ragged.constant( [['Who', 'is', 'George', 'Washington'], ['What', 'is', 'the', 'weather', 'tomorrow'], ['Goodnight']])
print(queries[1]) # A single query
print(queries[1, 2]) # A single word
print(queries[1:]) # Everything but the first row
print(queries[:, :3]) # The first 3 words of each query
print(queries[:, -2:]) # The last 2 words of each query

Ejemplos de indexación: tensor irregular 3D

rt = tf.ragged.constant([[[1, 2, 3], [4]], [[5], [], [6]], [[7]], [[8, 9], [10]]])
print(rt[1]) # Second row (2D RaggedTensor)
print(rt[3, 0]) # First element of fourth row (1D Tensor)
print(rt[:, 1:3]) # Items 1-3 of each row (3D RaggedTensor)
print(rt[:, -1:]) # Last item of each row (3D RaggedTensor)

RaggedTensor admite la indexación y la segmentación multidimensional con una restricción: no se permite la indexación en una dimensión irregular. Este caso es problemático porque el valor indicado puede existir en algunas filas pero no en otras. En tales casos, no queda claro si se debería (1) generar un IndexError; (2) utilizar un valor predeterminado; o (3) omitir ese valor y devolver un tensor con menos filas que las que tenía al principio. Según las directrices de Python ("Ante la ambigüedad, rechaze la tentación de adivinar"), actualmente, es una operación no permitida.

Conversión de tipo tensor

La clase RaggedTensor define métodos que se pueden usar para convertir entre los RaggedTensor y los tf.Tensor o tf.SparseTensors:

ragged_sentences = tf.ragged.constant([ ['Hi'], ['Welcome', 'to', 'the', 'fair'], ['Have', 'fun']])
# RaggedTensor -> Tensor print(ragged_sentences.to_tensor(default_value='', shape=[None, 10]))
# Tensor -> RaggedTensor x = [[1, 3, -1, -1], [2, -1, -1, -1], [4, 5, 8, 9]] print(tf.RaggedTensor.from_tensor(x, padding=-1))
#RaggedTensor -> SparseTensor print(ragged_sentences.to_sparse())
# SparseTensor -> RaggedTensor st = tf.SparseTensor(indices=[[0, 0], [2, 0], [2, 1]], values=['a', 'b', 'c'], dense_shape=[3, 3]) print(tf.RaggedTensor.from_sparse(st))

Evaluar tensores irregulares

Para acceder a los valores en un tensor irregular, se puede:

  1. Usar tf.RaggedTensor.to_list para convertir el tensor irregular en una lista de Python anidada.

  2. Usar tf.RaggedTensor.numpy para convertir el tensor irregular en un arreglo NumPy cuyos valores son arreglos NumPy anidados.

  3. Descomponer el tensor irregular en sus componentes, con las propiedades tf.RaggedTensor.values ​​y tf.RaggedTensor.row_splits, o métodos de partición de filas como tf.RaggedTensor.row_lengths y tf.RaggedTensor.value_rowids.

  4. Usar la indexación de Python para seleccionar los valores del tensor irregular.

rt = tf.ragged.constant([[1, 2], [3, 4, 5], [6], [], [7]]) print("Python list:", rt.to_list()) print("NumPy array:", rt.numpy()) print("Values:", rt.values.numpy()) print("Splits:", rt.row_splits.numpy()) print("Indexed value:", rt[1].numpy())

Formas irregulares

La forma de un tensor especifica el tamaño de cada eje. Por ejemplo, la forma de [[1, 2], [3, 4], [5, 6]] es [3, 2], ya que hay 3 filas y 2 columnas. TensorFlow tiene dos formas distintas pero que están relacionadas para describir formas:

  • forma estática: información sobre los tamaños de los ejes que se conocen estáticamente (por ejemplo, al trazar una tf.function). Puede especificarse parcialmente.

  • forma dinámica: información del tiempo de ejecución sobre los tamaños de los ejes.

Forma estática

La forma estática de un tensor contiene información sobre los tamaños de sus ejes que se conoce en el momento de la construcción del gráfico. Tanto para tf.Tensor como para tf.RaggedTensor, está disponible mediante la propiedad .shape y está codificado mediante tf.TensorShape:

x = tf.constant([[1, 2], [3, 4], [5, 6]]) x.shape # shape of a tf.tensor
rt = tf.ragged.constant([[1], [2, 3], [], [4]]) rt.shape # shape of a tf.RaggedTensor

La forma estática de una dimensión irregular es siempre None (es decir, sin especificar). Sin embargo, lo contrario no es cierto: si una dimensión TensorShape es None, podría indicar que la dimensión es irregular o podría indicar que la dimensión es uniforme pero que su tamaño no se conoce estáticamente.

Forma dinámica

La forma dinámica de un tensor contiene información sobre los tamaños de sus ejes que se conocen cuando se ejecuta el gráfico. Se construye con la operación tf.shape. Para tf.Tensor, tf.shape devuelve la forma como un Tensor entero de una dimensión, donde tf.shape(x)[i] es el tamaño del eje i.

x = tf.constant([['a', 'b'], ['c', 'd'], ['e', 'f']]) tf.shape(x)

Sin embargo, un Tensor una dimensión no es lo suficientemente expresivo para describir la forma de un tf.RaggedTensor. En cambio, la forma dinámica de los tensores irregulares se codifica mediante un tipo dedicado, tf.experimental.DynamicRaggedShape. En el siguiente ejemplo, DynamicRaggedShape que devuelve tf.shape(rt) indica que el tensor irregular tiene 4 filas, con longitudes de 1, 3, 0 y 2:

rt = tf.ragged.constant([[1], [2, 3, 4], [], [5, 6]]) rt_shape = tf.shape(rt) print(rt_shape)

Forma dinámica: operaciones

DynamicRaggedShape se puede usar con la mayoría de las operaciones de TensorFlow que necesitan formas, incluidas tf.reshape, tf.zeros, tf.ones. tf.fill, tf.broadcast_dynamic_shape y tf.broadcast_to.

print(f"tf.reshape(x, rt_shape) = {tf.reshape(x, rt_shape)}") print(f"tf.zeros(rt_shape) = {tf.zeros(rt_shape)}") print(f"tf.ones(rt_shape) = {tf.ones(rt_shape)}") print(f"tf.fill(rt_shape, 9) = {tf.fill(rt_shape, 'x')}")

Forma dinámica: indexación y segmentación

DynamicRaggedShape también se puede indexar para obtener los tamaños de las dimensiones uniformes. Por ejemplo, podemos encontrar el número de filas en un tensor irregular con tf.shape(rt)[0] (tal como lo haríamos para un tensor no irregular):

rt_shape[0]

Sin embargo, es un error utilizar la indexación para intentar recuperar el tamaño de una dimensión irregular, ya que no tiene un tamaño único. (Dado que RaggedTensor realiza un seguimiento de qué ejes son irregulares, este error solo se genera durante la ejecución eager o cuando se traza una tf.function.; jamás ocurrirá al ejecutar una función concreta).

try: rt_shape[1] except ValueError as e: print("Got expected ValueError:", e)

Los DynamicRaggedShape también se pueden segmentar, siempre que el segmento comience con el eje 0 o contenga solo dimensiones densas.

rt_shape[:1]

Forma dinámica: codificación

DynamicRaggedShape se codifica con dos campos:

  • inner_shape: Un vector entero que da la forma de un tf.Tensor denso.

  • row_partitions: una lista de objetos tf.experimental.RowPartition, que describe cómo se debe segmentar la dimensión más externa de esa forma interna para agregar ejes irregulares.

Para obtener más información sobre las particiones de filas, consulte la sección "Codificación de RaggedTensor" a continuación y la documentación de la API para tf.experimental.RowPartition.

Forma dinámica: construcción

DynamicRaggedShape se construye con mayor frecuencia al aplicar tf.shape a un RaggedTensor, pero también se puede construir directamente:

tf.experimental.DynamicRaggedShape( row_partitions=[tf.experimental.RowPartition.from_row_lengths([5, 3, 2])], inner_shape=[10, 8])

Si las longitudes de todas las filas se conocen estáticamente, también se puede usar DynamicRaggedShape.from_lengths para construir una forma irregular dinámica. (Esto es especialmente útil para probar y demostrar código, ya que es raro que las longitudes de las dimensiones irregulares se conozcan estáticamente).

tf.experimental.DynamicRaggedShape.from_lengths([4, (2, 1, 0, 8), 12])

Transmisión

La transmisión es el proceso de hacer que tensores con diferentes formas tengan formas compatibles para las operaciones por elementos. Para obtener más información sobre la transmisión, consulte:

Estos son los pasos básicos para transmitir dos entradas x e y para tener formas compatibles:

  1. Si x e y no tienen el mismo número de dimensiones, agregue las dimensiones exteriores (con tamaño 1) hasta que sean iguales.

  2. Para cada dimensión donde x y tengan tamaños diferentes:

  • Si x o y tienen el tamaño 1 en la dimensión d, repita sus valores en la dimensión d para que coincida con el otro tamaño de la entrada.

  • De lo contrario, genere una excepción (x e y no son compatibles con la transmisión).

Si el tamaño de un tensor en una dimensión uniforme es un solo número (el tamaño de los segmentos en esa dimensión); y el tamaño de un tensor en una dimensión irregular es una lista de longitudes de segmentos (para todos los segmentos en esa dimensión).

Ejemplos de transmisión

# x (2D ragged): 2 x (num_rows) # y (scalar) # result (2D ragged): 2 x (num_rows) x = tf.ragged.constant([[1, 2], [3]]) y = 3 print(x + y)
# x (2d ragged): 3 x (num_rows) # y (2d tensor): 3 x 1 # Result (2d ragged): 3 x (num_rows) x = tf.ragged.constant( [[10, 87, 12], [19, 53], [12, 32]]) y = [[1000], [2000], [3000]] print(x + y)
# x (3d ragged): 2 x (r1) x 2 # y (2d ragged): 1 x 1 # Result (3d ragged): 2 x (r1) x 2 x = tf.ragged.constant( [[[1, 2], [3, 4], [5, 6]], [[7, 8]]], ragged_rank=1) y = tf.constant([[10]]) print(x + y)
# x (3d ragged): 2 x (r1) x (r2) x 1 # y (1d tensor): 3 # Result (3d ragged): 2 x (r1) x (r2) x 3 x = tf.ragged.constant( [ [ [[1], [2]], [], [[3]], [[4]], ], [ [[5], [6]], [[7]] ] ], ragged_rank=2) y = tf.constant([10, 20, 30]) print(x + y)

A continuación se muestran algunos ejemplos de formas que no se transmiten:

# x (2d ragged): 3 x (r1) # y (2d tensor): 3 x 4 # trailing dimensions do not match x = tf.ragged.constant([[1, 2], [3, 4, 5, 6], [7]]) y = tf.constant([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) try: x + y except tf.errors.InvalidArgumentError as exception: print(exception)
# x (2d ragged): 3 x (r1) # y (2d ragged): 3 x (r2) # ragged dimensions do not match. x = tf.ragged.constant([[1, 2, 3], [4], [5, 6]]) y = tf.ragged.constant([[10, 20], [30, 40], [50]]) try: x + y except tf.errors.InvalidArgumentError as exception: print(exception)
# x (3d ragged): 3 x (r1) x 2 # y (3d ragged): 3 x (r1) x 3 # trailing dimensions do not match x = tf.ragged.constant([[[1, 2], [3, 4], [5, 6]], [[7, 8], [9, 10]]]) y = tf.ragged.constant([[[1, 2, 0], [3, 4, 0], [5, 6, 0]], [[7, 8, 0], [9, 10, 0]]]) try: x + y except tf.errors.InvalidArgumentError as exception: print(exception)

Codificar RaggedTensor

Los tensores irregulares se codifican mediante la clase RaggedTensor. Internamente, cada RaggedTensor se compone de:

  • Un tensor values, que concatena las filas de longitud variable en una lista aplanada.

  • Una row_partition, que indica cómo se dividen los valores aplanados en filas.

RaggedTensor encoding

La row_partition se puede almacenar con cuatro codificaciones diferentes:

  • row_splits es un vector entero que especifica los puntos de división entre filas.

  • value_rowids es un vector entero que especifica el índice de fila para cada valor.

  • row_lengths es un vector entero que especifica la longitud de cada fila.

  • uniform_row_length es un escalar entero que especifica una única longitud para todas las filas.

row_partition encodings

También se puede incluir un número entero escalar nrows en la codificación row_partition para tener en cuenta las filas finales vacías con value_rowids o las filas vacías con uniform_row_length.

rt = tf.RaggedTensor.from_row_splits( values=[3, 1, 4, 1, 5, 9, 2], row_splits=[0, 4, 4, 6, 7]) print(rt)

La elección de qué codificación usar para las particiones de las filas se gestiona internamente mediante tensores irregulares para mejorar la eficiencia en algunos contextos. En particular, algunas de las ventajas y desventajas de los diferentes esquemas de partición de filas son:

  • Indexación eficiente: la codificación row_splits permite la indexación en tiempo constante y la división en tensores irregulares.

  • Concatenación eficiente: la codificación row_lengths es más eficiente al concatenar tensores irregulares, ya que las longitudes de las filas no cambian cuando dos tensores se concatenan juntos.

  • Tamaño de codificación pequeño: la codificación value_rowids es más eficiente cuando se almacenan tensores irregulares que tienen una gran cantidad de filas vacías, ya que el tamaño del tensor depende solo del número total de valores. Por otro lado, las codificaciones row_splits y row_lengths son más eficientes cuando se almacenan tensores irregulares con filas más largas, ya que solo requieren un valor escalar para cada fila.

  • Compatibilidad: el esquema value_rowids coincide con el formato de segmentación que utilizan las operaciones, como tf.segment_sum. El esquema row_limits coincide con el formato que utilizan las operaciones como tf.sequence_mask.

  • Dimensiones uniformes: como se analiza a continuación, la codificación uniform_row_length se utiliza para codificar tensores irregulares con dimensiones uniformes.

Varias dimensiones irregulares

Un tensor irregular con varias dimensiones irregulares se codifica con un RaggedTensor anidado para el tensor values. Cada RaggedTensor anidado agrega una única dimensión irregular.

Encoding of a ragged tensor with multiple ragged dimensions (rank 2)

rt = tf.RaggedTensor.from_row_splits( values=tf.RaggedTensor.from_row_splits( values=[10, 11, 12, 13, 14, 15, 16, 17, 18, 19], row_splits=[0, 3, 3, 5, 9, 10]), row_splits=[0, 1, 1, 5]) print(rt) print("Shape: {}".format(rt.shape)) print("Number of partitioned dimensions: {}".format(rt.ragged_rank))

La función de fábrica tf.RaggedTensor.from_nested_row_splits se puede usar para construir un RaggedTensor con varias dimensiones irregulares directamente al proporcionar una lista de tensores row_splits:

rt = tf.RaggedTensor.from_nested_row_splits( flat_values=[10, 11, 12, 13, 14, 15, 16, 17, 18, 19], nested_row_splits=([0, 1, 1, 5], [0, 3, 3, 5, 9, 10])) print(rt)

Rango irregular y valores planos

El rango irregular de un tensor irregular es la cantidad de veces que se divide el tensor values subyacente (es decir, la profundidad de anidamiento de los objetos RaggedTensor). El tensor values más interno se conoce como flat_values. En el siguiente ejemplo, conversations tienen ragged_rank=3 y sus flat_values ​​son un Tensor de una dimension con 24 cadenas de texto:

# shape = [batch, (paragraph), (sentence), (word)] conversations = tf.ragged.constant( [[[["I", "like", "ragged", "tensors."]], [["Oh", "yeah?"], ["What", "can", "you", "use", "them", "for?"]], [["Processing", "variable", "length", "data!"]]], [[["I", "like", "cheese."], ["Do", "you?"]], [["Yes."], ["I", "do."]]]]) conversations.shape
assert conversations.ragged_rank == len(conversations.nested_row_splits) conversations.ragged_rank # Number of partitioned dimensions.
conversations.flat_values.numpy()

Dimensiones internas uniformes

Los tensores irregulares con dimensiones internas uniformes se codifican con un tf.Tensor multidimensional para los flat_values (es decir, los values más internos).

Encoding of ragged tensors with uniform inner dimensions

rt = tf.RaggedTensor.from_row_splits( values=[[1, 3], [0, 0], [1, 3], [5, 3], [3, 3], [1, 2]], row_splits=[0, 3, 4, 6]) print(rt) print("Shape: {}".format(rt.shape)) print("Number of partitioned dimensions: {}".format(rt.ragged_rank)) print("Flat values shape: {}".format(rt.flat_values.shape)) print("Flat values:\n{}".format(rt.flat_values))

Dimensiones no internas uniformes

Los tensores irregulares con dimensiones uniformes no internas se codifican al particionar las filas con uniform_row_length.

Encoding of ragged tensors with uniform non-inner dimensions

rt = tf.RaggedTensor.from_uniform_row_length( values=tf.RaggedTensor.from_row_splits( values=[10, 11, 12, 13, 14, 15, 16, 17, 18, 19], row_splits=[0, 3, 5, 9, 10]), uniform_row_length=2) print(rt) print("Shape: {}".format(rt.shape)) print("Number of partitioned dimensions: {}".format(rt.ragged_rank))