如何在张量流中的两个张量中创建频率张量

时间:2020-03-16 18:05:38

标签: python tensorflow

我有一个像这样的张量,其中的值是频率,行是索引(0 to 6):

tf_docs = 
[[0, 2],
 [1, 2],
 [2, 1],
 [5, 0],
 [0, 1],
 [7, 8],
 [9, 6]]

我有一个常数张量,其中张量的值是索引:

tf_topics = tf.constant([[1 2]
                        [1 3]
                        [1 0]
                        [2 3]
                        [2 0]
                        [3 0]
                        [3 4]
                        [3 2]
                        [3 1]
                        [4 2]
                        [4 1]
                        [2 1]], shape=(12, 2), dtype=int32)

我需要在tf_docs中逐行检查这些索引,结果矩阵将是tf_docs中其中两个索引都不为零的列数。

例如,我们在[1 2]中有tf_topics。这意味着检查1中行索引2tf_docs中的值。在tf_docs的第一和第二列中,两个值都不为零。这就是为什么[1 2]的频率为2的原因。

另一方面,[1,3]得到1作为频率。因为索引3的第二列中的值之一为零。

所以结果将是这样的张量(这显然是对称的)。对角线将是每个index的频率之和:

[[2,   1, 1, 0, null],
 [1,   3, 2, 1, 1   ],
 [1,   2, 3, 1, 1   ],
 [0,   1, 1, 5, 0   ],
 [null,1, 1, 0, 1   ]]

到目前为止我所做的:

我决定在两个矩阵上使用tf.gathertf.count_nonzero。因为我想将index中的topics拆分,看看这些indexes是否同时出现在tf_docs

tf.math.count_nonzero(tf.gather(tf_docs, tf_topics, axis=0), axis=1)

尽管如此,这似乎并没有给我我想要的结果。

2 个答案:

答案 0 :(得分:2)

nonzero_tf_docs定义为:

zero_tf_docs = tf.cast(tf.equal(tf_docs, tf.zeros_like(tf_docs)), tf.int32)
nonzero_tf_docs = 1 - tf.reduce_max(zero_tf_docs, axis=-1)

OP要求计算nonzero_tf_docs[i] + nonzero_tf_docs[j]中每对索引i, j的总和tf_topics并将结果显示在矩阵中。这可以通过以下方式实现:

def compute_result(tf_topics_, nonzero_tf_docs, tf_docs):
    # Find matrix lower part
    values = tf.reduce_sum(tf.gather(nonzero_tf_docs, tf_topics_), axis=-1)
    max_index = tf.reduce_max(tf_topics) + 1
    out_sparse = tf.sparse.SparseTensor(indices=tf_topics_, values=values, dense_shape=[max_index, max_index])
    out_sparse = tf.cast(out_sparse, dtype=tf.int32)
    out_sparse = tf.sparse.reorder(out_sparse)
    out_dense = tf.sparse.to_dense(out_sparse, default_value=-1)
    out_lower = tf.matrix_band_part(out_dense, -1, 0)

    # Compute diagonal
    diag_values = tf.reduce_sum(tf_docs, axis=-1)
    diag = tf.slice(diag_values,
                    begin=[0],
                    size=[max_index])

    # Construct output matrix
    out = out_lower + tf.transpose(out_lower)
    mask = tf.eye(max_index, dtype=tf.int32)
    out = (1 - mask) * out + mask * diag

    return out


# Find docs without zeros
zero_tf_docs = tf.cast(tf.equal(tf_docs, tf.zeros_like(tf_docs)), tf.int32)
nonzero_tf_docs = 1 - tf.reduce_max(zero_tf_docs, axis=-1)

# Transform counts into matrix format
tf_topics = tf.cast(tf_topics, dtype=tf.int64)
tf_topics_reversed = tf.reverse(tf_topics, [-1])
tf_topics_ = tf_topics_reversed
out_1 = compute_result(tf_topics_, nonzero_tf_docs, tf_docs)
out_2 = compute_result(tf_topics, nonzero_tf_docs, tf_docs)
out = tf.maximum(out_1, out_2)

with tf.Session() as sess:
    r = sess.run(out)
    print(r)  # prints [[ 2  1  1  0 -1]
              #         [ 1  3  2  1  1]
              #         [ 1  2  3  1  1]
              #         [ 0  1  1  5  0]
              #         [-1  1  1  0  1]]

答案 1 :(得分:2)

感谢您的最新修改和rvinas' answer,我想我终于了解了您的需求。使我感到困惑的一件事是,输出矩阵中存在“空”单元格。无论如何,这是一种方法:

import tensorflow as tf

def freq_matrix(tf_docs, tf_topics):
    tf_docs = tf.convert_to_tensor(tf_docs)
    tf_topics = tf.convert_to_tensor(tf_topics)
    # Sort indices to make upper diagonal
    tf_topics = tf.sort(tf_topics, axis=1)
    # Largest index
    m = tf.reduce_max(tf_topics) + 1
    # Remove duplicates
    topics_flat = tf_topics[:, 0] * m + tf_topics[:, 1]
    topics_uniq, _ = tf.unique(topics_flat)
    tf_topics = tf.stack([topics_uniq // m, topics_uniq % m], axis=1)
    # Make diagonal
    diag = tf.reduce_sum(tf_docs[:m], axis=1)
    # Find non-zero positions in docs
    docs_nz = tf.not_equal(tf_docs, 0)
    # Get for each pair
    docs_g = tf.gather(docs_nz, tf_topics)
    # Find number of matches
    matches = tf.math.logical_and(docs_g[:, 0], docs_g[:, 1])
    freq = tf.reduce_sum(tf.dtypes.cast(matches, tf_docs.dtype), axis=1)
    # Add one to all values to subtract one at the end
    diag += 1
    freq += 1
    # Make upper diagonal
    out = tf.scatter_nd(tf_topics, freq, [m, m])
    # Make symmetric
    out += tf.transpose(out)
    # Add diagonal
    out += tf.linalg.diag(diag)
    # Subtract one to mark empty cells
    out -= 1
    return out

# Test
tf_docs = tf.constant([[0, 2], [1, 2], [2, 1], [5, 0], [0, 1], [7, 8], [9, 6]])
tf_topics = tf.constant([[1, 2], [1, 3], [1, 0], [2, 3], [2, 0], [3, 0],
                         [3, 4], [3, 2], [3, 1], [4, 2], [4, 1], [2, 1]])
print(freq_matrix(tf_docs, tf_topics).numpy())
# [[ 2  1  1  0 -1]
#  [ 1  3  2  1  1]
#  [ 1  2  3  1  1]
#  [ 0  1  1  5  0]
#  [-1  1  1  0  1]]