Python 函数来稀疏给定特定块大小的矩阵 问题陈述

Python 函数来稀疏给定特定块大小的矩阵 问题陈述,python,numpy,matrix,Python,Numpy,Matrix,我试图编写一个函数,在给定目标稀疏性和一个名为block\u 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) 可以使用5

我试图编写一个函数,在给定目标稀疏性和一个名为
block\u 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]
]
请注意,输入可能有多个有效的稀疏版本。唯一的标准就是尽可能多地接近目标。限制条件之一是,只有shape
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]
]
到目前为止我所拥有的 目前,我有以下代码

将numpy导入为np
def稀疏(矩阵、目标、块形状=无):
如果块形状为无或块形状==1或块形状==(1,)或块形状==(1,1):
#1x1只是p=target的伯努利
概率=np.随机.均匀(大小=矩阵.形状)
掩码=np.零(矩阵形状)
掩码[probs>=目标]=1.0
其他:
如果isinstance(块形状,int):
块形状=(块形状,块形状)
如果len(块_形)==1:
块形状=(块形状[0],块形状[0])
掩码=np.ones(矩阵形状)
行,cols=matrix.shape
对于范围内的行(行):
对于范围内的列(列):
子掩码=掩码[行:行+块形状[0],列:列+块形状[1]]
如果子磁盘形状!=块形状:
#我们不在乎边缘,不能局部稀疏
持续
if(submask==0).any():
#如果当前(行、列)已在稀疏区域中,则跳过
持续
prob=np.random.random()
如果prob<目标:
子掩码[:,:]=np.Zero(子掩码形状)
返回矩阵*掩码,掩码
上述代码的问题在于,如果块大小不是
(1,1)

差异的原因(我认为) 我不知道为什么我没有达到我期望的目标,但我认为这与我检查每个元素的概率,而不是整个块的概率有关。但是,我的代码中有跳过条件,所以我认为应该包括它

编辑 编辑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)
开始的块具有有效的零形状,但结果不符合要求。原因是这些块中只有一个可以被认为是有效的。如果我们将零块标记为
z0
z1
,则该矩阵如下:

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

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

变为0的概率并不都相等

例如:块_形(2,2),矩阵(0,0)变为0有概率
target
,因为循环只经过一次。矩阵(1,0)的概率大于
target
,因为循环经过它两次。类似地,矩阵(1,1)的概率大于(1,0),因为循环在(0,0)、(1,0)、(0,1)、(1,1)处看到它四次

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

因此,影响结果的主要变量是块的形状

我已经摆弄了一段时间,这里有一种替代方法,使用
while
循环,而不是
for
循环。模拟通过,直到在
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

这是一个巧妙的技巧。我有点担心执行时间太长,但这比我现在拥有的要好。我想我可以添加一个
max_iteration
计数器,以确保我不会永远等待:)事实上,我认为这段代码中有一个bug——其中一个限制是所有零块必须是非重叠的(忘记明确提及)。您的代码允许块重叠。我现在会在问题中添加一个例子是的,我看到了<代码>子掩码应查看矩阵而不是掩码。对不起,我错过了。
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
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)