我想生成大小为m
x n
和等级r
的矩阵,其元素来自指定的有限集,例如{0,1}
或{1,2,3,4,5}
。我希望它们在某个非常松散的意义上是“随机的”,即我希望从算法获得各种可能的输出,其分布模糊地类似于具有指定等级的那组元素上的所有矩阵的分布。
事实上,我实际上并不关心它是否具有等级r
,只是它关闭到等级r
的矩阵(由Frobenius规范测量)
当手头的设置是实时时,我一直在做以下操作,这完全足以满足我的需求:生成大小为U
x m
的基质r
和{ V
x n
的{1}},其元素独立地从例如正常(0,2)。然后,r
是排名为U V'
的{{1}} x m
矩阵(嗯,n
,但我认为r
的概率很高。
如果我这样做然后再转到二进制/ 1-5,则排名会增加。
通过执行SVD并获取第一个<= r
奇异值,也可以获得矩阵的低秩近似。但是,这些值不会出现在所需的集合中,对它们进行舍入将再次提高等级。
This question是相关的,但接受的答案不是“随机的”,另一个答案表明SVD,这在这里不起作用。
我想到的一种可能性是从集合中生成r
线性独立的行或列向量,然后通过它们的线性组合得到矩阵的其余部分。不过,我不是很清楚如何获得“随机”线性独立向量,或者在此之后如何以准随机方式组合它们。
(不是说这是超级相关的,但我是在笨拙地做这件事。)
更新:我已经在评论中尝试了EMS建议的方法,这个简单的实现:
r
对小型矩阵很快起作用,但对于10x10 - 它似乎陷入了第6或第7级,至少在数十万次迭代中停滞不前。
看起来这可能会更好地用更好的(即不太平坦的)目标函数,但我不知道它会是什么。
我还尝试了一种简单的拒绝方法来构建矩阵:
r
这适用于例如具有任何等级的10x10二进制矩阵,但不适用于0-4矩阵或具有较低等级的更大二进制数。 (例如,获得排名15的20x20二进制矩阵花了我42,000个拒绝;排名10的20x20,花了120万。)
这显然是因为第一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)
行所跨越的空间太小,我正在采样的空间的一部分,例如, 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
,在这些情况下。
我们希望第一个r
行的范围与有效值集的交集。
因此,我们可以尝试从跨度中采样并寻找有效值,但由于跨度涉及实值系数,它们永远不会找到有效的向量(即使我们进行归一化,例如第一个组件在有效集中)。 / p>
也许这可以表示为整数编程问题,或者什么?
答案 0 :(得分:2)
我的朋友,上面评论过的丹尼尔约翰逊提出了一个想法,但我发现他从未发布过这个想法。它不是很充实,但你可以适应它。
如果
A
是m-by-r且B
是r-by-n且两者都具有等级r,则AB
具有等级r。现在,我们只需选择A
和B
,以便AB
仅在给定集合中具有值。最简单的情况是S = {0,1,2,...,j}
。一种选择是使
A
二进制文件具有适当的行/列总和 这保证了正确的排名和B
,列总和增加到否 超过j
(以便产品中的每个字词都在S
)和行总和 选择导致等级r
(或至少鼓励它,因为拒绝可以 使用)。我认为我们可以提出两个独立的抽样方法
A
和B
上的方案比尝试更简单,更快捷 立即攻击整个矩阵。不幸的是,我所有的矩阵 采样代码在另一台计算机上。我知道这很容易概括 允许条目大于{0,1}
的条目(即S
),但我不能 记住计算如何与m*n
一起缩放。
答案 1 :(得分: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)
答案 2 :(得分:1)
我不确定这个解决方案有多么有用,但你可以构建一个矩阵,允许你在另一个只有0和1作为条目的矩阵上搜索解决方案。如果你在二进制矩阵上随机搜索,它相当于随机修改最终矩阵的元素,但是有可能提出一些比随机搜索更好的规则。
如果您想在元素集 E 上生成m
- by - n
矩阵,其元素为e i ,{{1你开始使用0<=i<k
- by - m
矩阵, A :
显然,这个矩阵的等级 m 。现在,您可以构建另一个矩阵B,它在某些位置具有1,以从集合 E 中选择元素。该矩阵的结构是:
每个 B i 是k*m
- by - k
矩阵。那么, A B 的大小为n
- by - m
和 rank( A B )是 min(m,rank( B ))。如果我们希望输出矩阵只包含我们的集合 E 中的元素,那么 B i 的每一列必须只有一个元素集到n
,其余设置为1
。
如果你想随机搜索B上的某个等级,你需要从最高等级的有效 B 开始,并旋转一个随机列 j 随机量随机 B i 。这相当于将 A * B 的列 i 行 j 更改为我们的集合中的随机元素,因此它不是一个非常有用的方法。
但是,你可以用矩阵做一些技巧。例如,如果0
为2,并且 B 0 和 B 1 的第一行没有重叠,您可以通过添加这两个子矩阵的第一行来生成线性相关的行。第二行也将线性地依赖于这两个矩阵的行。我不确定这是否会轻易推广到大于2的k
,但我相信你可以采用其他技巧。
例如,最多生成排名k
的一种简单方法(当k
为m
时)是获得随机有效 B 0 ,继续旋转此矩阵的所有行以使 B 1 变为 B m-2 ,将 B m-1 的第一行设置为全1,其余行设置为全0。等级不能小于k+1
(假设{ {1}}&gt; k
),因为n
列只有1个非零元素。矩阵的其余行都是这些行的线性组合(实际上是几乎所有子矩阵的精确副本)。最后一个子矩阵的第一行是第一个子矩阵的所有行的总和,其余的所有行都是零。对于较大的k
值,您可以使用 B 0 行的排列,而不是简单的旋转。
一旦你生成了一个满足排名约束的矩阵,你就可以随意改变它的行和列来生成其他矩阵。