הצג באתר TensorFlow.org | הפעל בגוגל קולאב | הצג ב-GitHub | הורד מחברת |
כשעובדים עם טנזורים המכילים הרבה ערכים אפס, חשוב לאחסן אותם בצורה יעילה במרחב ובזמן. טנזורים דלילים מאפשרים אחסון ועיבוד יעיל של טנזורים המכילים הרבה ערכים אפסיים. טנזורים דלילים נמצאים בשימוש נרחב בסכימות קידוד כמו TF-IDF כחלק מעיבוד מוקדם של נתונים ביישומי NLP ועבור עיבוד מקדים של תמונות עם הרבה פיקסלים כהים ביישומי ראייה ממוחשבת.
טנסורים דלילים ב- TensorFlow
TensorFlow מייצג טנזורים דלילים דרך האובייקט tf.SparseTensor
. נכון לעכשיו, טנזורים דלילים ב- TensorFlow מקודדים באמצעות פורמט רשימת הקואורדינטות (COO). פורמט קידוד זה מותאם למטריצות דלילות במיוחד כגון הטבעות.
קידוד ה-COO עבור טנזורים דלילים מורכב מ:
-
values
: טנזור 1D עם צורה[N]
המכיל את כל הערכים שאינם אפס. -
indices
: טנזור דו-ממדי עם צורה[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
כדי לתפעל טנסור דל. אופציות כמו 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)
חבר טנסור דל יחד באמצעות 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)
שימוש ב- tf.SparseTensor
עם ממשקי API אחרים של TensorFlow
טנזורים דלילים עובדים בשקיפות עם ממשקי API אלה של TensorFlow:
-
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
תת-קבוצה של ה-API של tf.keras
תומכת בטנזורים דלילים ללא פעולות ליהוק או המרה יקרות. ה-API של Keras מאפשר לך להעביר טנזורים דלילים ככניסות לדגם Keras. הגדר 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
ה-API של tf.data
מאפשר לך לבנות צינורות קלט מורכבים מחלקים פשוטים הניתנים לשימוש חוזר. מבנה הנתונים הליבה שלו הוא tf.data.Dataset
, המייצג רצף של אלמנטים שבהם כל אלמנט מורכב ממרכיב אחד או יותר.
בניית מערכי נתונים עם טנזורים דלילים
בנו מערכי נתונים מטנזורים דלילים באותן שיטות המשמשות לבנייתם tf.Tensor
s או NumPy, כגון tf.data.Dataset.from_tensor_slices
. פעולה זו משמרת את הדלילות (או האופי הדליל) של הנתונים.
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}>
tf.train.דוגמה
tf.train.Example
הוא קידוד פרוטובוף סטנדרטי עבור נתוני 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)
הבחנה בין ערכים חסרים לאפס ערכים
רוב האופציות ב- tf.SparseTensor
מתייחסות לערכים חסרים ולערכים אפס מפורשים באופן זהה. זה בתכנון - tf.SparseTensor
אמור לפעול בדיוק כמו טנזור צפוף.
עם זאת, ישנם כמה מקרים שבהם זה יכול להיות שימושי להבחין בין ערכים אפס לערכים חסרים. בפרט, זה מאפשר דרך אחת לקודד נתונים חסרים/לא ידועים בנתוני האימון שלך. לדוגמה, שקול מקרה שימוש שבו יש לך טנסור של ציונים (שיכול להיות לו כל ערך של נקודה צפה מ-Inf עד +Inf), עם כמה ציונים חסרים. אתה יכול לקודד טנזור זה באמצעות טנזור דל שבו האפסים המפורשים הם ציוני אפס ידועים, אך ערכי האפס המרומזים למעשה מייצגים נתונים חסרים ולא אפס.
שימו לב שחלק מהאופציות כמו 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)
קריאה נוספת ומשאבים
- עיין במדריך טנסור כדי ללמוד על טנסור.
- קרא את מדריך הטנזור המרופט כדי ללמוד כיצד לעבוד עם טנסור מרופט, סוג של טנזור המאפשר לך לעבוד עם נתונים לא אחידים.
- בדוק את מודל זיהוי האובייקטים הזה בגן המודלים של TensorFlow המשתמש בטנסורים דלילים במפענח נתונים
tf.Example
.