9

我想生成大小为mxn和 rank的矩阵r,其中元素来自指定的有限集,例如{0,1}or {1,2,3,4,5}。我希望它们在该词的某种非常松散的意义上是“随机的”,即我希望从算法中获得各种可能的输出,其分布与具有指定等级的该组元素上的所有矩阵的分布模糊相似。

实际上,我实际上并不关心它是否具有 rank r,只是它接近于rank 矩阵r(由 Frobenius 范数测量)。

当手头的集合是实数时,我一直在执行以下操作,这完全可以满足我的需要:生成U大小为mxr和xV的矩阵,其中元素独立地从 Normal(0, 2) 中采样。然后是一个x秩矩阵(嗯,,但我认为它很有可能)。nrU V'mnr<= rr

但是,如果我只是这样做然后四舍五入到二进制/ 1-5,则排名会增加。

r也可以通过执行 SVD 并获取第一个奇异值来获得矩阵的低秩近似。但是,这些值不会位于所需的集合中,并且将它们四舍五入将再次提高排名。

这个问题是相关的,但接受的答案不是“随机的”,另一个答案建议 SVD,如前所述,它在这里不起作用。

我想到的一种可能性是r从集合中生成线性独立的行或列向量,然后通过它们的线性组合得到矩阵的其余部分。不过,我不太清楚如何获得“随机”线性独立向量,或者之后如何以准随机方式组合它们。

(并不是说它是超级相关的,但我在 numpy 中这样做。)


更新:我已经尝试了 EMS 在评论中建议的方法,这个简单的实现:

real = np.dot(np.random.normal(0, 1, (10, 3)), np.random.normal(0, 1, (3, 10)))
bin = (real > .5).astype(int)
rank = np.linalg.matrix_rank(bin)
niter = 0

while rank > des_rank:
    cand_changes = np.zeros((21, 5))
    for n in range(20):
        i, j = random.randrange(5), random.randrange(5)
        v = 1 - bin[i,j]
        x = bin.copy()
        x[i, j] = v
        x_rank = np.linalg.matrix_rank(x)
        cand_changes[n,:] = (i, j, v, x_rank, max((rank + 1e-4) - x_rank, 0))
    cand_changes[-1,:] = (0, 0, bin[0,0], rank, 1e-4)

    cdf = np.cumsum(cand_changes[:,-1])
    cdf /= cdf[-1]
    i, j, v, rank, score = cand_changes[np.searchsorted(cdf, random.random()), :]
    bin[i, j] = v
    niter += 1
    if niter % 1000 == 0:
        print(niter, rank)

它适用于小型矩阵,但在例如 10x10 时会分崩离析——它似乎卡在 6 或 7 级,至少在数十万次迭代中。

看起来这可能与更好(即不太平坦)的目标函数一起工作得更好,但我不知道那会是什么。


我还尝试了一种简单的拒绝方法来构建矩阵:

def fill_matrix(m, n, r, vals):
    assert m >= r and n >= r
    trans = False
    if m > n: # more columns than rows I think is better
        m, n = n, m
        trans = True

    get_vec = lambda: np.array([random.choice(vals) for i in range(n)])

    vecs = []
    n_rejects = 0

    # fill in r linearly independent rows
    while len(vecs) < r:
        v = get_vec()
        if np.linalg.matrix_rank(np.vstack(vecs + [v])) > len(vecs):
            vecs.append(v)
        else:
            n_rejects += 1
    print("have {} independent ({} rejects)".format(r, n_rejects))

    # fill in the rest of the dependent rows
    while len(vecs) < m:
        v = get_vec()
        if np.linalg.matrix_rank(np.vstack(vecs + [v])) > len(vecs):
            n_rejects += 1
            if n_rejects % 1000 == 0:
                print(n_rejects)
        else:
            vecs.append(v)
    print("done ({} total rejects)".format(n_rejects))

    m = np.vstack(vecs)
    return m.T if trans else m

这适用于例如具有任何等级的 10x10 二进制矩阵,但不适用于 0-4 矩阵或更大的具有较低等级的二进制矩阵。(例如,获得一个 20x20 的 15 级二进制矩阵需要 42,000 次拒绝;使用 20x20 的 10 级,需要 120 万次。)

这显然是因为第一r行所跨越的空间在我从中采样的空间的一部分中太小了,例如{0,1}^10,在这些情况下。

我们想要第一r行的跨度与一组有效值的交集。所以我们可以尝试从跨度中采样并寻找有效值,但由于跨度涉及实值系数,它永远不会找到我们有效的向量(即使我们进行归一化,例如第一个分量在有效集合中)。

也许这可以表述为一个整数规划问题,或者什么?

4

3 回答 3

2

我不确定这个解决方案会有多大用处,但您可以构建一个矩阵,让您可以在另一个矩阵上搜索解决方案,其中只有 0 和 1 作为条目。如果在二元矩阵上随机搜索,相当于随机修改最终矩阵的元素,但有可能想出一些规则比随机搜索做得更好。

如果要在元素集Em上生成一个逐矩阵,其中元素为 e i , ,则从逐矩阵A开始:n0<=i<kmk*m

生成矩阵

显然,这个矩阵的秩为m。现在,您可以构造另一个矩阵 B,它在某些位置具有 1,以从集合E中选择元素。这个矩阵的结构是:

选择矩阵

每个B i都是一个k矩阵n。因此,A B的大小是m-by-n并且rank( A B )min(m, rank( B ))。如果我们希望输出矩阵仅包含我们的集合E中的元素,则B i的每一列必须恰好有一个元素设置为1,其余的设置为0

如果要在 B 上随机搜索某个排名,则需要从具有最大排名的有效B开始,并将随机B i的随机列j旋转随机量。这相当于将A * B的第i行第j列更改为我们集合中的一个随机元素,因此它不是一个非常有用的方法。

但是,您可以对矩阵进行某些技巧。例如,如果为 2,并且B 0B 1k的第一行没有重叠,则可以通过将这两个子矩阵的第一行相加来生成线性相关行。第二行也将线性依赖于这两个矩阵的行。我不确定这是否会轻易推广到大于 2,但我相信您可以使用其他技巧。k

例如,生成最多秩k(何时mk+1)的一种简单方法是获取随机有效的B 0,继续旋转该矩阵的所有行以得到B 1B m-2 ,设置B m-1的第一行全部为 1,其余行全部为 0。排名不能小于k(假设n> k),因为B_0列正好有 1 个非零元素。矩阵的其余行都是这些行的线性组合(实际上是几乎所有子矩阵的精确副本)。最后一个子矩阵的第一行是第一个子矩阵所有行的总和,其余行全为零。对于较大的值m,您可以使用B 0行的排列而不是简单的旋转。

一旦你生成了一个满足秩约束的矩阵,你就可以通过随机打乱它的行和列来生成其他矩阵。

于 2012-04-19T22:01:31.433 回答
2

我的朋友,上面评论的丹尼尔约翰逊,想出了一个主意,但我看到他从未发布过。它不是很充实,但你可能能够适应它。

如果A是 m×r 并且B是 r×n 并且两者都具有秩 r,则AB具有秩 r。现在,我们只需要选择仅在给定集合中具有值的A和。最简单的情况是。BABS = {0,1,2,...,j}

一种选择是A使用适当的行/列总和来制作二进制,以保证正确的排名,B并且列总和不超过j(以便产品中的每个项都在 中S)并且选择行总和以导致排名r(或至少鼓励它作为拒绝可以使用)。

我只是认为我们可以提出两个独立的采样方案AB这比尝试一次攻击整个矩阵更简单、更快捷。不幸的是,我所有的矩阵采样代码都在另一台计算机上。我知道它很容易概括为允许比{0,1}(ie S) 更大的集合中的条目,但我不记得计算是如何缩放的m*n

于 2012-04-14T00:29:17.307 回答
2

像这样怎么样?

rank = 30
n1 = 100; n2 = 100
from sklearn.decomposition import NMF
model = NMF(n_components=rank, init='random', random_state=0)
U = model.fit_transform(np.random.randint(1, 5, size=(n1, n2)))
V = model.components_
M = np.around(U) @ np.around(V)
于 2018-03-29T06:58:45.907 回答