NumPy API على TensorFlow

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

ملخص

ينفذ TensorFlow مجموعة فرعية من NumPy API ، والمتاحة كـ tf.experimental.numpy . يسمح هذا بتشغيل كود NumPy ، مع تسريع TensorFlow ، مع السماح أيضًا بالوصول إلى جميع واجهات برمجة تطبيقات TensorFlow.

يثبت

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

تمكين سلوك NumPy

من أجل استخدام tnp كـ NumPy ، قم بتمكين سلوك NumPy لـ TensorFlow:

tnp.experimental_enable_numpy_behavior()

تتيح هذه المكالمة ترقية النوع في TensorFlow وتغير أيضًا استدلال النوع ، عند تحويل القيم الحرفية إلى موترات ، لاتباع معيار NumPy بشكل أكثر صرامة.

مجموعة TensorFlow NumPy ND

يمثل مثيل tf.experimental.numpy.ndarray ، المسمى ND Array ، مصفوفة كثيفة متعددة الأبعاد dtype معين يتم وضعه على جهاز معين. إنه اسم مستعار لـ tf.Tensor . تحقق من فئة مصفوفة ND للحصول على طرق مفيدة مثل ndarray.T و ndarray.reshape و ndarray.ravel وغيرها.

قم أولاً بإنشاء كائن مصفوفة ND ، ثم قم باستدعاء طرق مختلفة.

# 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,)

اكتب ترقية

تحتوي واجهات برمجة تطبيقات TensorFlow NumPy على دلالات محددة جيدًا لتحويل القيم الحرفية إلى مصفوفة ND ، وكذلك لأداء ترقية النوع على مدخلات مصفوفة ND. الرجاء مراجعة np.result_type لمزيد من التفاصيل.

تترك tf.Tensor API مدخلات tf.Tensor دون تغيير ولا تقوم بترقية النوع عليها ، بينما تقوم TensorFlow NumPy APIs بترقية جميع المدخلات وفقًا لقواعد الترويج من نوع NumPy. في المثال التالي ، ستقوم بإجراء ترقية النوع. أولاً ، قم بتشغيل الإضافة على مدخلات مصفوفة ND من أنواع مختلفة ولاحظ أنواع المخرجات. لن تسمح واجهات برمجة تطبيقات 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

أخيرًا ، قم بتحويل القيم الحرفية إلى مصفوفة ND باستخدام ndarray.asarray ولاحظ النوع الناتج.

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

عند تحويل القيم الحرفية إلى مصفوفة ND ، يفضل NumPy أنواعًا واسعة مثل tnp.int64 و tnp.float64 . في المقابل ، يفضل tf.convert_to_tensor نوعي tf.int32 و tf.float32 لتحويل الثوابت إلى tf.Tensor . تلتزم واجهات برمجة تطبيقات TensorFlow NumPy بسلوك NumPy للأعداد الصحيحة. بالنسبة إلى العوامات ، تتيح لك الوسيطة prefer_float32 experimental_enable_numpy_behavior enable_numpy_behavior التحكم في ما إذا كنت تفضل tf.float32 على tf.float64 (افتراضيًا على False ). فمثلا:

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

البث

على غرار TensorFlow ، يعرف NumPy دلالات غنية لقيم "البث". يمكنك الاطلاع على دليل البث NumPy لمزيد من المعلومات ومقارنة ذلك بدلالات البث 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))
Broadcasting shapes (2, 3), (3,) and (1, 2, 1) gives shape (1, 2, 3)

الفهرسة

تحدد NumPy قواعد فهرسة معقدة للغاية. راجع دليل فهرسة NumPy . لاحظ استخدام مصفوفات ND كمؤشرات أدناه.

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)
l10n-placeholder16 l10n-placeholder17 l10n-placeholder17l10n-placeholder17 l10n-placeholder15
# 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.

نموذج نموذج

بعد ذلك ، يمكنك معرفة كيفية إنشاء نموذج وتشغيل الاستدلال عليه. يطبق هذا النموذج البسيط طبقة relu متبوعة بإسقاط خطي. ستوضح الأقسام اللاحقة كيفية حساب التدرجات لهذا النموذج باستخدام شريط التدرج TensorFlow's 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 و NumPy

تنفذ TensorFlow NumPy مجموعة فرعية من مواصفات NumPy الكاملة. بينما سيتم إضافة المزيد من الرموز بمرور الوقت ، هناك ميزات منهجية لن يتم دعمها في المستقبل القريب. يتضمن ذلك دعم NumPy C API ، وتكامل Swig ، وترتيب تخزين Fortran ، وطرق العرض و stride_tricks ، وبعض dtype (مثل np.recarray و np.object ). لمزيد من التفاصيل ، يرجى الاطلاع على وثائق TensorFlow NumPy API .

إمكانية التشغيل المتداخل مع NumPy

يمكن أن تتفاعل مصفوفات TensorFlow ND مع وظائف NumPy. تقوم هذه الكائنات بتطبيق واجهة __array__ . يستخدم NumPy هذه الواجهة لتحويل وسيطات الدالة إلى قيم np.ndarray قبل معالجتها.

وبالمثل ، يمكن أن تقبل وظائف TensorFlow NumPy مدخلات من أنواع مختلفة بما في ذلك np.ndarray . يتم تحويل هذه المدخلات إلى مصفوفة ND عن طريق استدعاء ndarray.asarray عليها.

قد يؤدي تحويل مجموعة ND من np.ndarray إلى نسخ البيانات الفعلية. يرجى الاطلاع على قسم النسخ الاحتياطية لمزيد من التفاصيل.

# 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)

بي إن جي

نسخ المخزن المؤقت

قد يؤدي خلط TensorFlow NumPy مع كود NumPy إلى تشغيل نسخ البيانات. هذا لأن TensorFlow NumPy له متطلبات أكثر صرامة على محاذاة الذاكرة من تلك الخاصة بـ NumPy.

عندما يتم تمرير np.ndarray إلى TensorFlow NumPy ، فسوف يتحقق من متطلبات المحاذاة ويقوم بتشغيل نسخة إذا لزم الأمر. عند تمرير المخزن المؤقت لوحدة المعالجة المركزية لصفيف ND إلى NumPy ، فإن المخزن المؤقت يفي بمتطلبات المحاذاة بشكل عام ولن يحتاج NumPy إلى إنشاء نسخة.

يمكن أن تشير صفائف ND إلى المخازن المؤقتة الموضوعة على أجهزة غير ذاكرة وحدة المعالجة المركزية المحلية. في مثل هذه الحالات ، سيؤدي استدعاء وظيفة NumPy إلى تشغيل نسخ عبر الشبكة أو الجهاز حسب الحاجة.

بالنظر إلى ذلك ، يجب أن يتم الاختلاط مع استدعاءات NumPy API بشكل عام بحذر ويجب على المستخدم الانتباه إلى النفقات العامة لنسخ البيانات. يعتبر Interleaving TensorFlow NumPy مع مكالمات TensorFlow آمنًا بشكل عام ويتجنب نسخ البيانات. راجع القسم الخاص بإمكانية التشغيل التفاعلي TensorFlow لمزيد من التفاصيل.

أسبقية المشغل

يحدد __array_priority__ أعلى من NumPy. هذا يعني أنه بالنسبة للمشغلين الذين يشتملون على كل من مصفوفة ND و np.ndarray ، فإن الأول سيكون له الأسبقية ، على سبيل المثال ، سيتم تحويل إدخال np.ndarray إلى مصفوفة ND وسيتم استدعاء تنفيذ TensorFlow NumPy للمشغل.

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 و 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

يمكن تمرير مصفوفة ND إلى TensorFlow APIs ، نظرًا لأن مصفوفة ND هي مجرد اسم مستعار لـ tf.Tensor . كما ذكرنا سابقًا ، لا يقوم هذا التشغيل المتداخل بنسخ البيانات ، حتى بالنسبة للبيانات الموضوعة على المسرّعات أو الأجهزة البعيدة.

على العكس من ذلك ، يمكن تمرير كائنات tf.Tensor إلى واجهات برمجة تطبيقات tf.experimental.numpy ، دون إجراء نسخ البيانات.

# 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

يمكن استخدام شريط التدرج في TensorFlow للانتشار العكسي من خلال كود TensorFlow و TensorFlow NumPy.

استخدم النموذج الذي تم إنشاؤه في قسم النموذج النموذجي ، واحسب التدرجات والجاكوبيون.

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

تعمل وظيفة tf.f. في tf.function من خلال "تتبع تجميع" الشفرة ثم تحسين هذه التتبع للحصول على أداء أسرع بكثير. راجع مقدمة الرسوم البيانية والوظائف .

يمكن استخدام وظيفة tf لتحسين كود tf.function NumPy أيضًا. فيما يلي مثال بسيط لتوضيح عمليات التعجيل. لاحظ أن نص كود tf.function يتضمن استدعاءات لواجهات برمجة تطبيقات 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")
Eager performance
1.291419400013183 ms

tf.function compiled performance
0.5561202000080812 ms

التوجيه: tf.vectorized_map

يحتوي TensorFlow على دعم داخلي لتوجيه الحلقات المتوازية ، مما يسمح بزيادة السرعة بمقدار واحد إلى اثنين. يمكن الوصول إلى هذه التسريع عبر واجهة برمجة تطبيقات tf.vectorized_map وتنطبق على كود TensorFlow NumPy أيضًا.

من المفيد أحيانًا حساب التدرج اللوني لكل ناتج في دُفعة ، حيث يتم كتابة عنصر دُفعة الإدخال المقابل. يمكن إجراء هذا الحساب بكفاءة باستخدام 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)
l10n-placeholder37 l10n-placeholder38l10n-placeholder35 l10n-placeholder36
# 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

وضع الجهاز

يمكن لـ TensorFlow NumPy إجراء عمليات على وحدات المعالجة المركزية ووحدات معالجة الرسومات و TPU والأجهزة البعيدة. يستخدم آليات TensorFlow القياسية لوضع الجهاز. يوجد أدناه مثال بسيط يوضح كيفية سرد جميع الأجهزة ثم وضع بعض العمليات الحسابية على جهاز معين.

يحتوي TensorFlow أيضًا على واجهات برمجة تطبيقات لتكرار الحساب عبر الأجهزة وإجراء تخفيضات جماعية لن يتم تناولها هنا.

قائمة الأجهزة

يمكن استخدام 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')]
All physical devices: [PhysicalDevice(name='/physical_device:CPU:0', device_type='CPU'), PhysicalDevice(name='/physical_device:GPU:0', 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 مُحسّنة للغاية والتي يمكن إرسالها على وحدات المعالجة المركزية ووحدات معالجة الرسومات ووحدات المعالجة المركزية. ينفذ TensorFlow أيضًا العديد من تحسينات المترجم ، مثل اندماج العملية ، والتي تُترجم إلى تحسينات في الأداء والذاكرة. راجع تحسين الرسم البياني 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)

بي إن جي

قراءة متعمقة