¿Tengo una pregunta? Conéctese con la comunidad en el Foro de visita del foro de TensorFlow

Tensores irregulares

Ver en TensorFlow.org Ejecutar en Google Colab Ver fuente en GitHub Descargar cuaderno

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

Configuración

import math
import tensorflow as tf

Descripción general

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

  • Funciones de duración variable, como el conjunto de actores de una película.
  • Lotes de entradas secuenciales de longitud variable, como frases 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é puedes hacer con un tensor irregular

Los tensores irregulares son compatibles con más de cien operaciones de TensorFlow, incluidas operaciones matemáticas (como tf.add y tf.reduce_mean ), operaciones de matriz (como tf.concat y tf.tile ), operaciones de manipulación de cadenas (como tf.substr ), controlar las operaciones de flujo (como tf.while_loop y tf.map_fn ), y muchos otros:

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))
<tf.RaggedTensor [[6, 4, 7, 4], [], [8, 12, 5], [9], []]>
tf.Tensor([2.25              nan 5.33333333 6.                nan], shape=(5,), dtype=float64)
<tf.RaggedTensor [[3, 1, 4, 1], [], [5, 9, 2], [6], [], [5, 3]]>
<tf.RaggedTensor [[3, 1, 4, 1, 3, 1, 4, 1], [], [5, 9, 2, 5, 9, 2], [6, 6], []]>
<tf.RaggedTensor [[b'So', b'lo'], [b'th', b'fo', b'al', b'th', b'fi']]>
<tf.RaggedTensor [[9, 1, 16, 1], [], [25, 81, 4], [36], []]>

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, los métodos de conversión y las operaciones de mapeo de valores. Para obtener una lista de las operaciones admitidas, consulte la documentación del paquete 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 a continuación.

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

print(digits[0])       # First row
tf.Tensor([3 1 4 1], shape=(4,), dtype=int32)
print(digits[:, :2])   # First two values in each row.
<tf.RaggedTensor [[3, 1], [], [5, 9], [6], []]>
print(digits[:, -2:])  # Last two values in each row.
<tf.RaggedTensor [[4, 1], [], [9, 2], [6], []]>

Y al igual que los tensores normales, puede usar operadores aritméticos y de comparación de Python para realizar operaciones de elemento. Para obtener más información, consulte la sección sobre operadores sobrecargados a continuación.

print(digits + 3)
<tf.RaggedTensor [[6, 4, 7, 4], [], [8, 12, 5], [9], []]>
print(digits + tf.ragged.constant([[1, 2, 3, 4], [], [5, 6, 7], [8], []]))
<tf.RaggedTensor [[4, 3, 7, 5], [], [10, 15, 9], [14], []]>

Si necesita realizar una transformación de elemento a los valores de RaggedTensor , puede usar tf.ragged.map_flat_values , que toma una función más 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))
<tf.RaggedTensor [[7, 3, 9, 3], [], [11, 19, 5], [13], []]>

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

digits.to_list()
[[3, 1, 4, 1], [], [5, 9, 2], [6], []]
digits.numpy()
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/ragged/ragged_tensor.py:2053: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray
  return np.array(rows)
array([array([3, 1, 4, 1], dtype=int32), array([], dtype=int32),
       array([5, 9, 2], dtype=int32), array([6], dtype=int32),
       array([], dtype=int32)], dtype=object)

Construyendo un tensor irregular

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

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

Los tensores irregulares también se pueden construir emparejando tensores de valores planos con tensores de división de filas que indiquen cómo esos valores deben dividirse 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

Si sabe a qué fila pertenece cada valor, entonces puede construir un RaggedTensor usando un tensor de partición de filas value_rowids :

value_rowids tensor de partición de filas

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 [[3, 1, 4, 1], [], [5, 9], [2]]>

tf.RaggedTensor.from_row_lengths

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

row_lengths tensor de partición de filas

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

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 tensor de partición de filas

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

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

Lo que puedes almacenar en un tensor irregular

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

print(tf.ragged.constant([["Hi"], ["How", "are", "you"]]))  # ok: type=string, rank=2
<tf.RaggedTensor [[b'Hi'], [b'How', b'are', b'you']]>
print(tf.ragged.constant([[[1, 2], [3]], [[4, 5]]]))        # ok: type=int32, rank=3
<tf.RaggedTensor [[[1, 2], [3]], [[4, 5]]]>
try:
  tf.ragged.constant([["one", "two"], [3, 4]])              # bad: multiple types
except ValueError as exception:
  print(exception)
Can't convert Python sequence with mixed types to Tensor.
try:
  tf.ragged.constant(["A", ["B", "C"]])                     # bad: multiple nesting depths
except ValueError as exception:
  print(exception)
all scalar values must have the same nesting depth

Caso de uso de ejemplo

El siguiente ejemplo demuestra cómo se pueden usar RaggedTensor s para construir y combinar incrustaciones de unigrama y bigrama para un lote de consultas de longitud variable, usando marcadores especiales para el principio y el final de cada oración. Para obtener más detalles sobre las operaciones utilizadas 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)
tf.Tensor(
[[-0.23997106 -0.14016564 -0.2052617  -0.14209671]
 [ 0.17473209 -0.43044376 -0.33512878 -0.38062695]
 [ 0.16858886 -0.04922291  0.04416551  0.15116404]], shape=(3, 4), dtype=float32)

Ejemplo de tensor irregular

Dimensiones irregulares y uniformes

Una dimensión irregular es una dimensión cuyas rebanadas pueden tener diferentes longitudes. Por ejemplo, la dimensión interna (columna) de rt=[[3, 1, 4, 1], [], [5, 9, 2], [6], []] es irregular, ya que las secciones de columna ( rt[0, :] , ..., rt[4, :] ) tienen diferentes longitudes. Las dimensiones cuyas rebanadas tienen todas 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 corte (y, por lo tanto, no hay posibilidad de diferentes longitudes de corte). 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.

Incrustaciones de palabras con un tensor irregular

Los tensores irregulares pueden tener múltiples dimensiones irregulares. Por ejemplo, puede almacenar un lote de documentos de texto estructurado usando un tensor con forma [num_documents, (num_paragraphs), (num_sentences), (num_words)] (donde nuevamente se usan paréntesis para indicar 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 un tf.Tensor o untf.RaggedTensor .

Al describir la forma de un RaggedTensor, las dimensiones irregulares se indican convencionalmente encerrándolas entre paréntesis. Por ejemplo, como vio 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
TensorShape([2, None])

El método tf.RaggedTensor.bounding_shape se puede usar para encontrar una forma delimitadora ajustada para un RaggedTensor dado:

print(tf.ragged.constant([["Hi"], ["How", "are", "you"]]).bounding_shape())
tf.Tensor([2 3], shape=(2,), dtype=int64)

Harapiento vs escaso

Un tensor irregular no debe considerarse 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 expandida de datos. Esta diferencia es crucial a la hora de definir operaciones:

  • La aplicación de una op a un tensor denso o escaso siempre debería dar el mismo resultado.
  • La aplicación de una op a un tensor irregular o escaso puede dar resultados diferentes.

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

Concatenando tensores irregulares

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))
<tf.RaggedTensor [[b'John', b'fell', b'asleep'], [b'a', b'big', b'dog', b'barked'], [b'my', b'cat', b'is', b'fuzzy']]>

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

Concatenación de tensores dispersos

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, ''))
tf.Tensor(
[[b'John' b'' b'' b'fell' b'asleep']
 [b'a' b'big' b'dog' b'barked' b'']
 [b'my' b'cat' b'' b'is' b'fuzzy']], shape=(3, 5), dtype=string)

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 tf.reduce_mean 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).

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 estableciendo ragged=True en tf.keras.Input o tf.keras.layers.InputLayer . Los tensores irregulares también se pueden pasar entre las capas de Keras y los modelos de Keras los devuelven. El siguiente ejemplo muestra un modelo de juguete LSTM 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))
WARNING:tensorflow:Layer lstm will not use cuDNN kernels since it doesn't meet the criteria. It will use a generic GPU kernel as fallback when running on GPU.
WARNING:tensorflow:Please add `keras.layers.InputLayer` instead of `keras.Input` to Sequential model. `keras.Input` is intended to be used by Functional model.
Epoch 1/5
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/framework/indexed_slices.py:449: UserWarning: Converting sparse IndexedSlices(IndexedSlices(indices=Tensor("gradient_tape/sequential/lstm/RaggedToTensor/boolean_mask_1/GatherV2:0", shape=(None,), dtype=int32), values=Tensor("gradient_tape/sequential/lstm/RaggedToTensor/boolean_mask/GatherV2:0", shape=(None, 16), dtype=float32), dense_shape=Tensor("gradient_tape/sequential/lstm/RaggedToTensor/Shape:0", shape=(2,), dtype=int32))) to a dense Tensor of unknown shape. This may consume a large amount of memory.
  "shape. This may consume a large amount of memory." % value)
1/1 [==============================] - 2s 2s/step - loss: 7.7128
Epoch 2/5
1/1 [==============================] - 0s 18ms/step - loss: 7.7125
Epoch 3/5
1/1 [==============================] - 0s 16ms/step - loss: 7.7125
Epoch 4/5
1/1 [==============================] - 0s 17ms/step - loss: 7.7125
Epoch 5/5
1/1 [==============================] - 0s 15ms/step - loss: 7.7125
[[-0.00928741]
 [-0.01342061]
 [-0.00900517]
 [-0.01094673]]

tf.Ejemplo

tf.Example es una codificación protobuf estándar para datos de TensorFlow. Los datos codificados con tf.Example s a menudo incluyen características de longitud variable. Por ejemplo, el siguiente código define un lote de cuatro mensajes de tf.Example con diferentes longitudes de características:

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 mediante tf.io.parse_example , que toma un tensor de cadenas serializadas y un diccionario de especificación de características, y devuelve un diccionario que asigna los nombres de las características a los tensores. Para leer las características de longitud variable en tensores irregulares, simplemente use tf.io.RaggedFeature en el diccionario de especificación de características:

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))
colors=<tf.RaggedTensor [[b'red', b'blue'], [b'orange'], [b'black', b'yellow'], [b'green']]>
lengths=<tf.RaggedTensor [[7], [], [1, 3], [3, 5, 2]]>

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

Conjuntos de datos

tf.data es una API que le permite crear tuberías de entrada complejas a partir de piezas simples y reutilizables. Su estructura de datos central estf.data.Dataset , que representa una secuencia de elementos, en la que cada elemento consta 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))

Construyendo conjuntos de datos con tensores irregulares

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

dataset = tf.data.Dataset.from_tensor_slices(feature_tensors)
print_dictionary_dataset(dataset)
Element 0:
        colors = [b'red' b'blue']
       lengths = [7]
Element 1:
        colors = [b'orange']
       lengths = []
Element 2:
        colors = [b'black' b'yellow']
       lengths = [1 3]
Element 3:
        colors = [b'green']
       lengths = [3 5 2]

Procesamiento por lotes y desagregado de conjuntos de datos con tensores irregulares

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

batched_dataset = dataset.batch(2)
print_dictionary_dataset(batched_dataset)
Element 0:
        colors = <tf.RaggedTensor [[b'red', b'blue'], [b'orange']]>
       lengths = <tf.RaggedTensor [[7], []]>
Element 1:
        colors = <tf.RaggedTensor [[b'black', b'yellow'], [b'green']]>
       lengths = <tf.RaggedTensor [[1, 3], [3, 5, 2]]>

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

unbatched_dataset = batched_dataset.unbatch()
print_dictionary_dataset(unbatched_dataset)
Element 0:
        colors = [b'red' b'blue']
       lengths = [7]
Element 1:
        colors = [b'orange']
       lengths = []
Element 2:
        colors = [b'black' b'yellow']
       lengths = [1 3]
Element 3:
        colors = [b'green']
       lengths = [3 5 2]

Lote de 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 entre 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)
<tf.RaggedTensor [[0], [0, 1, 2, 3, 4]]>
<tf.RaggedTensor [[0, 1, 2], [0, 1]]>
<tf.RaggedTensor [[0, 1, 2, 3, 4, 5, 6, 7]]>

Transformar conjuntos de datos con tensores irregulares

También puede crear o transformar tensores irregulares en Datasets usando 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)
Element 0:
   mean_length = 7
 length_ranges = <tf.RaggedTensor [[0, 1, 2, 3, 4, 5, 6]]>
Element 1:
   mean_length = 0
 length_ranges = <tf.RaggedTensor []>
Element 2:
   mean_length = 2
 length_ranges = <tf.RaggedTensor [[0], [0, 1, 2]]>
Element 3:
   mean_length = 3
 length_ranges = <tf.RaggedTensor [[0, 1, 2], [0, 1, 2, 3, 4], [0, 1]]>

tf.function

tf.function es un decorador que calcula previamente los gráficos de TensorFlow para las funciones de Python, lo que puede mejorar sustancialmente el rendimiento de su código de TensorFlow. Los tensores irregulares se pueden utilizar de forma transparente con las funciones @tf.function con @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)
<tf.Tensor: shape=(3, 4), dtype=int32, numpy=
array([[1, 2, 2, 1],
       [3, 4, 4, 3],
       [5, 6, 6, 5]], dtype=int32)>
make_palindrome(tf.ragged.constant([[1, 2], [3], [4, 5, 6]]), axis=1)
<tf.RaggedTensor [[1, 2, 2, 1], [3, 3], [4, 5, 6, 6, 5, 4]]>

Si desea especificar explícitamente input_signature para tf.function , puede hacerlo usando 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]]))
(<tf.Tensor: shape=(3,), dtype=int32, numpy=array([2, 3, 6], dtype=int32)>,
 <tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 3, 4], dtype=int32)>)

Funciones concretas

Las funciones concretas encapsulan gráficos trazados individuales que son construidos por tf.function . Los tensores irregulares se pueden utilizar de forma transparente 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))
<tf.RaggedTensor [[2, 3], [4], [5, 6, 7]]>

Modelos guardados

Un modelo guardado es un programa de TensorFlow serializado, que incluye tanto los pesos como el cálculo. Puede construirse a partir de un modelo Keras o de un modelo personalizado. En cualquier caso, los tensores irregulares se pueden usar de forma transparente con las funciones y métodos definidos por un modelo guardado.

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)
WARNING:absl:Function `_wrapped_model` contains input name(s) args_0 with unsupported characters which will be renamed to args_0_1 in the SavedModel.
WARNING:tensorflow:FOR KERAS USERS: The object that you are saving contains one or more Keras models or layers. If you are loading the SavedModel with `tf.keras.models.load_model`, continue reading (otherwise, you may ignore the following instructions). Please change your code to save with `tf.keras.models.save_model` or `model.save`, and confirm that the file "keras.metadata" exists in the export directory. In the future, Keras will only load the SavedModels that have this file. In other words, `tf.saved_model.save` will no longer write SavedModels that can be recovered as Keras models (this will apply in TF 2.5).

FOR DEVS: If you are overwriting _tracking_metadata in your class, this property has been used to save metadata in the SavedModel. The metadta field will be deprecated soon, so please move the metadata to a different file.
WARNING:tensorflow:FOR KERAS USERS: The object that you are saving contains one or more Keras models or layers. If you are loading the SavedModel with `tf.keras.models.load_model`, continue reading (otherwise, you may ignore the following instructions). Please change your code to save with `tf.keras.models.save_model` or `model.save`, and confirm that the file "keras.metadata" exists in the export directory. In the future, Keras will only load the SavedModels that have this file. In other words, `tf.saved_model.save` will no longer write SavedModels that can be recovered as Keras models (this will apply in TF 2.5).

FOR DEVS: If you are overwriting _tracking_metadata in your class, this property has been used to save metadata in the SavedModel. The metadta field will be deprecated soon, so please move the metadata to a different file.
INFO:tensorflow:Assets written to: /tmp/tmp087u1gje/assets
INFO:tensorflow:Assets written to: /tmp/tmp087u1gje/assets
<tf.Tensor: shape=(4, 1), dtype=float32, numpy=
array([[-0.00928741],
       [-0.01342061],
       [-0.00900517],
       [-0.01094673]], dtype=float32)>

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]]))
INFO:tensorflow:Assets written to: /tmp/tmpm2n_d_rz/assets
INFO:tensorflow:Assets written to: /tmp/tmpm2n_d_rz/assets
<tf.RaggedTensor [[100.0, 400.0, 300.0], [200.0]]>

Operadores sobrecargados

La clase RaggedTensor sobrecarga los operadores aritméticos y de comparación estándar de Python, lo que facilita la realización de 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)
<tf.RaggedTensor [[2, 3], [5], [7, 8, 9]]>

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

x = tf.ragged.constant([[1, 2], [3], [4, 5, 6]])
print(x + 3)
<tf.RaggedTensor [[4, 5], [6], [7, 8, 9]]>

Para una discusión de casos más avanzados, consulte la sección sobre retransmisiones .

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 el corte multidimensionales. Los siguientes ejemplos demuestran la indexación de tensor irregular con un tensor irregular 2D y 3D.

Ejemplos de indexación: tensor desigual 2D

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

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)
<tf.RaggedTensor [[5], [], [6]]>
print(rt[3, 0])                     # First element of fourth row (1D Tensor)
tf.Tensor([8 9], shape=(2,), dtype=int32)
print(rt[:, 1:3])                   # Items 1-3 of each row (3D RaggedTensor)
<tf.RaggedTensor [[[4]], [[], [6]], [], [[10]]]>
print(rt[:, -1:])                   # Last item of each row (3D RaggedTensor)
<tf.RaggedTensor [[[4]], [[6]], [[7]], [[10]]]>

RaggedTensor s admite la indexación multidimensional y la división 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 es obvio si debe (1) generar un IndexError ; (2) use un valor predeterminado; o (3) omita ese valor y devuelva un tensor con menos filas de las que tenía al comienzo. Siguiendo los principios rectores de Python ("Ante la ambigüedad, rechace la tentación de adivinar"), esta operación actualmente no está permitida.

Conversión de tipo de tensor

La clase RaggedTensor define métodos que se pueden usar para convertir entre RaggedTensor sy tf.Tensor 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]))
tf.Tensor(
[[b'Hi' b'' b'' b'' b'' b'' b'' b'' b'' b'']
 [b'Welcome' b'to' b'the' b'fair' b'' b'' b'' b'' b'' b'']
 [b'Have' b'fun' b'' b'' b'' b'' b'' b'' b'' b'']], shape=(3, 10), dtype=string)
# Tensor -> RaggedTensor
x = [[1, 3, -1, -1], [2, -1, -1, -1], [4, 5, 8, 9]]
print(tf.RaggedTensor.from_tensor(x, padding=-1))
<tf.RaggedTensor [[1, 3], [2], [4, 5, 8, 9]]>
#RaggedTensor -> SparseTensor
print(ragged_sentences.to_sparse())
SparseTensor(indices=tf.Tensor(
[[0 0]
 [1 0]
 [1 1]
 [1 2]
 [1 3]
 [2 0]
 [2 1]], shape=(7, 2), dtype=int64), values=tf.Tensor([b'Hi' b'Welcome' b'to' b'the' b'fair' b'Have' b'fun'], shape=(7,), dtype=string), dense_shape=tf.Tensor([3 4], shape=(2,), dtype=int64))
# 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))
<tf.RaggedTensor [[b'a'], [], [b'b', b'c']]>

Evaluación de tensores irregulares

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

  1. Utilice tf.RaggedTensor.to_list para convertir el tensor irregular en una lista de Python anidada.
  2. Utilice tf.RaggedTensor.numpy para convertir el tensor irregular en una matriz NumPy cuyos valores son matrices NumPy anidadas.
  3. Descomponga el tensor desigual en sus componentes, utilizando las propiedades tf.RaggedTensor.values y tf.RaggedTensor.row_splits , o métodos de tf.RaggedTensor.row_lengths como tf.RaggedTensor.row_lengths y tf.RaggedTensor.value_rowids .
  4. Utilice la indexación de Python para seleccionar 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())
Python list: [[1, 2], [3, 4, 5], [6], [], [7]]
NumPy array: [array([1, 2], dtype=int32) array([3, 4, 5], dtype=int32)
 array([6], dtype=int32) array([], dtype=int32) array([7], dtype=int32)]
Values: [1 2 3 4 5 6 7]
Splits: [0 2 5 6 6 7]
Indexed value: [3 4 5]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/ragged/ragged_tensor.py:2053: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray
  return np.array(rows)

Radiodifusión

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

Los pasos básicos para la difusión de dos entradas x y y tener formas compatibles son:

  1. Si x y y no tienen el mismo número de dimensiones, a continuación, añadir dimensiones exteriores (con tamaño 1) hasta que lo hagan.

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

  • Si x o y tienen un tamaño de 1 en la dimensión d , y luego repetir sus valores a través de dimensión d para que coincida con el tamaño de la otra entrada.
  • De lo contrario, lanzar una excepción ( x , y y no se transmiten compatibles).

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

Ejemplos de retransmisiones

# 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)
<tf.RaggedTensor [[4, 5], [6]]>
# 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)
<tf.RaggedTensor [[1010, 1087, 1012], [2019, 2053], [3012, 3032]]>
# 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)
<tf.RaggedTensor [[[11, 12], [13, 14], [15, 16]], [[17, 18]]]>
# 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)
<tf.RaggedTensor [[[[11, 21, 31], [12, 22, 32]], [], [[13, 23, 33]], [[14, 24, 34]]], [[[15, 25, 35], [16, 26, 36]], [[17, 27, 37]]]]>

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)
Expected 'tf.Tensor(False, shape=(), dtype=bool)' to be true. Summarized data: b'Unable to broadcast: dimension size mismatch in dimension'
1
b'lengths='
4
b'dim_size='
2, 4, 1
# 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)
Expected 'tf.Tensor(False, shape=(), dtype=bool)' to be true. Summarized data: b'Unable to broadcast: dimension size mismatch in dimension'
1
b'lengths='
2, 2, 1
b'dim_size='
3, 1, 2
# 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)
Expected 'tf.Tensor(False, shape=(), dtype=bool)' to be true. Summarized data: b'Unable to broadcast: dimension size mismatch in dimension'
2
b'lengths='
3, 3, 3, 3, 3
b'dim_size='
2, 2, 2, 2, 2

Codificación RaggedTensor

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

  • Un tensor de values , que concatena las filas de longitud variable en una lista plana.
  • Una row_partition , que indica cómo esos valores aplanados se dividen en filas.

Codificación RaggedTensor

La row_partition se puede almacenar usando 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 sola longitud para todas las filas.

codificaciones row_partition

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 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)
<tf.RaggedTensor [[3, 1, 4, 1], [], [5, 9], [2]]>

La elección de qué codificación utilizar para las particiones de fila 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 y el corte en tiempo constante en tensores irregulares.
  • Concatenación eficiente : la codificación row_lengths es más eficiente cuando se concatenan 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 utilizado por las operaciones, como tf.segment_sum . El esquema row_limits coincide con el formato utilizado por operaciones como tf.sequence_mask .
  • Dimensiones uniformes : como se explica a continuación, la codificación uniform_row_length se utiliza para codificar tensores irregulares con dimensiones uniformes.

Múltiples dimensiones irregulares

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

Codificación de un tensor irregular con múltiples dimensiones irregulares (rango 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))
<tf.RaggedTensor [[[10, 11, 12]], [], [[], [13, 14], [15, 16, 17, 18], [19]]]>
Shape: (3, None, None)
Number of partitioned dimensions: 2

La función de fábrica tf.RaggedTensor.from_nested_row_splits puede usarse para construir un RaggedTensor con múltiples dimensiones irregulares directamente proporcionando 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)
<tf.RaggedTensor [[[10, 11, 12]], [], [[], [13, 14], [15, 16, 17, 18], [19]]]>

Rango irregular y valores planos

El rango irregular de un tensor irregular es el número de veces que se ha dividido el tensor de values subyacentes (es decir, la profundidad de RaggedTensor objetos RaggedTensor ). El tensor de values más interno se conoce como flat_values . En el siguiente ejemplo, conversations tiene ragged_rank = 3, y su flat_values es un Tensor 1D con 24 cadenas:

# 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
TensorShape([2, None, None, None])
assert conversations.ragged_rank == len(conversations.nested_row_splits)
conversations.ragged_rank  # Number of partitioned dimensions.
3
conversations.flat_values.numpy()
array([b'I', b'like', b'ragged', b'tensors.', b'Oh', b'yeah?', b'What',
       b'can', b'you', b'use', b'them', b'for?', b'Processing',
       b'variable', b'length', b'data!', b'I', b'like', b'cheese.', b'Do',
       b'you?', b'Yes.', b'I', b'do.'], dtype=object)

Dimensiones interiores uniformes

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

Codificación de tensores irregulares con dimensiones internas uniformes

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))
<tf.RaggedTensor [[[1, 3], [0, 0], [1, 3]], [[5, 3]], [[3, 3], [1, 2]]]>
Shape: (3, None, 2)
Number of partitioned dimensions: 1
Flat values shape: (6, 2)
Flat values:
[[1 3]
 [0 0]
 [1 3]
 [5 3]
 [3 3]
 [1 2]]

Dimensiones no interiores uniformes

Los tensores irregulares con dimensiones no internas uniformes se codifican dividiendo filas con uniform_row_length .

Codificación de tensores irregulares con dimensiones no internas uniformes

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))
<tf.RaggedTensor [[[10, 11, 12], [13, 14]], [[15, 16, 17, 18], [19]]]>
Shape: (2, 2, None)
Number of partitioned dimensions: 2