Path: blob/master/site/pt-br/guide/ragged_tensor.ipynb
25115 views
Copyright 2018 The TensorFlow Authors.
Tensores irregulares
Documentação da API: tf.RaggedTensor
tf.ragged
Configuração
Visão geral
Seus dados têm vários formatos diferentes: os tensores devem ter também. Os tensores irregulares (ragged tensors) são o equivalente do TensorFlow às listas aninhadas de comprimento variável. Eles facilitam o armazenamento e o processamento de dados com formatos não uniformes, incluindo:
Características de comprimento variável, como o elenco de atores em um filme.
Lotes de entradas sequenciais de comprimento variável, como frases ou videoclipes.
Entradas hierárquicas, como documentos de texto subdivididos em seções, parágrafos, frases e palavras.
Campos individuais em entradas estruturadas, como buffers de protocolo.
O que você pode fazer com um tensor irregular
Os tensores irregulares são compatíveis com mais de cem operações do TensorFlow, incluindo operações matemáticas (como tf.add
e tf.reduce_mean
), de arrays (como tf.concat
e tf.tile
), de manipulação de strings (como tf.strings.substr
), de fluxo de controle (como tf.while_loop
e tf.map_fn
) e várias outras:
Também há diversos métodos e operações específicos para tensores irregulares, incluindo factory methods, métodos de conversão e operações de mapeamento de valores. Para uma lista de ops compatíveis, veja a documentação do pacote tf.ragged
.
Os tensores irregulares são compatíveis com várias APIs TensorFlow, incluindo Keras, Datasets, tf.function, SavedModels e tf.Example. Para mais informações, confira a seção sobre APIs TensorFlow abaixo.
Assim como em tensores normais, você pode usar a indexação de estilo Python para acessar fatias específicas de um tensor irregular. Para mais informações, consulte a seção Indexação abaixo.
Além disso, exatamente como tensores normais, você pode usar operadores aritméticos e de comparação em Python para realizar operações elemento a elemento. Para mais informações, clique na seção Operadores sobrecarregados abaixo.
Se você precisa realizar uma transformação elemento a elemento para os valores de um RaggedTensor
, pode usar tf.ragged.map_flat_values
, que pega uma função de um ou mais argumentos e a aplica para transformar os valores do RaggedTensor
.
Os tensores irregulares podem ser convertidos em list
s aninhadas do Python e array
s do NumPy:
Construindo um tensor irregular
A maneira mais simples de construir um tensor irregular é usar tf.ragged.constant
, que cria o RaggedTensor
correspondente a uma determinada list
aninhada do Python ou array
do NumPy:
Os tensores irregulares também podem ser criados ao combinar tensores de valores flat com tensores de particionamento de linhas que indicam como esses valores devem ser divididos em linhas, usando factory classmethods como tf.RaggedTensor.from_value_rowids
, tf.RaggedTensor.from_row_lengths
e tf.RaggedTensor.from_row_splits
.
tf.RaggedTensor.from_value_rowids
Se você sabe a qual linha cada valor pertence, então pode criar um RaggedTensor
usando um tensor de particionamento de linhas value_rowids
:
tf.RaggedTensor.from_row_lengths
Se você sabe o comprimento de cada linha, então pode usar um tensor de particionamento de linhas row_lengths
:
tf.RaggedTensor.from_row_splits
Se você sabe o índice em que cada linha começa e termina, então pode usar um tensor de particionamento de linhas row_splits
:
Veja uma lista completa de factory methods na documentação da classe tf.RaggedTensor
.
Observação: por padrão, esses factory methods adicionam asserções de que o tensor de partição de linhas está bem formado e consistente com o número de valores. O parâmetro validate=False
pode ser usado para pular essas verificações se você conseguir garantir que as entradas estão bem formadas e consistentes.
O que você pode armazenar em um tensor irregular
Assim como em Tensor
s normais, todos os valores em um RaggedTensor
precisam ter o mesmo tipo e, além disso, estar na mesma profundidade de aninhamento (o posto do tensor):
Exemplo de caso de uso
O exemplo a seguir demonstra como RaggedTensor
s podem ser usados para construir e combinar embeddings de unigramas e bigramas para um lote de consultas de comprimento variável, usando marcadores especiais para o início e o final de cada frase. Para mais detalhes sobre as ops usadas neste exemplo, confira a documentação do pacote tf.ragged
.
Dimensões irregulares e uniformes
Uma dimensão irregular é uma dimensão com fatias de comprimentos diferentes. Por exemplo, a dimensão interna (coluna) de rt=[[3, 1, 4, 1], [], [5, 9, 2], [6], []]
é irregular, já que as fatias da coluna (rt[0, :]
, ..., rt[4, :]
) têm comprimentos diferentes. As dimensões com fatias de mesmo tamanho são chamadas de uniformes.
A dimensão mais externa de um tensor irregular é sempre uniforme, já que consiste em uma única fatia (portanto, não há possibilidade de diferenciar comprimentos de fatias). As dimensões restantes podem ser irregulares ou uniformes. Por exemplo, você pode armazenar embeddings de palavras para cada palavra de um lote de frases usando um tensor irregular com o formato [num_sentences, (num_words), embedding_size]
, em que os parênteses em volta de (num_words)
indicam que a dimensão é irregular.
Os tensores irregulares podem ter várias dimensões irregulares. Por exemplo, você pode armazenar um lote de documentos de texto estruturados usando um tensor com formato [num_documents, (num_paragraphs), (num_sentences), (num_words)]
(novamente, os parênteses são usados para indicar dimensões irregulares).
Como no tf.Tensor
, o posto de um tensor irregular é o número total de dimensões (incluindo irregulares e uniformes). Um tensor possivelmente irregular é um valor que pode ser um tf.Tensor
ou tf.RaggedTensor
.
Ao descrever o formato de um RaggedTensor, as dimensões irregulares são convencionalmente indicadas ao serem colocadas entre parênteses. Por exemplo, como você viu acima, o formato de um RaggedTensor 3D que armazena embeddings de palavras para cada palavra em um lote de frases pode ser escrito como [num_sentences, (num_words), embedding_size]
.
O atributo RaggedTensor.shape
retorna um tf.TensorShape
para um tensor irregular em que as dimensões têm o tamanho None
:
O método tf.RaggedTensor.bounding_shape
pode ser usado para encontrar um formato delimitador apertado para um determinado RaggedTensor
:
Irregular x esparso
Um tensor irregular não deve ser considerado como um tipo de tensor esparso. Particularmente, os tensores esparsos são codificações eficientes para tf.Tensor
que modelam os mesmos dados em um formato compacto, enquanto os tensores irregulares são uma extensão do tf.Tensor
que modelam uma classe expandida de dados. Essa diferença é fundamental ao definir operações:
Ao aplicar uma op a um tensor esparso ou denso, deve sempre dar o mesmo resultado.
Ao aplicar uma op a um tensor irregular ou esparso, os resultados podem ser diferentes.
Por exemplo, considere a maneira que as operações de arrays como concat
, stack
e tile
são definidas para os tensores irregulares em comparação com os esparsos. A concatenação de tensores irregulares mescla cada linha, formando uma única linha de comprimento combinado:
No entanto, a concatenação dos tensores esparsos equivale à concatenação dos tensores densos correspondentes, conforme ilustrado pelo exemplo a seguir (em que Ø indica valores ausentes):
Como outro exemplo da importância dessa distinção, considere a definição do "valor médio de cada linha" para uma op como tf.reduce_mean
. Para um tensor irregular, o valor médio de uma linha é a soma dos valores dessa linha divididos pela largura dela. No entanto, para um tensor esparso, o valor médio de uma linha é a soma dos valores dessa linha divididos pela largura geral do tensor esparso (que é maior ou igual à largura da linha mais longa).
APIs TensorFlow
Keras
tf.keras é uma API de alto nível do TensorFlow para criar e treinar modelos de aprendizado profundo. Os tensores irregulares podem ser passados como entradas para um modelo do Keras ao configurar ragged=True
no tf.keras.Input
ou tf.keras.layers.InputLayer
. Os tensores irregulares também podem ser passados entre camadas do Keras e retornados por modelos do Keras. O exemplo a seguir mostra um modelo LSTM de brinquedo treinado usando tensores irregulares.
tf.Example
tf.Example é uma codificação de protobuf padrão para os dados do TensorFlow. Os dados codificados com tf.Example
s geralmente incluem características de comprimento variável. Por exemplo, o código a seguir define um lote de quatro mensagens tf.Example
com diferentes comprimentos de características:
Você pode processar esses dados codificados usando tf.io.parse_example
, que pega um tensor de strings serializadas e um dicionário de especificações de características e retorna um dicionário que mapeia nomes de características a tensores. Para ler as características de comprimento variável nos tensores irregulares, basta usar tf.io.RaggedFeature
no dicionário de especificações de características:
tf.io.RaggedFeature
também pode ser usado para ler as características com várias dimensões irregulares. Para mais detalhes, consulte a documentação da API.
Datasets
tf.data é uma API que permite criar pipelines de entrada complexos a partir de partes simples e reutilizáveis. A estrutura de dados principal é tf.data.Dataset
, que representa uma sequência de elementos, onde cada um consiste em um ou mais componentes.
Criando datasets com tensores irregulares
Os datasets podem ser criados a partir de tensores irregulares usando os mesmos métodos utilizados para a criação a partir de tf.Tensor
s ou array
s do NumPy, como Dataset.from_tensor_slices
:
Observação: Dataset.from_generator
ainda não é compatível com tensores irregulares, mas o suporte será adicionado em breve.
Criando e separando lotes de datasets com tensores irregulares
Os datasets com tensores irregulares podem ser divididos em lotes (que combina n elementos consecutivos em um único elemento) usando o método Dataset.batch
.
Por outro lado, um dataset em lotes pode ser transformado em um dataset simples usando Dataset.unbatch
.
Dividindo datasets em lotes com tensores não irregulares de comprimento variável
Se você tiver um dataset com tensores não irregulares, e o comprimento dos tensores variar entre os elementos, então você pode dividir esses tensores não irregulares em lotes de tensores irregulares ao aplicar a transformação dense_to_ragged_batch
:
Transformando datasets com tensores irregulares
Você também pode criar ou transformar tensores irregulares em datasets usando Dataset.map
:
tf.function
tf.function é um decorador que pré-computa os grafos do TensorFlow para as funções em Python, o que pode melhorar significativamente o desempenho do seu código do TensorFlow. Os tensores irregulares podem ser usados de maneira transparente com funções @tf.function
decoradas. Por exemplo, a função a seguir funciona tanto com tensores irregulares quanto não irregulares.
Se você quiser especificar input_signature
explicitamente para tf.function
, pode fazer isso usando tf.RaggedTensorSpec
.
Funções concretas
As funções concretas encapsulam os grafos traçados individuais que são criados por tf.function
. Os tensores irregulares podem ser usados de maneira transparente com funções concretas.
SavedModels
Um SavedModel é um programa do TensorFlow serializado, incluindo tanto os pesos quanto a computação. Ele pode ser criado a partir de um modelo do Keras ou de um modelo personalizado. Nos dois casos, os tensores irregulares podem ser usados de maneira transparente com as funções e os métodos definidos por um SavedModel.
Exemplo: salvando um modelo do Keras
Exemplo: salvando um modelo personalizado
Observação: as assinaturas do SavedModel são funções concretas. Conforme discutido na seção "Funções concretas" acima, os tensores irregulares só são tratados corretamente pelas funções concretas a partir do TensorFlow 2.3. Se você precisar usar assinaturas do SavedModel em uma versão anterior do TensorFlow, é recomendável decompor o tensor irregular nos seus tensores componentes.
Operadores sobrecarregados
A classe RaggedTensor
sobrecarrega os operadores aritméticos e de comparação padrão do Python, facilitando realizar a matemática básica elemento a elemento:
Como os operadores sobrecarregados realizam computações elemento a elemento, é preciso que as entradas de todas as operações binárias tenham o mesmo formato ou possam fazer o broadcasting para o mesmo formato. No caso de broadcasting mais simples, um único escalar é combinado elemento a elemento com cada valor de um tensor irregular:
Para uma discussão de casos mais avançados, confira a seção sobre Broadcasting.
Os tensores irregulares sobrecarregam o mesmo conjunto de operadores que Tensor
s normais: os operadores unários -
, ~
e abs()
; e os operadores binários +
, -
, *
, /
, //
, %
, **
, &
, |
, ^
, ==
, <
, <=
, >
e >=
.
Indexação
Os tensores irregulares são compatíveis com a indexação de estilo Python, incluindo a indexação e o fatiamento multidimensional. Os exemplos a seguir demonstram a indexação de tensores irregulares com um tensor irregular 2D e 3D.
Exemplos de indexação: tensor irregular 2D
Exemplos de indexação: tensor irregular 3D
RaggedTensor
s são compatíveis com a indexação e o fatiamento multidimensional com uma restrição: a indexação em uma dimensão irregular não é permitida. Esse caso é problemático porque o valor indicado pode existir em algumas linhas, mas não em outras. Nesses casos, não é evidente se você deve (1) gerar um IndexError
; (2) usar um valor padrão; ou (3) pular esse valor e retornar um tensor com menos linhas que antes. Seguindo os princípios norteadores do Python ("Diante da ambiguidade, resista à tentação de adivinhar"), essa operação foi proibida.
Conversão de tipo de tensor
A classe RaggedTensor
define métodos que podem ser usados para a conversão entre RaggedTensor
s e tf.Tensor
s ou tf.SparseTensors
:
Avaliando tensores irregulares
Para acessar os valores em um tensor irregular, você pode:
Usar
tf.RaggedTensor.to_list
para converter o tensor irregular em uma lista Python aninhada.Usar
tf.RaggedTensor.numpy
para converter o tensor irregular em um array do NumPy com valores que são arrays do NumPy aninhados.Decompor o tensor irregular nos seus componentes, usando as propriedades
tf.RaggedTensor.values
etf.RaggedTensor.row_splits
ou métodos de particionamento de linhas, comotf.RaggedTensor.row_lengths
etf.RaggedTensor.value_rowids
.Usar a indexação do Python para selecionar valores do tensor irregular.
Formatos irregulares
O formato de um tensor especifica o tamanho de cada eixo. Por exemplo, o formato de [[1, 2], [3, 4], [5, 6]]
é [3, 2]
, já que há 3 linhas e 2 colunas. O TensorFlow tem duas maneiras separadas mas relacionadas de descrever formatos:
formato estático: são informações sobre os tamanhos dos eixos conhecidas estaticamente (por exemplo, ao traçar uma
tf.function
). Pode ser parcialmente especificado.formato dinâmico: são informações de runtime sobre os tamanhos dos eixos.
Formato estático
O formato estático de um tensor contém informações sobre os tamanhos dos eixos que são conhecidas no momento de construção do grafo. Para ambos o tf.Tensor
e o tf.RaggedTensor
, ele está disponível usando a propriedade .shape
e é codificado usando tf.TensorShape
:
O formato estático de uma dimensão irregular é sempre None
(ou seja, não especificada). No entanto, o inverso não é verdade — se uma dimensão TensorShape
for None
, então isso pode indicar que a dimensão é irregular ou que ela é uniforme, mas o tamanho dela não é conhecido estatisticamente.
Formato dinâmico
O formato dinâmico de um tensor contém informações sobre os tamanhos dos eixos que são conhecidas quando o grafo é executado. É criado usando a operação tf.shape
. Para tf.Tensor
, tf.shape
retorna o formato como um Tensor
de número inteiro 1D, em que tf.shape(x)[i]
é o tamanho do eixo i
.
No entanto, um Tensor
1D não é expressivo o suficiente para descrever o formato de um tf.RaggedTensor
. Em vez disso, o formato dinâmico para tensores irregulares é codificado usando um tipo dedicado, tf.experimental.DynamicRaggedShape
. No exemplo a seguir, o DynamicRaggedShape
retornado por tf.shape(rt)
indica que o tensor irregular tem 4 linhas, com comprimentos 1, 3, 0 e 2:
Formato dinâmico: operações
DynamicRaggedShape
s podem ser usados com a maioria das ops do TensorFlow que esperam formatos, incluindo tf.reshape
, tf.zeros
, tf.ones
. tf.fill
, tf.broadcast_dynamic_shape
e tf.broadcast_to
.
Formato dinâmico: indexação e fatiamento
DynamicRaggedShape
também pode ser indexado para obter os tamanhos das dimensões uniformes. Por exemplo, podemos encontrar o número de linhas em um raggedtensor usando tf.shape(rt)[0]
(como faríamos para um tensor não irregular):
No entanto, é um erro usar a indexação para tentar recuperar o tamanho de uma dimensão irregular, já que ela não tem um único tamanho. (Como o RaggedTensor
rastreia quais eixos são irregulares, esse erro só é gerado durante a eager execution ou ao traçar uma tf.function
. Ele nunca será gerado ao executar uma função concreta.)
DynamicRaggedShape
s também podem ser fatiados, desde que a fatia comece com o eixo 0
ou contenha apenas dimensões densas.
Formato dinâmico: codificação
DynamicRaggedShape
é codificado usando dois campos:
inner_shape
: um vetor de número inteiro que dá o formato de umtf.Tensor
denso.row_partitions
: uma lista de objetostf.experimental.RowPartition
, que descreve como a dimensão mais externa desse formato interno deve ser particionada para adicionar eixos irregulares.
Para mais informações sobre as partições de linhas, confira a seção "Codificação do RaggedTensor" abaixo e a documentação da API para tf.experimental.RowPartition
.
Formato dinâmico: construção
DynamicRaggedShape
é geralmente construído ao aplicar tf.shape
a um RaggedTensor
, mas ele também pode ser construído diretamente:
Se os comprimentos de todas as linhas são conhecidos estaticamente, DynamicRaggedShape.from_lengths
também pode ser usado para construir um formato irregular dinâmico (Isso é útil principalmente para teste e código de demonstração, já que é raro para os comprimentos das dimensões irregulares serem conhecidos estaticamente).
Broadcasting
Broadcasting é o processo de fazer com que tensores de diferentes formatos tenham formatos compatíveis para operações elemento a elemento. Para mais contexto sobre o broadcasting, consulte:
tf.broadcast_dynamic_shape
tf.broadcast_to
Os passos básicos de broadcasting de duas entradas x
e y
para que tenham formatos compatíveis são:
Se
x
ey
não têm o mesmo número de dimensões, adicione dimensões externas (com tamanho 1) até que tenham.Para cada dimensão em que
x
ey
têm tamanhos diferentes:
Se
x
ouy
tem o tamanho1
na dimensãod
, então repita os valores dele na dimensãod
para combinar com o tamanho da outra entrada.Caso contrário, gere uma exceção (
x
ey
não são compatíveis com o broadcasting).
Onde o tamanho de um tensor em uma dimensão uniforme é um único número (o tamanho das fatias nessa dimensão); e o tamanho de um tensor em uma dimensão irregular é uma lista de comprimentos de fatias (para todas as fatias nessa dimensão).
Exemplos de broadcasting
Aqui estão alguns exemplos de formatos que não realizam o broadcasting:
Codificação do RaggedTensor
Os tensores irregulares são codificados usando a classe RaggedTensor
. Internamente, cada RaggedTensor
consiste em:
Um tensor
values
, que concatena as linhas de comprimento variável em uma lista simples.Uma
row_partition
, que indica como esses valores simples são divididos em linhas.
A row_partition
pode ser armazenada usando quatro codificações diferentes:
row_splits
é um vetor de número inteiro que especifica os pontos de divisão entre as linhas.value_rowids
é um vetor de número inteiro que especifica o índice da linha para cada valor.row_lengths
é um vetor de número inteiro que especifica o comprimento de cada linha.uniform_row_length
é um escalar de número inteiro que especifica um único comprimento para todas as linhas.
Um nrows
escalar de número inteiro também pode ser incluído na codificação de row_partition
para compensar as linhas de espaços em branco à direita com value_rowids
ou as linhas vazias com uniform_row_length
.
A escolha de qual codificação usar para as partições de linhas é gerenciada internamente por tensores irregulares para melhorar a eficiência em alguns contextos. Em particular, algumas das vantagens e desvantagens de diferentes esquemas de particionamento de linhas são:
Indexação eficiente: a codificação
row_splits
permite a indexação de tempo constante e o fatiamento em tensores irregulares.Concatenação eficiente: a codificação
row_lengths
é mais eficiente ao concatenar tensores irregulares, já que os comprimentos de linhas não mudam quando dois tensores são concatenados.Codificação de tamanho pequeno: a codificação
value_rowids
é mais eficiente ao armazenar tensores irregulares com um grande número de linhas vazias, já que o tamanho do tensor depende apenas do número total de valores. Por outro lado, as codificaçõesrow_splits
erow_lengths
são mais eficientes ao armazenar tensores irregulares com linhas mais longas, porque exigem apenas um valor escalar para cada linha.Compatibilidade: o esquema
value_rowids
se ajusta ao formato de segmentação usado pelas operações, comotf.segment_sum
. O esquemarow_limits
se ajusta ao formato usado por ops comotf.sequence_mask
.Dimensões uniformes: conforme discutido abaixo, a codificação
uniform_row_length
é usada para codificar tensores irregulares com dimensões uniformes.
Várias dimensões irregulares
Um tensor irregular com várias dimensões irregulares é codificado ao usar um RaggedTensor
aninhado para o tensor values
. Cada RaggedTensor
aninhado adiciona uma única dimensão irregular.
A factory function tf.RaggedTensor.from_nested_row_splits
pode ser usada para construir um RaggedTensor com várias dimensões irregulares diretamente ao fornecer uma lista de tensores row_splits
:
Posto irregular e valores simples
O posto irregular de um tensor irregular é o número de vezes que o tensor values
subjacente foi particionado (ou seja, a profundidade de aninhamento dos objetos RaggedTensor
). O tensor values
mais interno é conhecido como seus flat_values. No exemplo a seguir, conversations
tem ragged_rank=3, e seus flat_values
é um Tensor
1D com 24 strings:
Dimensões internas uniformes
Os tensores irregulares com dimensões internas uniformes são codificados ao usar um tf.Tensor
multidimensional para os flat_values (ou seja, os values
mais internos).
Dimensões não internas uniformes
Os tensores irregulares com dimensões não internas uniformes são codificados por linhas de particionamento com uniform_row_length
.