
Почему 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.numpy
API, не выполняя копии данных.
# 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_map
API и примените также к коду 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
Первоначально опубликовано на
Оригинал