在 TensorFlow.org 上查看 |
在 Google Colab 中运行 |
在 Github 上查看源代码 |
下载笔记本 |
API 文档: tf.RaggedTensor tf.ragged
设置
!pip install --pre -U tensorflow
import math
import tensorflow as tf
2022-12-14 22:26:04.392854: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory 2022-12-14 22:26:04.392948: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory 2022-12-14 22:26:04.392958: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.
概述
数据有多种形状;张量也应当有多种形状。不规则张量是嵌套的可变长度列表的 TensorFlow 等效项。它们使存储和处理包含非均匀形状的数据变得容易,包括:
- 可变长度特征,例如电影的演员名单。
- 成批的可变长度顺序输入,例如句子或视频剪辑。
- 分层输入,例如细分为节、段落、句子和单词的文本文档。
- 结构化输入中的各个字段,例如协议缓冲区。
不规则张量的功能
超过一百种 TensorFlow 运算支持不规则张量,包括数学运算(如 tf.add 和 tf.reduce_mean)、数组运算(如 tf.concat 和 tf.tile)、字符串操作运算(如 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 包文档。
许多 TensorFlow API 都支持不规则张量,包括 Keras、Dataset、tf.function、SavedModel 和 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 的值。
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 和 NumPy array:
digits.to_list()
[[3, 1, 4, 1], [], [5, 9, 2], [6], []]
digits.numpy()
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,它会构建与给定的嵌套 Python list 或 NumPy array 相对应的 RaggedTensor:
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
如果您知道每个值属于哪一行,可以使用 value_rowids 行分区张量构建 RaggedTensor:

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 行分区张量:

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 行分区张量:

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 类文档。
注:默认情况下,这些工厂方法会添加断言,说明行分区张量结构良好且与值数量保持一致。如果您能够保证输入的结构良好且一致,可以使用 validate=False 参数跳过此类检查。
可以在不规则张量中存储什么
与普通 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.02510674 -0.04737939 0.01799836 0.1717977 ] [ 0.4977201 0.38424173 -0.40286708 -0.39982286] [ 0.16051094 0.2062596 -0.05239218 0.10338864]], 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 的形状时,按照惯例,不规则维度会通过括号进行指示。例如,如上面所见,存储一批句子中每个单词的单词嵌入向量的三维 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)
不规则张量和稀疏张量对比
不规则张量不应该被认为是一种稀疏张量。尤其是,稀疏张量是以紧凑的格式对相同数据建模的 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)的“每行平均值”的定义。对于不规则张量,一行的平均值是该行的值总和除以该行的宽度。但对于稀疏张量来说,一行的平均值是该行的值总和除以稀疏张量的总宽度(大于等于最长行的宽度)。
TensorFlow API
Keras
tf.keras 是 TensorFlow 的高级 API,用于构建和训练深度学习模型。通过在 tf.keras.Input 或 tf.keras.layers.InputLayer 上设置 ragged=True,不规则张量可以作为输入传送到 Keras 模型。不规则张量还可以在 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.
在 TensorFlow.org 上查看
在 Google Colab 中运行
在 Github 上查看源代码
下载笔记本