Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/pt-br/guide/tf_numpy.ipynb
25115 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.

API NumPy no TensorFlow

Visão geral

O TensorFlow implementa um subconjunto da API NumPy, disponível como tf.experimental.numpy. Isso permite a execução de código NumPy, acelerado pelo TensorFlow, ao mesmo tempo que permite acesso a todas as APIs do TensorFlow.

Configuração

import matplotlib.pyplot as plt import numpy as np import tensorflow as tf import tensorflow.experimental.numpy as tnp import timeit print("Using TensorFlow version %s" % tf.__version__)

Ativando o comportamento NumPy

Para usar tnp como NumPy, ative o comportamento NumPy para o TensorFlow:

tnp.experimental_enable_numpy_behavior()

Essa chamada permite a promoção de tipos no TensorFlow e também altera a inferência de tipos, ao converter literais em tensores, para seguir mais estritamente o padrão NumPy.

Observação: esta chamada mudará o comportamento de todo o TensorFlow, não apenas do módulo tf.experimental.numpy.

TensorFlow NumPy ND array

Uma instância de tf.experimental.numpy.ndarray, chamada ND Array, representa um array multidimensional denso de um determinado dtype colocado em um determinado dispositivo. É um alias para tf.Tensor. Dê uma olhada na classe de ND array para métodos úteis como ndarray.T, ndarray.reshape, ndarray.ravel e outros.

Primeiro crie um objeto ND array e, em seguida, chame métodos diferentes.

# Create an ND array and check out different attributes. ones = tnp.ones([5, 3], dtype=tnp.float32) print("Created ND array with shape = %s, rank = %s, " "dtype = %s on device = %s\n" % ( ones.shape, ones.ndim, ones.dtype, ones.device)) # `ndarray` is just an alias to `tf.Tensor`. print("Is `ones` an instance of tf.Tensor: %s\n" % isinstance(ones, tf.Tensor)) # Try commonly used member functions. print("ndarray.T has shape %s" % str(ones.T.shape)) print("narray.reshape(-1) has shape %s" % ones.reshape(-1).shape)

Promoção de tipos

Existem 4 opções para promoção de tipos no TensorFlow.

  • Por padrão, o TensorFlow gera erros em vez de promover tipos para operações de tipo misto.

  • A execução de tf.numpy.experimental_enable_numpy_behavior() faz o TensorFlow passar a usar as regras de promoção de tipos do {code}NumPy{/code} (descritas abaixo).

  • Após o TensorFlow 2.15, há duas novas opções (veja Promoção de tipos TF NumPy para mais detalhes):

    • tf.numpy.experimental_enable_numpy_behavior(dtype_conversion_mode="all") usa regras de promoção de tipos do Jax.

    • tf.numpy.experimental_enable_numpy_behavior(dtype_conversion_mode="safe") usa regras de promoção de tipos do Jax, mas não permite determinadas promoções inseguras.

Promoção de tipos NumPy

As APIs TensorFlow NumPy têm semântica bem definida para converter literais em ND arrays, bem como para realizar promoção de tipos em entradas de ND arrays. Veja np.result_type para mais detalhes.

As APIs TensorFlow deixam as entradas tf.Tensor inalteradas e não realizam promoção de tipo nelas, enquanto as APIs TensorFlow NumPy promovem todas as entradas de acordo com as regras de promoção de tipos do NumPy. No próximo exemplo, você realizará uma promoção de tipos. Primeiro, execute a adição nas entradas do ND array de diferentes tipos e observe os tipos de saída. Nenhuma dessas promoções de tipo seria permitida pelas APIs do TensorFlow.

print("Type promotion for operations") values = [tnp.asarray(1, dtype=d) for d in (tnp.int32, tnp.int64, tnp.float32, tnp.float64)] for i, v1 in enumerate(values): for v2 in values[i + 1:]: print("%s + %s => %s" % (v1.dtype.name, v2.dtype.name, (v1 + v2).dtype.name))

Por fim, converta literais em ND arrays usando ndarray.asarray e observe o tipo resultante.

print("Type inference during array creation") print("tnp.asarray(1).dtype == tnp.%s" % tnp.asarray(1).dtype.name) print("tnp.asarray(1.).dtype == tnp.%s\n" % tnp.asarray(1.).dtype.name)

Ao converter literais em ND arrays, o NumPy prefere tipos largos como tnp.int64 e tnp.float64. Por outro lado, tf.convert_to_tensor prefere os tipos tf.int32 e tf.float32 para converter constantes em tf.Tensor. As APIs TensorFlow NumPy aderem ao comportamento NumPy para números inteiros. Quanto aos floats, o argumento prefer_float32 de experimental_enable_numpy_behavior permite controlar se você prefere tf.float32 em vez de tf.float64 (o padrão é False). Por exemplo:

tnp.experimental_enable_numpy_behavior(prefer_float32=True) print("When prefer_float32 is True:") print("tnp.asarray(1.).dtype == tnp.%s" % tnp.asarray(1.).dtype.name) print("tnp.add(1., 2.).dtype == tnp.%s" % tnp.add(1., 2.).dtype.name) tnp.experimental_enable_numpy_behavior(prefer_float32=False) print("When prefer_float32 is False:") print("tnp.asarray(1.).dtype == tnp.%s" % tnp.asarray(1.).dtype.name) print("tnp.add(1., 2.).dtype == tnp.%s" % tnp.add(1., 2.).dtype.name)

Broadcasting

Semelhante ao TensorFlow, o NumPy define uma semântica rica para "broadcasting" de valores. Você pode consultar o guia de broadcasting do NumPy para obter mais informações e compará-lo com a semântica de broadcasting do TensorFlow.

x = tnp.ones([2, 3]) y = tnp.ones([3]) z = tnp.ones([1, 2, 1]) print("Broadcasting shapes %s, %s and %s gives shape %s" % ( x.shape, y.shape, z.shape, (x + y + z).shape))

Indexação

O NumPy define regras de indexação muito sofisticadas. Consulte o Guia de indexação NumPy. Preste atenção ao uso de ND arrays como índices no exemplo abaixo.

x = tnp.arange(24).reshape(2, 3, 4) print("Basic indexing") print(x[1, tnp.newaxis, 1:3, ...], "\n") print("Boolean indexing") print(x[:, (True, False, True)], "\n") print("Advanced indexing") print(x[1, (0, 0, 1), tnp.asarray([0, 1, 1])])
# Mutation is currently not supported try: tnp.arange(6)[1] = -1 except TypeError: print("Currently, TensorFlow NumPy does not support mutation.")

Modelo de exemplo

A seguir, você verá como criar um modelo e executar inferências nele. Este modelo simples aplica uma camada relu seguida por uma projeção linear. As seções posteriores mostrarão como calcular gradientes para este modelo usando GradientTape do TensorFlow.

class Model(object): """Model with a dense and a linear layer.""" def __init__(self): self.weights = None def predict(self, inputs): if self.weights is None: size = inputs.shape[1] # Note that type `tnp.float32` is used for performance. stddev = tnp.sqrt(size).astype(tnp.float32) w1 = tnp.random.randn(size, 64).astype(tnp.float32) / stddev bias = tnp.random.randn(64).astype(tnp.float32) w2 = tnp.random.randn(64, 2).astype(tnp.float32) / 8 self.weights = (w1, bias, w2) else: w1, bias, w2 = self.weights y = tnp.matmul(inputs, w1) + bias y = tnp.maximum(y, 0) # Relu return tnp.matmul(y, w2) # Linear projection model = Model() # Create input data and compute predictions. print(model.predict(tnp.ones([2, 32], dtype=tnp.float32)))

TensorFlow NumPy e NumPy

O TensorFlow NumPy implementa um subconjunto da especificação completa do NumPy. Embora mais símbolos devam ser adicionados ao longo do tempo, existem alguns recursos sistemáticos que não serão suportados num futuro próximo. Isto inclui o suporte à API NumPy C, integração com Swig, ordem de armazenamento no Fortran, visualizações e stride_tricks e alguns dtype (como np.recarray e np.object). Para mais detalhes, consulte a Documentação da API TensorFlow NumPy.

Interoperabilidade com o NumPy

Os ND arrays do TensorFlow podem interoperar com funções NumPy. Esses objetos implementam a interface __array__. NumPy usa esta interface para converter argumentos de função em valores np.ndarray antes de processá-los.

Da mesma forma, as funções TensorFlow NumPy podem aceitar entradas de diferentes tipos, incluindo np.ndarray. Essas entradas são convertidas em um ND array ao chamar ndarray.asarray nelas.

A conversão de um ND array de e para np.ndarray pode acionar cópias de dados. Consulte a seção sobre cópias buffer para mais detalhes.

# ND array passed into NumPy function. np_sum = np.sum(tnp.ones([2, 3])) print("sum = %s. Class: %s" % (float(np_sum), np_sum.__class__)) # `np.ndarray` passed into TensorFlow NumPy function. tnp_sum = tnp.sum(np.ones([2, 3])) print("sum = %s. Class: %s" % (float(tnp_sum), tnp_sum.__class__))
# It is easy to plot ND arrays, given the __array__ interface. labels = 15 + 2 * tnp.random.randn(1, 1000) _ = plt.hist(labels)

Cópias buffer

A mistura do TensorFlow NumPy com o código NumPy pode acionar cópias de dados. Isto ocorre porque o TensorFlow NumPy tem requisitos mais rigorosos de alinhamento de memória do que os do NumPy.

Quando um np.ndarray é passado para o TensorFlow NumPy, ele verifica os requisitos de alinhamento e aciona uma cópia, se necessário. Ao passar um buffer de CPU ND array para o NumPy, geralmente o buffer satisfará os requisitos de alinhamento e o NumPy não precisará criar uma cópia.

ND arrays podem se referir a buffers colocados em dispositivos diferentes da memória local da CPU. Nesses casos, chamar uma função NumPy acionará cópias na rede ou dispositivo conforme necessário.

Diante disso, a mistura com chamadas de API NumPy geralmente deve ser feita com cautela e o usuário deve estar atento aos overheads gerais relacionados à cópia de dados. Intercalar chamadas do TensorFlow NumPy com chamadas do TensorFlow geralmente é seguro e evita a cópia de dados. Veja a seção sobre Interoperabilidade com o TensorFlow para obter detalhes.

Precedência de operadores

O TensorFlow NumPy define uma __array_priority__ maior que o do NumPy. Isto significa que para operadores envolvendo ND arrays e np.ndarray, o primeiro terá precedência, ou seja, a entrada np.ndarray será convertida num ND array e a implementação TensorFlow NumPy do operador será chamada.

x = tnp.ones([2]) + np.ones([2]) print("x = %s\nclass = %s" % (x, x.__class__))

TF NumPy e TensorFlow

O TensorFlow NumPy é construído com base no TensorFlow e, portanto, interopera perfeitamente com o TensorFlow.

tf.Tensor e ND array

O ND array é um alias para tf.Tensor, então obviamente eles podem ser misturados sem produzir cópias de dados.

x = tf.constant([1, 2]) print(x) # `asarray` and `convert_to_tensor` here are no-ops. tnp_x = tnp.asarray(x) print(tnp_x) print(tf.convert_to_tensor(tnp_x)) # Note that tf.Tensor.numpy() will continue to return `np.ndarray`. print(x.numpy(), x.numpy().__class__)

Interoperabilidade com o TensorFlow

Um ND array pode ser passado para APIs do TensorFlow, já que o ND array é apenas um alias para tf.Tensor. Conforme mencionado anteriormente, tal interoperação não faz cópias de dados, nem mesmo para dados colocados em aceleradores ou dispositivos remotos.

Por outro lado, os objetos tf.Tensor podem ser passados ​​para APIs tf.experimental.numpy, sem a realização de cópias de dados.

# ND array passed into TensorFlow function. tf_sum = tf.reduce_sum(tnp.ones([2, 3], tnp.float32)) print("Output = %s" % tf_sum) # `tf.Tensor` passed into TensorFlow NumPy function. tnp_sum = tnp.sum(tf.ones([2, 3])) print("Output = %s" % tnp_sum)

Gradientes e Jacobianos: tf.GradientTape

O GradientTape do TensorFlow pode ser usado para retropropagação por meio do código TensorFlow e TensorFlow NumPy.

Use o modelo criado na seção Modelo de exemplo e calcule gradientes e jacobianos.

def create_batch(batch_size=32): """Creates a batch of input and labels.""" return (tnp.random.randn(batch_size, 32).astype(tnp.float32), tnp.random.randn(batch_size, 2).astype(tnp.float32)) def compute_gradients(model, inputs, labels): """Computes gradients of squared loss between model prediction and labels.""" with tf.GradientTape() as tape: assert model.weights is not None # Note that `model.weights` need to be explicitly watched since they # are not tf.Variables. tape.watch(model.weights) # Compute prediction and loss prediction = model.predict(inputs) loss = tnp.sum(tnp.square(prediction - labels)) # This call computes the gradient through the computation above. return tape.gradient(loss, model.weights) inputs, labels = create_batch() gradients = compute_gradients(model, inputs, labels) # Inspect the shapes of returned gradients to verify they match the # parameter shapes. print("Parameter shapes:", [w.shape for w in model.weights]) print("Gradient shapes:", [g.shape for g in gradients]) # Verify that gradients are of type ND array. assert isinstance(gradients[0], tnp.ndarray)
# Computes a batch of jacobians. Each row is the jacobian of an element in the # batch of outputs w.r.t. the corresponding input batch element. def prediction_batch_jacobian(inputs): with tf.GradientTape() as tape: tape.watch(inputs) prediction = model.predict(inputs) return prediction, tape.batch_jacobian(prediction, inputs) inp_batch = tnp.ones([16, 32], tnp.float32) output, batch_jacobian = prediction_batch_jacobian(inp_batch) # Note how the batch jacobian shape relates to the input and output shapes. print("Output shape: %s, input shape: %s" % (output.shape, inp_batch.shape)) print("Batch jacobian shape:", batch_jacobian.shape)

Compilação de rastreamento: tf.function

tf.function do TensorFlow funciona "compilando o rastreamento" do código e, em seguida, otimizando esses rastreamentos para um desempenho muito mais rápido. Veja a Introdução a grafos e funções.

tf.function também pode ser usada para otimizar o código TensorFlow NumPy. Aqui está um exemplo simples para demonstrar as acelerações. Observe que o corpo do código tf.function inclui chamadas para APIs TensorFlow NumPy.

inputs, labels = create_batch(512) print("Eager performance") compute_gradients(model, inputs, labels) print(timeit.timeit(lambda: compute_gradients(model, inputs, labels), number=10) * 100, "ms") print("\ntf.function compiled performance") compiled_compute_gradients = tf.function(compute_gradients) compiled_compute_gradients(model, inputs, labels) # warmup print(timeit.timeit(lambda: compiled_compute_gradients(model, inputs, labels), number=10) * 100, "ms")

Vetorização: tf.vectorized_map

O TensorFlow tem suporte integrado para vetorização de loops paralelos, o que permite acelerações de uma a duas ordens de magnitude. Essas acelerações são acessíveis via API tf.vectorized_map e também se aplicam a código TensorFlow NumPy.

Às vezes é útil calcular o gradiente de cada saída num lote em relação ao elemento de lote de entrada correspondente. Tal cálculo pode ser feito de forma eficiente usando tf.vectorized_map conforme mostrado abaixo.

@tf.function def vectorized_per_example_gradients(inputs, labels): def single_example_gradient(arg): inp, label = arg return compute_gradients(model, tnp.expand_dims(inp, 0), tnp.expand_dims(label, 0)) # Note that a call to `tf.vectorized_map` semantically maps # `single_example_gradient` over each row of `inputs` and `labels`. # The interface is similar to `tf.map_fn`. # The underlying machinery vectorizes away this map loop which gives # nice speedups. return tf.vectorized_map(single_example_gradient, (inputs, labels)) batch_size = 128 inputs, labels = create_batch(batch_size) per_example_gradients = vectorized_per_example_gradients(inputs, labels) for w, p in zip(model.weights, per_example_gradients): print("Weight shape: %s, batch size: %s, per example gradient shape: %s " % ( w.shape, batch_size, p.shape))
# Benchmark the vectorized computation above and compare with # unvectorized sequential computation using `tf.map_fn`. @tf.function def unvectorized_per_example_gradients(inputs, labels): def single_example_gradient(arg): inp, label = arg return compute_gradients(model, tnp.expand_dims(inp, 0), tnp.expand_dims(label, 0)) return tf.map_fn(single_example_gradient, (inputs, labels), fn_output_signature=(tf.float32, tf.float32, tf.float32)) print("Running vectorized computation") print(timeit.timeit(lambda: vectorized_per_example_gradients(inputs, labels), number=10) * 100, "ms") print("\nRunning unvectorized computation") per_example_gradients = unvectorized_per_example_gradients(inputs, labels) print(timeit.timeit(lambda: unvectorized_per_example_gradients(inputs, labels), number=10) * 100, "ms")

Posicionamento de dispositivos

O TensorFlow NumPy pode realizar operações em CPUs, GPUs, TPUs e dispositivos remotos. Ele usa os mecanismos padrão do TensorFlow para o posicionamento de dispositivos. Abaixo, um exemplo simples mostra como listar todos os dispositivos e, em seguida, aplicar alguma computação num dispositivo específico.

O TensorFlow também possui APIs para replicar a computação entre dispositivos e realizar reduções coletivas que não serão abordadas aqui.

Listando dispositivos

tf.config.list_logical_devices e tf.config.list_physical_devices podem ser usados ​​para encontrar quais dispositivos usar.

print("All logical devices:", tf.config.list_logical_devices()) print("All physical devices:", tf.config.list_physical_devices()) # Try to get the GPU device. If unavailable, fallback to CPU. try: device = tf.config.list_logical_devices(device_type="GPU")[0] except IndexError: device = "/device:CPU:0"

Posicionando operações: tf.device

As operações podem ser colocadas num dispositivo chamando-o num escopo de tf.device.

print("Using device: %s" % str(device)) # Run operations in the `tf.device` scope. # If a GPU is available, these operations execute on the GPU and outputs are # placed on the GPU memory. with tf.device(device): prediction = model.predict(create_batch(5)[0]) print("prediction is placed on %s" % prediction.device)

Copiando ND arrays entre dispositivos: tnp.copy

Uma chamada para tnp.copy, colocada num determinado escopo de dispositivo, copiará os dados para esse dispositivo, a menos que os dados já estejam nesse dispositivo.

with tf.device("/device:CPU:0"): prediction_cpu = tnp.copy(prediction) print(prediction.device) print(prediction_cpu.device)

Comparações de desempenho

O TensorFlow NumPy usa kernels TensorFlow altamente otimizados que podem ser despachados em CPUs, GPUs e TPUs. O TensorFlow também realiza muitas otimizações de compilador, como fusão de operações, que se traduzem em melhorias de desempenho e uso de memória. Veja Otimização de grafos do TensorFlow com o Grappler para saber mais.

No entanto, o TensorFlow tem overheads gerais mais altas para operações de despacho em comparação com o NumPy. Para workloads compostas por pequenas operações (menos de 10 microssegundos), esses overheads podem dominar o tempo de execução e o NumPy pode garantir melhor desempenho. Para outros casos, o TensorFlow geralmente deve entregar um melhor desempenho.

Execute o benchmark abaixo para comparar o desempenho do NumPy com o TensorFlow NumPy para diferentes tamanhos de entrada.

def benchmark(f, inputs, number=30, force_gpu_sync=False): """Utility to benchmark `f` on each value in `inputs`.""" times = [] for inp in inputs: def _g(): if force_gpu_sync: one = tnp.asarray(1) f(inp) if force_gpu_sync: with tf.device("CPU:0"): tnp.copy(one) # Force a sync for GPU case _g() # warmup t = timeit.timeit(_g, number=number) times.append(t * 1000. / number) return times def plot(np_times, tnp_times, compiled_tnp_times, has_gpu, tnp_times_gpu): """Plot the different runtimes.""" plt.xlabel("size") plt.ylabel("time (ms)") plt.title("Sigmoid benchmark: TF NumPy vs NumPy") plt.plot(sizes, np_times, label="NumPy") plt.plot(sizes, tnp_times, label="TF NumPy (CPU)") plt.plot(sizes, compiled_tnp_times, label="Compiled TF NumPy (CPU)") if has_gpu: plt.plot(sizes, tnp_times_gpu, label="TF NumPy (GPU)") plt.legend()
# Define a simple implementation of `sigmoid`, and benchmark it using # NumPy and TensorFlow NumPy for different input sizes. def np_sigmoid(y): return 1. / (1. + np.exp(-y)) def tnp_sigmoid(y): return 1. / (1. + tnp.exp(-y)) @tf.function def compiled_tnp_sigmoid(y): return tnp_sigmoid(y) sizes = (2 ** 0, 2 ** 5, 2 ** 10, 2 ** 15, 2 ** 20) np_inputs = [np.random.randn(size).astype(np.float32) for size in sizes] np_times = benchmark(np_sigmoid, np_inputs) with tf.device("/device:CPU:0"): tnp_inputs = [tnp.random.randn(size).astype(np.float32) for size in sizes] tnp_times = benchmark(tnp_sigmoid, tnp_inputs) compiled_tnp_times = benchmark(compiled_tnp_sigmoid, tnp_inputs) has_gpu = len(tf.config.list_logical_devices("GPU")) if has_gpu: with tf.device("/device:GPU:0"): tnp_inputs = [tnp.random.randn(size).astype(np.float32) for size in sizes] tnp_times_gpu = benchmark(compiled_tnp_sigmoid, tnp_inputs, 100, True) else: tnp_times_gpu = None plot(np_times, tnp_times, compiled_tnp_times, has_gpu, tnp_times_gpu)