1.0.3 • Published 5 years ago

ooooevan-jieba v1.0.3

Weekly downloads
2
License
ISC
Repository
github
Last release
5 years ago

学习jieba分词

实现了cut、cutHMM、cutAll、cutForSearch等方法

存在的问题:代码还没整理、cutHMM太慢未优化(字符串太长时构建成树耗时,内存不足)等等

参考:https://github.com/ustcdane/annotated_jieba

过程中的问题

1、同样的词库,自己的cut(str, true)和hmm方法和官方的结果不一致,cut(str)一样

如:

str = '到MI京研大厦'
nodejieba.cut(str); // ['到', 'M', 'I', '京', '研', '大厦']
nodejieba.cut(str, true); // ['到', 'MI', '京研', '大厦']
nodejieba.cutHMM(str);  // [ '到', 'MI', '京', '研大厦' ]
cut(str, true);   // [ '到M', 'I京', '研', '大厦' ]
hmm(str);  // ['到M', 'I京', '研大', '厦']

通过调试看源码,发现了hmm的两个正则匹配。中英文要区分

/([\u4E00-\u9FD5]+)/;   //将中文和非中文分开
/([a-zA-Z0-9]+(?:\.\d+)?%?)/; //匹配英文数字及其他符号,可以将英文数字和其他字符分开,后面的%符号挺奇怪的,是想匹配百分数吗
/([a-zA-Z0-9]+(?:\.\d)?)|([0-9]+(?:\.\d)?%?)/;  //匹配百分数,数字才能匹配百分号

在hmm函数添加了相关代码后,上面的问题解决了

2、hmm函数构建根据BEMS构建树未优化,耗时且太长会导致内存不足

通过比较,我发现是我和官方实现的方式不同,我的方式是:

const treeObj = getWordTree(str); //将字符串转为一个大对象,里面包含需要的字符节点相关数据
var treeArr = recursion(treeObj);  //将大对象转为同等的二维数组,里面有2**n个数组,也就是2**n个不同路径
var conarr = getAppropriate(treeArr); //从2**n个数组中选出最优的一个数组

这种方法很直白,是我根据hmm算法描述写的,有太多不需要的数据,导致内存可能爆掉。是2**n个而不是4**n,因为每个字符在所在位置都只有两种类型而不是四种,如开头字符只可能是B或S,不能是E或M。

str = '程序员'
treeObj = {
  b:{
    next:{
      e:{
        next:{...}
      },
      m:{
        next:{...}
      }
    },
    value: -7.5608
  },
  s:{
    next:{
      s:{
        next:{...}
      },
      b:{
        next:{...}
      }
    },
    value: -11.0326
  }
}
treeArr = [ //长度是8
  [{type:B,value:-7.5608},{type:E,value:-8.363},{type:B,value:-9.5638}],
  []
  ...
]
conarr = [ //最优路径
  {type: "B", value: -7.5608126080925, string: "程"},
  {type: "M", value: -9.818418731874154, string: "序"},
  {type: "E", value: -6.2620785681194855, string: "员"}
]

在测试较长字符串时,会内存不足。因为这个过程存储了太多无用变量

修改为官方的方式,思路如下小

str = '程序员'
const prevTrans = {   //上一个状态可能类型
  B: [E, S],
  E: [B, M],
  M: [B, M],
  S: [E, S]
}

"'程'的概率" = {B:'', E:'', M:'', S:''}
"'序'的概率" = {B:'', E:'', M:'', S:''}
"'员'的概率" = {B:'', E:'', M:'', S:''}
/*
开头字符概率是确定的,后一个要根据前一个来确定。如'序'(有BEMS)中的B,和前一个一起的可能类型组合是:EB或SB,则选择计算两种的较高概率的一种。
所以概率表保存的是组合的概率,每个字符的概率都基于前n个字符的概率。这里和cut函数最大切分组合类似,只是那里是从后往前,这里是从前往后的
"'程'的概率" = {B:'', E:'', M:'', S:''} //保存字符'程'的不同类型的概率
"'序'的概率" = {B:'', E:'', M:'', S:''} //保存字符'程序'的不同类型的概率
"'员'的概率" = {B:'', E:'', M:'', S:''} //保存字符'程序员'的不同类型的概率
*/

// 概率可以确定了,只要在计算概率时,同时保存路径
// 根据最后得到的最大概率类型,就可以得出是哪个路径了
path = {B: "BEB", E: "BME", M: "BMM", S: "BES"}

改成官方的方式,果然好多了,再也不怕用太多内存了

3、自定义字典的路径问题

没有测试时,处理路径是path.resolve(__dirname, './xx.utf8'),直接写也行,因为还不用考虑不同路径 当将它单独为一个模块时,用户可以调用load方法使用自定义字典,此时找不到自定义字典,因为路径还是相对模块自身的 若在test目录执行test.js,能找到文件正常执行,在其他目录都找不到文件无法使用

├jieba
│  ├─dict  #存储默认字典
│  ├─test
│  │  │─doc
│  │  │─test.js  #测试位置
│  │  └─user.dict.utf8  #字典文件
│  ├─index.js  #模块位置
│


初步了解了一下,好像用path模块也不能解决的,因为它是相对自身文件的,在模块内部无法知道用户文件的相对路径,需要借助node模块打包相关方案解决
1.0.3

5 years ago

1.1.2

5 years ago

1.1.0

5 years ago

1.0.2

5 years ago

1.0.0

5 years ago