普通方法
思路:
读入词典中的词,每个词对应一个概率;
读入句子,得到所有可能的句子的划分,返回unigram得分最大的一个分割;
其中得到所有可能的分割采用递归的方法,当前词在词典中,就继续递归划分后半段;
概率原理:P(x1,x2,x3,xn)=P(x1)* P(x2)*…*P(xn)
可以转化成-log的加法,返回最小值
代码:
#切割代码
def seg_all(string):
# string:读入的待分割的句子,递归进行
def to_seg(string,start,seg):
# start是子字符串开始下标 seg是string已经切割部分
if start >= len(string):
segments.append(list(seg))
print("segments_all",segments_all)
return True
# 遍历当前位置所有可能的切割
for i in range(start+1,len(string)+1):
sub = string[start:i]
# print("sub",sub)
if sub in dic_words:
seg.append(sub)
to_seg(string,i,seg)
seg.pop()
算概率部分就很简单了,dict是已知的,概率也是已知的
best_segment = []
best_score = 100
for seg in segments:
# TODO ...
score = 0
for w in seg:
if w in word_prob:
score += -math.log(word_prob[w])
else: # 不在给定的word_prob
score += -math.log(0.00001)
if score < best_score:
best_score = score
best_segment = seg
return best_segment
维特比算法(viterebi)
思路
1、以词典和输入的句子构建有向图
2、以动态规划的方式求有向图中的最优路径
其中构建有向图:图中的每条边都是合法分隔的概率(可以是log之后),从起点到终点的最短路径就是最优的分割(-log的和)
可以以每个字的概率做最基础的边,本身存在在词典中的词有合适的-log(weight),没有的可以给无穷大;然后考虑从每个字出发的子串是不是合法的词典,若是的话就添加上边,可以用二维数组来表示这个图也可以用邻接矩阵
对有向图求最短路径,可以用动态规划的算法
dp[i]表示到节点i时最短的路径,则:
dp[i] = min{ dp[j]+weight[j][i] }
代码
# 构建图:
inf = 99999
graph = [[inf]*len(input_str) for i in range(len(input_str))] # 初始化为二维矩阵
for s in range(len(input_str)):
for j in range(s+1,len(input_str)):
sub = input_str[s:j] # 从s开始的每一个子串
if sub in dic_words:
if sub in word_prob:
graph[s][j] = -math.log(word_prob[sub])
else:
graph[s][j] = -math.log(0.00001)
dp = [inf]*len(input_str)
path = [-1]*len(input_str)
dp[0] = 0
for i in range(1,len(input_str)):
for j in range(i):
if dp[j]+grapg[j][i] < dp[i]:
dp[i] = graph[j][i]
path[i] = j