TensorFlowでscipyのsparse matrixを学習に使う

巨大な疎行列をmodel.fit()させようとした時、numpyのままだとメモリに乗り切らなかったため、scipyの疎行列でtf.kerasを使う方法がないか調べてみた。

まずは公式チュートリアルを見てみる。

Working with sparse tensors  |  TensorFlow Core

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

tf.keras APIのサブセットは、高価なキャストや変換操作なしでスパーステンソルをサポートします。 Keras APIを使用すると、スパーステンソルをKerasモデルへの入力として渡すことができます。 tf.keras.Inputまたはtf.keras.layers.InputLayerを呼び出すときは、 sparse=Trueを設定します。 Kerasレイヤー間でスパーステンソルを渡すことができ、Kerasモデルにそれらを出力として返すようにすることもできます。モデルのtf.keras.layers.Denseレイヤーでスパーステンソルを使用すると、デンソルテンソルが出力されます。

COOはcoordinates形式の頭文字。

tf.SparseTensorを用意し、sparse=TrueにしたInputLayerに入力すれば良さそう。

チュートリアルのモデルを参考に、scipyのsparse matrixからtf.SparseTensorへの変換を目指す。

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)

tf.SparseTensorの作り方を見てみる。

tf.sparse.SparseTensor  |  TensorFlow v2.15.0.post1
Represents a sparse tensor.

tf.sparse.SparseTensor(
indices, values, dense_shape
)

indices: A 2-D int64 tensor of shape [N, ndims], which specifies the indices of the elements in the sparse tensor that contain nonzero values (elements are zero-indexed). For example, indices=[[1,3], [2,4]] specifies that the elements with indexes of [1,3] and [2,4] have nonzero values.

values: A 1-D tensor of any type and shape [N], which supplies the values for each element in indices. For example, given indices=[[1,3], [2,4]], the parameter values=[18, 3.6] specifies that element [1,3] of the sparse tensor has a value of 18, and element [2,4] of the tensor has a value of 3.6.

dense_shape: A 1-D int64 tensor of shape [ndims], which specifies the dense_shape of the sparse tensor. Takes a list indicating the number of elements in each dimension. For example, dense_shape=[3,6] specifies a two-dimensional 3×6 tensor, dense_shape=[2,3,4] specifies a three-dimensional 2x3x4 tensor, and dense_shape=[9] specifies a one-dimensional tensor with 9 elements.

3つの引数を与える必要がありそう。

scipyのcoo_matrixはrow属性とcol属性に座標が、data属性に値が、shape属性に形状が格納されている。

scipy.sparse.coo_matrix — SciPy v1.12.0 Manual

これらを引数にして、tf.SparseTensorを作る関数を組み立てれば完成。

def convert_sparse_matrix_to_sparse_tensor(sparse_matrix):
    coo = sparse_matrix.tocoo()
    indices = np.mat([coo.row, coo.col]).transpose()

    return tf.SparseTensor(indices, coo.data, coo.shape)

InputLayerでsparse=Trueにし、model.fitすれば疎行列で学習することができた。

tf.keras.Input(shape=shape, sparse=True)

参考

Scipy sparse CSR matrix to TensorFlow SparseTensor - Mini-Batch gradient descent
I have a Scipy sparse CSR matrix created from sparse TF-IDF feature matrix in SVM-Light format. The number of features i...

コメント

タイトルとURLをコピーしました