Почему Tensorflow Numpy может быть будущим дифференцируемого программирования
25 июля 2025 г.Обзор контента
TF Numpy и Tensorflow
Tf.tensor и ND Array
Tensorflow Feelsectionbility
Градиенты и якобийцы: tf.gradienttape
Следование компиляции: tf.function
Векторизация: TF.Vectorized_map
Размещение устройства
Сравнение производительности
Дальнейшее чтение
TF Numpy и Tensorflow
Tensorflow Numpy строится на вершине Tensorflow и, следовательно, беспрепятственно взаимодействует с Tensorflow.
tf.Tensorи nd массив
Nd массив - псевдоним дляtf.Tensor, так что, очевидно, они могут быть смешаны, не запуская фактические копии данных.
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'>
Tensorflow Feelsectionbility
Nd массив может быть передана API -интерфейсам Tensorflow, так как nd массив - просто псевдоним дляtf.TensorПолем Как упоминалось ранее, такое взаимодействие не выполняет копии данных, даже для данных, размещенных на ускорителях или удаленных устройствах.
Наоборот,tf.TensorОбъекты могут быть переданы вtf.experimental.numpyAPI, не выполняя копии данных.
# 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)
Градиенты и якобийцы: tf.gradienttape
GradientTape Tensorflow может использоваться для обратного распространения через Tensorflow и Tensorflow Numpy Code.
Используйте модель, созданную вПример модельраздел, и вычислить градиенты и якобийцы.
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)
Следование компиляции: tf.function
Tensorflow'stf.functionРаботает по «следом» кода, а затем оптимизирует эти следы для гораздо более высокой производительности. УвидетьВведение в графики и функцииПолем
tf.functionМожет также использоваться для оптимизации кода Tensorflow Numpy. Вот простой пример, чтобы продемонстрировать ускорение. Обратите внимание, что телоtf.functionКод включает в себя вызовы Tensorflow Numpy API.
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
2.710705300000882 ms
tf.function compiled performance
0.7041131000050882 ms
Векторизация: TF.Vectorized_map
Tensorflow обладает встроенной поддержкой векторизационных параллельных петлей, что позволяет ускорить от одного до двух порядков. Эти ускорения доступны черезtf.vectorized_mapAPI и примените также к коду TensorFlow Numpy.
Иногда полезно вычислить градиент каждого вывода в партии W.R.T. Соответствующий элемент входной партии. Такие вычисления могут быть выполнены эффективно, используяtf.vectorized_mapКак показано ниже.
@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.659675699989748 ms
Running unvectorized computation
29.259711299982882 ms
Размещение устройства
Tensorflow Numpy может поставить операции на процессоров, графических процессоров, TPU и удаленных устройств. Он использует стандартные механизмы Tensorflow для размещения устройств. Ниже простой пример показан, как перечислить все устройства, а затем разместить некоторые вычисления на конкретное устройство.
Tensorflow также имеет API для репликации вычислений на разных устройствах и выполнения коллективных сокращений, которые здесь не будут покрыты.
Список устройств
tf.config.list_logical_devicesиtf.config.list_physical_devicesможно использовать, чтобы найти, какие устройства использовать.
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'), LogicalDevice(name='/device:GPU:1', device_type='GPU'), LogicalDevice(name='/device:GPU:2', device_type='GPU'), LogicalDevice(name='/device:GPU:3', device_type='GPU')]
All physical devices: [PhysicalDevice(name='/physical_device:CPU:0', device_type='CPU'), PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU'), PhysicalDevice(name='/physical_device:GPU:1', device_type='GPU'), PhysicalDevice(name='/physical_device:GPU:2', device_type='GPU'), PhysicalDevice(name='/physical_device:GPU:3', device_type='GPU')]
Расположение операций:tf.device
Операции могут быть размещены на устройстве, позвонив в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)
Using device: LogicalDevice(name='/device:GPU:0', device_type='GPU')
prediction is placed on /job:localhost/replica:0/task:0/device:GPU:0
Копирование ND массивов на устройствах:tnp.copy
Призыв кtnp.copy, помещенное в определенную область устройства, скопирует данные на это устройство, если данные уже не на этом устройстве.
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
Сравнение производительности
Tensorflow Numpy использует высоко оптимизированные ядра Tensorflow, которые могут быть отправлены на процессоров, GPU и TPU. Tensorflow также выполняет множество оптимизаций компилятора, таких как операция Fusion, которые приводят к улучшению производительности и памяти. ВидетьОптимизация графика tensorflow с GrapplerЧтобы узнать больше.
Однако Tensorflow имеет более высокие накладные расходы на отправку операций по сравнению с Numpy. Для рабочих нагрузок, состоящих из небольших операций (менее чем около 10 микросекунд), эти накладные расходы могут доминировать во время выполнения, а Numpy может обеспечить лучшую производительность. Для других случаев TensorFlow, как правило, должен обеспечивать лучшую производительность.
Запустите эталон ниже, чтобы сравнить производительность Numpy и Tensorflow Numpy для различных размеров ввода.
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)

Дальнейшее чтение
- Tensorflow Numpy: учебник по классификации распределенных изображений
- Tensorflow Numpy: кера и стратегия распределения
- Анализ настроений с Trax и Tensorflow Numpy
Первоначально опубликовано на
Оригинал