知识大全 搜索引擎之中文分词实现(java版)

Posted 模型

篇首语:休言女子非英物,夜夜龙泉壁上鸣。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 搜索引擎之中文分词实现(java版)相关的知识,希望对你有一定的参考价值。

  前几天读到google研究员吴军的数学之美系列篇 颇有感触 而恰好自己前段时间做了个基于统计语言模型的中文切分系统的课程项目 于是乎 帖出来与大家共同学习

  分词技术在搜索引擎 信息提取 机器翻译等领域的重要地位与应用就不敖述了 步入正题 )

     一   项目概述

  本切分系统的统计语料是用我们学校自己开放的那部分 大家可以在 这里 下载 中文字符约 万 当然这都是已切分好了的 可以用此建立一个比较小的语料库 本系统我主要分下面四个步骤完成

     语料预处理

     建立 gram(统计二元模型)

     实现全切分

     评估测试

  下面我分别对这四个方面一一道来

       语料预处理

  下载的已切分的语料都是形如 /m  现实/n  的/u  顿悟/vn  却/d  被/p  描/v  出/v  形/Ng  来/v  /w 有的前面还保留了日期编号 因为这些切分语料的来源是人民日报 预处理主要是按标点符号分句 句子简单定义为( ?! )这五种标点符号结尾的词串 句子首尾分别添加<BOS>和<EOS>这两个表示句子开始和结束的标记 这在 gram建模时要用的 后面会提到 处理过程中 忽略词类信息和前面的日期信息 因为我这个切分系统不考虑词类标注 如前面这句预处理后应该为下面形式 <BOS>现实 的 顿悟 却 被 描 出 形 来 <EOS> 当然切分词之间你可以用你想用的符号标记 而不必是空格 因为考虑到所有的英文字符和数字的ASCII 我用了下面方法实现之

  

  out ; //输出流      

  in;  //输入流

  StringBuffer s = new StringBuffer();     //缓冲

  char a = in read();

  while (a != )             //判断是否已到流的终点

                   

  if ((a == || a == ? || a == ! || a == || a == ))  //一句结束

              

  String s = new String(s );

  out write( <BOS> );                //在句子前加 <BOS>

  out write(s );

  out write( <EOS> );                    //在句子末尾加 <EOS>

  out write( /n );        //换行

  s = new StringBuffer();

  

  else if ( a == / )                         

  s = s append((char) );   //分词位置空格

  else if (a > )                         

  s = s append((char)a);      

  a = in read();                                                        

  

  out close(); 

  in close();

   

       建立 gram模型(统计二元模型)

  在这里首先简单介绍一下n gram模型和 gram模型

  根据语言样本估计出的概率分布P就称为语言L的语言模型 对给定的句子s = w w …wn (数字 n i都为下标 wi为句子s的一个词) 由链式规则(Chain rule) P(s) = p(w )p(w |w )p(w |w w )……p(wn|w w w …w(n ))  对p(wi|w w …w(i ))而言 (w w …w(i ))即为wi的历史 考虑前面n 个词构成历史的模型即为n gram模型 n越大 提供的语境信息也越多 但代价就越大 且需训练语料多 n较小时 提供的信息比较少 但计算代价小 且无需太多训练语料

  令c(w … wi)表示词串w w …wi在训练语料中出现的次数 则由最大似然估计 P(wn|w … w(n )) = c(w … wn) / c(w … w(n )) 同理 则 gram为 P(wn|w(n )) = c(w(n ) wn) / c(w(n ))

  若想了解更多相关知识 大家找相关资料看看 随便把大学时的那本概率与统计课本拿出来翻翻 数学真是一个好东东 )

  回归项目 )  训练语料一共有 万多个不同的词 建立 gram统计模型时不断要把每个词在训练语料中出现频率统计出来 还要把每个词及其后面的那个词组成的 gram在训练语料中出现频率统计出来 因为在切分时会频繁的在建立的 gram模型中查找相关的数据 所有 存储这个 gram模型数据的数据结构一定要能提供高效的查找 故选择hash表 它能提供常数时间的查找 Java类库里提供了HashMap类 基于数据两还不是非常大 故可直接拿来用 在存储时 每一个key值对应一个在训练语料中出现过的词语 而每一个key值对应的value值又是一个HashMap 暂且称为子hashmap 这个结构有点类似文件结构里的二级索引 其相关代码如下

  怎么在预处理文件里把词分别读出来就不罗嗦了 方法 每读入一行 按空格分成String数组 用个正则表达式匹配下即能得到

  

  

  //此方法传入的两个词组成一个 gram prewd为前一个词 currwd为紧随其后的词

  public static void add(String prewd String currwd)              

  String key = prewd;

  String curr = currwd;

  boolean bb = ntainsKey(key);         //Hmap是一个已存在的HashMap 用来存储 gram统计模型 在这里判断 preword 是否在 主map 中

  if (bb == false)            //若 主map 中无 则添加

  HashMap hm = new HashMap();          //首先 新构造一个 子MAP

  hm put(key new Integer( ));             //存储 主KEY 的频率

  hm put(curr new Integer( ));            //存储 主KEY 后面紧接着的那个词频率

  HMap put(key hm);              //将 主KEY 和对应的 子MAP 放入 主MAP 中

  

  else         //若 主map 中含有该词            

  

  HashMap temp = (HashMap)HMap get(key);              //返回 主KEY 所对应的 子MAP 进行值的修改

  int count = ((Integer)temp get(key)) intValue() + ;           //在 子map 中将 主key 次数加

  temp put(key new Integer(count));                                

    if (ntainsKey(curr))                 //判断 子map 中是否含有该词

  

  int value = ((Integer)temp get(curr)) intValue() + ;

  temp put(curr new Integer(value));   

             

  else

  temp put(curr new Integer( ));                  //若无 则将其存入子map  

  HMap put(key temp);                 //子map 修改完毕 将其重新放入 主map

             

  

    

  

  

  因为语言中的大部分词属于低频词 所以稀疏问题肯定存在 而MLE(最大似然估计)给在训练语料中没有出现的 gram的赋给 概率 所以还得对 gram模型进行数据平滑 以期得到更好的参数 目前平滑技术比较多 如Add one Add delta Witten Bell held out留存平滑等 本系统主要采用了Add delta和held out两中平滑方式 下面就Add delta平滑技术为例 对 gram进行平滑 对 gram模型 其平滑公式为

   P(wn|w(n )) = [c(w(n ) wn) + delta ] / ( N + delta * V) 

  这里去delta为

  其中 N 训练语料中所有的 gram的数量

  V 所有的可能的不同的 gram的数量

  平滑思路 产生主hashmap的迭代器iterator 依次读key;

   对每一个key 又读出其value 即一个子hashmap;

   然后根据平滑公式对子map里的值进行计算修改

  算法框架

              Iterator it = 主hashmap keySet(erator();

  While(it hasNext())

  

  主key = it next();

  子hashmap = (HashMap)主hashmap get(主key);              

  Iterator itr = 子hashmap keySet(erator();

  While(itr hasNext())

  

  根据平滑公式依次计算修改

    

    

  注意问题 因为计算得出的概率值一般都比较小 为了防止出现下溢 可对其取对数 再取反

   每一个主key所对应的所有没有出现过的 即频率为零的 gram 统一用一个键值对存储在相应的子hashmap里即可

  完毕 对象序列化 使用该系统时 lazy load将其载入内存 然后可让其一直存活在内存 这会大大加快速度

  到此 gram模型建立完毕

    

                 全切分实现

  切词一般有最大匹配法(MM RMM) 基于规则的方法 基于统计的方法 关于前两者就不罗嗦了 所谓全切分就是要根据字典得到所以可能的切分形式 歧义识别的方法主要有 基于规则的方法和基于统计的方法 这里当然是采用基于 gram统计模型的方法了 )为了避免切分后再进行歧义分析的时间浪费 并且这里采用边切分边评价的方法 即在切分进行的同时进行评价的方法   

  对一个句子进行全切分的结果 即所以可能的组合 可以形成一棵解空间树

  于是 可用回溯法搜索最优解

  若将所有的全切分组合先搜索出来 然后再根据 gram选择最佳 显然会很浪费时间 因为过程中可能存在很多的重复搜索 而回溯搜索的时间复杂度为指数时间

  所以 在搜索过程中要结合 剪枝 避免无效搜索 可很大提高效率

  采用树的深度优先法则 可找到最优解

  具体算法如下

  

  Stack push(BOS) //树节点

  while stack不为空

  x=stack pop()

  pos =x.Pos w = x w  oldvalue = x value preword =x preword

  if m>O then    //m为首词串的个数

  forj = to m do

  FWj为fwc的第j个元素l

  if length(w+FWj) =length(c)且概率最大 then output w+FWjl且设置最新的句子最大概率值

  else

  posl =pos+length(FWj)l

  if probability(w+FWj posl newsate)>maxValue(pos )

  stack push(x)

  endif

  endfor

  endif

  endwhile

  end. 

  

  

在算法实现过程中需要考虑一些诸如树节点保存 首词串处理等问题

     评估测试

  环境 windows XP AMD Athlon + Memory m JDK

  Delta平滑 随着delta的取值变小 准确率上升

  召回率              

  准确率              

    

  留存平滑

  召回率        

  准确率        

一般情况下 留存平滑应该还是比delta平滑更好

    所有建模过程及平滑过程在 分钟内都可完成

  切分时间与效率

  n       测试语料 字符 (中文 ) 平均句长 个字 时间 ms   平均切分速度 万/S

  n       万测试语料(取自笑傲江湖) 预处理后 万 时间 MS 句子文本行数目 平均句长   切分时间 MS 平均 万 / 秒 

  n       万测试语料(取自笑傲江湖) 不预处理 平均句长 切分时间 S 平均 字/秒

  回溯算法是时间开销为O(N!) 所以在切分过程中句子长度直接决定了切分的速度 因为句子越长词越多

  经过预处理 句子短 平均句长 回溯短 故速度要快很多

           到此 该系统基本完成 告一段落 感觉写的挺乱的呵呵

cha138/Article/program/Java/hx/201311/25784

相关参考

知识大全 Java中国象棋博弈程序探秘之搜索算法

Java中国象棋博弈程序探秘之搜索算法  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  搜索是电脑

知识大全 Java SE 6之脚本引擎 让程序如虎添翼

JavaSE6之脚本引擎让程序如虎添翼  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!现在JavaS

知识大全 大白菜v5.0怎么装win7旗舰版64位系统

大白菜v5.0怎么装win7旗舰版64位系统在使用U盘装系统之前先下载一个“大白菜超级u盘启动制作工具”将U盘制作成启动盘:(1)打开浏览器,然后在搜索引擎中输入“大白菜超级u盘启动制作工具”然后按回

知识大全 egg中文是什么

egg中文是什么egg中文翻译为:1.;鸡蛋;2.卵;卵细胞;3.【口】(通常用形容词修饰)人,家伙。复数:eggs第三人称单数:eggs过去式:egged过去分词:egged现在分词:eggingn

知识大全 用Java+MySQL+PHP轻松构建跨平台的搜索引擎

用Java+MySQL+PHP轻松构建跨平台的搜索引擎  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧

知识大全 让Java说话-用Java实现语音引擎

让Java说话-用Java实现语音引擎  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  为应用

知识大全 Java中的排序和搜索

Java中的排序和搜索  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  Java添加了自己的一套实

知识大全 用Lucene做一个简单的Java搜索工具

用Lucene做一个简单的Java搜索工具  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  初学L

知识大全 英语中,现在分词与过去分词是怎么构成的

英语中,现在分词与过去分词是怎么构成的?它们有什么区别?谢谢了,大神帮忙啊分词1.分词的形式这里所讲的分词包括-ing分词和-ed分词,即传统意义上的现在分词和过去分词。现在分词由动词原形+-ing构

知识大全 Java版快速排序

Java版快速排序  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!