CRF分词的纯Java实现
与基于隐马尔可夫模型的最短路径分词、N-最短路径分词相比,基于随机条件场(CRF)的分词对未登录词有更好的支持。本文(HanLP)使用纯Java实现CRF模型的读取与维特比后向解码,内部特征函数采用 双数组Trie树(DoubleArrayTrie)储存,得到了一个高性能的中文分词器。
CRF简介
CRF是序列标注场景中常用的模型,比HMM能利用更多的特征,比MEMM更能抵抗标记偏置的问题。

CRF训练
这类耗时的任务,还是交给了用C++实现的CRF++。关于CRF++输出的CRF模型,请参考《CRF++模型格式说明》。
CRF解码
解码采用维特比算法实现。并且稍有改进,用中文伪码与白话描述如下:
首先任何字的标签不仅取决于它自己的参数,还取决于前一个字的标签。但是第一个字前面并没有字,何来标签?所以第一个字的处理稍有不同,假设第0个字的标签为X,遍历X计算第一个字的标签,取分数最大的那一个。
如何计算一个字的某个标签的分数呢?某个字根据CRF模型提供的模板生成了一系列特征函数,这些函数的输出值乘以该函数的权值最后求和得出了一个分数。该分数只是“点函数”的得分,还需加上“边函数”的得分。边函数在本分词模型中简化为f(s',s),其中s'为前一个字的标签,s为当前字的标签。于是该边函数就可以用一个4*4的矩阵描述,相当于HMM中的转移概率。
实现了评分函数后,从第二字开始即可运用维特比后向解码,为所有字打上BEMS标签。
实例
还是取经典的“商品和服务”为例,首先HanLP的CRFSegment分词器将其拆分为一张表:
|
1
2
3
4
5
|
商 null 品 null 和 null 服 null 务 null |
null表示分词器还没有对该字标注。
代码
上面说了这么多,其实我的实现非常简练:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
/** * 维特比后向算法标注 * @param table */public void tag(Table table){ int size = table.size(); double bestScore = 0; int bestTag = 0; int tagSize = id2tag.length; LinkedList<double[]> scoreList = computeScoreList(table, 0); // 0位置命中的特征函数 for (int i = 0; i < tagSize; ++i) // -1位置的标签遍历 { for (int j = 0; j < tagSize; ++j) // 0位置的标签遍历 { double curScore = matrix[i][j] + computeScore(scoreList, j); if (curScore > bestScore) { bestScore = curScore; bestTag = j; } } } table.setLast(0, id2tag[bestTag]); int preTag = bestTag; // 0位置打分完毕,接下来打剩下的 for (int i = 1; i < size; ++i) { scoreList = computeScoreList(table, i); // i位置命中的特征函数 bestScore = Double.MIN_VALUE; for (int j = 0; j < tagSize; ++j) // i位置的标签遍历 { double curScore = matrix[preTag][j] + computeScore(scoreList, j); if (curScore > bestScore) { bestScore = curScore; bestTag = j; } } table.setLast(i, id2tag[bestTag]); preTag = bestTag; }} |
标注结果
标注后将table打印出来:
|
1
2
3
4
5
6
|
CRF标注结果商 B 品 E 和 S 服 B 务 E |
最终处理
将BEMS该合并的合并,得到:
|
1
|
[商品/null, 和/null, 服务/null] |
然后将词语送到词典中查询一下,没查到的暂时当作nx,并记下位置(因为这是个新词,为了表示它的特殊性,最后词性设为null),再次使用维特比标注词性:
|
1
|
[商品/n, 和/cc, 服务/vn] |
新词识别
CRF对分词有很好的识别能力,比如:
|
1
2
3
|
CRFSegment segment = new CRFSegment();segment.enableSpeechTag(true);System.out.println(segment.seg("你看过穆赫兰道吗")); |
输出:
|
1
2
3
4
5
6
7
8
9
10
|
CRF标注结果你 S 看 S 过 S 穆 B 赫 M 兰 M 道 E 吗 S [你/rr, 看/v, 过/uguo, 穆赫兰道/null, 吗/y] |
null表示新词。
CRF分词的纯Java实现的更多相关文章
- Hanlp中使用纯JAVA实现CRF分词
Hanlp中使用纯JAVA实现CRF分词 与基于隐马尔可夫模型的最短路径分词.N-最短路径分词相比,基于条件随机场(CRF)的分词对未登录词有更好的支持.本文(HanLP)使用纯Java实现CRF模型 ...
- 纯java配置SpringMVC
一般情况下,我们会在web.xml下配置好Spring和SpringMVC,并指定好它们的配置文件 是最常用的也是最方便的方法 例如: web.xml <!-- The definition o ...
- spring纯java注解式开发(一)
习惯了用XML文件来配置spring,现在开始尝试使用纯java代码来配置spring. 其实,spring的纯java配置,简单来说就是将bean标签的内容通过注解转换成bean对象的过程,没什么神 ...
- 纯java从apk文件里获取包名、版本号、icon
简洁:不超过5个java文件 依赖:仅依赖aapt.exe 支持:仅限windows 功能:用纯java获取apk文集里的包名,版本号,图标文件[可获取到流直接保存到文件系统] 原理:比较上一篇文章里 ...
- 基于纯Java代码的Spring容器和Web容器零配置的思考和实现(3) - 使用配置
经过<基于纯Java代码的Spring容器和Web容器零配置的思考和实现(1) - 数据源与事务管理>和<基于纯Java代码的Spring容器和Web容器零配置的思考和实现(2) - ...
- 纯JAVA驱动:sqlserver版本不同,驱动与连接也有所区别
纯JAVA驱动:// 2005 版本:驱动:Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");连接:”jd ...
- Android 使用纯Java代码布局
java布局 java代码布局和xml布局的区别 1.Java纯布局更加的灵活,比如自定义控件或一些特殊要求时,使用java代码布局 2.常用的xml布局是所见即所得的编写方式,以及xml本身拥有一些 ...
- simpleImageTool又纯java图片水印、缩放工具
simpleImageTool又一个简单.好用的图片格式转换.缩放水印叠加等功能的纯Java图片工具库. simpleImageTool的由来,近期需要用到图片处理,通过网上的图片流直接进行缩放水印叠 ...
- DataX通过纯Java代码启动
DataX是阿里巴巴团队开发的一个很好开源项目,但是他们对如何使用只提供了python命令启动方式,这种方式对于只是想简单的用下DataX的人来说很是友好,仅仅需要几行代码就可以运行,但是如果你需要在 ...
随机推荐
- ASP.NET Core 中的框架级依赖注入
https://tech.io/playgrounds/5040/framework-level-dependency-injection-with-asp-net-core 作者: Gunnar P ...
- JAVA泛型中的有界类型(extends super)(转)
JDK1.5中引入了泛型(Generic)机制.泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法. Ja ...
- Git 忽略某个目录中的文件,同时保留这个目录
类似的一个问题是项目根目录下可能有 logs 一类的目录, 我们希望他人把仓库 clone 下来的时候能够已经携带了这个目录, 但又不希望让这个目录中的日志文件进版本库. 之前看到一些项目用了一种比较 ...
- MFC之菜单
1菜单与菜单项的操作 //获取菜单指针----CWnd::GetMenu() //GetSubMenu()获取子菜单 /CheckMenuItem()加入/取消标记 GetMenu()->Get ...
- [Asp.net mvc]国际化
摘要 在实际项目中,经常遇到,开发的项目要提供给不同的国家使用,如果根据国家来开发不同的站点,肯定是非常耗时又耗成本的.asp.net中,提供了一种比较方便的方式,可以使用资源文件的方式,使我们的站点 ...
- win8操作系统下使用telnet客户端
一.安装Telnet客户端 今天尝试在Win8操作系统下使用telnet客户端连接上搜狐的邮件服务器时,结果出现了'telnet' 不是内部或外部命令,也不是可运行的程序,如下图所示: 上网查了一下原 ...
- AngularJS路由系列(2)--刷新、查看路由,路由事件和URL格式,获取路由参数,路由的Resolve
本系列探寻AngularJS的路由机制,在WebStorm下开发.主要包括: ● 刷新路由● 查看当前路由以及所有路由● 路由触发事件● 获取路由参数 ● 路由的resolve属性● 路由URL格式 ...
- WCF实现多个服务
本篇体验使用WCF实现2个服务.一个使用HTTP通讯,一个使用TCP通讯. 大致思路是: → 创建WCF服务以及接口,写2个接口→ 实现2个接口→ 为WCF创建一个控制台项目的宿主,配置App.con ...
- GDI+用PNG图片做半透明异型窗口
{*******************************************************} { ...
- Windows Phone本地数据库(SQLCE):7、Database mapping(翻译)
这是“windows phone mango本地数据库(sqlce)”系列短片文章的第七篇. 为了让你开始在Windows Phone Mango中使用数据库,这一系列短片文章将覆盖所有你需要知道的知 ...