スパーステンソルの操作

TensorFlow.orgで表示 GoogleColabで実行 GitHubで表示 ノートブックをダウンロード

ゼロ値を多く含むテンソルを使用する場合は、スペースと時間の効率的な方法でテンソルを格納することが重要です。スパーステンソルは、多くのゼロ値を含むテンソルの効率的な保存と処理を可能にします。疎なテンソルは、同様のスキームコードに広く使用されているTF-IDFをデータの一部としてNLPアプリケーション及びコンピュータビジョン用途における暗いピクセルの多くの前処理画像に対して処理を事前。

TensorFlowのスパーステンソル

TensorFlowを介して疎テンソルを表すtf.SparseTensorオブジェクト。現在、TensorFlowのスパーステンソルは、座標リスト(COO)形式を使用してエンコードされています。このエンコード形式は、埋め込みなどの超疎行列用に最適化されています。

スパーステンソルのCOOエンコーディングは次のもので構成されます。

  • values :A形状の一次元テンソル[N] 、すべての非ゼロ値を含みます。
  • indices :形状A 2次元テンソル[N, rank] 、ゼロ以外の値のインデックスを含みます。
  • dense_shape :A形状の一次元テンソル[rank] 、テンソルの形状を特定します。

文脈でゼロ以外のtf.SparseTensor明示的にエンコードされていない値です。これは、ゼロの値明示的に含めることが可能であるvalues疎テンソルの非ゼロ値を参照するときCOO疎行列の、これらの「明示的なゼロ」は、一般的には含まれません。

作成tf.SparseTensor

直接その指定することにより、スパーステンソルを構築valuesindices 、およびdense_shape

import tensorflow as tf
2021-08-02 22:26:28.816491: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
st1 = tf.SparseTensor(indices=[[0, 3], [2, 4]],
                      values=[10, 20],
                      dense_shape=[3, 10])
2021-08-02 22:26:30.010920: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcuda.so.1
2021-08-02 22:26:30.662384: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-02 22:26:30.663256: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:00:05.0 name: Tesla V100-SXM2-16GB computeCapability: 7.0
coreClock: 1.53GHz coreCount: 80 deviceMemorySize: 15.78GiB deviceMemoryBandwidth: 836.37GiB/s
2021-08-02 22:26:30.663292: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-08-02 22:26:30.666888: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublas.so.11
2021-08-02 22:26:30.666978: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublasLt.so.11
2021-08-02 22:26:30.668092: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcufft.so.10
2021-08-02 22:26:30.668397: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcurand.so.10
2021-08-02 22:26:30.669374: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusolver.so.11
2021-08-02 22:26:30.670279: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusparse.so.11
2021-08-02 22:26:30.670461: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudnn.so.8
2021-08-02 22:26:30.670562: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-02 22:26:30.671467: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-02 22:26:30.672272: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-08-02 22:26:30.673044: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2021-08-02 22:26:30.673610: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-02 22:26:30.674535: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:00:05.0 name: Tesla V100-SXM2-16GB computeCapability: 7.0
coreClock: 1.53GHz coreCount: 80 deviceMemorySize: 15.78GiB deviceMemoryBandwidth: 836.37GiB/s
2021-08-02 22:26:30.674632: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-02 22:26:30.675658: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-02 22:26:30.676544: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-08-02 22:26:30.676594: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-08-02 22:26:31.265646: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1258] Device interconnect StreamExecutor with strength 1 edge matrix:
2021-08-02 22:26:31.265682: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1264]      0 
2021-08-02 22:26:31.265691: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1277] 0:   N 
2021-08-02 22:26:31.265892: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-02 22:26:31.266972: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-02 22:26:31.267844: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-02 22:26:31.268678: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1418] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 14646 MB memory) -> physical GPU (device: 0, name: Tesla V100-SXM2-16GB, pci bus id: 0000:00:05.0, compute capability: 7.0)

あなたが使用している場合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他のTensorFlow APIと

スパーステンソルは、次のTensorFlowAPIで透過的に機能します。

上記のAPIのいくつかの例を以下に示します。

tf.keras

サブセットtf.keras APIは、高価なキャスティングまたは変換OPSなしスパーステンソルをサポートしています。 Keras APIを使用すると、スパーステンソルを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)
2021-08-02 22:26:31.955169: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:176] None of the MLIR Optimization Passes are enabled (registered 2)
2021-08-02 22:26:31.956298: I tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 2000194999 Hz
array([[ 0.10812265,  1.0938541 ,  0.6068977 ,  0.4741317 ],
       [ 0.        ,  0.        ,  0.        ,  0.        ],
       [ 0.        ,  0.        ,  0.        ,  0.        ],
       [ 0.        ,  0.        ,  0.        ,  0.        ],
       [-0.3234046 , -0.20483321, -0.2781328 , -0.62424386],
       [ 0.29001385,  1.0482688 ,  0.41638905,  0.04135793]],
      dtype=float32)

tf.data

tf.data APIは、単純な、再利用可能なものから複雑な入力パイプラインを構築することができます。そのコアデータ構造であるtf.data.Dataset各要素は、1つのまたは複数のコンポーネントから構成されている要素のシーケンスを表します。

スパーステンソルを使用したデータセットの構築

それらを構築するために使用されるのと同じ方法を用いて疎なテンソルからビルド・データセット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.batchDataset.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.Example

tf.train.Example TensorFlowデータのための標準いるProtobufエンコーディングです。スパーステンソルを使用する場合はtf.train.Example 、次のことができます。

tf.function

tf.functionデコレータ事前計算TensorFlowグラフ、実質的にあなたのTensorFlowコードのパフォーマンスを向上させることができますPythonの機能のため。スパーステンソルは、両方で透過的に動作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ちょうど密テンソルのように行動することになっています。

ただし、ゼロ値と欠落値を区別すると便利な場合がいくつかあります。特に、これにより、トレーニングデータ内の欠落/不明なデータをエンコードする1つの方法が可能になります。たとえば、スコアのテンソル(-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)

さらなる読み物とリソース