cppjieba分词包主要提供中文分词、关键词提取、词性标注三种功能

一、分词

  cppjieba分词用的方法是最大概率分词(MP)和隐马尔科夫模型(HMM),以及将MP和HMM结合成的MixSegment分词器。除此之外,cppjieba支持三种模式的分词:

  • 精确模式,试图将句子最精确地切开,适合文本分析;
  • 全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义;

我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学

  • 搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词

小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造

1.最大概率分词(MP)

  • 默认基于jieba.dict.utf8生成前缀词典,用户可以添加自己的字典,并且按照一定权重比重一起生成前缀词典。构建字典时将utf-8格式的输入转变为unicode格式
  • 分词器中有一个类Prefilter,pre_fileter会将输入的字符串转变为unicode格式,根据某些特殊的字符symbols_,将输入的字符串切分成一段一段,对每一段分别分词
  • 构建DAG图,从后往前的动态规划算法,回溯概率最大的切词方法
    void Cut(RuneStrArray::const_iterator begin,
RuneStrArray::const_iterator end, vector<WordRange>& words,
size_t max_word_len = MAX_WORD_LENGTH) const {
vector<Dag> dags;
dictTrie_->Find(begin, end, dags, max_word_len); //构建DAG图
CalcDP(dags); //从后往前的动态规划算法
CutByDag(begin, end, dags, words);//回溯
}

2.隐藏马尔科夫分词(HMM)

参考:中文分词之HMM模型详解

cppjieba分词中提供了HMM模型的参数文件,保存在hmm_model.utf8中。cppjieba的HMM分词器,实际上就是加载HMM模型,然后根据输入的句子(观察序列),计算可能性最大的状态序列。状态空间由B(开始)、M(中间)、E(结束)、S(单个字)构成。下面是Viterbi算法的过程。

输入样例:

小明硕士毕业于中国科学院计算所

定义变量

二维数组 weight[4][15],4是状态数(0:B,1:E,2:M,3:S),15是输入句子的字数。比如 weight[0][2] 代表 状态B的条件下,出现'硕'这个字的可能性。

二维数组 path[4][15],4是状态数(0:B,1:E,2:M,3:S),15是输入句子的字数。比如 path[0][2] 代表 weight[0][2]取到最大时,前一个字的状态,比如 path[0][2] = 1, 则代表 weight[0][2]取到最大时,前一个字(也就是)的状态是E。记录前一个字的状态是为了使用viterbi算法计算完整个 weight[4][15] 之后,能对输入句子从右向左地回溯回来,找出对应的状态序列。

使用InitStatus对weight二维数组进行初始化

已知InitStatus如下:

#B
-0.26268660809250016
#E
-3.14e+100
#M
-3.14e+100
#S
-1.4652633398537678

且由EmitProbMatrix可以得出

Status(B) -> Observed(小)  :  -5.79545
Status(E) -> Observed(小) : -7.36797
Status(M) -> Observed(小) : -5.09518
Status(S) -> Observed(小) : -6.2475

所以可以初始化 weight[i][0] 的值如下:

weight[0][0] = -0.26268660809250016 + -5.79545 = -6.05814
weight[1][0] = -3.14e+100 + -7.36797 = -3.14e+100
weight[2][0] = -3.14e+100 + -5.09518 = -3.14e+100
weight[3][0] = -1.4652633398537678 + -6.2475 = -7.71276

注意上式计算的时候是相加而不是相乘,因为之前取过对数的原因。

遍历句子计算整个weight二维数组

//遍历句子,下标i从1开始是因为刚才初始化的时候已经对0初始化结束了
for(size_t i = 1; i < 15; i++)
{
// 遍历可能的状态
for(size_t j = 0; j < 4; j++)
{
weight[j][i] = MIN_DOUBLE;
path[j][i] = -1;
//遍历前一个字可能的状态
for(size_t k = 0; k < 4; k++)
{
double tmp = weight[k][i-1] + _transProb[k][j] + _emitProb[j][sentence[i]];
if(tmp > weight[j][i]) // 找出最大的weight[j][i]值
{
weight[j][i] = tmp;
path[j][i] = k;
}
}
}
}

如此遍历下来,weight[4][15] 和 path[4][15] 就都计算完毕。

确定边界条件和路径回溯

边界条件如下:

对于每个句子,最后一个字的状态只可能是 E 或者 S,不可能是 M 或者 B。

所以在本文的例子中我们只需要比较 weight[1(E)][14] 和 weight[3(S)][14] 的大小即可。

在本例中:

weight[1][14] = -102.492;
weight[3][14] = -101.632;

所以 S > E,也就是对于路径回溯的起点是 path[3][14]

回溯的路径是:

SEBEMBEBEMBEBEB

倒序一下就是:

BE/BE/BME/BE/BME/BE/S

所以切词结果就是:

小明/硕士/毕业于/中国/科学院/计算/所

到此,一个HMM模型中文分词算法过程就阐述完毕了。

也就是给定我们一个模型,我们对模型进行载入完毕之后,只要运行一遍Viterbi算法,就可以找出每个字对应的状态,根据状态也就可以对句子进行分词。

3、MixSegment是MP和HMM的结合,首先使用MP分词,然后对MP分词的结果使用HMM分词。其实,第二次使用HMM再分对原有分词结果调整得并不多,只是将MP结果中单字顺序收集再分词。

    void Cut(RuneStrArray::const_iterator begin,
RuneStrArray::const_iterator end, vector<WordRange>& res,
bool hmm) const {
if (!hmm) {
mpSeg_.Cut(begin, end, res);
return;
}
vector<WordRange> words;
assert(end >= begin);
words.reserve(end - begin);
mpSeg_.Cut(begin, end, words); vector<WordRange> hmmRes;
hmmRes.reserve(end - begin);
for (size_t i = ; i < words.size(); i++) {
//if mp Get a word, it's ok, put it into result
if (words[i].left != words[i].right
|| (words[i].left == words[i].right
&& mpSeg_.IsUserDictSingleChineseWord(
words[i].left->rune))) {
res.push_back(words[i]);
continue;
} // if mp Get a single one and it is not in userdict, collect it in sequence
size_t j = i;
while (j < words.size() && words[j].left == words[j].right
&& !mpSeg_.IsUserDictSingleChineseWord(words[j].left->rune)) {
j++;
} // Cut the sequence with hmm
assert(j - >= i);
// TODO
hmmSeg_.Cut(words[i].left, words[j - ].left + , hmmRes);
//put hmm result to result
for (size_t k = ; k < hmmRes.size(); k++) {
res.push_back(hmmRes[k]);
} //clear tmp vars
hmmRes.clear(); //let i jump over this piece
i = j - ;
}
}

二、关键词提取

cpp结巴的提供了两种关键词提取方法,分别基于TF-IDF和TextRank算法,下面分别介绍

1.基于TF-IDF算法的关键词抽取

TF(term frequency):一个词语在单个文档出现的次数
IDF(Inverse document frequency): 逆文档频率,是一个词语普遍度的度量。某一特定词语的IDF,有总文件数目除以包含该词语的文件数目,再将取得商取对数的得到。
总的来说某一特定文件内的高频率词语,以及词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。
cppjieba中提供接近26万词语的IDF(idf.utf8),对于在idf.utf8中没出现的词语,idf权重取平局值。除此之外,有些词语在文档的出现的频率TF很高,但是对于文档来说没什么实在的意义比如“的”,“是”等,因此cppjieba还提供了了一个1534个词语的stop_words.utf8。
cppjieba中根据权重(tf*idf)排序,取出topN个关键词。
partial_sort(keywords.begin(), keywords.begin() + topN, keywords.end(),Compare);

参考:http://blog.csdn.net/awj3584/article/details/18604901

2.基于TextRank的关键词提取

  PageRank根据网页之间的链接关系,建立网页之间的有向图,图中的节点表示每个网页,然后根据这个有向图迭代计算出每个网页的PER值,作为网页重要性排名的依据。TextRank方法与PageRank的方法非常类似,当使用TextRank方法做文本的关键字提取时,首先在给定的共现窗口内(cppjieba中默认为5)根据词语之间的共现关系建立链接关系图,图中的每个节点代表每个节点,每条边的权重表示词语共现的次数。然后根据词语之间链接关系图,迭代计算出每个词语的权重值,根据权重值选出文本中权重值大的几个关键词。

  TextRank 一般模型可以表示为一个有向有权图 G =(V, E), 由点集合 V和边集合 E 组成, E 是V ×V的子集。图中任两点 Vi , Vj 之间边的权重为 wji , 对于一个给定的点 Vi, In(Vi) 为 指 向 该 点 的 点 集 合 , Out(Vi) 为点 V指向的点集合。点 Vi 的得分定义如下:

  其中, d 为阻尼系数, 取值范围为 0 到 1, 代表从图中某一特定点指向其他任意点的概率, 一般取值为 0.85。使用TextRank 算法计算图中各点的得分时, 需要给图中的点指定任意的初值, 并递归计算直到收敛, 即图中任意一点的误差率小于给定的极限值时就可以达到收敛。cppjieba在实现默认迭代10次。

参考:http://www.cnblogs.com/clover-siyecao/p/5726480.html
参考:http://blog.sohu.com/s/MTAzMjM1NDY0/239636012.html(矩阵形式)

三、词性标注

cppjieba中的词性标注实现过程总的来说是基于词典的查询,词典中存有大约35万词汇的词性。单个词语的词性标注,直接查询词典,词典中不存在一个词语多个词性的问题。词典没有的词语,根据词语的特点的简单的标注为数字(m),英文(eng),以及x。对于整个句子的词性标注是用分词算法分词,然后用上述单个词语词性标注的方法逐个标注词性。由此可见cppjieba的词性标注非常依赖词典。对于句子的词性标注,没有考虑词性之间的关系。

参考: CppJieba代码详解

"结巴"(Jieba)中文分词系列性能评测

cppjieba分词学习笔记的更多相关文章

  1. Solr学习笔记之2、集成IK中文分词器

    Solr学习笔记之2.集成IK中文分词器 一.下载IK中文分词器 IK中文分词器 此文IK版本:IK Analyer 2012-FF hotfix 1 完整分发包 二.在Solr中集成IK中文分词器 ...

  2. spark学习笔记总结-spark入门资料精化

    Spark学习笔记 Spark简介 spark 可以很容易和yarn结合,直接调用HDFS.Hbase上面的数据,和hadoop结合.配置很容易. spark发展迅猛,框架比hadoop更加灵活实用. ...

  3. <老友记>学习笔记

    这是六个人的故事,从不服输而又有强烈控制欲的monica,未经世事的千金大小姐rachel,正直又专情的ross,幽默风趣的chandle,古怪迷人的phoebe,花心天真的joey——六个好友之间的 ...

  4. JavaSE中Collection集合框架学习笔记(2)——拒绝重复内容的Set和支持队列操作的Queue

    前言:俗话说“金三银四铜五”,不知道我要在这段时间找工作会不会很艰难.不管了,工作三年之后就当给自己放个暑假. 面试当中Collection(集合)是基础重点.我在网上看了几篇讲Collection的 ...

  5. R︱shiny实现交互式界面布置与搭建(案例讲解+学习笔记)

    要学的东西太多,无笔记不能学~~ 欢迎关注公众号,一起分享学习笔记,记录每一颗"贝壳"~ --------------------------- 看了看往期的博客,这个话题竟然是第 ...

  6. 《你不知道的 JavaScript 上卷》 学习笔记

    第一部分: 作用域和闭包 一.作用域 1. 作用域:存储变量并且查找变量的规则 2. 源代码在执行之前(编译)会经历三个步骤: 分词/此法分析:将代码字符串分解成有意义的代码块(词法单元) 解析/语法 ...

  7. elasticsearch学习笔记——相关插件和使用场景

    logstash-input-jdbc学习 ES(elasticsearch缩写)的一大优点就是开源,插件众多.所以扩展起来非常的方便,这也造成了它的生态系统越来越强大.这种开源分享的思想真是与天朝格 ...

  8. Deep learning with Python 学习笔记(5)

    本节讲深度学习用于文本和序列 用于处理序列的两种基本的深度学习算法分别是循环神经网络(recurrent neural network)和一维卷积神经网络(1D convnet) 与其他所有神经网络一 ...

  9. Python 3之str类型、string模块学习笔记

    Windows 10家庭中文版,Python 3.6.4, Python 3.7官文: Text Sequence Type — str string — Common string operatio ...

随机推荐

  1. pro2

    #include<iostream> double sum(int n,dounle[]) {  double  array[100]; foe(int i=0;i<100;i++; ...

  2. Docker 入门笔记

    Docker 可以理解为一个轻量化的虚拟机, 启动速度快,本身占的资源小 [重要], 容器里是不能保存数据的,容器只要一停止, 所有的数据都会丢失,所以如果重要的数据, 都需要通过配制,把数据保存在 ...

  3. HtmlHelper扩展之mvchtmlstring

    后台: using System;using System.Web;using System.Web.Mvc; namespace EwayFramework.Utils.Token{ public ...

  4. oracle中的序列,可以解决自增各种编号自动增长问题,说一下我的小问题。

    1.生成0001-9999的编码 //在PL/sql中 创建一个序列 Create sequence seq_cdptIncrement by 1 --自增数Start with 1 --开始累加数M ...

  5. C#中的类

    C#编程语言,从本质上讲是一组类型声明.所以,本人认为第一个要区分的点是:类型!=类. 当然,如果想要系统的学习C#还是应该先了解一下.Net框架,本文目的只是从相对宏观的角度讲清楚C#中的类.关于类 ...

  6. .Net Core IFormFile 始终为空的问题

    之前获取上传文件都是使用Request.Form.Files获取,直到这次改成定义形参 IFormFile时才遇到这个问题. // POST api/values [HttpPost] public ...

  7. DS博客作业01-日期抽象数据类型的设计与实现

    1.思维导图和学习体会 1.1绪论知识思维导图 1.2学习体会 通过这几节课数据结构的新学习,让我感到了难度,很多概念性的东西,不是很好理解,老师在讲内容的时候,很容易跟不上节奏,我发现这门课的学习一 ...

  8. ClamAV学习【8】——64位Windows7下编译运行实践

    之前用SourceInsight静态分析了ClamAV引擎源码,现在打算开始动态研究下.不过出师不利,一开始就遇到纠结的问题,能力还有待提高. 从官网下了一个VS2005工程的源码包(http://d ...

  9. LEFT JOIN条件写在where里是不会多查出数据来的

    因为WHERE条件是对前面整个数据集进行查询,但如果条件放在ON里是会把在前表不在后表的数据查出来的

  10. Servlet+JSP+JDBC设计实现图书系统——管理功能实现

    写在前面,之前由于种种原因博客好久没有更新.最近打算重拾JavaWeb,所以从头开始,先用servlet+jdbc+bootstrap最基础的代码实现一个图书系统.考虑有管理员端+用户端,项目完成后会 ...