每日一贴,今天的内容关键字为矩阵特征
大学时我的线性代数老师寿继麟,事先六十多岁带着一副金丝眼镜精力矍铄,传说是我最尊敬的余德鴻副校长的老师。上课的课本是寿老师写的打印出来给大家,很便宜。我虽然不好好学习,但是在好老师的率领下,也不至于学的太差。余校长隔段时光总会给我们上一堂课,虽然没有说过我们什么,但总能让我们羞愧进而耐劳上一段时光。记得他说过这样一个问题,会场人声嘈杂,给你两个麦克风,怎么在一无所知的情况下分离出你想听到的那个人的声音。十年后的今年我才真正开始考虑这个问题,余校长提出的问题,原来寿老师事先就帮我们处理了。
非负矩阵因式分解,非负是说矩阵的元素都是正数或者零,因式分解就是对矩阵A,寻觅矩阵B和C,使得A=B*C。这样分解有什么用呢?还是为了处理分类的问题。区别人声,也是一个分类问题。分类的问题在之前《集体伶俐编程学习之分类系统》中提到了贝叶斯和费舍尔,在《集体伶俐编程学习之聚类系统》中提到了K-均值聚类,贝叶斯和费舍尔需要事前定义好分类,然后训练模型,K-均值聚类的计算量还是蛮大的。而非负矩阵因式分解可以一次把所有的相关性计算出来,还是很有必要好好学习的。
矩阵因式分解的目标呢,就是把矩阵B拆分成为两个矩阵相乘,比如矩阵F和矩阵W,这样B=W*F。矩阵的乘法事先寿老师教的很清晰,第一个矩阵(比如W)的列必须于第二个矩阵(比如F)的行数相称才能相乘,乘积矩阵(比如B)的每一个元素的取值,是通过将矩阵W中雷同行的值与矩阵F中雷同列的值相乘,然后将乘积相加所得。(这里我不举例子了,随便Google就有了)。这里将B拆分为F和W的乘积,F代表特征矩阵,W代表权值矩阵,下面具体说说这两个货色。
F特征矩阵:在该矩阵中,每一个特征对应一行,每一个单词对应一列,矩阵中的数字代表了某个单词相对于某个特征的重要程度;
W权值矩阵:在改矩阵中,每一行对应一本书,每一列对应一个特征,矩阵中的数字代表了将某个特征应用于某篇文章的程度;
这样W*F就可以重构出B,一行对应一本书,一列对应一个单词。
通过计算最好的特征矩阵和权值矩阵,算法实验尽可能大地来从新结构文章矩阵。这里我们先定义一种方法来衡量最终结果与幻想结果的接近程度:
def difcost(a,b):
dif=0
for i in range(shape(a)[0]):
for j in range(shape(a)[1]):
# Euclidean Distance
dif+=pow(a[i,j]-b[i,j],2)
return dif
函数difcost针对两个同样大小的矩阵遍历其中的每一个值,并将两者的间差值的平方累加起来。
下面我们需要一种方法能够逐渐地更新矩阵,以使本钱函数的计算值逐步降低。《集体伶俐编程学习之优化系统》里的退火法和遗传算法都能满足要求。这里我来学习一种更为的方法,乘法更新矩阵规律。规律的具体道理先不学习,先直接拿来用。有兴趣的看这篇论文《Algorithms for Non-negative Matrix Factorization》
这个规律发生四个新的更新矩阵:
hn:转置后的权重矩阵与数据矩阵相乘而来;
hd:转置后的权重矩阵与原权重矩阵相乘,再与特征矩阵相乘而来;
wn:数据矩阵与转置后的特征矩阵相乘而来;
wd:权重矩阵与特征矩阵相乘,再与转置后的特征矩阵相乘而来;
更新特征矩阵和权重矩阵,我们将特征矩阵中的每一个值与hn中的对应值相乘,并除以hd中的对应值;再将权重矩阵中的每一个值与wn中的对应值相乘,并除以wd中的对应值下面看用python的算法实现:
def factorize(v,pc=10,iter=50):
ic=shape(v)[0]
fc=shape(v)[1]
# Initialize the weight and feature matrices with random values
w=matrix([[random.random() for j in range(pc)] for i in range(ic)])
h=matrix([[random.random() for i in range(fc)] for i in range(pc)])
# Perform operation a maximum of iter times
for i in range(iter):
wh=w*h
# Calculate the current difference
cost=difcost(v,wh)
if i%10==0: print cost
# Terminate if the matrix has been fully factorized
if cost==0: break
# Update feature matrix
hn=(transpose(w)*v)
hd=(transpose(w)*w*h)
h=matrix(array(h)*array(hn)/array(hd))
# Update weights matrix
wn=(v*transpose(h))
wd=(w*h*transpose(h))
w=matrix(array(w)*array(wn)/array(wd))
return w,h
参数pc指定我们希望找到的特征数,iter是最多执行次数。
来看看我机器执行的测试:
测试数据还是用之前的例子:每一行都是一本书,每一列都代表一个词,数组中的数字代表这个词在这本书中涌现的次数或者TF-IDF值:
books = [
[2, 3, 3, 12, 13, 12],
[3, 3, 1, 13, 12, 11],
[1, 10, 3, 5, 11, 13],
[13, 12, 11, 1, 3, 3],
[12, 13, 12, 3, 2, 2],
[5, 11, 13, 3, 1, 10]
]
测试过程:
>>> from numpy import *
>>> v=matrix(nnmf.books)
>>> w,f=nnmf.factorize(v,2)
2352.23524582
130.308626905
125.555272626
125.540256481
125.540046063
>>> w
matrix([[ 1.01861466, 0.11646996],
[ 0.99224682, 0.07461746],
[ 0.80748193, 0.400123 ],
[ 0.04713232, 1.2421583 ],
[ 0.04119911, 1.27694878],
[ 0.27820173, 1.02855868]])
>>> f
matrix([[ 0.21984673, 3.29533888, 0.77713346, 10.7432405 ,
12.31672543, 12.3422249 ],
[ 8.44995405, 10.06321664, 9.81092778, 0.6161926 ,
0.65151678, 2.88313112]])
>>> w*f
matrix([[ 1.20810496, 4.52874298, 1.93427795, 11.01499022,
12.62187925, 12.90776943],
[ 0.84865631, 4.02068115, 1.50317469, 10.70592492,
12.26984613, 12.46166529],
[ 3.5585432 , 6.68745101, 4.55309905, 8.92152545,
10.20622012, 11.1197307 ],
[ 10.50654247, 12.65542508, 12.2233535 , 1.27176259,
1.38980281, 4.16302294],
[ 10.79921601, 12.98597723, 12.56006946, 1.22945829,
1.33939163, 4.1900994 ],
[ 8.75243536, 11.26737784, 10.30731484, 3.6225783 ,
4.09665751, 6.39909782]])
我测试用的特征数选择为2。
先来看看权值矩阵w:
book1,特征一显著(1.0),特征二不显著(0.1);
book2,特征一显著(0.9),特征二不显著(0.0);
book3,特征一显著(0.8),特征二不显著(0.4);
book4,特征一不显著(0.0),特征二显著(1.2);
book5,特征一不显著(0.0),特征二显著(1.2);
book6,特征一不显著(0.2),特征二显著(1.0);
再看看特征矩阵:
特征一:word1不显著(0.2),word2不太显著(3.2),word3不显著(0.7),word4显著(10.7),word5显著(12.3),word6显著(12.3)
特征二:word1挺显著(8.4),word2显著(10.0),word3显著(9.8),word4不显著(0.6),word5不显著(0.6),word6不显著(2.8)
实现还是很简单的,主要是这类思惟,再次得成论断最NB的还是算法。上次听博士说一年要读100多篇论文,这才是学术研究,这才会发明核心技术。