Помогают защитить Большой Барьерный Риф с TensorFlow на Kaggle Присоединяйтесь вызов

Рваные тензоры

Посмотреть на TensorFlow.org Запускаем в Google Colab Посмотреть исходный код на GitHubСкачать блокнот

Документация по API: tf.RaggedTensor tf.ragged

Настраивать

import math
import tensorflow as tf

Обзор

Ваши данные бывают разных форм; ваши тензоры тоже должны. Рваные тензоры являются TensorFlow эквивалентом вложенных списков переменной длины. Они позволяют легко хранить и обрабатывать данные неоднородной формы, в том числе:

  • Функции переменной длины, например набор актеров в фильме.
  • Пакеты последовательных входов переменной длины, например предложения или видеоклипы.
  • Иерархические входные данные, например текстовые документы, разделенные на разделы, абзацы, предложения и слова.
  • Отдельные поля в структурированных входных данных, например буферы протокола.

Что можно сделать с рваным тензором

Рваные тензоры поддерживаются более ста TensorFlow операций, в том числе и математических операций (например, tf.add и tf.reduce_mean ), операций с массивами (например, tf.concat и tf.tile ), обработки строк OPS (таких как tf.substr ), контроль операций (например, поток tf.while_loop и tf.map_fn ), и многие другие:

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], []]>

Существует также ряд методов и операций, специфичных для рваных тензоров, включая фабричные методы, методы преобразования и операции отображения значений. Для получения списка поддерживаемых опс см tf.ragged документации пакета.

Рваные тензоры поддерживаются многим API , TensorFlow, в том числе Keras , Datasets , tf.function , SavedModels и tf.Example . Для получения дополнительной информации обратитесь в раздел TensorFlow API , ниже.

Как и в случае с обычными тензорами, вы можете использовать индексацию в стиле Python для доступа к определенным фрагментам рваного тензора. Для получения более подробной информации обратитесь к разделу Индексация ниже.

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], []]>

И, как и обычные тензоры, вы можете использовать арифметические операции и операторы сравнения Python для выполнения поэлементных операций. Для получения более подробной информации, обратитесь к разделу на перегруженных операторов ниже.

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], []]>

Если вам необходимо выполнить преобразование поэлементного к ценностям RaggedTensor , вы можете использовать tf.ragged.map_flat_values , который принимает функцию плюс один или несколько аргументов, и применяет функцию преобразования RaggedTensor значения «s.

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], []]>

Рваные тензоры могут быть преобразованы в вложенный Python list s и NumPy array s:

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:2063: 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)

Построение рваного тензора

Самый простой способ построить рваный тензор использует tf.ragged.constant , который строит RaggedTensor , соответствующим данный вложенный Python list или NumPy array :

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']]]>

Рваные тензоры также могут быть построены путем сопряжения плоских значений тензоров с строками разбиения тензоров с указанием того, как эти значения должны быть разделены на строки, используя заводские методы класса , такие как tf.RaggedTensor.from_value_rowids , tf.RaggedTensor.from_row_lengths и tf.RaggedTensor.from_row_splits .

tf.RaggedTensor.from_value_rowids

Если вы знаете , какая строка каждое значение принадлежит, то вы можете построить RaggedTensor с помощью value_rowids строки-разбиения тензором:

value_rowids тензор разделения строк

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

Если вы знаете , как долго каждая строка, то вы можете использовать row_lengths тензор строк разбиения:

row_lengths тензор разделения строк

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

Если вы знаете , индекс , в котором каждая строка начинается и заканчивается, то вы можете использовать row_splits тензор строк разбиения:

row_splits тензор разделения строк

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]]>

См tf.RaggedTensor документации класса для полного списка фабричных методов.

Что можно хранить в рваном тензоре

Как и с нормальной Tensor с, значение в RaggedTensor все должен иметь тот же тип; и значения должны быть все в одной и той же глубины вложенности (ранг тензора):

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

Пример использования

Следующий пример демонстрирует , как RaggedTensor ы могут быть использованы для построения и объединить Юниграмма и биграммы вложения для выпечки запросов переменной длины, используя специальные маркеры для начала и конца каждого предложения. Для получения более подробной информации о опс , используемых в данном примере, проверьте 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.14285272  0.02908629 -0.16327512 -0.14529026]
 [-0.4479212  -0.35615516  0.17110227  0.2522229 ]
 [-0.1987868  -0.13152348 -0.0325102   0.02125177]], shape=(3, 4), dtype=float32)

Пример рваного тензора

Рваные и однородные размеры

Рваное измерение является измерением которого ломтики могут иметь различную длину. Например, внутренняя (колонка) размерность rt=[[3, 1, 4, 1], [], [5, 9, 2], [6], []] является рваным, так как ломтики колонки ( rt[0, :] , ..., rt[4, :] ) имеют разную длину. Размеры , чьи ломтики все имеют одинаковую длину, называются одинаковые размеры.

Наружное измерение рваного тензора всегда однородно, поскольку оно состоит из одного среза (и, следовательно, нет возможности отличаться по длине среза). Остальные размеры могут быть как рваными, так и однородными. Например, вы можете хранить слово вложения для каждого слова в пакете предложений , используя рваный тензор с формы [num_sentences, (num_words), embedding_size] , где круглые скобки (num_words) указывают , что измерение рваный.

Вложения слов с использованием рваного тензора

Рваные тензоры могут иметь несколько рваных размеров. Например, вы могли бы хранить партию структурированных текстовых документов с помощью тензора с формой [num_documents, (num_paragraphs), (num_sentences), (num_words)] (где снова скобки используются для обозначения оборванных размеров).

Как и в случае tf.Tensor , ранг рваного тензора является его общее числом измерений ( в том числе как рваный и одинакового размера). Потенциально рваный тензор является значением , которое может быть либо tf.Tensor или tf.RaggedTensor .

При описании формы RaggedTensor неровные размеры обычно обозначаются заключением их в круглые скобки. Например, как вы видели выше, форма 3D RaggedTensor , которая хранит слово вложения для каждого слова в пакете предложений можно записать в виде [num_sentences, (num_words), embedding_size] .

RaggedTensor.shape атрибут возвращает tf.TensorShape для тензора рваного , где рваных размеры не имеют размер None :

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

Метод tf.RaggedTensor.bounding_shape может быть использован , чтобы найти плотную форму ограничительной для данного RaggedTensor :

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

Рваные vs редкие

Рваном тензор не следует рассматривать как тип разреженного тензора. В частности, разреженные тензоры являются эффективными для кодирования tf.Tensor , что модель одни и те же данные в компактном формате; но рваный тензор является расширением tf.Tensor , что модели расширенного класса данных. Это различие имеет решающее значение при определении операций:

  • Применение операции к разреженному или плотному тензору всегда должно давать один и тот же результат.
  • Применение операции к рваному или разреженному тензору может дать разные результаты.

В качестве иллюстративного примера, рассмотрим , как операции , такие как массив concat , stack , и tile определены для рваных против разреженных тензоров. Конкатенация рваных тензоров объединяет каждую строку в одну строку с комбинированной длиной:

Соединение рваных тензоров

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']]>

Однако объединение разреженных тензоров эквивалентно объединению соответствующих плотных тензоров, как показано в следующем примере (где Ø указывает на пропущенные значения):

Конкатенация разреженных тензоров

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)

В качестве другого примера того , почему это различие важно, рассмотрит определение «среднего значения каждой строки» для оп , таких как tf.reduce_mean . Для рваного тензора среднее значение для строки - это сумма значений строки, деленная на ширину строки. Но для разреженного тензора среднее значение для строки - это сумма значений строки, деленная на общую ширину разреженного тензора (которая больше или равна ширине самой длинной строки).

API-интерфейсы TensorFlow

Керас

tf.keras имеет высокий уровень API TensorFlow для построения и обучения глубоко обучения модели. Рваные тензоры могут быть переданы в качестве входных данных для модели Keras, установив ragged=True на tf.keras.Input или tf.keras.layers.InputLayer . Рваные тензоры также могут передаваться между слоями Keras и возвращаться моделями Keras. В следующем примере показана игрушечная модель LSTM, которая обучается с использованием рваных тензоров.

# 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.
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: 3.1269
Epoch 2/5
1/1 [==============================] - 0s 18ms/step - loss: 2.1197
Epoch 3/5
1/1 [==============================] - 0s 19ms/step - loss: 2.0196
Epoch 4/5
1/1 [==============================] - 0s 20ms/step - loss: 1.9371
Epoch 5/5
1/1 [==============================] - 0s 18ms/step - loss: 1.8857
[[0.02800461]
 [0.00945962]
 [0.02283431]
 [0.00252927]]

tf.Example

tf.Example является стандартной Protobuf кодирования данных TensorFlow. Данные , закодированные с помощью tf.Example часто включает в себя функции переменной длины сек. Например, следующий код определяет партию из четырех tf.Example сообщений с различными длинами удобства:

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]} } } }''')]

Вы можете разобрать эти кодированные данные с помощью tf.io.parse_example , который принимает тензор сериализовать строки и словаря спецификации функции и возвращает словарь имена элементы отображения на тензоры. Для чтения функции переменной длины в рваные тензоры, вы просто используете tf.io.RaggedFeature в словаре спецификации особенности:

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 также может быть использован для чтения функций с несколькими неровных размерами. Для получения дополнительной информации обратитесь к документации по API .

Наборы данных

tf.data является API , который позволяет создавать сложные входные трубопроводы из простых, повторно используемых фрагментов. Его основная структура данных tf.data.Dataset , которая представляет собой последовательность элементов, в которой каждый элемент состоит из одного или нескольких компонентов.

# 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))

Построение наборов данных с помощью рваных тензоров

Datasets может быть построен из рваных тензоров , используя те же методы, которые используются для построения их tf.Tensor s или NumPy array s, таких как 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]

Пакетирование и отключение наборов данных с рваными тензорами

Наборы данных с неровными тензорами можно порционными (который сочетает в себе п последовательных элементов в единые элементы) с использованием 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]]>

И наоборот, порционный набор данных может быть преобразован в плоский набор данных с использованием 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]

Пакетирование наборов данных с необработанными тензорами переменной длины

Если у Вас есть Dataset , который содержит не-рваные тензоры и тензорные длины различаются между элементами, то вы можете партию эти не-рваные тензоры в рваные тензоры, применяя 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]]>

Преобразование наборов данных с помощью рваных тензоров

Вы также можете создавать или трансформировать рваные тензоры в Datasets с помощью 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 является декоратором , который предварительно вычисляет TensorFlow графики для функций Python, которые могут существенно улучшить производительность вашего кода TensorFlow. Рваные тензоры могут быть использованы прозрачно @tf.function -decorated функции. Например, следующая функция работает как с рваными, так и с неровными тензорами:

@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)
2021-09-22 20:36:51.018367: W tensorflow/core/grappler/optimizers/loop_optimizer.cc:907] Skipping loop optimization for Merge node with control input: RaggedConcat/assert_equal_1/Assert/AssertGuard/branch_executed/_9
<tf.RaggedTensor [[1, 2, 2, 1], [3, 3], [4, 5, 6, 6, 5, 4]]>

Если вы хотите , чтобы явно указать input_signature для tf.function , то вы можете сделать это с помощью 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)>)

Конкретные функции

Конкретные функции инкапсулировать отдельные прорисованные графики, которые построены по tf.function . Рваные тензоры можно прозрачно использовать с конкретными функциями.

@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]]>

Сохраненные модели

SavedModel сериализованной программа TensorFlow, включая и весы и вычисления. Он может быть построен из модели Keras или из нестандартной модели. В любом случае рваные тензоры можно прозрачно использовать с функциями и методами, определенными SavedModel.

Пример: сохранение модели 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)
2021-09-22 20:36:52.069689: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
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.
INFO:tensorflow:Assets written to: /tmp/tmp114axtt7/assets
INFO:tensorflow:Assets written to: /tmp/tmp114axtt7/assets
<tf.Tensor: shape=(4, 1), dtype=float32, numpy=
array([[0.02800461],
       [0.00945962],
       [0.02283431],
       [0.00252927]], dtype=float32)>

Пример: сохранение пользовательской модели

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

Перегруженные операторы

В RaggedTensor класс перегружает стандартный Python арифметических и операторы сравнения, что делает его легко выполнять основные поэлементной математики:

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]]>

Поскольку перегруженные операторы выполняют поэлементные вычисления, входные данные для всех двоичных операций должны иметь одинаковую форму или передаваться в одну и ту же форму. В простейшем случае широковещательной рассылки один скаляр поэлементно комбинируется с каждым значением в рваном тензоре:

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

Для обсуждения более сложных случаев, проверьте раздел вещания.

Рваные тензоры перегружать тот же набор операторов , как обычно Tensor с: унарные операторы - , ~ , и abs() ; и бинарные операторы + , - , * , / , // , % , ** , & , | , ^ , == , < , <= , > и >= .

Индексирование

Рваные тензоры поддерживают индексацию в стиле Python, включая многомерное индексирование и срезы. Следующие примеры демонстрируют рваную тензорную индексацию с двухмерным и трехмерным рваным тензором.

Примеры индексации: 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']]>

Примеры индексации: 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 поддержки сек многомерная индексация и нарезка с одним ограничением: индексация в рваное измерение не допускаются. Этот случай проблематичен, потому что указанное значение может существовать в некоторых строках, но не в других. В таких случаях, это не очевидно , должны ли вы (1) поднять IndexError ; (2) используйте значение по умолчанию; или (3) пропустить это значение и вернуть тензор с меньшим количеством строк, чем вы начали. После руководящих принципов Python ( «В условиях неопределенности, отказаться от соблазна угадать»), эта операция в настоящее время запрещена.

Преобразование тензорного типа

В RaggedTensor методы определяет класс , который может быть использован для преобразования между RaggedTensor с и 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']]>

Оценка рваных тензоров

Чтобы получить доступ к значениям в рваном тензоре, вы можете:

  1. Использование tf.RaggedTensor.to_list для преобразования оборванного тензора в вложен список Python.
  2. Использование tf.RaggedTensor.numpy для преобразования оборванного тензора в массив NumPy, значение которых вложены Numpy массивы.
  3. Разложить рваную тензор на его компоненты, используя tf.RaggedTensor.values и tf.RaggedTensor.row_splits свойства или строки-paritioning методы , такие как tf.RaggedTensor.row_lengths и tf.RaggedTensor.value_rowids .
  4. Используйте индексирование Python для выбора значений из рваного тензора.
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:2063: 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)

Вещание

Широковещательная рассылка - это процесс приведения тензоров различной формы к совместимым формам для поэлементных операций. Для получения дополнительной информации о вещании см .:

Основные шаги для вещания двух входов x и y , чтобы иметь совместимые формы являются:

  1. Если x и y не имеют одинаковое число измерений, а затем добавить наружные размеры (с размером 1) , пока они не делают.

  2. Для каждого измерения , где x и y имеют разные размеры:

  • Если x или y имеют размер 1 в размерности d , а затем повторить его значения по размерности d , чтобы соответствовать размеру другой входной в.
  • В противном случае, сгенерирует исключение ( x и y не транслируется совместим).

Где размер тензора в однородном измерении - это одно число (размер срезов в этом измерении); а размер тензора в рваном измерении - это список длин срезов (для всех срезов в этом измерении).

Примеры трансляций

# 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]]]]>

Вот несколько примеров фигур, которые не транслируются:

# 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

Кодировка RaggedTensor

Рваные тензоры кодируются с использованием RaggedTensor класса. Внутри каждого RaggedTensor состоит из:

  • values тензор, который сцепляет строки переменной длины в уплощенный список.
  • row_partition , что указывает на то, каким образом эти уплощенные значения разделяются на строки.

Кодировка RaggedTensor

row_partition могут быть сохранены с помощью четырех различных кодировок:

  • row_splits представляет собой целое число вектора с указанием точки разделения между рядами.
  • value_rowids представляет собой целое число вектор , описывающий индекс строки для каждого значения.
  • row_lengths представляет собой целое число вектор , задающий длину каждой строки.
  • uniform_row_length представляет собой целое число скаляр , описывающий одну длину для всех строк.

кодировки row_partition

Целое число скалярные nrows также может быть включено в row_partition кодирования для учета пустых хвостовых строк с value_rowids или пустыми строками с 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]]>

Выбор кодировки для использования в разделах строк осуществляется внутренне с помощью рваных тензоров для повышения эффективности в некоторых контекстах. В частности, некоторые из преимуществ и недостатков различных схем разделения строк:

  • Эффективная индексация: row_splits кодирование обеспечивает постоянное время индексацию и нарезку в рваные тензоры.
  • Эффективная конкатенация: В row_lengths кодирующий является более эффективным , когда конкатенациями рваных тензоров, так как длина строки не изменяется , когда два тензора соединяется вместе.
  • Малый размер кодирования: В value_rowids кодирующий является более эффективным при хранении рваных тензоров , которые имеют большое количество пустых строк, так как размер тензора зависит только от общего числа значений. С другой стороны, row_splits и row_lengths кодирование является более эффективными при хранении рваных тензоров с более длинными строками, так как они требуют только одно скалярного значения для каждой строки.
  • Совместимость: value_rowids схема соответствует сегментации формата , используемого операциями, такими как tf.segment_sum . row_limits схема соответствует формату используемой ОПС , таких как tf.sequence_mask .
  • Унифицированные размеры: Как описано ниже, uniform_row_length кодирования используется для кодирования рваных тензоров с однородными размерами.

Множественные рваные размеры

Рваный тензор с несколькими рваных размерами кодируются с помощью вложенной RaggedTensor для values тензора. Каждая вложенная RaggedTensor добавляет одну рваное измерение.

Кодирование рваного тензора с множеством рваных размерностей (ранг 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

Функция завода tf.RaggedTensor.from_nested_row_splits может быть использован для построения RaggedTensor с несколькими рваных размерами непосредственно, предоставляя список 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]]]>

Рваные ранги и плоские значения

Оборванный ранг рваных тензорные является количеством раз, лежащего в основе values тензора был секционированное (т.е. глубины вложенности RaggedTensor объектов). Самые внутренние values тензора известен как его flat_values. В следующем примере, conversations имеют ragged_rank = 3, и его flat_values является 1D Tensor с 24 строками:

# 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)

Единые внутренние размеры

Рваные тензоры с равномерными внутренними размерами кодируются с использованием многомерного tf.Tensor для flat_values (то есть, самые внутренние values ).

Кодирование рваных тензоров с одинаковыми внутренними размерами

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]]

Однородные невнутренние размеры

Рваные тензоры с равномерными без внутренних размеров кодируются путем разделения строк с uniform_row_length .

Кодирование рваных тензоров с однородными невнутренними размерами

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