NLP文本处理之分词实现---维特比和暴力

余华《活着》
 1. 检验一个人的标准,就是看他把时间放在了哪儿。
 	别自欺欺人;当生命走到尽头,只有时间不会撒谎。
 	 
 2. 这两只鸡养大了变成鹅,鹅养大了变成羊,
 	羊大了又变成牛。我们啊,也就越来越有钱啦。
#2020-6-7晚读了一遍活着,虽然自己现在技术远不如人,
#但我愿意为了NLP的技术学习而“活”,这又何尝不是一种活着呢。
#近来学习有感,一定要重视基础的算法及数据结构,任何时候,任何场合,任何对象,
#都能用到,今天的博文将会用到<动态规划>及<回溯递归>

搭建一个分词工具

1.构建中文分词词典

# TODO: 第一步: 从综合类中文词库.xlsx 中读取所有中文词。
#  hint: 思考一下用什么数据结构来存储这个词典会比较好? 要考虑我们每次查询一个单词的效率。 
import xlrd

def create_dic(file_path,sheet_index=0, col_index=0):
    temp = xlrd.open_workbook(file_path)
    table = temp.sheets()[sheet_index]
    col_values = table.col_values(col_index)

    max_len_word = 0
    dic_words = {}   # 保存词典库中读取的单词
    for word in col_values:
        dic_words[word] = 0.00001
        len_word = len(word)
        if len_word > max_len_word:
            max_len_word = len_word

    print(len(dic_words))
    print(max_len_word)
    return dic_words, max_len_word  

dic_words, max_len_word = create_dic(file_path = './综合类中文词库.xlsx')

# 以下是每一个单词出现的概率。为了问题的简化,我们只列出了一小部分单词的概率。 在这里没有出现的的单词但是出现在词典里的,统一把概率设置成为0.00001
# 比如 p("学院")=p("概率")=...0.00001
word_prob = {"北京":0.03,"的":0.08,"天":0.005,"气":0.005,"天气":0.06,"真":0.04,"好":0.05,"真好":0.04,"啊":0.01,"真好啊":0.02, 
             "今":0.01,"今天":0.07,"课程":0.06,"内容":0.06,"有":0.05,"很":0.03,"很有":0.04,"意思":0.06,"有意思":0.005,"课":0.01,
             "程":0.005,"经常":0.08,"意见":0.08,"意":0.01,"见":0.005,"有意见":0.02,"分歧":0.04,"分":0.02, "歧":0.005}
print(sum(word_prob.values()))

2. 基于枚举方法来搭建中文分词工具

2.1 对于给定字符串:找出所有可能的分割方式

”我们学习人工智能,人工智能是未来“,
[我们,学习,人工智能,人工智能,是,未来]
[我们,学习,人工,智能,人工智能,是,未来]
[我们,学习,人工,智能,人工,智能,是,未来]
[我们,学习,人工智能,人工,智能,是,未来] .......

2.2 我们也可以计算出每一个切分之后句子的概率

p(我们,学习,人工智能,人工智能,是,未来)= -log p(我们)-log p(学习)-log p(人工智能)-log p(人工智能)-log p()-log p(未来)
p(我们,学习,人工,智能,人工智能,是,未来)=-log p(我们)-log p(学习)-log p(人工)-log p(智能)-log p(人工智能)-log p()-log p(未来)
p(我们,学习,人工,智能,人工,智能,是,未来)=-log p(我们)-log p(学习)-log p(人工)-log p(智能)-log p(人工)-log p(智能)-log p()-log p(未来)
p(我们,学习,人工智能,人工,智能,是,未来)=-log p(我们)-log p(学习)-log p(人工智能)-log p(人工)-log p(智能)-log()-log p(未来) .....

2.3 返回第二步中概率最大的结果

import numpy as np
## TODO 请编写word_segment_naive函数来实现对输入字符串的分词
def seg_all(string):
    segments = []
    def recur_seg(string,start,seg):
        if(start == len(string)):
            segments.append(list(seg))
            return True
        for i in range(start+1,len(string)+1):
            sub_str = string[start:i]
            if sub_str in dic_words:
                seg.append(sub_str)
                recur_seg(string,i,seg)
                seg.pop()   # 回溯递归    
    recur_seg(string,0,[])
    return segments  
segments = seg_all("北京的天气真好啊")
print(segments)
def best_seg(string):
    segments = seg_all(string)
    best_segment = []
    best_score = np.inf
    for seg in segments:
        score = 0
        for w in seg:
            if w in word_prob:
                score += -np.log(word_prob[w])
            else:
                score += -np.log(0.00001)
        if score < best_score:
            best_score = score
            best_segment = seg
    return best_segment
best_seg("北京的天气真好啊")

在这里插入图片描述

3. 基于维特比算法来搭建中文分词工具

Step 1: 根据词典,输入的句子和 word_prob来创建带权重的有向图(Directed Graph)
有向图的每一条边是一个单词的概率(只要存在于词典里的都可以作为一个合法的单词),这些概率已经给出(存放在word_prob)。 注意:思考用什么方式来存储这种有向图比较合适? 不一定只有一种方式来存储这种结构。
Step 2: 编写维特比算法(viterebi)算法来找出其中最好的PATH, 也就是最好的句子切分
Step 3: 返回结果

import numpy as np
## TODO 请编写word_segment_viterbi函数来实现对输入字符串的分词
def word_segment_viterbi(input_str):
    """
    1. 基于输入字符串,词典,以及给定的unigram概率来创建DAG(有向图)。
    2. 编写维特比算法来寻找最优的PATH
    3. 返回分词结果
    
    input_str: 输入字符串   输入格式:“今天天气好”
    best_segment: 最好的分词结果  输出格式:["今天","天气","好"]
    """
    
    # TODO: 第一步:根据词典,输入的句子,以及给定的unigram概率来创建带权重的有向图(Directed Graph) 参考:课程内容
    #      有向图的每一条边是一个单词的概率(只要存在于词典里的都可以作为一个合法的单词),这些概率在 word_prob,如果不在word_prob里的单词但在
    #      词典里存在的,统一用概率值0.00001。
    #      注意:思考用什么方式来存储这种有向图比较合适? 不一定有只有一种方式来存储这种结构。 
    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]
            if sub in dic_words:
                if sub in word_prob:
                    graph[s][j] = -np.log(word_prob[sub])
                else:
                    graph[s][j] = -np.log(0.00001)
    # TODO: 第二步: 利用维特比算法来找出最好的PATH, 这个PATH是P(sentence)最大或者 -log P(sentence)最小的PATH。
    #              hint: 思考为什么不用相乘: p(w1)p(w2)...而是使用negative log sum:  -log(w1)-log(w2)-...
    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]+graph[j][i]<dp[i]:
                dp[i] = graph[j][i]
                path[i]=j
    # TODO: 第三步: 根据最好的PATH, 返回最好的切分
    seg = []
    i = 0
    for j in range(len(path)):
        if path[j]!=-1:
            seg.append(input_str[i:j+1])
            i = j+1
    return seg     

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值