jieba.posseg.cut(sentence)是initialize操作之后分词程序的入口
__cut_internal(sentence)函数内部定义了四个正则表达式
re_han = re.compile(ur"([\u4E00-\u9FA5a-zA-Z0-9+#&\._]+)")
re_skip = re.compile(ur"(\r\n|\s)")
re_eng = re.compile(ur"[a-zA-Z0-9]+")
re_num = re.compile(ur"[\.0-9]+")
函数内部语句blocks = re_han.split(sentence)把整个句子多个部分blk,若blk满足re_han模式,说明是有汉字的字符串,然后调用__cut_DAG(blk),构建字符串DAG和route
1、get_DAG(sentence)函数
DAG是有向无环图,结巴分词中的DAG的数据结构是dict,元素的key是int,value是int列表
下面部分内容引用自http://www.xuebuyuan.com/1547637.html
举一个例子,比如sentence 是 "国庆节我在研究结巴分词",对应生成的DAG是这样的:
{0: [0, 1, 2], 1: [1], 2: [2], 3: [3], 4: [4], 5: [5, 6], 6: [6], 7: [7, 8], 8: [8], 9: [9, 10], 10: [10]}
其中的数字表示每个汉字在sentence中的位置,所以0:[0,1,2] 表示 在trie 树中,"国"开头的词语中对应于该 sentence 有三种匹配情况:国,国庆,国庆节;分别对应3条有向图的路径:0->1->2->3,0->2->3,0->3。
initialize中生成的FREQ是一个dict类型的变量,它用来保存dict.txt中每个词语的频度打分,打分的公式是 log(float(v)/total),其中v就是被打分词语的频度值。
目标就很明确了:我们已经有了sentence的DAG和sentence中每个词语的频度得分,要在所有的路径中找出一条路径使频度得分的总和最大,这同时也是动态规划的一个典型应用。
2、calc(sentence,DAG,idx,route)函数
calc函数更新route
作者实现的代码如下:
def calc(sentence,DAG,idx,route):
N = len(sentence)
route[N] = (0.0,'')
for idx in xrange(N-1,-1,-1):
candidates = [ ( FREQ.get(sentence[idx:x+1],min_freq) + route[x+1][0],x ) for x in DAG[idx] ]
route[idx] = max(candidates)
这是一个自底向上的动态规划,从sentence的最后一个字开始倒序遍历每个分词方式的得分(必须是倒序,正序不行,大家可以思考下为什么)。然后将得分最高的情况以(频度得分值,词语最后一个字的位置)这样的tuple保存在route中。(注:从代码上看idx是一个无用的参数~)
3、__cut_DAG(sentence)函数的源码
def __cut_DAG(sentence):
DAG = jieba.get_DAG(sentence)
route ={}
jieba.calc(sentence,DAG,0,route=route) #根据语料库和频度打分机制生成一个初步的分词结果
x = 0
buf =u''
N = len(sentence)
while x
l_word = sentence[x:y]
if y-x==1: #单个的字符会把它放到buf中
buf+= l_word
else: #l_word的长度大于1
if len(buf)>0:
if len(buf)==1:
yield pair(buf,word_tag_tab.get(buf,'x')) ###
buf=u''
else:
######################################################################
if (buf not in jieba.FREQ): ### buf不在jieba.FREQ中
regognized = __cut_detail(buf) ### __cut_detail 处理未登录词
for t in regognized:
yield t
else:
for elem in buf:
yield pair(elem,word_tag_tab.get(elem,'x')) ###
buf=u''
######################################################################
yield pair(l_word,word_tag_tab.get(l_word,'x')) ###l_word的输出
x =y
###边界问题处理(细节)
4.__cut_detail(sentence)函数
__cut_detail(sentence)函数内部定义了四个正则表达式
re_han = re.compile(ur"([\u4E00-\u9FA5]+)")
re_skip = re.compile(ur"([\.0-9]+|[a-zA-Z0-9]+)")
re_eng = re.compile(ur"[a-zA-Z0-9]+")
re_num = re.compile(ur"[\.0-9]+")
函数内部语句blocks = re_han.split(sentence)把整个句子多个部分blk,若blk满足re_han模式,说明blk是汉字串,然后调用__cut(blk)
5.__cut(sentence)函数