CSpan是标注器,词性标注和角色标注都是使用这个类来完成的,使用的是相同结构的词典,另标注算法是隐马模型(HMM)完成的。
类中使用的一些宏变量如下:
#define MAX_WORDS_PER_SENTENCE 120 //!用于记录每个句子中最多词的个数
#define MAX_UNKNOWN_PER_SENTENCE 200 //!用于定义每个句子中的最多未登陆词
#define MAX_POS_PER_WORD 20 //!用于定义每个词的最多词性
#define LITTLE_FREQUENCY 6 //!频率?
enum TAG_TYPE{
TT_NORMAL,
TT_PERSON,
TT_PLACE,
TT_TRANS_PERSON
};
//!标注的类型,如果是词性标注用的是第一个,否则是未登陆词,目前只处理这些类型
CSpan类的公有成员变量里面,用一个未登陆词的计数m_nUnknownIndex和二维数组
m_nUnknownWords来配合记录未登陆词的个数及每个未登陆词起始位置,数组m_dWordsPossibility则配合记录每个未登陆词的概率值(即dValue)。为什么感觉m_nUnknownIndex的名字有些名不符实呢?似乎应该是m_nUnknownCount才对,呵呵。
另外一个公有成员m_context是CContextStat类的,用于处理上下文文法吧,呵呵。待定//!
私有成员变量比较多,依次说明。m_tagType表示标记的类型,如果是TT_NORMAL就是词性标记,否则则是各个不同命名实体的角色标注;m_nStartPos记录起始位置;而数组m_nBestTag记录每个词的最佳标注结果;一起配合使用的几个数组m_sWords记录切出来的词,m_nTags记录每个词的所有可能词性(或者标注角色类型),m_nWordPosition记录词的位置,而m_nBestPrev是用于Viterbi解码算法中记录最佳路径的数组;m_dFrequency记录每个词的每个词性(标注类型)的概率。最后,m_nCurLenght记录当前长度,其实也可以看作是一个上述几个数组的有效元素的个数。
下面是重头戏,即各个成员函数的分析。
构造函数就是将上述数组、变量初始化,先看看这段代码再说要注意的事项:
if(m_tagType!=TT_NORMAL)
m_nTags[0][0]=100;//Begin tag
else
m_nTags[0][0]=0;//Begin tag
m_nTags[0][1]=-1;
m_dFrequency[0][0]=0;
m_nCurLength=1;
m_nUnknownIndex=0;
m_nStartPos=0;
m_nWordPosition[1]=0;
m_sWords[0][0]=0;
m_tagType=TT_NORMAL;//Default tagging type
第一就是构造函数的第一句对m_tgType的判断,由于是在构造函数中m_tagType其实未被赋值,那么m_tagType就是编译器的默认值(这里就是枚举类型的第一个值TT_NORMAL),而且最后还对m_tagType又赋了个初值TT_NORMAL,从最终结果上看似乎不错,但感觉赋值很无厘头。
另外就是m_nCurLength的初值不是0而是1,m_nWordPosition初值不是m_nWordPosition[0]而是m_nWordPosition [1]。具体的含义看看代码再解释//!
POSTagging函数应该是进行标注的主函数部分,在这之前要做的首先是初始化,即读入相应的未登陆词词典和上下文规则,这部分应该是在系统的初始化过程中完成的,对应的函数为LoadContext。传入的参数用做m_context初始化时使用。
下面正式说POSTagging函数。传入的第一个参数是NSP算法生成的粗分结果,后面的两个词典分别是核心词典和未登陆词词典。读分词结果,从当前读到的词开始,猜测该词的最好词性(或角色标注结果),然后根据当前标注器的类型(标词性或者相应的未登陆词)进行相应的处理。如果是词性,则根据猜测的结果对词性进行相应的更新;如果是命名实体,则调用相应的函数进一步处理,这里有一点要注意的是TT_PLACE(地名)和TT_TRANS_PERSON(音译人名)用的都是PlaceRecognize函数,不知道是写错了还是怎么着。每猜完一次都要对变量进行重置Reset()。大概过程就是这样,这里面比较核心的部分是猜测词性以及命名实体识别对应的几个函数。
GetFrom函数用于从传入的词链中根据起始位置nIndex读入一个词,读入的信息存在相应的成员变量中。主循环中有两个索引变量,I从1开始取值,控制的是m_sWords、m_nWordPosition、m_nTags、m_nBestPrev、m_dFrequency等成员变量的索引值,最后的值记录在m_nCurLength中用于表示标注后词的个数。而nWordsIndex则从传入的起始位置nIndex开始取值,控制的是传入的原始词串数据结构pWordItems的索引。I的取值不会超过MAX_WORDS_PER_SENTENCE(120)而nWordsIndex的取值不会超过MAX_WORDS(650)。主循环对I的最大取值进行了判断但是没有判断nWordsIndex的最大取值,只是根据对应位置的字符串是否为空来决定的,如果前面的计算中没有清空工作现场或者传入参数非法则有可能非法访问造成系统崩溃。
在标注的主循环中,如果进行词性标记或者未登陆词典中没有出现当前处理的这个词pWordItems[nWordsIndex].sWord,那么简单的将词拷入m_sWords[i]中,并记录下位置信息在m_nWordPosition[I+1]中,这也是为什么构造函数初始化时m_nWordPosition是从m_nWordPosition [1]开始赋值的,因为I的取值从1开始。这里同样存在一个隐患,因为只判断i<MAX_WORDS_PER_SENTENCE,当I的取值为MAX_WORDS_PER_SENTENCE-1时,对m_nWordPosition[I+1]的访问会造成越界。对于角色标注且在词典中找到词的情况,会做一个切分,把第一个字和该串的其余部分分开考虑。
记录下当前词的起始位置在m_nStartPos中,然后根据标注的不同类型进行处理。对于角色标注,如果是外国人名标注,会对半角符号“.”和“-”分别替换为相应的全角符号“.”“-”进行处理。从未登陆词词典中读入该词的所有词性及相应概率值,并记录在相应的m_nTags和m_dFrequency中。如果当前词为开始标记或者结束标记且从未登陆词词典中只出现了一次要更新m_nTags和m_dFrequency的相应值,注意对于开始标记更新的是[i][j-1]而结束标记更新的是[i][j]。其他的需要标记角色的词,从核心词典中读入该词的词性及相应频率,如果词典中已经有相应的词及词性,则将角色标记结果置为0,概率结果也重置。注意这里更新了j的值,以便后面判断是否已有标注结果。相对而言词性标注的工作则比较简单,如果当前词条的nHandle值大于0,即只有一个唯一确定的词性那么记录下来就OK了;否则,属于有多个词性的情况,记录词性的时候取个负值即可。然后从核心词典中读入该词的多个词性,依次记录入m_nTags和m_dFrequency的后面的位置。
上面两种类型的标记都做完以后,如果j的取值仍然为0,即什么标记也没有得到,那么调用GuessPOS猜一个词性。至此会有一个标记了,不要忘记给m_nTags[i][j]添上结束标记-1。如果此时j的取值为1即只有一个标注结果且又不是开始标注(结束标记就可以退出了)那么表示无歧
义了可以跳出循环,否则继续。最后要做一些清理工作,包括判断是否已到词串的结尾,下面这段代码的含义没有看太明白,先记录下后补:
if(m_nTags[i-1][1]!=-1)//||m_sWords[i][0]==0
{//Set end for words like "张/华/平"
if(m_tagType!=TT_NORMAL)
m_nTags[i][0]=101;
else
m_nTags[i][0]=1;
m_dFrequency[i][0]=0;
m_sWords[i][0]=0;//Set virtual ending
m_nTags[i++][1]=-1;
}
GuessPos函数只对未登陆词进行角色的猜测,如果是词性标记则直接忽略掉了。对于中国人名、音译人名和地名,分别有更新m_nTags和m_dFrequency的计算方法,但是全部使用的是数值不清楚确切含义。
Disamb()函数用于消歧,即使用Viterbi算法选取全局最优的标注结果。对每个词的每个标注结果m_nTags[i][j],考察当前词的前面一个词的每个词性标注结果m_nTags[i-1][k],得到最小代价的路径之后将路径和频率值分别记录在m_nBestPrev[i][j]和m_dFrequency[i][j]之中。
Reset函数顾名思义用来重置内部变量,参数bContinue是用来控制重置的程度,如果bContinue为真则只是将上次的结果拿来做为初始状态,否则清空重新开始。
GetBestPOS()函数就很简单的,先进行消歧(调用Disamb函数),根据计算得到的结果从后向前回溯,将最好结果记录在m_nBestTag中。
PersonRecognize函数是专门用来做中国人名识别的,几种不同模式的定义在代码中都有说明,例如BBCD是表示姓+姓+名1+名2,这种模式的参数值是0.003606,模式的长度为4。先将计算得到的最佳角色标注结果的数值转换为字符。然后对于切好的词串进行于全排列的方式进行组合来匹配所有的模式,如果匹配到则更新相应的m_nUnknownWords和m_dWordsPossibility的值,并增加m_nUnknownIndex的计数。
PlaceRecognize的过程类似,但代码写得没有人名部分那么清楚。只有在m_nBestTag[i]的值为1或者2的时候才会引发识别过程,这两个数字代表的含义目前未知。程序同时引入了惩罚因子来对长度进行惩罚。最终更新m_nUnknownWords和m_dWordsPossibility的值。这个函数中使用多处while循环,并没有索引的值进行判断,可能会出现溢出的情况吧。
ComputePossibility函数就是计算从传入的起始位置nStartPos长度为nLength的词串的相应概论,用的是词的最佳标注结果来进行计算,没有更多可说的内容。
CSpan类的总结:
至此,CSpan的内容基本上写完了,因为最初写的时候也没有完整的概念体系所以比较凌乱,现在再总结一下。这个类是一个“通用的”进行标注类,使用的算法是隐马模型,解码用的是Viterbi算法。使用此类可进行词性的标注,也可以进行角色的标注,在部分代码中需要一定的判断。类的对外接口有两种功能类型,一种是初始化的时候读入相应的词典(未登陆词词典)及相应的上下文无关信息,另外一种就是对传入的词串进行标注,可能是词性也可能是命名实体,这部分的框架在POSTagging函数即可看到。读入一个词串和相应的词性概率等信息,用Viterbi算法计算得到最好的标注结果后,如果是词性标记则修改相应的词性标注结果,否则调用相应的命名实体单元来进行进一步的识别工作。直到处理完所有的结果。

ICTCLAS代码学习笔记之CSpan类相关推荐

  1. Kotlin学习笔记12——数据类和密封类

    Kotlin学习笔记12--数据类和密封类 前言 数据类 在类体中声明的属性 复制 componentN 解构声明 密封类 尾巴 前言 上一篇,我们学习了Kotlin中的拓展,今天继续来学习Kotli ...

  2. frustum pointnets训练代码学习笔记——kitti_object.py

    frustum pointnets训练代码学习笔记--kitti_object.py 本文记录了博主学习frustum pointnets过程中遇到的2D和3D数据库显示程序.为了画出输出结果,博主希 ...

  3. JVM学习笔记(Ⅰ):Class类文件结构解析(带你读懂Java字节码,这一篇就够了)

    JVM学习笔记(Ⅰ):Class类文件结构解析,带你读懂Java字节码 前言:本文属于博主个人的学习笔记,博主也是小白.如果有不对的地方希望各位帮忙指出.本文主要还是我的学习总结,因为网上的一些知识分 ...

  4. GEE(Google Earth Engine) 代码学习笔记一 快速入门

    GEE 代码学习笔记一 (GEE 基于JavaScript语言和python语言,我记录的是JavaScript语言) 1.GEE 快速入门 quick start. 2.基本语句 - 简单输出 pr ...

  5. GEE (Google Earth Engine)最基础代码学习笔记三

    GEE (Google Earth Engine)代码学习笔记三 本次学习核心为:将JavaScript objects and primitives放入Earth Engine 容器传到服务器,并处 ...

  6. DSB2017项目grt123代码学习笔记一:项目基本情况

    DSB2017项目grt123代码学习笔记一:项目基本情况 Kaggle上Data Science Bowl 2017年肺结节检测比赛第一名grt123团队的算法. github地址:https:// ...

  7. java学习笔记7--抽象类与抽象方法

    接着前面的学习: java学习笔记6--类的继承.Object类 java学习笔记5--类的方法 java学习笔记4--类与对象的基本概念(2) java学习笔记3--类与对象的基本概念(1) jav ...

  8. Python3 基础学习笔记 C08 【类】

    CSDN 课程推荐:<8小时Python零基础轻松入门>,讲师齐伟,苏州研途教育科技有限公司CTO,苏州大学应用统计专业硕士生指导委员会委员:已出版<跟老齐学Python:轻松入门& ...

  9. 冰冰学习笔记:string类的简单模拟

    欢迎各位大佬光临本文章!!! 还请各位大佬提出宝贵的意见,如发现文章错误请联系冰冰,冰冰一定会虚心接受,及时改正. 本系列文章为冰冰学习编程的学习笔记,如果对您也有帮助,还请各位大佬.帅哥.美女点点支 ...

  10. Mr.J-- jQuery学习笔记(七)--CSS类操作文本值操作

    不了解属性以及属性操作的同学可以看我之前的博客:Mr.J-- jQuery学习笔记(五)--属性及属性节点 下面demo 中btn用到的角标,之前写验证码动态强度测试时也用过同样方法:Mr.J--密码 ...

最新文章

  1. 近期活动盘点:数据化思维困局和0.01突破讲座、极简大数据决策,培养数据化思维讲座...
  2. jquery设置复选框为只读_checkbox设置复选框的只读效果不让用户勾选
  3. emui10有机会升级鸿蒙,鸿蒙OS+EMUI10,能否助力华为实现科技突围?
  4. 转:UniqueID和ClientID的来源
  5. nodejs+vue.js+webpack
  6. iOS 日期格式的转换
  7. [原创]部门年会活动流程
  8. 请使用recaptcha_如何在30分钟内使用ReCaptcha和PHP构建Bootstrap电子邮件表单
  9. cocos2d-x的popScene的动画效果
  10. VMWare 全屏后最小化死机
  11. 码云上传项目时出现空目录的解决办法
  12. CuteEditor 6.0一个很奇怪的Bug
  13. php+编辑器+显示html,thinkphp——通过在线编辑器添加的内容在模板里正确显示(只显示内容,而不是html代码)...
  14. Rails I18n验证弃用警告
  15. Git:常用的命令(不断迭代完善)
  16. Mac上安装fastboot等工具与烧写images
  17. matlab|已知多点坐标,求两两之间的距离
  18. linux forge服务器,linux下搭建我的世界spongeforge 服务器 (海绵端)
  19. NetBean 6.0初探
  20. python3GUI--抖音无水印视频下载工具(附源码)

热门文章

  1. 史上最清晰的雷霆战机游戏开发全过程(基于java,素材和源码均齐全)
  2. SHU-“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛-K-购买装备
  3. Notepad++ 替代品开源了!
  4. juniper防火墙配置
  5. 手写Bundle Adjustment
  6. 聚沙成塔,浙江形成1000 万千瓦“虚拟电厂”
  7. 电脑是怎样执行编程语言的
  8. 《Intel汇编语言程序设计》环境搭建
  9. 把一个godaddy的域名转回国内的注册商
  10. python乘积函数_Python中的乘法函数