ดูบน TensorFlow.org | ทำงานใน Google Colab | ดูบน GitHub | ดาวน์โหลดโน๊ตบุ๊ค |
เมื่อทำงานกับเทนเซอร์ที่มีค่าศูนย์จำนวนมาก สิ่งสำคัญคือต้องจัดเก็บค่าเหล่านี้ไว้ในลักษณะที่ประหยัดพื้นที่และเวลา เมตริกซ์แบบกระจายช่วยให้สามารถจัดเก็บและประมวลผลเทนเซอร์ที่มีค่าศูนย์จำนวนมากได้อย่างมีประสิทธิภาพ เมตริกซ์แบบกระจายมีการใช้กันอย่างแพร่หลายในรูปแบบการเข้ารหัส เช่น TF-IDF โดยเป็นส่วนหนึ่งของการประมวลผลข้อมูลล่วงหน้าในแอปพลิเคชัน NLP และสำหรับการประมวลผลภาพล่วงหน้าที่มีพิกเซลมืดจำนวนมากในแอปพลิเคชันการมองเห็นด้วยคอมพิวเตอร์
เมตริกซ์แบบกระจายใน TensorFlow
TensorFlow แสดงถึงเทนเซอร์แบบเบาบางผ่านอ็อบเจ็กต์ tf.SparseTensor
ปัจจุบัน เมตริกซ์แบบกระจายใน TensorFlow ได้รับการเข้ารหัสโดยใช้รูปแบบรายการพิกัด (COO) รูปแบบการเข้ารหัสนี้ได้รับการปรับให้เหมาะสมสำหรับเมทริกซ์ที่มีการกระจายมากเกินไป เช่น การฝัง
การเข้ารหัส COO สำหรับเมตริกซ์แบบกระจายประกอบด้วย:
-
values
: เทนเซอร์ 1D ที่มีรูปร่าง[N]
ที่มีค่าที่ไม่ใช่ศูนย์ทั้งหมด -
indices
: เทนเซอร์ 2 มิติที่มีรูปร่าง[N, rank]
ซึ่งประกอบด้วยดัชนีของค่าที่ไม่ใช่ศูนย์ -
dense_shape
: เทนเซอร์ 1D ที่มีรูปร่าง[rank]
ระบุรูปร่างของเทนเซอร์
ค่าที่ ไม่ใช่ศูนย์ ในบริบทของ tf.SparseTensor
คือค่าที่ไม่ได้เข้ารหัสอย่างชัดเจน เป็นไปได้ที่จะรวมค่าศูนย์อย่างชัดเจนใน values
ของเมทริกซ์กระจัดกระจาย COO แต่โดยทั่วไป "ศูนย์ที่ชัดเจน" เหล่านี้จะไม่รวมอยู่ด้วยเมื่อพูดถึงค่าที่ไม่ใช่ศูนย์ในเมตริกซ์แบบกระจาย
การสร้าง tf.SparseTensor
สร้างเทนเซอร์แบบกระจัดกระจายโดยการระบุ values
indices
และ dense_shape
ของพวกมันโดยตรง
import tensorflow as tf
st1 = tf.SparseTensor(indices=[[0, 3], [2, 4]],
values=[10, 20],
dense_shape=[3, 10])
เมื่อคุณใช้ฟังก์ชัน print()
เพื่อพิมพ์เทนเซอร์แบบเบาบาง ฟังก์ชันนี้จะแสดงเนื้อหาของเทนเซอร์สามองค์ประกอบ:
print(st1)
SparseTensor(indices=tf.Tensor( [[0 3] [2 4]], shape=(2, 2), dtype=int64), values=tf.Tensor([10 20], shape=(2,), dtype=int32), dense_shape=tf.Tensor([ 3 10], shape=(2,), dtype=int64))
จะเข้าใจเนื้อหาของเทนเซอร์เบาบางได้ง่ายขึ้นหาก values
ที่ไม่ใช่ศูนย์สอดคล้องกับ indices
ที่เกี่ยวข้องกัน กำหนดฟังก์ชันตัวช่วยเพื่อพิมพ์เทนเซอร์แบบกระจัดกระจาย เพื่อให้แต่ละค่าที่ไม่ใช่ศูนย์แสดงบนบรรทัดของตัวเอง
def pprint_sparse_tensor(st):
s = "<SparseTensor shape=%s \n values={" % (st.dense_shape.numpy().tolist(),)
for (index, value) in zip(st.indices, st.values):
s += f"\n %s: %s" % (index.numpy().tolist(), value.numpy().tolist())
return s + "}>"
print(pprint_sparse_tensor(st1))
<SparseTensor shape=[3, 10] values={ [0, 3]: 10 [2, 4]: 20}>
คุณยังสามารถสร้างเทนเซอร์เบาบางจากเทนเซอร์หนาแน่นได้โดยใช้ tf.sparse.from_dense
และแปลงกลับเป็นเทนเซอร์หนาแน่นโดยใช้ tf.sparse.to_dense
st2 = tf.sparse.from_dense([[1, 0, 0, 8], [0, 0, 0, 0], [0, 0, 3, 0]])
print(pprint_sparse_tensor(st2))
<SparseTensor shape=[3, 4] values={ [0, 0]: 1 [0, 3]: 8 [2, 2]: 3}>
st3 = tf.sparse.to_dense(st2)
print(st3)
tf.Tensor( [[1 0 0 8] [0 0 0 0] [0 0 3 0]], shape=(3, 4), dtype=int32)
การจัดการเทนเซอร์กระจัดกระจาย
ใช้ยูทิลิตีในแพ็คเกจ tf.sparse
เพื่อจัดการเทนเซอร์แบบเบาบาง Ops เช่น tf.math.add
ที่คุณสามารถใช้สำหรับการจัดการเลขคณิตของเทนเซอร์ที่มีความหนาแน่นสูงจะไม่ทำงานกับเทนเซอร์แบบเบาบาง
เพิ่มเทนเซอร์ที่มีรูปร่างเหมือนกันโดยใช้ tf.sparse.add
st_a = tf.SparseTensor(indices=[[0, 2], [3, 4]],
values=[31, 2],
dense_shape=[4, 10])
st_b = tf.SparseTensor(indices=[[0, 2], [7, 0]],
values=[56, 38],
dense_shape=[4, 10])
st_sum = tf.sparse.add(st_a, st_b)
print(pprint_sparse_tensor(st_sum))
<SparseTensor shape=[4, 10] values={ [0, 2]: 87 [3, 4]: 2 [7, 0]: 38}>
ใช้ tf.sparse.sparse_dense_matmul
เพื่อคูณเทนเซอร์แบบกระจายด้วยเมทริกซ์หนาแน่น
st_c = tf.SparseTensor(indices=([0, 1], [1, 0], [1, 1]),
values=[13, 15, 17],
dense_shape=(2,2))
mb = tf.constant([[4], [6]])
product = tf.sparse.sparse_dense_matmul(st_c, mb)
print(product)
tf.Tensor( [[ 78] [162]], shape=(2, 1), dtype=int32)
ใส่ sparse tensor เข้าด้วยกันโดยใช้ tf.sparse.concat
และแยกส่วนโดยใช้ tf.sparse.slice
sparse_pattern_A = tf.SparseTensor(indices = [[2,4], [3,3], [3,4], [4,3], [4,4], [5,4]],
values = [1,1,1,1,1,1],
dense_shape = [8,5])
sparse_pattern_B = tf.SparseTensor(indices = [[0,2], [1,1], [1,3], [2,0], [2,4], [2,5], [3,5],
[4,5], [5,0], [5,4], [5,5], [6,1], [6,3], [7,2]],
values = [1,1,1,1,1,1,1,1,1,1,1,1,1,1],
dense_shape = [8,6])
sparse_pattern_C = tf.SparseTensor(indices = [[3,0], [4,0]],
values = [1,1],
dense_shape = [8,6])
sparse_patterns_list = [sparse_pattern_A, sparse_pattern_B, sparse_pattern_C]
sparse_pattern = tf.sparse.concat(axis=1, sp_inputs=sparse_patterns_list)
print(tf.sparse.to_dense(sparse_pattern))
tf.Tensor( [[0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0] [0 0 0 0 1 1 0 0 0 1 1 0 0 0 0 0 0] [0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0] [0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0] [0 0 0 0 1 1 0 0 0 1 1 0 0 0 0 0 0] [0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0]], shape=(8, 17), dtype=int32)
sparse_slice_A = tf.sparse.slice(sparse_pattern_A, start = [0,0], size = [8,5])
sparse_slice_B = tf.sparse.slice(sparse_pattern_B, start = [0,5], size = [8,6])
sparse_slice_C = tf.sparse.slice(sparse_pattern_C, start = [0,10], size = [8,6])
print(tf.sparse.to_dense(sparse_slice_A))
print(tf.sparse.to_dense(sparse_slice_B))
print(tf.sparse.to_dense(sparse_slice_C))
tf.Tensor( [[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 1] [0 0 0 1 1] [0 0 0 1 1] [0 0 0 0 1] [0 0 0 0 0] [0 0 0 0 0]], shape=(8, 5), dtype=int32) tf.Tensor( [[0] [0] [1] [1] [1] [1] [0] [0]], shape=(8, 1), dtype=int32) tf.Tensor([], shape=(8, 0), dtype=int32)
หากคุณใช้ TensorFlow 2.4 หรือสูงกว่า ให้ใช้ tf.sparse.map_values
สำหรับการดำเนินการตามองค์ประกอบบนค่าที่ไม่ใช่ศูนย์ในเทนเซอร์แบบเบาบาง
st2_plus_5 = tf.sparse.map_values(tf.add, st2, 5)
print(tf.sparse.to_dense(st2_plus_5))
tf.Tensor( [[ 6 0 0 13] [ 0 0 0 0] [ 0 0 8 0]], shape=(3, 4), dtype=int32)
โปรดทราบว่ามีการแก้ไขเฉพาะค่าที่ไม่ใช่ศูนย์ – ค่าศูนย์ยังคงเป็นศูนย์
ในทำนองเดียวกัน คุณสามารถทำตามรูปแบบการออกแบบด้านล่างสำหรับ TensorFlow เวอร์ชันก่อนหน้าได้:
st2_plus_5 = tf.SparseTensor(
st2.indices,
st2.values + 5,
st2.dense_shape)
print(tf.sparse.to_dense(st2_plus_5))
tf.Tensor( [[ 6 0 0 13] [ 0 0 0 0] [ 0 0 8 0]], shape=(3, 4), dtype=int32)ตัวยึดตำแหน่ง23
การใช้ tf.SparseTensor
กับ TensorFlow API อื่นๆ
เมตริกซ์แบบกระจายทำงานอย่างโปร่งใสกับ TensorFlow API เหล่านี้:
-
tf.keras
-
tf.data
-
tf.Train.Example
ตัวอย่าง protobuf -
tf.function
-
tf.while_loop
-
tf.cond
-
tf.identity
-
tf.cast
-
tf.print
-
tf.saved_model
-
tf.io.serialize_sparse
-
tf.io.serialize_many_sparse
-
tf.io.deserialize_many_sparse
-
tf.math.abs
-
tf.math.negative
-
tf.math.sign
-
tf.math.square
-
tf.math.sqrt
-
tf.math.erf
-
tf.math.tanh
-
tf.math.bessel_i0e
-
tf.math.bessel_i1e
ตัวอย่างแสดงไว้ด้านล่างสำหรับ API บางส่วนข้างต้น
tf.keras
ชุดย่อยของ tf.keras
API รองรับเทนเซอร์แบบเบาบางโดยไม่ต้องแคสต์หรือการแปลงค่า ops ที่มีราคาแพง Keras API ให้คุณส่งผ่านเทนเซอร์แบบเบาบางเป็นอินพุตไปยังโมเดล Keras Set sparse=True
เมื่อโทร tf.keras.Input
หรือ tf.keras.layers.InputLayer
คุณสามารถส่งเทนเซอร์แบบกระจัดกระจายระหว่างเลเยอร์ Keras และให้โมเดล Keras ส่งคืนเป็นเอาต์พุตได้ หากคุณใช้เทนเซอร์แบบเบาบางในเลเยอร์ tf.keras.layers.Dense
ในโมเดลของคุณ เลเยอร์เหล่านี้จะแสดงเทนเซอร์ที่มีความหนาแน่นสูง
ตัวอย่างด้านล่างแสดงวิธีส่งเทนเซอร์เบาบางเป็นอินพุตไปยังโมเดล Keras หากคุณใช้เฉพาะเลเยอร์ที่รองรับอินพุตแบบเบาบางเท่านั้น
x = tf.keras.Input(shape=(4,), sparse=True)
y = tf.keras.layers.Dense(4)(x)
model = tf.keras.Model(x, y)
sparse_data = tf.SparseTensor(
indices = [(0,0),(0,1),(0,2),
(4,3),(5,0),(5,1)],
values = [1,1,1,1,1,1],
dense_shape = (6,4)
)
model(sparse_data)
model.predict(sparse_data)
array([[-1.3111044 , -1.7598825 , 0.07225233, -0.44544357], [ 0. , 0. , 0. , 0. ], [ 0. , 0. , 0. , 0. ], [ 0. , 0. , 0. , 0. ], [ 0.8517609 , -0.16835624, 0.7307872 , -0.14531797], [-0.8916302 , -0.9417639 , 0.24563438, -0.9029659 ]], dtype=float32)
tf.data
tf.data
API ช่วยให้คุณสร้างไพพ์ไลน์อินพุตที่ซับซ้อนจากชิ้นส่วนที่เรียบง่ายและนำกลับมาใช้ใหม่ได้ โครงสร้างข้อมูลหลักของมันคือ tf.data.Dataset
ซึ่งแสดงถึงลำดับขององค์ประกอบที่แต่ละองค์ประกอบประกอบด้วยส่วนประกอบตั้งแต่หนึ่งองค์ประกอบขึ้นไป
การสร้างชุดข้อมูลที่มีเทนเซอร์เบาบาง
สร้างชุดข้อมูลจากเทนเซอร์แบบกระจายโดยใช้วิธีการเดียวกับที่ใช้สร้างจาก tf.Tensor
หรือ NumPy เช่น tf.data.Dataset.from_tensor_slices
op นี้จะรักษาข้อมูลที่กระจัดกระจาย (หรือลักษณะเบาบาง) ของข้อมูล
dataset = tf.data.Dataset.from_tensor_slices(sparse_data)
for element in dataset:
print(pprint_sparse_tensor(element))
<SparseTensor shape=[4] values={ [0]: 1 [1]: 1 [2]: 1}> <SparseTensor shape=[4] values={}> <SparseTensor shape=[4] values={}> <SparseTensor shape=[4] values={}> <SparseTensor shape=[4] values={ [3]: 1}> <SparseTensor shape=[4] values={ [0]: 1 [1]: 1}>
แบทช์และเลิกแบทช์ชุดข้อมูลที่มีเทนเซอร์เบาบาง
คุณสามารถแบทช์ (รวมองค์ประกอบที่ต่อเนื่องกันเป็นองค์ประกอบเดียว) และเลิกแบทช์ชุดข้อมูลที่มีเทนเซอร์แบบกระจายโดยใช้ Dataset.batch
และ Dataset.unbatch
ตามลำดับ
batched_dataset = dataset.batch(2)
for element in batched_dataset:
print (pprint_sparse_tensor(element))
<SparseTensor shape=[2, 4] values={ [0, 0]: 1 [0, 1]: 1 [0, 2]: 1}> <SparseTensor shape=[2, 4] values={}> <SparseTensor shape=[2, 4] values={ [0, 3]: 1 [1, 0]: 1 [1, 1]: 1}>
unbatched_dataset = batched_dataset.unbatch()
for element in unbatched_dataset:
print (pprint_sparse_tensor(element))
<SparseTensor shape=[4] values={ [0]: 1 [1]: 1 [2]: 1}> <SparseTensor shape=[4] values={}> <SparseTensor shape=[4] values={}> <SparseTensor shape=[4] values={}> <SparseTensor shape=[4] values={ [3]: 1}> <SparseTensor shape=[4] values={ [0]: 1 [1]: 1}>
คุณยังสามารถใช้ tf.data.experimental.dense_to_sparse_batch
เพื่อแบทช์องค์ประกอบชุดข้อมูลของรูปร่างที่แตกต่างกันไปเป็นเทนเซอร์แบบกระจาย
การแปลงชุดข้อมูลด้วยเทนเซอร์เบาบาง
แปลงและสร้างเทนเซอร์แบบกระจายในชุดข้อมูลโดยใช้ Dataset.map
transform_dataset = dataset.map(lambda x: x*2)
for i in transform_dataset:
print(pprint_sparse_tensor(i))
<SparseTensor shape=[4] values={ [0]: 2 [1]: 2 [2]: 2}> <SparseTensor shape=[4] values={}> <SparseTensor shape=[4] values={}> <SparseTensor shape=[4] values={}> <SparseTensor shape=[4] values={ [3]: 2}> <SparseTensor shape=[4] values={ [0]: 2 [1]: 2}>ตัวยึดตำแหน่ง33
tf.train.ตัวอย่าง
tf.train.Example
คือการเข้ารหัส protobuf มาตรฐานสำหรับข้อมูล TensorFlow เมื่อใช้เมตริกซ์แบบกระจายกับ tf.train.Example
คุณสามารถ:
อ่านข้อมูลความยาวผันแปรลงใน
tf.SparseTensor
โดยใช้tf.io.VarLenFeature
อย่างไรก็ตาม คุณควรพิจารณาใช้tf.io.RaggedFeature
แทนอ่านข้อมูลกระจัดกระจายตามอำเภอใจลงใน
tf.SparseTensor
โดยใช้tf.io.SparseFeature
ซึ่งใช้ปุ่มคุณสมบัติสามปุ่มแยกกันเพื่อจัดเก็บindices
values
และdense_shape
tf.function
มัณฑนากร tf.function
จะคำนวณกราฟ TensorFlow ล่วงหน้าสำหรับฟังก์ชัน Python ซึ่งสามารถปรับปรุงประสิทธิภาพของโค้ด TensorFlow ของคุณได้อย่างมาก เมตริกซ์แบบกระจายทำงานอย่างโปร่งใสกับทั้ง tf.function
และ ฟังก์ชันที่เป็นรูปธรรม
@tf.function
def f(x,y):
return tf.sparse.sparse_dense_matmul(x,y)
a = tf.SparseTensor(indices=[[0, 3], [2, 4]],
values=[15, 25],
dense_shape=[3, 10])
b = tf.sparse.to_dense(tf.sparse.transpose(a))
c = f(a,b)
print(c)
tf.Tensor( [[225 0 0] [ 0 0 0] [ 0 0 625]], shape=(3, 3), dtype=int32)
แยกแยะค่าที่หายไปจากค่าศูนย์
ops ส่วนใหญ่บน tf.SparseTensor
จะจัดการกับค่าที่หายไปและค่าศูนย์ที่ชัดเจนเหมือนกัน นี่คือการออกแบบ — tf.SparseTensor
ควรจะทำหน้าที่เหมือนกับเทนเซอร์ที่มีความหนาแน่นสูง
อย่างไรก็ตาม มีบางกรณีที่การแยกค่าศูนย์ออกจากค่าที่ขาดหายไปอาจเป็นประโยชน์ โดยเฉพาะอย่างยิ่ง วิธีนี้ช่วยให้มีวิธีหนึ่งในการเข้ารหัสข้อมูลที่ขาดหายไป/ไม่รู้จักในข้อมูลการฝึกของคุณ ตัวอย่างเช่น พิจารณากรณีการใช้งานที่คุณมีเทนเซอร์ของคะแนน (ซึ่งสามารถมีค่าทศนิยมจาก -Inf ถึง +Inf) โดยมีคะแนนหายไปบางส่วน คุณสามารถเข้ารหัสเทนเซอร์นี้ได้โดยใช้เทนเซอร์แบบเบาบาง โดยที่ค่าศูนย์ที่ชัดเจนนั้นเรียกว่าคะแนนเป็นศูนย์ แต่ค่าศูนย์โดยนัยจะแสดงข้อมูลที่ขาดหายไปจริง ๆ ไม่ใช่ศูนย์
โปรดทราบว่า ops บางอย่างเช่น tf.sparse.reduce_max
จะไม่ถือว่าค่าที่หายไปเหมือนกับว่าเป็นศูนย์ ตัวอย่างเช่น เมื่อคุณเรียกใช้บล็อคโค้ดด้านล่าง ผลลัพธ์ที่คาดไว้คือ 0
อย่างไรก็ตาม เนื่องจากข้อยกเว้นนี้ เอาต์พุตจึงเป็น -3
print(tf.sparse.reduce_max(tf.sparse.from_dense([-5, 0, -3])))
tf.Tensor(-3, shape=(), dtype=int32)
ในทางตรงกันข้าม เมื่อคุณใช้ tf.math.reduce_max
กับเทนเซอร์ที่หนาแน่น เอาต์พุตจะเป็น 0 ตามที่คาดไว้
print(tf.math.reduce_max([-5, 0, -3]))
tf.Tensor(0, shape=(), dtype=int32)ตัวยึดตำแหน่ง39
อ่านเพิ่มเติมและทรัพยากร
- ดู คู่มือเทนเซอร์ เพื่อเรียนรู้เกี่ยวกับเมตริกซ์
- อ่าน คู่มือเทนเซอร์แบบขาด ๆ เพื่อเรียนรู้วิธีทำงานกับเมตริกซ์แบบขาดๆ ซึ่งเป็นประเภทของเมตริกซ์ที่ช่วยให้คุณทำงานกับข้อมูลที่ไม่สม่ำเสมอได้
- ลองดูโมเดลการตรวจจับวัตถุนี้ใน TensorFlow Model Garden ที่ใช้เมตริกซ์แบบกระจายใน ตัวถอดรหัสข้อมูล
tf.Example