【问题标题】:Function to sparsify a matrix given a specific block size在给定特定块大小的情况下稀疏矩阵的函数
【发布时间】:2021-05-25 07:04:03
【问题描述】:

问题陈述

我正在尝试编写一个函数,该函数将在给定目标稀疏度和一个名为block_shape 的参数的情况下对矩阵进行稀疏化,该参数定义了矩阵中零块的最小大小。目标不必完美实现,但要尽可能接近。

例如,给定以下参数,

>>> matrix = [
  [1, 1, 1, 1],
  [1, 1, 1, 1],
  [1, 1, 1, 1],
  [1, 1, 1, 1]
]
>>> target = 0.5
>>> block_shape = (2, 2)

50% 稀疏度的有效输出可能是

>>> sparse_matrix = sparsify(matrix, target, block_shape)
>>> sparse_matrix
[
  [1, 1, 0, 0],
  [1, 1, 0, 0],
  [0, 0, 1, 1],
  [0, 0, 1, 1]
]

>>> sparse_matrix = sparsify(matrix, target, block_shape)
>>> sparse_matrix
[
  [1, 0, 0, 1],
  [1, 0, 0, 1],
  [0, 0, 1, 1],
  [0, 0, 1, 1]
]

请注意,输入可能有多个有效的稀疏版本。唯一的标准是尽可能多地到达目标。约束之一是只有形状 block_size 的零点被认为是稀疏的。

例如,在给定参数的情况下,下面的矩阵的稀疏度为 0%

>>> sparse_matrix = sparsify(matrix, target, block_shape)
>>> sparse_matrix
[
  [1, 0, 0, 1],
  [1, 1, 0, 0],
  [0, 1, 1, 1],
  [0, 0, 0, 0]
]

到目前为止我所拥有的

目前,我有以下代码

import numpy as np

def sparsify(matrix, target, block_shape=None):
    if block_shape is None or block_shape == 1 or block_shape == (1,) or block_shape == (1, 1):
        # 1x1 is just bernoulli with p=target
        probs = np.random.uniform(size=matrix.shape)
        mask = np.zeros(matrix.shape)
        mask[probs >= target] = 1.0
    else:
        if isinstance(block_shape, int):
            block_shape = (block_shape, block_shape)
        if len(block_shape) == 1:
            block_shape = (block_shape[0], block_shape[0])
        mask = np.ones(matrix.shape)
        rows, cols = matrix.shape
        for row in range(rows):
            for col in range(cols):
                submask = mask[row:row+block_shape[0], col:col+block_shape[1]]
                if submask.shape != block_shape:
                    # we don't care about the edges, cannot partially sparsify
                    continue
                if (submask == 0).any():
                    # If current (row, col) is already in the sparsified area, skip
                    continue
                prob = np.random.random()
                if prob < target:
                    submask[:, :] = np.zeros(submask.shape)
    return matrix * mask, mask

上面代码的问题是,如果块大小不是(1, 1),则与目标不匹配

>>> matrix = np.random.randn(100, 100)
>>> matrix, mask = sparsify(matrix, target=0.5, block_shape=(2, 2))

>>> print((matrix == 0).mean())
0.73
>>> print((mask == 0).mean())
0.73

差异的原因(我认为)

我不确定为什么我没有得到我期望的目标,但我认为这与我检查每个元素的概率而不是整个块的概率有关。但是,我的代码中有跳过条件,所以我认为应该涵盖它

编辑

编辑 1 -- 附加示例

只是再举一些例子。

示例1:给定不同的块大小

>>> sparse_matrix = sparsify(matrix, 0.25, (3, 3))
>>> sparse_matrix
[
  [0, 0, 0, 1],
  [0, 0, 0, 1],
  [0, 0, 0, 1],
  [1, 1, 1, 1]
]

上面的例子是一个有效的稀疏矩阵,虽然稀疏度不是 25%,但另一个有效的结果可能是一个全 1 的矩阵。

示例 2:给定不同的块大小和目标

>>> sparse_matrix = sparsify(matrix, 0.6, (1, 2))
>>> sparse_matrix
[
  [0, 0, 0, 0],
  [1, 0, 0, 1],
  [0, 0, 1, 1],
  [1, 1, 0, 0]
]

请注意,(1, 2) 的块中可以放入所有零,并且稀疏度 = 60%

编辑 2 -- 忘记了约束

另一个我忘记提及但尝试合并到我的代码中的约束是零块必须不重叠。

示例 1:以下结果无效

>>> sparse_matrix = sparsify(matrix, 0.5, (2, 2))
>>> sparse_matrix
[
  [0, 0, 1, 1],
  [0, 0, 0, 1],
  [1, 0, 0, 1],
  [1, 1, 1, 1]
]

虽然从索引(0, 0)(1, 1) 开始的块具有有效的零形状,但结果不符合要求。原因是这些块中只有一个可以被认为是有效的。如果我们将零块标记为z0z1,这就是这个矩阵:

[
  [z0, z0,  1, 1],
  [z0, z0, z1, 1],
  [ 1, z1, z1, 1],
  [ 1,  1,  1, 1]
]

(1, 1) 处的元素可以被视为属于z0z1。这意味着只有一个稀疏块,这使得稀疏度为 25%(而不是 ~44%)。

【问题讨论】:

    标签: python numpy matrix


    【解决方案1】:

    变成0的概率并不全都相等。

    例如:block_shape (2, 2), matrix(0, 0) 变为 0 的概率为target,因为循环只经过一次。 matrix(1, 0) 的概率大于target,因为循环通过了两次。同样,matrix(1, 1) 的概率大于 (1, 0),因为循环在 (0, 0)、(1, 0)、(0, 1)、(1, 1) 处看到它四次。

    由于之前的循环操作,这也发生在矩阵的中间。

    所以影响结果的主要变量是block_shape。

    我一直在摆弄,这是使用while 循环而不是for 循环的另一种方法。模拟直到达到err 内的目标概率。由于err 太小,您只需要注意 inf 循环。

    import numpy as np
    
    def sparsify(matrix, target, block_shape=None):
        if block_shape is None or block_shape == 1 or block_shape == (1,) or block_shape == (1, 1):
            # 1x1 is just bernoulli with p=target
            probs = np.random.uniform(size=matrix.shape)
            mask = np.zeros(matrix.shape)
            mask[probs >= target] = 1.0
        else:
            if isinstance(block_shape, int):
                block_shape = (block_shape, block_shape)
            if len(block_shape) == 1:
                block_shape = (block_shape[0], block_shape[0])
            mask = np.ones(matrix.shape)
            rows, cols = matrix.shape
    
            # vars for probability check
            total = float(rows * cols)
            zero_cnt= total - np.count_nonzero(matrix)
            err = 0.005  # .5%
    
            # simulate until we reach target probability range
            while not target - err < (zero_cnt/ total) < target + err:
    
                # pick a random point in the matrix
                row = np.random.randint(rows)
                col = np.random.randint(cols)
    
                # submask = mask[row:row + block_shape[0], col:col + block_shape[1]]
                submask = matrix[row:row + block_shape[0], col:col + block_shape[1]]
    
                if submask.shape != block_shape:
                    # we don't care about the edges, cannot partially sparsify
                    continue
                if (submask == 0).any():
                    # If current (row, col) is already in the sparsified area, skip
                    continue
    
                # need more 0s to reach target probability range
                if zero_cnt/ total < target - err:  
                    matrix[row:row + block_shape[0], col:col + block_shape[1]] = 0
    
                # need more 1s to reach target probability range
                else:                               
                    matrix[row:row + block_shape[0], col:col + block_shape[1]] = 1
    
                # update 0 count
                zero_cnt = total - np.count_nonzero(matrix)
    
        return matrix * mask, mask
    

    注意。

    1. 未检查任何优化或代码重构。
    2. 没有使用mask 变量。直接处理矩阵。
    matrix = np.ones((100, 100))
    matrix, mask = sparsify(matrix, target=0.5, block_shape=(2, 2))
    print((matrix == 0).mean())
    # prints somewhere between target - err and target + err
    # likely to see a lower value in the range since we're counting up (0s)
    

    【讨论】:

    • 这是一个巧妙的技巧。我有点担心执行时间长,但它比我拥有的要好。我想我可以添加一个max_iteration 计数器以确保我不会永远等待 :)
    • 实际上,我认为这段代码有一个错误——其中一个限制是所有零块必须不重叠(忘了明确提及)。您的代码允许块重叠。我现在将在问题中添加一个示例
    • 是的,我明白了。 submask 应该查看矩阵而不是掩码。对不起,我错过了。
    猜你喜欢
    • 1970-01-01
    • 2018-01-19
    • 2019-12-11
    • 2021-09-22
    • 2018-06-18
    • 1970-01-01
    • 2011-08-15
    • 1970-01-01
    • 2017-04-26
    相关资源
    最近更新 更多