一、知识储备
1.单词的编辑距离
这个概念表示的就是一个单词经过几次编辑形成新的单词。这个编辑包含了增加,删除,替换三种。例如 apple 的编辑距离为 1 的单词可以有 aapple bapple pple aple bpple 等,这就是所谓的编辑距离。如果需要得到编辑距离为2的单词。只需要在编辑距离为 1 的基础上再次调用函数。python的实现代码如下
功能,生成所有的编辑距离为 1 的单词。
def generate_candidates(word): """ :param word: 给定的输入单词 :return: 返回所有候选集合 """ ##生成编辑距离为 1 的单词 1)insert 2)delete 3)replace #假设添加的字母如下 letters = 'abcdefghijklmnopqrstuvwxyz' #将单词进行拆分,明确可以插入字母的位置 splits = [(word[:i],word[i:]) for i in range(len(word)+1)] #插入操作 inserts = [L+c+R for L,R in splits for c in letters] #删除操作 删除右边部分的第一个字母 deletes = [L+R[1:] for L,R in splits if R] #替换操作 将右边的第一个字符替换 replaces = [L+c+R[1:] for L,R in splits if R for c in letters] #生成的所有结果 candidates = set(inserts+deletes+replaces) #将不再词库的单词去掉 下边的词库为vocab 的 txt文件,后边也会用到 result = [word for word in candidates if word in vocab] return result
2.语言模型
目前,我学习到的语言模型是比较简单的 n-gream 。 这个涉及到了一些概率论的知识。举个例子说明一下:句子 I like learning NLP。我们需要计算 like 在I 之后出现的概率,我们选取n-gream 中 n 为2 的情况,即 Bi-gream。P(like| I )=P(I | like)*P(like) /P(I) 这个公式就是贝叶斯的公式。 P(Wi | Wi-1)=P(Wi-1 | PW)*P(W)/P(Wi-1)
计算的这个概率即为出现这种组合的概率。同理,如果 n = 3 则计算 P(Wi | Wi-1 Wi-2)的概率。这种联合概率的计算是概率论的知识。
以下是根据nltk库构建语言模型的代码。
#导入nltk from nltk.corpus import reuters #读取语料库 categories = reuters.categories() corpus = reuters.sents(categories=categories) #构建语言模型 ,使用bigram term_count = {} bigram_count = {} for doc in corpus: doc=['<s>']+doc for i in range(0,len(doc)-1): # [i,i+1] term = doc[i] bigram = doc[i:i+2] if term in term_count: term_count[term]+=1 else: term_count[term]=1 bigram = ' '.join(bigram) if bigram in bigram_count: bigram_count[bigram]+=1 else: bigram_count[bigram]=1 print(term_count) print(bigram_count )
3.平滑操作
目前我学习的平滑操作包括 add-one smoothing ; add-n smoothing ;good turning smoothing 。简单介绍一下 add-one smoothing 。 当我们训练语言模型的的时候,有些单词没有出现在训练的数据中,所以会有单词的概率为 0 ,这样就会导致部分的计算结果为0 ,会影响最后的效果。所以,为了效果更好一点,需要在计算概率的时候,分子加1 ,分母加上所有单词出现频率。P = P(Wi-1|Wi)+1 / P(Wi)+V 。这样最终结果的效果会好一点。
二、所有的代码如下
用到的数据集为
放到百度网盘
链接:https://pan.baidu.com/s/1pZbTOx9C8Y1xqV9rUWF4Eg
提取码:mir6
import numpy as np #读入词典库 vocab=set([line.rstrip() for line in open("data/vocab.txt")]) # print(vocab) #生成所有首选集合 def generate_candidates(word): """ :param word: 给定的输入单词 :return: 返回所有候选集合 """ ##生成编辑距离为 1 的单词 1)insert 2)delete 3)replace #假设添加的字母如下 letters = 'abcdefghijklmnopqrstuvwxyz' #将单词进行拆分,明确可以插入字母的位置 splits = [(word[:i],word[i:]) for i in range(len(word)+1)] #插入操作 inserts = [L+c+R for L,R in splits for c in letters] #删除操作 删除右边部分的第一个字母 deletes = [L+R[1:] for L,R in splits if R] #替换操作 将右边的第一个字符替换 replaces = [L+c+R[1:] for L,R in splits if R for c in letters] #生成的所有结果 candidates = set(inserts+deletes+replaces) #将不再词库的单词去掉 result = [word for word in candidates if word in vocab] return result #导入nltk from nltk.corpus import reuters #读取语料库 categories = reuters.categories() corpus = reuters.sents(categories=categories) #构建语言模型 ,使用bigram term_count = {} bigram_count = {} for doc in corpus: doc=['<s>']+doc for i in range(0,len(doc)-1): # [i,i+1] term = doc[i] bigram = doc[i:i+2] if term in term_count: term_count[term]+=1 else: term_count[term]=1 bigram = ' '.join(bigram) if bigram in bigram_count: bigram_count[bigram]+=1 else: bigram_count[bigram]=1 print(term_count) #用户犯错的概率 channel probability channel_pro = {} #读入写错的概率 for line in open('data/spell-errors.txt'): items = line.split(":") correct = items[0].strip() mistacks = [errorword.strip() for errorword in items[1].strip().split(",")] channel_pro[correct] = {} for mis in mistacks: channel_pro[correct][mis]=1.0/len(mistacks) V = len(term_count.keys()) file = open("data/testdata.txt",'r') for line in file: items = line.rstrip().split(" ") sentence = items[2].split() for word in sentence: if word not in vocab: #找到不再词典里的单词 s生成候选集合 然后 计算分数 score = P(correct)*P(mis|correct)= log(correct)+log(mis|correct) candidates = generate_candidates(word) probs = [] for candi in candidates: prob = 0 if candi in channel_pro and word in channel_pro[candi]: prob += np.log(channel_pro[candi][word]) else: prob += np.log(0.00001) #计算语言模型概率 I like apply computer 计算apply的语言模型概率 idx = items[2].index(word)+1 if items[2][idx-1] in bigram_count and candi in bigram_count[items[2][idx-1]]: # P = P(apply|like)+1 / P(like) + V prob += np.log( (bigram_count[items[2][idx-1]][candi] +1.0) / (term_count[items[2][idx-1]]+ V) ) else: prob += np.log(1.0/V) #平滑操作 probs.append(prob) max_idx = probs.index(max(probs)) print(word , candidates[max_idx])