NumPy API บน TensorFlow

ดูใน TensorFlow.org เรียกใช้ใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดสมุดบันทึก

ภาพรวม

TensorFlow ใช้ชุดย่อยของ NumPy API ซึ่งมีให้ใน tf.experimental.numpy สิ่งนี้ช่วยให้สามารถรันโค้ด NumPy ซึ่งเร่งโดย TensorFlow ในขณะเดียวกันก็อนุญาตให้เข้าถึง API ทั้งหมดของ 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.5.0

การเปิดใช้งานพฤติกรรม NumPy

ในการใช้ tnp เป็น NumPy ให้เปิดใช้งานพฤติกรรม NumPy สำหรับ TensorFlow:

tnp.experimental_enable_numpy_behavior()

การเรียกนี้เปิดใช้งานการส่งเสริมประเภทใน TensorFlow และยังเปลี่ยนการอนุมานประเภทเมื่อแปลงตัวอักษรเป็นเทนเซอร์เพื่อให้เป็นไปตามมาตรฐาน NumPy อย่างเคร่งครัดมากขึ้น

อาร์เรย์ ND TensorFlow NumPy

อินสแตนซ์ของ tf.experimental.numpy.ndarray เรียกว่า ND Array แสดงถึงอาร์เรย์หนาแน่นหลายมิติของ dtype กำหนด 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 APIs มีความหมายที่กำหนดไว้อย่างดีสำหรับการแปลงตัวอักษรเป็นอาร์เรย์ ND เช่นเดียวกับการดำเนินการส่งเสริมประเภทบนอินพุตอาร์เรย์ ND โปรดดู np.result_type สำหรับรายละเอียดเพิ่มเติม

TensorFlow APIs ปล่อยให้ tf.Tensor อินพุตของ tf.Tensor ไม่เปลี่ยนแปลงและไม่ทำการส่งเสริมประเภทในขณะที่ TensorFlow NumPy APIs ส่งเสริมอินพุตทั้งหมดตามกฎการส่งเสริมประเภท NumPy ในตัวอย่างถัดไปคุณจะดำเนินการส่งเสริมประเภท ขั้นแรกให้รันการเพิ่มอินพุตอาร์เรย์ ND ประเภทต่างๆและจดบันทึกประเภทเอาต์พุต TensorFlow APIs ไม่อนุญาตการโปรโมตประเภทนี้

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

เมื่อแปลง tnp.int64 อาร์เรย์ ND NumPy จะชอบประเภทกว้าง ๆ เช่น tnp.int64 และ tnp.float64 ในทางตรงกันข้าม tf.convert_to_tensor ชอบ tf.int32 และ tf.float32 ประเภทสำหรับการแปลงค่าคงที่จะ tf.Tensor TensorFlow NumPy APIs เป็นไปตามลักษณะการทำงานของ NumPy สำหรับจำนวนเต็ม สำหรับการลอยตัวอาร์กิวเมนต์ prefer_float32 ของ experimental_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)
# 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 ตามด้วยการฉายภาพเชิงเส้น ส่วนต่อมาจะแสดงวิธีคำนวณการไล่ระดับสีสำหรับโมเดลนี้โดยใช้ GradientTape ของ TensorFlow

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(
[[-0.36533368 -1.5596298 ]
 [-0.36533368 -1.5596298 ]], shape=(2, 2), dtype=float32)

TensorFlow NumPy และ NumPy

TensorFlow NumPy ใช้ชุดย่อยของข้อมูลจำเพาะ NumPy แบบเต็ม แม้ว่าจะมีการเพิ่มสัญลักษณ์เพิ่มเติมเมื่อเวลาผ่านไป แต่ก็มีคุณลักษณะที่เป็นระบบซึ่งจะไม่ได้รับการสนับสนุนในอนาคตอันใกล้นี้ สิ่งเหล่านี้รวมถึงการสนับสนุน NumPy C API, การรวม Swig, ลำดับพื้นที่จัดเก็บ Fortran, มุมมองและ stride_tricks และ 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)

png

สำเนาบัฟเฟอร์

การผสม TensorFlow NumPy กับโค้ด NumPy อาจทำให้เกิดการคัดลอกข้อมูล เนื่องจาก TensorFlow NumPy มีข้อกำหนดที่เข้มงวดกว่าในการจัดตำแหน่งหน่วยความจำมากกว่า NumPy

เมื่อ np.ndarray ถูกส่งผ่านไปยัง TensorFlow NumPy ระบบจะตรวจสอบข้อกำหนดการจัดตำแหน่งและเรียกใช้สำเนาหากจำเป็น เมื่อส่งผ่านบัฟเฟอร์ซีพียูอาร์เรย์ ND ไปยัง NumPy โดยทั่วไปแล้วบัฟเฟอร์จะตอบสนองความต้องการในการจัดตำแหน่งและ NumPy ไม่จำเป็นต้องสร้างสำเนา

อาร์เรย์ ND สามารถอ้างถึงบัฟเฟอร์ที่วางอยู่บนอุปกรณ์อื่นที่ไม่ใช่หน่วยความจำ CPU ภายในเครื่อง ในกรณีเช่นนี้การเรียกใช้ฟังก์ชัน NumPy จะทริกเกอร์สำเนาผ่านเครือข่ายหรืออุปกรณ์ตามต้องการ

ด้วยเหตุนี้การผสมผสานกับการเรียก NumPy API โดยทั่วไปควรทำด้วยความระมัดระวังและผู้ใช้ควรระวังค่าใช้จ่ายในการคัดลอกข้อมูล การแทรกระหว่างการโทร TensorFlow NumPy ด้วยการโทร TensorFlow โดยทั่วไปปลอดภัยและหลีกเลี่ยงการคัดลอกข้อมูล ดูส่วนเกี่ยวกับความ สามารถในการทำงานร่วมกันของ TensorFlow สำหรับรายละเอียดเพิ่มเติม

ลำดับความสำคัญของตัวดำเนินการ

TensorFlow NumPy กำหนด __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 API ได้เนื่องจากอาร์เรย์ 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 สามารถใช้สำหรับ backpropagation ผ่านรหัส 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.function

ฟังก์ชัน tf. ของ tf.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
1.1682818999986466 ms

tf.function compiled performance
0.43585590000247976 ms

Vectorization: tf.vectorized_map

TensorFlow มีการสนับสนุนในตัวสำหรับ vectorizing ลูปคู่ขนานซึ่งช่วยให้เพิ่มความเร็วได้ตั้งแต่หนึ่งถึงสองลำดับ การ tf.vectorized_map ความเร็วเหล่านี้สามารถเข้าถึงได้ผ่าน tf.vectorized_map API และนำไปใช้กับรหัส TensorFlow NumPy ด้วย

บางครั้งก็มีประโยชน์ในการคำนวณการไล่ระดับสีของแต่ละเอาต์พุตในแบตช์ wrt องค์ประกอบแบตช์อินพุตที่เกี่ยวข้อง การคำนวณดังกล่าวสามารถทำได้อย่างมีประสิทธิภาพโดยใช้ 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.4654576999996607 ms

Running unvectorized computation
36.925241400001596 ms

ตำแหน่งอุปกรณ์

TensorFlow NumPy สามารถวางการทำงานบน CPU, GPU, 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')]
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 ที่ได้รับการปรับปรุงประสิทธิภาพสูงซึ่งสามารถส่งไปบน CPU, GPU และ TPU ได้ 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)

png

อ่านเพิ่มเติม