余华《活着》
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