Esta página foi traduzida pela API Cloud Translation.
Switch to English

tf.map_fn

Versão TensorFlow 1 Ver fonte no GitHub

Transforma os elems aplicando fn a cada elemento não empilhado no eixo 0. (argumentos obsoletos)

Usado nos notebooks

Utilizado no guia Usado nos tutoriais

Veja também tf.scan .

map_fn desempilha os elems no eixo 0 para obter uma sequência de elementos; chama fn para transformar cada elemento; e depois empilha os valores transformados novamente.

Funções de mapeamento com entradas e saídas de tensor único

Se elems for um tensor único e a assinatura de fn for tf.Tensor->tf.Tensor , map_fn(fn, elems) será equivalente a tf.stack([fn(elem) for elem in tf.unstack(elems)]) Por exemplo:

tf.map_fn(fn=lambda t: tf.range(t, t + 3), elems=tf.constant([3, 5, 2]))
<tf.Tensor: shape=(3, 3), dtype=int32, numpy=
  array([[3, 4, 5],
         [5, 6, 7],
         [2, 3, 4]], dtype=int32)>

map_fn(fn, elems).shape = [elems.shape[0]] + fn(elems[0]).shape .

Funções de mapeamento com entradas e saídas com várias áreas

map_fn também suporta funções com entradas e saídas com várias áreas:

  • Se elems for uma tupla (ou estrutura aninhada) de tensores, todos esses tensores deverão ter o mesmo tamanho de dimensão externa ( num_elems ); e fn é usado para transformar cada tupla (ou estrutura) das fatias correspondentes dos elems . Por exemplo, se elems for uma tupla (t1, t2, t3) , então fn é usado para transformar cada tupla de fatias (t1[i], t2[i], t3[i]) (onde 0 <= i < num_elems ) .

  • Se fn retornar uma tupla (ou estrutura aninhada) de tensores, o resultado será formado pelo empilhamento dos elementos correspondentes dessas estruturas.

Especificando a assinatura de saída de fn

Se as assinaturas de entrada e saída de fn forem diferentes, a assinatura de saída deverá ser especificada usando fn_output_signature . (As assinaturas de entrada e saída são diferentes se suas estruturas, tipos ou tipos de tensores não corresponderem). Por exemplo:

tf.map_fn(fn=tf.strings.length,  # input & output have different dtypes
          elems=tf.constant(["hello", "moon"]),
          fn_output_signature=tf.int32)
<tf.Tensor: shape=(2,), dtype=int32, numpy=array([5, 4], dtype=int32)>
tf.map_fn(fn=tf.strings.join,  # input & output have different structures
          elems=[tf.constant(['The', 'A']), tf.constant(['Dog', 'Cat'])],
          fn_output_signature=tf.string)
<tf.Tensor: shape=(2,), dtype=string,
 numpy=array([b'TheDog', b'ACat'], dtype=object)>

fn_output_signature pode ser especificado usando qualquer um dos seguintes:

RaggedTensors

map_fn suporta entradas e saídas tf.RaggedTensor . Em particular:

  • Se elems for um RaggedTensor , fn será chamado com cada linha desse tensor irregular.

    • Se elems tiver apenas uma dimensão irregular, os valores passados ​​para fn serão tf.Tensor s.
    • Se os elems tiverem várias dimensões irregulares, os valores passados ​​para fn serão tf.RaggedTensor s com uma dimensão irregular menor.
  • Se o resultado de map_fn for um RaggedTensor , use um tf.RaggedTensorSpec para especificar fn_output_signature .

# Example: RaggedTensor input
rt = tf.ragged.constant([[1, 2, 3], [], [4, 5], [6]])
tf.map_fn(tf.reduce_sum, rt, fn_output_signature=tf.int32)
<tf.Tensor: shape=(4,), dtype=int32, numpy=array([6, 0, 9, 6], dtype=int32)>
# Example: RaggedTensor output
elems = tf.constant([3, 5, 0, 2])
tf.map_fn(tf.range, elems,
          fn_output_signature=tf.RaggedTensorSpec(shape=[None],
                                                  dtype=tf.int32))
<tf.RaggedTensor [[0, 1, 2], [0, 1, 2, 3, 4], [], [0, 1]]>

Por exemplo:

rt = tf.ragged.constant([[1, 2, 3], [], [4, 5], [6]])
tf.ragged.map_flat_values(lambda x: x + 2, rt)
<tf.RaggedTensor [[3, 4, 5], [], [6, 7], [8]]>

SparseTensors

map_fn suporta entradas e saídas tf.sparse.SparseTensor . Em particular:

  • Se elems for um SparseTensor , fn será chamado com cada linha desse tensor esparso. Em particular, o valor passado para fn será um tf.sparse.SparseTensor com uma dimensão a menos do que elems .

  • Se o resultado de map_fn for um SparseTensor , use um tf.SparseTensorSpec para especificar fn_output_signature . Os SparseTensor s individuais retornados por fn serão empilhados em um único SparseTensor com mais uma dimensão.

# Example: SparseTensor input
st = tf.sparse.SparseTensor([[0, 0], [2, 0], [2, 1]], [2, 3, 4], [4, 4])
tf.map_fn(tf.sparse.reduce_sum, st, fn_output_signature=tf.int32)
<tf.Tensor: shape=(4,), dtype=int32, numpy=array([2, 0, 7, 0], dtype=int32)>
# Example: SparseTensor output
tf.sparse.to_dense(
    tf.map_fn(tf.sparse.eye, tf.constant([2, 3]),
              fn_output_signature=tf.SparseTensorSpec(None, tf.float32)))
<tf.Tensor: shape=(2, 3, 3), dtype=float32, numpy=
  array([[[1., 0., 0.],
          [0., 1., 0.],
          [0., 0., 0.]],
         [[1., 0., 0.],
          [0., 1., 0.],
          [0., 0., 1.]]], dtype=float32)>
  • Se a função for expressável como TensorFlow ops, use:
 tf.sparse.SparseTensor(st.indices, fn(st.values), st.dense_shape)
 
  • Caso contrário, use:
 tf.sparse.SparseTensor(st.indices, tf.map_fn(fn, st.values),
                       st.dense_shape)
 

map_fn vs. operações vetorizadas

map_fn aplicará as operações usadas por fn a cada elemento de elems , resultando em operações totais de O(elems.shape[0]) . Isso é mitigado pelo fato de o map_fn poder processar elementos em paralelo. No entanto, uma transformação expressa usando map_fn ainda é normalmente menos eficiente do que uma transformação equivalente expressa usando operações vetorizadas.

map_fn normalmente deve ser usado apenas se uma das seguintes map_fn for verdadeira:

  • É difícil ou caro expressar a transformação desejada com operações vetorizadas.
  • fn cria grandes valores intermediários, portanto, uma transformação vetorizada equivalente levaria muita memória.
  • O processamento de elementos em paralelo é mais eficiente do que uma transformação vetorizada equivalente.
  • A eficiência da transformação não é crítica e o uso do map_fn é mais legível.

Por exemplo, o exemplo dado acima que mapeia fn=lambda t: tf.range(t, t + 3) através dos elems pode ser reescrito com mais eficiência usando operações vetorizadas:

elems = tf.constant([3, 5, 2])
tf.range(3) + tf.expand_dims(elems, 1)
<tf.Tensor: shape=(3, 3), dtype=int32, numpy=
  array([[3, 4, 5],
         [5, 6, 7],
         [2, 3, 4]], dtype=int32)>

Em alguns casos, tf.vectorized_map pode ser usado para converter automaticamente uma função em um eqivalente vetorizado.

Execução ansiosa

Ao executar ansiosamente, map_fn não é executado em paralelo, mesmo se parallel_iterations está definido para um valor> 1. Você ainda pode obter os benefícios de executar uma função em paralelo, utilizando o desempenho tf.function decorador:

fn=lambda t: tf.range(t, t + 3)
@tf.function
def func(elems):
  return tf.map_fn(fn, elems, parallel_iterations=3)
func(tf.constant([3, 5, 2]))
<tf.Tensor: shape=(3, 3), dtype=int32, numpy=
  array([[3, 4, 5],
         [5, 6, 7],
         [2, 3, 4]], dtype=int32)>