今天写的是2018年发表在IEEE transactions上的一篇关于矩阵分解的论文。文章链接如下:
先上文章的思维导图,除了模型的数学推导部分,其余的重点都囊括在内了。
1、该文章的主要创新点是引入一个sigmoid函数,使得矩阵分解出来的两个因子模型中的所有值全为非负的。那sigmoid函数的优势在哪呢?
一、值域在[0,1]之间,这就满足了元素值的非负性特点;
二、sigmoid函数成S形状,较为平滑,并且导数是大于0的。所以在使用梯度下降更新元素值的时候,不容易出现局部最优代替全局最优的情况。
三、较传统的梯度下降和非负乘法更新等模型相比,INLF模型既有传统模型的计算速度快、易存储等优点,同时又能兼容大多数一般性的优化方法。
四、引入sigmoid函数的INLF模型,能够使得分解出来的因子矩阵的值大体服从正态分布,数据波动较小,有利于实验。
五、引入sigmoid函数的INLF模型在大规模数据缺失值预测场景中,预测准确率高,收敛速度快,RSME(均方根误差)和MAE(平均绝对误差)指标小。故在工业场景中可大量应用。
2、简要说一下模型
主体思想还是针对原始矩阵中的值和预测的值作残差平方和,然后记为损失函数,通过最小化损失函数,完成优化;
不同的是引入一个sigmoid函数,实现非负性,并对带有sigmoid的损失函数做优化,得到第一次的因子矩阵,之后对因子矩阵
做第二次sigmoid变换,得到最终的因子矩阵。
1)、初始损失函数:
2)、引入sigmoid函数后的损失函数:(其中函数为sigmoid函数)
3)、引入正则化项防止过拟合:
4)、梯度下降
5)、求导,因子矩阵中值的更新公式
其中,求导为,为学习率,为正则化系数。
3、该文章作者的论证逻辑严谨,是值得学习一下的。为了凸显INLF模型的优越性,在实验比较部分,共进行了三方面的输出。
首先,实验说明INLF模型本身效果好,因子矩阵分布大体为高斯分布,数据波动小;模型RMSE和MAE较小,且收敛速度快。
其次,与一些线性发非负矩阵分解模型,像如WNLF、PNLF、ANLF等做实验比较,证明了INLF在各方面指标上性能突出。
再者,与非线性模型Kernel 非负矩阵分解模型做实验比较,突出INLF性能的优越性。
这种横向、纵向多方面的实验结果比对的逻辑是非常值得学习的。
4、最后,上代码、实验。
import numpy as np
import matplotlib.pyplot as plt
import time
#sigmoid函数
def sigmoidFunc(x):
return 1/(1+np.exp(-x))
#实现INLF内在非负性矩阵分解
#参数分别是原始矩阵,行数,列数,因子矩阵的维度,迭代次数,学习率1,学习率2
def inlfFun(ma,m,n,d,count,a1,a2):
#初始化两个因子矩阵(0-0.05范围内)
A=np.random.uniform(0,0.05,[m,d])#基矩阵
B=np.random.uniform(0,0.05,[n,d])#系数矩阵
#列表记录每一次迭代过后的损失值
resultLoss=[0]
#记录模型消耗的时间
time_start=time.time()
#标记变量,记录迭代次数
ii=0
print("开始训练")
#算法具体实现
while ii<count:
for row in range(m):
for column in range(n):
if ma[row,column]>0:#只拟合大于0的数
Rmn=0#预测值
rmn=ma[row,column]#原始值
#临时变量,记录sigmoid变换
ak=0
bk=0
for k in range(d):
ak=sigmoidFunc(A[row,k])
bk=sigmoidFunc(B[column,k])
#向量点乘得到预测值
Rmn+=ak*bk
#标记预测值与真实值之间的误差值
ERRmn=rmn-Rmn
#梯度下降更新LF特征因子
for k in range(d):
A[row,k]=A[row,k]+a1*ak*(1-ak)*(bk*ERRmn-a2*ak)
B[column,k]=B[column,k]+a1*bk*(1-bk)*(ak*ERRmn-a2*bk)
ii+=1#更新迭代次数
#计算每次迭代完成后的损失值,添加到列表用于作图分析
e=0
for i in range(len(ma)):
for j in range(len(ma[i])):
if ma[i,j]>0:
e+=pow(ma[i,j]-(np.dot(A[i,:],B[j,:])),2)
resultLoss.append(e)
#将因子再经过一次sigmoid变化,得到最后的因子LF矩阵
for row in range(m):
for k in range(d):
A[row,k]=sigmoidFunc(A[row,k])
for column in range(n):
for k in range(d):
B[column,k]=sigmoidFunc(B[column,k])
time_end=time.time()
#算法消耗的总时间
total_time=time_end-time_start
print("总共耗时:",total_time)
return list(resultLoss)
#作图分析
def matplotAnalys(resultLoss):#参数为损失值列表
x=range(len(resultLoss))
plt.plot(x,resultLoss,color='r',linewidth=3)
plt.title("Convergence Curve")
plt.xlabel("iterations")
plt.ylabel("Loss value")
plt.show()
#计算最小损失值及其迭代次数
def getMinLoss(resultLoss):
min=10000
minIndex=0
for i in range(1,len(resultLoss)):
if resultLoss[i]<min:
min=resultLoss[i]
minIndex=i
return (min,minIndex)
if __name__=="__main__":
# inlfFun(ma,m,n,d,count,a1,a2)
#矩阵行列
m=20
n=15
#初始矩阵
ma=np.random.randint(0,6,size=(m,n))
#因子矩阵维度
d=8
#迭代次数
count=3000
#学习率
a1=0.005
a2=0.005
#跑算法模型
resultLoss=inlfFun(ma,m,n,d,count,a1,a2)
#计算最小损失值
min,minIndex=getMinLoss(resultLoss)
print("最小损失值是:",min)
print("最优迭代次数数是:",minIndex)
#做图分析
matplotAnalys(resultLoss)
这个损失值看着是比较大的,因为损失值是误差的平方累加和,所以初始矩阵越大,损失值就越大。
其次,因为sigmoid函数最后得到的值都在(0,1)之间,可能会使得数据在真实场景中失去一定的代表意义,也是损失值较大的原因,如果能对sigmoid函数得到的数据做一些数据处理,损失值会降低很多。
其实,针对INLF矩阵分解模型,最好用RMSE和MAE两个评价指标,因为误差值更小,更能反应效果。