巨大な疎行列をmodel.fit()させようとした時、numpyのままだとメモリに乗り切らなかったため、scipyの疎行列でtf.kerasを使う方法がないか調べてみた。
まずは公式チュートリアルを見てみる。
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(
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属性に形状が格納されている。
これらを引数にして、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)
参考
コメント