我正在尝试使用 Kullback-Liebler 散度作为相似性度量来实现非负矩阵分解。该算法描述于:http ://hebb.mit.edu/people/seung/papers/nmfconverge.pdf 。下面是我的 python / numpy 实现,带有一个示例矩阵来运行它。
简而言之,该算法应该学习矩阵 W(n x r) 和 H(r x m),使得 V(n x m) 近似为 WH。您从 W 和 H 中的随机值开始,并遵循 Seung 和 Lee 论文中描述的更新规则,您应该越来越接近 W 和 H 的良好近似值。
该算法被证明可以单调地减少散度度量,但这不是我的实现中发生的情况。相反,它会在两个分歧值之间交替出现。如果您查看 W 和 H,您会发现生成的因式分解并不是特别好。
我想知道在计算 W 的更新时是使用更新的 H 还是旧的 H。我尝试了两种方法,它不会改变实现的行为。
我已经根据论文检查了很多次我的实现,但我看不出我做错了什么。任何人都可以阐明这个问题吗?
import numpy as np
def update(V, W, H, r, n, m):
n,m = V.shape
WH = W.dot(H)
# equation (5)
H_coeff = np.zeros(H.shape)
for a in range(r):
for mu in range(m):
for i in range(n):
H_coeff[a, mu] += W[i, a] * V[i, mu] / WH[i, mu]
H_coeff[a, mu] /= sum(W)[a]
H = H * H_coeff
W_coeff = np.zeros(W.shape)
for i in range(n):
for a in range(r):
for mu in range(m):
W_coeff[i, a] += H[a, mu] * V[i, mu] / WH[i, mu]
W_coeff[i, a] /= sum(H.T)[a]
W = W * W_coeff
return W, H
def factor(V, r, iterations=100):
n, m = V.shape
avg_V = sum(sum(V))/n/m
W = np.random.random(n*r).reshape(n,r)*avg_V
H = np.random.random(r*m).reshape(r,m)*avg_V
for i in range(iterations):
WH = W.dot(H)
divergence = sum(sum(V * np.log(V/WH) - V + WH)) # equation (3)
print "At iteration " + str(i) + ", the Kullback-Liebler divergence is", divergence
W,H = update(V, W, H, r, n, m)
return W, H
V = np.arange(0.01,1.01,0.01).reshape(10,10)
W, H = factor(V, 6)