عرض على 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)