这篇简书主要解决两个问题:
1、NMF的原理以及代码实现
2、CNMF的原理以及代码实现
NMF(非负矩阵分解)
NMF算法的基本思想是将原始非负矩阵分解为两个非负矩阵的乘积从而对原始高维矩阵进行降维表示。
理论参考NMF理论
代码如下:
import numpy as np
import random
def nmf(X, r, k, e):
'''
X是原始矩阵
r是分解的两个非负矩阵的隐变量维度,要远小于原始矩阵的维度
k是迭代次数
e是理想误差
input X
output U,V
'''
d, n = X.shape
#print(d,n)
#U = np.mat(random.random((d, r)))
U = np.mat(np.random.rand(d, r))
#V = np.mat(random.random((n, r)))
V = np.mat(np.random.rand(n, r))
#print(U, V)
x = 1
for x in range(k):
print('---------------------------------------------------')
print('开始第', x, '轮迭代')
#error
X_pre = U * V.T
E = X - X_pre
#print E
err= 0.0
for i in range(d):
for j in range(n):
err += E[i,j] * E[i,j]
print('误差:', err)
if err < e:
break
#update U
a_u = U * (V.T) * V
b_u = X * V
for i_1 in range(d):
for j_1 in range(r):
if a_u[i_1, j_1] != 0:
U[i_1, j_1] = U[i_1, j_1] * b_u[i_1, j_1] / a_u[i_1, j_1]
#print(U)
#update V
a_v = V * (U.T) * U
b_v = X.T * U
print(r,n)
for i_2 in range(n):
for j_2 in range(r):
if a_v[i_2,j_2] != 0:
V[i_2,j_2] = V[i_2,j_2] * b_v[i_2,j_2] / a_v[i_2,j_2]
#print(V)
print('第', x, '轮迭代结束')
return U, V
if __name__ == "__main__":
X = [[5, 3, 2, 1, 2, 3],
[4, 2, 2, 1, 1, 5],
[1, 1, 2, 5, 2, 3],
[1, 2, 2, 4, 3, 2],
[2, 1, 5, 4, 1, 1],
[1, 2, 2, 5, 3, 2],
[2, 5, 3, 2, 2, 5],
[2, 1, 2, 5, 1, 1], ]
X = np.mat(X)
#print(X)
U, V = nmf(X, 2, 100, 0.001)
print(U*V.T)
最后的预测结果如下:
误差: 28.69594473130072
第 99 轮迭代结束
[[3.50424419 3.18998965 2.14210184 0.99792358 1.56726071 4.19468352]
[3.56862446 3.23018616 2.0705948 0.75724926 1.50964704 4.25611752]
[1.39276441 1.57416579 2.69585404 4.70589609 2.06052087 1.9272435 ]
[1.47982345 1.61370253 2.50991841 4.17195012 1.91305754 1.99773602]
[1.33855744 1.51799656 2.62162885 4.59446215 2.00425149 1.85656277]
[1.37519953 1.5777549 2.80301544 4.97634219 2.14455706 1.92284106]
[3.80872563 3.54556494 2.80032795 2.18760705 2.07140131 4.62572104]
[0.93447475 1.15300686 2.39181342 4.51956309 1.83692932 1.37528837]]
CNMF
约束非负矩阵分解(CNMF)算法,该算法将标签信息作为附加的硬约束,使得具有相同类标签信息的数据在新的低维空间中仍然保持一致。
但是,CNMF算法对于无标签数据样本没有任何约束,因此在很少的标签信息时它的性能受限,并且对于类中只有一个样本有标签的情形,CNMF算法中构建的约束矩阵将退化为单位矩阵,失去其意义。
理论参考CNMF理论知识
简单说一下,就是在原有的NMF的基础上增加了一个标签信息的硬约束。把原本的V改成了AZ,其中A就是表示样本标签的一个矩阵。
代码如下:
import numpy as np
import random
def cnmf(X, C, r, k, e):
'''
X是原始矩阵,维度为d*n
C是有标签样本指示矩阵,维度为l*c(l——有标签的样本数量,c——类别数量)
r是分解的两个非负矩阵的隐变量维度,要远小于原始矩阵的维度
k是迭代次数
e是理想误差
input X,C
output U,V
'''
d, n = X.shape
l, c = C.shape
#计算A矩阵
I = np.mat(np.identity(n-l))
A = np.zeros((n, n + c - l))
for i in range(l):
for j in range(c):
A[i,j] = C[i,j]
for i2 in range(n-l):
A[l+i2, c+i2] = I[i2, i2]
A = np.mat(A)
U = np.mat(np.random.rand(d, r))
Z = np.mat(np.random.rand(n + c - l, r))
#print(A)
x = 1
for x in range(k):
print('---------------------------------------------------')
print('开始第', x, '轮迭代')
#error
X_pre = U * (A*Z).T
E = X - X_pre
#print E
err= 0.0
for i in range(d):
for j in range(n):
err += E[i,j] * E[i,j]
print('误差:', err)
if err < e:
break
#update U
a_u = U * Z.T * A.T * A * Z
b_u = X * A * Z
for i_1 in range(d):
for j_1 in range(r):
if a_u[i_1, j_1] != 0:
U[i_1, j_1] = U[i_1, j_1] * b_u[i_1, j_1] / a_u[i_1, j_1]
#print(U)
#update Z
#print(Z.shape,n,r)
a_z = A.T * A * Z * U.T * U
b_z = A.T* X.T * U
for i_2 in range(n + c - l):
for j_2 in range(r):
#print(i_2, j_2, a_z[i_2,j_2])
if a_z[i_2,j_2] != 0:
Z[i_2,j_2] = Z[i_2,j_2] * b_z[i_2,j_2] / a_z[i_2,j_2]
#print(V)
print('第', x, '轮迭代结束')
V = (A*Z).T
return U, V
if __name__ == "__main__":
X = [[5, 3, 2, 1, 2, 3],
[4, 2, 2, 1, 1, 5],
[1, 1, 2, 5, 2, 3],
[1, 2, 2, 4, 3, 2],
[2, 1, 5, 4, 1, 1],
[1, 2, 2, 5, 3, 2],
[2, 5, 3, 2, 2, 5],
[2, 1, 2, 5, 1, 1],]#8*6,6个样本
X = np.mat(X)
C = [[0, 0, 1],
[0, 1, 0],
[0, 1, 0],
[1, 0, 0],]#4*3,假设有4个样本有标签,总共有三类标签
#print(X)
C = np.mat(C)
U, V = cnmf(X, C, 2, 100, 0.01)
print(U.shape, V.shape)
print(U * V)
预测结果如下:
误差: 32.59918266947106
第 99 轮迭代结束
[[3.61011796 2.68973499 2.68973499 0.97391408 1.48041323 4.15359667]
[3.75960922 2.68718431 2.68718431 0.57599168 1.38321767 4.28178193]
[1.31126098 2.11712532 2.11712532 4.73958607 2.1238973 1.94710429]
[1.33779502 2.03446009 2.03446009 4.35271177 1.99227239 1.9382424 ]
[1.58299541 2.10935627 2.10935627 4.00422135 1.94286418 2.17890561]
[1.1877958 2.15457453 2.15457453 5.20418261 2.25333957 1.85482653]
[3.73558295 3.16306163 3.16306163 2.4689148 2.06030397 4.44401748]
[0.87749353 1.76087249 1.76087249 4.49534829 1.90001085 1.43531836]]
通过对比误差,是NMF比CNMF的误差更小。后期更换其他数据再进行比较。