转自 http://blog.sina.com.cn/s/blog_5980285201018m6r.html
花了一两天时间捣鼓CRF++的代码(LBFGS部分代码接下来会再总结一下),暂时总结一下(理论部分就不再赘述了):
需要注意的地方也不多:
最需要注意的是TaggerImpl::gradient()函数
1.首先调用buildLattice函数来构造出一个表
buildLattice函数调用rebuildFeatures函数
在rebuildFeatures函数中,建立了tagger需要的node_矩阵,同时填充了path信息。
重要需要关注的是node类:
2. buildLattice函数接下来调用calcCost()函数
分别对应于论文【条件随机场理论综述,韩雪冬,周彩根】中公式(55)的第一项和第二项:
(注意,这里已经都经过了取对数的处理)
从公式中可以看到, Node和Path的cost是对应的特征的权重的和,这在代码calcCost(Node *)和calcCost(Path *)中体现的很明显
3.gradient函数接下来调用forwardbackward函数
calcAlpha, calcBeta
这里的Alpha和Beta分别对应于论文【点击打开链接条件随机场理论综述,韩雪冬,周彩根】中公式(59,60)中的Alpha, Beta.
计算如下是:
这几个公式在代码中体现的都很明显,Alpha和Beta是用动态规划的方法进行计算(跟HMM如出一辙)
Z是partition function,等于所有M矩阵的乘积,在代码中体现也很明显
4.gradient函数接下来调用calcExpectation函数
Node和Path对应的expectation都是用论文【条件随机场理论综述,韩雪冬,周彩根】中公式(61)
5.计算梯度
根据论文【条件随机场理论综述,韩雪冬,周彩根】中公式(52)来计算梯度
公式中的第一项已经在代码的calcExpectation中加上去了
公式中的第二项体现在gradient函数中的两个地方:
--expected[*f + answer_[i]];
--expected[*f +(*it)->lnode->y * ysize_ +(*it)->rnode->y];
5.gradient函数接下来调用viterbi函数
这个跟HMM中的viterbi函数是一样的,略过
6.gradient的目标函数
目标函数是似然函数的相反数
似然函数定义如下:论文【条件随机场理论综述,韩雪冬,周彩根】中公式(49)
代码中的体现如下:
return Z_ - s;
===========================================================================================================
先吐槽一下:练车总算的事总算可以告一段落了!
LBFGS算法
CRF++中与L-BFGS相关的代码都存放在lbfgs.h和lbfgs.cpp中
其中mcsrch函数用于线性搜索,找到给定搜索方向后合适的步长
lbfgs_optimize函数实现了L-BFGS算法。
LBFGS算法的理论相关的最重要的论文:
[Jorge Nocedal; updating quasi-newton matrices with limited storage
是一个拟牛顿算法,它是70年代以来无约束最优化领域最佳的算法
其他的无约束最优化算法包括:最速下降法、牛顿法、共轭梯度法、DFP算法、BFGS算法等。
也可以参照下边这篇文章来了解一下:(这篇文章讲到了CRF++中LBFGS算法的几乎所有细节)
http://blog.pfan.cn/miaowei/52948.html(CRF++详解)
直接读CRF++代码还是稍微有些恶心,参照以下几个链接中的文章读一下
http://code.google.com/p/bungee-view/source/browse/trunk/lbfgs/edu/cmu/cs/bungee/lbfgs/Mcsrch.java?r=108(mcsrch函数的java版本,注释稍微多一些)
http://blog.youkuaiyun.com/settingsun1225/article/details/6142739(LBFGS算法使用,讲的是其他版本的LBFGS算法细节)
这篇文章详细介绍了w_这个变量的内部数据存储情况:
C
C
C
C
C
C
C
C
C
C
C
C
C
C
有了这些帮助读LBFGS的C++版本就变得轻松加愉快了!