这篇文章主要讲解CRF++实现预测的过程,预测的算法以及代码实现相对来说比较简单,所以这篇文章理解起来也会比上一篇条件随机场训练的内容要容易。

预测

  上一篇条件随机场训练的源码详解中,有一个地方并没有介绍。 就是训练结束后,会把待优化权重alpha等变量保存到文件中,也就是输出到指定的模型文件。在执行预测的时候会从模型文件读出相关的变量,这个过程其实就是数据序列化与反序列化,该过程跟条件随机场算法关系不大,因此为了突出重点源码解析里就没有介绍这部分,有兴趣的朋友可以自己研究一下。

  CRF++预测的入口代码在crf_test.cpp的main函数中,最终会调用tragger.cpp的int crfpp_test(const Param &param)函数,期间会做一些输入参数的处理、异常处理、读取模型文件等操作。一切准备就绪就会打开待预测的文件,进行预测。正式探讨预测代码之前,我们先看下预测的理论基础。条件随机场的预测用到了维特比算法,公式如下:

\begin{aligned} y^* &= \arg \max_yP_w(y|x) \\ &=  \arg \max_y\frac{ \exp \left \{ \sum_{k=1}^Kw_kf_k(y,x) \right\}}{Z_w(x)} \\ &=  \arg \max_y \exp \left \{\sum_{k=1}^Kw_kf_k(y,x) \right\} \\ &= \arg \max_y \ \sum_{k=1}^Kw_kf_k(y,x) \end{aligned}

从公式我们可以看出,我们求的概率最大值就是要求代价最大。接下来就看下CRF++的源码,代码在tragger.cpp的crfpp_test函数中:

while (*is) {//is是打开的测试文件,可以输入多个测试文件做预测
tagger.parse_stream(is.get(), os.get());
} bool TaggerImpl::parse_stream(std::istream *is,
std::ostream *os) {
if (!read(is) || !parse()) {//read函数在特征篇讲过,不再赘述,调用parse函数进行预测
return false;
}
if (x_.empty()) {
return true;
}
toString(); //格式化输出,-v 会输出每个词预测为某个label的概率,-n会输出预测序列概率最大的前n个,如果理解上一篇训练过程,再看这个函数就比较容易理解,无非就是概率计算,这里不再赘述
os->write(os_.data(), os_.size()); //输出到输出文件
return true;
}
bool TaggerImpl::parse() {
CHECK_FALSE(feature_index_->buildFeatures(this)) //构建特征,同特征篇代码,不再赘述
<< feature_index_->what(); if (x_.empty()) {
return true;
}
buildLattice(); //构建无向图,因为要计算代价最大的序列,训练篇讲过,不再赘述
if (nbest_ || vlevel_ >= ) {
forwardbackward(); //前向后向算法,为了计算单词节点的概率,训练篇讲过,不再赘述
}
viterbi(); //维特比算法, 做预测的代码
if (nbest_) {
initNbest();
} return true;
}
void TaggerImpl::viterbi() {
for (size_t i = ; i < x_.size(); ++i) { //遍历每个词
for (size_t j = ; j < ysize_; ++j) { //遍历每个词的每个label
double bestc = -1e37;
Node *best = ;
const std::vector<Path *> &lpath = node_[i][j]->lpath;
for (const_Path_iterator it = lpath.begin(); it != lpath.end(); ++it) { //从前一个词到当前词的代价之和 = max(前一个节点的代价 + 前一个节点的边代价 + 当前节点代价)
double cost = (*it)->lnode->bestCost +(*it)->cost +
node_[i][j]->cost;
if (cost > bestc) { //记录截止当前节点最大的代价, 以及对应的前一个节点
bestc = cost;
best = (*it)->lnode;
}
}
node_[i][j]->prev = best; //记录前一个几点
node_[i][j]->bestCost = best ? bestc : node_[i][j]->cost; //记录最大的代价值, 如果best = 0代表第一个词,没有左边,最大代价就是节点的代价node_[i][j]->cost
}
} double bestc = -1e37;
Node *best = ;
size_t s = x_.size()-;
for (size_t j = ; j < ysize_; ++j) { //遍历最后一个词的节点,截止到最后一个词的代价最大值就是整个句子的最大代价
if (bestc < node_[s][j]->bestCost) {
best = node_[s][j];
bestc = node_[s][j]->bestCost;
}
} for (Node *n = best; n; n = n->prev) {//记录代价最大的预测序列
result_[n->x] = n->y;
} cost_ = -node_[x_.size()-][result_[x_.size()-]]->bestCost;
}

预测的核心代码就看完了,大部分复用了训练过程的逻辑。可以看到预测的过程跟公式是一致的,无非就是求能够让代价最大的label序列(标记序列),这就是维特比算法。

总结

  至此,我们的条件随机场之CRF++源码详解系列就结束了,主要涵盖了特征处理、训练以及预测三个核心过程。结合CRF++源码我们可以更形象的、更通俗的去理解条件随机场模型。以后想起条件随机场模型,我们脑海浮现的不再是一堆公式,而是一个无向图,在图上进行代价计算、前向后向计算、期望值的计算以及梯度的计算等一系列的过程。希望这个系列对于正在学习条件随机场的朋友能有帮助,如果本文阐述的有歧义、不通俗、不容易理解的地方,欢迎留言区交流,我将及时更正、回复,希望我们一起提高。

条件随机场之CRF++源码详解-预测的更多相关文章

  1. 条件随机场之CRF++源码详解-训练

    上篇的CRF++源码阅读中, 我们看到CRF++如何处理样本以及如何构造特征.本篇文章将继续探讨CRF++的源码,并且本篇文章将是整个系列的重点,会介绍条件随机场中如何构造无向图.前向后向算法.如何计 ...

  2. 条件随机场之CRF++源码详解-特征

    我在学习条件随机场的时候经常有这样的疑问,crf预测当前节点label如何利用其他节点的信息.crf的训练样本与其他的分类器有什么不同.crf的公式中特征函数是什么以及这些特征函数是如何表示的.在这一 ...

  3. 条件随机场之CRF++源码详解-开篇

    介绍 最近在用条件随机场做切分标注相关的工作,系统学习了下条件随机场模型.能够理解推导过程,但还是比较抽象.因此想研究下模型实现的具体过程,比如:1) 状态特征和转移特征具体是什么以及如何构造 2)前 ...

  4. Spark Streaming揭秘 Day25 StreamingContext和JobScheduler启动源码详解

    Spark Streaming揭秘 Day25 StreamingContext和JobScheduler启动源码详解 今天主要理一下StreamingContext的启动过程,其中最为重要的就是Jo ...

  5. [转]Linux内核源码详解--iostat

    Linux内核源码详解——命令篇之iostat 转自:http://www.cnblogs.com/york-hust/p/4846497.html 本文主要分析了Linux的iostat命令的源码, ...

  6. saltstack源码详解一

    目录 初识源码流程 入口 1.grains.items 2.pillar.items 2/3: 是否可以用python脚本实现 总结pillar源码分析: @(python之路)[saltstack源 ...

  7. Activiti架构分析及源码详解

    目录 Activiti架构分析及源码详解 引言 一.Activiti设计解析-架构&领域模型 1.1 架构 1.2 领域模型 二.Activiti设计解析-PVM执行树 2.1 核心理念 2. ...

  8. 源码详解系列(六) ------ 全面讲解druid的使用和源码

    简介 druid是用于创建和管理连接,利用"池"的方式复用连接减少资源开销,和其他数据源一样,也具有连接数控制.连接可靠性测试.连接泄露控制.缓存语句等功能,另外,druid还扩展 ...

  9. Mybatis源码详解系列(四)--你不知道的Mybatis用法和细节

    简介 这是 Mybatis 系列博客的第四篇,我本来打算详细讲解 mybatis 的配置.映射器.动态 sql 等,但Mybatis官方中文文档对这部分内容的介绍已经足够详细了,有需要的可以直接参考. ...

随机推荐

  1. js获取当前日期时间“yyyy-MM-dd HH:MM:SS”

    获取当前的日期时间 格式“yyyy-MM-dd HH:MM:SS” 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function getNowFormatDat ...

  2. python 面向对象的类

    参考<learn python hard way> 网址:https://learnpythonthehardway.org/book/ex41.html class X(Y) " ...

  3. 【漏洞分析】两个例子-数组溢出修改返回函数与strcpy覆盖周边内存地址

    修改返回函数 return 0 下面的程序的运行流程为main()函数调用了Magic()函数,通常执行完Magic()函数后会调用return 0 的地址, 但是在执行Magic()函数中时,数组下 ...

  4. ARMV8 Procedure Call Standard

    1.前言 2.  术语说明 Term Note ABI Application Binary Interface 应用程序二进制接口 EABI Embedded ABI  嵌入式ABI PCS Pro ...

  5. UML和模式应用4:初始阶段(2)--需求科目之进化式需求

    1. 前言 UP开发包括四个阶段:初始阶段.细化阶段.构建阶段.移交阶段: UP每个阶段包括 业务建模.需求.设计等科目: 需求是UP科目之一,在初始阶段需求科目的工作量占据较大的部分.但是初始阶段的 ...

  6. MySQL日志——Undo | Redo【转】

    本文是介绍MySQL数据库InnoDB存储引擎重做日志漫游 00 – Undo LogUndo Log 是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版 ...

  7. xpath与nodejs解析xml

    测试xpath的工具 http://www.freeformatter.com/xpath-tester.html#ad-output http://www.xpathtester.com/test ...

  8. 【转】SourceInsight4破解笔记

    时隔好多年,sourceinsight4以迅雷不及掩耳之势的来了.与3.5相比,sourceinsight4多了代码折叠以及文件标签功能,可谓是让sourceinsight迷兴奋了好几晚上.废话不多说 ...

  9. 转载:Linux内核参数的优化(1.3.4)《深入理解Nginx》(陶辉)

    原文:https://book.2cto.com/201304/19615.html 由于默认的Linux内核参数考虑的是最通用的场景,这明显不符合用于支持高并发访问的Web服务器的定义,所以需要修改 ...

  10. android测试点整理

    Android的功能测试点 安装\卸载 App具体功能点 联网(默认的联网方式是什么?Wifi orSim卡? 网络切换是否有相应的提示说明? 飞行模式) 程序进入输入功能时,是否正常弹出键盘;键盘是 ...