Ajuda a proteger a Grande Barreira de Corais com TensorFlow em Kaggle Junte Desafio

API NumPy no TensorFlow

Ver no TensorFlow.org Executar no Google Colab Ver fonte no GitHub Baixar caderno

Visão geral

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

Configurar

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__)
Using TensorFlow version 2.6.0

Habilitando o comportamento NumPy

Para utilizar tnp como NumPy, ativar o comportamento NumPy para TensorFlow:

tnp.experimental_enable_numpy_behavior()

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

Matriz TensorFlow NumPy ND

Um exemplo de tf.experimental.numpy.ndarray , chamado ND matriz, representa uma matriz multidimensional densa de um determinado dtype colocado sobre um determinado dispositivo. É um alias para tf.Tensor . Confira a classe Array ND para métodos úteis como ndarray.T , ndarray.reshape , ndarray.ravel e outros.

Primeiro crie um objeto de matriz ND e, em seguida, invoque 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)
Created ND array with shape = (5, 3), rank = 2, dtype = <dtype: 'float32'> on device = /job:localhost/replica:0/task:0/device:GPU:0

Is `ones` an instance of tf.Tensor: True

ndarray.T has shape (3, 5)
narray.reshape(-1) has shape (15,)

Promoção de tipo

As APIs TensorFlow NumPy têm semântica bem definida para converter literais em array ND, bem como para realizar promoção de tipo em entradas de array ND. Por favor, veja np.result_type para mais detalhes.

APIs TensorFlow deixar tf.Tensor entradas inalterado e não realizar a promoção de tipos sobre eles, enquanto APIs TensorFlow Numpy promover todas as entradas de acordo com regras de promoção do tipo Numpy. No próximo exemplo, você executará promoção de tipo. Primeiro, execute a adição em entradas de array ND de diferentes tipos e observe os tipos de saída. Nenhuma dessas promoções 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))
Type promotion for operations
int32 + int64 => int64
int32 + float32 => float64
int32 + float64 => float64
int64 + float32 => float64
int64 + float64 => float64
float32 + float64 => float64

Finalmente, literais converter para matriz ND usando ndarray.asarray e observar 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)
Type inference during array creation
tnp.asarray(1).dtype == tnp.int64
tnp.asarray(1.).dtype == tnp.float64

Ao converter literais a série ND, NumPy prefere largas tipos como tnp.int64 e tnp.float64 . Em contraste, tf.convert_to_tensor prefere tf.int32 e tf.float32 tipos para converter constantes para tf.Tensor . As APIs TensorFlow NumPy aderem ao comportamento NumPy para inteiros. Quanto aos carros alegóricos, o prefer_float32 argumento de experimental_enable_numpy_behavior permite controlar se a preferir tf.float32 sobre tf.float64 (padrão para 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)
When prefer_float32 is True:
tnp.asarray(1.).dtype == tnp.float32
tnp.add(1., 2.).dtype == tnp.float32
When prefer_float32 is False:
tnp.asarray(1.).dtype == tnp.float64
tnp.add(1., 2.).dtype == tnp.float64

Transmissão

Semelhante ao TensorFlow, o NumPy define uma semântica rica para valores de "transmissão". Você pode conferir o guia de radiodifusão NumPy para mais informações e comparar isso com TensorFlow transmitindo semântica .

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))
Broadcasting shapes (2, 3), (3,) and (1, 2, 1) gives shape (1, 2, 3)

Indexando

NumPy define regras de indexação muito sofisticadas. Veja o guia de indexação NumPy . Observe o uso de matrizes ND como índices 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])])
Basic indexing
tf.Tensor(
[[[16 17 18 19]
  [20 21 22 23]]], shape=(1, 2, 4), dtype=int64) 

Boolean indexing
tf.Tensor(
[[[ 0  1  2  3]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [20 21 22 23]]], shape=(2, 2, 4), dtype=int64) 

Advanced indexing
tf.Tensor([12 13 17], shape=(3,), dtype=int64)
# Mutation is currently not supported
try:
  tnp.arange(6)[1] = -1
except TypeError:
  print("Currently, TensorFlow NumPy does not support mutation.")
Currently, TensorFlow NumPy does not support mutation.

Modelo de exemplo

A seguir, você pode ver como criar um modelo e executar inferência nele. Este modelo simples aplica uma camada relu seguida por uma projeção linear. As seções seguintes mostram como calcular gradientes para este modelo usando de TensorFlow GradientTape .

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)))
tf.Tensor(
[[-1.7706785  1.1137733]
 [-1.7706785  1.1137733]], shape=(2, 2), dtype=float32)

TensorFlow NumPy e NumPy

TensorFlow NumPy implementa um subconjunto da especificação NumPy completa. Embora mais símbolos sejam adicionados ao longo do tempo, há recursos sistemáticos que não serão suportados em um futuro próximo. Estes incluem apoio NumPy C API, integração Swig, a fim de armazenamento Fortran, pontos de vista e stride_tricks , e alguns dtype s (como np.recarray e np.object ). Para mais detalhes, consulte o Documentação API TensorFlow NumPy .

Interoperabilidade NumPy

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

Da mesma forma, as funções TensorFlow Numpy pode aceitar entradas de diferentes tipos, incluindo np.ndarray . Estas entradas são convertidas para uma matriz ND chamando ndarray.asarray sobre eles.

A conversão da matriz ND de e para np.ndarray pode desencadear cópias de dados reais. Por favor, veja a seção sobre cópias de 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__))
sum = 6.0. Class: <class 'numpy.float64'>
sum = 6.0. Class: <class 'tensorflow.python.framework.ops.EagerTensor'>
# It is easy to plot ND arrays, given the __array__ interface.
labels = 15 + 2 * tnp.random.randn(1, 1000)
_ = plt.hist(labels)

png

Cópias de buffer

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

Quando um np.ndarray é passado para TensorFlow NumPy, ele irá verificar se há requisitos de alinhamento e desencadear uma cópia, se necessário. Ao passar um buffer de CPU de matriz ND para NumPy, geralmente o buffer irá satisfazer os requisitos de alinhamento e NumPy não precisará criar uma cópia.

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

Diante disso, a combinação com chamadas de API NumPy geralmente deve ser feita com cuidado e o usuário deve estar atento a sobrecargas de cópia de dados. Intercalar chamadas TensorFlow NumPy com chamadas TensorFlow geralmente é seguro e evita a cópia de dados. Veja a seção sobre TensorFlow interoperabilidade para mais detalhes.

Operador precedente

TensorFlow NumPy define um __array_priority__ maior do que NumPy de. Isto significa que para os operadores que envolvem tanto a matriz ND e np.ndarray , o ex terá precedência, ou seja, np.ndarray entrada será convertido para uma matriz de valores medidos e a implementação TensorFlow NumPy do operador vai ter invocado.

x = tnp.ones([2]) + np.ones([2])
print("x = %s\nclass = %s" % (x, x.__class__))
x = tf.Tensor([2. 2.], shape=(2,), dtype=float64)
class = <class 'tensorflow.python.framework.ops.EagerTensor'>

TF NumPy e TensorFlow

O TensorFlow NumPy é baseado no TensorFlow e, portanto, interopera perfeitamente com o TensorFlow.

tf.Tensor matriz e ND

ND matriz é uma abreviatura para tf.Tensor , assim, obviamente, que eles podem ser misturados sem accionar cópias de dados reais.

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__)
tf.Tensor([1 2], shape=(2,), dtype=int32)
tf.Tensor([1 2], shape=(2,), dtype=int32)
tf.Tensor([1 2], shape=(2,), dtype=int32)
[1 2] <class 'numpy.ndarray'>

Interoperabilidade do TensorFlow

Array Uma ND podem ser passados para APIs TensorFlow, desde série ND é apenas um alias para tf.Tensor . Conforme mencionado anteriormente, essa interoperação não faz cópias de dados, mesmo para dados colocados em aceleradores ou dispositivos remotos.

Por outro lado, tf.Tensor objectos podem ser passados para tf.experimental.numpy APIs, 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)
Output = tf.Tensor(6.0, shape=(), dtype=float32)
Output = tf.Tensor(6.0, shape=(), dtype=float32)

Gradientes e Jacobianos: tf.GradientTape

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

Usar o modelo criado no Exemplo Modelo secção, e calcular os 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)
Parameter shapes: [TensorShape([32, 64]), TensorShape([64]), TensorShape([64, 2])]
Gradient shapes: [TensorShape([32, 64]), TensorShape([64]), TensorShape([64, 2])]
# 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)
Output shape: (16, 2), input shape: (16, 32)
Batch jacobian shape: (16, 2, 32)

Compilação de rastreamento: tf.function

De TensorFlow tf.function funciona por "trace compilar" o código e, em seguida, otimizar esses traços para muito mais rápido desempenho. Veja a Introdução a Gráficos e funções .

tf.function pode ser utilizada para optimizar código TensorFlow NumPy bem. Aqui está um exemplo simples para demonstrar os aceleramentos. Note-se que o corpo de tf.function código inclui chamadas para TensorFlow Numpy APIs.

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")
Eager performance
1.291419400013183 ms

tf.function compiled performance
0.5561202000080812 ms

Vetorização: tf.vectorized_map

O TensorFlow tem suporte integrado para vetorizar loops paralelos, o que permite acelerações de uma a duas ordens de magnitude. Estes aumentos de velocidade são acessíveis através do tf.vectorized_map API e aplicar ao código TensorFlow NumPy assim.

Às vezes, é útil calcular o gradiente de cada saída em um lote em relação ao elemento de lote de entrada correspondente. Tal cálculo pode ser feito de forma eficiente utilizando tf.vectorized_map como 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))
Weight shape: (32, 64), batch size: 128, per example gradient shape: (128, 32, 64) 
Weight shape: (64,), batch size: 128, per example gradient shape: (128, 64) 
Weight shape: (64, 2), batch size: 128, per example gradient shape: (128, 64, 2)
# 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")
Running vectorized computation
0.5265710999992734 ms

Running unvectorized computation
40.35122630002661 ms

Posicionamento do dispositivo

O TensorFlow NumPy pode colocar operações em CPUs, GPUs, TPUs e dispositivos remotos. Ele usa mecanismos padrão do TensorFlow para o posicionamento do dispositivo. Abaixo, um exemplo simples mostra como listar todos os dispositivos e, em seguida, colocar alguns cálculos em um dispositivo específico.

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

Dispositivos de lista

tf.config.list_logical_devices e tf.config.list_physical_devices pode ser usado para encontrar o que dispositivos de 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"
All logical devices: [LogicalDevice(name='/device:CPU:0', device_type='CPU'), LogicalDevice(name='/device:GPU:0', device_type='GPU')]
All physical devices: [PhysicalDevice(name='/physical_device:CPU:0', device_type='CPU'), PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]

Operações de colocar: tf.device

As operações podem ser colocados em um dispositivo chamando-o em um tf.device escopo.

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)
Using device: LogicalDevice(name='/device:GPU:0', device_type='GPU')
prediction is placed on /job:localhost/replica:0/task:0/device:GPU:0

Copiar matrizes ND através de dispositivos: tnp.copy

Uma chamada para tnp.copy , colocado em um determinado escopo do dispositivo, irá copiar os dados para esse dispositivo, a menos que os dados já estão nesse dispositivo.

with tf.device("/device:CPU:0"):
  prediction_cpu = tnp.copy(prediction)
print(prediction.device)
print(prediction_cpu.device)
/job:localhost/replica:0/task:0/device:GPU:0
/job:localhost/replica:0/task:0/device:CPU:0

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ção, que se traduzem em melhorias de desempenho e memória. Veja otimização gráfico TensorFlow com Grappler para saber mais.

No entanto, o TensorFlow tem sobrecargas mais altas para operações de despacho em comparação com o NumPy. Para cargas de trabalho compostas de pequenas operações (menos de cerca de 10 microssegundos), essas sobrecargas podem dominar o tempo de execução e o NumPy pode fornecer melhor desempenho. Para outros casos, o TensorFlow geralmente deve fornecer melhor desempenho.

Execute o benchmark abaixo para comparar o desempenho do NumPy e do 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)

png

Leitura adicional