结巴分词1.8.2版本源代码解析(一)
2015-05-27 15:27
176 查看
概要说明:结巴分词是基于python的开源分词工具。在其根目录下的结构为
.
|--analyse
|--finalseg
|--posseg
|--__init__.py
|--__main__.py
|--_compat.py
|--dict.txt
其中analyse是对分词结果进行分析的文件夹,提供了TF-IDF算法和textrank算法。finalseg提供了vertbit算法需要的初始矩阵。posseg是进行词性标注的代码。
结巴分词的核心代码在根目录下的__init__.py中。
官方文档中关于算法的说明:
1、基于Trie树结构实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图(DAG)
2、采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合
3、对于未登录词,采用了基于汉字成词能力的HMM模型,使用了Viterbi算法
__init__.py:
全局变量:
DICTIONARY = "dict.txt" #默认字典名
DICT_LOCK = threading.RLock() #线程锁
FREQ = {} # to be initialized #tire树
total = 0 #计数器
user_word_tag_tab = {}
initialized = False #标记是否初始化
pool = None
tmp_dir = None
_curpath = os.path.normpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))#得到当前绝对路径
log_console = logging.StreamHandler(sys.stderr)
#日志相关
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(log_console)
关键函数:
1、 函数名:gen_pfdict(f_name) 这个是算法说明1中基于Trie树结构实现高效的词图扫描,但是通过代码可以看出这里所谓的Tire树,只是是一个字典,并且没有嵌套。
通过代码54-57行看出这里对每一个词从第一个字开始逐字增加判断是否包含在Tire树(字典)中。
2、 函数名:initialize(dictionary=None)。这里是对程序的初始化,主要工作是载入词典,这里运用了缓存技术(tempfile库),还没看这个库(mark)。
3、 函数名:get_DAG(sentence) 函数功能为把输入的句子生成有向无环图。测试sentence="英语单词的词形变化主要是增加前后缀"。
运行结果为:{"0": [0, 1, 3], "1": [1], "2": [2, 3], "3": [3], "4": [4], "5": [5, 6, 8], "6": [6, 7], "7": [7, 8], "8": [8], "9": [9, 10], "10": [10, 11], "11": [11], "12": [12, 13], "13": [13], "14": [14, 15], "15": [15, 16],
"16": [16]}
这个字典即为DAG,key为字所在的位置,value为从字开始能在FREQ中的匹配到的词末尾位置所在的list。句子中的第一个字为'英',所在位置即key为0,
value为[0, 1, 3],表示'英'、'英语'、'英语单词'可以再FREQ中找到。
4、函数名:__cut_all(sentence) 函数功能就是结巴分词的全模式分词,作用即把DAG中的结果全部显示出来。这里使用了yield来迭代返回结果。
5、函数名:__cut_DAG_NO_HMM(sentence)。函数功能:对sentence进行不加hmm的精确分词。精确分词是在全分词的基础上计算各种路径的概率,选取概率最大的路径。
函数calc(sentence, DAG, route)就是计算概率的过程。其中语句 xrange(N - 1, -1, -1)是从句子的末尾开始计算,
route[idx] = max((log(FREQ.get(sentence[idx:x + 1]) or 1) -
logtotal + route[x + 1][0], x) for x in DAG[idx])
max函数返回的是一个元组,计算方法是log(freq/total)+后一个字得到的最大概率路径的概率。这里即为动态规划查找最大概率路径。注意的是动态规划的方向是
从后往前。
6、函数名:__cut_DAG(sentence)功能是对语句进行精确分词并且使用HMM模型,对比函数__cut__DAG_NO_HMM(sentence)的主要区别是有一个未登录词的识别功能。代码为
if not FREQ.get(buf):
recognized = finalseg.cut(buf)
for t in recognized:
yield t
可以看到调用的是finalseg.cut()函数
7、函数名:cut(sentence, cut_all=False, HMM=True) 这里源代码中带有注释,函数根据参数的不同调用上面不同的函数4、5、6
测试代码:
.
|--analyse
|--finalseg
|--posseg
|--__init__.py
|--__main__.py
|--_compat.py
|--dict.txt
其中analyse是对分词结果进行分析的文件夹,提供了TF-IDF算法和textrank算法。finalseg提供了vertbit算法需要的初始矩阵。posseg是进行词性标注的代码。
结巴分词的核心代码在根目录下的__init__.py中。
官方文档中关于算法的说明:
1、基于Trie树结构实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图(DAG)
2、采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合
3、对于未登录词,采用了基于汉字成词能力的HMM模型,使用了Viterbi算法
__init__.py:
全局变量:
DICTIONARY = "dict.txt" #默认字典名
DICT_LOCK = threading.RLock() #线程锁
FREQ = {} # to be initialized #tire树
total = 0 #计数器
user_word_tag_tab = {}
initialized = False #标记是否初始化
pool = None
tmp_dir = None
_curpath = os.path.normpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))#得到当前绝对路径
log_console = logging.StreamHandler(sys.stderr)
#日志相关
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(log_console)
关键函数:
1、 函数名:gen_pfdict(f_name) 这个是算法说明1中基于Trie树结构实现高效的词图扫描,但是通过代码可以看出这里所谓的Tire树,只是是一个字典,并且没有嵌套。
通过代码54-57行看出这里对每一个词从第一个字开始逐字增加判断是否包含在Tire树(字典)中。
2、 函数名:initialize(dictionary=None)。这里是对程序的初始化,主要工作是载入词典,这里运用了缓存技术(tempfile库),还没看这个库(mark)。
3、 函数名:get_DAG(sentence) 函数功能为把输入的句子生成有向无环图。测试sentence="英语单词的词形变化主要是增加前后缀"。
运行结果为:{"0": [0, 1, 3], "1": [1], "2": [2, 3], "3": [3], "4": [4], "5": [5, 6, 8], "6": [6, 7], "7": [7, 8], "8": [8], "9": [9, 10], "10": [10, 11], "11": [11], "12": [12, 13], "13": [13], "14": [14, 15], "15": [15, 16],
"16": [16]}
这个字典即为DAG,key为字所在的位置,value为从字开始能在FREQ中的匹配到的词末尾位置所在的list。句子中的第一个字为'英',所在位置即key为0,
value为[0, 1, 3],表示'英'、'英语'、'英语单词'可以再FREQ中找到。
4、函数名:__cut_all(sentence) 函数功能就是结巴分词的全模式分词,作用即把DAG中的结果全部显示出来。这里使用了yield来迭代返回结果。
5、函数名:__cut_DAG_NO_HMM(sentence)。函数功能:对sentence进行不加hmm的精确分词。精确分词是在全分词的基础上计算各种路径的概率,选取概率最大的路径。
函数calc(sentence, DAG, route)就是计算概率的过程。其中语句 xrange(N - 1, -1, -1)是从句子的末尾开始计算,
route[idx] = max((log(FREQ.get(sentence[idx:x + 1]) or 1) -
logtotal + route[x + 1][0], x) for x in DAG[idx])
max函数返回的是一个元组,计算方法是log(freq/total)+后一个字得到的最大概率路径的概率。这里即为动态规划查找最大概率路径。注意的是动态规划的方向是
从后往前。
6、函数名:__cut_DAG(sentence)功能是对语句进行精确分词并且使用HMM模型,对比函数__cut__DAG_NO_HMM(sentence)的主要区别是有一个未登录词的识别功能。代码为
if not FREQ.get(buf):
recognized = finalseg.cut(buf)
for t in recognized:
yield t
可以看到调用的是finalseg.cut()函数
7、函数名:cut(sentence, cut_all=False, HMM=True) 这里源代码中带有注释,函数根据参数的不同调用上面不同的函数4、5、6
测试代码:
#coding=utf-8 #author:zhangyang #2015-5-27 #程序用于结巴分词根目录__init__.py测试 from __future__ import absolute_import, unicode_literals import os from math import log import json dirname = os.path.dirname(__file__) print dirname cwd=os.getcwd() print cwd ww=os.path.join(os.getcwd(), os.path.dirname(__file__)) print ww FREQ={} total=0 def gen_pfdict(f_name): lfreq = {} ltotal = 0 with open(f_name, 'rb') as f: lineno = 0 for line in f.read().rstrip().decode('utf-8').splitlines(): lineno += 1 try: word, freq = line.split(' ')[:2] freq = int(freq) lfreq[word] = freq ltotal += freq for ch in xrange(len(word)): wfrag = word[:ch + 1] if wfrag not in lfreq: lfreq[wfrag] = 0 except ValueError as e: logger.debug('%s at line %s %s' % (f_name, lineno, line)) raise e return lfreq, ltotal def get_DAG(sentence): global FREQ DAG = {} N = len(sentence) for k in xrange(N): tmplist = [] i = k frag = sentence[k] while i < N and frag in FREQ: if FREQ[frag]: tmplist.append(i) i += 1 frag = sentence[k:i + 1] if not tmplist: tmplist.append(k) DAG[k] = tmplist return DAG def calc(sentence, DAG, route): N = len(sentence) route = (0, 0) logtotal = log(total) for idx in xrange(N - 1, -1, -1): route[idx] = max((log(FREQ.get(sentence[idx:x + 1]) or 1) - logtotal + route[x + 1][0], x) for x in DAG[idx]) print json.dumps(route) #for k,v in route.items(): #print k,str(v) def main(): global FREQ,total dictfile='./dict.txt' FREQ,total=gen_pfdict(dictfile) print "total frequnce is "+str(total) print "dict length is "+str(len(FREQ)) #i=0 #g = lambda m: '\n'.join([ '%s=%d'%(k, v) for k, v in m.items() ]) sent="英语单词很难记忆" dag=get_DAG(sent) print json.dumps(dag) route={} calc(sent,dag,route) N=len(sent) x=0 while x < N: y = route[x][1] + 1 print y lword = sent[x:y] print lword x=y if __name__=='__main__': main()
相关文章推荐
- 结巴分词JAVA版本源码解析(二)
- 结巴分词JAVA版本源码解析(三)
- IK中文分词扩展自定义词典【源码解析:文中是Configuration类,但是我的是Configuration接口,DefaultConfig类,可能ik版本不一致】
- [转帖]中文分词源代码解析和下载
- libsvm最新源代码(版本3.21)理解解析(二)
- libsvm最新源代码(版本3.21)理解解析(三)
- 利用结巴分词的Python版本实现分词并统计词频
- MFC开发-待整理 --VS调试 不会命中断点,源代码与原始版本不同的解决办法
- 【结巴分词资料汇编】结巴中文分词基本操作(3)
- Torque源代码解析(一)
- 声波通信原理商用改进版本和源代码
- Java Arrays.sort源代码解析
- Android DiskLruCache 源代码解析 硬盘缓存的绝佳方案
- nutch源代码--html的头信息解析
- Kubernetes 1.9 容器存储接口(CSI)Alpha 版本全解析
- python解析百度网页源代码:取搜索引擎返回的前page_num*10个链接的url
- 当前不会命中断点,源代码与原始版本不同
- disksim raid 部分源代码解析
- 对Python中文分词模块结巴分词算法过程的理解和分析
- jQuery工作原理解析以及源代码示例