Seyrek tensörlerle çalışma

TensorFlow.org'da görüntüleyin Google Colab'da çalıştırın GitHub'da görüntüle Not defterini indir

Çok sayıda sıfır değeri içeren tensörlerle çalışırken, bunları yer ve zaman açısından verimli bir şekilde depolamak önemlidir. Seyrek tensörler, çok sayıda sıfır değeri içeren tensörlerin verimli bir şekilde depolanmasını ve işlenmesini sağlar. Seyrek tensörler, NLP uygulamalarında veri ön işlemenin bir parçası olarak TF-IDF gibi kodlama şemalarında ve bilgisayarla görme uygulamalarında çok sayıda koyu piksel içeren görüntülerin ön işlemesi için yaygın olarak kullanılmaktadır.

TensorFlow'da seyrek tensörler

TensorFlow, tf.SparseTensor nesnesi aracılığıyla seyrek tensörleri temsil eder. Şu anda, TensorFlow'daki seyrek tensörler, koordinat listesi (COO) formatı kullanılarak kodlanmıştır. Bu kodlama biçimi, yerleştirmeler gibi aşırı seyrek matrisler için optimize edilmiştir.

Seyrek tensörler için COO kodlaması şunlardan oluşur:

  • values : Tüm sıfır olmayan değerleri içeren [N] şeklinde bir 1D tensör.
  • indices : Sıfır olmayan değerlerin indekslerini içeren [N, rank] şeklinde bir 2B tensör.
  • dense_shape : Tensörün şeklini belirten [rank] şekilli bir 1D tensör.

tf.SparseTensor bağlamında sıfır olmayan bir değer, açıkça kodlanmamış bir değerdir. Bir COO seyrek matrisinin values açıkça sıfır değerleri eklemek mümkündür, ancak bu "açık sıfırlar" genellikle bir seyrek tensörde sıfır olmayan değerlere atıfta bulunulduğunda dahil edilmez.

Bir tf.SparseTensor oluşturma

values , indices ve dense_shape doğrudan belirterek seyrek tensörler oluşturun.

import tensorflow as tf
tutucu1 l10n-yer
st1 = tf.SparseTensor(indices=[[0, 3], [2, 4]],
                      values=[10, 20],
                      dense_shape=[3, 10])

Bir seyrek tensörü yazdırmak için print() işlevini kullandığınızda, üç bileşen tensörünün içeriğini gösterir:

print(st1)
tutucu3 l10n-yer
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))

Sıfırdan farklı values karşılık gelen indices hizalanırsa, seyrek bir tensörün içeriğini anlamak daha kolaydır. Seyrek tensörleri, sıfırdan farklı her değer kendi satırında gösterilecek şekilde güzel bir şekilde yazdırmak için bir yardımcı işlev tanımlayın.

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))
-yer tutucu6 l10n-yer
<SparseTensor shape=[3, 10] 
 values={
  [0, 3]: 10
  [2, 4]: 20}>

Ayrıca tf.sparse.from_dense kullanarak yoğun tensörlerden seyrek tensörler oluşturabilir ve tf.sparse.from_dense kullanarak bunları yoğun tensörlere 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}>
yer tutucu9 l10n-yer
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)

Seyrek tensörleri manipüle etme

Seyrek tensörleri işlemek için tf.sparse paketindeki yardımcı programları kullanın. Yoğun tensörlerin aritmetik manipülasyonu için kullanabileceğiniz tf.math.add gibi işlemler seyrek tensörlerle çalışmaz.

tf.sparse.add kullanarak aynı şekle sahip seyrek tensörler ekleyin.

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))
tutucu12 l10n-yer
<SparseTensor shape=[4, 10] 
 values={
  [0, 2]: 87
  [3, 4]: 2
  [7, 0]: 38}>

Seyrek tensörleri yoğun matrislerle çarpmak için tf.sparse.sparse_dense_matmul kullanın.

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)
tutucu14 l10n-yer
tf.Tensor(
[[ 78]
 [162]], shape=(2, 1), dtype=int32)

Seyrek tensörleri tf.sparse.concat kullanarak bir araya getirin ve tf.sparse.concat kullanarak 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)
yer tutucu17 l10n-yer
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 veya üstünü kullanıyorsanız, seyrek tensörlerde sıfır olmayan değerler üzerinde eleman bazında işlemler için tf.sparse.map_values kullanın.

st2_plus_5 = tf.sparse.map_values(tf.add, st2, 5)
print(tf.sparse.to_dense(st2_plus_5))
tutucu20 l10n-yer
tf.Tensor(
[[ 6  0  0 13]
 [ 0  0  0  0]
 [ 0  0  8  0]], shape=(3, 4), dtype=int32)

Yalnızca sıfır olmayan değerlerin değiştirildiğini unutmayın - sıfır değerleri sıfır olarak kalır.

Benzer şekilde, TensorFlow'un önceki sürümleri için aşağıdaki tasarım modelini takip edebilirsiniz:

st2_plus_5 = tf.SparseTensor(
    st2.indices,
    st2.values + 5,
    st2.dense_shape)
print(tf.sparse.to_dense(st2_plus_5))
tutucu22 l10n-yer
tf.Tensor(
[[ 6  0  0 13]
 [ 0  0  0  0]
 [ 0  0  8  0]], shape=(3, 4), dtype=int32)

Diğer TensorFlow API'leri ile tf.SparseTensor kullanma

Seyrek tensörler, bu TensorFlow API'leriyle şeffaf bir şekilde çalışır:

Yukarıdaki API'lerden birkaçı için örnekler aşağıda gösterilmiştir.

tf.keras

tf.keras API'sinin bir alt kümesi, pahalı döküm veya dönüştürme işlemleri olmadan seyrek tensörleri destekler. Keras API, seyrek tensörleri bir Keras modeline girdi olarak geçirmenizi sağlar. tf.keras.Input veya tf.keras.layers.InputLayer çağrılırken sparse=True ayarlayın. Seyrek tensörleri Keras katmanları arasında geçirebilir ve ayrıca Keras modellerinin bunları çıktı olarak döndürmesini sağlayabilirsiniz. tf.keras.layers.Dense katmanlarında seyrek tensörler kullanırsanız, bunlar yoğun tensörler verir.

Aşağıdaki örnek, yalnızca seyrek girdileri destekleyen katmanlar kullanıyorsanız, bir Keras modeline girdi olarak seyrek bir tensörü nasıl ileteceğinizi gösterir.

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)
tutucu24 l10n-yer
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, basit, yeniden kullanılabilir parçalardan karmaşık girdi ardışık düzenleri oluşturmanıza olanak tanır. Temel veri yapısı, her öğenin bir veya daha fazla bileşenden oluştuğu bir dizi öğeyi temsil eden tf.data.Dataset .

Seyrek tensörlerle veri kümeleri oluşturma

Veri kümelerini tf.Tensor s veya NumPy dizilerinden oluşturmak için kullanılan yöntemlerin aynısını kullanarak seyrek tensörlerden veri kümeleri oluşturun, örneğin tf.data.Dataset.from_tensor_slices . Bu işlem, verilerin seyrekliğini (veya seyrek doğasını) korur.

dataset = tf.data.Dataset.from_tensor_slices(sparse_data)
for element in dataset: 
  print(pprint_sparse_tensor(element))
tutucu26 l10n-yer
<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}>

Seyrek tensörlerle veri kümelerini gruplama ve gruplamadan çıkarma

Sırasıyla Dataset.batch ve Dataset.unbatch yöntemlerini kullanarak toplu işleyebilir (ardışık öğeleri tek bir öğede birleştirebilir) ve seyrek tensörlerle veri kümelerini çözebilirsiniz.

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}>
yer tutucu29 l10n-yer
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 , farklı şekillerdeki veri kümesi öğelerini seyrek tensörler halinde gruplamak için de kullanabilirsiniz.

Seyrek tensörlerle Veri Kümelerini Dönüştürme

Dataset.map kullanarak Veri Kümelerinde seyrek tensörleri dönüştürün ve oluşturun.

transform_dataset = dataset.map(lambda x: x*2)
for i in transform_dataset:
  print(pprint_sparse_tensor(i))
tutucu32 l10n-yer
<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.tren.Örnek

tf.train.Example , TensorFlow verileri için standart bir protobuf kodlamasıdır. tf.train.Example ile seyrek tensörler kullanırken şunları yapabilirsiniz:

tf.function

tf.function dekoratörü, TensorFlow kodunuzun performansını önemli ölçüde artırabilen Python işlevleri için TensorFlow grafiklerini önceden hesaplar. Seyrek tensörler hem tf.function hem de somut işlevlerle şeffaf bir şekilde çalışır.

@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)
tutucu34 l10n-yer
tf.Tensor(
[[225   0   0]
 [  0   0   0]
 [  0   0 625]], shape=(3, 3), dtype=int32)

Eksik değerleri sıfır değerlerinden ayırt etme

tf.SparseTensor s üzerindeki çoğu işlem, eksik değerleri ve açık sıfır değerlerini aynı şekilde ele alır. Bu tasarım gereğidir — bir tf.SparseTensor tıpkı yoğun bir tensör gibi davranması gerekir.

Ancak, sıfır değerleri eksik değerlerden ayırmanın faydalı olabileceği birkaç durum vardır. Özellikle, bu, eğitim verilerinizdeki eksik/bilinmeyen verileri kodlamanın bir yolunu sağlar. Örneğin, puanların tensörünün (-Inf ile +Inf arasında herhangi bir kayan nokta değerine sahip olabilen) ve bazı eksik puanların olduğu bir kullanım durumunu düşünün. Bu tensörü, açık sıfırların bilinen sıfır puanları olduğu ancak örtülü sıfır değerlerinin aslında eksik verileri temsil ettiği ve sıfırı temsil etmediği seyrek bir tensör kullanarak kodlayabilirsiniz.

tf.sparse.reduce_max gibi bazı işlemlerin eksik değerleri tf.sparse.reduce_max gibi işlemediğini unutmayın. Örneğin, aşağıdaki kod bloğunu çalıştırdığınızda, beklenen çıktı 0 . Ancak, bu istisna nedeniyle çıktı -3 .

print(tf.sparse.reduce_max(tf.sparse.from_dense([-5, 0, -3])))
tutucu36 l10n-yer
tf.Tensor(-3, shape=(), dtype=int32)

Buna karşılık, yoğun bir tf.math.reduce_max uyguladığınızda, çıktı beklendiği gibi 0 olur.

print(tf.math.reduce_max([-5, 0, -3]))
tutucu38 l10n-yer
tf.Tensor(0, shape=(), dtype=int32)

Daha fazla okuma ve kaynaklar